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 applyPlaywrightAttributes(shadowAttribute: string, scrollTopAttribute: string, scrollLeftAttribute: string) {
|
||||
function applyPlaywrightAttributes(shadowAttribute: string, scrollTopAttribute: string, scrollLeftAttribute: string, styleSheetAttribute: string) {
|
||||
const scrollTops: Element[] = [];
|
||||
const scrollLefts: Element[] = [];
|
||||
|
||||
@ -152,6 +152,17 @@ function snapshotScript() {
|
||||
template.remove();
|
||||
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);
|
||||
|
||||
@ -172,5 +183,6 @@ function snapshotScript() {
|
||||
const kShadowAttribute = '__playwright_shadow_root_';
|
||||
const kScrollTopAttribute = '__playwright_scroll_top_';
|
||||
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 kScrollTopAttribute = '__playwright_scroll_top_';
|
||||
const kScrollLeftAttribute = '__playwright_scroll_left_';
|
||||
const kStyleSheetAttribute = '__playwright_style_sheet_';
|
||||
|
||||
// Symbols for our own info on Nodes/StyleSheets.
|
||||
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)
|
||||
attrs[kShadowAttribute] = 'open';
|
||||
|
||||
@ -345,6 +355,15 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
|
||||
}
|
||||
for (let child = node.firstChild; child; child = child.nextSibling)
|
||||
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.
|
||||
@ -397,6 +416,21 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
|
||||
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;
|
||||
if (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>');
|
||||
});
|
||||
|
||||
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 }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/style.css', route => {
|
||||
@ -166,6 +174,49 @@ it.describe('snapshots', () => {
|
||||
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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user