mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(expect): expose expect timeout (#30969)
Fixes https://github.com/microsoft/playwright/issues/30583
This commit is contained in:
parent
c906448fe2
commit
9884c851ff
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const expect: typeof import('../../bundles/expect/node_modules/expect/build').expect = require('./expectBundleImpl').expect;
|
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 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 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;
|
export const printReceived: typeof import('../../bundles/expect/node_modules/jest-matcher-utils/build').printReceived = require('./expectBundleImpl').printReceived;
|
||||||
|
|||||||
@ -33,24 +33,6 @@ export function currentlyLoadingFileSuite() {
|
|||||||
return currentFileSuite;
|
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;
|
let _isWorkerProcess = false;
|
||||||
|
|
||||||
export function setIsWorkerProcess() {
|
export function setIsWorkerProcess() {
|
||||||
|
|||||||
@ -49,8 +49,8 @@ import {
|
|||||||
toPass
|
toPass
|
||||||
} from './matchers';
|
} from './matchers';
|
||||||
import { toMatchSnapshot, toHaveScreenshot, toHaveScreenshotStepTitle } from './toMatchSnapshot';
|
import { toMatchSnapshot, toHaveScreenshot, toHaveScreenshotStepTitle } from './toMatchSnapshot';
|
||||||
import type { Expect } from '../../types/test';
|
import type { Expect, ExpectMatcherState } from '../../types/test';
|
||||||
import { currentTestInfo, currentExpectTimeout, setCurrentExpectConfigureTimeout } from '../common/globals';
|
import { currentTestInfo } from '../common/globals';
|
||||||
import { filteredStackTrace, trimLongString } from '../util';
|
import { filteredStackTrace, trimLongString } from '../util';
|
||||||
import {
|
import {
|
||||||
expect as expectLibrary,
|
expect as expectLibrary,
|
||||||
@ -58,7 +58,6 @@ import {
|
|||||||
RECEIVED_COLOR,
|
RECEIVED_COLOR,
|
||||||
printReceived,
|
printReceived,
|
||||||
} from '../common/expectBundle';
|
} from '../common/expectBundle';
|
||||||
export type { ExpectMatcherContext } from '../common/expectBundle';
|
|
||||||
import { zones } from 'playwright-core/lib/utils';
|
import { zones } from 'playwright-core/lib/utils';
|
||||||
import { TestInfoImpl } from '../worker/testInfo';
|
import { TestInfoImpl } from '../worker/testInfo';
|
||||||
import { ExpectError } from './matcherHint';
|
import { ExpectError } from './matcherHint';
|
||||||
@ -129,7 +128,20 @@ function createExpect(info: ExpectMetaInfo) {
|
|||||||
|
|
||||||
if (property === 'extend') {
|
if (property === 'extend') {
|
||||||
return (matchers: any) => {
|
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;
|
return expectInstance;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -171,8 +183,6 @@ function createExpect(info: ExpectMetaInfo) {
|
|||||||
return expectInstance;
|
return expectInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const expect: Expect<{}> = createExpect({});
|
|
||||||
|
|
||||||
expectLibrary.setState({ expand: false });
|
expectLibrary.setState({ expand: false });
|
||||||
|
|
||||||
const customAsyncMatchers = {
|
const customAsyncMatchers = {
|
||||||
@ -245,7 +255,7 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
|||||||
if (this._info.isPoll) {
|
if (this._info.isPoll) {
|
||||||
if ((customAsyncMatchers as any)[matcherName] || matcherName === 'resolves' || matcherName === 'rejects')
|
if ((customAsyncMatchers as any)[matcherName] || matcherName === 'resolves' || matcherName === 'rejects')
|
||||||
throw new Error(`\`expect.poll()\` does not support "${matcherName}" matcher.`);
|
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[]) => {
|
return (...args: any[]) => {
|
||||||
const testInfo = currentTestInfo();
|
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[]) {
|
function computeArgsSuffix(matcherName: string, args: any[]) {
|
||||||
let value = '';
|
let value = '';
|
||||||
if (matcherName === 'toHaveScreenshot')
|
if (matcherName === 'toHaveScreenshot')
|
||||||
@ -344,7 +370,7 @@ function computeArgsSuffix(matcherName: string, args: any[]) {
|
|||||||
return value ? `(${value})` : '';
|
return value ? `(${value})` : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
expectLibrary.extend(customMatchers);
|
export const expect: Expect<{}> = createExpect({}).extend(customMatchers);
|
||||||
|
|
||||||
export function mergeExpects(...expects: any[]) {
|
export function mergeExpects(...expects: any[]) {
|
||||||
return expect;
|
return expect;
|
||||||
|
|||||||
@ -15,14 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { colors } from 'playwright-core/lib/utilsBundle';
|
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 { Locator } from 'playwright-core';
|
||||||
import type { StackFrame } from '@protocol/channels';
|
import type { StackFrame } from '@protocol/channels';
|
||||||
import { stringifyStackFrames } from 'playwright-core/lib/utils';
|
import { stringifyStackFrames } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
export const kNoElementsFoundError = '<element(s) not found>';
|
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';
|
let header = state.utils.matcherHint(matcherName, expression, actual, matcherOptions).replace(/ \/\/ deep equality/, '') + '\n\n';
|
||||||
if (timeout)
|
if (timeout)
|
||||||
header = colors.red(`Timed out ${timeout}ms waiting for `) + header;
|
header = colors.red(`Timed out ${timeout}ms waiting for `) + header;
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import { toExpectedTextValues, toMatchText } from './toMatchText';
|
|||||||
import { constructURLBasedOnBaseURL, isRegExp, isString, isTextualMimeType, pollAgainstDeadline } from 'playwright-core/lib/utils';
|
import { constructURLBasedOnBaseURL, isRegExp, isString, isTextualMimeType, pollAgainstDeadline } from 'playwright-core/lib/utils';
|
||||||
import { currentTestInfo } from '../common/globals';
|
import { currentTestInfo } from '../common/globals';
|
||||||
import { TestInfoImpl } from '../worker/testInfo';
|
import { TestInfoImpl } from '../worker/testInfo';
|
||||||
import type { ExpectMatcherContext } from './expect';
|
import type { ExpectMatcherState } from '../../types/test';
|
||||||
import { takeFirst } from '../common/config';
|
import { takeFirst } from '../common/config';
|
||||||
|
|
||||||
interface LocatorEx extends Locator {
|
interface LocatorEx extends Locator {
|
||||||
@ -36,7 +36,7 @@ interface APIResponseEx extends APIResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeAttached(
|
export function toBeAttached(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { attached?: boolean, timeout?: number },
|
options?: { attached?: boolean, timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -50,7 +50,7 @@ export function toBeAttached(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeChecked(
|
export function toBeChecked(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { checked?: boolean, timeout?: number },
|
options?: { checked?: boolean, timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -64,7 +64,7 @@ export function toBeChecked(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeDisabled(
|
export function toBeDisabled(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -74,7 +74,7 @@ export function toBeDisabled(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeEditable(
|
export function toBeEditable(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { editable?: boolean, timeout?: number },
|
options?: { editable?: boolean, timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -88,7 +88,7 @@ export function toBeEditable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeEmpty(
|
export function toBeEmpty(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -98,7 +98,7 @@ export function toBeEmpty(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeEnabled(
|
export function toBeEnabled(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { enabled?: boolean, timeout?: number },
|
options?: { enabled?: boolean, timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -112,7 +112,7 @@ export function toBeEnabled(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeFocused(
|
export function toBeFocused(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -122,7 +122,7 @@ export function toBeFocused(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeHidden(
|
export function toBeHidden(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -132,7 +132,7 @@ export function toBeHidden(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeVisible(
|
export function toBeVisible(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { visible?: boolean, timeout?: number },
|
options?: { visible?: boolean, timeout?: number },
|
||||||
) {
|
) {
|
||||||
@ -146,7 +146,7 @@ export function toBeVisible(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBeInViewport(
|
export function toBeInViewport(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number, ratio?: number },
|
options?: { timeout?: number, ratio?: number },
|
||||||
) {
|
) {
|
||||||
@ -156,7 +156,7 @@ export function toBeInViewport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toContainText(
|
export function toContainText(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string | RegExp | (string | RegExp)[],
|
expected: string | RegExp | (string | RegExp)[],
|
||||||
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {},
|
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {},
|
||||||
@ -175,7 +175,7 @@ export function toContainText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveAccessibleDescription(
|
export function toHaveAccessibleDescription(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options?: { timeout?: number, ignoreCase?: boolean },
|
options?: { timeout?: number, ignoreCase?: boolean },
|
||||||
@ -187,7 +187,7 @@ export function toHaveAccessibleDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveAccessibleName(
|
export function toHaveAccessibleName(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options?: { timeout?: number, ignoreCase?: boolean },
|
options?: { timeout?: number, ignoreCase?: boolean },
|
||||||
@ -199,7 +199,7 @@ export function toHaveAccessibleName(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveAttribute(
|
export function toHaveAttribute(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
name: string,
|
name: string,
|
||||||
expected: string | RegExp | undefined | { timeout?: number },
|
expected: string | RegExp | undefined | { timeout?: number },
|
||||||
@ -224,7 +224,7 @@ export function toHaveAttribute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveClass(
|
export function toHaveClass(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string | RegExp | (string | RegExp)[],
|
expected: string | RegExp | (string | RegExp)[],
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
@ -243,7 +243,7 @@ export function toHaveClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveCount(
|
export function toHaveCount(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: number,
|
expected: number,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
@ -254,7 +254,7 @@ export function toHaveCount(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveCSS(
|
export function toHaveCSS(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
name: string,
|
name: string,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
@ -267,7 +267,7 @@ export function toHaveCSS(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveId(
|
export function toHaveId(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
@ -279,7 +279,7 @@ export function toHaveId(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveJSProperty(
|
export function toHaveJSProperty(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
name: string,
|
name: string,
|
||||||
expected: any,
|
expected: any,
|
||||||
@ -291,7 +291,7 @@ export function toHaveJSProperty(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveRole(
|
export function toHaveRole(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string,
|
expected: string,
|
||||||
options?: { timeout?: number, ignoreCase?: boolean },
|
options?: { timeout?: number, ignoreCase?: boolean },
|
||||||
@ -305,7 +305,7 @@ export function toHaveRole(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveText(
|
export function toHaveText(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string | RegExp | (string | RegExp)[],
|
expected: string | RegExp | (string | RegExp)[],
|
||||||
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {},
|
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {},
|
||||||
@ -324,7 +324,7 @@ export function toHaveText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveValue(
|
export function toHaveValue(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
@ -336,7 +336,7 @@ export function toHaveValue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveValues(
|
export function toHaveValues(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
expected: (string | RegExp)[],
|
expected: (string | RegExp)[],
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
@ -348,7 +348,7 @@ export function toHaveValues(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveTitle(
|
export function toHaveTitle(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
page: Page,
|
page: Page,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options: { timeout?: number } = {},
|
options: { timeout?: number } = {},
|
||||||
@ -361,7 +361,7 @@ export function toHaveTitle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveURL(
|
export function toHaveURL(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
page: Page,
|
page: Page,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options?: { ignoreCase?: boolean, timeout?: number },
|
options?: { ignoreCase?: boolean, timeout?: number },
|
||||||
@ -376,7 +376,7 @@ export function toHaveURL(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function toBeOK(
|
export async function toBeOK(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
response: APIResponseEx
|
response: APIResponseEx
|
||||||
) {
|
) {
|
||||||
const matcherName = 'toBeOK';
|
const matcherName = 'toBeOK';
|
||||||
@ -398,7 +398,7 @@ export async function toBeOK(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function toPass(
|
export async function toPass(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
callback: () => any,
|
callback: () => any,
|
||||||
options: {
|
options: {
|
||||||
intervals?: number[];
|
intervals?: number[];
|
||||||
|
|||||||
@ -17,12 +17,11 @@
|
|||||||
import { expectTypes, callLogText } from '../util';
|
import { expectTypes, callLogText } from '../util';
|
||||||
import { kNoElementsFoundError, matcherHint } from './matcherHint';
|
import { kNoElementsFoundError, matcherHint } from './matcherHint';
|
||||||
import type { MatcherResult } from './matcherHint';
|
import type { MatcherResult } from './matcherHint';
|
||||||
import { currentExpectTimeout } from '../common/globals';
|
import type { ExpectMatcherState } from '../../types/test';
|
||||||
import type { ExpectMatcherContext } from './expect';
|
|
||||||
import type { Locator } from 'playwright-core';
|
import type { Locator } from 'playwright-core';
|
||||||
|
|
||||||
export async function toBeTruthy(
|
export async function toBeTruthy(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
matcherName: string,
|
matcherName: string,
|
||||||
receiver: Locator,
|
receiver: Locator,
|
||||||
receiverType: string,
|
receiverType: string,
|
||||||
@ -39,7 +38,7 @@ export async function toBeTruthy(
|
|||||||
promise: this.promise,
|
promise: this.promise,
|
||||||
};
|
};
|
||||||
|
|
||||||
const timeout = currentExpectTimeout(options);
|
const timeout = options.timeout ?? this.timeout;
|
||||||
const { matches, log, timedOut, received } = await query(!!this.isNot, timeout);
|
const { matches, log, timedOut, received } = await query(!!this.isNot, timeout);
|
||||||
const notFound = received === kNoElementsFoundError ? received : undefined;
|
const notFound = received === kNoElementsFoundError ? received : undefined;
|
||||||
const actual = matches ? expected : unexpected;
|
const actual = matches ? expected : unexpected;
|
||||||
|
|||||||
@ -17,8 +17,7 @@
|
|||||||
import { expectTypes, callLogText } from '../util';
|
import { expectTypes, callLogText } from '../util';
|
||||||
import { matcherHint } from './matcherHint';
|
import { matcherHint } from './matcherHint';
|
||||||
import type { MatcherResult } from './matcherHint';
|
import type { MatcherResult } from './matcherHint';
|
||||||
import { currentExpectTimeout } from '../common/globals';
|
import type { ExpectMatcherState } from '../../types/test';
|
||||||
import type { ExpectMatcherContext } from './expect';
|
|
||||||
import type { Locator } from 'playwright-core';
|
import type { Locator } from 'playwright-core';
|
||||||
|
|
||||||
// Omit colon and one or more spaces, so can call getLabelPrinter.
|
// Omit colon and one or more spaces, so can call getLabelPrinter.
|
||||||
@ -26,7 +25,7 @@ const EXPECTED_LABEL = 'Expected';
|
|||||||
const RECEIVED_LABEL = 'Received';
|
const RECEIVED_LABEL = 'Received';
|
||||||
|
|
||||||
export async function toEqual<T>(
|
export async function toEqual<T>(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
matcherName: string,
|
matcherName: string,
|
||||||
receiver: Locator,
|
receiver: Locator,
|
||||||
receiverType: string,
|
receiverType: string,
|
||||||
@ -42,7 +41,7 @@ export async function toEqual<T>(
|
|||||||
promise: this.promise,
|
promise: this.promise,
|
||||||
};
|
};
|
||||||
|
|
||||||
const timeout = currentExpectTimeout(options);
|
const timeout = options.timeout ?? this.timeout;
|
||||||
|
|
||||||
const { matches: pass, received, log, timedOut } = await query(!!this.isNot, timeout);
|
const { matches: pass, received, log, timedOut } = await query(!!this.isNot, timeout);
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import type { Locator, Page } from 'playwright-core';
|
import type { Locator, Page } from 'playwright-core';
|
||||||
import type { ExpectScreenshotOptions, Page as PageEx } from 'playwright-core/lib/client/page';
|
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 type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils';
|
||||||
import { getComparator, sanitizeForFilePath } from 'playwright-core/lib/utils';
|
import { getComparator, sanitizeForFilePath } from 'playwright-core/lib/utils';
|
||||||
import {
|
import {
|
||||||
@ -30,7 +30,7 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { mime } from 'playwright-core/lib/utilsBundle';
|
import { mime } from 'playwright-core/lib/utilsBundle';
|
||||||
import type { TestInfoImpl } from '../worker/testInfo';
|
import type { TestInfoImpl } from '../worker/testInfo';
|
||||||
import type { ExpectMatcherContext } from './expect';
|
import type { ExpectMatcherState } from '../../types/test';
|
||||||
import type { MatcherResult } from './matcherHint';
|
import type { MatcherResult } from './matcherHint';
|
||||||
import type { FullProjectInternal } from '../common/config';
|
import type { FullProjectInternal } from '../common/config';
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ class SnapshotHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toMatchSnapshot(
|
export function toMatchSnapshot(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
received: Buffer | string,
|
received: Buffer | string,
|
||||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ImageComparatorOptions = {},
|
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ImageComparatorOptions = {},
|
||||||
optOptions: ImageComparatorOptions = {}
|
optOptions: ImageComparatorOptions = {}
|
||||||
@ -348,7 +348,7 @@ export function toHaveScreenshotStepTitle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function toHaveScreenshot(
|
export async function toHaveScreenshot(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
pageOrLocator: Page | Locator,
|
pageOrLocator: Page | Locator,
|
||||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {},
|
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {},
|
||||||
optOptions: ToHaveScreenshotOptions = {}
|
optOptions: ToHaveScreenshotOptions = {}
|
||||||
@ -380,7 +380,7 @@ export async function toHaveScreenshot(
|
|||||||
scale: helper.options.scale ?? 'css',
|
scale: helper.options.scale ?? 'css',
|
||||||
style,
|
style,
|
||||||
isNot: !!this.isNot,
|
isNot: !!this.isNot,
|
||||||
timeout: currentExpectTimeout(helper.options),
|
timeout: helper.options.timeout ?? this.timeout,
|
||||||
comparator: helper.options.comparator,
|
comparator: helper.options.comparator,
|
||||||
maxDiffPixels: helper.options.maxDiffPixels,
|
maxDiffPixels: helper.options.maxDiffPixels,
|
||||||
maxDiffPixelRatio: helper.options.maxDiffPixelRatio,
|
maxDiffPixelRatio: helper.options.maxDiffPixelRatio,
|
||||||
|
|||||||
@ -19,17 +19,18 @@ import type { ExpectedTextValue } from '@protocol/channels';
|
|||||||
import { isRegExp, isString } from 'playwright-core/lib/utils';
|
import { isRegExp, isString } from 'playwright-core/lib/utils';
|
||||||
import { expectTypes, callLogText } from '../util';
|
import { expectTypes, callLogText } from '../util';
|
||||||
import {
|
import {
|
||||||
type ExpectMatcherContext,
|
|
||||||
printReceivedStringContainExpectedResult,
|
printReceivedStringContainExpectedResult,
|
||||||
printReceivedStringContainExpectedSubstring
|
printReceivedStringContainExpectedSubstring
|
||||||
} from './expect';
|
} from './expect';
|
||||||
|
import { EXPECTED_COLOR } from '../common/expectBundle';
|
||||||
|
import type { ExpectMatcherState } from '../../types/test';
|
||||||
import { kNoElementsFoundError, matcherHint } from './matcherHint';
|
import { kNoElementsFoundError, matcherHint } from './matcherHint';
|
||||||
import type { MatcherResult } from './matcherHint';
|
import type { MatcherResult } from './matcherHint';
|
||||||
import { currentExpectTimeout } from '../common/globals';
|
|
||||||
import type { Locator } from 'playwright-core';
|
import type { Locator } from 'playwright-core';
|
||||||
|
import { colors } from 'playwright-core/lib/utilsBundle';
|
||||||
|
|
||||||
export async function toMatchText(
|
export async function toMatchText(
|
||||||
this: ExpectMatcherContext,
|
this: ExpectMatcherState,
|
||||||
matcherName: string,
|
matcherName: string,
|
||||||
receiver: Locator,
|
receiver: Locator,
|
||||||
receiverType: string,
|
receiverType: string,
|
||||||
@ -48,18 +49,15 @@ export async function toMatchText(
|
|||||||
!(typeof expected === 'string') &&
|
!(typeof expected === 'string') &&
|
||||||
!(expected && typeof expected.test === 'function')
|
!(expected && typeof expected.test === 'function')
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
// Same format as jest's matcherErrorMessage
|
||||||
this.utils.matcherErrorMessage(
|
throw new Error([
|
||||||
matcherHint(this, receiver, matcherName, receiver, expected, matcherOptions),
|
matcherHint(this, receiver, matcherName, receiver, expected, matcherOptions),
|
||||||
`${this.utils.EXPECTED_COLOR(
|
`${colors.bold('Matcher error')}: ${EXPECTED_COLOR('expected',)} value must be a string or regular expression`,
|
||||||
'expected',
|
this.utils.printWithType('Expected', expected, this.utils.printExpected)
|
||||||
)} value must be a string or regular expression`,
|
].join('\n\n'));
|
||||||
this.utils.printWithType('Expected', expected, this.utils.printExpected),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = currentExpectTimeout(options);
|
const timeout = options.timeout ?? this.timeout;
|
||||||
|
|
||||||
const { matches: pass, received, log, timedOut } = await query(!!this.isNot, timeout);
|
const { matches: pass, received, log, timedOut } = await query(!!this.isNot, timeout);
|
||||||
const stringSubstring = options.matchSubstring ? 'substring' : 'string';
|
const stringSubstring = options.matchSubstring ? 'substring' : 'string';
|
||||||
|
|||||||
12
packages/playwright/types/test.d.ts
vendored
12
packages/playwright/types/test.d.ts
vendored
@ -6510,9 +6510,21 @@ export interface ExpectMatcherUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ExpectMatcherState = {
|
export type ExpectMatcherState = {
|
||||||
|
/**
|
||||||
|
* Whether this matcher was called with the negated .not modifier.
|
||||||
|
*/
|
||||||
isNot: boolean;
|
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' | '';
|
promise: 'rejects' | 'resolves' | '';
|
||||||
utils: ExpectMatcherUtils;
|
utils: ExpectMatcherUtils;
|
||||||
|
/**
|
||||||
|
* Timeout in milliseconds for the assertion to be fulfilled.
|
||||||
|
*/
|
||||||
|
timeout: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MatcherReturnType = {
|
export type MatcherReturnType = {
|
||||||
|
|||||||
@ -1000,3 +1000,42 @@ test('should respect timeout from configured expect when used outside of the tes
|
|||||||
expect(stdout).toBe('');
|
expect(stdout).toBe('');
|
||||||
expect(stripAnsi(stderr)).toContain('Timed out 10ms waiting for expect(locator).toBeAttached()');
|
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);
|
||||||
|
});
|
||||||
|
|||||||
12
utils/generate_types/overrides-test.d.ts
vendored
12
utils/generate_types/overrides-test.d.ts
vendored
@ -358,9 +358,21 @@ export interface ExpectMatcherUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ExpectMatcherState = {
|
export type ExpectMatcherState = {
|
||||||
|
/**
|
||||||
|
* Whether this matcher was called with the negated .not modifier.
|
||||||
|
*/
|
||||||
isNot: boolean;
|
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' | '';
|
promise: 'rejects' | 'resolves' | '';
|
||||||
utils: ExpectMatcherUtils;
|
utils: ExpectMatcherUtils;
|
||||||
|
/**
|
||||||
|
* Timeout in milliseconds for the assertion to be fulfilled.
|
||||||
|
*/
|
||||||
|
timeout: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MatcherReturnType = {
|
export type MatcherReturnType = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user