| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | import test from 'tape'; | 
					
						
							|  |  |  | import sinon from 'sinon'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   getNode, | 
					
						
							|  |  |  |   createMapUi, | 
					
						
							|  |  |  |   traverseMapUi, | 
					
						
							|  |  |  |   updateSingleNode, | 
					
						
							|  |  |  |   toggleThisPanel, | 
					
						
							|  |  |  |   expandAllPanels, | 
					
						
							| 
									
										
										
										
											2018-01-09 10:41:44 +03:00
										 |  |  |   collapseAllPanels, | 
					
						
							|  |  |  |   updatePath, | 
					
						
							|  |  |  |   openPath | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | } from './utils.js'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test('createMapUi', t => { | 
					
						
							|  |  |  |   t.plan(3); | 
					
						
							|  |  |  |   t.test('should return an `{}` when proper args not supplied', t => { | 
					
						
							|  |  |  |     t.plan(3); | 
					
						
							|  |  |  |     t.equal( | 
					
						
							|  |  |  |       Object.keys(createMapUi()).length, | 
					
						
							|  |  |  |       0 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     t.equal( | 
					
						
							|  |  |  |       Object.keys(createMapUi({}, [])).length, | 
					
						
							|  |  |  |       0 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     t.equal( | 
					
						
							|  |  |  |       Object.keys(createMapUi({ superBlock: {} }, [])).length, | 
					
						
							|  |  |  |       0 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   t.test('should return a map tree', t => { | 
					
						
							|  |  |  |     const expected = { | 
					
						
							|  |  |  |       children: [{ | 
					
						
							|  |  |  |         name: 'superBlockA', | 
					
						
							|  |  |  |         children: [{ | 
					
						
							|  |  |  |           name: 'blockA', | 
					
						
							|  |  |  |           children: [{ | 
					
						
							|  |  |  |             name: 'challengeA' | 
					
						
							|  |  |  |           }] | 
					
						
							|  |  |  |         }] | 
					
						
							|  |  |  |       }] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const actual = createMapUi({ | 
					
						
							|  |  |  |       superBlock: { | 
					
						
							|  |  |  |         superBlockA: { | 
					
						
							|  |  |  |           blocks: [ | 
					
						
							|  |  |  |             'blockA' | 
					
						
							|  |  |  |           ] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       block: { | 
					
						
							|  |  |  |         blockA: { | 
					
						
							|  |  |  |           challenges: [ | 
					
						
							|  |  |  |             'challengeA' | 
					
						
							|  |  |  |           ] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { superBlocks: ['superBlockA'] }, | 
					
						
							|  |  |  |     { challengeA: 'ChallengeA title'} | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |     t.plan(3); | 
					
						
							|  |  |  |     t.equal(actual.children[0].name, expected.children[0].name); | 
					
						
							|  |  |  |     t.equal( | 
					
						
							|  |  |  |       actual.children[0].children[0].name, | 
					
						
							|  |  |  |       expected.children[0].children[0].name | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     t.equal( | 
					
						
							|  |  |  |       actual.children[0].children[0].children[0].name, | 
					
						
							|  |  |  |       expected.children[0].children[0].children[0].name | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   t.test('should protect against malformed data', t => { | 
					
						
							|  |  |  |     t.plan(2); | 
					
						
							|  |  |  |     t.equal( | 
					
						
							|  |  |  |       createMapUi({ | 
					
						
							|  |  |  |         superBlock: {}, | 
					
						
							|  |  |  |         block: { | 
					
						
							|  |  |  |           blockA: { | 
					
						
							|  |  |  |             challenges: [ | 
					
						
							|  |  |  |               'challengeA' | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, { superBlocks: ['superBlockA'] }).children[0].children.length, | 
					
						
							|  |  |  |       0 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     t.equal( | 
					
						
							|  |  |  |       createMapUi({ | 
					
						
							|  |  |  |         superBlock: { | 
					
						
							|  |  |  |           superBlockA: { | 
					
						
							|  |  |  |             blocks: [ | 
					
						
							|  |  |  |               'blockA' | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         block: {} | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { superBlocks: ['superBlockA'] }).children[0].children[0].children.length, | 
					
						
							|  |  |  |       0 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | test('traverseMapUi', t => { | 
					
						
							|  |  |  |   t.test('should return tree', t => { | 
					
						
							|  |  |  |     t.plan(2); | 
					
						
							|  |  |  |     const expectedTree = {}; | 
					
						
							|  |  |  |     const actaulTree = traverseMapUi(expectedTree, tree => { | 
					
						
							|  |  |  |       t.equal(tree, expectedTree); | 
					
						
							|  |  |  |       return tree; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     t.equal(actaulTree, expectedTree); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   t.test('should hit every node', t => { | 
					
						
							|  |  |  |     t.plan(4); | 
					
						
							|  |  |  |     const expected = { children: [{ children: [{}] }] }; | 
					
						
							|  |  |  |     const spy = sinon.spy(t => t); | 
					
						
							|  |  |  |     spy.withArgs(expected); | 
					
						
							|  |  |  |     spy.withArgs(expected.children[0]); | 
					
						
							|  |  |  |     spy.withArgs(expected.children[0].children[0]); | 
					
						
							|  |  |  |     traverseMapUi(expected, spy); | 
					
						
							|  |  |  |     t.equal(spy.callCount, 3); | 
					
						
							|  |  |  |     t.ok(spy.withArgs(expected).calledOnce, 'foo'); | 
					
						
							|  |  |  |     t.ok(spy.withArgs(expected.children[0]).calledOnce, 'bar'); | 
					
						
							|  |  |  |     t.ok(spy.withArgs(expected.children[0].children[0]).calledOnce, 'baz'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   t.test('should create new object when children change', t => { | 
					
						
							|  |  |  |     t.plan(9); | 
					
						
							|  |  |  |     const expected = { children: [{ bar: true }, {}] }; | 
					
						
							|  |  |  |     const actual = traverseMapUi(expected, node => ({ ...node, foo: true })); | 
					
						
							|  |  |  |     t.notEqual(actual, expected); | 
					
						
							|  |  |  |     t.notEqual(actual.children, expected.children); | 
					
						
							|  |  |  |     t.notEqual(actual.children[0], expected.children[0]); | 
					
						
							|  |  |  |     t.notEqual(actual.children[1], expected.children[1]); | 
					
						
							|  |  |  |     t.equal(actual.children[0].bar, expected.children[0].bar); | 
					
						
							|  |  |  |     t.notOk(expected.children[0].foo); | 
					
						
							|  |  |  |     t.notOk(expected.children[1].foo); | 
					
						
							|  |  |  |     t.true(actual.children[0].foo); | 
					
						
							|  |  |  |     t.true(actual.children[1].foo); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | test('getNode', t => { | 
					
						
							|  |  |  |   t.test('should return node', t => { | 
					
						
							|  |  |  |     t.plan(1); | 
					
						
							|  |  |  |     const expected = { name: 'foo' }; | 
					
						
							|  |  |  |     const tree = { children: [{ name: 'notfoo' }, expected ] }; | 
					
						
							|  |  |  |     const actual = getNode(tree, 'foo'); | 
					
						
							|  |  |  |     t.equal(expected, actual); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   t.test('should returned undefined if not found', t => { | 
					
						
							|  |  |  |     t.plan(1); | 
					
						
							|  |  |  |     const tree = { | 
					
						
							|  |  |  |       children: [ { name: 'foo' }, { children: [ { name: 'bar' } ] } ] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const actual = getNode(tree, 'baz'); | 
					
						
							|  |  |  |     t.notOk(actual); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | test('updateSingleNode', t => { | 
					
						
							|  |  |  |   t.test('should update single node', t => { | 
					
						
							|  |  |  |     const expected = { name: 'foo' }; | 
					
						
							|  |  |  |     const untouched = { name: 'notFoo' }; | 
					
						
							|  |  |  |     const actual = updateSingleNode( | 
					
						
							|  |  |  |       { children: [ untouched, expected ] }, | 
					
						
							|  |  |  |       'foo', | 
					
						
							|  |  |  |       node => ({ ...node, tag: true }) | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     t.plan(4); | 
					
						
							|  |  |  |     t.ok(actual.children[1].tag); | 
					
						
							|  |  |  |     t.equal(actual.children[1].name, expected.name); | 
					
						
							|  |  |  |     t.notEqual(actual.children[1], expected); | 
					
						
							|  |  |  |     t.equal(actual.children[0], untouched); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | test('toggleThisPanel', t => { | 
					
						
							|  |  |  |   t.test('should update single node', t => { | 
					
						
							|  |  |  |     const expected = { name: 'foo', isOpen: true }; | 
					
						
							|  |  |  |     const actual = toggleThisPanel( | 
					
						
							|  |  |  |       { children: [ { name: 'foo', isOpen: false }] }, | 
					
						
							|  |  |  |       'foo' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     t.plan(1); | 
					
						
							|  |  |  |     t.deepLooseEqual(actual.children[0], expected); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | test('toggleAllPanels', t => { | 
					
						
							|  |  |  |   t.test('should add `isOpen: true` to every node without children', t => { | 
					
						
							|  |  |  |     const expected = { | 
					
						
							|  |  |  |       isOpen: true, | 
					
						
							|  |  |  |       children: [{ | 
					
						
							|  |  |  |         isOpen: true, | 
					
						
							|  |  |  |         children: [{}, {}] | 
					
						
							|  |  |  |       }] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const actual = expandAllPanels({ children: [{ children: [{}, {}] }] }); | 
					
						
							|  |  |  |     t.plan(1); | 
					
						
							|  |  |  |     t.deepLooseEqual(actual, expected); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   t.test('should add `isOpen: false` to every node without children', t => { | 
					
						
							|  |  |  |     const leaf = {}; | 
					
						
							|  |  |  |     const expected = { | 
					
						
							|  |  |  |       isOpen: false, | 
					
						
							|  |  |  |       children: [{ | 
					
						
							|  |  |  |         isOpen: false, | 
					
						
							|  |  |  |         children: [{}, leaf] | 
					
						
							|  |  |  |       }] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const actual = collapseAllPanels( | 
					
						
							|  |  |  |       { isOpen: true, children: [{ children: [{}, leaf]}]}, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     t.plan(2); | 
					
						
							|  |  |  |     t.deepLooseEqual(actual, expected); | 
					
						
							|  |  |  |     t.equal(actual.children[0].children[1], leaf); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2018-01-09 10:41:44 +03:00
										 |  |  | test('updatePath', t => { | 
					
						
							|  |  |  |   t.test('should call update function for each node in the path', t => { | 
					
						
							|  |  |  |     const expected = { | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name: 'superFoo', | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               name: 'blockBar', | 
					
						
							|  |  |  |               children: [{name: 'challBar'}] | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               name: 'blockFoo', | 
					
						
							|  |  |  |               children: [{name: 'challFoo'}] | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ] | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name: 'superBaz', | 
					
						
							|  |  |  |           isOpen: false, | 
					
						
							|  |  |  |           children: [] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const spy = sinon.spy(t => ({ ...t}) ); | 
					
						
							|  |  |  |     spy.withArgs(expected.children[0]); | 
					
						
							|  |  |  |     spy.withArgs(expected.children[0].children[1]); | 
					
						
							|  |  |  |     spy.withArgs(expected.children[0].children[1].children[0]); | 
					
						
							|  |  |  |     updatePath(expected, 'challFoo', spy); | 
					
						
							|  |  |  |     t.plan(4); | 
					
						
							|  |  |  |     t.equal(spy.callCount, 3); | 
					
						
							|  |  |  |     t.ok(spy.withArgs(expected.children[0]).calledOnce, 'superBlock'); | 
					
						
							|  |  |  |     t.ok(spy.withArgs(expected.children[0].children[1]).calledOnce, 'block'); | 
					
						
							|  |  |  |     t.ok( | 
					
						
							|  |  |  |       spy.withArgs(expected.children[0].children[1].children[0]).calledOnce, | 
					
						
							|  |  |  |       'chall' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | test('openPath', t=> { | 
					
						
							|  |  |  |   t.test('should open all nodes in the path', t => { | 
					
						
							|  |  |  |     const expected = { | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name: 'superFoo', | 
					
						
							|  |  |  |           isOpen: true, | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               name: 'blockBar', | 
					
						
							|  |  |  |               isOpen: false, | 
					
						
							|  |  |  |               children: [] | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               name: 'blockFoo', | 
					
						
							|  |  |  |               isOpen: true, | 
					
						
							|  |  |  |               children: [{ | 
					
						
							|  |  |  |                 name: 'challFoo' | 
					
						
							|  |  |  |               }] | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ] | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name: 'superBar', | 
					
						
							|  |  |  |           isOpen: false, | 
					
						
							|  |  |  |           children: [] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const actual = openPath({ | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name: 'superFoo', | 
					
						
							|  |  |  |           isOpen: false, | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               name: 'blockBar', | 
					
						
							|  |  |  |               isOpen: false, | 
					
						
							|  |  |  |               children: [] | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               name: 'blockFoo', | 
					
						
							|  |  |  |               isOpen: false, | 
					
						
							|  |  |  |               children: [{ | 
					
						
							|  |  |  |                 name: 'challFoo' | 
					
						
							|  |  |  |               }] | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ] | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name: 'superBar', | 
					
						
							|  |  |  |           isOpen: false, | 
					
						
							|  |  |  |           children: [] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     }, 'challFoo'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     t.plan(1); | 
					
						
							|  |  |  |     t.deepLooseEqual(actual, expected); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); |