test(e2e): wait for overlay CSS transition to settle
Poll pane document-block computed styles until two frames match. Pure requires transparent background and box-shadow none; manual/all require a real overlay mark without hard-coded theme colors. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -436,11 +436,54 @@ function overlayPaneKey(samples: OverlaySample[]): string {
|
||||
.join(';');
|
||||
}
|
||||
|
||||
function overlayPaneHasMark(samples: OverlaySample[]): boolean {
|
||||
return samples.some((s) => s.overlayKind !== '' || (s.boxShadow !== '' && s.boxShadow !== 'none'));
|
||||
function overlaySampleIsTransparent(s: OverlaySample): boolean {
|
||||
const bg = s.backgroundColor.replace(/\s+/g, '');
|
||||
return (bg === 'transparent' || bg === 'rgba(0,0,0,0)') && (s.boxShadow === '' || s.boxShadow === 'none');
|
||||
}
|
||||
|
||||
/** Sample every real pane block. Overlay styles live on the document-block wrapper itself. */
|
||||
function overlayPaneHasMark(samples: OverlaySample[]): boolean {
|
||||
return samples.some((s) => s.overlayKind !== '' || !overlaySampleIsTransparent(s));
|
||||
}
|
||||
|
||||
async function readOverlaySamples(page: Page): Promise<OverlaySample[]> {
|
||||
return page.getByTestId('active-document-pane').getByTestId('document-block').evaluateAll((els) =>
|
||||
els.map((node) => {
|
||||
const el = node as HTMLElement;
|
||||
const s = window.getComputedStyle(el);
|
||||
return {
|
||||
backgroundColor: s.backgroundColor,
|
||||
boxShadow: s.boxShadow,
|
||||
className: el.className || '',
|
||||
overlayKind: el.getAttribute('data-overlay-kind') || '',
|
||||
blockId: el.getAttribute('data-block-id') || '',
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/** Poll computed styles until two identical frames; do not sample the 200ms transition mid-frame. */
|
||||
async function waitForOverlayStylesSettled(page: Page, mode: 'pure' | 'manual' | 'all') {
|
||||
let previous = '';
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const samples = await readOverlaySamples(page);
|
||||
if (samples.length === 0) return 'empty';
|
||||
const key = overlayPaneKey(samples);
|
||||
const stable = previous !== '' && previous === key;
|
||||
previous = key;
|
||||
if (mode === 'pure') {
|
||||
return stable && !overlayPaneHasMark(samples) ? 'settled' : 'pending';
|
||||
}
|
||||
const marked = samples.some((s) => s.overlayKind) && overlayPaneHasMark(samples);
|
||||
return stable && marked ? 'settled' : 'pending';
|
||||
},
|
||||
{ timeout: 15_000 },
|
||||
)
|
||||
.toBe('settled');
|
||||
}
|
||||
|
||||
/** Sample every real pane block after overlay fetch + CSS transition settle. */
|
||||
async function captureOverlayFingerprint(page: Page, mode: 'pure' | 'manual' | 'all') {
|
||||
const pane = page.getByTestId('active-document-pane');
|
||||
const modeSelect = pane.locator('select[aria-label="变更显示模式"]');
|
||||
@@ -463,20 +506,9 @@ async function captureOverlayFingerprint(page: Page, mode: 'pure' | 'manual' | '
|
||||
} else {
|
||||
await expect(marked.first()).toBeVisible({ timeout: 15_000 });
|
||||
}
|
||||
await waitForOverlayStylesSettled(page, mode);
|
||||
|
||||
const samples = await blocks.evaluateAll((els) =>
|
||||
els.map((node) => {
|
||||
const el = node as HTMLElement;
|
||||
const s = window.getComputedStyle(el);
|
||||
return {
|
||||
backgroundColor: s.backgroundColor,
|
||||
boxShadow: s.boxShadow,
|
||||
className: el.className || '',
|
||||
overlayKind: el.getAttribute('data-overlay-kind') || '',
|
||||
blockId: el.getAttribute('data-block-id') || '',
|
||||
};
|
||||
}),
|
||||
);
|
||||
const samples = await readOverlaySamples(page);
|
||||
if (samples.length === 0) {
|
||||
fail(`UI overlay mode=${mode}: no document-block to sample computed style`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user