From 7acbd052e4fbf017837fbfdf72288d53f785592d Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 10 Aug 2022 15:10:25 -0700 Subject: [PATCH] test: migrate some expect() tests to be more readable (#16394) This moves some expect() matchers tests from test runner tests to page tests, because these are implemented through a library call anyway. Makes tests more readbable, faster and easier to test specific details. --- tests/config/utils.ts | 5 + tests/page/expect-boolean.spec.ts | 365 ++++++++++ tests/page/expect-misc.spec.ts | 257 +++++++ tests/page/expect-to-have-text.spec.ts | 226 ++++++ tests/page/expect-to-have-value.spec.ts | 139 ++++ tests/playwright-test/expect.spec.ts | 141 ++++ .../playwright.expect.misc.spec.ts | 387 ---------- .../playwright.expect.text.spec.ts | 683 ------------------ .../playwright.expect.true.spec.ts | 510 ------------- 9 files changed, 1133 insertions(+), 1580 deletions(-) create mode 100644 tests/page/expect-boolean.spec.ts create mode 100644 tests/page/expect-misc.spec.ts create mode 100644 tests/page/expect-to-have-text.spec.ts create mode 100644 tests/page/expect-to-have-value.spec.ts delete mode 100644 tests/playwright-test/playwright.expect.misc.spec.ts delete mode 100644 tests/playwright-test/playwright.expect.text.spec.ts delete mode 100644 tests/playwright-test/playwright.expect.true.spec.ts diff --git a/tests/config/utils.ts b/tests/config/utils.ts index 51ae0128e0..05f0666125 100644 --- a/tests/config/utils.ts +++ b/tests/config/utils.ts @@ -132,3 +132,8 @@ export function waitForTestLog(page: Page, prefix: string): Promise { }); }); } + +const ansiRegex = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g'); +export function stripAnsi(str: string): string { + return str.replace(ansiRegex, ''); +} diff --git a/tests/page/expect-boolean.spec.ts b/tests/page/expect-boolean.spec.ts new file mode 100644 index 0000000000..1e3c850723 --- /dev/null +++ b/tests/page/expect-boolean.spec.ts @@ -0,0 +1,365 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './pageTest'; + +test.describe('toBeChecked', () => { + test('default', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeChecked(); + }); + + test('with checked:true', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeChecked({ checked: true }); + }); + + test('with checked:false', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).not.toBeChecked({ checked: false }); + }); + + test('fail', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + const error = await expect(locator).toBeChecked({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`expect.toBeChecked with timeout 1000ms`); + }); + + test('with not', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).not.toBeChecked(); + }); + + test('with not and checked:false', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeChecked({ checked: false }); + }); + + test('fail with not', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + const error = await expect(locator).not.toBeChecked({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`expect.toBeChecked with timeout 1000ms`); + expect(error.message).toContain(`selector resolved to `); + }); + + test('fail with checked:false', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + const error = await expect(locator).toBeChecked({ checked: false, timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`expect.toBeChecked with timeout 1000ms`); + }); + + test('fail missing', async ({ page }) => { + await page.setContent('
no inputs here
'); + const locator2 = page.locator('input2'); + const error = await expect(locator2).not.toBeChecked({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`expect.toBeChecked with timeout 1000ms`); + expect(error.message).toContain('waiting for selector "input2"'); + }); +}); + +test('toBeEditable', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeEditable(); +}); + +test('toBeEnabled', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).toBeEnabled(); +}); + +test('toBeEnabled failed', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + const error = await expect(locator).toBeEnabled({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); +}); + +test('toBeEnabled eventually', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + setTimeout(() => { + locator.evaluate(e => e.removeAttribute('disabled')).catch(() => {}); + }, 500); + await expect(locator).toBeEnabled(); +}); + +test('not.toBeEnabled eventually', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + setTimeout(() => { + locator.evaluate(e => e.setAttribute('disabled', '')).catch(() => {}); + }, 500); + await expect(locator).not.toBeEnabled(); +}); + +test('toBeDisabled', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).toBeDisabled(); +}); + +test('toBeEmpty input', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeEmpty(); +}); + +test('not.toBeEmpty', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).not.toBeEmpty(); +}); + +test('toBeEmpty div', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('div'); + await expect(locator).toBeEmpty(); +}); + +test('toBeDisabled with value', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).toBeDisabled(); +}); + +test('toBeChecked with value', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeChecked(); +}); + +test('toBeHidden with value', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeHidden(); +}); + +test('not.toBeDisabled div', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('div'); + await expect(locator).not.toBeDisabled(); +}); + +test('toBeVisible', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeVisible(); +}); + +test('not.toBeVisible', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).not.toBeVisible(); +}); + +test('toBeHidden', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).toBeHidden(); +}); + +test('toBeHidden when nothing matches', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('button'); + await expect(locator).toBeHidden(); +}); + +test('not.toBeHidden', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).not.toBeHidden(); +}); + +test('toBeVisible eventually', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); + }, 0); + await expect(locator).toBeVisible(); +}); + +test('not.toBeHidden eventually', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); + }, 0); + await expect(locator).not.toBeHidden(); +}); + +test('not.toBeVisible eventually', async ({ page }) => { + await page.setContent('
Hello
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('span', span => span.textContent = '').catch(() => {}); + }, 0); + await expect(locator).not.toBeVisible(); +}); + +test('toBeHidden eventually', async ({ page }) => { + await page.setContent('
Hello
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('span', span => span.textContent = '').catch(() => {}); + }, 0); + await expect(locator).toBeHidden(); +}); + +test('toBeVisible fail', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + const error = await expect(locator).toBeVisible({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); +}); + +test('not.toBeVisible fail', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + const error = await expect(locator).not.toBeVisible({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); +}); + +test('toBeHidden fail', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + const error = await expect(locator).toBeHidden({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); +}); + +test('not.toBeHidden fail', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); +}); + +test('not.toBeHidden fail not matching', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('button'); + const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`expect.toBeHidden with timeout 1000ms`); +}); + +test('toBeFocused', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await locator.focus(); + await expect(locator).toBeFocused(); +}); + +test('toBeFocused with shadow elements', async ({ page }) => { + await page.setContent(` +
+
+ + `); + + await page.locator('input').focus(); + expect(await page.evaluate(() => document.activeElement.shadowRoot.activeElement.id)).toBe('my-input'); + await expect(page.locator('#app')).toBeFocused(); + await expect(page.locator('input')).toBeFocused(); +}); + +test('should print unknown engine error', async ({ page }) => { + const error = await expect(page.locator('row="row"')).toBeVisible().catch(e => e); + expect(error.message).toContain(`Unknown engine "row" while parsing selector row="row"`); +}); + +test('should print selector syntax error', async ({ page }) => { + const error = await expect(page.locator('row]')).toBeVisible().catch(e => e); + expect(error.message).toContain(`Unexpected token "]" while parsing selector "row]"`); +}); + +test('toBeOK', async ({ page, server }) => { + const res = await page.request.get(server.EMPTY_PAGE); + await expect(res).toBeOK(); +}); + +test('not.toBeOK', async ({ page, server }) => { + const res = await page.request.get(`${server.PREFIX}/unknown`); + await expect(res).not.toBeOK(); +}); + +test('toBeOK fail with invalid argument', async ({ page }) => { + const error = await (expect(page) as any).toBeOK().catch(e => e); + expect(error.message).toContain('toBeOK can be only used with APIResponse object'); +}); + +test('toBeOK fail with promise', async ({ page, server }) => { + const res = page.request.get(server.EMPTY_PAGE).catch(e => {}); + const error = await (expect(res) as any).toBeOK().catch(e => e); + expect(error.message).toContain('toBeOK can be only used with APIResponse object'); +}); + +test.describe('toBeOK should print response with text content type when fails', () => { + test.beforeEach(async ({ server }) => { + server.setRoute('/text-content-type', (req, res) => { + res.statusCode = 404; + res.setHeader('Content-type', 'text/plain'); + res.end('Text error'); + }); + server.setRoute('/no-content-type', (req, res) => { + res.statusCode = 404; + res.end('No content type error'); + }); + server.setRoute('/binary-content-type', (req, res) => { + res.statusCode = 404; + res.setHeader('Content-type', 'image/bmp'); + res.end('Image content type error'); + }); + }); + + test('text content type', async ({ page, server }) => { + const res = await page.request.get(`${server.PREFIX}/text-content-type`); + const error = await expect(res).toBeOK().catch(e => e); + expect(error.message).toContain(`→ GET ${server.PREFIX}/text-content-type`); + expect(error.message).toContain(`← 404 Not Found`); + expect(error.message).toContain(`Text error`); + }); + + test('no content type', async ({ page, server }) => { + const res = await page.request.get(`${server.PREFIX}/no-content-type`); + const error = await expect(res).toBeOK().catch(e => e); + expect(error.message).toContain(`→ GET ${server.PREFIX}/no-content-type`); + expect(error.message).toContain(`← 404 Not Found`); + expect(error.message).not.toContain(`No content type error`); + }); + + test('image content type', async ({ page, server }) => { + const res = await page.request.get(`${server.PREFIX}/image-content-type`); + const error = await expect(res).toBeOK().catch(e => e); + expect(error.message).toContain(`→ GET ${server.PREFIX}/image-content-type`); + expect(error.message).toContain(`← 404 Not Found`); + expect(error.message).not.toContain(`Image content type error`); + }); +}); diff --git a/tests/page/expect-misc.spec.ts b/tests/page/expect-misc.spec.ts new file mode 100644 index 0000000000..3b48c0d49e --- /dev/null +++ b/tests/page/expect-misc.spec.ts @@ -0,0 +1,257 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './pageTest'; + +test.describe('toHaveCount', () => { + test('toHaveCount pass', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('option'); + let done = false; + const promise = expect(locator).toHaveCount(2).then(() => { done = true; }); + await page.waitForTimeout(1000); + expect(done).toBe(false); + await page.setContent(''); + await promise; + expect(done).toBe(true); + }); + + test('pass zero', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('span'); + await expect(locator).toHaveCount(0); + await expect(locator).not.toHaveCount(1); + }); + + test('eventually pass zero', async ({ page }) => { + await page.setContent('
hello
'); + const locator = page.locator('span'); + setTimeout(() => page.evaluate(() => document.querySelector('div').textContent = '').catch(() => {}), 200); + await expect(locator).toHaveCount(0); + await expect(locator).not.toHaveCount(1); + }); + + test('eventually pass non-zero', async ({ page }) => { + await page.setContent('
    '); + setTimeout(async () => { + await page.setContent('
    • one
    • two
    '); + }, 500); + const locator = page.locator('li'); + await expect(locator).toHaveCount(2); + }); + + test('eventually pass not non-zero', async ({ page }) => { + await page.setContent('
    • one
    • two
    '); + setTimeout(async () => { + await page.setContent('
      '); + }, 500); + const locator = page.locator('li'); + await expect(locator).not.toHaveCount(2); + }); + + test('fail zero', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('span'); + const error = await expect(locator).toHaveCount(0, { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('expect.toHaveCount with timeout 1000ms'); + }); + + test('fail zero 2', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('span'); + const error = await expect(locator).not.toHaveCount(1, { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('expect.toHaveCount with timeout 1000ms'); + }); +}); + +test.describe('toHaveJSProperty', () => { + test('pass', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = { a: 1, b: 'string', c: new Date(1627503992000) }); + const locator = page.locator('div'); + await expect(locator).toHaveJSProperty('foo', { a: 1, b: 'string', c: new Date(1627503992000) }); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = { a: 1, b: 'string', c: new Date(1627503992000) }); + const locator = page.locator('div'); + const error = await expect(locator).toHaveJSProperty('foo', { a: 1, b: 'string', c: new Date(1627503992001) }, { timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`- "c"`); + }); + + test('pass string', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = 'string'); + const locator = page.locator('div'); + await expect(locator).toHaveJSProperty('foo', 'string'); + }); + + test('fail string', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = 'string'); + const locator = page.locator('div'); + const error = await expect(locator).toHaveJSProperty('foo', 'error', { timeout: 200 }).catch(e => e); + expect(error.message).toContain(`expect.toHaveJSProperty with timeout 200ms`); + }); + + test('pass number', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = 2021); + const locator = page.locator('div'); + await expect(locator).toHaveJSProperty('foo', 2021); + }); + + test('fail number', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = 2021); + const locator = page.locator('div'); + const error = await expect(locator).toHaveJSProperty('foo', 1, { timeout: 200 }).catch(e => e); + expect(error.message).toContain(`expect.toHaveJSProperty with timeout 200ms`); + }); + + test('pass boolean', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = true); + const locator = page.locator('div'); + await expect(locator).toHaveJSProperty('foo', true); + }); + + test('fail boolean', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = false); + const locator = page.locator('div'); + const error = await expect(locator).toHaveJSProperty('foo', true, { timeout: 200 }).catch(e => e); + expect(error.message).toContain(`expect.toHaveJSProperty with timeout 200ms`); + }); + + test('pass boolean 2', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = false); + const locator = page.locator('div'); + await expect(locator).toHaveJSProperty('foo', false); + }); + + test('fail boolean 2', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = false); + const locator = page.locator('div'); + const error = await expect(locator).toHaveJSProperty('foo', true, { timeout: 200 }).catch(e => e); + expect(error.message).toContain(`expect.toHaveJSProperty with timeout 200ms`); + }); + + test('pass undefined', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('div'); + await expect(locator).toHaveJSProperty('foo', undefined); + }); + + test('pass null', async ({ page }) => { + await page.setContent('
      '); + await page.$eval('div', e => (e as any).foo = null); + const locator = page.locator('div'); + await expect(locator).toHaveJSProperty('foo', null); + }); +}); + +test.describe('toHaveClass', () => { + test('pass', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('div'); + await expect(locator).toHaveClass('foo bar baz'); + }); + + test('pass with SVGs', async ({ page }) => { + await page.setContent(``); + await expect(page.locator('svg')).toHaveClass(/c1/); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('div'); + const error = await expect(locator).toHaveClass('foo bar baz', { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('expect.toHaveClass with timeout 1000ms'); + }); + + test('pass with array', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('div'); + await expect(locator).toHaveClass(['foo', 'bar', /[a-z]az/]); + }); + + test('fail with array', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('div'); + const error = await expect(locator).toHaveClass(['foo', 'bar', /[a-z]az/], { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('expect.toHaveClass with timeout 1000ms'); + }); +}); + +test.describe('toHaveTitle', () => { + test('pass', async ({ page }) => { + await page.setContent(' Hello world'); + await expect(page).toHaveTitle('Hello world'); + }); + + test('fail', async ({ page }) => { + await page.setContent('Bye'); + const error = await expect(page).toHaveTitle('Hello', { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('expect.toHaveTitle with timeout 1000ms'); + }); +}); + +test.describe('toHaveURL', () => { + test('pass', async ({ page }) => { + await page.goto('data:text/html,
      A
      '); + await expect(page).toHaveURL('data:text/html,
      A
      '); + }); + + test('fail', async ({ page }) => { + await page.goto('data:text/html,
      B
      '); + const error = await expect(page).toHaveURL('wrong', { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('expect.toHaveURL with timeout 1000ms'); + }); +}); + +test.describe('toHaveAttribute', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toHaveAttribute('id', 'node'); + }); +}); + +test.describe('toHaveCSS', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toHaveCSS('color', 'rgb(255, 0, 0)'); + }); + + test('custom css properties', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toHaveCSS('--custom-color-property', '#FF00FF'); + }); +}); + +test.describe('toHaveId', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toHaveId('node'); + }); +}); diff --git a/tests/page/expect-to-have-text.spec.ts b/tests/page/expect-to-have-text.spec.ts new file mode 100644 index 0000000000..8e25ba91e2 --- /dev/null +++ b/tests/page/expect-to-have-text.spec.ts @@ -0,0 +1,226 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { stripAnsi } from '../config/utils'; +import { test, expect } from './pageTest'; + +test.describe('toHaveText with regex', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toHaveText(/Text/); + + // Should not normalize whitespace. + await expect(locator).toHaveText(/Text content/); + // Should respect ignoreCase. + await expect(locator).toHaveText(/text content/, { ignoreCase: true }); + // Should override regex flag with ignoreCase. + await expect(locator).not.toHaveText(/text content/i, { ignoreCase: false }); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + const error = await expect(locator).toHaveText(/Text 2/, { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('Expected pattern: /Text 2/'); + expect(stripAnsi(error.message)).toContain('Received string: "Text content"'); + }); +}); + +test.describe('toContainText with regex', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toContainText(/ex/); + + // Should not normalize whitespace. + await expect(locator).toContainText(/ext cont/); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + const error = await expect(locator).toContainText(/ex2/, { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('Expected pattern: /ex2/'); + expect(stripAnsi(error.message)).toContain('Received string: "Text content"'); + }); +}); + +test.describe('toHaveText with text', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text \ncontent 
      '); + const locator = page.locator('#node'); + // Should normalize whitespace. + await expect(locator).toHaveText('Text content'); + // Should normalize zero width whitespace. + await expect(locator).toHaveText('T\u200be\u200bx\u200bt content'); + // Should support ignoreCase. + await expect(locator).toHaveText('text CONTENT', { ignoreCase: true }); + // Should support falsy ignoreCase. + await expect(locator).not.toHaveText('TEXT', { ignoreCase: false }); + }); + + test('pass contain', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toContainText('Text'); + // Should normalize whitespace. + await expect(locator).toContainText(' ext cont\n '); + // Should support ignoreCase. + await expect(locator).toContainText('EXT', { ignoreCase: true }); + // Should support falsy ignoreCase. + await expect(locator).not.toContainText('TEXT', { ignoreCase: false }); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + const error = await expect(locator).toHaveText('Text', { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('Expected string: "Text"'); + expect(stripAnsi(error.message)).toContain('Received string: "Text content"'); + }); + + test('pass eventually', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await Promise.all([ + expect(locator).toHaveText(/Text 2/), + page.waitForTimeout(1000).then(() => locator.evaluate(element => element.textContent = 'Text 2 content')), + ]); + }); + + test('with userInnerText', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).toHaveText('Text content', { useInnerText: true }); + }); +}); + +test.describe('not.toHaveText', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + await expect(locator).not.toHaveText('Text2'); + // Should be case-sensitive by default. + await expect(locator).not.toHaveText('TEXT'); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      Text content
      '); + const locator = page.locator('#node'); + const error = await expect(locator).not.toHaveText('Text content', { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('Expected string: not "Text content"'); + expect(stripAnsi(error.message)).toContain('Received string: "Text content'); + }); + + test('should work when selector does not match', async ({ page }) => { + await page.setContent('
      hello
      '); + const error = await expect(page.locator('span')).not.toHaveText('hello', { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('Expected string: not "hello"'); + expect(stripAnsi(error.message)).toContain('Received string: ""'); + expect(stripAnsi(error.message)).toContain('waiting for selector "span"'); + }); +}); + +test.describe('toHaveText with array', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text \n1
      Text 2a
      '); + const locator = page.locator('div'); + // Should only normalize whitespace in the first item. + await expect(locator).toHaveText(['Text 1', /Text \d+a/]); + // Should support ignoreCase. + await expect(locator).toHaveText(['tEXT 1', 'TExt 2A'], { ignoreCase: true }); + }); + + test('pass lazy', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('p'); + setTimeout(() => { + page.evaluate(() => { + document.querySelector('div').innerHTML = '

      Text 1

      Text 2

      '; + }).catch(() => {}); + }, 500); + await expect(locator).toHaveText(['Text 1', 'Text 2']); + }); + + test('pass empty', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('p'); + await expect(locator).toHaveText([]); + }); + + test('pass not empty', async ({ page }) => { + await page.setContent('

      Test

      '); + const locator = page.locator('p'); + await expect(locator).not.toHaveText([]); + }); + + test('pass on empty', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('p'); + await expect(locator).not.toHaveText(['Test']); + }); + + test('fail on not+empty', async ({ page }) => { + await page.setContent('
      '); + const locator = page.locator('p'); + const error = await expect(locator).not.toHaveText([], { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('expect.toHaveText with timeout 1000ms'); + }); + + test('pass eventually empty', async ({ page }) => { + await page.setContent('

      Text

      '); + const locator = page.locator('p'); + setTimeout(() => { + page.evaluate(() => document.querySelector('div').innerHTML = '').catch(() => {}); + }, 500); + await expect(locator).not.toHaveText([]); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      Text 1
      Text 3
      '); + const locator = page.locator('div'); + const error = await expect(locator).toHaveText(['Text 1', /Text \d/, 'Extra'], { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('- "Extra"'); + expect(error.message).toContain('expect.toHaveText with timeout 1000ms'); + expect(error.message).toContain('waiting for selector "div"'); + expect(error.message).toContain('selector resolved to 2 elements'); + }); + + test('fail on repeating array matchers', async ({ page }) => { + await page.setContent('
      KekFoo
      '); + const locator = page.locator('div'); + const error = await expect(locator).toContainText(['KekFoo', 'KekFoo', 'KekFoo'], { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('selector resolved to 1 element'); + }); +}); + +test.describe('toContainText with array', () => { + test('pass', async ({ page }) => { + await page.setContent('
      Text \n1
      Text2
      Text3
      '); + const locator = page.locator('div'); + await expect(locator).toContainText(['ext 1', /ext3/]); + // Should support ignoreCase. + await expect(locator).toContainText(['EXT 1', 'eXt3'], { ignoreCase: true }); + }); + + test('fail', async ({ page }) => { + await page.setContent('
      Text 1
      Text 3
      '); + const locator = page.locator('div'); + const error = await expect(locator).toContainText(['Text 2'], { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('- "Text 2"'); + }); +}); diff --git a/tests/page/expect-to-have-value.spec.ts b/tests/page/expect-to-have-value.spec.ts new file mode 100644 index 0000000000..0518eac725 --- /dev/null +++ b/tests/page/expect-to-have-value.spec.ts @@ -0,0 +1,139 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { stripAnsi } from '../config/utils'; +import { test, expect } from './pageTest'; + +test('should work', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('#node'); + await locator.fill('Text content'); + await expect(locator).toHaveValue('Text content'); +}); + +test('should work with label', async ({ page }) => { + await page.setContent(''); + await page.locator('label input').fill('Text content'); + await expect(page.locator('label')).toHaveValue('Text content'); +}); + +test('should work with regex', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('#node'); + await locator.fill('Text content'); + await expect(locator).toHaveValue(/Text/); +}); + +test('should support failure', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('#node'); + await locator.fill('Text content'); + const error = await expect(locator).toHaveValue(/Text2/, { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('"Text content"'); +}); + +test.describe('toHaveValues with multi-select', () => { + test('works with text', async ({ page }) => { + await page.setContent(` + + `); + const locator = page.locator('select'); + await locator.selectOption(['R', 'G']); + await expect(locator).toHaveValues(['R', 'G']); + }); + + test('follows labels', async ({ page }) => { + await page.setContent(` + + + `); + const locator = page.locator('text=Pick a Color'); + await locator.selectOption(['R', 'G']); + await expect(locator).toHaveValues(['R', 'G']); + }); + + test('exact match with text failure', async ({ page }) => { + await page.setContent(` + + `); + const locator = page.locator('select'); + await locator.selectOption(['RR', 'GG']); + const error = await expect(locator).toHaveValues(['R', 'G'], { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('- "R"'); + expect(stripAnsi(error.message)).toContain('+ "RR"'); + }); + + test('works with regex', async ({ page }) => { + await page.setContent(` + + `); + const locator = page.locator('select'); + await locator.selectOption(['R', 'G']); + await expect(locator).toHaveValues([/R/, /G/]); + }); + + test('fails when items not selected', async ({ page }) => { + await page.setContent(` + + `); + const locator = page.locator('select'); + await locator.selectOption(['B']); + const error = await expect(locator).toHaveValues([/R/, /G/], { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('+ "B"'); + }); + + test('fails when multiple not specified', async ({ page }) => { + await page.setContent(` + + `); + const locator = page.locator('select'); + await locator.selectOption(['B']); + const error = await expect(locator).toHaveValues([/R/, /G/], { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('Not a select element with a multiple attribute'); + }); + + test('fails when not a select element', async ({ page }) => { + await page.setContent(` + + `); + const locator = page.locator('input'); + const error = await expect(locator).toHaveValues([/R/, /G/], { timeout: 1000 }).catch(e => e); + expect(error.message).toContain('Not a select element with a multiple attribute'); + }); +}); diff --git a/tests/playwright-test/expect.spec.ts b/tests/playwright-test/expect.spec.ts index dd21af5a49..bc5c3c6a11 100644 --- a/tests/playwright-test/expect.spec.ts +++ b/tests/playwright-test/expect.spec.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import path from 'path'; import { test, expect, stripAnsi } from './playwright-test-fixtures'; test('should be able to call expect.extend in config', async ({ runInlineTest }) => { @@ -375,3 +376,143 @@ test('should reasonably work in global setup', async ({ runInlineTest }) => { expect(result.exitCode).toBe(1); expect(stripAnsi(result.output)).toContain('> 11 | expect(1).toBe(2);'); }); + +test('should support toHaveURL with baseURL from webServer', async ({ runInlineTest }, testInfo) => { + const port = testInfo.workerIndex + 10500; + const result = await runInlineTest({ + 'a.test.ts': ` + const { test } = pwt; + + test('pass', async ({ page }) => { + await page.goto('/foobar'); + await expect(page).toHaveURL('/foobar'); + await expect(page).toHaveURL('http://localhost:${port}/foobar'); + }); + + test('fail', async ({ page }) => { + await page.goto('/foobar'); + await expect(page).toHaveURL('/kek', { timeout: 1000 }); + }); + `, + 'playwright.config.ts': ` + module.exports = { + webServer: { + command: 'node ${JSON.stringify(path.join(__dirname, 'assets', 'simple-server.js'))} ${port}', + port: ${port}, + }, + }; + `, + }, { workers: 1 }); + const output = stripAnsi(result.output); + expect(output).toContain('expect(page).toHaveURL'); + expect(output).toContain(`Expected string: \"http://localhost:${port}/kek\"`); + expect(result.passed).toBe(1); + expect(result.failed).toBe(1); + expect(result.exitCode).toBe(1); +}); + +test('should respect expect.timeout', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.js': `module.exports = { expect: { timeout: 1000 } }`, + 'a.test.ts': ` + const { test } = pwt; + + test('timeout', async ({ page }) => { + await page.goto('data:text/html,
      A
      '); + await Promise.all([ + expect(page).toHaveURL('data:text/html,
      B
      '), + new Promise(f => setTimeout(f, 2000)).then(() => expect(true).toBe(false)) + ]); + }); + `, + }, { workers: 1 }); + const output = stripAnsi(result.output); + expect(output).toContain('expect(received).toHaveURL(expected)'); + expect(output).toContain('expect.toHaveURL with timeout 1000ms'); + expect(result.failed).toBe(1); + expect(result.exitCode).toBe(1); +}); + +test('should log scale the time', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + const { test } = pwt; + + test('pass', async ({ page }) => { + await page.setContent('
      Wrong
      '); + await expect(page.locator('div')).toHaveText('Text', { timeout: 2000 }); + }); + `, + }, { workers: 1 }); + const output = stripAnsi(result.output); + const tokens = output.split('unexpected value'); + // Log scale: 0, 100, 250, 500, 1000, 1000, should be less than 8. + expect(tokens.length).toBeGreaterThan(1); + expect(tokens.length).toBeLessThan(8); + expect(result.passed).toBe(0); + expect(result.exitCode).toBe(1); +}); + + +test('should print expected/received before timeout', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + const { test } = pwt; + + test('times out waiting for text', async ({ page }) => { + await page.setContent('
      Text content
      '); + await expect(page.locator('#node')).toHaveText('Text 2'); + }); + `, + }, { workers: 1, timeout: 2000 }); + expect(result.exitCode).toBe(1); + expect(result.passed).toBe(0); + expect(result.failed).toBe(1); + expect(result.output).toContain('Test timeout of 2000ms exceeded.'); + expect(stripAnsi(result.output)).toContain('Expected string: "Text 2"'); + expect(stripAnsi(result.output)).toContain('Received string: "Text content"'); +}); + +test('should print pending operations for toHaveText', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + const { test } = pwt; + + test('fail', async ({ page }) => { + await page.setContent('
      Text content
      '); + await expect(page.locator('no-such-thing')).toHaveText('Text'); + }); + `, + }, { workers: 1, timeout: 2000 }); + expect(result.failed).toBe(1); + expect(result.exitCode).toBe(1); + const output = stripAnsi(result.output); + expect(output).toContain('Pending operations:'); + expect(output).toContain('Error: expect(received).toHaveText(expected)'); + expect(output).toContain('Expected string: "Text"'); + expect(output).toContain('Received string: ""'); + expect(output).toContain('waiting for selector "no-such-thing"'); +}); + +test('should print expected/received on Ctrl+C', async ({ runInlineTest }) => { + test.skip(process.platform === 'win32', 'No sending SIGINT on Windows'); + + const result = await runInlineTest({ + 'a.test.ts': ` + const { test } = pwt; + + test('times out waiting for text', async ({ page }) => { + await page.setContent('
      Text content
      '); + const promise = expect(page.locator('#node')).toHaveText('Text 2'); + await new Promise(f => setTimeout(f, 500)); + console.log('\\n%%SEND-SIGINT%%'); + await promise; + }); + `, + }, { workers: 1 }, {}, { sendSIGINTAfter: 1 }); + expect(result.exitCode).toBe(130); + expect(result.passed).toBe(0); + expect(result.interrupted).toBe(1); + expect(stripAnsi(result.output)).toContain('Expected string: "Text 2"'); + expect(stripAnsi(result.output)).toContain('Received string: "Text content"'); +}); diff --git a/tests/playwright-test/playwright.expect.misc.spec.ts b/tests/playwright-test/playwright.expect.misc.spec.ts deleted file mode 100644 index b19ecda9c5..0000000000 --- a/tests/playwright-test/playwright.expect.misc.spec.ts +++ /dev/null @@ -1,387 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import path from 'path'; -import { test, expect, stripAnsi } from './playwright-test-fixtures'; - -test('should support toHaveCount', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('option'); - let done = false; - const promise = expect(locator).toHaveCount(2).then(() => { done = true; }); - await page.waitForTimeout(1000); - expect(done).toBe(false); - await page.setContent(''); - await promise; - expect(done).toBe(true); - }); - - test('pass zero', async ({ page }) => { - await page.setContent('
      '); - const locator = page.locator('span'); - await expect(locator).toHaveCount(0); - await expect(locator).not.toHaveCount(1); - }); - - test('eventually pass zero', async ({ page }) => { - await page.setContent('
      '); - const locator = page.locator('span'); - setTimeout(() => page.evaluate(() => div.textContent = '').catch(() => {}), 200); - await expect(locator).toHaveCount(0); - await expect(locator).not.toHaveCount(1); - }); - - test('eventually pass non-zero', async ({ page }) => { - await page.setContent('
        '); - setTimeout(async () => { - await page.setContent("
        • one
        • two
        "); - }, 500); - const locator = page.locator('li'); - await expect(locator).toHaveCount(2); - }); - - test('eventually pass not non-zero', async ({ page }) => { - await page.setContent('
        • one
        • two
        '); - setTimeout(async () => { - await page.setContent("
          "); - }, 500); - const locator = page.locator('li'); - await expect(locator).not.toHaveCount(2); - }); - - test('fail zero', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('span'); - await expect(locator).toHaveCount(0, { timeout: 1000 }); - }); - - test('fail zero 2', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('span'); - await expect(locator).not.toHaveCount(1, { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(result.passed).toBe(5); - expect(result.failed).toBe(2); - expect(result.exitCode).toBe(1); - expect(output).toContain('Expected: 0'); - expect(output).toContain('Received: 1'); - expect(output).toContain('expect.toHaveCount with timeout 1000ms'); -}); - -test('should support toHaveJSProperty', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) }); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', { a: 1, b: 'string', c: new Date(1627503992000) }); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) }); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', { a: 1, b: 'string', c: new Date(1627503992001) }, { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('- "c"'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - - -test('should support toHaveJSProperty with builtin types', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass string', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = 'string'); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', 'string'); - }); - - test('fail string', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = 'string'); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', 'error', {timeout: 200}); - }); - - test('pass number', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = 2021); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', 2021); - }); - - test('fail number', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = 2021); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', 1, {timeout: 200}); - }); - - test('pass boolean', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = true); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', true); - }); - - test('fail boolean', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = false); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', true, {timeout: 200}); - }); - - test('pass boolean 2', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = false); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', false); - }); - - test('fail boolean 2', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = false); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', true, {timeout: 200}); - }); - - test('pass undefined', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', undefined); - }); - - test('pass null', async ({ page }) => { - await page.setContent('
          '); - await page.$eval('div', e => e.foo = null); - const locator = page.locator('div'); - await expect(locator).toHaveJSProperty('foo', null); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(result.passed).toBe(6); - expect(result.failed).toBe(4); - expect(result.exitCode).toBe(1); - expect(output).toContain('Expected: "error"'); - expect(output).toContain('Received: "string"'); - expect(output).toContain('Expected: 1'); - expect(output).toContain('Received: 2021'); - expect(output).toContain('Expected: true'); - expect(output).toContain('Received: false'); -}); - - -test('should support toHaveClass', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('div'); - await expect(locator).toHaveClass('foo bar baz'); - }); - - test('pass with SVGs', async ({ page }) => { - await page.setContent(\`\`); - await expect(page.locator('svg')).toHaveClass(/c1/); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('div'); - await expect(locator).toHaveClass('foo bar baz', { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('expect(locator).toHaveClass'); - expect(output).toContain('Expected string: \"foo bar baz\"'); - expect(result.passed).toBe(2); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveClass w/ array', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('div'); - 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', /[a-z]az/], { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('expect(received).toHaveClass(expected)'); - expect(output).toContain('- /[a-z]az/,'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveTitle', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(' Hello world'); - await expect(page).toHaveTitle('Hello world'); - }); - - test('fail', async ({ page }) => { - await page.setContent('Bye'); - await expect(page).toHaveTitle('Hello', { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('expect(page).toHaveTitle'); - expect(output).toContain('Expected string: \"Hello\"'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveURL', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.goto('data:text/html,
          A
          '); - await expect(page).toHaveURL('data:text/html,
          A
          '); - }); - - test('fail', async ({ page }) => { - await page.goto('data:text/html,
          B
          '); - await expect(page).toHaveURL('wrong', { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('expect(page).toHaveURL'); - expect(output).toContain('Expected string: \"wrong\"'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveURL with baseURL from webServer', async ({ runInlineTest }, testInfo) => { - const port = testInfo.workerIndex + 10500; - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.goto('/foobar'); - await expect(page).toHaveURL('/foobar'); - await expect(page).toHaveURL('http://localhost:${port}/foobar'); - }); - - test('fail', async ({ page }) => { - await page.goto('/foobar'); - await expect(page).toHaveURL('/kek', { timeout: 1000 }); - }); - `, - 'playwright.config.ts': ` - module.exports = { - webServer: { - command: 'node ${JSON.stringify(path.join(__dirname, 'assets', 'simple-server.js'))} ${port}', - port: ${port}, - }, - }; - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('expect(page).toHaveURL'); - expect(output).toContain(`Expected string: \"http://localhost:${port}/kek\"`); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should respect expect.timeout', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'playwright.config.js': `module.exports = { expect: { timeout: 1000 } }`, - 'a.test.ts': ` - const { test } = pwt; - - test('timeout', async ({ page }) => { - await page.goto('data:text/html,
          A
          '); - await Promise.all([ - expect(page).toHaveURL('data:text/html,
          B
          '), - new Promise(f => setTimeout(f, 2000)).then(() => expect(true).toBe(false)) - ]); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('expect(received).toHaveURL(expected)'); - expect(output).toContain('expect.toHaveURL with timeout 1000ms'); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should log scale the time', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Wrong
          '); - await expect(page.locator('div')).toHaveText('Text', { timeout: 2000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - const tokens = output.split('unexpected value'); - // Log scale: 0, 100, 250, 500, 1000, 1000, should be less than 8. - expect(tokens.length).toBeGreaterThan(1); - expect(tokens.length).toBeLessThan(8); - expect(result.passed).toBe(0); - 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 deleted file mode 100644 index fa0a9c2da7..0000000000 --- a/tests/playwright-test/playwright.expect.text.spec.ts +++ /dev/null @@ -1,683 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { test, expect, stripAnsi } from './playwright-test-fixtures'; - -test('should support toHaveText w/ regex', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveText(/Text/); - - // Should not normalize whitespace. - await expect(locator).toHaveText(/Text content/); - // Should respect ignoreCase. - await expect(locator).toHaveText(/text content/, { ignoreCase: true }); - // Should override regex flag with ignoreCase. - await expect(locator).not.toHaveText(/text content/i, { ignoreCase: false }); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveText(/Text 2/, { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('Error: expect(received).toHaveText(expected)'); - expect(output).toContain('Expected pattern: /Text 2/'); - expect(output).toContain('Received string: "Text content"'); - expect(output).toContain('expect(locator).toHaveText'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toContainText w/ regex', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toContainText(/ex/); - - // Should not normalize whitespace. - await expect(locator).toContainText(/ext cont/); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toContainText(/ex2/, { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('Error: expect(received).toContainText(expected)'); - expect(output).toContain('Expected pattern: /ex2/'); - expect(output).toContain('Received string: "Text content"'); - expect(output).toContain('expect(locator).toContainText'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveText w/ text', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text \\ncontent 
          '); - const locator = page.locator('#node'); - // Should normalize whitespace. - await expect(locator).toHaveText('Text content'); - // Should normalize zero width whitespace. - await expect(locator).toHaveText('T\u200be\u200bx\u200bt content'); - // Should support ignoreCase. - await expect(locator).toHaveText('text CONTENT', { ignoreCase: true }); - // Should support falsy ignoreCase. - await expect(locator).not.toHaveText('TEXT', { ignoreCase: false }); - }); - - test('pass contain', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toContainText('Text'); - // Should normalize whitespace. - await expect(locator).toContainText(' ext cont\\n '); - // Should support ignoreCase. - await expect(locator).toContainText('EXT', { ignoreCase: true }); - // Should support falsy ignoreCase. - await expect(locator).not.toContainText('TEXT', { ignoreCase: false }); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveText('Text', { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('Error: expect(received).toHaveText(expected)'); - expect(output).toContain('Expected string: "Text"'); - expect(output).toContain('Received string: "Text content"'); - expect(output).toContain('expect(locator).toHaveText'); - expect(result.passed).toBe(2); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveText w/ not', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).not.toHaveText('Text2'); - // Should be case-sensitive by default. - await expect(locator).not.toHaveText('TEXT'); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).not.toHaveText('Text content', { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('Error: expect(received).not.toHaveText(expected)'); - expect(output).toContain('Expected string: not "Text content"'); - expect(output).toContain('Received string: "Text content'); - expect(output).toContain('expect(locator).not.toHaveText'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveText w/ array', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text \\n1
          Text 2a
          '); - const locator = page.locator('div'); - // Should only normalize whitespace in the first item. - await expect(locator).toHaveText(['Text 1', /Text \\d+a/]); - // Should support ignoreCase. - await expect(locator).toHaveText(['tEXT 1', 'TExt 2A'], { ignoreCase: true }); - }); - - test('pass lazy', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('p'); - setTimeout(() => { - page.evaluate(() => { - div.innerHTML = "

          Text 1

          Text 2

          "; - }).catch(() => {}); - }, 500); - await expect(locator).toHaveText(['Text 1', 'Text 2']); - }); - - test('pass empty', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('p'); - await expect(locator).toHaveText([]); - }); - - test('pass not empty', async ({ page }) => { - await page.setContent('

          Test

          '); - const locator = page.locator('p'); - await expect(locator).not.toHaveText([]); - }); - - test('pass on empty', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('p'); - await expect(locator).not.toHaveText(['Test']); - }); - - test('fail on not+empty', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('p'); - await expect(locator).not.toHaveText([], { timeout: 1000 }); - }); - - test('pass eventually empty', async ({ page }) => { - await page.setContent('

          Text

          '); - const locator = page.locator('p'); - setTimeout(() => { - page.evaluate(() => div.innerHTML = "").catch(() => {}); - }, 500); - await expect(locator).not.toHaveText([]); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          Text 1
          Text 3
          '); - const locator = page.locator('div'); - await expect(locator).toHaveText(['Text 1', /Text \\d/, 'Extra'], { timeout: 1000 }); - }); - - test('fail on repeating array matchers', async ({ page }) => { - await page.setContent('
          KekFoo
          '); - const locator = page.locator('div'); - await expect(locator).toContainText(['KekFoo', 'KekFoo', 'KekFoo'], { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('Error: expect(received).toHaveText(expected) // deep equality'); - expect(output).toContain('await expect(locator).toHaveText'); - expect(output).toContain('- "Extra"'); - expect(output).toContain('waiting for selector "div"'); - expect(output).toContain('selector resolved to 2 elements'); - expect(result.passed).toBe(6); - expect(result.failed).toBe(3); - expect(result.exitCode).toBe(1); -}); - -test('should support toContainText w/ array', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text \\n1
          Text2
          Text3
          '); - const locator = page.locator('div'); - await expect(locator).toContainText(['ext 1', /ext3/]); - // Should support ignoreCase. - await expect(locator).toContainText(['EXT 1', 'eXt3'], { ignoreCase: true }); - }); - - test('fail', async ({ page }) => { - await page.setContent('
          Text 1
          Text 3
          '); - const locator = page.locator('div'); - await expect(locator).toContainText(['Text 2'], { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('Error: expect(received).toContainText(expected)'); - expect(output).toContain('await expect(locator).toContainText'); - expect(output).toContain('- "Text 2"'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toHaveText eventually', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass eventually', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await Promise.all([ - expect(locator).toHaveText(/Text 2/), - page.waitForTimeout(1000).then(() => locator.evaluate(element => element.textContent = 'Text 2 content')), - ]); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.failed).toBe(0); - expect(result.exitCode).toBe(0); -}); - -test('should support toHaveText with innerText', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveText('Text content', { useInnerText: true }); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); -}); - -test('should support toHaveAttribute', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveAttribute('id', 'node'); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); -}); - -test('should support toHaveCSS', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveCSS('color', 'rgb(255, 0, 0)'); - }); - - test('pass with custom css properties', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveCSS('--custom-color-property', '#FF00FF'); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(2); - expect(result.exitCode).toBe(0); -}); - -test('should support toHaveId', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent('
          Text content
          '); - const locator = page.locator('#node'); - await expect(locator).toHaveId('node'); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); -}); - -test('should support toHaveValue', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('#node'); - await locator.fill('Text content'); - await expect(locator).toHaveValue('Text content'); - }); - - test('pass on label', async ({ page }) => { - await page.setContent(''); - await page.locator('label input').fill('Text content'); - await expect(page.locator('label')).toHaveValue('Text content'); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(2); - expect(result.exitCode).toBe(0); -}); - -test('should support toHaveValue regex', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('#node'); - await locator.fill('Text content'); - await expect(locator).toHaveValue(/Text/); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); -}); - -test('should support toHaveValue failing', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('#node'); - await locator.fill('Text content'); - await expect(locator).toHaveValue(/Text2/, { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(0); - expect(result.exitCode).toBe(1); - expect(result.output).toContain('"Text content"'); -}); - -test.describe('should support toHaveValues with multi-select', () => { - test('works with text', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` - - \`); - const locator = page.locator('select'); - await locator.selectOption(['R', 'G']); - await expect(locator).toHaveValues(['R', 'G']); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); - }); - - test('follows labels', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` - - - \`); - const locator = page.locator('text=Pick a Color'); - await locator.selectOption(['R', 'G']); - await expect(locator).toHaveValues(['R', 'G']); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); - }); - - test('exact match with text', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` - - \`); - const locator = page.locator('select'); - await locator.selectOption(['RR', 'GG']); - await expect(locator).toHaveValues(['R', 'G']); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(0); - expect(result.exitCode).toBe(1); - expect(stripAnsi(result.output)).toContain(` - - Expected - 2 - + Received + 2 - - Array [ - - "R", - - "G", - + "RR", - + "GG", - ] -`); - }); - - test('works with regex', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` - - \`); - const locator = page.locator('select'); - await locator.selectOption(['R', 'G']); - await expect(locator).toHaveValues([/R/, /G/]); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); - }); - - test('fails when items not selected', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` - - \`); - const locator = page.locator('select'); - await locator.selectOption(['B']); - await expect(locator).toHaveValues([/R/, /G/]); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(0); - expect(result.exitCode).toBe(1); - expect(stripAnsi(result.output)).toContain(` - - Expected - 2 - + Received + 1 - - Array [ - - /R/, - - /G/, - + "B", - ] -`); - }); - - test('fails when multiple not specified', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` - - \`); - const locator = page.locator('select'); - await locator.selectOption(['B']); - await expect(locator).toHaveValues([/R/, /G/]); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(0); - expect(result.exitCode).toBe(1); - expect(result.output).toContain('Not a select element with a multiple attribute'); - }); - - test('fails when not a select element', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` - - \`); - const locator = page.locator('input'); - await expect(locator).toHaveValues([/R/, /G/]); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(0); - expect(result.exitCode).toBe(1); - expect(result.output).toContain('Not a select element with a multiple attribute'); - }); -}); - -test('should print expected/received before timeout', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('times out waiting for text', async ({ page }) => { - await page.setContent('
          Text content
          '); - await expect(page.locator('#node')).toHaveText('Text 2'); - }); - `, - }, { workers: 1, timeout: 2000 }); - expect(result.exitCode).toBe(1); - expect(result.passed).toBe(0); - expect(result.failed).toBe(1); - expect(result.output).toContain('Test timeout of 2000ms exceeded.'); - expect(stripAnsi(result.output)).toContain('Expected string: "Text 2"'); - expect(stripAnsi(result.output)).toContain('Received string: "Text content"'); -}); - -test('should print nice error for toHaveText', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('fail', async ({ page }) => { - await page.setContent('
          Text content
          '); - await expect(page.locator('no-such-thing')).toHaveText('Text'); - }); - `, - }, { workers: 1, timeout: 2000 }); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); - const output = stripAnsi(result.output); - expect(output).toContain('Pending operations:'); - expect(output).toContain('Error: expect(received).toHaveText(expected)'); - expect(output).toContain('Expected string: "Text"'); - expect(output).toContain('Received string: ""'); - expect(output).toContain('waiting for selector "no-such-thing"'); -}); - -test('should print expected/received on Ctrl+C', async ({ runInlineTest }) => { - test.skip(process.platform === 'win32', 'No sending SIGINT on Windows'); - - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('times out waiting for text', async ({ page }) => { - await page.setContent('
          Text content
          '); - const promise = expect(page.locator('#node')).toHaveText('Text 2'); - await new Promise(f => setTimeout(f, 500)); - console.log('\\n%%SEND-SIGINT%%'); - await promise; - }); - `, - }, { workers: 1 }, {}, { sendSIGINTAfter: 1 }); - expect(result.exitCode).toBe(130); - expect(result.passed).toBe(0); - expect(result.interrupted).toBe(1); - expect(stripAnsi(result.output)).toContain('Expected string: "Text 2"'); - expect(stripAnsi(result.output)).toContain('Received string: "Text content"'); -}); - -test('should support not.toHaveText when selector does not match', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('fails', async ({ page }) => { - await page.setContent('
          hello
          '); - await expect(page.locator('span')).not.toHaveText('hello', { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(1); - expect(result.passed).toBe(0); - expect(result.failed).toBe(1); - const output = stripAnsi(result.output); - expect(output).toContain('Expected string: not "hello"'); - expect(output).toContain('Received string: ""'); - expect(output).toContain('waiting for selector "span"'); -}); diff --git a/tests/playwright-test/playwright.expect.true.spec.ts b/tests/playwright-test/playwright.expect.true.spec.ts deleted file mode 100644 index a4ea3fbcc7..0000000000 --- a/tests/playwright-test/playwright.expect.true.spec.ts +++ /dev/null @@ -1,510 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { test, expect, stripAnsi } from './playwright-test-fixtures'; - -test('should support toBeChecked', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeChecked(); - }); - - test('pass 2', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeChecked({ checked: true }); - }); - - test('pass 3', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).not.toBeChecked({ checked: false }); - }); - - test('fail', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeChecked({ timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('Error: expect(received).toBeChecked()'); - expect(output).toContain('expect(locator).toBeChecked'); - expect(result.passed).toBe(3); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); -}); - -test('should support toBeChecked w/ not', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass not', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).not.toBeChecked(); - }); - - test('pass 2', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeChecked({ checked: false }); - }); - - test('fail not', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).not.toBeChecked({ timeout: 1000 }); - }); - - test('fail 2', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeChecked({ checked: false, timeout: 1000 }); - }); - - test('fail missing', async ({ page }) => { - await page.setContent('
          no inputs here
          '); - const locator2 = page.locator('input2'); - await expect(locator2).not.toBeChecked({ timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(result.passed).toBe(2); - expect(result.failed).toBe(3); - expect(result.exitCode).toBe(1); - // fail not - expect(output).toContain('Error: expect(received).not.toBeChecked()'); - expect(output).toContain('expect(locator).not.toBeChecked'); - expect(output).toContain('selector resolved to '); - // fail missing - expect(output).toContain('expect(locator2).not.toBeChecked'); - expect(output).toContain('waiting for selector "input2"'); -}); - -test('should support toBeEditable, toBeEnabled, toBeDisabled, toBeEmpty', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('editable', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeEditable(); - }); - - test('enabled', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeEnabled(); - }); - - test('failed', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeEnabled({ timeout: 1000 }); - }); - - test('eventually enabled', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - setTimeout(() => { - locator.evaluate(e => e.removeAttribute('disabled')).catch(() => {}); - }, 500); - await expect(locator).toBeEnabled(); - }); - - test('eventually disabled', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - setTimeout(() => { - locator.evaluate(e => e.setAttribute('disabled', '')).catch(() => {}); - }, 500); - await expect(locator).not.toBeEnabled(); - }); - - test('disabled', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeDisabled(); - }); - - test('empty input', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeEmpty(); - }); - - test('non-empty input', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).not.toBeEmpty(); - }); - - test('empty DOM', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('div'); - await expect(locator).toBeEmpty(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(8); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); - const output = stripAnsi(result.output); - expect(output).toContain('expect(locator).toBeEnabled({ timeout: 1000 }'); -}); - -test('should support toBeDisabled,toBeChecked,toBeHidden w/ value', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('disabled', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeDisabled(); - }); - test('checked', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeChecked(); - }); - test('hidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeHidden(); - }); - test('div disabled', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('div'); - await expect(locator).not.toBeDisabled(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(4); - expect(result.failed).toBe(0); - expect(result.exitCode).toBe(0); -}); - - -test('should support toBeVisible, toBeHidden', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('visible', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeVisible(); - }); - - test('not visible', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).not.toBeVisible(); - }); - - test('hidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeHidden(); - }); - - test('was hidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeHidden(); - }); - - test('not hidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).not.toBeHidden(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(5); - expect(result.exitCode).toBe(0); -}); - -test('should support toBeVisible, toBeHidden wait', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('visible', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); - }, 0); - await expect(locator).toBeVisible(); - }); - - test('not hidden', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); - }, 0); - await expect(locator).not.toBeHidden(); - }); - - test('not visible', async ({ page }) => { - await page.setContent('
          Hello
          '); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('span', span => span.textContent = '').catch(() => {}); - }, 0); - await expect(locator).not.toBeVisible(); - }); - - test('hidden', async ({ page }) => { - await page.setContent('
          Hello
          '); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('span', span => span.textContent = '').catch(() => {}); - }, 0); - await expect(locator).toBeHidden(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(4); - expect(result.exitCode).toBe(0); -}); - -test('should support toBeVisible, toBeHidden fail', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('visible', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeVisible({ timeout: 1000 }); - }); - - test('not visible', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).not.toBeVisible({ timeout: 1000 }); - }); - - test('hidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeHidden({ timeout: 1000 }); - }); - - test('not hidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).not.toBeHidden({ timeout: 1000 }); - }); - - test('not hidden 2', async ({ page }) => { - await page.setContent('
          '); - const locator = page.locator('button'); - await expect(locator).not.toBeHidden({ timeout: 1000 }); - }); - `, - }, { workers: 1 }); - expect(result.failed).toBe(5); - expect(result.exitCode).toBe(1); -}); - -test('should support toBeFocused', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('focused', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await locator.focus(); - await expect(locator).toBeFocused({ timeout: 1000 }); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); -}); - -test('should support toBeFocused with shadow elements', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('focused', async ({ page }) => { - await page.setContent(\` -
          -
          - - \`); - - await page.locator("input").focus(); - expect(await page.evaluate(() => document.activeElement.shadowRoot.activeElement.id)).toBe("my-input"); - await expect(page.locator("#app")).toBeFocused(); - await expect(page.locator("input")).toBeFocused(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(1); - expect(result.exitCode).toBe(0); -}); - -test('should print unknown engine error', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - test('focused', async ({ page }) => { - await expect(page.locator('row="row"]')).toBeVisible(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(0); - expect(result.exitCode).toBe(1); - expect(result.output).toContain(`Unknown engine "row" while parsing selector row="row"]`); -}); - -test('should print syntax error', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - test('focused', async ({ page }) => { - await expect(page.locator('row]')).toBeVisible(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(0); - expect(result.exitCode).toBe(1); - expect(result.output).toContain(`Unexpected token "]" while parsing selector "row]"`); -}); - -test('should support toBeOK', async ({ runInlineTest, server }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass with response', async ({ page }) => { - const res = await page.request.get('${server.EMPTY_PAGE}'); - await expect(res).toBeOK(); - }); - - test('pass with not', async ({ page }) => { - const res = await page.request.get('${server.PREFIX}/unknown'); - await expect(res).not.toBeOK(); - }); - - test('fail with invalid argument', async ({ page }) => { - await expect(page).toBeOK(); - }); - - test('fail with promise', async ({ page }) => { - const res = page.request.get('${server.EMPTY_PAGE}').catch(e => {}); - await expect(res).toBeOK(); - }); - - test('fail', async ({ page }) => { - const res = await page.request.get('${server.PREFIX}/unknown'); - await expect(res).toBeOK(); - }); - `, - }, { workers: 1 }); - expect(result.passed).toBe(2); - expect(result.failed).toBe(3); - expect(result.exitCode).toBe(1); - expect(result.output).toContain(`→ GET ${server.PREFIX}/unknown`); - expect(result.output).toContain(`← 404 Not Found`); - expect(result.output).toContain(`Error: toBeOK can be only used with APIResponse object`); -}); - -test('should print response text if toBeOK fails', async ({ runInlineTest, server }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('fail', async ({ page }) => { - const res = await page.request.get('${server.PREFIX}/unknown'); - await expect(res).toBeOK(); - }); - `, - }, { workers: 1 }); - expect(result.failed).toBe(1); - expect(result.exitCode).toBe(1); - expect(result.output).toContain(`→ GET ${server.PREFIX}/unknown`); - expect(result.output).toContain(`← 404 Not Found`); - expect(result.output).toContain(`Response text:`); - expect(result.output).toContain(`File not found`); -}); - -test('should only print response with text content type if toBeOK fails', async ({ runInlineTest, server }) => { - server.setRoute('/text-content-type', (req, res) => { - res.statusCode = 404; - res.setHeader('Content-type', 'text/plain'); - res.end('Text error'); - }); - server.setRoute('/no-content-type', (req, res) => { - res.statusCode = 404; - res.end('No content type error'); - }); - server.setRoute('/binary-content-type', (req, res) => { - res.statusCode = 404; - res.setHeader('Content-type', 'image/bmp'); - res.end('Image content type error'); - }); - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('text content type', async ({ page }) => { - const res = await page.request.get('${server.PREFIX}/text-content-type'); - await expect(res).toBeOK(); - }); - - test('no content type', async ({ page }) => { - const res = await page.request.get('${server.PREFIX}/no-content-type'); - await expect(res).toBeOK(); - }); - - test('image content type', async ({ page }) => { - const res = await page.request.get('${server.PREFIX}/image-content-type'); - await expect(res).toBeOK(); - }); - `, - }, { workers: 1 }); - expect(result.failed).toBe(3); - expect(result.exitCode).toBe(1); - expect(result.output).toContain(`← 404 Not Found`); - expect(result.output).toContain(`Text error`); - expect(result.output).not.toContain(`No content type error`); - expect(result.output).not.toContain(`Image content type error`); -});