feat: restore toHaveScreenshot as experimental feature (#13549)

- Restore docs.
- Make `TestConfig.expect` generated.
- Allow experimental properties with "e" marker: `- foo e<float>`.
This commit is contained in:
Dmitry Gozman 2022-04-14 13:22:42 -07:00 committed by GitHub
parent 11179982fc
commit c86c2e8762
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 871 additions and 263 deletions

View File

@ -998,6 +998,44 @@ Property value.
### option: LocatorAssertions.toHaveJSProperty.timeout = %%-js-assertions-timeout-%%
### option: LocatorAssertions.toHaveJSProperty.timeout = %%-csharp-java-python-assertions-timeout-%%
## async method: LocatorAssertions.toHaveScreenshot
* langs: js
* experimental
Ensures that [Locator] resolves to a given screenshot. This function will re-take
screenshots until it matches with the saved expectation.
If there's no expectation yet, it will wait until two consecutive screenshots
yield the same result, and save the last one as an expectation.
```js
const locator = page.locator('button');
await expect(locator).toHaveScreenshot();
```
### option: LocatorAssertions.toHaveScreenshot.timeout = %%-js-assertions-timeout-%%
### option: LocatorAssertions.toHaveScreenshot.timeout = %%-csharp-java-python-assertions-timeout-%%
### option: LocatorAssertions.toHaveScreenshot.animations = %%-screenshot-option-animations-%%
### option: LocatorAssertions.toHaveScreenshot.caret = %%-screenshot-option-caret-%%
### option: LocatorAssertions.toHaveScreenshot.fonts = %%-screenshot-option-fonts-%%
### option: LocatorAssertions.toHaveScreenshot.mask = %%-screenshot-option-mask-%%
### option: LocatorAssertions.toHaveScreenshot.omitBackground = %%-screenshot-option-omit-background-%%
### option: LocatorAssertions.toHaveScreenshot.scale = %%-screenshot-option-scale-%%
### option: LocatorAssertions.toHaveScreenshot.maxDiffPixels = %%-assertions-max-diff-pixels-%%
### option: LocatorAssertions.toHaveScreenshot.maxDiffPixelRatio = %%-assertions-max-diff-pixel-ratio-%%
### option: LocatorAssertions.toHaveScreenshot.threshold = %%-assertions-threshold-%%
## async method: LocatorAssertions.toHaveText
* langs:
- alias-java: hasText

View File

@ -114,6 +114,47 @@ Expected substring or RegExp.
### option: PageAssertions.NotToHaveURL.timeout = %%-js-assertions-timeout-%%
### option: PageAssertions.NotToHaveURL.timeout = %%-csharp-java-python-assertions-timeout-%%
## async method: PageAssertions.toHaveScreenshot
* langs: js
* experimental
Ensures that the page resolves to a given screenshot. This function will re-take
screenshots until it matches with the saved expectation.
If there's no expectation yet, it will wait until two consecutive screenshots
yield the same result, and save the last one as an expectation.
```js
await expect(page).toHaveScreenshot();
```
### option: PageAssertions.toHaveScreenshot.timeout = %%-js-assertions-timeout-%%
### option: PageAssertions.toHaveScreenshot.timeout = %%-csharp-java-python-assertions-timeout-%%
### option: PageAssertions.toHaveScreenshot.animations = %%-screenshot-option-animations-%%
### option: PageAssertions.toHaveScreenshot.caret = %%-screenshot-option-caret-%%
### option: PageAssertions.toHaveScreenshot.clip = %%-screenshot-option-clip-%%
### option: PageAssertions.toHaveScreenshot.fonts = %%-screenshot-option-fonts-%%
### option: PageAssertions.toHaveScreenshot.fullPage = %%-screenshot-option-full-page-%%
### option: PageAssertions.toHaveScreenshot.mask = %%-screenshot-option-mask-%%
### option: PageAssertions.toHaveScreenshot.omitBackground = %%-screenshot-option-omit-background-%%
### option: PageAssertions.toHaveScreenshot.scale = %%-screenshot-option-scale-%%
### option: PageAssertions.toHaveScreenshot.maxDiffPixels = %%-assertions-max-diff-pixels-%%
### option: PageAssertions.toHaveScreenshot.maxDiffPixelRatio = %%-assertions-max-diff-pixel-ratio-%%
### option: PageAssertions.toHaveScreenshot.threshold = %%-assertions-threshold-%%
## async method: PageAssertions.toHaveTitle
* langs:
- alias-java: hasTitle

View File

@ -34,9 +34,17 @@ export default config;
```
## property: TestConfig.expect
- type: <[Object]>
- `timeout` <[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
- `toMatchSnapshot` <[Object]>
- type: ?<[Object]>
- `timeout` ?<[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
- `toHaveScreenshot` ?e<[Object]> Configuration for the [`method: PageAssertions.toHaveScreenshot`] method.
- `threshold` ?<[float]> 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). Defaults to `0.2`.
- `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default.
- `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
- `animations` ?<[ScreenshotAnimations]<"allow"|"disable">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disable"`.
- `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`.
- `fonts` ?<[ScreenshotFonts]<"ready"|"nowait">> See [`option: fonts`] in [`method: Page.screenshot`]. Defaults to `"ready"`.
- `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`.
- `toMatchSnapshot` ?<[Object]> Configuration for the [`method: ScreenshotAssertions.toMatchSnapshot#1`] method.
- `threshold` ?<[float]> 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). Defaults to `0.2`.
- `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default.
- `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
@ -408,6 +416,42 @@ const config: PlaywrightTestConfig = {
export default config;
```
## property: TestConfig.screenshotsDir
* experimental
- type: ?<[string]>
The base directory, relative to the config file, for screenshot files created with [`method: PageAssertions.toHaveScreenshot`]. Defaults to
```
<directory-of-configuration-file>/__screenshots__/<platform name>/<project name>
```
This path will serve as the base directory for each test file screenshot directory. For example, the following test structure:
```
smoke-tests/
└── basic.spec.ts
```
will result in the following screenshots folder structure:
```
__screenshots__/
└── darwin/
├── Mobile Safari/
│ └── smoke-tests/
│ └── basic.spec.ts/
│ └── screenshot-expectation.png
└── Desktop Chrome/
└── smoke-tests/
└── basic.spec.ts/
└── screenshot-expectation.png
```
where:
* `darwin/` - a platform name folder
* `Mobile Safari` and `Desktop Chrome` - project names
## property: TestConfig.shard
- type: <[Object]>
- `total` <[int]> The total number of shards.

View File

@ -105,12 +105,20 @@ export default config;
```
## property: TestProject.expect
- type: <[Object]>
- `timeout` <[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
- `toMatchSnapshot` <[Object]>
- `threshold` <[float]> 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). Defaults to `0.2`.
- `maxDiffPixels` <[int]> an acceptable amount of pixels that could be different, unset by default.
- `maxDiffPixelRatio` <[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
- type: ?<[Object]>
- `timeout` ?<[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
- `toHaveScreenshot` ?e<[Object]> Configuration for the [`method: PageAssertions.toHaveScreenshot`] method.
- `threshold` ?<[float]> 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). Defaults to `0.2`.
- `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default.
- `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
- `animations` ?<[ScreenshotAnimations]<"allow"|"disable">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disable"`.
- `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`.
- `fonts` ?<[ScreenshotFonts]<"ready"|"nowait">> See [`option: fonts`] in [`method: Page.screenshot`]. Defaults to `"ready"`.
- `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`.
- `toMatchSnapshot` ?<[Object]> Configuration for the [`method: ScreenshotAssertions.toMatchSnapshot#1`] method.
- `threshold` ?<[float]> 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). Defaults to `0.2`.
- `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default.
- `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
Configuration for the `expect` assertion library.
@ -149,6 +157,43 @@ Any JSON-serializable metadata that will be put directly to the test report.
Project name is visible in the report and during test execution.
## property: TestProject.screenshotsDir
* experimental
- type: ?<[string]>
The base directory, relative to the config file, for screenshot files created with `toHaveScreenshot`. Defaults to
```
<directory-of-configuration-file>/__screenshots__/<platform name>/<project name>
```
This path will serve as the base directory for each test file screenshot directory. For example, the following test structure:
```
smoke-tests/
└── basic.spec.ts
```
will result in the following screenshots folder structure:
```
__screenshots__/
└── darwin/
├── Mobile Safari/
│ └── smoke-tests/
│ └── basic.spec.ts/
│ └── screenshot-expectation.png
└── Desktop Chrome/
└── smoke-tests/
└── basic.spec.ts/
└── screenshot-expectation.png
```
where:
* `darwin/` - a platform name folder
* `Mobile Safari` and `Desktop Chrome` - project names
## property: TestProject.snapshotDir
- type: <[string]>

View File

@ -44,7 +44,7 @@ import {
toHaveURL,
toHaveValue
} from './matchers/matchers';
import { toMatchSnapshot, toHaveScreenshot as _toHaveScreenshot } from './matchers/toMatchSnapshot';
import { toMatchSnapshot, toHaveScreenshot } from './matchers/toMatchSnapshot';
import type { Expect } from './types';
import { currentTestInfo } from './globals';
import { serializeError, captureStackTrace, currentExpectTimeout } from './util';
@ -142,7 +142,7 @@ const customMatchers = {
toHaveURL,
toHaveValue,
toMatchSnapshot,
_toHaveScreenshot,
toHaveScreenshot,
};
type Generator = () => any;

View File

@ -16,6 +16,7 @@
import type { FullConfigInternal, GlobalInfo } from './types';
import { normalizeAndSaveAttachment } from './util';
import fs from 'fs';
export class GlobalInfoImpl implements GlobalInfo {
private _fullConfig: FullConfigInternal;
private _attachments: { name: string; path?: string | undefined; body?: Buffer | undefined; contentType: string; }[] = [];

View File

@ -25,8 +25,8 @@ import * as path from 'path';
import * as url from 'url';
import * as fs from 'fs';
import { ProjectImpl } from './project';
import type { Reporter } from '../types/testReporter';
import type { BuiltInReporter } from './runner';
import type { Reporter } from '../types/testReporter';
import { builtInReporters } from './runner';
import { isRegExp } from 'playwright-core/lib/utils';
import { serializeError } from './util';
@ -92,8 +92,8 @@ export class Loader {
config.testDir = path.resolve(configDir, config.testDir);
if (config.outputDir !== undefined)
config.outputDir = path.resolve(configDir, config.outputDir);
if ((config as any)._screenshotsDir !== undefined)
(config as any)._screenshotsDir = path.resolve(configDir, (config as any)._screenshotsDir);
if ((config as any).screenshotsDir !== undefined)
(config as any).screenshotsDir = path.resolve(configDir, (config as any).screenshotsDir);
if (config.snapshotDir !== undefined)
config.snapshotDir = path.resolve(configDir, config.snapshotDir);
@ -210,8 +210,8 @@ export class Loader {
projectConfig.testDir = path.resolve(this._configDir, projectConfig.testDir);
if (projectConfig.outputDir !== undefined)
projectConfig.outputDir = path.resolve(this._configDir, projectConfig.outputDir);
if ((projectConfig as any)._screenshotsDir !== undefined)
(projectConfig as any)._screenshotsDir = path.resolve(this._configDir, (projectConfig as any)._screenshotsDir);
if ((projectConfig as any).screenshotsDir !== undefined)
(projectConfig as any).screenshotsDir = path.resolve(this._configDir, (projectConfig as any).screenshotsDir);
if (projectConfig.snapshotDir !== undefined)
projectConfig.snapshotDir = path.resolve(this._configDir, projectConfig.snapshotDir);
@ -220,7 +220,7 @@ export class Loader {
const outputDir = takeFirst(this._configOverrides.outputDir, projectConfig.outputDir, config.outputDir, path.join(throwawayArtifactsPath, 'test-results'));
const snapshotDir = takeFirst(this._configOverrides.snapshotDir, projectConfig.snapshotDir, config.snapshotDir, testDir);
const name = takeFirst(this._configOverrides.name, projectConfig.name, config.name, '');
const screenshotsDir = takeFirst((this._configOverrides as any)._screenshotsDir, (projectConfig as any)._screenshotsDir, (config as any)._screenshotsDir, path.join(testDir, '__screenshots__', process.platform, name));
const screenshotsDir = takeFirst((this._configOverrides as any).screenshotsDir, (projectConfig as any).screenshotsDir, (config as any).screenshotsDir, path.join(testDir, '__screenshots__', process.platform, name));
const fullProject: FullProjectInternal = {
fullyParallel: takeFirst(this._configOverrides.fullyParallel, projectConfig.fullyParallel, config.fullyParallel, undefined),
expect: takeFirst(this._configOverrides.expect, projectConfig.expect, config.expect, undefined),
@ -478,7 +478,6 @@ const baseFullConfig: FullConfigInternal = {
_globalOutputDir: path.resolve(process.cwd()),
_configDir: '',
_testGroupsCount: 0,
_screenshotsDir: '',
};
function resolveReporters(reporters: Config['reporter'], rootDir: string): ReporterDescription[]|undefined {

View File

@ -17,7 +17,7 @@
import type { Locator, Page } from 'playwright-core';
import type { Page as PageEx } from 'playwright-core/lib/client/page';
import type { Locator as LocatorEx } from 'playwright-core/lib/client/locator';
import type { Expect } from '../types';
import type { Expect, UpdateSnapshots } from '../types';
import { currentTestInfo } from '../globals';
import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils/comparators';
import { getComparator } from 'playwright-core/lib/utils/comparators';
@ -26,7 +26,6 @@ import {
addSuffixToFilePath, serializeError, sanitizeForFilePath,
trimLongString, callLogText, currentExpectTimeout,
expectTypes, captureStackTrace } from '../util';
import type { UpdateSnapshots } from '../types';
import colors from 'colors/safe';
import fs from 'fs';
import path from 'path';
@ -290,10 +289,12 @@ export async function toHaveScreenshot(
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & HaveScreenshotOptions = {},
optOptions: HaveScreenshotOptions = {}
): Promise<SyncExpectationResult> {
if (!process.env.PLAYWRIGHT_EXPERIMENTAL_FEATURES)
throw new Error(`To use the experimental method "toHaveScreenshot", set PLAYWRIGHT_EXPERIMENTAL_FEATURES=1 enviroment variable.`);
const testInfo = currentTestInfo();
if (!testInfo)
throw new Error(`toHaveScreenshot() must be called during the test`);
const config = (testInfo.project.expect as any)?._toHaveScreenshot;
const config = (testInfo.project.expect as any)?.toHaveScreenshot;
const helper = new SnapshotHelper(
testInfo, testInfo._screenshotPath.bind(testInfo), 'png',
{

View File

@ -48,7 +48,6 @@ export interface FullConfigInternal extends FullConfigPublic {
_globalOutputDir: string;
_configDir: string;
_testGroupsCount: number;
_screenshotsDir: string;
// Overrides the public field.
projects: FullProjectInternal[];

View File

@ -36,26 +36,6 @@ export type UpdateSnapshots = 'all' | 'none' | 'missing';
type UseOptions<TestArgs, WorkerArgs> = { [K in keyof WorkerArgs]?: WorkerArgs[K] } & { [K in keyof TestArgs]?: TestArgs[K] };
type ExpectSettings = {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
toMatchSnapshot?: {
/** An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between pixels in compared images, between zero (strict) and one (lax). Defaults to `0.2`.
*/
threshold?: number,
/**
* An acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number,
/**
* An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number,
}
};
/**
* Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple
* configurations. For example, consider running tests against multiple browsers.
@ -116,13 +96,6 @@ type ExpectSettings = {
*
*/
interface TestProject {
/**
* Configuration for the `expect` assertion library.
*
* Use [testConfig.expect](https://playwright.dev/docs/api/class-testconfig#test-config-expect) to change this option for
* all projects.
*/
expect?: ExpectSettings;
/**
* Playwright Test runs tests in parallel. In order to achieve that, it runs several worker processes that run at the same
* time. By default, **test files** are run in parallel. Tests in a single file are run in order, in the same worker
@ -288,7 +261,41 @@ interface TestProject {
* all projects.
*/
timeout?: number;
}
/**
* Configuration for the `expect` assertion library.
*
* Use [testConfig.expect](https://playwright.dev/docs/api/class-testconfig#test-config-expect) to change this option for
* all projects.
*/
expect?: {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
/**
* Configuration for the
* [screenshotAssertions.toMatchSnapshot(name[, options])](https://playwright.dev/docs/api/class-screenshotassertions#screenshot-assertions-to-match-snapshot-1)
* method.
*/
toMatchSnapshot?: {
/**
* 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). Defaults to `0.2`.
*/
threshold?: number;
/**
* an acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number;
/**
* an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number;
};
};}
/**
* Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple
@ -687,26 +694,6 @@ interface TestConfig {
*/
workers?: number;
/**
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
*
* ```ts
* // playwright.config.ts
* import { PlaywrightTestConfig } from '@playwright/test';
*
* const config: PlaywrightTestConfig = {
* expect: {
* timeout: 10000,
* toMatchSnapshot: {
* maxDiffPixels: 10,
* },
* },
* };
* export default config;
* ```
*
*/
expect?: ExpectSettings;
/**
* Any JSON-serializable metadata that will be put directly to the test report.
*/
@ -854,7 +841,54 @@ interface TestConfig {
*
*/
timeout?: number;
}
/**
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
*
* ```ts
* // playwright.config.ts
* import { PlaywrightTestConfig } from '@playwright/test';
*
* const config: PlaywrightTestConfig = {
* expect: {
* timeout: 10000,
* toMatchSnapshot: {
* maxDiffPixels: 10,
* },
* },
* };
* export default config;
* ```
*
*/
expect?: {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
/**
* Configuration for the
* [screenshotAssertions.toMatchSnapshot(name[, options])](https://playwright.dev/docs/api/class-screenshotassertions#screenshot-assertions-to-match-snapshot-1)
* method.
*/
toMatchSnapshot?: {
/**
* 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). Defaults to `0.2`.
*/
threshold?: number;
/**
* an acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number;
/**
* an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number;
};
};}
/**
* Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or

View File

@ -16164,26 +16164,6 @@ export type UpdateSnapshots = 'all' | 'none' | 'missing';
type UseOptions<TestArgs, WorkerArgs> = { [K in keyof WorkerArgs]?: WorkerArgs[K] } & { [K in keyof TestArgs]?: TestArgs[K] };
type ExpectSettings = {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
toMatchSnapshot?: {
/** An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between pixels in compared images, between zero (strict) and one (lax). Defaults to `0.2`.
*/
threshold?: number,
/**
* An acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number,
/**
* An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number,
}
};
/**
* Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple
* configurations. For example, consider running tests against multiple browsers.
@ -16244,13 +16224,6 @@ type ExpectSettings = {
*
*/
interface TestProject {
/**
* Configuration for the `expect` assertion library.
*
* Use [testConfig.expect](https://playwright.dev/docs/api/class-testconfig#test-config-expect) to change this option for
* all projects.
*/
expect?: ExpectSettings;
/**
* Playwright Test runs tests in parallel. In order to achieve that, it runs several worker processes that run at the same
* time. By default, **test files** are run in parallel. Tests in a single file are run in order, in the same worker
@ -16416,7 +16389,124 @@ interface TestProject {
* all projects.
*/
timeout?: number;
}
/**
* Configuration for the `expect` assertion library.
*
* Use [testConfig.expect](https://playwright.dev/docs/api/class-testconfig#test-config-expect) to change this option for
* all projects.
*/
expect?: {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
/**
* Configuration for the
* [pageAssertions.toHaveScreenshot([options])](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot)
* method.
*/
toHaveScreenshot?: {
/**
* 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). Defaults to `0.2`.
*/
threshold?: number;
/**
* an acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number;
/**
* an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number;
/**
* See `animations` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults
* to `"disable"`.
*/
animations?: "allow"|"disable";
/**
* See `caret` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to
* `"hide"`.
*/
caret?: "hide"|"initial";
/**
* See `fonts` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to
* `"ready"`.
*/
fonts?: "ready"|"nowait";
/**
* See `scale` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to
* `"css"`.
*/
scale?: "css"|"device";
};
/**
* Configuration for the
* [screenshotAssertions.toMatchSnapshot(name[, options])](https://playwright.dev/docs/api/class-screenshotassertions#screenshot-assertions-to-match-snapshot-1)
* method.
*/
toMatchSnapshot?: {
/**
* 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). Defaults to `0.2`.
*/
threshold?: number;
/**
* an acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number;
/**
* an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number;
};
};
/**
* The base directory, relative to the config file, for screenshot files created with `toHaveScreenshot`. Defaults to
*
* ```
* <directory-of-configuration-file>/__screenshots__/<platform name>/<project name>
* ```
*
* This path will serve as the base directory for each test file screenshot directory. For example, the following test
* structure:
*
* ```
* smoke-tests/
* basic.spec.ts
* ```
*
* will result in the following screenshots folder structure:
*
* ```
* __screenshots__/
* darwin/
* Mobile Safari/
* smoke-tests/
* basic.spec.ts/
* screenshot-expectation.png
* Desktop Chrome/
* smoke-tests/
* basic.spec.ts/
* screenshot-expectation.png
* ```
*
* where:
* - `darwin/` - a platform name folder
* - `Mobile Safari` and `Desktop Chrome` - project names
*/
screenshotsDir?: string;}
/**
* Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple
@ -16815,26 +16905,6 @@ interface TestConfig {
*/
workers?: number;
/**
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
*
* ```ts
* // playwright.config.ts
* import { PlaywrightTestConfig } from '@playwright/test';
*
* const config: PlaywrightTestConfig = {
* expect: {
* timeout: 10000,
* toMatchSnapshot: {
* maxDiffPixels: 10,
* },
* },
* };
* export default config;
* ```
*
*/
expect?: ExpectSettings;
/**
* Any JSON-serializable metadata that will be put directly to the test report.
*/
@ -16982,7 +17052,139 @@ interface TestConfig {
*
*/
timeout?: number;
}
/**
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
*
* ```ts
* // playwright.config.ts
* import { PlaywrightTestConfig } from '@playwright/test';
*
* const config: PlaywrightTestConfig = {
* expect: {
* timeout: 10000,
* toMatchSnapshot: {
* maxDiffPixels: 10,
* },
* },
* };
* export default config;
* ```
*
*/
expect?: {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
/**
* Configuration for the
* [pageAssertions.toHaveScreenshot([options])](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot)
* method.
*/
toHaveScreenshot?: {
/**
* 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). Defaults to `0.2`.
*/
threshold?: number;
/**
* an acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number;
/**
* an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number;
/**
* See `animations` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults
* to `"disable"`.
*/
animations?: "allow"|"disable";
/**
* See `caret` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to
* `"hide"`.
*/
caret?: "hide"|"initial";
/**
* See `fonts` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to
* `"ready"`.
*/
fonts?: "ready"|"nowait";
/**
* See `scale` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to
* `"css"`.
*/
scale?: "css"|"device";
};
/**
* Configuration for the
* [screenshotAssertions.toMatchSnapshot(name[, options])](https://playwright.dev/docs/api/class-screenshotassertions#screenshot-assertions-to-match-snapshot-1)
* method.
*/
toMatchSnapshot?: {
/**
* 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). Defaults to `0.2`.
*/
threshold?: number;
/**
* an acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number;
/**
* an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number;
};
};
/**
* The base directory, relative to the config file, for screenshot files created with
* [pageAssertions.toHaveScreenshot([options])](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot).
* Defaults to
*
* ```
* <directory-of-configuration-file>/__screenshots__/<platform name>/<project name>
* ```
*
* This path will serve as the base directory for each test file screenshot directory. For example, the following test
* structure:
*
* ```
* smoke-tests/
* basic.spec.ts
* ```
*
* will result in the following screenshots folder structure:
*
* ```
* __screenshots__/
* darwin/
* Mobile Safari/
* smoke-tests/
* basic.spec.ts/
* screenshot-expectation.png
* Desktop Chrome/
* smoke-tests/
* basic.spec.ts/
* screenshot-expectation.png
* ```
*
* where:
* - `darwin/` - a platform name folder
* - `Mobile Safari` and `Desktop Chrome` - project names
*/
screenshotsDir?: string;}
/**
* Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or
@ -19496,6 +19698,88 @@ interface LocatorAssertions {
timeout?: number;
}): Promise<void>;
/**
* Ensures that [Locator] resolves to a given screenshot. This function will re-take screenshots until it matches with the
* saved expectation.
*
* If there's no expectation yet, it will wait until two consecutive screenshots yield the same result, and save the last
* one as an expectation.
*
* ```js
* const locator = page.locator('button');
* await expect(locator).toHaveScreenshot();
* ```
*
* @param options
*/
toHaveScreenshot(options?: {
/**
* When set to `"disabled"`, stops CSS animations, CSS transitions and Web Animations. Animations get different treatment
* depending on their duration:
* - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.
* - infinite animations are canceled to initial state, and then played over after the screenshot.
*
* Defaults to `"allow"` that leaves animations untouched.
*/
animations?: "disabled"|"allow";
/**
* When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`, text caret behavior will not be changed.
* Defaults to `"hide"`.
*/
caret?: "hide"|"initial";
/**
* 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
* `#FF00FF` that completely covers its bounding box.
*/
mask?: Array<Locator>;
/**
* An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1`. Default is
* configurable with `TestConfig.expect`. Unset by default.
*/
maxDiffPixelRatio?: number;
/**
* An acceptable amount of pixels that could be different, default is configurable with `TestConfig.expect`. Default is
* configurable with `TestConfig.expect`. Unset by default.
*/
maxDiffPixels?: number;
/**
* Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.
* Defaults to `false`.
*/
omitBackground?: boolean;
/**
* 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"`.
*/
scale?: "css"|"device";
/**
* 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 `TestConfig.expect`.
* Defaults to `0.2`.
*/
threshold?: number;
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an element with the given text. You can use regular expressions for the value as well.
*
@ -19575,6 +19859,118 @@ interface PageAssertions {
*/
not: PageAssertions;
/**
* Ensures that the page resolves to a given screenshot. This function will re-take screenshots until it matches with the
* saved expectation.
*
* If there's no expectation yet, it will wait until two consecutive screenshots yield the same result, and save the last
* one as an expectation.
*
* ```js
* await expect(page).toHaveScreenshot();
* ```
*
* @param options
*/
toHaveScreenshot(options?: {
/**
* When set to `"disabled"`, stops CSS animations, CSS transitions and Web Animations. Animations get different treatment
* depending on their duration:
* - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.
* - infinite animations are canceled to initial state, and then played over after the screenshot.
*
* Defaults to `"allow"` that leaves animations untouched.
*/
animations?: "disabled"|"allow";
/**
* When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`, text caret behavior will not be changed.
* Defaults to `"hide"`.
*/
caret?: "hide"|"initial";
/**
* An object which specifies clipping of the resulting image. Should have the following fields:
*/
clip?: {
/**
* x-coordinate of top-left corner of clip area
*/
x: number;
/**
* y-coordinate of top-left corner of clip area
*/
y: number;
/**
* width of clipping area
*/
width: number;
/**
* height of clipping area
*/
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
* `false`.
*/
fullPage?: boolean;
/**
* 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.
*/
mask?: Array<Locator>;
/**
* An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1`. Default is
* configurable with `TestConfig.expect`. Unset by default.
*/
maxDiffPixelRatio?: number;
/**
* An acceptable amount of pixels that could be different, default is configurable with `TestConfig.expect`. Default is
* configurable with `TestConfig.expect`. Unset by default.
*/
maxDiffPixels?: number;
/**
* Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.
* Defaults to `false`.
*/
omitBackground?: boolean;
/**
* 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"`.
*/
scale?: "css"|"device";
/**
* 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 `TestConfig.expect`.
* Defaults to `0.2`.
*/
threshold?: number;
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the page has the given title.
*

View File

@ -181,7 +181,7 @@ test('should include multiple image diffs', async ({ runInlineTest, page, showRe
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = {
_screenshotsDir: '__screenshots__',
screenshotsDir: '__screenshots__',
use: { viewport: { width: ${IMG_WIDTH}, height: ${IMG_HEIGHT} }}
};
`,
@ -192,12 +192,12 @@ test('should include multiple image diffs', async ({ runInlineTest, page, showRe
const { test } = pwt;
test('fails', async ({ page }, testInfo) => {
testInfo.snapshotSuffix = '';
await expect.soft(page)._toHaveScreenshot({ timeout: 1000 });
await expect.soft(page)._toHaveScreenshot({ timeout: 1000 });
await expect.soft(page)._toHaveScreenshot({ timeout: 1000 });
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
});
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never', PLAYWRIGHT_EXPERIMENTAL_FEATURES: '1' });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
@ -260,10 +260,10 @@ test('should include image diff when screenshot failed to generate due to animat
document.body.textContent = Date.now();
}, 50);
});
await expect.soft(page)._toHaveScreenshot({ timeout: 1000 });
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
});
`,
}, { 'reporter': 'dot,html', 'update-snapshots': true }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
}, { 'reporter': 'dot,html', 'update-snapshots': true }, { PW_TEST_HTML_REPORT_OPEN: 'never', PLAYWRIGHT_EXPERIMENTAL_FEATURES: '1' });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);

View File

@ -37,7 +37,7 @@ test('should fail to screenshot a page with infinite animation', async ({ runInl
const result = await runInlineTest({
...playwrightConfig({
expect: {
_toHaveScreenshot: {
toHaveScreenshot: {
animations: 'allow',
},
},
@ -45,7 +45,7 @@ test('should fail to screenshot a page with infinite animation', async ({ runInl
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page)._toHaveScreenshot({ timeout: 2000 });
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
});
@ -67,7 +67,7 @@ test('should disable animations by default', async ({ runInlineTest }, testInfo)
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await page.goto('${cssTransitionURL}');
await expect(page)._toHaveScreenshot({ timeout: 2000 });
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
}, { 'update-snapshots': true });
@ -76,7 +76,7 @@ test('should disable animations by default', async ({ runInlineTest }, testInfo)
test('should have scale:css by default', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ browser }) => {
const context = await browser.newContext({
@ -84,7 +84,7 @@ test('should have scale:css by default', async ({ runInlineTest }, testInfo) =>
deviceScaleFactor: 2,
});
const page = await context.newPage();
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
await context.close();
});
`
@ -95,19 +95,19 @@ test('should have scale:css by default', async ({ runInlineTest }, testInfo) =>
expect(pngComparator(fs.readFileSync(snapshotOutputPath), whiteImage)).toBe(null);
});
test('should ignore non-documented options in _toHaveScreenshot config', async ({ runInlineTest }, testInfo) => {
test('should ignore non-documented options in toHaveScreenshot config', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({
_screenshotsDir: '__screenshots__',
screenshotsDir: '__screenshots__',
expect: {
_toHaveScreenshot: {
toHaveScreenshot: {
clip: { x: 0, y: 0, width: 10, height: 10 },
},
},
}),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
@ -127,17 +127,17 @@ test('screenshotPath should include platform and project name by default', async
}),
'a.spec.js': `
pwt.test('is a test', async ({ page }, testInfo) => {
await pwt.expect(page)._toHaveScreenshot('snapshot.png');
await pwt.expect(page).toHaveScreenshot('snapshot.png');
});
`,
'foo/b.spec.js': `
pwt.test('is a test', async ({ page }, testInfo) => {
await pwt.expect(page)._toHaveScreenshot('snapshot.png');
await pwt.expect(page).toHaveScreenshot('snapshot.png');
});
`,
'foo/bar/baz/c.spec.js': `
pwt.test('is a test', async ({ page }, testInfo) => {
await pwt.expect(page)._toHaveScreenshot('snapshot.png');
await pwt.expect(page).toHaveScreenshot('snapshot.png');
});
`,
}, { 'update-snapshots': true });
@ -147,7 +147,7 @@ test('screenshotPath should include platform and project name by default', async
expect(fs.existsSync(testInfo.outputPath('__screenshots__', process.platform, PROJECT_NAME, 'foo', 'bar', 'baz', 'c.spec.js', 'snapshot.png'))).toBeTruthy();
});
test('should report _toHaveScreenshot step with expectation name in title', async ({ runInlineTest }) => {
test('should report toHaveScreenshot step with expectation name in title', async ({ runInlineTest }) => {
const result = await runInlineTest({
'reporter.ts': `
class Reporter {
@ -161,9 +161,9 @@ test('should report _toHaveScreenshot step with expectation name in title', asyn
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
// Named expectation.
await expect(page)._toHaveScreenshot('foo.png', { timeout: 2000 });
await expect(page).toHaveScreenshot('foo.png', { timeout: 2000 });
// Anonymous expectation.
await expect(page)._toHaveScreenshot({ timeout: 2000 });
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
}, { 'reporter': '', 'workers': 1, 'update-snapshots': true });
@ -172,8 +172,8 @@ test('should report _toHaveScreenshot step with expectation name in title', asyn
expect(result.output.split('\n').filter(line => line.startsWith('%%'))).toEqual([
`%% end browserContext.newPage`,
`%% end Before Hooks`,
`%% end expect._toHaveScreenshot(foo.png)`,
`%% end expect._toHaveScreenshot(is-a-test-1.png)`,
`%% end expect.toHaveScreenshot(foo.png)`,
`%% end expect.toHaveScreenshot(is-a-test-1.png)`,
`%% end browserContext.close`,
`%% end After Hooks`,
]);
@ -183,14 +183,14 @@ test('should not fail when racing with navigation', async ({ runInlineTest }, te
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({
_screenshotsDir: '__screenshots__',
screenshotsDir: '__screenshots__',
}),
'__screenshots__/a.spec.js/snapshot.png': createImage(10, 10, 255, 0, 0),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await Promise.all([
page.goto('${infiniteAnimationURL}'),
expect(page)._toHaveScreenshot({
expect(page).toHaveScreenshot({
name: 'snapshot.png',
animations: "disabled",
clip: { x: 0, y: 0, width: 10, height: 10 },
@ -205,11 +205,11 @@ test('should not fail when racing with navigation', async ({ runInlineTest }, te
test('should successfully screenshot a page with infinite animation with disableAnimation: true', async ({ runInlineTest }, testInfo) => {
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page)._toHaveScreenshot({
await expect(page).toHaveScreenshot({
animations: "disabled",
});
});
@ -221,11 +221,11 @@ test('should successfully screenshot a page with infinite animation with disable
test('should support clip option for page', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': createImage(50, 50, 255, 255, 255),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot({
await expect(page).toHaveScreenshot({
name: 'snapshot.png',
clip: { x: 0, y: 0, width: 50, height: 50, },
});
@ -237,14 +237,14 @@ test('should support clip option for page', async ({ runInlineTest }, testInfo)
test('should support omitBackground option for locator', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await page.evaluate(() => {
document.body.style.setProperty('width', '100px');
document.body.style.setProperty('height', '100px');
});
await expect(page.locator('body'))._toHaveScreenshot({
await expect(page.locator('body')).toHaveScreenshot({
name: 'snapshot.png',
omitBackground: true,
});
@ -267,10 +267,10 @@ test('should fail to screenshot an element with infinite animation', async ({ ru
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({
_screenshotsDir: '__screenshots__',
screenshotsDir: '__screenshots__',
projects: [{
expect: {
_toHaveScreenshot: {
toHaveScreenshot: {
animations: 'allow',
},
},
@ -279,7 +279,7 @@ test('should fail to screenshot an element with infinite animation', async ({ ru
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page.locator('body'))._toHaveScreenshot({ timeout: 2000 });
await expect(page.locator('body')).toHaveScreenshot({ timeout: 2000 });
});
`
});
@ -297,9 +297,9 @@ test('should fail to screenshot an element that keeps moving', async ({ runInlin
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({
_screenshotsDir: '__screenshots__',
screenshotsDir: '__screenshots__',
expect: {
_toHaveScreenshot: {
toHaveScreenshot: {
animations: 'allow',
},
},
@ -307,7 +307,7 @@ test('should fail to screenshot an element that keeps moving', async ({ runInlin
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page.locator('div'))._toHaveScreenshot({ timeout: 2000 });
await expect(page.locator('div')).toHaveScreenshot({ timeout: 2000 });
});
`
});
@ -322,10 +322,10 @@ test('should fail to screenshot an element that keeps moving', async ({ runInlin
test('should generate default name', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot();
await expect(page).toHaveScreenshot();
});
`
});
@ -335,10 +335,11 @@ test('should generate default name', async ({ runInlineTest }, testInfo) => {
});
test('should compile with different option combinations', async ({ runTSC }) => {
test.skip(true, 'Enable when enabling _toHaveScreenshot');
const experimentalPath = path.resolve(__dirname, '..', 'config', 'experimental.d.ts');
const result = await runTSC({
'playwright.config.ts': `
//@no-header
/// <reference path=${JSON.stringify(experimentalPath)} />
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
expect: {
@ -360,7 +361,7 @@ test('should compile with different option combinations', async ({ runTSC }) =>
const { test } = pwt;
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot();
await expect(page.locator('body'))._toHaveScreenshot({ threshold: 0.2 });
await expect(page.locator('body')).toHaveScreenshot({ threshold: 0.2 });
await expect(page).toHaveScreenshot({ maxDiffPixelRatio: 0.2 });
await expect(page).toHaveScreenshot({
threshold: 0.2,
@ -381,11 +382,11 @@ test('should compile with different option combinations', async ({ runTSC }) =>
test('should fail when screenshot is different size', async ({ runInlineTest }) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': createImage(22, 33),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', { timeout: 2000 });
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
@ -400,7 +401,7 @@ test('should fail when given non-png snapshot name', async ({ runInlineTest }) =
...playwrightConfig({}),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.jpeg');
await expect(page).toHaveScreenshot('snapshot.jpeg');
});
`
});
@ -413,7 +414,7 @@ test('should fail when given buffer', async ({ runInlineTest }) => {
...playwrightConfig({}),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(Buffer.from([1]))._toHaveScreenshot();
await expect(Buffer.from([1])).toHaveScreenshot();
});
`
});
@ -423,11 +424,11 @@ test('should fail when given buffer', async ({ runInlineTest }) => {
test('should fail when screenshot is different pixels', async ({ runInlineTest }) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': paintBlackPixels(whiteImage, 12345),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', { timeout: 2000 });
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
@ -442,11 +443,11 @@ test('should fail when screenshot is different pixels', async ({ runInlineTest }
test('doesn\'t create comparison artifacts in an output folder for passed negated snapshot matcher', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': blueImage,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page).not._toHaveScreenshot('snapshot.png');
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
});
@ -463,11 +464,11 @@ test('doesn\'t create comparison artifacts in an output folder for passed negate
test('should fail on same snapshots with negate matcher', async ({ runInlineTest }) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': whiteImage,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page).not._toHaveScreenshot('snapshot.png', { timeout: 2000 });
await expect(page).not.toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
@ -479,11 +480,11 @@ test('should fail on same snapshots with negate matcher', async ({ runInlineTest
test('should write missing expectations locally twice and continue', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page)._toHaveScreenshot('snapshot2.png');
await expect(page).toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot2.png');
console.log('Here we are!');
});
`
@ -509,10 +510,10 @@ test('should write missing expectations locally twice and continue', async ({ ru
test('shouldn\'t write missing expectations locally for negated matcher', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page).not._toHaveScreenshot('snapshot.png');
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
});
@ -525,11 +526,11 @@ test('shouldn\'t write missing expectations locally for negated matcher', async
test('should update snapshot with the update-snapshots flag', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': blueImage,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
@ -543,11 +544,11 @@ test('should update snapshot with the update-snapshots flag', async ({ runInline
test('shouldn\'t update snapshot with the update-snapshots flag for negated matcher', async ({ runInlineTest }, testInfo) => {
const EXPECTED_SNAPSHOT = blueImage;
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page).not._toHaveScreenshot('snapshot.png');
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
@ -559,10 +560,10 @@ test('shouldn\'t update snapshot with the update-snapshots flag for negated matc
test('should silently write missing expectations locally with the update-snapshots flag', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
@ -576,10 +577,10 @@ test('should silently write missing expectations locally with the update-snapsho
test('should not write missing expectations locally with the update-snapshots flag for negated matcher', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page).not._toHaveScreenshot('snapshot.png');
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
@ -592,7 +593,7 @@ test('should not write missing expectations locally with the update-snapshots fl
test('should match multiple snapshots', async ({ runInlineTest }) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/red.png': redImage,
'__screenshots__/a.spec.js/green.png': greenImage,
'__screenshots__/a.spec.js/blue.png': blueImage,
@ -600,15 +601,15 @@ test('should match multiple snapshots', async ({ runInlineTest }) => {
pwt.test('is a test', async ({ page }) => {
await Promise.all([
page.evaluate(() => document.documentElement.style.setProperty('background', '#f00')),
expect(page)._toHaveScreenshot('red.png'),
expect(page).toHaveScreenshot('red.png'),
]);
await Promise.all([
page.evaluate(() => document.documentElement.style.setProperty('background', '#0f0')),
expect(page)._toHaveScreenshot('green.png'),
expect(page).toHaveScreenshot('green.png'),
]);
await Promise.all([
page.evaluate(() => document.documentElement.style.setProperty('background', '#00f')),
expect(page)._toHaveScreenshot('blue.png'),
expect(page).toHaveScreenshot('blue.png'),
]);
});
`
@ -618,11 +619,11 @@ test('should match multiple snapshots', async ({ runInlineTest }) => {
test('should use provided name', async ({ runInlineTest }) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/provided.png': whiteImage,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('provided.png');
await expect(page).toHaveScreenshot('provided.png');
});
`
});
@ -631,11 +632,11 @@ test('should use provided name', async ({ runInlineTest }) => {
test('should use provided name via options', async ({ runInlineTest }) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/provided.png': whiteImage,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot({ name: 'provided.png' });
await expect(page).toHaveScreenshot({ name: 'provided.png' });
});
`
});
@ -647,21 +648,21 @@ test('should respect maxDiffPixels option', async ({ runInlineTest }) => {
const EXPECTED_SNAPSHOT = paintBlackPixels(whiteImage, BAD_PIXELS);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', { timeout: 2000 });
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
})).exitCode, 'make sure default comparison fails').toBe(1);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${BAD_PIXELS}
});
});
@ -672,9 +673,9 @@ test('should respect maxDiffPixels option', async ({ runInlineTest }) => {
...playwrightConfig({
projects: [
{
_screenshotsDir: '__screenshots__',
screenshotsDir: '__screenshots__',
expect: {
_toHaveScreenshot: {
toHaveScreenshot: {
maxDiffPixels: BAD_PIXELS
}
},
@ -684,7 +685,7 @@ test('should respect maxDiffPixels option', async ({ runInlineTest }) => {
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
});
`
})).exitCode, 'make sure maxDiffPixels option in project config is respected').toBe(0);
@ -696,21 +697,21 @@ test('should satisfy both maxDiffPixelRatio and maxDiffPixels', async ({ runInli
const EXPECTED_SNAPSHOT = paintBlackPixels(whiteImage, BAD_COUNT);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', { timeout: 2000 });
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
})).exitCode, 'make sure default comparison fails').toBe(1);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${Math.floor(BAD_COUNT / 2)},
maxDiffPixelRatio: ${BAD_RATIO},
timeout: 2000,
@ -720,11 +721,11 @@ test('should satisfy both maxDiffPixelRatio and maxDiffPixels', async ({ runInli
})).exitCode, 'make sure it fails when maxDiffPixels < actualBadPixels < maxDiffPixelRatio').toBe(1);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${BAD_COUNT},
maxDiffPixelRatio: ${BAD_RATIO / 2},
timeout: 2000,
@ -734,11 +735,11 @@ test('should satisfy both maxDiffPixelRatio and maxDiffPixels', async ({ runInli
})).exitCode, 'make sure it fails when maxDiffPixelRatio < actualBadPixels < maxDiffPixels').toBe(1);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${BAD_COUNT},
maxDiffPixelRatio: ${BAD_RATIO},
});
@ -753,21 +754,21 @@ test('should respect maxDiffPixelRatio option', async ({ runInlineTest }) => {
const EXPECTED_SNAPSHOT = paintBlackPixels(whiteImage, BAD_PIXELS);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', { timeout: 2000 });
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
})).exitCode, 'make sure default comparison fails').toBe(1);
expect((await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixelRatio: ${BAD_RATIO}
});
});
@ -777,9 +778,9 @@ test('should respect maxDiffPixelRatio option', async ({ runInlineTest }) => {
expect((await runInlineTest({
...playwrightConfig({
projects: [{
_screenshotsDir: '__screenshots__',
screenshotsDir: '__screenshots__',
expect: {
_toHaveScreenshot: {
toHaveScreenshot: {
maxDiffPixelRatio: BAD_RATIO,
},
},
@ -788,7 +789,7 @@ test('should respect maxDiffPixelRatio option', async ({ runInlineTest }) => {
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
});
`
})).exitCode, 'make sure maxDiffPixels option in project config is respected').toBe(0);
@ -799,7 +800,7 @@ test('should throw for invalid maxDiffPixels values', async ({ runInlineTest })
...playwrightConfig({}),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot({
await expect(page).toHaveScreenshot({
maxDiffPixels: -1,
});
});
@ -812,7 +813,7 @@ test('should throw for invalid maxDiffPixelRatio values', async ({ runInlineTest
...playwrightConfig({}),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot({
await expect(page).toHaveScreenshot({
maxDiffPixelRatio: 12,
});
});
@ -823,14 +824,14 @@ test('should throw for invalid maxDiffPixelRatio values', async ({ runInlineTest
test('should attach expected/actual and no diff when sizes are different', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({ _screenshotsDir: '__screenshots__' }),
...playwrightConfig({ screenshotsDir: '__screenshots__' }),
'__screenshots__/a.spec.js/snapshot.png': createImage(2, 2),
'a.spec.js': `
pwt.test.afterEach(async ({}, testInfo) => {
console.log('## ' + JSON.stringify(testInfo.attachments));
});
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png', { timeout: 2000 });
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
@ -859,11 +860,11 @@ test('should fail with missing expectations and retries', async ({ runInlineTest
const result = await runInlineTest({
...playwrightConfig({
retries: 1,
_screenshotsDir: '__screenshots__'
screenshotsDir: '__screenshots__'
}),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
});
`
});
@ -880,11 +881,11 @@ test('should update expectations with retries', async ({ runInlineTest }, testIn
const result = await runInlineTest({
...playwrightConfig({
retries: 1,
_screenshotsDir: '__screenshots__'
screenshotsDir: '__screenshots__'
}),
'a.spec.js': `
pwt.test('is a test', async ({ page }) => {
await expect(page)._toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
@ -900,6 +901,7 @@ test('should update expectations with retries', async ({ runInlineTest }, testIn
function playwrightConfig(obj: any) {
return {
'playwright.config.js': `
process.env.PLAYWRIGHT_EXPERIMENTAL_FEATURES = '1';
module.exports = ${JSON.stringify(obj, null, 2)}
`,
};

View File

@ -91,7 +91,7 @@ class ApiParser {
let optional = false;
for (const item of spec.children || []) {
if (item.type === 'li' && item.liType === 'default') {
const parsed = this.parseType(item);;
const parsed = this.parseType(item);
returnType = parsed.type;
optional = parsed.optional;
}
@ -164,6 +164,7 @@ class ApiParser {
method.argsArray.push(arg);
}
} else {
// match[1] === 'option'
let options = method.argsArray.find(o => o.name === 'options');
if (!options) {
const type = new Documentation.Type('Object', []);
@ -183,7 +184,7 @@ class ApiParser {
const param = childrenWithoutProperties(spec)[0];
const text = param.text;
let typeStart = text.indexOf('<');
if (text[typeStart - 1] === '?')
while ('?e'.includes(text[typeStart - 1]))
typeStart--;
const name = text.substring(0, typeStart).replace(/\`/g, '').trim();
const comments = extractComments(spec);
@ -193,7 +194,7 @@ class ApiParser {
/**
* @param {MarkdownNode=} spec
* @return {{ type: Documentation.Type, optional: boolean }}
* @return {{ type: Documentation.Type, optional: boolean, experimental: boolean }}
*/
parseType(spec) {
const arg = parseVariable(spec.text);
@ -202,16 +203,16 @@ class ApiParser {
const { name, text } = parseVariable(child.text);
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
const childType = this.parseType(child);
properties.push(Documentation.Member.createProperty({}, false /* experimental */, name, childType.type, comments, !childType.optional));
properties.push(Documentation.Member.createProperty({}, childType.experimental, name, childType.type, comments, !childType.optional));
}
const type = Documentation.Type.parse(arg.type, properties);
return { type, optional: arg.optional };
return { type, optional: arg.optional, experimental: arg.experimental };
}
}
/**
* @param {string} line
* @returns {{ name: string, type: string, text: string, optional: boolean }}
* @returns {{ name: string, type: string, text: string, optional: boolean, experimental: boolean }}
*/
function parseVariable(line) {
let match = line.match(/^`([^`]+)` (.*)/);
@ -226,8 +227,12 @@ function parseVariable(line) {
const name = match[1];
let remainder = match[2];
let optional = false;
if (remainder.startsWith('?')) {
optional = true;
let experimental = false;
while ('?e'.includes(remainder[0])) {
if (remainder[0] === '?')
optional = true;
else if (remainder[0] === 'e')
experimental = true;
remainder = remainder.substring(1);
}
if (!remainder.startsWith('<'))
@ -240,7 +245,7 @@ function parseVariable(line) {
if (c === '>')
--depth;
if (depth === 0)
return { name, type: remainder.substring(1, i), text: remainder.substring(i + 2), optional };
return { name, type: remainder.substring(1, i), text: remainder.substring(i + 2), optional, experimental };
}
throw new Error('Should not be reached');
}

View File

@ -32,10 +32,10 @@ class TypesGenerator {
/**
* @param {{
* documentation: Documentation,
* classNamesToGenerate: Set<string>,
* overridesToDocsClassMapping?: Map<string, string>,
* ignoreMissing?: Set<string>,
* doNotExportClassNames?: Set<string>,
* doNotGenerate?: Set<string>,
* includeExperimental?: boolean,
* }} options
*/
@ -45,10 +45,10 @@ class TypesGenerator {
/** @type {Set<string>} */
this.handledMethods = new Set();
this.documentation = options.documentation;
this.classNamesToGenerate = options.classNamesToGenerate;
this.overridesToDocsClassMapping = options.overridesToDocsClassMapping || new Map();
this.ignoreMissing = options.ignoreMissing || new Set();
this.doNotExportClassNames = options.doNotExportClassNames || new Set();
this.doNotGenerate = options.doNotGenerate || new Set();
this.documentation.filterForLanguage('js');
if (!options.includeExperimental)
this.documentation.filterOutExperimental();
@ -116,13 +116,15 @@ class TypesGenerator {
return this.memberJSDOC(method, ' ').trimLeft();
}, (className) => {
const docClass = this.docClassForName(className);
if (!docClass || !this.classNamesToGenerate.has(docClass.name))
if (!docClass || !this.shouldGenerate(docClass.name))
return '';
if (docClass.name !== className) // Do not generate members for name-mapped classes.
return '';
return this.classBody(docClass);
});
const classes = this.documentation.classesArray
.filter(cls => this.classNamesToGenerate.has(cls.name))
.filter(cls => this.shouldGenerate(cls.name))
.filter(cls => !handledClasses.has(cls.name));
{
const playwright = this.documentation.classesArray.find(c => c.name === 'Playwright');
@ -151,6 +153,16 @@ class TypesGenerator {
return this.ignoreMissing.has(name) || this.ignoreMissing.has(parts[0]);
}
/**
* @param {string} name
*/
shouldGenerate(name) {
const parts = name.split('.');
// Either the class is skipped, or a specific method.
const skip = this.doNotGenerate.has(name) || this.doNotGenerate.has(parts[0]);
return !skip;
}
/**
* @param {string} name
*/
@ -269,6 +281,8 @@ class TypesGenerator {
const members = classDesc.membersArray.filter(member => member.kind !== 'event');
parts.push(members.map(member => {
if (!this.shouldGenerate(`${classDesc.name}.${member.name}`))
return '';
if (member.kind === 'event')
return '';
if (member.alias === 'waitForEvent') {
@ -490,7 +504,7 @@ class TypesGenerator {
const coreDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
const testDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-api'), path.join(PROJECT_DIR, 'docs', 'src', 'api', 'params.md'));
const reporterDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-reporter-api'));
const assertionClasses = new Set(['LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions']);
const assertionClasses = new Set(['LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions', 'PlaywrightAssertions']);
/**
* @param {boolean} includeExperimental
@ -500,7 +514,7 @@ class TypesGenerator {
const documentation = coreDocumentation.clone();
const generator = new TypesGenerator({
documentation,
classNamesToGenerate: new Set(coreDocumentation.classesArray.map(cls => cls.name).filter(name => !assertionClasses.has(name) && name !== 'PlaywrightAssertions')),
doNotGenerate: new Set([...assertionClasses]),
includeExperimental,
});
let types = await generator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
@ -531,7 +545,15 @@ class TypesGenerator {
const documentation = coreDocumentation.mergeWith(testDocumentation);
const generator = new TypesGenerator({
documentation,
classNamesToGenerate: new Set(['TestError', 'TestInfo', 'WorkerInfo', ...assertionClasses]),
doNotGenerate: new Set([
...coreDocumentation.classesArray.map(cls => cls.name).filter(name => !assertionClasses.has(name)),
'PlaywrightAssertions',
'Test',
'Fixtures',
'TestOptions',
'TestConfig.use',
'TestProject.use',
]),
overridesToDocsClassMapping: new Map([
['TestType', 'Test'],
['Config', 'TestConfig'],
@ -565,7 +587,10 @@ class TypesGenerator {
const documentation = coreDocumentation.mergeWith(testDocumentation).mergeWith(reporterDocumentation);
const generator = new TypesGenerator({
documentation,
classNamesToGenerate: new Set(reporterDocumentation.classesArray.map(cls => cls.name)),
doNotGenerate: new Set([
...coreDocumentation.classesArray.map(cls => cls.name),
...testDocumentation.classesArray.map(cls => cls.name),
]),
ignoreMissing: new Set(['FullResult']),
includeExperimental,
});

View File

@ -35,28 +35,7 @@ export type UpdateSnapshots = 'all' | 'none' | 'missing';
type UseOptions<TestArgs, WorkerArgs> = { [K in keyof WorkerArgs]?: WorkerArgs[K] } & { [K in keyof TestArgs]?: TestArgs[K] };
type ExpectSettings = {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
toMatchSnapshot?: {
/** An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between pixels in compared images, between zero (strict) and one (lax). Defaults to `0.2`.
*/
threshold?: number,
/**
* An acceptable amount of pixels that could be different, unset by default.
*/
maxDiffPixels?: number,
/**
* An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
*/
maxDiffPixelRatio?: number,
}
};
interface TestProject {
expect?: ExpectSettings;
fullyParallel?: boolean;
grep?: RegExp | RegExp[];
grepInvert?: RegExp | RegExp[] | null;
@ -139,7 +118,6 @@ interface TestConfig {
webServer?: WebServerConfig;
workers?: number;
expect?: ExpectSettings;
metadata?: any;
name?: string;
snapshotDir?: string;