Skip to content

Commit f0278ea

Browse files
committed
test(ui): cover scrollIntoView for imported Word SDTs (SD-3310)
Load the NDA template fixture (13 real Word content controls) and scroll its first inline field and last block clause into view from off-screen. Guards the imported-docx path; the other specs only used programmatically-created controls. Fixture copied into the suite to avoid coupling to demos/.
1 parent 049ec91 commit f0278ea

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* SD-3310 regression for imported Word-authored content controls.
3+
*
4+
* `nda-template.docx` is the contract-templates demo's fixture: 13 real
5+
* Word SDTs (7 inline smart fields + 6 block clauses). It is copied here
6+
* (not loaded from demos/) so the behavior suite isn't coupled to demo
7+
* paths.
8+
*
9+
* Confirms `ui.contentControls.scrollIntoView` resolves controls imported
10+
* from a real .docx (not just programmatically-created ones) and scrolls
11+
* them into view from an off-screen start.
12+
*/
13+
14+
import path from 'node:path';
15+
import { fileURLToPath } from 'node:url';
16+
import { test, expect } from '../../fixtures/superdoc.js';
17+
18+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
19+
const NDA = path.resolve(__dirname, 'fixtures/nda-template.docx');
20+
21+
test.use({ viewport: { width: 1000, height: 360 } });
22+
23+
type Item = { id: string; kind: string; text?: string };
24+
25+
async function snapshotItems(page: import('@playwright/test').Page): Promise<Item[]> {
26+
return page.evaluate(() => {
27+
const ui = (window as any).__bootSuperDocUI?.();
28+
if (!ui) return [];
29+
return ui.contentControls.getSnapshot().items.map((it: any) => ({ id: it.id, kind: it.kind, text: it.text }));
30+
});
31+
}
32+
33+
async function probeVisible(page: import('@playwright/test').Page, id: string) {
34+
return page.evaluate((sdtId) => {
35+
const el = document.querySelector<HTMLElement>(`[data-sdt-id="${sdtId}"]`);
36+
if (!el) return { painted: false, inViewport: false };
37+
const r = el.getBoundingClientRect();
38+
return { painted: true, inViewport: r.top >= 0 && r.top <= window.innerHeight };
39+
}, id);
40+
}
41+
42+
async function scrollTo(page: import('@playwright/test').Page, id: string): Promise<{ success: boolean }> {
43+
return page.evaluate(async (sdtId) => {
44+
const ui = (window as any).__bootSuperDocUI?.();
45+
if (!ui) return { success: false };
46+
return ui.contentControls.scrollIntoView({ id: sdtId, block: 'center', behavior: 'auto' });
47+
}, id);
48+
}
49+
50+
async function scrollContainerTo(page: import('@playwright/test').Page, edge: 'top' | 'bottom'): Promise<void> {
51+
await page.evaluate((to) => {
52+
let node: HTMLElement | null = document.querySelector<HTMLElement>('.presentation-editor__pages');
53+
let scroller: HTMLElement | null = null;
54+
while (node) {
55+
if (node.scrollHeight > node.clientHeight + 4) {
56+
scroller = node;
57+
break;
58+
}
59+
node = node.parentElement;
60+
}
61+
const target = to === 'top' ? 0 : 1_000_000;
62+
if (scroller) scroller.scrollTop = target;
63+
else window.scrollTo(0, target);
64+
}, edge);
65+
}
66+
67+
test('@behavior SD-3310: scrolls real imported NDA-template controls (first field + last clause) into view', async ({
68+
superdoc,
69+
}) => {
70+
await superdoc.loadDocument(NDA);
71+
await superdoc.waitForStable();
72+
73+
const items = await snapshotItems(superdoc.page);
74+
// Sanity: the fixture's controls are visible to the handle.
75+
expect(items.length).toBeGreaterThanOrEqual(6);
76+
77+
const first = items[0]; // top-most (an inline smart field)
78+
const last = items[items.length - 1]; // bottom-most (a block clause)
79+
expect(first.id).toBeTruthy();
80+
expect(last.id).toBeTruthy();
81+
82+
// Bottom clause: scroll to top so it's off-screen, then scroll it in.
83+
await scrollContainerTo(superdoc.page, 'top');
84+
await superdoc.waitForStable();
85+
expect((await probeVisible(superdoc.page, last.id)).inViewport).toBe(false);
86+
expect((await scrollTo(superdoc.page, last.id)).success).toBe(true);
87+
await superdoc.waitForStable();
88+
expect(await probeVisible(superdoc.page, last.id)).toEqual({ painted: true, inViewport: true });
89+
90+
// Top field: scroll to bottom so it's off-screen, then scroll it in.
91+
await scrollContainerTo(superdoc.page, 'bottom');
92+
await superdoc.waitForStable();
93+
expect((await probeVisible(superdoc.page, first.id)).inViewport).toBe(false);
94+
expect((await scrollTo(superdoc.page, first.id)).success).toBe(true);
95+
await superdoc.waitForStable();
96+
expect(await probeVisible(superdoc.page, first.id)).toEqual({ painted: true, inViewport: true });
97+
});
21.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)