Skip to content

Commit 47781a2

Browse files
JoviDeCroockjviide
authored andcommitted
Add displacement edge-case tests
Mirrors the edge-case coverage added to the v10.x displacement heuristic (#5172), where the minimal-move pass produces identical operation logs for every case: - a far swap moves only the two swapped children - displacing more than half the list moves the shorter suffix - displacement combined with an appended or removed child - three consecutive displacements to catch state accumulation issues - correctness of raw text siblings around displaced keyed children
1 parent c29a8ab commit 47781a2

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

test/browser/keys.test.jsx

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,153 @@ describe('keys', () => {
401401
]);
402402
});
403403

404+
it('should not displace when the suffix after the match is shorter than the jump', () => {
405+
const values = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
406+
407+
render(<List values={values} />, scratch);
408+
expect(scratch.textContent).to.equal('abcdefghij');
409+
410+
// Swap two far apart children; only the two swapped children are out of
411+
// order, so only those two may move.
412+
[values[1], values[8]] = [values[8], values[1]];
413+
clearLog();
414+
415+
render(<List values={values} />, scratch);
416+
expect(scratch.textContent).to.equal('aicdefghbj');
417+
expect(getLog()).to.deep.equal([
418+
'<ol>abcdefghij.insertBefore(<li>i, <li>b)',
419+
'<ol>aibcdefghj.insertBefore(<li>b, <li>j)'
420+
]);
421+
});
422+
423+
it('should move the shorter suffix when more than half the list is displaced', () => {
424+
const values = ['a', 'b', 'c', 'd', 'e', 'f'];
425+
426+
render(<List values={values} />, scratch);
427+
expect(scratch.textContent).to.equal('abcdef');
428+
429+
// Displacing 4 of 6 children: moving the two-child suffix is the
430+
// minimal set of moves.
431+
values.push(...values.splice(0, 4));
432+
clearLog();
433+
434+
render(<List values={values} />, scratch);
435+
expect(scratch.textContent).to.equal('efabcd');
436+
expect(getLog()).to.deep.equal([
437+
'<ol>abcdef.insertBefore(<li>e, <li>a)',
438+
'<ol>eabcdf.insertBefore(<li>f, <li>a)'
439+
]);
440+
});
441+
442+
it('should displace multiple keyed children to the end while the list grows', () => {
443+
const values = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
444+
445+
render(<List values={values} />, scratch);
446+
expect(scratch.textContent).to.equal('abcdefghij');
447+
448+
values.push(...values.splice(0, 3));
449+
values.push('k');
450+
clearLog();
451+
452+
render(<List values={values} />, scratch);
453+
expect(scratch.textContent).to.equal('defghijabck');
454+
expect(getLog()).to.deep.equal([
455+
'<ol>abcdefghij.appendChild(<li>a)',
456+
'<ol>bcdefghija.appendChild(<li>b)',
457+
'<ol>cdefghijab.appendChild(<li>c)',
458+
'<li>.appendChild(#text)',
459+
'<ol>defghijabc.appendChild(<li>k)'
460+
]);
461+
});
462+
463+
it('should displace multiple keyed children to the end while another is removed', () => {
464+
const values = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
465+
466+
render(<List values={values} />, scratch);
467+
expect(scratch.textContent).to.equal('abcdefghij');
468+
469+
values.push(...values.splice(0, 3));
470+
values.splice(values.indexOf('j'), 1);
471+
clearLog();
472+
473+
render(<List values={values} />, scratch);
474+
expect(scratch.textContent).to.equal('defghiabc');
475+
expect(getLog()).to.deep.equal([
476+
'<li>j.remove()',
477+
'<ol>abcdefghi.appendChild(<li>a)',
478+
'<ol>bcdefghia.appendChild(<li>b)',
479+
'<ol>cdefghiab.appendChild(<li>c)'
480+
]);
481+
});
482+
483+
it('should displace keyed children to the end repeatedly', () => {
484+
const values = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
485+
const expectedDisplaceLogs = [
486+
[
487+
'<ol>abcdefghij.appendChild(<li>a)',
488+
'<ol>bcdefghija.appendChild(<li>b)',
489+
'<ol>cdefghijab.appendChild(<li>c)'
490+
],
491+
[
492+
'<ol>defghijabc.appendChild(<li>d)',
493+
'<ol>efghijabcd.appendChild(<li>e)',
494+
'<ol>fghijabcde.appendChild(<li>f)'
495+
],
496+
[
497+
'<ol>ghijabcdef.appendChild(<li>g)',
498+
'<ol>hijabcdefg.appendChild(<li>h)',
499+
'<ol>ijabcdefgh.appendChild(<li>i)'
500+
]
501+
];
502+
503+
render(<List values={values} />, scratch);
504+
expect(scratch.textContent).to.equal('abcdefghij');
505+
506+
for (let n = 0; n < 3; n++) {
507+
values.push(...values.splice(0, 3));
508+
clearLog();
509+
510+
render(<List values={values} />, scratch);
511+
expect(scratch.textContent).to.equal(values.join(''));
512+
expect(getLog()).to.deep.equal(expectedDisplaceLogs[n], `round ${n}`);
513+
}
514+
});
515+
516+
it('should keep text siblings correct around displaced keyed children', () => {
517+
const content = condition => (
518+
<ol>
519+
{condition
520+
? [
521+
<li key="a">a</li>,
522+
<li key="b">b</li>,
523+
<li key="c">c</li>,
524+
'mid',
525+
<li key="d">d</li>,
526+
<li key="e">e</li>
527+
]
528+
: [
529+
<li key="c">c</li>,
530+
'mid',
531+
<li key="a">a</li>,
532+
<li key="b">b</li>,
533+
<li key="d">d</li>,
534+
<li key="e">e</li>
535+
]}
536+
</ol>
537+
);
538+
539+
render(content(true), scratch);
540+
expect(scratch.innerHTML).to.equal(
541+
'<ol><li>a</li><li>b</li><li>c</li>mid<li>d</li><li>e</li></ol>'
542+
);
543+
544+
clearLog();
545+
render(content(false), scratch);
546+
expect(scratch.innerHTML).to.equal(
547+
'<ol><li>c</li>mid<li>a</li><li>b</li><li>d</li><li>e</li></ol>'
548+
);
549+
});
550+
404551
it('should move keyed children to the end of the list', () => {
405552
const values = ['a', 'b', 'c', 'd'];
406553

0 commit comments

Comments
 (0)