mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(screenshot): introduce new "fonts" option for screenshots (#12661)
This option will wait for webfonts to load before taking screenshots.
This commit is contained in:
parent
12d8a262be
commit
49e66c7f08
@ -955,7 +955,12 @@ An object which specifies clipping of the resulting image. Should have the follo
|
|||||||
## screenshot-option-size
|
## screenshot-option-size
|
||||||
- `size` <[ScreenshotSize]<"css"|"device">>
|
- `size` <[ScreenshotSize]<"css"|"device">>
|
||||||
|
|
||||||
When set to `css`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will keep screenshots small. Using `device` option will produce a single pixel per each device pixel, so screenhots of high-dpi devices will be twice as large or even larger. Defaults to `device`.
|
When set to `"css"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will keep screenshots small. Using `"device"` option will produce a single pixel per each device pixel, so screenhots of high-dpi devices will be twice as large or even larger. Defaults to `"device"`.
|
||||||
|
|
||||||
|
## screenshot-option-fonts
|
||||||
|
- `fonts` <[ScreenshotFonts]<"ready"|"nowait">>
|
||||||
|
|
||||||
|
When set to `"ready"`, screenshot will wait for [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) promise to resolve in all frames. Defaults to `"nowait"`.
|
||||||
|
|
||||||
## screenshot-options-common-list
|
## screenshot-options-common-list
|
||||||
- %%-screenshot-option-animations-%%
|
- %%-screenshot-option-animations-%%
|
||||||
@ -963,6 +968,7 @@ When set to `css`, screenshot will have a single pixel per each css pixel on the
|
|||||||
- %%-screenshot-option-quality-%%
|
- %%-screenshot-option-quality-%%
|
||||||
- %%-screenshot-option-path-%%
|
- %%-screenshot-option-path-%%
|
||||||
- %%-screenshot-option-size-%%
|
- %%-screenshot-option-size-%%
|
||||||
|
- %%-screenshot-option-fonts-%%
|
||||||
- %%-screenshot-option-type-%%
|
- %%-screenshot-option-type-%%
|
||||||
- %%-screenshot-option-mask-%%
|
- %%-screenshot-option-mask-%%
|
||||||
- %%-input-timeout-%%
|
- %%-input-timeout-%%
|
||||||
|
@ -1550,6 +1550,7 @@ export type PageScreenshotParams = {
|
|||||||
animations?: 'disabled',
|
animations?: 'disabled',
|
||||||
clip?: Rect,
|
clip?: Rect,
|
||||||
size?: 'css' | 'device',
|
size?: 'css' | 'device',
|
||||||
|
fonts?: 'ready' | 'nowait',
|
||||||
mask?: {
|
mask?: {
|
||||||
frame: FrameChannel,
|
frame: FrameChannel,
|
||||||
selector: string,
|
selector: string,
|
||||||
@ -1564,6 +1565,7 @@ export type PageScreenshotOptions = {
|
|||||||
animations?: 'disabled',
|
animations?: 'disabled',
|
||||||
clip?: Rect,
|
clip?: Rect,
|
||||||
size?: 'css' | 'device',
|
size?: 'css' | 'device',
|
||||||
|
fonts?: 'ready' | 'nowait',
|
||||||
mask?: {
|
mask?: {
|
||||||
frame: FrameChannel,
|
frame: FrameChannel,
|
||||||
selector: string,
|
selector: string,
|
||||||
@ -2864,6 +2866,7 @@ export type ElementHandleScreenshotParams = {
|
|||||||
omitBackground?: boolean,
|
omitBackground?: boolean,
|
||||||
animations?: 'disabled',
|
animations?: 'disabled',
|
||||||
size?: 'css' | 'device',
|
size?: 'css' | 'device',
|
||||||
|
fonts?: 'ready' | 'nowait',
|
||||||
mask?: {
|
mask?: {
|
||||||
frame: FrameChannel,
|
frame: FrameChannel,
|
||||||
selector: string,
|
selector: string,
|
||||||
@ -2876,6 +2879,7 @@ export type ElementHandleScreenshotOptions = {
|
|||||||
omitBackground?: boolean,
|
omitBackground?: boolean,
|
||||||
animations?: 'disabled',
|
animations?: 'disabled',
|
||||||
size?: 'css' | 'device',
|
size?: 'css' | 'device',
|
||||||
|
fonts?: 'ready' | 'nowait',
|
||||||
mask?: {
|
mask?: {
|
||||||
frame: FrameChannel,
|
frame: FrameChannel,
|
||||||
selector: string,
|
selector: string,
|
||||||
|
@ -1055,6 +1055,11 @@ Page:
|
|||||||
literals:
|
literals:
|
||||||
- css
|
- css
|
||||||
- device
|
- device
|
||||||
|
fonts:
|
||||||
|
type: enum?
|
||||||
|
literals:
|
||||||
|
- ready
|
||||||
|
- nowait
|
||||||
mask:
|
mask:
|
||||||
type: array?
|
type: array?
|
||||||
items:
|
items:
|
||||||
@ -2224,6 +2229,11 @@ ElementHandle:
|
|||||||
literals:
|
literals:
|
||||||
- css
|
- css
|
||||||
- device
|
- device
|
||||||
|
fonts:
|
||||||
|
type: enum?
|
||||||
|
literals:
|
||||||
|
- ready
|
||||||
|
- nowait
|
||||||
mask:
|
mask:
|
||||||
type: array?
|
type: array?
|
||||||
items:
|
items:
|
||||||
|
@ -573,6 +573,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||||||
animations: tOptional(tEnum(['disabled'])),
|
animations: tOptional(tEnum(['disabled'])),
|
||||||
clip: tOptional(tType('Rect')),
|
clip: tOptional(tType('Rect')),
|
||||||
size: tOptional(tEnum(['css', 'device'])),
|
size: tOptional(tEnum(['css', 'device'])),
|
||||||
|
fonts: tOptional(tEnum(['ready', 'nowait'])),
|
||||||
mask: tOptional(tArray(tObject({
|
mask: tOptional(tArray(tObject({
|
||||||
frame: tChannel('Frame'),
|
frame: tChannel('Frame'),
|
||||||
selector: tString,
|
selector: tString,
|
||||||
@ -1068,6 +1069,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||||||
omitBackground: tOptional(tBoolean),
|
omitBackground: tOptional(tBoolean),
|
||||||
animations: tOptional(tEnum(['disabled'])),
|
animations: tOptional(tEnum(['disabled'])),
|
||||||
size: tOptional(tEnum(['css', 'device'])),
|
size: tOptional(tEnum(['css', 'device'])),
|
||||||
|
fonts: tOptional(tEnum(['ready', 'nowait'])),
|
||||||
mask: tOptional(tArray(tObject({
|
mask: tOptional(tArray(tObject({
|
||||||
frame: tChannel('Frame'),
|
frame: tChannel('Frame'),
|
||||||
selector: tString,
|
selector: tString,
|
||||||
|
@ -41,6 +41,7 @@ export type ScreenshotOptions = {
|
|||||||
fullPage?: boolean,
|
fullPage?: boolean,
|
||||||
clip?: Rect,
|
clip?: Rect,
|
||||||
size?: 'css' | 'device',
|
size?: 'css' | 'device',
|
||||||
|
fonts?: 'ready' | 'nowait',
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Screenshotter {
|
export class Screenshotter {
|
||||||
@ -84,7 +85,7 @@ export class Screenshotter {
|
|||||||
const format = validateScreenshotOptions(options);
|
const format = validateScreenshotOptions(options);
|
||||||
return this._queue.postTask(async () => {
|
return this._queue.postTask(async () => {
|
||||||
const { viewportSize } = await this._originalViewportSize(progress);
|
const { viewportSize } = await this._originalViewportSize(progress);
|
||||||
await this._preparePageForScreenshot(progress, options.animations === 'disabled');
|
await this._preparePageForScreenshot(progress, options.animations === 'disabled', options.fonts === 'ready');
|
||||||
progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup.
|
progress.throwIfAborted(); // Avoid restoring after failure - should be done by cleanup.
|
||||||
|
|
||||||
if (options.fullPage) {
|
if (options.fullPage) {
|
||||||
@ -112,7 +113,7 @@ export class Screenshotter {
|
|||||||
return this._queue.postTask(async () => {
|
return this._queue.postTask(async () => {
|
||||||
const { viewportSize } = await this._originalViewportSize(progress);
|
const { viewportSize } = await this._originalViewportSize(progress);
|
||||||
|
|
||||||
await this._preparePageForScreenshot(progress, options.animations === 'disabled');
|
await this._preparePageForScreenshot(progress, options.animations === 'disabled', options.fonts === 'ready');
|
||||||
progress.throwIfAborted(); // Do not do extra work.
|
progress.throwIfAborted(); // Do not do extra work.
|
||||||
|
|
||||||
await handle._waitAndScrollIntoViewIfNeeded(progress);
|
await handle._waitAndScrollIntoViewIfNeeded(progress);
|
||||||
@ -136,9 +137,9 @@ export class Screenshotter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preparePageForScreenshot(progress: Progress, disableAnimations: boolean) {
|
async _preparePageForScreenshot(progress: Progress, disableAnimations: boolean, waitForFonts: boolean) {
|
||||||
await Promise.all(this._page.frames().map(async frame => {
|
await Promise.all(this._page.frames().map(async frame => {
|
||||||
await frame.nonStallingEvaluateInExistingContext('(' + (function(disableAnimations: boolean) {
|
await frame.nonStallingEvaluateInExistingContext('(' + (async function(disableAnimations: boolean, waitForFonts: boolean) {
|
||||||
const styleTag = document.createElement('style');
|
const styleTag = document.createElement('style');
|
||||||
styleTag.textContent = `
|
styleTag.textContent = `
|
||||||
*:not(#playwright-aaaaaaaaaa.playwright-bbbbbbbbbbb.playwright-cccccccccc.playwright-dddddddddd.playwright-eeeeeeeee) {
|
*:not(#playwright-aaaaaaaaaa.playwright-bbbbbbbbbbb.playwright-cccccccccc.playwright-dddddddddd.playwright-eeeeeeeee) {
|
||||||
@ -212,7 +213,10 @@ export class Screenshotter {
|
|||||||
cleanupCallback();
|
cleanupCallback();
|
||||||
delete window.__cleanupScreenshot;
|
delete window.__cleanupScreenshot;
|
||||||
};
|
};
|
||||||
}).toString() + `)(${disableAnimations || false})`, false, 'utility').catch(() => {});
|
|
||||||
|
if (waitForFonts)
|
||||||
|
await document.fonts.ready;
|
||||||
|
}).toString() + `)(${disableAnimations}, ${waitForFonts})`, false, 'utility').catch(() => {});
|
||||||
}));
|
}));
|
||||||
progress.cleanupWhenAborted(() => this._restorePageAfterScreenshot());
|
progress.cleanupWhenAborted(() => this._restorePageAfterScreenshot());
|
||||||
}
|
}
|
||||||
|
39
packages/playwright-core/types/types.d.ts
vendored
39
packages/playwright-core/types/types.d.ts
vendored
@ -8072,6 +8072,13 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
|
|||||||
*/
|
*/
|
||||||
animations?: "disabled";
|
animations?: "disabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to `"ready"`, screenshot will wait for
|
||||||
|
* [`document.fonts.ready()`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) promise to resolve in all
|
||||||
|
* frames. Defaults to `"nowait"`.
|
||||||
|
*/
|
||||||
|
fonts?: "ready"|"nowait";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlayed with a pink box
|
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlayed with a pink box
|
||||||
* `#FF00FF` that completely covers its bounding box.
|
* `#FF00FF` that completely covers its bounding box.
|
||||||
@ -8097,9 +8104,9 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
|
|||||||
quality?: number;
|
quality?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When set to `css`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will
|
* When set to `"css"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will
|
||||||
* keep screenshots small. Using `device` option will produce a single pixel per each device pixel, so screenhots of
|
* keep screenshots small. Using `"device"` option will produce a single pixel per each device pixel, so screenhots of
|
||||||
* high-dpi devices will be twice as large or even larger. Defaults to `device`.
|
* high-dpi devices will be twice as large or even larger. Defaults to `"device"`.
|
||||||
*/
|
*/
|
||||||
size?: "css"|"device";
|
size?: "css"|"device";
|
||||||
|
|
||||||
@ -15593,6 +15600,13 @@ export interface LocatorScreenshotOptions {
|
|||||||
*/
|
*/
|
||||||
animations?: "disabled";
|
animations?: "disabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to `"ready"`, screenshot will wait for
|
||||||
|
* [`document.fonts.ready()`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) promise to resolve in all
|
||||||
|
* frames. Defaults to `"nowait"`.
|
||||||
|
*/
|
||||||
|
fonts?: "ready"|"nowait";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlayed with a pink box
|
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlayed with a pink box
|
||||||
* `#FF00FF` that completely covers its bounding box.
|
* `#FF00FF` that completely covers its bounding box.
|
||||||
@ -15618,9 +15632,9 @@ export interface LocatorScreenshotOptions {
|
|||||||
quality?: number;
|
quality?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When set to `css`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will
|
* When set to `"css"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will
|
||||||
* keep screenshots small. Using `device` option will produce a single pixel per each device pixel, so screenhots of
|
* keep screenshots small. Using `"device"` option will produce a single pixel per each device pixel, so screenhots of
|
||||||
* high-dpi devices will be twice as large or even larger. Defaults to `device`.
|
* high-dpi devices will be twice as large or even larger. Defaults to `"device"`.
|
||||||
*/
|
*/
|
||||||
size?: "css"|"device";
|
size?: "css"|"device";
|
||||||
|
|
||||||
@ -15762,6 +15776,13 @@ export interface PageScreenshotOptions {
|
|||||||
height: number;
|
height: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to `"ready"`, screenshot will wait for
|
||||||
|
* [`document.fonts.ready()`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) promise to resolve in all
|
||||||
|
* frames. Defaults to `"nowait"`.
|
||||||
|
*/
|
||||||
|
fonts?: "ready"|"nowait";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Defaults to
|
* When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Defaults to
|
||||||
* `false`.
|
* `false`.
|
||||||
@ -15793,9 +15814,9 @@ export interface PageScreenshotOptions {
|
|||||||
quality?: number;
|
quality?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When set to `css`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will
|
* When set to `"css"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will
|
||||||
* keep screenshots small. Using `device` option will produce a single pixel per each device pixel, so screenhots of
|
* keep screenshots small. Using `"device"` option will produce a single pixel per each device pixel, so screenhots of
|
||||||
* high-dpi devices will be twice as large or even larger. Defaults to `device`.
|
* high-dpi devices will be twice as large or even larger. Defaults to `"device"`.
|
||||||
*/
|
*/
|
||||||
size?: "css"|"device";
|
size?: "css"|"device";
|
||||||
|
|
||||||
|
3
tests/assets/webfont/README.md
Normal file
3
tests/assets/webfont/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This icon font was generated:
|
||||||
|
- using SVG icons from https://github.com/primer/octicons
|
||||||
|
- bundling icons into webfonts using https://github.com/fontello/fontello
|
14
tests/assets/webfont/iconfont.svg
Normal file
14
tests/assets/webfont/iconfont.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata>Copyright (C) 2022 by original authors @ fontello.com</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="fontello" horiz-adv-x="1000" >
|
||||||
|
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||||
|
<missing-glyph horiz-adv-x="1000" />
|
||||||
|
<glyph glyph-name="plus" unicode="+" d="M531 527a31 31 0 0 1-62 0v-146h-146a31 31 0 0 1 0-62h146v-146a31 31 0 0 1 62 0v146h146a31 31 0 0 1 0 62h-146v146z m-31 281c-253 0-458-205-458-458s205-458 458-458 458 205 458 458-205 458-458 458z m-396-458a396 396 0 1 0 792 0 396 396 0 0 0-792 0z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="minus" unicode="-" d="M104 350a396 396 0 1 0 792 0 396 396 0 0 0-792 0z m396 458c-253 0-458-205-458-458s205-458 458-458 458 205 458 458-205 458-458 458z m260-489a31 31 0 0 1 0 62h-520a31 31 0 0 1 0-62h520z" horiz-adv-x="1000" />
|
||||||
|
</font>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
BIN
tests/assets/webfont/iconfont.woff2
Normal file
BIN
tests/assets/webfont/iconfont.woff2
Normal file
Binary file not shown.
18
tests/assets/webfont/webfont.html
Normal file
18
tests/assets/webfont/webfont.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: 'pwtest-iconfont';
|
||||||
|
src: url('./iconfont.woff2') format('woff2');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'pwtest-iconfont';
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<span>+-</span>
|
||||||
|
|
@ -710,5 +710,27 @@ it.describe('page screenshot animations', () => {
|
|||||||
'onfinish', 'animationend'
|
'onfinish', 'animationend'
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should respect fonts option', async ({ page, server }) => {
|
||||||
|
await page.setViewportSize({ width: 500, height: 500 });
|
||||||
|
let serverRequest, serverResponse;
|
||||||
|
// Stall font loading.
|
||||||
|
server.setRoute('/webfont/iconfont.woff2', (req, res) => {
|
||||||
|
serverRequest = req;
|
||||||
|
serverResponse = res;
|
||||||
|
});
|
||||||
|
await page.goto(server.PREFIX + '/webfont/webfont.html', {
|
||||||
|
waitUntil: 'domcontentloaded', // 'load' will not happen if webfont is pending
|
||||||
|
});
|
||||||
|
// Make sure we can take screenshot.
|
||||||
|
const noIconsScreenshot = await page.screenshot();
|
||||||
|
const [iconsScreenshot] = await Promise.all([
|
||||||
|
page.screenshot({ fonts: 'ready' }),
|
||||||
|
server.serveFile(serverRequest, serverResponse),
|
||||||
|
]);
|
||||||
|
expect(iconsScreenshot).toMatchSnapshot('screenshot-web-font.png');
|
||||||
|
expect(noIconsScreenshot).not.toMatchSnapshot('screenshot-web-font.png');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
Loading…
x
Reference in New Issue
Block a user