diff --git a/docs/src/api/class-elementhandle.md b/docs/src/api/class-elementhandle.md index 1a7297be31..df73e558d0 100644 --- a/docs/src/api/class-elementhandle.md +++ b/docs/src/api/class-elementhandle.md @@ -749,6 +749,9 @@ Returns the buffer with the captured screenshot. ### option: ElementHandle.screenshot.maskColor = %%-screenshot-option-mask-color-%% * since: v1.34 +### option: ElementHandle.screenshot.style = %%-screenshot-option-style-%% +* since: v1.41 + ## async method: ElementHandle.scrollIntoViewIfNeeded * since: v1.8 diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index 5781878f6f..f59e0f09f3 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -1913,6 +1913,9 @@ Returns the buffer with the captured screenshot. ### option: Locator.screenshot.maskColor = %%-screenshot-option-mask-color-%% * since: v1.34 +### option: Locator.screenshot.style = %%-screenshot-option-style-%% +* since: v1.41 + ## async method: Locator.scrollIntoViewIfNeeded * since: v1.14 diff --git a/docs/src/api/class-locatorassertions.md b/docs/src/api/class-locatorassertions.md index cf78c6ce95..ccca23455d 100644 --- a/docs/src/api/class-locatorassertions.md +++ b/docs/src/api/class-locatorassertions.md @@ -1541,6 +1541,9 @@ Snapshot name. ### option: LocatorAssertions.toHaveScreenshot#1.maskColor = %%-screenshot-option-mask-color-%% * since: v1.35 +### option: LocatorAssertions.toHaveScreenshot#1.style = %%-screenshot-option-style-%% +* since: v1.41 + ### option: LocatorAssertions.toHaveScreenshot#1.omitBackground = %%-screenshot-option-omit-background-%% * since: v1.23 @@ -1587,6 +1590,9 @@ Note that screenshot assertions only work with Playwright test runner. ### option: LocatorAssertions.toHaveScreenshot#2.maskColor = %%-screenshot-option-mask-color-%% * since: v1.35 +### option: LocatorAssertions.toHaveScreenshot#2.style = %%-screenshot-option-style-%% +* since: v1.41 + ### option: LocatorAssertions.toHaveScreenshot#2.omitBackground = %%-screenshot-option-omit-background-%% * since: v1.23 diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index e875547905..807f749c95 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -3399,6 +3399,9 @@ Returns the buffer with the captured screenshot. ### option: Page.screenshot.maskColor = %%-screenshot-option-mask-color-%% * since: v1.34 +### option: Page.screenshot.style = %%-screenshot-option-style-%% +* since: v1.41 + ## async method: Page.selectOption * since: v1.8 * discouraged: Use locator-based [`method: Locator.selectOption`] instead. Read more about [locators](../locators.md). diff --git a/docs/src/api/class-pageassertions.md b/docs/src/api/class-pageassertions.md index fcb4f38652..c79ddb50eb 100644 --- a/docs/src/api/class-pageassertions.md +++ b/docs/src/api/class-pageassertions.md @@ -161,6 +161,9 @@ Snapshot name. ### option: PageAssertions.toHaveScreenshot#1.maskColor = %%-screenshot-option-mask-color-%% * since: v1.35 +### option: PageAssertions.toHaveScreenshot#1.style = %%-screenshot-option-style-%% +* since: v1.41 + ### option: PageAssertions.toHaveScreenshot#1.omitBackground = %%-screenshot-option-omit-background-%% * since: v1.23 @@ -212,6 +215,9 @@ Note that screenshot assertions only work with Playwright test runner. ### option: PageAssertions.toHaveScreenshot#2.maskColor = %%-screenshot-option-mask-color-%% * since: v1.35 +### option: PageAssertions.toHaveScreenshot#2.style = %%-screenshot-option-style-%% +* since: v1.41 + ### option: PageAssertions.toHaveScreenshot#2.omitBackground = %%-screenshot-option-omit-background-%% * since: v1.23 diff --git a/docs/src/api/params.md b/docs/src/api/params.md index 293b415c44..441ad4e544 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -1132,6 +1132,13 @@ Defaults to `"css"`. When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`, text caret behavior will not be changed. Defaults to `"hide"`. +## screenshot-option-style +- `style` + +Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements invisible +or change their properties to help you creating repeatable screenshots. This stylesheet pierces the Shadow DOM and applies +to the inner frames. + ## screenshot-options-common-list-v1.8 - %%-screenshot-option-animations-%% - %%-screenshot-option-omit-background-%% diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index 4ecc8e6206..5df9578846 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -47,6 +47,7 @@ export default defineConfig({ - `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`. - `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`. - `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`. + - `style` ?<[string]> See [`option: style`] in [`method: Page.screenshot`]. - `toMatchSnapshot` ?<[Object]> Configuration for the [`method: SnapshotAssertions.toMatchSnapshot#1`] method. - `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default. diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index a9bb86a892..5a6e9363d8 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1071,6 +1071,7 @@ scheme.PageExpectScreenshotParams = tObject({ selector: tString, }))), maskColor: tOptional(tString), + style: tOptional(tString), })), }); scheme.PageExpectScreenshotResult = tObject({ @@ -1095,6 +1096,7 @@ scheme.PageScreenshotParams = tObject({ selector: tString, }))), maskColor: tOptional(tString), + style: tOptional(tString), }); scheme.PageScreenshotResult = tObject({ binary: tBinary, @@ -1896,6 +1898,7 @@ scheme.ElementHandleScreenshotParams = tObject({ selector: tString, }))), maskColor: tOptional(tString), + style: tOptional(tString), }); scheme.ElementHandleScreenshotResult = tObject({ binary: tBinary, diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index f46484c579..68430aaeb3 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -847,7 +847,7 @@ export class Frame extends SdkObject { return result; } - async maskSelectors(selectors: ParsedSelector[], color?: string): Promise { + async maskSelectors(selectors: ParsedSelector[], color: string): Promise { const context = await this._utilityContext(); const injectedScript = await context.injectedScript(); await injectedScript.evaluate((injected, { parsed, color }) => { diff --git a/packages/playwright-core/src/server/injected/highlight.ts b/packages/playwright-core/src/server/injected/highlight.ts index 66e05491bc..3052846104 100644 --- a/packages/playwright-core/src/server/injected/highlight.ts +++ b/packages/playwright-core/src/server/injected/highlight.ts @@ -118,8 +118,8 @@ export class Highlight { this._innerUpdateHighlight(elements, options); } - maskElements(elements: Element[], color?: string) { - this._innerUpdateHighlight(elements, { color: color ? color : '#F0F' }); + maskElements(elements: Element[], color: string) { + this._innerUpdateHighlight(elements, { color: color }); } private _innerUpdateHighlight(elements: Element[], options: HighlightOptions) { diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index 1c87f8bf42..3af647e082 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -1125,7 +1125,7 @@ export class InjectedScript { return error; } - maskSelectors(selectors: ParsedSelector[], color?: string) { + maskSelectors(selectors: ParsedSelector[], color: string) { if (this._highlight) this.hideHighlight(); this._highlight = new Highlight(this); diff --git a/packages/playwright-core/src/server/screenshotter.ts b/packages/playwright-core/src/server/screenshotter.ts index 464d95bdc0..d6103786b6 100644 --- a/packages/playwright-core/src/server/screenshotter.ts +++ b/packages/playwright-core/src/server/screenshotter.ts @@ -28,24 +28,25 @@ import { MultiMap } from '../utils/multimap'; declare global { interface Window { - __cleanupScreenshot?: () => void; + __pwCleanupScreenshot?: () => void; } } export type ScreenshotOptions = { - type?: 'png' | 'jpeg', - quality?: number, - omitBackground?: boolean, - animations?: 'disabled' | 'allow', - mask?: { frame: Frame, selector: string}[], - maskColor?: string, - fullPage?: boolean, - clip?: Rect, - scale?: 'css' | 'device', - caret?: 'hide' | 'initial', + type?: 'png' | 'jpeg'; + quality?: number; + omitBackground?: boolean; + animations?: 'disabled' | 'allow'; + mask?: { frame: Frame, selector: string}[]; + maskColor?: string; + fullPage?: boolean; + clip?: Rect; + scale?: 'css' | 'device'; + caret?: 'hide' | 'initial'; + style?: string; }; -function inPagePrepareForScreenshots(hideCaret: boolean, disableAnimations: boolean) { +function inPagePrepareForScreenshots(screenshotStyle: string, disableAnimations: boolean) { const collectRoots = (root: Document | ShadowRoot, roots: (Document|ShadowRoot)[] = []): (Document|ShadowRoot)[] => { roots.push(root); const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT); @@ -58,29 +59,23 @@ function inPagePrepareForScreenshots(hideCaret: boolean, disableAnimations: bool return roots; }; - let documentRoots: (Document|ShadowRoot)[] | undefined; - const memoizedRoots = () => documentRoots ??= collectRoots(document); - - const styleTags: Element[] = []; - if (hideCaret) { - for (const root of memoizedRoots()) { - const styleTag = document.createElement('style'); - styleTag.textContent = ` - *:not(#playwright-aaaaaaaaaa.playwright-bbbbbbbbbbb.playwright-cccccccccc.playwright-dddddddddd.playwright-eeeeeeeee) { - caret-color: transparent !important; - } - `; - if (root === document) - document.documentElement.append(styleTag); - else - root.append(styleTag); - styleTags.push(styleTag); - } - } - const infiniteAnimationsToResume: Set = new Set(); + const roots = collectRoots(document); const cleanupCallbacks: (() => void)[] = []; + for (const root of roots) { + const styleTag = document.createElement('style'); + styleTag.textContent = screenshotStyle; + if (root === document) + document.documentElement.append(styleTag); + else + root.append(styleTag); + cleanupCallbacks.push(() => { + styleTag.remove(); + }); + + } if (disableAnimations) { + const infiniteAnimationsToResume: Set = new Set(); const handleAnimations = (root: Document|ShadowRoot): void => { for (const animation of root.getAnimations()) { if (!animation.effect || animation.playbackRate === 0 || infiniteAnimationsToResume.has(animation)) @@ -106,7 +101,7 @@ function inPagePrepareForScreenshots(hideCaret: boolean, disableAnimations: bool } } }; - for (const root of memoizedRoots()) { + for (const root of roots) { const handleRootAnimations: (() => void) = handleAnimations.bind(null, root); handleRootAnimations(); root.addEventListener('transitionrun', handleRootAnimations); @@ -116,23 +111,22 @@ function inPagePrepareForScreenshots(hideCaret: boolean, disableAnimations: bool root.removeEventListener('animationstart', handleRootAnimations); }); } + cleanupCallbacks.push(() => { + for (const animation of infiniteAnimationsToResume) { + try { + animation.play(); + } catch (e) { + // animation.play() should never throw, but + // we'd like to be on the safe side. + } + } + }); } - window.__cleanupScreenshot = () => { - for (const styleTag of styleTags) - styleTag.remove(); - - for (const animation of infiniteAnimationsToResume) { - try { - animation.play(); - } catch (e) { - // animation.play() should never throw, but - // we'd like to be on the safe side. - } - } + window.__pwCleanupScreenshot = () => { for (const cleanupCallback of cleanupCallbacks) cleanupCallback(); - delete window.__cleanupScreenshot; + delete window.__pwCleanupScreenshot; }; } @@ -178,7 +172,7 @@ export class Screenshotter { return this._queue.postTask(async () => { progress.log('taking page screenshot'); const { viewportSize } = await this._originalViewportSize(progress); - await this._preparePageForScreenshot(progress, options.caret !== 'initial', options.animations === 'disabled'); + await this._preparePageForScreenshot(progress, screenshotStyle(options), options.animations === 'disabled'); progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup. if (options.fullPage) { @@ -207,7 +201,7 @@ export class Screenshotter { progress.log('taking element screenshot'); const { viewportSize } = await this._originalViewportSize(progress); - await this._preparePageForScreenshot(progress, options.caret !== 'initial', options.animations === 'disabled'); + await this._preparePageForScreenshot(progress, screenshotStyle(options), options.animations === 'disabled'); progress.throwIfAborted(); // Do not do extra work. await handle._waitAndScrollIntoViewIfNeeded(progress, true /* waitForVisible */); @@ -231,14 +225,11 @@ export class Screenshotter { }); } - async _preparePageForScreenshot(progress: Progress, hideCaret: boolean, disableAnimations: boolean) { - if (!hideCaret && !disableAnimations) - return; - + async _preparePageForScreenshot(progress: Progress, screenshotStyle: string, disableAnimations: boolean) { if (disableAnimations) progress.log(' disabled all CSS animations'); await Promise.all(this._page.frames().map(async frame => { - await frame.nonStallingEvaluateInExistingContext('(' + inPagePrepareForScreenshots.toString() + `)(${hideCaret}, ${disableAnimations})`, false, 'utility').catch(() => {}); + await frame.nonStallingEvaluateInExistingContext('(' + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${disableAnimations})`, false, 'utility').catch(() => {}); })); if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) { progress.log('waiting for fonts to load...'); @@ -252,7 +243,7 @@ export class Screenshotter { async _restorePageAfterScreenshot() { await Promise.all(this._page.frames().map(async frame => { - frame.nonStallingEvaluateInExistingContext('window.__cleanupScreenshot && window.__cleanupScreenshot()', false, 'utility').catch(() => {}); + frame.nonStallingEvaluateInExistingContext('window.__pwCleanupScreenshot && window.__pwCleanupScreenshot()', false, 'utility').catch(() => {}); })); } @@ -276,7 +267,7 @@ export class Screenshotter { progress.throwIfAborted(); // Avoid extra work. await Promise.all([...framesToParsedSelectors.keys()].map(async frame => { - await frame.maskSelectors(framesToParsedSelectors.get(frame), options.maskColor); + await frame.maskSelectors(framesToParsedSelectors.get(frame), options.maskColor || '#F0F'); })); progress.cleanupWhenAborted(cleanup); return cleanup; @@ -368,3 +359,16 @@ export function validateScreenshotOptions(options: ScreenshotOptions): 'png' | ' } return format; } + +function screenshotStyle(options: ScreenshotOptions): string { + const parts: string[] = []; + if (options.caret !== 'initial') { + parts.push(` + *:not(#playwright-aaaaaaaaaa.playwright-bbbbbbbbbbb.playwright-cccccccccc.playwright-dddddddddd.playwright-eeeeeeeee) { + caret-color: transparent !important; + }`); + } + if (options.style) + parts.push(options.style); + return parts.join('\n'); +} diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 51979c06cd..31dc4798ed 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -9967,6 +9967,13 @@ export interface ElementHandle extends JSHandle { */ scale?: "css"|"device"; + /** + * Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements + * invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the + * Shadow DOM and applies to the inner frames. + */ + style?: string; + /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` * option in the config, or by using the @@ -20037,6 +20044,13 @@ export interface LocatorScreenshotOptions { */ scale?: "css"|"device"; + /** + * Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements + * invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the + * Shadow DOM and applies to the inner frames. + */ + style?: string; + /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` * option in the config, or by using the @@ -20230,6 +20244,13 @@ export interface PageScreenshotOptions { */ scale?: "css"|"device"; + /** + * Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements + * invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the + * Shadow DOM and applies to the inner frames. + */ + style?: string; + /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` * option in the config, or by using the diff --git a/packages/playwright/src/matchers/toMatchSnapshot.ts b/packages/playwright/src/matchers/toMatchSnapshot.ts index bddc6d4178..0140abf899 100644 --- a/packages/playwright/src/matchers/toMatchSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchSnapshot.ts @@ -356,6 +356,7 @@ export async function toHaveScreenshot( animations: config?.animations ?? 'disabled', scale: config?.scale ?? 'css', caret: config?.caret ?? 'hide', + style: config?.style ?? '', ...helper.allOptions, mask: (helper.allOptions.mask || []) as LocatorEx[], maskColor: helper.allOptions.maskColor, diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index d8a7d5b8bd..0c7f8d37a3 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -662,6 +662,11 @@ interface TestConfig { * to `"css"`. */ scale?: "css"|"device"; + + /** + * See `style` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). + */ + style?: string; }; /** @@ -5951,6 +5956,13 @@ interface LocatorAssertions { */ scale?: "css"|"device"; + /** + * Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements + * invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the + * Shadow DOM and applies to the inner frames. + */ + style?: string; + /** * An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between the * same pixel in compared images, between zero (strict) and one (lax), default is configurable with @@ -6034,6 +6046,13 @@ interface LocatorAssertions { */ scale?: "css"|"device"; + /** + * Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements + * invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the + * Shadow DOM and applies to the inner frames. + */ + style?: string; + /** * An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between the * same pixel in compared images, between zero (strict) and one (lax), default is configurable with @@ -6298,6 +6317,13 @@ interface PageAssertions { */ scale?: "css"|"device"; + /** + * Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements + * invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the + * Shadow DOM and applies to the inner frames. + */ + style?: string; + /** * An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between the * same pixel in compared images, between zero (strict) and one (lax), default is configurable with @@ -6411,6 +6437,13 @@ interface PageAssertions { */ scale?: "css"|"device"; + /** + * Stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements + * invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the + * Shadow DOM and applies to the inner frames. + */ + style?: string; + /** * An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between the * same pixel in compared images, between zero (strict) and one (lax), default is configurable with diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index 68d28335e1..cf2cd36501 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -1944,6 +1944,7 @@ export type PageExpectScreenshotParams = { selector: string, }[], maskColor?: string, + style?: string, }, }; export type PageExpectScreenshotOptions = { @@ -1971,6 +1972,7 @@ export type PageExpectScreenshotOptions = { selector: string, }[], maskColor?: string, + style?: string, }, }; export type PageExpectScreenshotResult = { @@ -1995,6 +1997,7 @@ export type PageScreenshotParams = { selector: string, }[], maskColor?: string, + style?: string, }; export type PageScreenshotOptions = { timeout?: number, @@ -2011,6 +2014,7 @@ export type PageScreenshotOptions = { selector: string, }[], maskColor?: string, + style?: string, }; export type PageScreenshotResult = { binary: Binary, @@ -3355,6 +3359,7 @@ export type ElementHandleScreenshotParams = { selector: string, }[], maskColor?: string, + style?: string, }; export type ElementHandleScreenshotOptions = { timeout?: number, @@ -3369,6 +3374,7 @@ export type ElementHandleScreenshotOptions = { selector: string, }[], maskColor?: string, + style?: string, }; export type ElementHandleScreenshotResult = { binary: Binary, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index aac64bdc50..7eb7a96a9b 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -379,6 +379,7 @@ CommonScreenshotOptions: frame: Frame selector: string maskColor: string? + style: string? LaunchOptions: type: mixin diff --git a/tests/page/page-screenshot.spec.ts b/tests/page/page-screenshot.spec.ts index ecb876cd15..ef7bdd522c 100644 --- a/tests/page/page-screenshot.spec.ts +++ b/tests/page/page-screenshot.spec.ts @@ -543,6 +543,36 @@ it.describe('page screenshot', () => { maskColor: '#00FF00', })).toMatchSnapshot('mask-color-should-work.png'); }); + + it('should hide elements based on attr', async ({ page, server }) => { + await page.setViewportSize({ width: 500, height: 500 }); + await page.goto(server.PREFIX + '/grid.html'); + await page.locator('div').nth(5).evaluate(element => { + element.setAttribute('data-test-screenshot', 'hide'); + }); + expect(await page.screenshot({ + style: `[data-test-screenshot="hide"] { + visibility: hidden; + }` + })).toMatchSnapshot('hide-should-work.png'); + const visibility = await page.locator('div').nth(5).evaluate(element => element.style.visibility); + expect(visibility).toBe(''); + }); + + it('should remove elements based on attr', async ({ page, server }) => { + await page.setViewportSize({ width: 500, height: 500 }); + await page.goto(server.PREFIX + '/grid.html'); + await page.locator('div').nth(5).evaluate(element => { + element.setAttribute('data-test-screenshot', 'remove'); + }); + expect(await page.screenshot({ + style: `[data-test-screenshot="remove"] { + display: none; + }` + })).toMatchSnapshot('remove-should-work.png'); + const display = await page.locator('div').nth(5).evaluate(element => element.style.display); + expect(display).toBe(''); + }); }); }); diff --git a/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-chromium.png new file mode 100644 index 0000000000..dd0549a411 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-firefox.png b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-firefox.png new file mode 100644 index 0000000000..7af4f1af70 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-firefox.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-webkit.png b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-webkit.png new file mode 100644 index 0000000000..b3d74594d4 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-webkit.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-chromium.png new file mode 100644 index 0000000000..d35ddd4289 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-firefox.png b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-firefox.png new file mode 100644 index 0000000000..cbae7f34d0 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-firefox.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-webkit.png b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-webkit.png new file mode 100644 index 0000000000..88699136c1 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-webkit.png differ diff --git a/tests/playwright-test/to-have-screenshot.spec.ts b/tests/playwright-test/to-have-screenshot.spec.ts index 20cac36936..92942780b3 100644 --- a/tests/playwright-test/to-have-screenshot.spec.ts +++ b/tests/playwright-test/to-have-screenshot.spec.ts @@ -1216,6 +1216,47 @@ test('should support maskColor option', async ({ runInlineTest }) => { expect(result.exitCode).toBe(0); }); +test('should support style option', async ({ runInlineTest }) => { + const result = await runInlineTest({ + ...playwrightConfig({ + snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}', + }), + '__screenshots__/a.spec.js/snapshot.png': createImage(IMG_WIDTH, IMG_HEIGHT, 0, 255, 0), + 'a.spec.js': ` + const { test, expect } = require('@playwright/test'); + test('png', async ({ page }) => { + await page.setContent(''); + await expect(page).toHaveScreenshot('snapshot.png', { + style: 'body { background: #00FF00; }', + }); + }); + `, + }); + expect(result.exitCode).toBe(0); +}); + +test('should support style option in config', async ({ runInlineTest }) => { + const result = await runInlineTest({ + ...playwrightConfig({ + snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}', + expect: { + toHaveScreenshot: { + style: 'body { background: #00FF00; }', + }, + }, + }), + '__screenshots__/a.spec.js/snapshot.png': createImage(IMG_WIDTH, IMG_HEIGHT, 0, 255, 0), + 'a.spec.js': ` + const { test, expect } = require('@playwright/test'); + test('png', async ({ page }) => { + await page.setContent(''); + await expect(page).toHaveScreenshot('snapshot.png'); + }); + `, + }); + expect(result.exitCode).toBe(0); +}); + function playwrightConfig(obj: any) { return { 'playwright.config.js': `