mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
parent
36c5395d2d
commit
10a82f862c
@ -124,7 +124,7 @@ function snapshotNodes(snapshot: FrameSnapshot): NodeSnapshot[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function snapshotScript() {
|
function snapshotScript() {
|
||||||
function applyPlaywrightAttributes(shadowAttribute: string, scrollTopAttribute: string, scrollLeftAttribute: string) {
|
function applyPlaywrightAttributes(shadowAttribute: string, scrollTopAttribute: string, scrollLeftAttribute: string, styleSheetAttribute: string) {
|
||||||
const scrollTops: Element[] = [];
|
const scrollTops: Element[] = [];
|
||||||
const scrollLefts: Element[] = [];
|
const scrollLefts: Element[] = [];
|
||||||
|
|
||||||
@ -152,6 +152,17 @@ function snapshotScript() {
|
|||||||
template.remove();
|
template.remove();
|
||||||
visit(shadowRoot);
|
visit(shadowRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('adoptedStyleSheets' in (root as any)) {
|
||||||
|
const adoptedSheets: CSSStyleSheet[] = [...(root as any).adoptedStyleSheets];
|
||||||
|
for (const element of root.querySelectorAll(`template[${styleSheetAttribute}]`)) {
|
||||||
|
const template = element as HTMLTemplateElement;
|
||||||
|
const sheet = new CSSStyleSheet();
|
||||||
|
(sheet as any).replaceSync(template.getAttribute(styleSheetAttribute));
|
||||||
|
adoptedSheets.push(sheet);
|
||||||
|
}
|
||||||
|
(root as any).adoptedStyleSheets = adoptedSheets;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
visit(document);
|
visit(document);
|
||||||
|
|
||||||
@ -172,5 +183,6 @@ function snapshotScript() {
|
|||||||
const kShadowAttribute = '__playwright_shadow_root_';
|
const kShadowAttribute = '__playwright_shadow_root_';
|
||||||
const kScrollTopAttribute = '__playwright_scroll_top_';
|
const kScrollTopAttribute = '__playwright_scroll_top_';
|
||||||
const kScrollLeftAttribute = '__playwright_scroll_left_';
|
const kScrollLeftAttribute = '__playwright_scroll_left_';
|
||||||
return `\n(${applyPlaywrightAttributes.toString()})('${kShadowAttribute}', '${kScrollTopAttribute}', '${kScrollLeftAttribute}')`;
|
const kStyleSheetAttribute = '__playwright_style_sheet_';
|
||||||
|
return `\n(${applyPlaywrightAttributes.toString()})('${kShadowAttribute}', '${kScrollTopAttribute}', '${kScrollLeftAttribute}', '${kStyleSheetAttribute}')`;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
|
|||||||
const kShadowAttribute = '__playwright_shadow_root_';
|
const kShadowAttribute = '__playwright_shadow_root_';
|
||||||
const kScrollTopAttribute = '__playwright_scroll_top_';
|
const kScrollTopAttribute = '__playwright_scroll_top_';
|
||||||
const kScrollLeftAttribute = '__playwright_scroll_left_';
|
const kScrollLeftAttribute = '__playwright_scroll_left_';
|
||||||
|
const kStyleSheetAttribute = '__playwright_style_sheet_';
|
||||||
|
|
||||||
// Symbols for our own info on Nodes/StyleSheets.
|
// Symbols for our own info on Nodes/StyleSheets.
|
||||||
const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
|
const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
|
||||||
@ -296,6 +297,15 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const visitChildStyleSheet = (child: CSSStyleSheet) => {
|
||||||
|
const snapshot = visitStyleSheet(child);
|
||||||
|
if (snapshot) {
|
||||||
|
result.push(snapshot.n);
|
||||||
|
expectValue(child);
|
||||||
|
equals = equals && snapshot.equals;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (nodeType === Node.DOCUMENT_FRAGMENT_NODE)
|
if (nodeType === Node.DOCUMENT_FRAGMENT_NODE)
|
||||||
attrs[kShadowAttribute] = 'open';
|
attrs[kShadowAttribute] = 'open';
|
||||||
|
|
||||||
@ -345,6 +355,15 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
|
|||||||
}
|
}
|
||||||
for (let child = node.firstChild; child; child = child.nextSibling)
|
for (let child = node.firstChild; child; child = child.nextSibling)
|
||||||
visitChild(child);
|
visitChild(child);
|
||||||
|
let documentOrShadowRoot = null;
|
||||||
|
if (node.ownerDocument!.documentElement === node)
|
||||||
|
documentOrShadowRoot = node.ownerDocument;
|
||||||
|
else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE)
|
||||||
|
documentOrShadowRoot = node;
|
||||||
|
if (documentOrShadowRoot) {
|
||||||
|
for (const sheet of (documentOrShadowRoot as any).adoptedStyleSheets || [])
|
||||||
|
visitChildStyleSheet(sheet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process iframe src attribute before bailing out since it depends on a symbol, not the DOM.
|
// Process iframe src attribute before bailing out since it depends on a symbol, not the DOM.
|
||||||
@ -397,6 +416,21 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
|
|||||||
return checkAndReturn(result);
|
return checkAndReturn(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const visitStyleSheet = (sheet: CSSStyleSheet) => {
|
||||||
|
const data = ensureCachedData(sheet);
|
||||||
|
const oldCSSText = data.cssText;
|
||||||
|
const cssText = this._updateStyleElementStyleSheetTextIfNeeded(sheet) || '';
|
||||||
|
if (cssText === oldCSSText)
|
||||||
|
return { equals: true, n: [[ snapshotNumber - data.ref![0], data.ref![1] ]] };
|
||||||
|
data.ref = [snapshotNumber, nodeCounter++];
|
||||||
|
return {
|
||||||
|
equals: false,
|
||||||
|
n: ['template', {
|
||||||
|
[kStyleSheetAttribute]: cssText,
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
let html: NodeSnapshot;
|
let html: NodeSnapshot;
|
||||||
if (document.documentElement) {
|
if (document.documentElement) {
|
||||||
const { n } = visitNode(document.documentElement)!;
|
const { n } = visitNode(document.documentElement)!;
|
||||||
|
@ -75,6 +75,14 @@ it.describe('snapshots', () => {
|
|||||||
expect(distillSnapshot(snapshot2)).toBe('<style>button { color: blue; }</style><BUTTON>Hello</BUTTON>');
|
expect(distillSnapshot(snapshot2)).toBe('<style>button { color: blue; }</style><BUTTON>Hello</BUTTON>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should have a custom doctype', async ({page, server, toImpl, snapshotter}) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.setContent('<!DOCTYPE foo><body>hi</body>');
|
||||||
|
|
||||||
|
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');
|
||||||
|
expect(distillSnapshot(snapshot)).toBe('<!DOCTYPE foo>hi');
|
||||||
|
});
|
||||||
|
|
||||||
it('should respect subresource CSSOM change', async ({ page, server, toImpl, snapshotter }) => {
|
it('should respect subresource CSSOM change', async ({ page, server, toImpl, snapshotter }) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.route('**/style.css', route => {
|
await page.route('**/style.css', route => {
|
||||||
@ -166,6 +174,49 @@ it.describe('snapshots', () => {
|
|||||||
expect(distillSnapshot(snapshot)).toBe('<BUTTON data="two">Hello</BUTTON>');
|
expect(distillSnapshot(snapshot)).toBe('<BUTTON data="two">Hello</BUTTON>');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should contain adopted style sheets', async ({ page, toImpl, contextFactory, snapshotPort, snapshotter, browserName }) => {
|
||||||
|
it.skip(browserName !== 'chromium', 'Constructed stylesheets are only in Chromium.');
|
||||||
|
await page.setContent('<button>Hello</button>');
|
||||||
|
await page.evaluate(() => {
|
||||||
|
const sheet = new CSSStyleSheet();
|
||||||
|
sheet.addRule('button', 'color: red');
|
||||||
|
(document as any).adoptedStyleSheets = [sheet];
|
||||||
|
|
||||||
|
const div = document.createElement('div');
|
||||||
|
const root = div.attachShadow({
|
||||||
|
mode: 'open'
|
||||||
|
});
|
||||||
|
root.append('foo');
|
||||||
|
const sheet2 = new CSSStyleSheet();
|
||||||
|
sheet2.addRule(':host', 'color: blue');
|
||||||
|
(root as any).adoptedStyleSheets = [sheet2];
|
||||||
|
document.body.appendChild(div);
|
||||||
|
});
|
||||||
|
const snapshot1 = await snapshotter.captureSnapshot(toImpl(page), 'snapshot1');
|
||||||
|
|
||||||
|
const previewContext = await contextFactory();
|
||||||
|
const previewPage = await previewContext.newPage();
|
||||||
|
previewPage.on('console', console.log);
|
||||||
|
await previewPage.goto(`http://localhost:${snapshotPort}/snapshot/`);
|
||||||
|
await previewPage.evaluate(snapshotId => {
|
||||||
|
(window as any).showSnapshot(snapshotId);
|
||||||
|
}, `${snapshot1.snapshot().pageId}?name=snapshot1`);
|
||||||
|
// wait for the render frame to load
|
||||||
|
while (previewPage.frames().length < 2)
|
||||||
|
await new Promise(f => previewPage.once('frameattached', f));
|
||||||
|
// wait for it to render
|
||||||
|
await previewPage.frames()[1].waitForSelector('button');
|
||||||
|
const buttonColor = await previewPage.frames()[1].$eval('button', button => {
|
||||||
|
return window.getComputedStyle(button).color;
|
||||||
|
});
|
||||||
|
expect(buttonColor).toBe('rgb(255, 0, 0)');
|
||||||
|
const divColor = await previewPage.frames()[1].$eval('div', div => {
|
||||||
|
return window.getComputedStyle(div).color;
|
||||||
|
});
|
||||||
|
expect(divColor).toBe('rgb(0, 0, 255)');
|
||||||
|
await previewContext.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function distillSnapshot(snapshot) {
|
function distillSnapshot(snapshot) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user