1 | import { expect } from '../test';
|
2 | import { TreeQuery } from '.';
|
3 | const create = TreeQuery.create;
|
4 | describe('TreeQuery', () => {
|
5 | describe('create', () => {
|
6 | it('with root (default node type)', () => {
|
7 | const root = { id: 'root' };
|
8 | const query = create(root);
|
9 | expect(query.root).to.equal(root);
|
10 | expect(query.namespace).to.eql('');
|
11 | });
|
12 | it('with root (specific node type)', () => {
|
13 | const root = { id: 'root', count: 0 };
|
14 | const query = create(root);
|
15 | expect(query.root).to.equal(root);
|
16 | expect(query.namespace).to.eql('');
|
17 | });
|
18 | it('with namespace', () => {
|
19 | const test = (namespace, expected) => {
|
20 | const root = { id: 'root' };
|
21 | const query = create({ root, namespace });
|
22 | expect(query.namespace).to.eql(expected);
|
23 | };
|
24 | test('', '');
|
25 | test(' ', '');
|
26 | test('foo', 'foo');
|
27 | test(' foo ', 'foo');
|
28 | });
|
29 | });
|
30 | describe('children (static)', () => {
|
31 | it('no arguments', () => {
|
32 | let count = 0;
|
33 | const res = TreeQuery.children(undefined, () => count++);
|
34 | expect(res).to.eql([]);
|
35 | expect(count).to.eql(1);
|
36 | });
|
37 | it('no childen => empty array (and assigns by default)', () => {
|
38 | const node = { id: 'root' };
|
39 | expect(node.children).to.eql(undefined);
|
40 | const res = TreeQuery.children(node);
|
41 | expect(res).to.eql([]);
|
42 | expect(node.children).to.equal(res);
|
43 | });
|
44 | it('no childen => empty array (and does not assign)', () => {
|
45 | const node = { id: 'root' };
|
46 | expect(node.children).to.eql(undefined);
|
47 | const res1 = TreeQuery.children(node, { assign: false });
|
48 | expect(res1).to.eql([]);
|
49 | expect(node.children).to.equal(undefined);
|
50 | const res2 = TreeQuery.children(node, undefined, { assign: false });
|
51 | expect(res2).to.eql([]);
|
52 | expect(node.children).to.equal(undefined);
|
53 | });
|
54 | it('returns child array (instance)', () => {
|
55 | const node = { id: 'root', children: [{ id: 'child' }] };
|
56 | expect(TreeQuery.children(node)).to.eql([{ id: 'child' }]);
|
57 | });
|
58 | it('runs visitor function', () => {
|
59 | const node = { id: 'root', children: [{ id: 'child-1' }, { id: 'child-2' }] };
|
60 | const visits = [];
|
61 | TreeQuery.children(node, (children) => children.forEach((child) => visits.push(child)));
|
62 | expect(visits.map((c) => c.id)).to.eql(['child-1', 'child-2']);
|
63 | });
|
64 | });
|
65 | describe('hasChild (static)', () => {
|
66 | const root = { id: 'A', children: [{ id: 'B' }, { id: 'C' }] };
|
67 | it('does have child', () => {
|
68 | expect(TreeQuery.hasChild(root, 'B')).to.eql(true);
|
69 | expect(TreeQuery.hasChild(root, 'C')).to.eql(true);
|
70 | expect(TreeQuery.hasChild(root, { id: 'C' })).to.eql(true);
|
71 | });
|
72 | it('does not have child', () => {
|
73 | expect(TreeQuery.hasChild(root, 'A')).to.eql(false);
|
74 | expect(TreeQuery.hasChild(root, 'NO_MATCH')).to.eql(false);
|
75 | expect(TreeQuery.hasChild(root, undefined)).to.eql(false);
|
76 | expect(TreeQuery.hasChild(undefined, undefined)).to.eql(false);
|
77 | });
|
78 | });
|
79 | describe('walkDown', () => {
|
80 | it('walkDown from root', () => {
|
81 | const tree = {
|
82 | id: 'root',
|
83 | children: [{ id: 'child-1' }, { id: 'child-2' }],
|
84 | };
|
85 | const query = create(tree);
|
86 | const items = [];
|
87 | query.walkDown((e) => items.push(e));
|
88 | expect(items.length).to.eql(3);
|
89 | expect(items[0].node).to.equal(tree);
|
90 | expect(items[1].node).to.equal(tree.children && tree.children[0]);
|
91 | expect(items[2].node).to.equal(tree.children && tree.children[1]);
|
92 | expect(items.every((m) => m.namespace === '')).to.eql(true);
|
93 | });
|
94 | it('walkDown from root (with namespace)', () => {
|
95 | const tree = {
|
96 | id: 'ns:root',
|
97 | children: [{ id: 'ns:child-1' }, { id: 'ns:child-2' }],
|
98 | };
|
99 | const query = create(tree);
|
100 | const items = [];
|
101 | query.walkDown((e) => items.push(e));
|
102 | expect(items.length).to.eql(3);
|
103 | expect(items.every((m) => m.namespace === 'ns')).to.eql(true);
|
104 | expect(items[0].key).to.equal('root');
|
105 | expect(items[0].id).to.equal('ns:root');
|
106 | expect(items[0].node.id).to.equal('ns:root');
|
107 | expect(items[1].key).to.equal('child-1');
|
108 | expect(items[1].id).to.equal('ns:child-1');
|
109 | expect(items[1].node.id).to.equal('ns:child-1');
|
110 | expect(items[2].key).to.equal('child-2');
|
111 | expect(items[2].id).to.equal('ns:child-2');
|
112 | expect(items[2].node.id).to.equal('ns:child-2');
|
113 | });
|
114 | it('walkDown: skip (children)', () => {
|
115 | const tree = {
|
116 | id: 'root',
|
117 | children: [
|
118 | { id: 'child-1', children: [{ id: 'child-1.1' }, { id: 'child-1.1' }] },
|
119 | { id: 'child-2' },
|
120 | ],
|
121 | };
|
122 | const query = create(tree);
|
123 | const nodes = [];
|
124 | query.walkDown((e) => {
|
125 | nodes.push(e.node);
|
126 | if (e.node.id === 'child-1') {
|
127 | e.skip();
|
128 | }
|
129 | });
|
130 | expect(nodes.length).to.eql(3);
|
131 | expect(nodes[0].id).to.eql('root');
|
132 | expect(nodes[1].id).to.eql('child-1');
|
133 | expect(nodes[2].id).to.eql('child-2');
|
134 | });
|
135 | it('walkDown: stop mid-way', () => {
|
136 | const root = {
|
137 | id: 'root',
|
138 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'child-2.1' }] }],
|
139 | };
|
140 | const state = create(root);
|
141 | const walked = [];
|
142 | state.walkDown((e) => {
|
143 | walked.push(e);
|
144 | if (e.id === 'child-1') {
|
145 | e.stop();
|
146 | }
|
147 | });
|
148 | expect(walked.length).to.eql(2);
|
149 | expect(walked[0].id).to.eql('root');
|
150 | expect(walked[1].id).to.eql('child-1');
|
151 | });
|
152 | it('walkDown: passes level/parent to visitor', () => {
|
153 | const grandchild = { id: 'grandchild' };
|
154 | const child = { id: 'child', children: [grandchild] };
|
155 | const root = { id: 'root', children: [child] };
|
156 | const query = create(root);
|
157 | const items = [];
|
158 | query.walkDown((e) => items.push(e));
|
159 | expect(items.length).to.eql(3);
|
160 | expect(items[0].level).to.eql(0);
|
161 | expect(items[1].level).to.eql(1);
|
162 | expect(items[2].level).to.eql(2);
|
163 | expect(items[0].parent).to.eql(undefined);
|
164 | expect(items[1].parent).to.eql(root);
|
165 | expect(items[2].parent).to.eql(child);
|
166 | });
|
167 | it('walkDown: passes index to visitor (sibling position)', () => {
|
168 | const root = {
|
169 | id: 'root',
|
170 | children: [{ id: 'child-1' }, { id: 'child-2' }],
|
171 | };
|
172 | const query = create(root);
|
173 | const items = [];
|
174 | query.walkDown((e) => items.push(e));
|
175 | expect(items[0].index).to.eql(-1);
|
176 | expect(items[1].index).to.eql(0);
|
177 | expect(items[2].index).to.eql(1);
|
178 | });
|
179 | it('walkDown: does not walk down into child namespace', () => {
|
180 | var _a;
|
181 | const root = {
|
182 | id: 'ns1:root',
|
183 | children: [{ id: 'ns1:child-1' }, { id: 'ns1:child-2', children: [{ id: 'ns2:foo' }] }],
|
184 | };
|
185 | const query = create({ root, namespace: 'ns1' });
|
186 | expect(query.namespace).to.eql('ns1');
|
187 | expect((_a = create(root).findById('ns2:foo')) === null || _a === void 0 ? void 0 : _a.id).to.eql('ns2:foo');
|
188 | const walked = [];
|
189 | query.walkDown((e) => walked.push(e));
|
190 | const ids = walked.map((e) => e.id);
|
191 | expect(ids.length).to.eql(3);
|
192 | expect(ids.includes('ns2:foo')).to.eql(false);
|
193 | });
|
194 | });
|
195 | describe('walkUp', () => {
|
196 | it('walkUp: to root', () => {
|
197 | const tree = {
|
198 | id: 'root',
|
199 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'grandchild-1' }] }],
|
200 | };
|
201 | const query = create(tree);
|
202 | const start = query.findById('grandchild-1');
|
203 | const items = [];
|
204 | query.walkUp(start, (e) => items.push(e));
|
205 | expect(items.length).to.eql(3);
|
206 | expect(items.map((e) => e.node.id)).to.eql(['grandchild-1', 'child-2', 'root']);
|
207 | expect(items.every((m) => m.namespace === '')).to.eql(true);
|
208 | expect(items[0].parent && items[0].parent.id).to.eql('child-2');
|
209 | expect(items[1].parent && items[1].parent.id).to.eql('root');
|
210 | expect(items[2].parent && items[2].parent.id).to.eql(undefined);
|
211 | expect(items[0].index).to.eql(0);
|
212 | expect(items[1].index).to.eql(1);
|
213 | expect(items[2].index).to.eql(-1);
|
214 | });
|
215 | it('walkUp: to root (with namespace)', () => {
|
216 | const tree = {
|
217 | id: 'ns:root',
|
218 | children: [
|
219 | { id: 'ns:child-1' },
|
220 | { id: 'ns:child-2', children: [{ id: 'ns:grandchild-1' }] },
|
221 | ],
|
222 | };
|
223 | const query = create(tree);
|
224 | const start = query.findById('ns:grandchild-1');
|
225 | const items = [];
|
226 | query.walkUp(start, (e) => items.push(e));
|
227 | expect(items.length).to.eql(3);
|
228 | expect(items.every((m) => m.namespace === 'ns')).to.eql(true);
|
229 | expect(items.map((e) => e.key)).to.eql(['grandchild-1', 'child-2', 'root']);
|
230 | expect(items.map((e) => e.node.id)).to.eql(['ns:grandchild-1', 'ns:child-2', 'ns:root']);
|
231 | });
|
232 | it('walkUp: stop mid-way', () => {
|
233 | const tree = {
|
234 | id: 'root',
|
235 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'grandchild-1' }] }],
|
236 | };
|
237 | const query = create(tree);
|
238 | const start = query.findById('grandchild-1');
|
239 | const list = [];
|
240 | query.walkUp(start, (e) => {
|
241 | list.push(e);
|
242 | if (e.node.id === 'child-2') {
|
243 | e.stop();
|
244 | }
|
245 | });
|
246 | expect(list.length).to.eql(2);
|
247 | expect(list.map((e) => e.id)).to.eql(['grandchild-1', 'child-2']);
|
248 | });
|
249 | it('walkUp: startAt not found', () => {
|
250 | const tree = {
|
251 | id: 'root',
|
252 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'grandchild-1' }] }],
|
253 | };
|
254 | const query = create(tree);
|
255 | const test = (startAt) => {
|
256 | const walked = [];
|
257 | query.walkUp(startAt, (e) => walked.push(e));
|
258 | expect(walked).to.eql([]);
|
259 | };
|
260 | test();
|
261 | test('404');
|
262 | test({ id: '404' });
|
263 | });
|
264 | it('walkUp: passes level (from start of ascent)', () => {
|
265 | const tree = {
|
266 | id: 'root',
|
267 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'grandchild-1' }] }],
|
268 | };
|
269 | const query = create(tree);
|
270 | const walked = [];
|
271 | query.walkUp('grandchild-1', (e) => walked.push(e));
|
272 | expect(walked.map((e) => e.level)).to.eql([0, 1, 2]);
|
273 | });
|
274 | it('walkUp: does not walk up into parent namespace', () => {
|
275 | const tree = {
|
276 | id: 'ns1:root',
|
277 | children: [
|
278 | { id: 'ns1:child-1' },
|
279 | { id: 'ns2:child-2', children: [{ id: 'ns2:child-2.1' }] },
|
280 | ],
|
281 | };
|
282 | const root = create(tree).findById('ns2:child-2');
|
283 | const child = create(tree).findById('ns2:child-2.1');
|
284 | expect(child).to.exist;
|
285 | const query = create({ root, namespace: 'ns2' });
|
286 | const test = (startAt) => {
|
287 | const walked = [];
|
288 | query.walkUp(startAt, (e) => walked.push(e));
|
289 | expect(walked.map((e) => e.key)).to.eql(['child-2.1', 'child-2']);
|
290 | };
|
291 | test(child);
|
292 | test(child === null || child === void 0 ? void 0 : child.id);
|
293 | test('child-2.1');
|
294 | });
|
295 | });
|
296 | describe('find', () => {
|
297 | it('no namespace', () => {
|
298 | const tree = {
|
299 | id: 'root',
|
300 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'child-3' }] }],
|
301 | };
|
302 | const query = create(tree);
|
303 | const res1 = query.find((e) => e.node.id === 'child-3');
|
304 | const res2 = query.find((e) => e.node.id === 'NO_EXIT');
|
305 | expect(res1).to.eql({ id: 'child-3' });
|
306 | expect(res2).to.eql(undefined);
|
307 | });
|
308 | it('with namespace', () => {
|
309 | const tree = {
|
310 | id: 'ns:root',
|
311 | children: [{ id: 'child-1' }, { id: 'ns:child-2', children: [{ id: 'ns:child-3' }] }],
|
312 | };
|
313 | const query = create(tree);
|
314 | const res1a = query.find((e) => e.namespace === 'ns' && e.key === 'child-3');
|
315 | const res1b = query.find((e) => e.id === 'ns:child-3');
|
316 | const res2 = query.find((e) => e.namespace === 'foo' && e.key === 'child-3');
|
317 | expect(res1a).to.eql({ id: 'ns:child-3' });
|
318 | expect(res1b).to.eql({ id: 'ns:child-3' });
|
319 | expect(res2).to.eql(undefined);
|
320 | });
|
321 | it('root with namespace', () => {
|
322 | const tree = {
|
323 | id: 'root',
|
324 | children: [
|
325 | { id: 'child-1' },
|
326 | { id: 'ns:child-2', children: [{ id: 'ns:child-2.1' }, { id: 'ns:child-2.1' }] },
|
327 | ],
|
328 | };
|
329 | const query = create(tree);
|
330 | const res = query.find((e) => e.namespace === 'ns');
|
331 | expect(res === null || res === void 0 ? void 0 : res.id).to.eql('ns:child-2');
|
332 | });
|
333 | });
|
334 | describe('findById', () => {
|
335 | it('no namespace', () => {
|
336 | const tree = {
|
337 | id: 'root',
|
338 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'child-3' }] }],
|
339 | };
|
340 | const query = create(tree);
|
341 | const res1 = query.findById('child-3');
|
342 | const res2 = query.findById('NO_EXIST');
|
343 | const res3 = query.findById(undefined);
|
344 | const res4 = query.findById({ id: 'child-2' });
|
345 | expect(res1).to.eql({ id: 'child-3' });
|
346 | expect(res2).to.eql(undefined);
|
347 | expect(res3).to.eql(undefined);
|
348 | expect(res4).to.eql({ id: 'child-2', children: [{ id: 'child-3' }] });
|
349 | });
|
350 | it('within namespace', () => {
|
351 | var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
352 | const root = {
|
353 | id: 'ns1:root',
|
354 | children: [{ id: 'ns1:child-1' }, { id: 'ns2:child-2' }, { id: 'foo' }],
|
355 | };
|
356 | const query = create({ root });
|
357 | const ns1 = create({ root, namespace: 'ns1' });
|
358 | expect((_a = query.findById('root')) === null || _a === void 0 ? void 0 : _a.id).to.eql('ns1:root');
|
359 | expect(query.findById('foo:root')).to.eql(undefined);
|
360 | expect((_b = query.findById('ns1:root')) === null || _b === void 0 ? void 0 : _b.id).to.eql('ns1:root');
|
361 | expect((_c = query.findById('ns1:child-1')) === null || _c === void 0 ? void 0 : _c.id).to.eql('ns1:child-1');
|
362 | expect((_d = query.findById('ns2:child-2')) === null || _d === void 0 ? void 0 : _d.id).to.eql('ns2:child-2');
|
363 | expect((_e = query.findById('foo')) === null || _e === void 0 ? void 0 : _e.id).to.eql('foo');
|
364 | expect((_f = ns1.findById('root')) === null || _f === void 0 ? void 0 : _f.id).to.eql('ns1:root');
|
365 | expect((_g = ns1.findById('ns1:root')) === null || _g === void 0 ? void 0 : _g.id).to.eql('ns1:root');
|
366 | expect(ns1.findById('foo:root')).to.eql(undefined);
|
367 | expect(ns1.findById('404')).to.eql(undefined);
|
368 | expect(ns1.findById('ns1:404')).to.eql(undefined);
|
369 | expect((_h = ns1.findById('child-1')) === null || _h === void 0 ? void 0 : _h.id).to.eql('ns1:child-1');
|
370 | expect((_j = ns1.findById('ns1:child-1')) === null || _j === void 0 ? void 0 : _j.id).to.eql('ns1:child-1');
|
371 | expect(ns1.findById('child-2')).to.eql(undefined);
|
372 | expect(ns1.findById('ns2:child-2')).to.eql(undefined);
|
373 | expect(ns1.findById('foo')).to.eql(undefined);
|
374 | });
|
375 | });
|
376 | describe('parent', () => {
|
377 | it('has a parent', () => {
|
378 | const grandchild = { id: 'grandchild' };
|
379 | const child = { id: 'child', children: [grandchild] };
|
380 | const root = { id: 'root', children: [child] };
|
381 | const query = create(root);
|
382 | expect(query.parent(child)).to.equal(root);
|
383 | expect(query.parent(grandchild)).to.equal(child);
|
384 | });
|
385 | it('has no parent', () => {
|
386 | const grandchild = { id: 'grandchild' };
|
387 | const child = { id: 'child', children: [grandchild] };
|
388 | const root = { id: 'root', children: [] };
|
389 | const query = create(root);
|
390 | expect(query.parent(child)).to.equal(undefined);
|
391 | expect(query.parent(grandchild)).to.equal(undefined);
|
392 | });
|
393 | });
|
394 | describe('ancestor', () => {
|
395 | const tree = {
|
396 | id: 'root',
|
397 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'child-3' }] }],
|
398 | };
|
399 | it('matches self (first)', () => {
|
400 | const query = create(tree);
|
401 | const node = query.findById('child-3');
|
402 | const res = query.ancestor(node, (e) => e.node.id === 'child-3');
|
403 | expect(res && res.id).to.eql('child-3');
|
404 | });
|
405 | it('finds matching ancestor', () => {
|
406 | const query = create(tree);
|
407 | const node = query.findById('child-3');
|
408 | const res1 = query.ancestor(node, (e) => e.node.id === 'child-2');
|
409 | const res2 = query.ancestor(node, (e) => e.node.id === 'root');
|
410 | expect(res1 && res1.id).to.eql('child-2');
|
411 | expect(res2 && res2.id).to.eql('root');
|
412 | });
|
413 | it('no match', () => {
|
414 | const query = create(tree);
|
415 | const node = query.findById('root');
|
416 | const res = query.ancestor(node, (e) => e.node.id === 'child-1');
|
417 | expect(res).to.eql(undefined);
|
418 | });
|
419 | });
|
420 | describe('depth', () => {
|
421 | const root = {
|
422 | id: 'A',
|
423 | children: [{ id: 'B' }, { id: 'C', children: [{ id: 'D' }] }],
|
424 | };
|
425 | it('retrieves depth', () => {
|
426 | const query = create(root);
|
427 | expect(query.depth('A')).to.eql(0);
|
428 | expect(query.depth({ id: 'A' })).to.eql(0);
|
429 | expect(query.depth('B')).to.eql(1);
|
430 | expect(query.depth('C')).to.eql(1);
|
431 | expect(query.depth('D')).to.eql(2);
|
432 | expect(query.depth({ id: 'D' })).to.eql(2);
|
433 | });
|
434 | it('-1', () => {
|
435 | const query = create({ id: 'root' });
|
436 | expect(query.depth('C')).to.eql(-1);
|
437 | expect(query.depth(undefined)).to.eql(-1);
|
438 | expect(query.depth('NO_EXIST')).to.eql(-1);
|
439 | expect(query.depth({ id: 'NO_EXIST' })).to.eql(-1);
|
440 | });
|
441 | });
|
442 | describe('exists', () => {
|
443 | const tree = {
|
444 | id: 'root',
|
445 | children: [{ id: 'child-1' }, { id: 'child-2', children: [{ id: 'child-3' }] }],
|
446 | };
|
447 | it('exists', () => {
|
448 | const query = create(tree);
|
449 | expect(query.exists('root')).to.eql(true);
|
450 | expect(query.exists('child-3')).to.eql(true);
|
451 | expect(query.exists({ id: 'root' })).to.eql(true);
|
452 | expect(query.exists({ id: 'child-3' })).to.eql(true);
|
453 | expect(query.exists((e) => e.id === 'child-3')).to.eql(true);
|
454 | });
|
455 | it('does not exist', () => {
|
456 | const query = create(tree);
|
457 | expect(query.exists('404')).to.eql(false);
|
458 | expect(query.exists({ id: '404' })).to.eql(false);
|
459 | expect(query.exists((e) => e.id === '404')).to.eql(false);
|
460 | });
|
461 | });
|
462 | });
|