chore: generate expect types (#13439)

This commit is contained in:
Dmitry Gozman 2022-04-11 10:42:19 -07:00 committed by GitHub
parent 1ee42a366d
commit 1e1df6395f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 886 additions and 291 deletions

View file

@ -8,9 +8,7 @@ expected values stored in files.
expect(screenshot).toMatchSnapshot('landing-page.png');
```
<!-- TOC -->
## method: ScreenshotAssertions.toMatchSnapshot
## method: ScreenshotAssertions.toMatchSnapshot#1
Ensures that passed value, either a [string] or a [Buffer], matches the expected snapshot stored in the test snapshots directory.
@ -18,11 +16,8 @@ Ensures that passed value, either a [string] or a [Buffer], matches the expected
// Basic usage.
expect(await page.screenshot()).toMatchSnapshot('landing-page.png');
// Basic usage and the file name is derived from the test name.
expect(await page.screenshot()).toMatchSnapshot();
// Pass options to customize the snapshot comparison and have a generated name.
expect(await page.screenshot()).toMatchSnapshot({
expect(await page.screenshot()).toMatchSnapshot('landing-page.png', {
maxDiffPixels: 27, // allow no more than 27 different pixels.
});
@ -36,13 +31,49 @@ expect(await page.screenshot()).toMatchSnapshot(['landing', 'step3.png']);
Learn more about [visual comparisons](./test-snapshots.md).
### param: ScreenshotAssertions.toMatchSnapshot.nameOrOptions
- `nameOrOptions` <[string]|[Array]<[string]>|[Object]>
### param: ScreenshotAssertions.toMatchSnapshot#1.name
- `name` <[string]|[Array]<[string]>>
Optional snapshot name. If not passed, the test name and ordinals are used when called multiple times. Also passing the options here is supported.
Snapshot name.
### option: ScreenshotAssertions.toMatchSnapshot.maxDiffPixels = %%-assertions-max-diff-pixels-%%
### option: ScreenshotAssertions.toMatchSnapshot#1.maxDiffPixels = %%-assertions-max-diff-pixels-%%
### option: ScreenshotAssertions.toMatchSnapshot.maxDiffPixelRatio = %%-assertions-max-diff-pixel-ratio-%%
### option: ScreenshotAssertions.toMatchSnapshot#1.maxDiffPixelRatio = %%-assertions-max-diff-pixel-ratio-%%
### option: ScreenshotAssertions.toMatchSnapshot#1.threshold = %%-assertions-threshold-%%
## method: ScreenshotAssertions.toMatchSnapshot#2
Ensures that passed value, either a [string] or a [Buffer], matches the expected snapshot stored in the test snapshots directory.
```js
// Basic usage and the file name is derived from the test name.
expect(await page.screenshot()).toMatchSnapshot();
// Pass options to customize the snapshot comparison and have a generated name.
expect(await page.screenshot()).toMatchSnapshot({
maxDiffPixels: 27, // allow no more than 27 different pixels.
});
// Configure image matching threshold and snapshot name.
expect(await page.screenshot()).toMatchSnapshot({
name: 'landing-page.png',
threshold: 0.3,
});
```
Learn more about [visual comparisons](./test-snapshots.md).
### option: ScreenshotAssertions.toMatchSnapshot#2.maxDiffPixels = %%-assertions-max-diff-pixels-%%
### option: ScreenshotAssertions.toMatchSnapshot#2.maxDiffPixelRatio = %%-assertions-max-diff-pixel-ratio-%%
### option: ScreenshotAssertions.toMatchSnapshot#2.name
- `name` <[string]|[Array]<[string]>>
Snapshot name. If not passed, the test name and ordinals are used when called multiple times.
### option: ScreenshotAssertions.toMatchSnapshot#2.threshold = %%-assertions-threshold-%%
### option: ScreenshotAssertions.toMatchSnapshot.threshold = %%-assertions-threshold-%%

View file

@ -15,10 +15,7 @@
* limitations under the License.
*/
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials } from 'playwright-core';
import type { Expect } from './testExpect';
export type { Expect } from './testExpect';
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core';
export type ReporterDescription =
['dot'] |
@ -2802,7 +2799,7 @@ export interface PlaywrightTestOptions {
* [fixtures.page](https://playwright.dev/docs/api/class-fixtures#fixtures-page).
*/
export interface PlaywrightWorkerArgs {
playwright: typeof import('..');
playwright: typeof import('playwright-core');
/**
* [Browser] instance is shared between all tests in the [same worker](https://playwright.dev/docs/test-parallel) - this makes testing efficient.
* However, each test runs in an isolated [BrowserContext] and gets a fresh environment.
@ -2891,6 +2888,121 @@ export interface PlaywrightTestArgs {
export type PlaywrightTestProject<TestArgs = {}, WorkerArgs = {}> = Project<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>;
export type PlaywrightTestConfig<TestArgs = {}, WorkerArgs = {}> = Config<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>;
import type * as expectType from 'expect';
type AsymmetricMatcher = Record<string, any>;
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type ExtraMatchers<T, Type, Matchers> = T extends Type ? Matchers : IfAny<T, Matchers, {}>;
type BaseMatchers<R, T> = Pick<expectType.Matchers<R>, SupportedExpectProperties> & PlaywrightTest.Matchers<R, T>;
type MakeMatchers<R, T> = BaseMatchers<R, T> & {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: MakeMatchers<R, T>;
/**
* Use resolves to unwrap the value of a fulfilled promise so any other
* matcher can be chained. If the promise is rejected the assertion fails.
*/
resolves: MakeMatchers<Promise<R>, Awaited<T>>;
/**
* Unwraps the reason of a rejected promise so any other matcher can be chained.
* If the promise is fulfilled the assertion fails.
*/
rejects: MakeMatchers<Promise<R>, Awaited<T>>;
} & ScreenshotAssertions &
ExtraMatchers<T, Page, PageAssertions> &
ExtraMatchers<T, Locator, LocatorAssertions> &
ExtraMatchers<T, APIResponse, APIResponseAssertions>;
export declare type Expect = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: BaseMatchers<Promise<void>, T>;
};
extend(arg0: any): void;
getState(): expectType.MatcherState;
setState(state: Partial<expectType.MatcherState>): void;
any(expectedObject: any): AsymmetricMatcher;
anything(): AsymmetricMatcher;
arrayContaining(sample: Array<unknown>): AsymmetricMatcher;
objectContaining(sample: Record<string, unknown>): AsymmetricMatcher;
stringContaining(expected: string): AsymmetricMatcher;
stringMatching(expected: string | RegExp): AsymmetricMatcher;
/**
* Removed following methods because they rely on a test-runner integration from Jest which we don't support:
* - assertions()
* - extractExpectedAssertionsErrors()
* hasAssertions()
*/
};
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
/**
* Removed methods require the jest.fn() integration from Jest to spy on function calls which we don't support:
* - lastCalledWith()
* - lastReturnedWith()
* - nthCalledWith()
* - nthReturnedWith()
* - toBeCalled()
* - toBeCalledTimes()
* - toBeCalledWith()
* - toHaveBeenCalled()
* - toHaveBeenCalledTimes()
* - toHaveBeenCalledWith()
* - toHaveBeenLastCalledWith()
* - toHaveBeenNthCalledWith()
* - toHaveLastReturnedWith()
* - toHaveNthReturnedWith()
* - toHaveReturned()
* - toHaveReturnedTimes()
* - toHaveReturnedWith()
* - toReturn()
* - toReturnTimes()
* - toReturnWith()
* - toThrowErrorMatchingSnapshot()
* - toThrowErrorMatchingInlineSnapshot()
*/
type SupportedExpectProperties =
'toBe' |
'toBeCloseTo' |
'toBeDefined' |
'toBeFalsy' |
'toBeGreaterThan' |
'toBeGreaterThanOrEqual' |
'toBeInstanceOf' |
'toBeLessThan' |
'toBeLessThanOrEqual' |
'toBeNaN' |
'toBeNull' |
'toBeTruthy' |
'toBeUndefined' |
'toContain' |
'toContainEqual' |
'toEqual' |
'toHaveLength' |
'toHaveProperty' |
'toMatch' |
'toMatchObject' |
'toStrictEqual' |
'toThrow' |
'toThrowError'
declare global {
export namespace PlaywrightTest {
export interface Matchers<R, T = unknown> {
}
}
}
/**
* These tests are executed in Playwright environment that launches the browser
* and provides a fresh page to each test.
@ -2905,6 +3017,581 @@ export const expect: Expect;
export {};
/**
* The [APIResponseAssertions] class provides assertion methods that can be used to make assertions about the [APIResponse]
* in the tests. A new instance of [APIResponseAssertions] is created by calling
* [expect(response)](https://playwright.dev/docs/api/class-playwrightassertions#playwright-assertions-expect-api-response):
*
* ```js
* import { test, expect } from '@playwright/test';
*
* test('navigates to login', async ({ page }) => {
* // ...
* const response = await page.request.get('https://playwright.dev');
* await expect(response).toBeOK();
* });
* ```
*
*/
interface APIResponseAssertions {
/**
* Makes the assertion check for the opposite condition. For example, this code tests that the response status is not
* successful:
*
* ```js
* await expect(response).not.toBeOK();
* ```
*
*/
not: APIResponseAssertions;
/**
* Ensures the response status code is within [200..299] range.
*
* ```js
* await expect(response).toBeOK();
* ```
*
*/
toBeOK(): Promise<void>;
}
/**
* The [LocatorAssertions] class provides assertion methods that can be used to make assertions about the [Locator] state
* in the tests. A new instance of [LocatorAssertions] is created by calling
* [expect(locator)](https://playwright.dev/docs/api/class-playwrightassertions#playwright-assertions-expect-locator):
*
* ```js
* import { test, expect } from '@playwright/test';
*
* test('status becomes submitted', async ({ page }) => {
* // ...
* await page.click('#submit-button');
* await expect(page.locator('.status')).toHaveText('Submitted');
* });
* ```
*
*/
interface LocatorAssertions {
/**
* Makes the assertion check for the opposite condition. For example, this code tests that the Locator doesn't contain text
* `"error"`:
*
* ```js
* await expect(locator).not.toContainText('error');
* ```
*
*/
not: LocatorAssertions;
/**
* Ensures the [Locator] points to a checked input.
*
* ```js
* const locator = page.locator('.subscribe');
* await expect(locator).toBeChecked();
* ```
*
* @param options
*/
toBeChecked(options?: {
checked?: boolean;
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to a disabled element.
*
* ```js
* const locator = page.locator('button.submit');
* await expect(locator).toBeDisabled();
* ```
*
* @param options
*/
toBeDisabled(options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an editable element.
*
* ```js
* const locator = page.locator('input');
* await expect(locator).toBeEditable();
* ```
*
* @param options
*/
toBeEditable(options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an empty editable element or to a DOM node that has no text.
*
* ```js
* const locator = page.locator('div.warning');
* await expect(locator).toBeEmpty();
* ```
*
* @param options
*/
toBeEmpty(options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an enabled element.
*
* ```js
* const locator = page.locator('button.submit');
* await expect(locator).toBeEnabled();
* ```
*
* @param options
*/
toBeEnabled(options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to a focused DOM node.
*
* ```js
* const locator = page.locator('input');
* await expect(locator).toBeFocused();
* ```
*
* @param options
*/
toBeFocused(options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to a hidden DOM node, which is the opposite of [visible](https://playwright.dev/docs/api/actionability#visible).
*
* ```js
* const locator = page.locator('.my-element');
* await expect(locator).toBeHidden();
* ```
*
* @param options
*/
toBeHidden(options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to a [visible](https://playwright.dev/docs/api/actionability#visible) DOM node.
*
* ```js
* const locator = page.locator('.my-element');
* await expect(locator).toBeVisible();
* ```
*
* @param options
*/
toBeVisible(options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an element that contains the given text. You can use regular expressions for the value
* as well.
*
* ```js
* const locator = page.locator('.title');
* await expect(locator).toContainText('substring');
* await expect(locator).toContainText(/\d messages/);
* ```
*
* Note that if array is passed as an expected value, entire lists of elements can be asserted:
*
* ```js
* const locator = page.locator('list > .list-item');
* await expect(locator).toContainText(['Text 1', 'Text 4', 'Text 5']);
* ```
*
* @param expected Expected substring or RegExp or a list of those.
* @param options
*/
toContainText(expected: string|RegExp|Array<string|RegExp>, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
/**
* Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.
*/
useInnerText?: boolean;
}): Promise<void>;
/**
* Ensures the [Locator] points to an element with given attribute.
*
* ```js
* const locator = page.locator('input');
* await expect(locator).toHaveAttribute('type', 'text');
* ```
*
* @param name Attribute name.
* @param value Expected attribute value.
* @param options
*/
toHaveAttribute(name: string, value: string|RegExp, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an element with given CSS class.
*
* ```js
* const locator = page.locator('#component');
* await expect(locator).toHaveClass(/selected/);
* ```
*
* Note that if array is passed as an expected value, entire lists of elements can be asserted:
*
* ```js
* const locator = page.locator('list > .component');
* await expect(locator).toHaveClass(['component', 'component selected', 'component']);
* ```
*
* @param expected Expected class or RegExp or a list of those.
* @param options
*/
toHaveClass(expected: string|RegExp|Array<string|RegExp>, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] resolves to an exact number of DOM nodes.
*
* ```js
* const list = page.locator('list > .component');
* await expect(list).toHaveCount(3);
* ```
*
* @param count Expected count.
* @param options
*/
toHaveCount(count: number, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] resolves to an element with the given computed CSS style.
*
* ```js
* const locator = page.locator('button');
* await expect(locator).toHaveCSS('display', 'flex');
* ```
*
* @param name CSS property name.
* @param value CSS property value.
* @param options
*/
toHaveCSS(name: string, value: string|RegExp, options?: {
/**
* 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 DOM Node ID.
*
* ```js
* const locator = page.locator('input');
* await expect(locator).toHaveId('lastname');
* ```
*
* @param id Element id.
* @param options
*/
toHaveId(id: string|RegExp, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an element with given JavaScript property. Note that this property can be of a primitive
* type as well as a plain serializable JavaScript object.
*
* ```js
* const locator = page.locator('.component');
* await expect(locator).toHaveJSProperty('loaded', true);
* ```
*
* @param name Property name.
* @param value Property value.
* @param options
*/
toHaveJSProperty(name: string, value: any, options?: {
/**
* 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.
*
* ```js
* const locator = page.locator('.title');
* await expect(locator).toHaveText(/Welcome, Test User/);
* await expect(locator).toHaveText(/Welcome, .*\/);
* ```
*
* Note that if array is passed as an expected value, entire lists of elements can be asserted:
*
* ```js
* const locator = page.locator('list > .component');
* await expect(locator).toHaveText(['Text 1', 'Text 2', 'Text 3']);
* ```
*
* @param expected Expected substring or RegExp or a list of those.
* @param options
*/
toHaveText(expected: string|RegExp|Array<string|RegExp>, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
/**
* Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.
*/
useInnerText?: boolean;
}): Promise<void>;
/**
* Ensures the [Locator] points to an element with the given input value. You can use regular expressions for the value as
* well.
*
* ```js
* const locator = page.locator('input[type=number]');
* await expect(locator).toHaveValue(/[0-9]/);
* ```
*
* @param value Expected value.
* @param options
*/
toHaveValue(value: string|RegExp, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
}
/**
* The [PageAssertions] class provides assertion methods that can be used to make assertions about the [Page] state in the
* tests. A new instance of [PageAssertions] is created by calling
* [expect(page)](https://playwright.dev/docs/api/class-playwrightassertions#playwright-assertions-expect-page):
*
* ```js
* import { test, expect } from '@playwright/test';
*
* test('navigates to login', async ({ page }) => {
* // ...
* await page.click('#login');
* await expect(page).toHaveURL(/.*\/login/);
* });
* ```
*
*/
interface PageAssertions {
/**
* Makes the assertion check for the opposite condition. For example, this code tests that the page URL doesn't contain
* `"error"`:
*
* ```js
* await expect(page).not.toHaveURL('error');
* ```
*
*/
not: PageAssertions;
/**
* Ensures the page has the given title.
*
* ```js
* await expect(page).toHaveTitle(/.*checkout/);
* ```
*
* @param titleOrRegExp Expected title or RegExp.
* @param options
*/
toHaveTitle(titleOrRegExp: string|RegExp, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the page is navigated to the given URL.
*
* ```js
* await expect(page).toHaveURL(/.*checkout/);
* ```
*
* @param urlOrRegExp Expected substring or RegExp.
* @param options
*/
toHaveURL(urlOrRegExp: string|RegExp, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
}
/**
* Playwright provides methods for comparing page and element screenshots with expected values stored in files.
*
* ```js
* expect(screenshot).toMatchSnapshot('landing-page.png');
* ```
*
*/
interface ScreenshotAssertions {
/**
* Ensures that passed value, either a [string] or a [Buffer], matches the expected snapshot stored in the test snapshots
* directory.
*
* ```js
* // Basic usage.
* expect(await page.screenshot()).toMatchSnapshot('landing-page.png');
*
* // Pass options to customize the snapshot comparison and have a generated name.
* expect(await page.screenshot()).toMatchSnapshot('landing-page.png', {
* maxDiffPixels: 27, // allow no more than 27 different pixels.
* });
*
* // Configure image matching threshold.
* expect(await page.screenshot()).toMatchSnapshot('landing-page.png', { threshold: 0.3 });
*
* // Bring some structure to your snapshot files by passing file path segments.
* expect(await page.screenshot()).toMatchSnapshot(['landing', 'step2.png']);
* expect(await page.screenshot()).toMatchSnapshot(['landing', 'step3.png']);
* ```
*
* Learn more about [visual comparisons](https://playwright.dev/docs/api/test-snapshots).
* @param name Snapshot name.
* @param options
*/
toMatchSnapshot(name: string|Array<string>, options?: {
/**
* 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;
/**
* 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;
}): void;
/**
* Ensures that passed value, either a [string] or a [Buffer], matches the expected snapshot stored in the test snapshots
* directory.
*
* ```js
* // Basic usage and the file name is derived from the test name.
* expect(await page.screenshot()).toMatchSnapshot();
*
* // Pass options to customize the snapshot comparison and have a generated name.
* expect(await page.screenshot()).toMatchSnapshot({
* maxDiffPixels: 27, // allow no more than 27 different pixels.
* });
*
* // Configure image matching threshold and snapshot name.
* expect(await page.screenshot()).toMatchSnapshot({
* name: 'landing-page.png',
* threshold: 0.3,
* });
* ```
*
* Learn more about [visual comparisons](https://playwright.dev/docs/api/test-snapshots).
* @param options
*/
toMatchSnapshot(options?: {
/**
* 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;
/**
* Snapshot name. If not passed, the test name and ordinals are used when called multiple times.
*/
name?: string|Array<string>;
/**
* An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between the same
* pixel in compared images, between zero (strict) and one (lax), default is configurable with `TestConfig.expect`.
* Defaults to `0.2`.
*/
threshold?: number;
}): void;
}
/**
* Information about an error thrown during test execution.
*/

View file

@ -1,246 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type * as expect from 'expect';
import type { Page, Locator, APIResponse, PageScreenshotOptions, LocatorScreenshotOptions } from 'playwright-core';
export declare type AsymmetricMatcher = Record<string, any>;
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type ExtraMatchers<T, Type, Matchers> = T extends Type ? Matchers : IfAny<T, Matchers, {}>;
type MakeMatchers<R, T> = PlaywrightTest.Matchers<R, T> &
ExtraMatchers<T, Page, PageMatchers> &
ExtraMatchers<T, Locator, LocatorMatchers> &
ExtraMatchers<T, APIResponse, APIResponseMatchers>;
export declare type Expect = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => Omit<PlaywrightTest.Matchers<Promise<void>, T>, 'rejects' | 'resolves'>;
extend(arg0: any): void;
getState(): expect.MatcherState;
setState(state: Partial<expect.MatcherState>): void;
any(expectedObject: any): AsymmetricMatcher;
anything(): AsymmetricMatcher;
arrayContaining(sample: Array<unknown>): AsymmetricMatcher;
objectContaining(sample: Record<string, unknown>): AsymmetricMatcher;
stringContaining(expected: string): AsymmetricMatcher;
stringMatching(expected: string | RegExp): AsymmetricMatcher;
/**
* Removed following methods because they rely on a test-runner integration from Jest which we don't support:
* - assertions()
* - extractExpectedAssertionsErrors()
* hasAssertions()
*/
};
type ImageComparatorOptions = {
threshold?: number,
maxDiffPixels?: number,
maxDiffPixelRatio?: number,
};
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
/**
* Removed methods require the jest.fn() integration from Jest to spy on function calls which we don't support:
* - lastCalledWith()
* - lastReturnedWith()
* - nthCalledWith()
* - nthReturnedWith()
* - toBeCalled()
* - toBeCalledTimes()
* - toBeCalledWith()
* - toHaveBeenCalled()
* - toHaveBeenCalledTimes()
* - toHaveBeenCalledWith()
* - toHaveBeenLastCalledWith()
* - toHaveBeenNthCalledWith()
* - toHaveLastReturnedWith()
* - toHaveNthReturnedWith()
* - toHaveReturned()
* - toHaveReturnedTimes()
* - toHaveReturnedWith()
* - toReturn()
* - toReturnTimes()
* - toReturnWith()
* - toThrowErrorMatchingSnapshot()
* - toThrowErrorMatchingInlineSnapshot()
*/
type SupportedExpectProperties =
'toBe' |
'toBeCloseTo' |
'toBeDefined' |
'toBeFalsy' |
'toBeGreaterThan' |
'toBeGreaterThanOrEqual' |
'toBeInstanceOf' |
'toBeLessThan' |
'toBeLessThanOrEqual' |
'toBeNaN' |
'toBeNull' |
'toBeTruthy' |
'toBeUndefined' |
'toContain' |
'toContainEqual' |
'toEqual' |
'toHaveLength' |
'toHaveProperty' |
'toMatch' |
'toMatchObject' |
'toStrictEqual' |
'toThrow' |
'toThrowError'
declare global {
export namespace PlaywrightTest {
export interface Matchers<R, T = unknown> extends Pick<expect.Matchers<R>, SupportedExpectProperties> {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: MakeMatchers<R, T>;
/**
* Use resolves to unwrap the value of a fulfilled promise so any other
* matcher can be chained. If the promise is rejected the assertion fails.
*/
resolves: MakeMatchers<Promise<R>, Awaited<T>>;
/**
* Unwraps the reason of a rejected promise so any other matcher can be chained.
* If the promise is fulfilled the assertion fails.
*/
rejects: MakeMatchers<Promise<R>, Awaited<T>>;
/**
* Match snapshot
*/
toMatchSnapshot(options?: ImageComparatorOptions & {
name?: string | string[],
}): R;
/**
* Match snapshot
*/
toMatchSnapshot(name: string | string[], options?: ImageComparatorOptions): R;
}
}
}
interface LocatorMatchers {
/**
* Asserts input is checked (or unchecked if { checked: false } is passed).
*/
toBeChecked(options?: { checked?: boolean, timeout?: number }): Promise<void>;
/**
* Asserts input is disabled.
*/
toBeDisabled(options?: { timeout?: number }): Promise<void>;
/**
* Asserts input is editable.
*/
toBeEditable(options?: { timeout?: number }): Promise<void>;
/**
* Asserts given DOM node or input has no text content or no input value.
*/
toBeEmpty(options?: { timeout?: number }): Promise<void>;
/**
* Asserts input is enabled.
*/
toBeEnabled(options?: { timeout?: number }): Promise<void>;
/**
* Asserts given DOM is a focused (active) in document.
*/
toBeFocused(options?: { timeout?: number }): Promise<void>;
/**
* Asserts given DOM node is hidden or detached from DOM.
*/
toBeHidden(options?: { timeout?: number }): Promise<void>;
/**
* Asserts element's text content matches given pattern or contains given substring.
*/
toContainText(expected: string | RegExp | (string | RegExp)[], options?: { timeout?: number, useInnerText?: boolean }): Promise<void>;
/**
* Asserts element's attributes `name` matches expected value.
*/
toHaveAttribute(name: string, expected: string | RegExp, options?: { timeout?: number }): Promise<void>;
/**
* Asserts that DOM node has a given CSS class.
*/
toHaveClass(className: string | RegExp | (string | RegExp)[], options?: { timeout?: number }): Promise<void>;
/**
* Asserts number of DOM nodes matching given locator.
*/
toHaveCount(expected: number, options?: { timeout?: number }): Promise<void>;
/**
* Asserts element's computed CSS property `name` matches expected value.
*/
toHaveCSS(name: string, expected: string | RegExp, options?: { timeout?: number }): Promise<void>;
/**
* Asserts element's `id` attribute matches expected value.
*/
toHaveId(expected: string | RegExp, options?: { timeout?: number }): Promise<void>;
/**
* Asserts JavaScript object that corresponds to the Node has a property with given value.
*/
toHaveJSProperty(name: string, value: any, options?: { timeout?: number }): Promise<void>;
/**
* Asserts element's text content.
*/
toHaveText(expected: string | RegExp | (string | RegExp)[], options?: { timeout?: number, useInnerText?: boolean }): Promise<void>;
/**
* Asserts input element's value.
*/
toHaveValue(expected: string | RegExp, options?: { timeout?: number }): Promise<void>;
/**
* Asserts given DOM node visible on the screen.
*/
toBeVisible(options?: { timeout?: number }): Promise<void>;
}
interface PageMatchers {
/**
* Asserts page's title.
*/
toHaveTitle(expected: string | RegExp, options?: { timeout?: number }): Promise<void>;
/**
* Asserts page's URL.
*/
toHaveURL(expected: string | RegExp, options?: { timeout?: number }): Promise<void>;
}
interface APIResponseMatchers {
/**
* Asserts given APIResponse's status is between 200 and 299.
*/
toBeOK(): Promise<void>;
}
export { };

View file

@ -45,7 +45,7 @@ test('should compile', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
const { test } = pwt;
test('should poll sync predicate', () => {
test('should poll sync predicate', async ({ page }) => {
let i = 0;
test.expect.poll(() => ++i).toBe(3);
test.expect.poll(() => ++i, 'message').toBe(3);
@ -57,6 +57,11 @@ test('should compile', async ({ runTSC }) => {
return ++i;
}).toBe(3);
test.expect.poll(() => Promise.resolve(++i)).toBe(3);
// @ts-expect-error
await test.expect.poll(() => page.locator('foo')).toBeEnabled();
// @ts-expect-error
await test.expect.poll(() => page.locator('foo')).not.toBeEnabled();
});
`
});

View file

@ -212,9 +212,12 @@ test('should propose only the relevant matchers when custom expect matcher class
const { test } = pwt;
test('custom matchers', async ({ page }) => {
await test.expect(page).toHaveURL('https://example.com');
await test.expect(page).not.toHaveURL('https://example.com');
await test.expect(page).toBe(true);
// @ts-expect-error
await test.expect(page).toBeEnabled();
// @ts-expect-error
await test.expect(page).not.toBeEnabled();
await test.expect(page.locator('foo')).toBeEnabled();
await test.expect(page.locator('foo')).toBe(true);

View file

@ -34,9 +34,10 @@ class TypesGenerator {
/**
* @param {{
* documentation: Documentation,
* classNames: Set<string>,
* overridesToDocsClassMapping: Map<string, string>,
* ignoreMissing: Set<string>,
* classNamesToGenerate: Set<string>,
* overridesToDocsClassMapping?: Map<string, string>,
* ignoreMissing?: Set<string>,
* doNotExportClassNames?: Set<string>,
* }} options
*/
constructor(options) {
@ -45,9 +46,10 @@ class TypesGenerator {
/** @type {Set<string>} */
this.handledMethods = new Set();
this.documentation = options.documentation;
this.classNames = options.classNames;
this.overridesToDocsClassMapping = options.overridesToDocsClassMapping;
this.ignoreMissing = options.ignoreMissing;
this.classNamesToGenerate = options.classNamesToGenerate;
this.overridesToDocsClassMapping = options.overridesToDocsClassMapping || new Map();
this.ignoreMissing = options.ignoreMissing || new Set();
this.doNotExportClassNames = options.doNotExportClassNames || new Set();
}
/**
@ -61,7 +63,7 @@ class TypesGenerator {
const createMarkdownLink = (member, text) => {
const className = toKebabCase(member.clazz.name);
const memberName = toKebabCase(member.name);
let hash = null
let hash = null;
if (member.kind === 'property' || member.kind === 'method')
hash = `${className}-${memberName}`.toLowerCase();
else if (member.kind === 'event')
@ -76,12 +78,13 @@ class TypesGenerator {
return `\`${option}\``;
if (clazz)
return `[${clazz.name}]`;
const className = member.clazz.varName === 'playwrightAssertions' ? '' : member.clazz.varName + '.';
if (member.kind === 'method')
return createMarkdownLink(member, `${member.clazz.varName}.${member.alias}(${this.renderJSSignature(member.argsArray)})`);
return createMarkdownLink(member, `${className}${member.alias}(${this.renderJSSignature(member.argsArray)})`);
if (member.kind === 'event')
return createMarkdownLink(member, `${member.clazz.varName}.on('${member.alias.toLowerCase()}')`);
return createMarkdownLink(member, `${className}on('${member.alias.toLowerCase()}')`);
if (member.kind === 'property')
return createMarkdownLink(member, `${member.clazz.varName}.${member.alias}`);
return createMarkdownLink(member, `${className}${member.alias}`);
throw new Error('Unknown member kind ' + member.kind);
});
this.documentation.generateSourceCodeComments();
@ -113,13 +116,13 @@ class TypesGenerator {
return this.memberJSDOC(method, ' ').trimLeft();
}, (className) => {
const docClass = this.docClassForName(className);
if (!docClass || !this.classNames.has(docClass.name))
if (!docClass || !this.classNamesToGenerate.has(docClass.name))
return '';
return this.classBody(docClass);
});
const classes = this.documentation.classesArray
.filter(cls => this.classNames.has(cls.name))
.filter(cls => this.classNamesToGenerate.has(cls.name))
.filter(cls => !handledClasses.has(cls.name));
{
const playwright = this.documentation.classesArray.find(c => c.name === 'Playwright');
@ -152,7 +155,7 @@ class TypesGenerator {
* @param {string} name
*/
docClassForName(name) {
const mappedName = (this.overridesToDocsClassMapping ? this.overridesToDocsClassMapping.get(name) : undefined) || name;
const mappedName = this.overridesToDocsClassMapping.get(name) || name;
const docClass = this.documentation.classes.get(mappedName);
if (!docClass && !this.canIgnoreMissingName(name))
throw new Error(`Unknown override class ${name}`);
@ -189,7 +192,8 @@ class TypesGenerator {
if (classDesc.comment) {
parts.push(this.writeComment(classDesc.comment))
}
parts.push(`export interface ${classDesc.name} ${classDesc.extends ? `extends ${classDesc.extends} ` : ''}{`);
const shouldExport = !this.doNotExportClassNames.has(classDesc.name);
parts.push(`${shouldExport ? 'export ' : ''}interface ${classDesc.name} ${classDesc.extends ? `extends ${classDesc.extends} ` : ''}{`);
parts.push(this.classBody(classDesc));
parts.push('}\n');
return parts.join('\n');
@ -497,14 +501,12 @@ class TypesGenerator {
fs.mkdirSync(testTypesDir)
writeFile(path.join(coreTypesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'packages', 'playwright-core', 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'));
const assertionClasses = new Set(['PlaywrightAssertions', 'LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions']);
const assertionClasses = new Set(['LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions']);
const apiDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
apiDocumentation.index();
const apiTypesGenerator = new TypesGenerator({
documentation: apiDocumentation,
classNames: new Set(apiDocumentation.classesArray.map(cls => cls.name).filter(name => !assertionClasses.has(name))),
overridesToDocsClassMapping: new Map(),
ignoreMissing: new Set(),
classNamesToGenerate: new Set(apiDocumentation.classesArray.map(cls => cls.name).filter(name => !assertionClasses.has(name) && name !== 'PlaywrightAssertions')),
});
let apiTypes = await apiTypesGenerator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
const namedDevices = Object.keys(devices).map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`).join('\n');
@ -530,7 +532,7 @@ class TypesGenerator {
const testDocumentation = apiDocumentation.mergeWith(testOnlyDocumentation);
const testTypesGenerator = new TypesGenerator({
documentation: testDocumentation,
classNames: new Set(['TestError', 'TestInfo', 'WorkerInfo']),
classNamesToGenerate: new Set(['TestError', 'TestInfo', 'WorkerInfo', ...assertionClasses]),
overridesToDocsClassMapping: new Map([
['TestType', 'Test'],
['Config', 'TestConfig'],
@ -548,7 +550,9 @@ class TypesGenerator {
'TestFunction',
'PlaywrightWorkerOptions.defaultBrowserType',
'PlaywrightWorkerArgs.playwright',
'Matchers',
]),
doNotExportClassNames: new Set(assertionClasses),
});
let testTypes = await testTypesGenerator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'));
testTypes = testTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
@ -558,8 +562,7 @@ class TypesGenerator {
const testReporterDocumentation = testDocumentation.mergeWith(testReporterOnlyDocumentation);
const testReporterTypesGenerator = new TypesGenerator({
documentation: testReporterDocumentation,
classNames: new Set(testReporterOnlyDocumentation.classesArray.map(cls => cls.name)),
overridesToDocsClassMapping: new Map(),
classNamesToGenerate: new Set(testReporterOnlyDocumentation.classesArray.map(cls => cls.name)),
ignoreMissing: new Set(['FullResult']),
});
let testReporterTypes = await testReporterTypesGenerator.generateTypes(path.join(__dirname, 'overrides-testReporter.d.ts'));

View file

@ -14,10 +14,7 @@
* limitations under the License.
*/
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials } from 'playwright-core';
import type { Expect } from './testExpect';
export type { Expect } from './testExpect';
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core';
export type ReporterDescription =
['dot'] |
@ -332,7 +329,7 @@ export interface PlaywrightTestOptions {
export interface PlaywrightWorkerArgs {
playwright: typeof import('..');
playwright: typeof import('playwright-core');
browser: Browser;
}
@ -345,6 +342,121 @@ export interface PlaywrightTestArgs {
export type PlaywrightTestProject<TestArgs = {}, WorkerArgs = {}> = Project<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>;
export type PlaywrightTestConfig<TestArgs = {}, WorkerArgs = {}> = Config<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>;
import type * as expectType from 'expect';
type AsymmetricMatcher = Record<string, any>;
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type ExtraMatchers<T, Type, Matchers> = T extends Type ? Matchers : IfAny<T, Matchers, {}>;
type BaseMatchers<R, T> = Pick<expectType.Matchers<R>, SupportedExpectProperties> & PlaywrightTest.Matchers<R, T>;
type MakeMatchers<R, T> = BaseMatchers<R, T> & {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: MakeMatchers<R, T>;
/**
* Use resolves to unwrap the value of a fulfilled promise so any other
* matcher can be chained. If the promise is rejected the assertion fails.
*/
resolves: MakeMatchers<Promise<R>, Awaited<T>>;
/**
* Unwraps the reason of a rejected promise so any other matcher can be chained.
* If the promise is fulfilled the assertion fails.
*/
rejects: MakeMatchers<Promise<R>, Awaited<T>>;
} & ScreenshotAssertions &
ExtraMatchers<T, Page, PageAssertions> &
ExtraMatchers<T, Locator, LocatorAssertions> &
ExtraMatchers<T, APIResponse, APIResponseAssertions>;
export declare type Expect = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: BaseMatchers<Promise<void>, T>;
};
extend(arg0: any): void;
getState(): expectType.MatcherState;
setState(state: Partial<expectType.MatcherState>): void;
any(expectedObject: any): AsymmetricMatcher;
anything(): AsymmetricMatcher;
arrayContaining(sample: Array<unknown>): AsymmetricMatcher;
objectContaining(sample: Record<string, unknown>): AsymmetricMatcher;
stringContaining(expected: string): AsymmetricMatcher;
stringMatching(expected: string | RegExp): AsymmetricMatcher;
/**
* Removed following methods because they rely on a test-runner integration from Jest which we don't support:
* - assertions()
* - extractExpectedAssertionsErrors()
* hasAssertions()
*/
};
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
/**
* Removed methods require the jest.fn() integration from Jest to spy on function calls which we don't support:
* - lastCalledWith()
* - lastReturnedWith()
* - nthCalledWith()
* - nthReturnedWith()
* - toBeCalled()
* - toBeCalledTimes()
* - toBeCalledWith()
* - toHaveBeenCalled()
* - toHaveBeenCalledTimes()
* - toHaveBeenCalledWith()
* - toHaveBeenLastCalledWith()
* - toHaveBeenNthCalledWith()
* - toHaveLastReturnedWith()
* - toHaveNthReturnedWith()
* - toHaveReturned()
* - toHaveReturnedTimes()
* - toHaveReturnedWith()
* - toReturn()
* - toReturnTimes()
* - toReturnWith()
* - toThrowErrorMatchingSnapshot()
* - toThrowErrorMatchingInlineSnapshot()
*/
type SupportedExpectProperties =
'toBe' |
'toBeCloseTo' |
'toBeDefined' |
'toBeFalsy' |
'toBeGreaterThan' |
'toBeGreaterThanOrEqual' |
'toBeInstanceOf' |
'toBeLessThan' |
'toBeLessThanOrEqual' |
'toBeNaN' |
'toBeNull' |
'toBeTruthy' |
'toBeUndefined' |
'toContain' |
'toContainEqual' |
'toEqual' |
'toHaveLength' |
'toHaveProperty' |
'toMatch' |
'toMatchObject' |
'toStrictEqual' |
'toThrow' |
'toThrowError'
declare global {
export namespace PlaywrightTest {
export interface Matchers<R, T = unknown> {
}
}
}
/**
* These tests are executed in Playwright environment that launches the browser
* and provides a fresh page to each test.