diff --git a/packages/playwright/src/matchers/error.ts b/packages/playwright/src/matchers/error.ts index 5bcff78636..12762a3378 100644 --- a/packages/playwright/src/matchers/error.ts +++ b/packages/playwright/src/matchers/error.ts @@ -15,10 +15,12 @@ */ import type { ExpectMatcherState } from '../../types/test'; -import { matcherHint } from './matcherHint'; +import { kNoElementsFoundError, matcherHint } from './matcherHint'; import { colors } from 'playwright-core/lib/utilsBundle'; import type { Locator } from 'playwright-core'; -import { EXPECTED_COLOR } from '../common/expectBundle'; +import { EXPECTED_COLOR, printReceived } from '../common/expectBundle'; +import { printReceivedStringContainExpectedResult, printReceivedStringContainExpectedSubstring } from './expect'; +import { callLogText } from '../util'; export function toMatchExpectedStringOrPredicateVerification( state: ExpectMatcherState, @@ -49,3 +51,54 @@ export function toMatchExpectedStringOrPredicateVerification( ].join('\n\n')); } } + +export function textMatcherMessage(state: ExpectMatcherState, matcherName: string, receiver: Locator | undefined, expression: string, expected: string | RegExp | Function, received: string | undefined, callLog: string[] | undefined, stringName: string, pass: boolean, didTimeout: boolean, timeout: number): string { + const matcherOptions = { + isNot: state.isNot, + promise: state.promise, + }; + const receivedString = received || ''; + const messagePrefix = matcherHint(state, receiver, matcherName, expression, undefined, matcherOptions, didTimeout ? timeout : undefined); + const notFound = received === kNoElementsFoundError; + + let printedReceived: string | undefined; + let printedExpected: string | undefined; + let printedDiff: string | undefined; + if (typeof expected === 'function') { + printedExpected = `Expected predicate to ${!state.isNot ? 'succeed' : 'fail'}`; + printedReceived = `Received string: ${printReceived(receivedString)}`; + } else { + if (pass) { + if (typeof expected === 'string') { + if (notFound) { + printedExpected = `Expected ${stringName}: not ${state.utils.printExpected(expected)}`; + printedReceived = `Received: ${received}`; + } else { + printedExpected = `Expected ${stringName}: not ${state.utils.printExpected(expected)}`; + const formattedReceived = printReceivedStringContainExpectedSubstring(receivedString, receivedString.indexOf(expected), expected.length); + printedReceived = `Received string: ${formattedReceived}`; + } + } else { + if (notFound) { + printedExpected = `Expected pattern: not ${state.utils.printExpected(expected)}`; + printedReceived = `Received: ${received}`; + } else { + printedExpected = `Expected pattern: not ${state.utils.printExpected(expected)}`; + const formattedReceived = printReceivedStringContainExpectedResult(receivedString, typeof expected.exec === 'function' ? expected.exec(receivedString) : null); + printedReceived = `Received string: ${formattedReceived}`; + } + } + } else { + const labelExpected = `Expected ${typeof expected === 'string' ? stringName : 'pattern'}`; + if (notFound) { + printedExpected = `${labelExpected}: ${state.utils.printExpected(expected)}`; + printedReceived = `Received: ${received}`; + } else { + printedDiff = state.utils.printDiffOrStringify(expected, receivedString, labelExpected, 'Received string', false); + } + } + } + + const resultDetails = printedDiff ? printedDiff : printedExpected + '\n' + printedReceived; + return messagePrefix + resultDetails + callLogText(callLog); +} diff --git a/packages/playwright/src/matchers/matchers.ts b/packages/playwright/src/matchers/matchers.ts index c2e9390400..8085269342 100644 --- a/packages/playwright/src/matchers/matchers.ts +++ b/packages/playwright/src/matchers/matchers.ts @@ -26,8 +26,7 @@ import { currentTestInfo } from '../common/globals'; import { TestInfoImpl } from '../worker/testInfo'; import type { ExpectMatcherState } from '../../types/test'; import { takeFirst } from '../common/config'; -import { matcherHint } from './matcherHint'; -import { toMatchExpectedStringOrPredicateVerification } from './error'; +import { textMatcherMessage, toMatchExpectedStringOrPredicateVerification } from './error'; export interface LocatorEx extends Locator { _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }>; @@ -407,17 +406,20 @@ export async function toHaveURL( const timeout = options?.timeout ?? this.timeout; let conditionSucceeded = false; + let lastCheckedURLString: string | undefined = undefined; try { await page.mainFrame().waitForURL( url => { const baseURL: string | undefined = (page.context() as any)._options .baseURL; + lastCheckedURLString = url.toString(); + if (options?.ignoreCase) { return ( !this.isNot === urlMatches( baseURL?.toLocaleLowerCase(), - url.toString().toLocaleLowerCase(), + lastCheckedURLString.toLocaleLowerCase(), typeof expected === 'string' ? expected.toLocaleLowerCase() : expected, @@ -429,7 +431,7 @@ export async function toHaveURL( !this.isNot === urlMatches( baseURL, - url.toString(), + lastCheckedURLString, expected, ) ); @@ -445,22 +447,24 @@ export async function toHaveURL( if (conditionSucceeded) return { pass: !this.isNot, message: () => '' }; - const matcherOptions = { - isNot: this.isNot, - promise: this.promise, - }; return { pass: this.isNot, message: () => - matcherHint( + textMatcherMessage( this, - undefined, matcherName, - expression, undefined, - matcherOptions, + expression, + expected, + lastCheckedURLString, + undefined, + 'string', + this.isNot, + true, timeout, ), + actual: lastCheckedURLString, + timeout, }; } diff --git a/packages/playwright/src/matchers/toMatchText.ts b/packages/playwright/src/matchers/toMatchText.ts index 251da7e84c..b4ded21e0e 100644 --- a/packages/playwright/src/matchers/toMatchText.ts +++ b/packages/playwright/src/matchers/toMatchText.ts @@ -15,16 +15,11 @@ */ -import { expectTypes, callLogText } from '../util'; -import { - printReceivedStringContainExpectedResult, - printReceivedStringContainExpectedSubstring -} from './expect'; +import { expectTypes } from '../util'; import type { ExpectMatcherState } from '../../types/test'; -import { kNoElementsFoundError, matcherHint } from './matcherHint'; import type { MatcherResult } from './matcherHint'; import type { Locator } from 'playwright-core'; -import { toMatchExpectedStringOrPredicateVerification } from './error'; +import { textMatcherMessage, toMatchExpectedStringOrPredicateVerification } from './error'; export async function toMatchText( this: ExpectMatcherState, @@ -50,57 +45,25 @@ export async function toMatchText( }; } - const matcherOptions = { - isNot: this.isNot, - promise: this.promise, - }; const stringSubstring = options.matchSubstring ? 'substring' : 'string'; - const receivedString = received || ''; - const messagePrefix = matcherHint(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined); - const notFound = received === kNoElementsFoundError; - - let printedReceived: string | undefined; - let printedExpected: string | undefined; - let printedDiff: string | undefined; - if (pass) { - if (typeof expected === 'string') { - if (notFound) { - printedExpected = `Expected ${stringSubstring}: not ${this.utils.printExpected(expected)}`; - printedReceived = `Received: ${received}`; - } else { - printedExpected = `Expected ${stringSubstring}: not ${this.utils.printExpected(expected)}`; - const formattedReceived = printReceivedStringContainExpectedSubstring(receivedString, receivedString.indexOf(expected), expected.length); - printedReceived = `Received string: ${formattedReceived}`; - } - } else { - if (notFound) { - printedExpected = `Expected pattern: not ${this.utils.printExpected(expected)}`; - printedReceived = `Received: ${received}`; - } else { - printedExpected = `Expected pattern: not ${this.utils.printExpected(expected)}`; - const formattedReceived = printReceivedStringContainExpectedResult(receivedString, typeof expected.exec === 'function' ? expected.exec(receivedString) : null); - printedReceived = `Received string: ${formattedReceived}`; - } - } - } else { - const labelExpected = `Expected ${typeof expected === 'string' ? stringSubstring : 'pattern'}`; - if (notFound) { - printedExpected = `${labelExpected}: ${this.utils.printExpected(expected)}`; - printedReceived = `Received: ${received}`; - } else { - printedDiff = this.utils.printDiffOrStringify(expected, receivedString, labelExpected, 'Received string', false); - } - } - - const message = () => { - const resultDetails = printedDiff ? printedDiff : printedExpected + '\n' + printedReceived; - return messagePrefix + resultDetails + callLogText(log); - }; return { name: matcherName, expected, - message, + message: () => + textMatcherMessage( + this, + matcherName, + receiver, + 'locator', + expected, + received, + log, + stringSubstring, + pass, + !!timedOut, + timeout, + ), pass, actual: received, log, diff --git a/tests/page/expect-misc.spec.ts b/tests/page/expect-misc.spec.ts index 5534f16078..3c51975920 100644 --- a/tests/page/expect-misc.spec.ts +++ b/tests/page/expect-misc.spec.ts @@ -241,20 +241,47 @@ test.describe('toHaveURL', () => { await expect(page).toHaveURL('data:text/html,