You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

684 lines
20 KiB

4 years ago
  1. var expect = require('chai').expect,
  2. objectPath = require('./index.js');
  3. function getTestObj() {
  4. return {
  5. a: 'b',
  6. b: {
  7. c: [],
  8. d: ['a', 'b'],
  9. e: [{},{f: 'g'}],
  10. f: 'i'
  11. }
  12. };
  13. }
  14. describe('get', function() {
  15. it('should return the value under shallow object', function() {
  16. var obj = getTestObj();
  17. expect(objectPath.get(obj, 'a')).to.be.equal('b');
  18. expect(objectPath.get(obj, ['a'])).to.be.equal('b');
  19. });
  20. it('should work with number path', function() {
  21. var obj = getTestObj();
  22. expect(objectPath.get(obj.b.d, 0)).to.be.equal('a');
  23. expect(objectPath.get(obj.b, 0)).to.be.equal(void 0);
  24. });
  25. it('should return the value under deep object', function() {
  26. var obj = getTestObj();
  27. expect(objectPath.get(obj, 'b.f')).to.be.equal('i');
  28. expect(objectPath.get(obj, ['b','f'])).to.be.equal('i');
  29. });
  30. it('should return the value under array', function() {
  31. var obj = getTestObj();
  32. expect(objectPath.get(obj, 'b.d.0')).to.be.equal('a');
  33. expect(objectPath.get(obj, ['b','d',0])).to.be.equal('a');
  34. });
  35. it('should return the value under array deep', function() {
  36. var obj = getTestObj();
  37. expect(objectPath.get(obj, 'b.e.1.f')).to.be.equal('g');
  38. expect(objectPath.get(obj, ['b','e',1,'f'])).to.be.equal('g');
  39. });
  40. it('should return undefined for missing values under object', function() {
  41. var obj = getTestObj();
  42. expect(objectPath.get(obj, 'a.b')).to.not.exist;
  43. expect(objectPath.get(obj, ['a','b'])).to.not.exist;
  44. });
  45. it('should return undefined for missing values under array', function() {
  46. var obj = getTestObj();
  47. expect(objectPath.get(obj, 'b.d.5')).to.not.exist;
  48. expect(objectPath.get(obj, ['b','d','5'])).to.not.exist;
  49. });
  50. it('should return the value under integer-like key', function() {
  51. var obj = { '1a': 'foo' };
  52. expect(objectPath.get(obj, '1a')).to.be.equal('foo');
  53. expect(objectPath.get(obj, ['1a'])).to.be.equal('foo');
  54. });
  55. it('should return the default value when the key doesnt exist', function() {
  56. var obj = { '1a': 'foo' };
  57. expect(objectPath.get(obj, '1b', null)).to.be.equal(null);
  58. expect(objectPath.get(obj, ['1b'], null)).to.be.equal(null);
  59. });
  60. it('should return the default value when path is empty', function() {
  61. var obj = { '1a': 'foo' };
  62. expect(objectPath.get(obj, '', null)).to.be.deep.equal({ '1a': 'foo' });
  63. expect(objectPath.get(obj, [])).to.be.deep.equal({ '1a': 'foo' });
  64. expect(objectPath.get({ }, ['1'])).to.be.equal(undefined);
  65. });
  66. it('should skip non own properties with isEmpty', function(){
  67. var Base = function(enabled){ };
  68. Base.prototype = {
  69. one: {
  70. two: true
  71. }
  72. };
  73. var Extended = function(){
  74. Base.call(this, true);
  75. };
  76. Extended.prototype = Object.create(Base.prototype);
  77. var extended = new Extended();
  78. expect(objectPath.get(extended, ['one','two'])).to.be.equal(undefined);
  79. extended.enabled = true;
  80. expect(objectPath.get(extended, 'enabled')).to.be.equal(true);
  81. });
  82. });
  83. describe('set', function() {
  84. it('should set value under shallow object', function() {
  85. var obj = getTestObj();
  86. objectPath.set(obj, 'c', {m: 'o'});
  87. expect(obj).to.have.deep.property('c.m', 'o');
  88. obj = getTestObj();
  89. objectPath.set(obj, ['c'], {m: 'o'});
  90. expect(obj).to.have.deep.property('c.m', 'o');
  91. });
  92. it('should set value using number path', function() {
  93. var obj = getTestObj();
  94. objectPath.set(obj.b.d, 0, 'o');
  95. expect(obj).to.have.deep.property('b.d.0', 'o');
  96. });
  97. it('should set value under deep object', function() {
  98. var obj = getTestObj();
  99. objectPath.set(obj, 'b.c', 'o');
  100. expect(obj).to.have.deep.property('b.c', 'o');
  101. obj = getTestObj();
  102. objectPath.set(obj, ['b','c'], 'o');
  103. expect(obj).to.have.deep.property('b.c', 'o');
  104. });
  105. it('should set value under array', function() {
  106. var obj = getTestObj();
  107. objectPath.set(obj, 'b.e.1.g', 'f');
  108. expect(obj).to.have.deep.property('b.e.1.g', 'f');
  109. obj = getTestObj();
  110. objectPath.set(obj, ['b','e',1,'g'], 'f');
  111. expect(obj).to.have.deep.property('b.e.1.g', 'f');
  112. });
  113. it('should create intermediate objects', function() {
  114. var obj = getTestObj();
  115. objectPath.set(obj, 'c.d.e.f', 'l');
  116. expect(obj).to.have.deep.property('c.d.e.f', 'l');
  117. obj = getTestObj();
  118. objectPath.set(obj, ['c','d','e','f'], 'l');
  119. expect(obj).to.have.deep.property('c.d.e.f', 'l');
  120. });
  121. it('should create intermediate arrays', function() {
  122. var obj = getTestObj();
  123. objectPath.set(obj, 'c.0.1.m', 'l');
  124. expect(obj.c).to.be.an('array');
  125. expect(obj.c[0]).to.be.an('array');
  126. expect(obj).to.have.deep.property('c.0.1.m', 'l');
  127. obj = getTestObj();
  128. objectPath.set(obj, ['c','0', 1,'m'], 'l');
  129. expect(obj.c).to.be.an('object');
  130. expect(obj.c[0]).to.be.an('array');
  131. expect(obj).to.have.deep.property('c.0.1.m', 'l');
  132. });
  133. it('should set value under integer-like key', function() {
  134. var obj = getTestObj();
  135. objectPath.set(obj, '1a', 'foo');
  136. expect(obj).to.have.deep.property('1a', 'foo');
  137. obj = getTestObj();
  138. objectPath.set(obj, ['1a'], 'foo');
  139. expect(obj).to.have.deep.property('1a', 'foo');
  140. });
  141. it('should set value under empty array', function() {
  142. var obj = [];
  143. objectPath.set(obj, [0], 'foo');
  144. expect(obj[0]).to.be.equal('foo');
  145. obj = [];
  146. objectPath.set(obj, '0', 'foo');
  147. expect(obj[0]).to.be.equal('foo');
  148. });
  149. });
  150. describe('push', function() {
  151. it('should push value to existing array', function() {
  152. var obj = getTestObj();
  153. objectPath.push(obj, 'b.c', 'l');
  154. expect(obj).to.have.deep.property('b.c.0', 'l');
  155. obj = getTestObj();
  156. objectPath.push(obj, ['b','c'], 'l');
  157. expect(obj).to.have.deep.property('b.c.0', 'l');
  158. });
  159. it('should push value to new array', function() {
  160. var obj = getTestObj();
  161. objectPath.push(obj, 'b.h', 'l');
  162. expect(obj).to.have.deep.property('b.h.0', 'l');
  163. obj = getTestObj();
  164. objectPath.push(obj, ['b','h'], 'l');
  165. expect(obj).to.have.deep.property('b.h.0', 'l');
  166. });
  167. it('should push value to existing array using number path', function() {
  168. var obj = getTestObj();
  169. objectPath.push(obj.b.e, 0, 'l');
  170. expect(obj).to.have.deep.property('b.e.0.0', 'l');
  171. });
  172. });
  173. describe('ensureExists', function() {
  174. it('should create the path if it does not exists', function() {
  175. var obj = getTestObj();
  176. var oldVal = objectPath.ensureExists(obj, 'b.g.1.l', 'test');
  177. expect(oldVal).to.not.exist;
  178. expect(obj).to.have.deep.property('b.g.1.l', 'test');
  179. oldVal = objectPath.ensureExists(obj, 'b.g.1.l', 'test1');
  180. expect(oldVal).to.be.equal('test');
  181. expect(obj).to.have.deep.property('b.g.1.l', 'test');
  182. });
  183. it('should return the object if path is empty', function() {
  184. var obj = getTestObj();
  185. expect(objectPath.ensureExists(obj, [], 'test')).to.have.property('a', 'b');
  186. });
  187. it('Issue #26', function() {
  188. var any = {};
  189. objectPath.ensureExists(any, ['1','1'], {});
  190. expect(any).to.be.an('object');
  191. expect(any[1]).to.be.an('object');
  192. expect(any[1][1]).to.be.an('object');
  193. });
  194. });
  195. describe('coalesce', function(){
  196. it('should return the first non-undefined value', function(){
  197. var obj = {
  198. should: {have: 'prop'}
  199. };
  200. expect(objectPath.coalesce(obj, [
  201. 'doesnt.exist',
  202. ['might','not','exist'],
  203. 'should.have'
  204. ])).to.equal('prop');
  205. });
  206. it('should work with falsy values (null, 0, \'\', false)', function(){
  207. var obj = {
  208. is: {
  209. false: false,
  210. null: null,
  211. empty: '',
  212. zero: 0
  213. }
  214. };
  215. expect(objectPath.coalesce(obj, [
  216. 'doesnt.exist',
  217. 'is.zero'
  218. ])).to.equal(0);
  219. expect(objectPath.coalesce(obj, [
  220. 'doesnt.exist',
  221. 'is.false'
  222. ])).to.equal(false);
  223. expect(objectPath.coalesce(obj, [
  224. 'doesnt.exist',
  225. 'is.null'
  226. ])).to.equal(null);
  227. expect(objectPath.coalesce(obj, [
  228. 'doesnt.exist',
  229. 'is.empty'
  230. ])).to.equal('');
  231. });
  232. it('returns defaultValue if no paths found', function(){
  233. var obj = {
  234. doesnt: 'matter'
  235. };
  236. expect(objectPath.coalesce(obj, ['some.inexistant','path',['on','object']], 'false')).to.equal('false');
  237. });
  238. });
  239. describe('empty', function(){
  240. it('should ignore invalid arguments safely', function(){
  241. var obj = {};
  242. expect(objectPath.empty()).to.equal(void 0);
  243. expect(objectPath.empty(obj, 'path')).to.equal(void 0);
  244. expect(objectPath.empty(obj, '')).to.equal(obj);
  245. obj.path = true;
  246. expect(objectPath.empty(obj, 'inexistant')).to.equal(obj);
  247. });
  248. it('should empty each path according to their types', function(){
  249. function Instance(){
  250. this.notOwn = true;
  251. }
  252. /*istanbul ignore next: not part of code */
  253. Instance.prototype.test = function(){};
  254. /*istanbul ignore next: not part of code */
  255. Instance.prototype.arr = [];
  256. var
  257. obj = {
  258. string: 'some string',
  259. array: ['some','array',[1,2,3]],
  260. number: 21,
  261. boolean: true,
  262. object: {
  263. some:'property',
  264. sub: {
  265. 'property': true
  266. }
  267. },
  268. instance: new Instance()
  269. };
  270. /*istanbul ignore next: not part of code */
  271. obj['function'] = function(){};
  272. objectPath.empty(obj, ['array','2']);
  273. expect(obj.array[2]).to.deep.equal([]);
  274. objectPath.empty(obj, 'object.sub');
  275. expect(obj.object.sub).to.deep.equal({});
  276. objectPath.empty(obj, 'instance.test');
  277. expect(obj.instance.test).to.equal(null);
  278. expect(Instance.prototype.test).to.be.a('function');
  279. objectPath.empty(obj, 'string');
  280. objectPath.empty(obj, 'number');
  281. objectPath.empty(obj, 'boolean');
  282. objectPath.empty(obj, 'function');
  283. objectPath.empty(obj, 'array');
  284. objectPath.empty(obj, 'object');
  285. objectPath.empty(obj, 'instance');
  286. expect(obj.string).to.equal('');
  287. expect(obj.array).to.deep.equal([]);
  288. expect(obj.number).to.equal(0);
  289. expect(obj.boolean).to.equal(false);
  290. expect(obj.object).to.deep.equal({});
  291. expect(obj.instance.notOwn).to.be.an('undefined');
  292. expect(obj.instance.arr).to.be.an('array');
  293. expect(obj['function']).to.equal(null);
  294. });
  295. });
  296. describe('del', function(){
  297. it('should return undefined on empty object', function(){
  298. expect(objectPath.del({}, 'a')).to.equal(void 0);
  299. });
  300. it('should work with number path', function(){
  301. var obj = getTestObj();
  302. objectPath.del(obj.b.d, 1);
  303. expect(obj.b.d).to.deep.equal(['a']);
  304. });
  305. it('should delete deep paths', function(){
  306. var obj = getTestObj();
  307. expect(objectPath.del(obj)).to.be.equal(obj);
  308. objectPath.set(obj, 'b.g.1.0', 'test');
  309. objectPath.set(obj, 'b.g.1.1', 'test');
  310. objectPath.set(obj, 'b.h.az', 'test');
  311. expect(obj).to.have.deep.property('b.g.1.0','test');
  312. expect(obj).to.have.deep.property('b.g.1.1','test');
  313. expect(obj).to.have.deep.property('b.h.az','test');
  314. objectPath.del(obj, 'b.h.az');
  315. expect(obj).to.not.have.deep.property('b.h.az');
  316. expect(obj).to.have.deep.property('b.h');
  317. objectPath.del(obj, 'b.g.1.1');
  318. expect(obj).to.not.have.deep.property('b.g.1.1');
  319. expect(obj).to.have.deep.property('b.g.1.0','test');
  320. objectPath.del(obj, ['b','g','1','0']);
  321. expect(obj).to.not.have.deep.property('b.g.1.0');
  322. expect(obj).to.have.deep.property('b.g.1');
  323. expect(objectPath.del(obj, ['b'])).to.not.have.deep.property('b.g');
  324. expect(obj).to.be.deep.equal({'a':'b'});
  325. });
  326. it('should remove items from existing array', function(){
  327. var obj = getTestObj();
  328. objectPath.del(obj, 'b.d.0');
  329. expect(obj.b.d).to.have.length(1);
  330. expect(obj.b.d).to.be.deep.equal(['b']);
  331. objectPath.del(obj, 'b.d.0');
  332. expect(obj.b.d).to.have.length(0);
  333. expect(obj.b.d).to.be.deep.equal([]);
  334. });
  335. it('should skip undefined paths', function(){
  336. var obj = getTestObj();
  337. expect(objectPath.del(obj, 'do.not.exist')).to.be.equal(obj);
  338. expect(objectPath.del(obj, 'a.c')).to.be.equal('b');
  339. });
  340. });
  341. describe('insert', function(){
  342. it('should insert value into existing array', function(){
  343. var obj = getTestObj();
  344. objectPath.insert(obj, 'b.c', 'asdf');
  345. expect(obj).to.have.deep.property('b.c.0', 'asdf');
  346. expect(obj).to.not.have.deep.property('b.c.1');
  347. });
  348. it('should create intermediary array', function(){
  349. var obj = getTestObj();
  350. objectPath.insert(obj, 'b.c.0', 'asdf');
  351. expect(obj).to.have.deep.property('b.c.0.0', 'asdf');
  352. });
  353. it('should insert in another index', function(){
  354. var obj = getTestObj();
  355. objectPath.insert(obj, 'b.d', 'asdf', 1);
  356. expect(obj).to.have.deep.property('b.d.1', 'asdf');
  357. expect(obj).to.have.deep.property('b.d.0', 'a');
  358. expect(obj).to.have.deep.property('b.d.2', 'b');
  359. });
  360. it('should handle sparse array', function(){
  361. var obj = getTestObj();
  362. obj.b.d = new Array(4);
  363. obj.b.d[0] = 'a';
  364. obj.b.d[1] = 'b';
  365. objectPath.insert(obj, 'b.d', 'asdf', 3);
  366. expect(obj.b.d).to.have.members([
  367. 'a',
  368. 'b',
  369. ,
  370. ,
  371. 'asdf'
  372. ]);
  373. });
  374. });
  375. describe('has', function () {
  376. it('should return false for empty object', function () {
  377. expect(objectPath.has({}, 'a')).to.be.false;
  378. });
  379. it('should return false for empty path', function () {
  380. var obj = getTestObj();
  381. expect(objectPath.has(obj, '')).to.be.false;
  382. expect(objectPath.has(obj, [])).to.be.false;
  383. expect(objectPath.has(obj, [''])).to.be.false;
  384. });
  385. it('should test under shallow object', function() {
  386. var obj = getTestObj();
  387. expect(objectPath.has(obj, 'a')).to.be.true;
  388. expect(objectPath.has(obj, ['a'])).to.be.true;
  389. expect(objectPath.has(obj, 'z')).to.be.false;
  390. expect(objectPath.has(obj, ['z'])).to.be.false;
  391. });
  392. it('should work with number path', function() {
  393. var obj = getTestObj();
  394. expect(objectPath.has(obj.b.d, 0)).to.be.true;
  395. expect(objectPath.has(obj.b, 0)).to.be.false;
  396. expect(objectPath.has(obj.b.d, 10)).to.be.false;
  397. expect(objectPath.has(obj.b, 10)).to.be.false;
  398. });
  399. it('should test under deep object', function() {
  400. var obj = getTestObj();
  401. expect(objectPath.has(obj, 'b.f')).to.be.true;
  402. expect(objectPath.has(obj, ['b','f'])).to.be.true;
  403. expect(objectPath.has(obj, 'b.g')).to.be.false;
  404. expect(objectPath.has(obj, ['b','g'])).to.be.false;
  405. });
  406. it('should test value under array', function() {
  407. var obj = getTestObj();
  408. expect(objectPath.has(obj, 'b.d.0')).to.be.true;
  409. expect(objectPath.has(obj, ['b','d',0])).to.be.true;
  410. });
  411. it('should test the value under array deep', function() {
  412. var obj = getTestObj();
  413. expect(objectPath.has(obj, 'b.e.1.f')).to.be.true;
  414. expect(objectPath.has(obj, ['b','e',1,'f'])).to.be.true;
  415. expect(objectPath.has(obj, 'b.e.1.f.g.h.i')).to.be.false;
  416. expect(objectPath.has(obj, ['b','e',1,'f','g','h','i'])).to.be.false;
  417. });
  418. it('should test the value under integer-like key', function() {
  419. var obj = { '1a': 'foo' };
  420. expect(objectPath.has(obj, '1a')).to.be.true;
  421. expect(objectPath.has(obj, ['1a'])).to.be.true;
  422. });
  423. it('should distinct nonexistent key and key = undefined', function() {
  424. var obj = {};
  425. expect(objectPath.has(obj, 'key')).to.be.false;
  426. obj.key = undefined;
  427. expect(objectPath.has(obj, 'key')).to.be.true;
  428. });
  429. });
  430. describe('bind object', function () {
  431. // just get one scenario from each feature, so whole functionality is proxied well
  432. it('should return the value under shallow object', function() {
  433. var obj = getTestObj();
  434. var model = objectPath(obj);
  435. expect(model.get('a')).to.be.equal('b');
  436. expect(model.get(['a'])).to.be.equal('b');
  437. });
  438. it('should set value under shallow object', function() {
  439. var obj = getTestObj();
  440. var model = objectPath(obj);
  441. model.set('c', {m: 'o'});
  442. expect(obj).to.have.deep.property('c.m', 'o');
  443. obj = getTestObj();
  444. model = objectPath(obj);
  445. model.set(['c'], {m: 'o'});
  446. expect(obj).to.have.deep.property('c.m', 'o');
  447. });
  448. it('should push value to existing array', function() {
  449. var obj = getTestObj();
  450. var model = objectPath(obj);
  451. model.push('b.c', 'l');
  452. expect(obj).to.have.deep.property('b.c.0', 'l');
  453. obj = getTestObj();
  454. model = objectPath(obj);
  455. model.push(['b','c'], 'l');
  456. expect(obj).to.have.deep.property('b.c.0', 'l');
  457. });
  458. it('should create the path if it does not exists', function() {
  459. var obj = getTestObj();
  460. var model = objectPath(obj);
  461. var oldVal = model.ensureExists('b.g.1.l', 'test');
  462. expect(oldVal).to.not.exist;
  463. expect(obj).to.have.deep.property('b.g.1.l', 'test');
  464. oldVal = model.ensureExists('b.g.1.l', 'test1');
  465. expect(oldVal).to.be.equal('test');
  466. expect(obj).to.have.deep.property('b.g.1.l', 'test');
  467. });
  468. it('should return the first non-undefined value', function(){
  469. var obj = {
  470. should: {have: 'prop'}
  471. };
  472. var model = objectPath(obj);
  473. expect(model.coalesce([
  474. 'doesnt.exist',
  475. ['might','not','exist'],
  476. 'should.have'
  477. ])).to.equal('prop');
  478. });
  479. it('should empty each path according to their types', function(){
  480. function Instance(){
  481. this.notOwn = true;
  482. }
  483. /*istanbul ignore next: not part of code */
  484. Instance.prototype.test = function(){};
  485. /*istanbul ignore next: not part of code */
  486. Instance.prototype.arr = [];
  487. var
  488. obj = {
  489. string: 'some string',
  490. array: ['some','array',[1,2,3]],
  491. number: 21,
  492. boolean: true,
  493. object: {
  494. some:'property',
  495. sub: {
  496. 'property': true
  497. }
  498. },
  499. instance: new Instance()
  500. };
  501. /*istanbul ignore next: not part of code */
  502. obj['function'] = function(){};
  503. var model = objectPath(obj);
  504. model.empty(['array','2']);
  505. expect(obj.array[2]).to.deep.equal([]);
  506. model.empty('object.sub');
  507. expect(obj.object.sub).to.deep.equal({});
  508. model.empty('instance.test');
  509. expect(obj.instance.test).to.equal(null);
  510. expect(Instance.prototype.test).to.be.a('function');
  511. model.empty('string');
  512. model.empty('number');
  513. model.empty('boolean');
  514. model.empty('function');
  515. model.empty('array');
  516. model.empty('object');
  517. model.empty('instance');
  518. expect(obj.string).to.equal('');
  519. expect(obj.array).to.deep.equal([]);
  520. expect(obj.number).to.equal(0);
  521. expect(obj.boolean).to.equal(false);
  522. expect(obj.object).to.deep.equal({});
  523. expect(obj.instance.notOwn).to.be.an('undefined');
  524. expect(obj.instance.arr).to.be.an('array');
  525. expect(obj['function']).to.equal(null);
  526. });
  527. it('should delete deep paths', function(){
  528. var obj = getTestObj();
  529. var model = objectPath(obj);
  530. expect(model.del()).to.be.equal(obj);
  531. model.set('b.g.1.0', 'test');
  532. model.set('b.g.1.1', 'test');
  533. model.set('b.h.az', 'test');
  534. expect(obj).to.have.deep.property('b.g.1.0','test');
  535. expect(obj).to.have.deep.property('b.g.1.1','test');
  536. expect(obj).to.have.deep.property('b.h.az','test');
  537. model.del('b.h.az');
  538. expect(obj).to.not.have.deep.property('b.h.az');
  539. expect(obj).to.have.deep.property('b.h');
  540. model.del('b.g.1.1');
  541. expect(obj).to.not.have.deep.property('b.g.1.1');
  542. expect(obj).to.have.deep.property('b.g.1.0','test');
  543. model.del(['b','g','1','0']);
  544. expect(obj).to.not.have.deep.property('b.g.1.0');
  545. expect(obj).to.have.deep.property('b.g.1');
  546. expect(model.del(['b'])).to.not.have.deep.property('b.g');
  547. expect(obj).to.be.deep.equal({'a':'b'});
  548. });
  549. it('should insert value into existing array', function(){
  550. var obj = getTestObj();
  551. var model = objectPath(obj);
  552. model.insert('b.c', 'asdf');
  553. expect(obj).to.have.deep.property('b.c.0', 'asdf');
  554. expect(obj).to.not.have.deep.property('b.c.1');
  555. });
  556. it('should test under shallow object', function() {
  557. var obj = getTestObj();
  558. var model = objectPath(obj);
  559. expect(model.has('a')).to.be.true;
  560. expect(model.has(['a'])).to.be.true;
  561. expect(model.has('z')).to.be.false;
  562. expect(model.has(['z'])).to.be.false;
  563. });
  564. });