feat(screenshot): Add customizable color option for masked elements (#23185)

I added a new option to the screenshot method to customize the color of
the box when we want to mask some elements for the screenshot.

The default color is pink `#FF00FF`, but with this new option you can
specify the color you like the most, like a nice green `#00FF00`:

```js
await page.screenshot({
  mask: [page.locator('div').nth(5)],
  maskColor: "#00FF00",
})
```


![ss](https://github.com/microsoft/playwright/assets/23271049/05f754de-0ba6-47a3-ae3e-769720d3da3b)

---------

Signed-off-by: Jasiel Guillén <darkensses@gmail.com>
This commit is contained in:
Jasiel Guillén 2023-05-22 19:44:44 -06:00 committed by GitHub
parent 10b7cb3979
commit 700062c836
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 62 additions and 9 deletions

View File

@ -741,6 +741,9 @@ Returns the buffer with the captured screenshot.
### option: ElementHandle.screenshot.timeout = %%-input-timeout-js-%% ### option: ElementHandle.screenshot.timeout = %%-input-timeout-js-%%
* since: v1.8 * since: v1.8
### option: ElementHandle.screenshot.maskColor = %%-screenshot-option-mask-color-%%
* since: v1.34
## async method: ElementHandle.scrollIntoViewIfNeeded ## async method: ElementHandle.scrollIntoViewIfNeeded
* since: v1.8 * since: v1.8

View File

@ -1768,6 +1768,9 @@ Returns the buffer with the captured screenshot.
### option: Locator.screenshot.timeout = %%-input-timeout-js-%% ### option: Locator.screenshot.timeout = %%-input-timeout-js-%%
* since: v1.14 * since: v1.14
### option: Locator.screenshot.maskColor = %%-screenshot-option-mask-color-%%
* since: v1.34
## async method: Locator.scrollIntoViewIfNeeded ## async method: Locator.scrollIntoViewIfNeeded
* since: v1.14 * since: v1.14

View File

@ -3381,6 +3381,9 @@ Returns the buffer with the captured screenshot.
### option: Page.screenshot.clip = %%-screenshot-option-clip-%% ### option: Page.screenshot.clip = %%-screenshot-option-clip-%%
* since: v1.8 * since: v1.8
### option: Page.screenshot.maskColor = %%-screenshot-option-mask-color-%%
* since: v1.34
## async method: Page.selectOption ## async method: Page.selectOption
* since: v1.8 * since: v1.8
* discouraged: Use locator-based [`method: Locator.selectOption`] instead. Read more about [locators](../locators.md). * discouraged: Use locator-based [`method: Locator.selectOption`] instead. Read more about [locators](../locators.md).

View File

@ -1092,6 +1092,12 @@ Specify screenshot type, defaults to `png`.
Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with
a pink box `#FF00FF` that completely covers its bounding box. a pink box `#FF00FF` that completely covers its bounding box.
## screenshot-option-mask-color
* since: v1.34
- `maskColor` <[string]>
Specify the color of the overlay box for masked elements, in [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.
## screenshot-option-full-page ## screenshot-option-full-page
- `fullPage` <[boolean]> - `fullPage` <[boolean]>

View File

@ -1046,6 +1046,7 @@ scheme.PageExpectScreenshotParams = tObject({
frame: tChannel(['Frame']), frame: tChannel(['Frame']),
selector: tString, selector: tString,
}))), }))),
maskColor: tOptional(tString),
})), })),
}); });
scheme.PageExpectScreenshotResult = tObject({ scheme.PageExpectScreenshotResult = tObject({
@ -1069,6 +1070,7 @@ scheme.PageScreenshotParams = tObject({
frame: tChannel(['Frame']), frame: tChannel(['Frame']),
selector: tString, selector: tString,
}))), }))),
maskColor: tOptional(tString),
}); });
scheme.PageScreenshotResult = tObject({ scheme.PageScreenshotResult = tObject({
binary: tBinary, binary: tBinary,
@ -1876,6 +1878,7 @@ scheme.ElementHandleScreenshotParams = tObject({
frame: tChannel(['Frame']), frame: tChannel(['Frame']),
selector: tString, selector: tString,
}))), }))),
maskColor: tOptional(tString),
}); });
scheme.ElementHandleScreenshotResult = tObject({ scheme.ElementHandleScreenshotResult = tObject({
binary: tBinary, binary: tBinary,

View File

@ -857,12 +857,12 @@ export class Frame extends SdkObject {
return result; return result;
} }
async maskSelectors(selectors: ParsedSelector[]): Promise<void> { async maskSelectors(selectors: ParsedSelector[], color?: string): Promise<void> {
const context = await this._utilityContext(); const context = await this._utilityContext();
const injectedScript = await context.injectedScript(); const injectedScript = await context.injectedScript();
await injectedScript.evaluate((injected, { parsed }) => { await injectedScript.evaluate((injected, { parsed, color }) => {
injected.maskSelectors(parsed); injected.maskSelectors(parsed, color);
}, { parsed: selectors }); }, { parsed: selectors, color: color });
} }
async querySelectorAll(selector: string): Promise<dom.ElementHandle<Element>[]> { async querySelectorAll(selector: string): Promise<dom.ElementHandle<Element>[]> {

View File

@ -154,8 +154,8 @@ export class Highlight {
this._innerUpdateHighlight(elements, { color, tooltipText: selector ? asLocator(this._language, selector) : '' }); this._innerUpdateHighlight(elements, { color, tooltipText: selector ? asLocator(this._language, selector) : '' });
} }
maskElements(elements: Element[]) { maskElements(elements: Element[], color?: string) {
this._innerUpdateHighlight(elements, { color: '#F0F' }); this._innerUpdateHighlight(elements, { color: color ? color : '#F0F' });
} }
private _innerUpdateHighlight(elements: Element[], options: { color: string, tooltipText?: string }) { private _innerUpdateHighlight(elements: Element[], options: { color: string, tooltipText?: string }) {

View File

@ -1093,7 +1093,7 @@ export class InjectedScript {
return error; return error;
} }
maskSelectors(selectors: ParsedSelector[]) { maskSelectors(selectors: ParsedSelector[], color?: string) {
if (this._highlight) if (this._highlight)
this.hideHighlight(); this.hideHighlight();
this._highlight = new Highlight(this); this._highlight = new Highlight(this);
@ -1101,7 +1101,7 @@ export class InjectedScript {
const elements = []; const elements = [];
for (const selector of selectors) for (const selector of selectors)
elements.push(this.querySelectorAll(selector, this.document.documentElement)); elements.push(this.querySelectorAll(selector, this.document.documentElement));
this._highlight.maskElements(elements.flat()); this._highlight.maskElements(elements.flat(), color);
} }
highlight(selector: ParsedSelector) { highlight(selector: ParsedSelector) {

View File

@ -38,6 +38,7 @@ export type ScreenshotOptions = {
omitBackground?: boolean, omitBackground?: boolean,
animations?: 'disabled' | 'allow', animations?: 'disabled' | 'allow',
mask?: { frame: Frame, selector: string}[], mask?: { frame: Frame, selector: string}[],
maskColor?: string,
fullPage?: boolean, fullPage?: boolean,
clip?: Rect, clip?: Rect,
scale?: 'css' | 'device', scale?: 'css' | 'device',
@ -268,7 +269,7 @@ export class Screenshotter {
progress.throwIfAborted(); // Avoid extra work. progress.throwIfAborted(); // Avoid extra work.
await Promise.all([...framesToParsedSelectors.keys()].map(async frame => { await Promise.all([...framesToParsedSelectors.keys()].map(async frame => {
await frame.maskSelectors(framesToParsedSelectors.get(frame)); await frame.maskSelectors(framesToParsedSelectors.get(frame), options.maskColor);
})); }));
progress.cleanupWhenAborted(cleanup); progress.cleanupWhenAborted(cleanup);
return cleanup; return cleanup;

View File

@ -9828,6 +9828,12 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
*/ */
mask?: Array<Locator>; mask?: Array<Locator>;
/**
* Specify the color of the overlay box for masked elements, in
* [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.
*/
maskColor?: string;
/** /**
* Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images. * Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.
* Defaults to `false`. * Defaults to `false`.
@ -19639,6 +19645,12 @@ export interface LocatorScreenshotOptions {
*/ */
mask?: Array<Locator>; mask?: Array<Locator>;
/**
* Specify the color of the overlay box for masked elements, in
* [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.
*/
maskColor?: string;
/** /**
* Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images. * Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.
* Defaults to `false`. * Defaults to `false`.
@ -19826,6 +19838,12 @@ export interface PageScreenshotOptions {
*/ */
mask?: Array<Locator>; mask?: Array<Locator>;
/**
* Specify the color of the overlay box for masked elements, in
* [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.
*/
maskColor?: string;
/** /**
* Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images. * Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.
* Defaults to `false`. * Defaults to `false`.

View File

@ -1910,6 +1910,7 @@ export type PageExpectScreenshotParams = {
frame: FrameChannel, frame: FrameChannel,
selector: string, selector: string,
}[], }[],
maskColor?: string,
}, },
}; };
export type PageExpectScreenshotOptions = { export type PageExpectScreenshotOptions = {
@ -1936,6 +1937,7 @@ export type PageExpectScreenshotOptions = {
frame: FrameChannel, frame: FrameChannel,
selector: string, selector: string,
}[], }[],
maskColor?: string,
}, },
}; };
export type PageExpectScreenshotResult = { export type PageExpectScreenshotResult = {
@ -1959,6 +1961,7 @@ export type PageScreenshotParams = {
frame: FrameChannel, frame: FrameChannel,
selector: string, selector: string,
}[], }[],
maskColor?: string,
}; };
export type PageScreenshotOptions = { export type PageScreenshotOptions = {
timeout?: number, timeout?: number,
@ -1974,6 +1977,7 @@ export type PageScreenshotOptions = {
frame: FrameChannel, frame: FrameChannel,
selector: string, selector: string,
}[], }[],
maskColor?: string,
}; };
export type PageScreenshotResult = { export type PageScreenshotResult = {
binary: Binary, binary: Binary,
@ -3327,6 +3331,7 @@ export type ElementHandleScreenshotParams = {
frame: FrameChannel, frame: FrameChannel,
selector: string, selector: string,
}[], }[],
maskColor?: string,
}; };
export type ElementHandleScreenshotOptions = { export type ElementHandleScreenshotOptions = {
timeout?: number, timeout?: number,
@ -3340,6 +3345,7 @@ export type ElementHandleScreenshotOptions = {
frame: FrameChannel, frame: FrameChannel,
selector: string, selector: string,
}[], }[],
maskColor?: string,
}; };
export type ElementHandleScreenshotResult = { export type ElementHandleScreenshotResult = {
binary: Binary, binary: Binary,

View File

@ -376,6 +376,7 @@ CommonScreenshotOptions:
properties: properties:
frame: Frame frame: Frame
selector: string selector: string
maskColor: string?
LaunchOptions: LaunchOptions:
type: mixin type: mixin

View File

@ -534,6 +534,15 @@ it.describe('page screenshot', () => {
}); });
await page.screenshot({ mask: [page.locator('non-existent')] }); await page.screenshot({ mask: [page.locator('non-existent')] });
}); });
it('should work when mask color is not pink #F0F', async ({ page, server }) => {
await page.setViewportSize({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html');
expect(await page.screenshot({
mask: [page.locator('div').nth(5)],
maskColor: '#00FF00',
})).toMatchSnapshot('mask-color-should-work.png');
});
}); });
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB