From 9884c851ffbccdd62fce39d49c7f586ba5a304b4 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 24 May 2024 08:56:43 -0700 Subject: [PATCH] feat(expect): expose expect timeout (#30969) Fixes https://github.com/microsoft/playwright/issues/30583 --- .../playwright/src/common/expectBundle.ts | 2 +- packages/playwright/src/common/globals.ts | 18 ------ packages/playwright/src/matchers/expect.ts | 42 +++++++++++--- .../playwright/src/matchers/matcherHint.ts | 4 +- packages/playwright/src/matchers/matchers.ts | 56 +++++++++---------- .../playwright/src/matchers/toBeTruthy.ts | 7 +-- packages/playwright/src/matchers/toEqual.ts | 7 +-- .../src/matchers/toMatchSnapshot.ts | 10 ++-- .../playwright/src/matchers/toMatchText.ts | 24 ++++---- packages/playwright/types/test.d.ts | 12 ++++ tests/playwright-test/expect.spec.ts | 39 +++++++++++++ utils/generate_types/overrides-test.d.ts | 12 ++++ 12 files changed, 150 insertions(+), 83 deletions(-) diff --git a/packages/playwright/src/common/expectBundle.ts b/packages/playwright/src/common/expectBundle.ts index 40c6256dd0..3322c36277 100644 --- a/packages/playwright/src/common/expectBundle.ts +++ b/packages/playwright/src/common/expectBundle.ts @@ -15,7 +15,7 @@ */ export const expect: typeof import('../../bundles/expect/node_modules/expect/build').expect = require('./expectBundleImpl').expect; -export type ExpectMatcherContext = import('../../bundles/expect/node_modules/expect/build').MatcherContext; +export const EXPECTED_COLOR: typeof import('../../bundles/expect/node_modules/jest-matcher-utils/build').EXPECTED_COLOR = require('./expectBundleImpl').EXPECTED_COLOR; export const INVERTED_COLOR: typeof import('../../bundles/expect/node_modules/jest-matcher-utils/build').INVERTED_COLOR = require('./expectBundleImpl').INVERTED_COLOR; export const RECEIVED_COLOR: typeof import('../../bundles/expect/node_modules/jest-matcher-utils/build').RECEIVED_COLOR = require('./expectBundleImpl').RECEIVED_COLOR; export const printReceived: typeof import('../../bundles/expect/node_modules/jest-matcher-utils/build').printReceived = require('./expectBundleImpl').printReceived; diff --git a/packages/playwright/src/common/globals.ts b/packages/playwright/src/common/globals.ts index c2ead505e6..746e1ec847 100644 --- a/packages/playwright/src/common/globals.ts +++ b/packages/playwright/src/common/globals.ts @@ -33,24 +33,6 @@ export function currentlyLoadingFileSuite() { return currentFileSuite; } -let currentExpectConfigureTimeout: number | undefined; - -export function setCurrentExpectConfigureTimeout(timeout: number | undefined) { - currentExpectConfigureTimeout = timeout; -} - -export function currentExpectTimeout(options: { timeout?: number }) { - const testInfo = currentTestInfo(); - if (options.timeout !== undefined) - return options.timeout; - if (currentExpectConfigureTimeout !== undefined) - return currentExpectConfigureTimeout; - let defaultExpectTimeout = testInfo?._projectInternal?.expect?.timeout; - if (typeof defaultExpectTimeout === 'undefined') - defaultExpectTimeout = 5000; - return defaultExpectTimeout; -} - let _isWorkerProcess = false; export function setIsWorkerProcess() { diff --git a/packages/playwright/src/matchers/expect.ts b/packages/playwright/src/matchers/expect.ts index 0d5d226039..9a010992da 100644 --- a/packages/playwright/src/matchers/expect.ts +++ b/packages/playwright/src/matchers/expect.ts @@ -49,8 +49,8 @@ import { toPass } from './matchers'; import { toMatchSnapshot, toHaveScreenshot, toHaveScreenshotStepTitle } from './toMatchSnapshot'; -import type { Expect } from '../../types/test'; -import { currentTestInfo, currentExpectTimeout, setCurrentExpectConfigureTimeout } from '../common/globals'; +import type { Expect, ExpectMatcherState } from '../../types/test'; +import { currentTestInfo } from '../common/globals'; import { filteredStackTrace, trimLongString } from '../util'; import { expect as expectLibrary, @@ -58,7 +58,6 @@ import { RECEIVED_COLOR, printReceived, } from '../common/expectBundle'; -export type { ExpectMatcherContext } from '../common/expectBundle'; import { zones } from 'playwright-core/lib/utils'; import { TestInfoImpl } from '../worker/testInfo'; import { ExpectError } from './matcherHint'; @@ -129,7 +128,20 @@ function createExpect(info: ExpectMetaInfo) { if (property === 'extend') { return (matchers: any) => { - expectLibrary.extend(matchers); + const wrappedMatchers: any = {}; + for (const [name, matcher] of Object.entries(matchers)) { + wrappedMatchers[name] = function(...args: any[]) { + const { isNot, promise, utils } = this; + const newThis: ExpectMatcherState = { + isNot, + promise, + utils, + timeout: currentExpectTimeout() + }; + return (matcher as any).call(newThis, ...args); + }; + } + expectLibrary.extend(wrappedMatchers); return expectInstance; }; } @@ -171,8 +183,6 @@ function createExpect(info: ExpectMetaInfo) { return expectInstance; } -export const expect: Expect<{}> = createExpect({}); - expectLibrary.setState({ expand: false }); const customAsyncMatchers = { @@ -245,7 +255,7 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler { if (this._info.isPoll) { if ((customAsyncMatchers as any)[matcherName] || matcherName === 'resolves' || matcherName === 'rejects') throw new Error(`\`expect.poll()\` does not support "${matcherName}" matcher.`); - matcher = (...args: any[]) => pollMatcher(matcherName, !!this._info.isNot, this._info.pollIntervals, currentExpectTimeout({ timeout: this._info.pollTimeout }), this._info.generator!, ...args); + matcher = (...args: any[]) => pollMatcher(matcherName, !!this._info.isNot, this._info.pollIntervals, this._info.pollTimeout ?? currentExpectTimeout(), this._info.generator!, ...args); } return (...args: any[]) => { const testInfo = currentTestInfo(); @@ -337,6 +347,22 @@ async function pollMatcher(matcherName: any, isNot: boolean, pollIntervals: numb } } +let currentExpectConfigureTimeout: number | undefined; + +function setCurrentExpectConfigureTimeout(timeout: number | undefined) { + currentExpectConfigureTimeout = timeout; +} + +function currentExpectTimeout() { + if (currentExpectConfigureTimeout !== undefined) + return currentExpectConfigureTimeout; + const testInfo = currentTestInfo(); + let defaultExpectTimeout = testInfo?._projectInternal?.expect?.timeout; + if (typeof defaultExpectTimeout === 'undefined') + defaultExpectTimeout = 5000; + return defaultExpectTimeout; +} + function computeArgsSuffix(matcherName: string, args: any[]) { let value = ''; if (matcherName === 'toHaveScreenshot') @@ -344,7 +370,7 @@ function computeArgsSuffix(matcherName: string, args: any[]) { return value ? `(${value})` : ''; } -expectLibrary.extend(customMatchers); +export const expect: Expect<{}> = createExpect({}).extend(customMatchers); export function mergeExpects(...expects: any[]) { return expect; diff --git a/packages/playwright/src/matchers/matcherHint.ts b/packages/playwright/src/matchers/matcherHint.ts index 2db79cbf6f..e8aba2bbff 100644 --- a/packages/playwright/src/matchers/matcherHint.ts +++ b/packages/playwright/src/matchers/matcherHint.ts @@ -15,14 +15,14 @@ */ import { colors } from 'playwright-core/lib/utilsBundle'; -import type { ExpectMatcherContext } from './expect'; +import type { ExpectMatcherState } from '../../types/test'; import type { Locator } from 'playwright-core'; import type { StackFrame } from '@protocol/channels'; import { stringifyStackFrames } from 'playwright-core/lib/utils'; export const kNoElementsFoundError = ''; -export function matcherHint(state: ExpectMatcherContext, locator: Locator | undefined, matcherName: string, expression: any, actual: any, matcherOptions: any, timeout?: number) { +export function matcherHint(state: ExpectMatcherState, locator: Locator | undefined, matcherName: string, expression: any, actual: any, matcherOptions: any, timeout?: number) { let header = state.utils.matcherHint(matcherName, expression, actual, matcherOptions).replace(/ \/\/ deep equality/, '') + '\n\n'; if (timeout) header = colors.red(`Timed out ${timeout}ms waiting for `) + header; diff --git a/packages/playwright/src/matchers/matchers.ts b/packages/playwright/src/matchers/matchers.ts index 505fd24351..08ae8b6385 100644 --- a/packages/playwright/src/matchers/matchers.ts +++ b/packages/playwright/src/matchers/matchers.ts @@ -24,7 +24,7 @@ import { toExpectedTextValues, toMatchText } from './toMatchText'; import { constructURLBasedOnBaseURL, isRegExp, isString, isTextualMimeType, pollAgainstDeadline } from 'playwright-core/lib/utils'; import { currentTestInfo } from '../common/globals'; import { TestInfoImpl } from '../worker/testInfo'; -import type { ExpectMatcherContext } from './expect'; +import type { ExpectMatcherState } from '../../types/test'; import { takeFirst } from '../common/config'; interface LocatorEx extends Locator { @@ -36,7 +36,7 @@ interface APIResponseEx extends APIResponse { } export function toBeAttached( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { attached?: boolean, timeout?: number }, ) { @@ -50,7 +50,7 @@ export function toBeAttached( } export function toBeChecked( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { checked?: boolean, timeout?: number }, ) { @@ -64,7 +64,7 @@ export function toBeChecked( } export function toBeDisabled( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { timeout?: number }, ) { @@ -74,7 +74,7 @@ export function toBeDisabled( } export function toBeEditable( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { editable?: boolean, timeout?: number }, ) { @@ -88,7 +88,7 @@ export function toBeEditable( } export function toBeEmpty( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { timeout?: number }, ) { @@ -98,7 +98,7 @@ export function toBeEmpty( } export function toBeEnabled( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { enabled?: boolean, timeout?: number }, ) { @@ -112,7 +112,7 @@ export function toBeEnabled( } export function toBeFocused( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { timeout?: number }, ) { @@ -122,7 +122,7 @@ export function toBeFocused( } export function toBeHidden( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { timeout?: number }, ) { @@ -132,7 +132,7 @@ export function toBeHidden( } export function toBeVisible( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { visible?: boolean, timeout?: number }, ) { @@ -146,7 +146,7 @@ export function toBeVisible( } export function toBeInViewport( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, options?: { timeout?: number, ratio?: number }, ) { @@ -156,7 +156,7 @@ export function toBeInViewport( } export function toContainText( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string | RegExp | (string | RegExp)[], options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {}, @@ -175,7 +175,7 @@ export function toContainText( } export function toHaveAccessibleDescription( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string | RegExp, options?: { timeout?: number, ignoreCase?: boolean }, @@ -187,7 +187,7 @@ export function toHaveAccessibleDescription( } export function toHaveAccessibleName( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string | RegExp, options?: { timeout?: number, ignoreCase?: boolean }, @@ -199,7 +199,7 @@ export function toHaveAccessibleName( } export function toHaveAttribute( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, name: string, expected: string | RegExp | undefined | { timeout?: number }, @@ -224,7 +224,7 @@ export function toHaveAttribute( } export function toHaveClass( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string | RegExp | (string | RegExp)[], options?: { timeout?: number }, @@ -243,7 +243,7 @@ export function toHaveClass( } export function toHaveCount( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: number, options?: { timeout?: number }, @@ -254,7 +254,7 @@ export function toHaveCount( } export function toHaveCSS( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, name: string, expected: string | RegExp, @@ -267,7 +267,7 @@ export function toHaveCSS( } export function toHaveId( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string | RegExp, options?: { timeout?: number }, @@ -279,7 +279,7 @@ export function toHaveId( } export function toHaveJSProperty( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, name: string, expected: any, @@ -291,7 +291,7 @@ export function toHaveJSProperty( } export function toHaveRole( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string, options?: { timeout?: number, ignoreCase?: boolean }, @@ -305,7 +305,7 @@ export function toHaveRole( } export function toHaveText( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string | RegExp | (string | RegExp)[], options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {}, @@ -324,7 +324,7 @@ export function toHaveText( } export function toHaveValue( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: string | RegExp, options?: { timeout?: number }, @@ -336,7 +336,7 @@ export function toHaveValue( } export function toHaveValues( - this: ExpectMatcherContext, + this: ExpectMatcherState, locator: LocatorEx, expected: (string | RegExp)[], options?: { timeout?: number }, @@ -348,7 +348,7 @@ export function toHaveValues( } export function toHaveTitle( - this: ExpectMatcherContext, + this: ExpectMatcherState, page: Page, expected: string | RegExp, options: { timeout?: number } = {}, @@ -361,7 +361,7 @@ export function toHaveTitle( } export function toHaveURL( - this: ExpectMatcherContext, + this: ExpectMatcherState, page: Page, expected: string | RegExp, options?: { ignoreCase?: boolean, timeout?: number }, @@ -376,7 +376,7 @@ export function toHaveURL( } export async function toBeOK( - this: ExpectMatcherContext, + this: ExpectMatcherState, response: APIResponseEx ) { const matcherName = 'toBeOK'; @@ -398,7 +398,7 @@ export async function toBeOK( } export async function toPass( - this: ExpectMatcherContext, + this: ExpectMatcherState, callback: () => any, options: { intervals?: number[]; diff --git a/packages/playwright/src/matchers/toBeTruthy.ts b/packages/playwright/src/matchers/toBeTruthy.ts index 1da531a6fb..0941ab7a63 100644 --- a/packages/playwright/src/matchers/toBeTruthy.ts +++ b/packages/playwright/src/matchers/toBeTruthy.ts @@ -17,12 +17,11 @@ import { expectTypes, callLogText } from '../util'; import { kNoElementsFoundError, matcherHint } from './matcherHint'; import type { MatcherResult } from './matcherHint'; -import { currentExpectTimeout } from '../common/globals'; -import type { ExpectMatcherContext } from './expect'; +import type { ExpectMatcherState } from '../../types/test'; import type { Locator } from 'playwright-core'; export async function toBeTruthy( - this: ExpectMatcherContext, + this: ExpectMatcherState, matcherName: string, receiver: Locator, receiverType: string, @@ -39,7 +38,7 @@ export async function toBeTruthy( promise: this.promise, }; - const timeout = currentExpectTimeout(options); + const timeout = options.timeout ?? this.timeout; const { matches, log, timedOut, received } = await query(!!this.isNot, timeout); const notFound = received === kNoElementsFoundError ? received : undefined; const actual = matches ? expected : unexpected; diff --git a/packages/playwright/src/matchers/toEqual.ts b/packages/playwright/src/matchers/toEqual.ts index 9dbf30ec5b..29d3fd4866 100644 --- a/packages/playwright/src/matchers/toEqual.ts +++ b/packages/playwright/src/matchers/toEqual.ts @@ -17,8 +17,7 @@ import { expectTypes, callLogText } from '../util'; import { matcherHint } from './matcherHint'; import type { MatcherResult } from './matcherHint'; -import { currentExpectTimeout } from '../common/globals'; -import type { ExpectMatcherContext } from './expect'; +import type { ExpectMatcherState } from '../../types/test'; import type { Locator } from 'playwright-core'; // Omit colon and one or more spaces, so can call getLabelPrinter. @@ -26,7 +25,7 @@ const EXPECTED_LABEL = 'Expected'; const RECEIVED_LABEL = 'Received'; export async function toEqual( - this: ExpectMatcherContext, + this: ExpectMatcherState, matcherName: string, receiver: Locator, receiverType: string, @@ -42,7 +41,7 @@ export async function toEqual( promise: this.promise, }; - const timeout = currentExpectTimeout(options); + const timeout = options.timeout ?? this.timeout; const { matches: pass, received, log, timedOut } = await query(!!this.isNot, timeout); diff --git a/packages/playwright/src/matchers/toMatchSnapshot.ts b/packages/playwright/src/matchers/toMatchSnapshot.ts index 83d20d22aa..7a3242100e 100644 --- a/packages/playwright/src/matchers/toMatchSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchSnapshot.ts @@ -16,7 +16,7 @@ import type { Locator, Page } from 'playwright-core'; import type { ExpectScreenshotOptions, Page as PageEx } from 'playwright-core/lib/client/page'; -import { currentTestInfo, currentExpectTimeout } from '../common/globals'; +import { currentTestInfo } from '../common/globals'; import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils'; import { getComparator, sanitizeForFilePath } from 'playwright-core/lib/utils'; import { @@ -30,7 +30,7 @@ import fs from 'fs'; import path from 'path'; import { mime } from 'playwright-core/lib/utilsBundle'; import type { TestInfoImpl } from '../worker/testInfo'; -import type { ExpectMatcherContext } from './expect'; +import type { ExpectMatcherState } from '../../types/test'; import type { MatcherResult } from './matcherHint'; import type { FullProjectInternal } from '../common/config'; @@ -291,7 +291,7 @@ class SnapshotHelper { } export function toMatchSnapshot( - this: ExpectMatcherContext, + this: ExpectMatcherState, received: Buffer | string, nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ImageComparatorOptions = {}, optOptions: ImageComparatorOptions = {} @@ -348,7 +348,7 @@ export function toHaveScreenshotStepTitle( } export async function toHaveScreenshot( - this: ExpectMatcherContext, + this: ExpectMatcherState, pageOrLocator: Page | Locator, nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {}, optOptions: ToHaveScreenshotOptions = {} @@ -380,7 +380,7 @@ export async function toHaveScreenshot( scale: helper.options.scale ?? 'css', style, isNot: !!this.isNot, - timeout: currentExpectTimeout(helper.options), + timeout: helper.options.timeout ?? this.timeout, comparator: helper.options.comparator, maxDiffPixels: helper.options.maxDiffPixels, maxDiffPixelRatio: helper.options.maxDiffPixelRatio, diff --git a/packages/playwright/src/matchers/toMatchText.ts b/packages/playwright/src/matchers/toMatchText.ts index 1d66ab8d68..790b402d2f 100644 --- a/packages/playwright/src/matchers/toMatchText.ts +++ b/packages/playwright/src/matchers/toMatchText.ts @@ -19,17 +19,18 @@ import type { ExpectedTextValue } from '@protocol/channels'; import { isRegExp, isString } from 'playwright-core/lib/utils'; import { expectTypes, callLogText } from '../util'; import { - type ExpectMatcherContext, printReceivedStringContainExpectedResult, printReceivedStringContainExpectedSubstring } from './expect'; +import { EXPECTED_COLOR } from '../common/expectBundle'; +import type { ExpectMatcherState } from '../../types/test'; import { kNoElementsFoundError, matcherHint } from './matcherHint'; import type { MatcherResult } from './matcherHint'; -import { currentExpectTimeout } from '../common/globals'; import type { Locator } from 'playwright-core'; +import { colors } from 'playwright-core/lib/utilsBundle'; export async function toMatchText( - this: ExpectMatcherContext, + this: ExpectMatcherState, matcherName: string, receiver: Locator, receiverType: string, @@ -48,18 +49,15 @@ export async function toMatchText( !(typeof expected === 'string') && !(expected && typeof expected.test === 'function') ) { - throw new Error( - this.utils.matcherErrorMessage( - matcherHint(this, receiver, matcherName, receiver, expected, matcherOptions), - `${this.utils.EXPECTED_COLOR( - 'expected', - )} value must be a string or regular expression`, - this.utils.printWithType('Expected', expected, this.utils.printExpected), - ), - ); + // Same format as jest's matcherErrorMessage + throw new Error([ + matcherHint(this, receiver, matcherName, receiver, expected, matcherOptions), + `${colors.bold('Matcher error')}: ${EXPECTED_COLOR('expected',)} value must be a string or regular expression`, + this.utils.printWithType('Expected', expected, this.utils.printExpected) + ].join('\n\n')); } - const timeout = currentExpectTimeout(options); + const timeout = options.timeout ?? this.timeout; const { matches: pass, received, log, timedOut } = await query(!!this.isNot, timeout); const stringSubstring = options.matchSubstring ? 'substring' : 'string'; diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 4f52130d10..e1299272a8 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -6510,9 +6510,21 @@ export interface ExpectMatcherUtils { } export type ExpectMatcherState = { + /** + * Whether this matcher was called with the negated .not modifier. + */ isNot: boolean; + /** + * - 'rejects' if matcher was called with the promise .rejects modifier + * - 'resolves' if matcher was called with the promise .resolves modifier + * - '' if matcher was not called with a promise modifier + */ promise: 'rejects' | 'resolves' | ''; utils: ExpectMatcherUtils; + /** + * Timeout in milliseconds for the assertion to be fulfilled. + */ + timeout: number; }; export type MatcherReturnType = { diff --git a/tests/playwright-test/expect.spec.ts b/tests/playwright-test/expect.spec.ts index e79a3f9c19..1393bdd3ff 100644 --- a/tests/playwright-test/expect.spec.ts +++ b/tests/playwright-test/expect.spec.ts @@ -1000,3 +1000,42 @@ test('should respect timeout from configured expect when used outside of the tes expect(stdout).toBe(''); expect(stripAnsi(stderr)).toContain('Timed out 10ms waiting for expect(locator).toBeAttached()'); }); + +test('should expose timeout to custom matchers', async ({ runInlineTest, runTSC }) => { + const files = { + 'playwright.config.ts': ` + export default { + expect: { timeout: 1100 } + }; + `, + 'a.test.ts': ` + import type { ExpectMatcherState, MatcherReturnType } from '@playwright/test'; + import { test, expect as base } from '@playwright/test'; + + const expect = base.extend({ + assertTimeout(page: any, value: number) { + const pass = this.timeout === value; + return { + message: () => 'Unexpected timeout: ' + this.timeout, + pass, + name: 'assertTimeout', + }; + } + }); + + test('from config', async ({ page }) => { + expect(page).assertTimeout(1100); + }); + test('from expect.configure', async ({ page }) => { + expect.configure({ timeout: 2200 })(page).assertTimeout(2200); + }); + `, + }; + const { exitCode } = await runTSC(files); + expect(exitCode).toBe(0); + + const result = await runInlineTest(files); + expect(result.exitCode).toBe(0); + expect(result.failed).toBe(0); + expect(result.passed).toBe(2); +}); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 002525f89b..0367f3259c 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -358,9 +358,21 @@ export interface ExpectMatcherUtils { } export type ExpectMatcherState = { + /** + * Whether this matcher was called with the negated .not modifier. + */ isNot: boolean; + /** + * - 'rejects' if matcher was called with the promise .rejects modifier + * - 'resolves' if matcher was called with the promise .resolves modifier + * - '' if matcher was not called with a promise modifier + */ promise: 'rejects' | 'resolves' | ''; utils: ExpectMatcherUtils; + /** + * Timeout in milliseconds for the assertion to be fulfilled. + */ + timeout: number; }; export type MatcherReturnType = {