diff --git a/docs/src/test-assertions-js.md b/docs/src/test-assertions-js.md index 9e86381a12..86af70d88c 100644 --- a/docs/src/test-assertions-js.md +++ b/docs/src/test-assertions-js.md @@ -145,7 +145,7 @@ await expect(locator).toHaveAttribute('type', 'text'); ``` ## expect(locator).toHaveClass(expected) -- `expected`: <[string] | [RegExp] | [Array]<[string]>> +- `expected`: <[string] | [RegExp] | [Array]<[string]|[RegExp]>> - `options` - `timeout`: <[number]> Time to retry assertion for, defaults to `timeout` in [`property: TestProject.expect`]. @@ -159,7 +159,7 @@ await expect(locator).toHaveClass(/selected/); Note that if array is passed as an expected value, entire lists can be asserted: ```js -const locator = page.locator('list > #component'); +const locator = page.locator('list > .component'); await expect(locator).toHaveClass(['component', 'component selected', 'component']); ``` @@ -215,7 +215,7 @@ await expect(locator).toHaveJSProperty('loaded', true); ``` ## expect(locator).toHaveText(expected, options) -- `expected`: <[string] | [RegExp] | [Array]<[string]>> +- `expected`: <[string] | [RegExp] | [Array]<[string]|[RegExp]>> - `options` - `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. diff --git a/src/test/matchers/matchers.ts b/src/test/matchers/matchers.ts index 0b7fa86e11..852dd76d30 100644 --- a/src/test/matchers/matchers.ts +++ b/src/test/matchers/matchers.ts @@ -134,7 +134,7 @@ export function toHaveAttribute( export function toHaveClass( this: ReturnType, locator: Locator, - expected: string | RegExp | string[], + expected: string | RegExp | (string | RegExp)[], options?: { timeout?: number }, ) { if (Array.isArray(expected)) { @@ -199,7 +199,7 @@ export function toHaveJSProperty( export function toHaveText( this: ReturnType, locator: Locator, - expected: string | RegExp | string[], + expected: string | RegExp | (string | RegExp)[], options?: { timeout?: number, useInnerText?: boolean }, ) { if (Array.isArray(expected)) { diff --git a/src/test/matchers/toBeTruthy.ts b/src/test/matchers/toBeTruthy.ts index 3a2d1327f1..0b57bec4e3 100644 --- a/src/test/matchers/toBeTruthy.ts +++ b/src/test/matchers/toBeTruthy.ts @@ -43,7 +43,6 @@ export async function toBeTruthy( let received: T; let pass = false; - // TODO: interrupt on timeout for nice message. await pollUntilDeadline(testInfo, async remainingTime => { received = await query(remainingTime); pass = !!received; diff --git a/src/test/matchers/toEqual.ts b/src/test/matchers/toEqual.ts index f76b9af39c..3b3cf0e171 100644 --- a/src/test/matchers/toEqual.ts +++ b/src/test/matchers/toEqual.ts @@ -36,6 +36,13 @@ const RECEIVED_LABEL = 'Received'; // The optional property of matcher context is true if undefined. const isExpand = (expand?: boolean): boolean => expand !== false; +function regExpTester(a: any, b: any): boolean | undefined { + if (typeof a === 'string' && b instanceof RegExp) { + b.lastIndex = 0; + return b.test(a); + } +} + export async function toEqual( this: ReturnType, matcherName: string, @@ -59,10 +66,9 @@ export async function toEqual( let received: T | undefined = undefined; let pass = false; - // TODO: interrupt on timeout for nice message. await pollUntilDeadline(testInfo, async remainingTime => { received = await query(remainingTime); - pass = equals(received, expected, [iterableEquality]); + pass = equals(received, expected, [iterableEquality, regExpTester]); return pass === !matcherOptions.isNot; }, options.timeout, testInfo._testFinished); diff --git a/src/test/matchers/toMatchText.ts b/src/test/matchers/toMatchText.ts index 15b0169d57..17e01a44b6 100644 --- a/src/test/matchers/toMatchText.ts +++ b/src/test/matchers/toMatchText.ts @@ -68,7 +68,6 @@ export async function toMatchText( let received: string; let pass = false; - // TODO: interrupt on timeout for nice message. await pollUntilDeadline(testInfo, async remainingTime => { received = await query(remainingTime); if (options.matchSubstring) diff --git a/tests/playwright-test/playwright.expect.misc.spec.ts b/tests/playwright-test/playwright.expect.misc.spec.ts index 3ac77969fb..0c3c69a5db 100644 --- a/tests/playwright-test/playwright.expect.misc.spec.ts +++ b/tests/playwright-test/playwright.expect.misc.spec.ts @@ -93,19 +93,19 @@ test('should support toHaveClass w/ array', async ({ runInlineTest }) => { test('pass', async ({ page }) => { await page.setContent('
'); const locator = page.locator('div'); - await expect(locator).toHaveClass(['foo', 'bar', 'baz']); + await expect(locator).toHaveClass(['foo', 'bar', /[a-z]az/]); }); test('fail', async ({ page }) => { await page.setContent('
'); const locator = page.locator('div'); - await expect(locator).toHaveClass(['foo', 'bar', 'baz'], { timeout: 1000 }); + await expect(locator).toHaveClass(['foo', 'bar', /[a-z]az/], { timeout: 1000 }); }); `, }, { workers: 1 }); const output = stripAscii(result.output); expect(output).toContain('expect(received).toHaveClass(expected)'); - expect(output).toContain('- \"baz\",'); + expect(output).toContain('- /[a-z]az/,'); expect(result.passed).toBe(1); expect(result.failed).toBe(1); expect(result.exitCode).toBe(1); diff --git a/tests/playwright-test/playwright.expect.text.spec.ts b/tests/playwright-test/playwright.expect.text.spec.ts index d0dd33c827..6f5e4b2c32 100644 --- a/tests/playwright-test/playwright.expect.text.spec.ts +++ b/tests/playwright-test/playwright.expect.text.spec.ts @@ -84,22 +84,22 @@ test('should support toHaveText w/ array', async ({ runInlineTest }) => { const { test } = pwt; test('pass', async ({ page }) => { - await page.setContent('
Text 1
Text 2
'); + await page.setContent('
Text 1
Text 2a
'); const locator = page.locator('div'); - await expect(locator).toHaveText(['Text 1', 'Text 2']); + await expect(locator).toHaveText(['Text 1', /Text \\d+a/]); }); test('fail', async ({ page }) => { await page.setContent('
Text 1
Text 3
'); const locator = page.locator('div'); - await expect(locator).toHaveText(['Text 1', 'Text 2'], { timeout: 1000 }); + await expect(locator).toHaveText(['Text 1', /Text \\d+a/], { timeout: 1000 }); }); `, }, { workers: 1 }); const output = stripAscii(result.output); expect(output).toContain('Error: expect(received).toHaveText(expected) // deep equality'); expect(output).toContain('await expect(locator).toHaveText'); - expect(output).toContain('- \"Text 2\"'); + expect(output).toContain('- /Text \\d+a/'); expect(result.passed).toBe(1); expect(result.failed).toBe(1); expect(result.exitCode).toBe(1);