133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
import { describe, test, expect } from 'vitest';
|
|||
|
|
import type { NodeTree, Node } from '@craftjs/core';
|
||
|
|
import { regenerateTreeIds } from './craft-tree';
|
||
|
|
|
||
|
|
function makeNode(overrides: Partial<Node>): Node {
|
||
|
|
return {
|
||
|
|
id: 'placeholder',
|
||
|
|
data: {
|
||
|
|
props: {},
|
||
|
|
type: 'div',
|
||
|
|
name: 'div',
|
||
|
|
displayName: 'div',
|
||
|
|
isCanvas: false,
|
||
|
|
parent: null,
|
||
|
|
linkedNodes: {},
|
||
|
|
nodes: [],
|
||
|
|
hidden: false,
|
||
|
|
},
|
||
|
|
info: {},
|
||
|
|
events: { selected: false, dragged: false, hovered: false },
|
||
|
|
dom: null,
|
||
|
|
related: {},
|
||
|
|
rules: {
|
||
|
|
canDrag: () => true,
|
||
|
|
canDrop: () => true,
|
||
|
|
canMoveIn: () => true,
|
||
|
|
canMoveOut: () => true,
|
||
|
|
},
|
||
|
|
_hydrationTimestamp: 0,
|
||
|
|
...overrides,
|
||
|
|
} as Node;
|
||
|
|
}
|
||
|
|
|
||
|
|
function makeTree(): NodeTree {
|
||
|
|
// root -> [childA, childB]; root also has a linkedNodes entry -> childB
|
||
|
|
const root = makeNode({
|
||
|
|
id: 'root-1',
|
||
|
|
data: {
|
||
|
|
...makeNode({}).data,
|
||
|
|
parent: null,
|
||
|
|
nodes: ['child-a-1'],
|
||
|
|
linkedNodes: { slot: 'child-b-1' },
|
||
|
|
},
|
||
|
|
});
|
||
|
|
const childA = makeNode({
|
||
|
|
id: 'child-a-1',
|
||
|
|
data: {
|
||
|
|
...makeNode({}).data,
|
||
|
|
parent: 'root-1',
|
||
|
|
nodes: [],
|
||
|
|
linkedNodes: {},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
const childB = makeNode({
|
||
|
|
id: 'child-b-1',
|
||
|
|
data: {
|
||
|
|
...makeNode({}).data,
|
||
|
|
parent: 'root-1',
|
||
|
|
nodes: [],
|
||
|
|
linkedNodes: {},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
return {
|
||
|
|
rootNodeId: 'root-1',
|
||
|
|
nodes: {
|
||
|
|
'root-1': root,
|
||
|
|
'child-a-1': childA,
|
||
|
|
'child-b-1': childB,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('regenerateTreeIds', () => {
|
||
|
|
test('produces an id set fully disjoint from the input', () => {
|
||
|
|
const input = makeTree();
|
||
|
|
const inputIds = Object.keys(input.nodes);
|
||
|
|
const output = regenerateTreeIds(input);
|
||
|
|
const outputIds = Object.keys(output.nodes);
|
||
|
|
|
||
|
|
for (const id of outputIds) {
|
||
|
|
expect(inputIds).not.toContain(id);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('rootNodeId is a key in nodes and equals that node id', () => {
|
||
|
|
const output = regenerateTreeIds(makeTree());
|
||
|
|
expect(output.nodes[output.rootNodeId]).toBeDefined();
|
||
|
|
expect(output.nodes[output.rootNodeId].id).toBe(output.rootNodeId);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('every child data.parent points to a NEW id that exists in the output', () => {
|
||
|
|
const output = regenerateTreeIds(makeTree());
|
||
|
|
for (const id of Object.keys(output.nodes)) {
|
||
|
|
const node = output.nodes[id];
|
||
|
|
if (node.data.parent !== null) {
|
||
|
|
expect(output.nodes[node.data.parent]).toBeDefined();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// specifically the two children under the (new) root
|
||
|
|
const newRoot = output.nodes[output.rootNodeId];
|
||
|
|
expect(newRoot.data.nodes.length).toBe(1);
|
||
|
|
const newChildAId = newRoot.data.nodes[0];
|
||
|
|
expect(output.nodes[newChildAId].data.parent).toBe(output.rootNodeId);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('linkedNodes values are remapped to existing new ids', () => {
|
||
|
|
const output = regenerateTreeIds(makeTree());
|
||
|
|
const newRoot = output.nodes[output.rootNodeId];
|
||
|
|
const linkedId = newRoot.data.linkedNodes.slot;
|
||
|
|
expect(linkedId).toBeDefined();
|
||
|
|
expect(output.nodes[linkedId]).toBeDefined();
|
||
|
|
// linked node's parent should still reference the new root
|
||
|
|
expect(output.nodes[linkedId].data.parent).toBe(output.rootNodeId);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('structure/count is preserved', () => {
|
||
|
|
const input = makeTree();
|
||
|
|
const output = regenerateTreeIds(input);
|
||
|
|
expect(Object.keys(output.nodes).length).toBe(Object.keys(input.nodes).length);
|
||
|
|
expect(output.nodes[output.rootNodeId].data.nodes.length).toBe(
|
||
|
|
input.nodes[input.rootNodeId].data.nodes.length
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('does not mutate the input tree', () => {
|
||
|
|
const input = makeTree();
|
||
|
|
const snapshot = JSON.parse(JSON.stringify(input));
|
||
|
|
regenerateTreeIds(input);
|
||
|
|
expect(JSON.parse(JSON.stringify(input))).toEqual(snapshot);
|
||
|
|
});
|
||
|
|
});
|