Skip to content

Commit

Permalink
feat(expect): expose expect timeout (#30969)
Browse files Browse the repository at this point in the history
Fixes #30583
  • Loading branch information
yury-s committed May 24, 2024
1 parent c906448 commit 9884c85
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 83 deletions.
2 changes: 1 addition & 1 deletion packages/playwright/src/common/expectBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
18 changes: 0 additions & 18 deletions packages/playwright/src/common/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
42 changes: 34 additions & 8 deletions packages/playwright/src/matchers/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,15 @@ 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,
INVERTED_COLOR,
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';
Expand Down Expand Up @@ -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;
};
}
Expand Down Expand Up @@ -171,8 +183,6 @@ function createExpect(info: ExpectMetaInfo) {
return expectInstance;
}

export const expect: Expect<{}> = createExpect({});

expectLibrary.setState({ expand: false });

const customAsyncMatchers = {
Expand Down Expand Up @@ -245,7 +255,7 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
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();
Expand Down Expand Up @@ -337,14 +347,30 @@ 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')
value = toHaveScreenshotStepTitle(...args);
return value ? `(${value})` : '';
}

expectLibrary.extend(customMatchers);
export const expect: Expect<{}> = createExpect({}).extend(customMatchers);

export function mergeExpects(...expects: any[]) {
return expect;
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright/src/matchers/matcherHint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '<element(s) not found>';

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;
Expand Down
56 changes: 28 additions & 28 deletions packages/playwright/src/matchers/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -36,7 +36,7 @@ interface APIResponseEx extends APIResponse {
}

export function toBeAttached(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { attached?: boolean, timeout?: number },
) {
Expand All @@ -50,7 +50,7 @@ export function toBeAttached(
}

export function toBeChecked(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { checked?: boolean, timeout?: number },
) {
Expand All @@ -64,7 +64,7 @@ export function toBeChecked(
}

export function toBeDisabled(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { timeout?: number },
) {
Expand All @@ -74,7 +74,7 @@ export function toBeDisabled(
}

export function toBeEditable(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { editable?: boolean, timeout?: number },
) {
Expand All @@ -88,7 +88,7 @@ export function toBeEditable(
}

export function toBeEmpty(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { timeout?: number },
) {
Expand All @@ -98,7 +98,7 @@ export function toBeEmpty(
}

export function toBeEnabled(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { enabled?: boolean, timeout?: number },
) {
Expand All @@ -112,7 +112,7 @@ export function toBeEnabled(
}

export function toBeFocused(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { timeout?: number },
) {
Expand All @@ -122,7 +122,7 @@ export function toBeFocused(
}

export function toBeHidden(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { timeout?: number },
) {
Expand All @@ -132,7 +132,7 @@ export function toBeHidden(
}

export function toBeVisible(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { visible?: boolean, timeout?: number },
) {
Expand All @@ -146,7 +146,7 @@ export function toBeVisible(
}

export function toBeInViewport(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
options?: { timeout?: number, ratio?: number },
) {
Expand All @@ -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 } = {},
Expand All @@ -175,7 +175,7 @@ export function toContainText(
}

export function toHaveAccessibleDescription(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: string | RegExp,
options?: { timeout?: number, ignoreCase?: boolean },
Expand All @@ -187,7 +187,7 @@ export function toHaveAccessibleDescription(
}

export function toHaveAccessibleName(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: string | RegExp,
options?: { timeout?: number, ignoreCase?: boolean },
Expand All @@ -199,7 +199,7 @@ export function toHaveAccessibleName(
}

export function toHaveAttribute(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
name: string,
expected: string | RegExp | undefined | { timeout?: number },
Expand All @@ -224,7 +224,7 @@ export function toHaveAttribute(
}

export function toHaveClass(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: string | RegExp | (string | RegExp)[],
options?: { timeout?: number },
Expand All @@ -243,7 +243,7 @@ export function toHaveClass(
}

export function toHaveCount(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: number,
options?: { timeout?: number },
Expand All @@ -254,7 +254,7 @@ export function toHaveCount(
}

export function toHaveCSS(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
name: string,
expected: string | RegExp,
Expand All @@ -267,7 +267,7 @@ export function toHaveCSS(
}

export function toHaveId(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: string | RegExp,
options?: { timeout?: number },
Expand All @@ -279,7 +279,7 @@ export function toHaveId(
}

export function toHaveJSProperty(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
name: string,
expected: any,
Expand All @@ -291,7 +291,7 @@ export function toHaveJSProperty(
}

export function toHaveRole(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: string,
options?: { timeout?: number, ignoreCase?: boolean },
Expand All @@ -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 } = {},
Expand All @@ -324,7 +324,7 @@ export function toHaveText(
}

export function toHaveValue(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: string | RegExp,
options?: { timeout?: number },
Expand All @@ -336,7 +336,7 @@ export function toHaveValue(
}

export function toHaveValues(
this: ExpectMatcherContext,
this: ExpectMatcherState,
locator: LocatorEx,
expected: (string | RegExp)[],
options?: { timeout?: number },
Expand All @@ -348,7 +348,7 @@ export function toHaveValues(
}

export function toHaveTitle(
this: ExpectMatcherContext,
this: ExpectMatcherState,
page: Page,
expected: string | RegExp,
options: { timeout?: number } = {},
Expand All @@ -361,7 +361,7 @@ export function toHaveTitle(
}

export function toHaveURL(
this: ExpectMatcherContext,
this: ExpectMatcherState,
page: Page,
expected: string | RegExp,
options?: { ignoreCase?: boolean, timeout?: number },
Expand All @@ -376,7 +376,7 @@ export function toHaveURL(
}

export async function toBeOK(
this: ExpectMatcherContext,
this: ExpectMatcherState,
response: APIResponseEx
) {
const matcherName = 'toBeOK';
Expand All @@ -398,7 +398,7 @@ export async function toBeOK(
}

export async function toPass(
this: ExpectMatcherContext,
this: ExpectMatcherState,
callback: () => any,
options: {
intervals?: number[];
Expand Down
Loading

0 comments on commit 9884c85

Please sign in to comment.