Files
freeCodeCamp/common/app/Map/redux/utils.test.js
Vasily Belolapotkov decb6eb936 fix(map): Expand map when challenge opened
changed fetchMapUi epic to add extra param - initialNode
added util method to open path in the map by node name
changed action handler for fetchMapUi.complete to open initialNode
changed map component to set scroll on component mount and update
added attribute to challenge component to find challenge node by name
extracted createEventMetaCreator into separate file to break circular
dependencies

Closes #16248
2018-03-04 20:43:10 +03:00

320 lines
7.9 KiB
JavaScript

import test from 'tape';
import sinon from 'sinon';
import {
getNode,
createMapUi,
traverseMapUi,
updateSingleNode,
toggleThisPanel,
expandAllPanels,
collapseAllPanels,
updatePath,
openPath
} 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);
});
});
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);
});
});