Skip to content

Commit 5ba249c

Browse files
gpardhivvarmacaio-pizzol
authored andcommitted
test: add incremental update tests for between-border cache invalidation
Cover the patch path in renderer.ts where a fragment switches in/out of a between-border group, verifying the DOM node is rebuilt when the betweenBorderMismatch flag flips.
1 parent b615274 commit 5ba249c

1 file changed

Lines changed: 128 additions & 1 deletion

File tree

packages/layout-engine/painters/dom/src/between-borders.test.ts

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
import { describe, expect, it } from 'vitest';
1+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
22
import {
33
applyParagraphBorderStyles,
44
getFragmentParagraphBorders,
55
computeBetweenBorderFlags,
66
type BlockLookup,
77
} from './renderer.js';
8+
import { createDomPainter } from './index.js';
89
import type {
910
ParagraphBorders,
1011
ParagraphBorder,
1112
ParagraphBlock,
1213
ListBlock,
1314
Fragment,
15+
FlowBlock,
16+
Layout,
17+
Measure,
1418
ParaFragment,
1519
ListItemFragment,
1620
ImageFragment,
@@ -523,3 +527,126 @@ describe('computeBetweenBorderFlags', () => {
523527
expect(flags.has(0)).toBe(false);
524528
});
525529
});
530+
531+
// ---------------------------------------------------------------------------
532+
// Incremental update — between-border cache invalidation
533+
// ---------------------------------------------------------------------------
534+
535+
describe('DomPainter between-border incremental update', () => {
536+
let mount: HTMLElement;
537+
538+
beforeEach(() => {
539+
mount = document.createElement('div');
540+
document.body.appendChild(mount);
541+
});
542+
543+
afterEach(() => {
544+
mount.remove();
545+
});
546+
547+
const makeMeasure = (): Measure => ({
548+
kind: 'paragraph',
549+
lines: [
550+
{
551+
fromRun: 0,
552+
fromChar: 0,
553+
toRun: 0,
554+
toChar: 0,
555+
width: 100,
556+
ascent: 14,
557+
descent: 2,
558+
lineHeight: 16,
559+
},
560+
],
561+
totalHeight: 16,
562+
});
563+
564+
const layout: Layout = {
565+
pageSize: { w: 400, h: 500 },
566+
pages: [
567+
{
568+
number: 1,
569+
fragments: [
570+
{ kind: 'para', blockId: 'b1', fromLine: 0, toLine: 1, x: 0, y: 0, width: 100 },
571+
{ kind: 'para', blockId: 'b2', fromLine: 0, toLine: 1, x: 0, y: 16, width: 100 },
572+
],
573+
},
574+
],
575+
};
576+
577+
it('rebuilds fragment when between-border flag switches on via setData', () => {
578+
// Initial: no between borders
579+
const b1: FlowBlock = { kind: 'paragraph', id: 'b1', runs: [] };
580+
const b2: FlowBlock = { kind: 'paragraph', id: 'b2', runs: [] };
581+
582+
const painter = createDomPainter({ blocks: [b1, b2], measures: [makeMeasure(), makeMeasure()] });
583+
painter.paint(layout, mount);
584+
585+
const page = mount.querySelector('[data-page-number="1"]') as HTMLElement;
586+
const fragsBefore = page.querySelectorAll('[data-block-id]');
587+
const frag1Before = fragsBefore[0] as HTMLElement;
588+
expect(frag1Before.dataset.betweenBorder).toBeUndefined();
589+
590+
// Update: add matching between borders to both blocks
591+
const b1Updated: FlowBlock = {
592+
kind: 'paragraph',
593+
id: 'b1',
594+
runs: [],
595+
attrs: { borders: MATCHING_BORDERS },
596+
};
597+
const b2Updated: FlowBlock = {
598+
kind: 'paragraph',
599+
id: 'b2',
600+
runs: [],
601+
attrs: { borders: MATCHING_BORDERS },
602+
};
603+
604+
painter.setData!([b1Updated, b2Updated], [makeMeasure(), makeMeasure()]);
605+
painter.paint(layout, mount);
606+
607+
const fragsAfter = page.querySelectorAll('[data-block-id]');
608+
const frag1After = fragsAfter[0] as HTMLElement;
609+
// Fragment was rebuilt (different DOM node)
610+
expect(frag1After).not.toBe(frag1Before);
611+
// Between border is now active
612+
expect(frag1After.dataset.betweenBorder).toBe('true');
613+
});
614+
615+
it('rebuilds fragment when between-border flag switches off via setData', () => {
616+
// Initial: with matching between borders
617+
const b1: FlowBlock = {
618+
kind: 'paragraph',
619+
id: 'b1',
620+
runs: [],
621+
attrs: { borders: MATCHING_BORDERS },
622+
};
623+
const b2: FlowBlock = {
624+
kind: 'paragraph',
625+
id: 'b2',
626+
runs: [],
627+
attrs: { borders: MATCHING_BORDERS },
628+
};
629+
630+
const painter = createDomPainter({ blocks: [b1, b2], measures: [makeMeasure(), makeMeasure()] });
631+
painter.paint(layout, mount);
632+
633+
const page = mount.querySelector('[data-page-number="1"]') as HTMLElement;
634+
const fragsBefore = page.querySelectorAll('[data-block-id]');
635+
const frag1Before = fragsBefore[0] as HTMLElement;
636+
expect(frag1Before.dataset.betweenBorder).toBe('true');
637+
638+
// Update: remove borders from both blocks
639+
const b1Updated: FlowBlock = { kind: 'paragraph', id: 'b1', runs: [] };
640+
const b2Updated: FlowBlock = { kind: 'paragraph', id: 'b2', runs: [] };
641+
642+
painter.setData!([b1Updated, b2Updated], [makeMeasure(), makeMeasure()]);
643+
painter.paint(layout, mount);
644+
645+
const fragsAfter = page.querySelectorAll('[data-block-id]');
646+
const frag1After = fragsAfter[0] as HTMLElement;
647+
// Fragment was rebuilt (different DOM node)
648+
expect(frag1After).not.toBe(frag1Before);
649+
// Between border is no longer active
650+
expect(frag1After.dataset.betweenBorder).toBeUndefined();
651+
});
652+
});

0 commit comments

Comments
 (0)