diff --git a/docs/src/test-api/class-fixtures.md b/docs/src/test-api/class-fixtures.md index 24499cae4f..f79b17687c 100644 --- a/docs/src/test-api/class-fixtures.md +++ b/docs/src/test-api/class-fixtures.md @@ -118,7 +118,7 @@ export default config; ## property: Fixtures.actionTimeout - type: <[int]> -Timeout for each action and expect in milliseconds. Defaults to 0 (no timeout). +Default timeout for each Playwright action in milliseconds, defaults to 0 (no timeout). This is a default timeout for all Playwright actions, same as configured via [`method: Page.setDefaultTimeout`]. diff --git a/docs/src/test-api/class-testproject.md b/docs/src/test-api/class-testproject.md index 24649339f9..9a60a0d768 100644 --- a/docs/src/test-api/class-testproject.md +++ b/docs/src/test-api/class-testproject.md @@ -106,6 +106,7 @@ export default config; ## property: TestProject.expect - type: <[Object]> + - `timeout` <[float]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms. - `toMatchSnapshot` <[Object]> - `threshold` <[float]> Image matching threshold between zero (strict) and one (lax). diff --git a/docs/src/test-assertions-js.md b/docs/src/test-assertions-js.md index b9be989e8b..16cc71308c 100644 --- a/docs/src/test-assertions-js.md +++ b/docs/src/test-assertions-js.md @@ -25,14 +25,14 @@ await expect(page.locator('.status')).toHaveText('Submitted'); Playwright Test will be re-testing the node with the selector `.status` until fetched Node has the `"Submitted"` text. It will be re-fetching the node and checking it over and over, until the condition is met or until the timeout is -reached. You can either pass this timeout or configure it once via the [`property: Fixtures.actionTimeout`] value +reached. You can either pass this timeout or configure it once via the [`property: TestProject.expect`] value in test config. By default, the timeout for assertions is not set, so it'll wait forever, until the whole test times out. ## expect(locator).toBeChecked - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to the checked input. @@ -43,7 +43,7 @@ await expect(locator).toBeChecked(); ## expect(locator).toBeDisabled - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to a disabled element. @@ -54,7 +54,7 @@ await expect(locator).toBeDisabled(); ## expect(locator).toBeEditable - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to an editable element. @@ -65,7 +65,7 @@ await expect(locator).toBeEditable(); ## expect(locator).toBeEmpty - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to an empty editable element or to a DOM node that has no text. @@ -76,7 +76,7 @@ await expect(locator).toBeEmpty(); ## expect(locator).toBeEnabled - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to an enabled element. @@ -87,7 +87,7 @@ await expect(locator).toBeEnabled(); ## expect(locator).toBeFocused - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to a focused DOM node. @@ -98,7 +98,7 @@ await expect(locator).toBeFocused(); ## expect(locator).toBeHidden - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to a hidden DOM node, which is the opposite of [visible](./actionability.md#visible). @@ -109,7 +109,7 @@ await expect(locator).toBeHidden(); ## expect(locator).toBeVisible - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to a [visible](./actionability.md#visible) DOM node. @@ -121,7 +121,7 @@ await expect(locator).toBeVisible(); ## expect(locator).toContainText(text, options?) - `text`: <[string]> Text to look for inside the element - `options` - - `timeout`: <[number]> Time to wait for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to wait for, defaults to `timeout` in [`property: TestProject.expect`]. - `useInnerText`: <[boolean]> Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text. Ensures [Locator] points to a selected option. @@ -135,7 +135,7 @@ await expect(locator).toContainText('substring'); - `name`: <[string]> Attribute name - `value`: <[string]|[RegExp]> Attribute value - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to an element with given attribute. @@ -147,7 +147,7 @@ await expect(locator).toHaveAttribute('type', 'text'); ## expect(locator).toHaveClass(expected) - `expected`: <[string] | [RegExp] | [Array]<[string]>> - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to an element with given CSS class. @@ -166,7 +166,7 @@ await expect(locator).toHaveClass(['component', 'component selected', 'component ## expect(locator).toHaveCount(count) - `count`: <[number]> - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] resolves to an exact number of DOM nodes. @@ -179,7 +179,7 @@ await expect(list).toHaveCount(3); - `name`: <[string]> CSS property name - `value`: <[string]|[RegExp]> CSS property value - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] resolves to an element with the given computed CSS style @@ -191,7 +191,7 @@ await expect(locator).toHaveCSS('display', 'flex'); ## expect(locator).toHaveId(id) - `id`: <[string]> Element id - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to an element with the given DOM Node ID. @@ -204,7 +204,7 @@ await expect(locator).toHaveId('lastname'); - `name`: <[string]> Property name - `value`: <[any]> Property value - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [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. @@ -217,7 +217,7 @@ await expect(locator).toHaveJSProperty('loaded', true); ## expect(locator).toHaveText(expected, options) - `expected`: <[string] | [RegExp] | [Array]<[string]>> - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. - `useInnerText`: <[boolean]> Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text. Ensures [Locator] points to an element with the given text. You can use regular expressions for the value as well. @@ -237,7 +237,7 @@ await expect(locator).toHaveText(['Text 1', 'Text 2', 'Text 3']); ## expect(page).toHaveTitle(title) - `title`: <[string] | [RegExp]>> - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures page has a given title. @@ -248,7 +248,7 @@ await expect(page).toHaveTitle(/.*checkout/); ## expect(page).toHaveURL(url) - `url`: <[string] | [RegExp]>> - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures page is navigated to a given URL. @@ -259,7 +259,7 @@ await expect(page).toHaveURL(/.*checkout/); ## expect(locator).toHaveValue(value) - `value`: <[string] | [RegExp]>> - `options` - - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. + - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. Ensures [Locator] points to an element with the given input value. You can use regular expressions for the value as well. diff --git a/src/test/index.ts b/src/test/index.ts index 075a70f5cd..b7230ee297 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import expectLibrary from 'expect'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; @@ -191,7 +190,6 @@ export const test = _baseTest.extend allPages.push(page)); if (captureTrace) { diff --git a/src/test/matchers/toBeTruthy.ts b/src/test/matchers/toBeTruthy.ts index 1927ea67a1..3a2d1327f1 100644 --- a/src/test/matchers/toBeTruthy.ts +++ b/src/test/matchers/toBeTruthy.ts @@ -44,7 +44,7 @@ export async function toBeTruthy( let pass = false; // TODO: interrupt on timeout for nice message. - await pollUntilDeadline(this, async remainingTime => { + await pollUntilDeadline(testInfo, async remainingTime => { received = await query(remainingTime); pass = !!received; return pass === !matcherOptions.isNot; diff --git a/src/test/matchers/toEqual.ts b/src/test/matchers/toEqual.ts index 0c337af63a..f76b9af39c 100644 --- a/src/test/matchers/toEqual.ts +++ b/src/test/matchers/toEqual.ts @@ -60,7 +60,7 @@ export async function toEqual( let pass = false; // TODO: interrupt on timeout for nice message. - await pollUntilDeadline(this, async remainingTime => { + await pollUntilDeadline(testInfo, async remainingTime => { received = await query(remainingTime); pass = equals(received, expected, [iterableEquality]); return pass === !matcherOptions.isNot; diff --git a/src/test/matchers/toMatchText.ts b/src/test/matchers/toMatchText.ts index a060e5559a..8f1ddae651 100644 --- a/src/test/matchers/toMatchText.ts +++ b/src/test/matchers/toMatchText.ts @@ -70,7 +70,7 @@ export async function toMatchText( let pass = false; // TODO: interrupt on timeout for nice message. - await pollUntilDeadline(this, async remainingTime => { + await pollUntilDeadline(testInfo, async remainingTime => { received = await query(remainingTime); if (options.matchSubstring) pass = received.includes(expected as string); diff --git a/src/test/util.ts b/src/test/util.ts index a941065a46..72bad72737 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Expect } from './types'; +import type { TestInfoImpl } from './types'; import util from 'util'; import path from 'path'; import type { TestError, Location } from './types'; @@ -71,9 +71,11 @@ export async function raceAgainstDeadline(promise: Promise, deadline: numb return (new DeadlineRunner(promise, deadline)).result; } -export async function pollUntilDeadline(state: ReturnType, func: (remainingTime: number) => Promise, pollTime: number | undefined, deadlinePromise: Promise): Promise { - const playwrightActionTimeout = (state as any).playwrightActionTimeout; - pollTime = pollTime === 0 ? 0 : pollTime || playwrightActionTimeout; +export async function pollUntilDeadline(testInfo: TestInfoImpl, func: (remainingTime: number) => Promise, pollTime: number | undefined, deadlinePromise: Promise): Promise { + let defaultExpectTimeout = testInfo.project.expect?.timeout; + if (typeof defaultExpectTimeout === 'undefined') + defaultExpectTimeout = 5000; + pollTime = pollTime === 0 ? 0 : pollTime || defaultExpectTimeout; const deadline = pollTime ? monotonicTime() + pollTime : 0; let aborted = false; diff --git a/tests/playwright-test/playwright.expect.misc.spec.ts b/tests/playwright-test/playwright.expect.misc.spec.ts index 0e7f78e3f6..8b35a99190 100644 --- a/tests/playwright-test/playwright.expect.misc.spec.ts +++ b/tests/playwright-test/playwright.expect.misc.spec.ts @@ -161,7 +161,7 @@ test('should support toHaveURL', async ({ runInlineTest }) => { test('should support respect actionTimeout', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.js': `module.exports = { use: { actionTimeout: 1000 } }`, + 'playwright.config.js': `module.exports = { expect: { timeout: 1000 } }`, 'a.test.ts': ` const { test } = pwt; diff --git a/types/test.d.ts b/types/test.d.ts index 508c324b46..89a71791e2 100644 --- a/types/test.d.ts +++ b/types/test.d.ts @@ -37,6 +37,8 @@ export type UpdateSnapshots = 'all' | 'none' | 'missing'; type FixtureDefine = { test: TestType, fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs> }; type ExpectSettings = { + // Default timeout for async expect matchers in milliseconds, defaults to 5000ms. + timeout?: number; toMatchSnapshot?: { // Pixel match threshold. threshold?: number @@ -2520,7 +2522,7 @@ export interface PlaywrightTestOptions { */ contextOptions: BrowserContextOptions; /** - * Timeout for each action and expect in milliseconds. Defaults to 0 (no timeout). + * Default timeout for each Playwright action in milliseconds, defaults to 0 (no timeout). * * This is a default timeout for all Playwright actions, same as configured via * [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout). diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 39f563a2c9..40073c70d6 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -36,6 +36,8 @@ export type UpdateSnapshots = 'all' | 'none' | 'missing'; type FixtureDefine = { test: TestType, fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs> }; type ExpectSettings = { + // Default timeout for async expect matchers in milliseconds, defaults to 5000ms. + timeout?: number; toMatchSnapshot?: { // Pixel match threshold. threshold?: number