Skip to content

Commit 81d4b43

Browse files
cherry-pick(release-1.12): account for last child node removal (#7335)
PR #7332 SHA 02538fb Co-authored-by: Pavel Feldman <[email protected]>
1 parent c25e00b commit 81d4b43

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

src/server/snapshot/snapshotterInjected.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
4444
// Symbols for our own info on Nodes/StyleSheets.
4545
const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
4646
const kCachedData = Symbol('__playwright_snapshot_cache_');
47+
const kEndOfList = Symbol('__playwright_end_of_list_');
4748
type CachedData = {
4849
cached?: any[], // Cached values to determine whether the snapshot will be the same.
4950
ref?: [number, number], // Previous snapshotNumber and nodeIndex.
@@ -355,6 +356,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
355356
}
356357
for (let child = node.firstChild; child; child = child.nextSibling)
357358
visitChild(child);
359+
expectValue(kEndOfList);
358360
let documentOrShadowRoot = null;
359361
if (node.ownerDocument!.documentElement === node)
360362
documentOrShadowRoot = node.ownerDocument;
@@ -363,20 +365,19 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
363365
if (documentOrShadowRoot) {
364366
for (const sheet of (documentOrShadowRoot as any).adoptedStyleSheets || [])
365367
visitChildStyleSheet(sheet);
368+
expectValue(kEndOfList);
366369
}
367370
}
368371

369372
// Process iframe src attribute before bailing out since it depends on a symbol, not the DOM.
370373
if (nodeName === 'IFRAME' || nodeName === 'FRAME') {
371374
const element = node as Element;
372-
for (let i = 0; i < element.attributes.length; i++) {
373-
const frameId = (element as any)[kSnapshotFrameId];
374-
const name = 'src';
375-
const value = frameId ? `/snapshot/${frameId}` : '';
376-
expectValue(name);
377-
expectValue(value);
378-
attrs[name] = value;
379-
}
375+
const frameId = (element as any)[kSnapshotFrameId];
376+
const name = 'src';
377+
const value = frameId ? `/snapshot/${frameId}` : '';
378+
expectValue(name);
379+
expectValue(value);
380+
attrs[name] = value;
380381
}
381382

382383
// We can skip attributes comparison because nothing else has changed,
@@ -409,6 +410,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
409410
expectValue(value);
410411
attrs[name] = value;
411412
}
413+
expectValue(kEndOfList);
412414
}
413415

414416
if (result.length === 2 && !Object.keys(attrs).length)

tests/snapshotter.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,26 @@ it.describe('snapshots', () => {
7575
expect(distillSnapshot(snapshot2)).toBe('<style>button { color: blue; }</style><BUTTON>Hello</BUTTON>');
7676
});
7777

78+
it('should respect node removal', async ({ page, toImpl, snapshotter }) => {
79+
page.on('console', console.log);
80+
await page.setContent('<div><button id="button1"></button><button id="button2"></button></div>');
81+
const snapshot1 = await snapshotter.captureSnapshot(toImpl(page), 'snapshot1');
82+
expect(distillSnapshot(snapshot1)).toBe('<DIV><BUTTON id=\"button1\"></BUTTON><BUTTON id=\"button2\"></BUTTON></DIV>');
83+
await page.evaluate(() => document.getElementById('button2').remove());
84+
const snapshot2 = await snapshotter.captureSnapshot(toImpl(page), 'snapshot2');
85+
expect(distillSnapshot(snapshot2)).toBe('<DIV><BUTTON id=\"button1\"></BUTTON></DIV>');
86+
});
87+
88+
it('should respect attr removal', async ({ page, toImpl, snapshotter }) => {
89+
page.on('console', console.log);
90+
await page.setContent('<div id="div" attr1="1" attr2="2"></div>');
91+
const snapshot1 = await snapshotter.captureSnapshot(toImpl(page), 'snapshot1');
92+
expect(distillSnapshot(snapshot1)).toBe('<DIV id=\"div\" attr1=\"1\" attr2=\"2\"></DIV>');
93+
await page.evaluate(() => document.getElementById('div').removeAttribute('attr2'));
94+
const snapshot2 = await snapshotter.captureSnapshot(toImpl(page), 'snapshot2');
95+
expect(distillSnapshot(snapshot2)).toBe('<DIV id=\"div\" attr1=\"1\"></DIV>');
96+
});
97+
7898
it('should have a custom doctype', async ({page, server, toImpl, snapshotter}) => {
7999
await page.goto(server.EMPTY_PAGE);
80100
await page.setContent('<!DOCTYPE foo><body>hi</body>');

0 commit comments

Comments
 (0)