mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-13 17:14:02 +03:00
feat(expect): even more matchers (#7902)
This commit is contained in:
parent
600d82b17c
commit
1807142eb7
@ -28,12 +28,14 @@ import {
|
||||
toContainText,
|
||||
toHaveAttr,
|
||||
toHaveClass,
|
||||
toHaveCount,
|
||||
toHaveCSS,
|
||||
toHaveData,
|
||||
toHaveId,
|
||||
toHaveLength,
|
||||
toHaveProp,
|
||||
toHaveText,
|
||||
toHaveTitle,
|
||||
toHaveURL,
|
||||
toHaveValue
|
||||
} from './matchers/matchers';
|
||||
import { toMatchSnapshot } from './matchers/toMatchSnapshot';
|
||||
@ -54,12 +56,14 @@ expectLibrary.extend({
|
||||
toContainText,
|
||||
toHaveAttr,
|
||||
toHaveClass,
|
||||
toHaveCount,
|
||||
toHaveCSS,
|
||||
toHaveData,
|
||||
toHaveId,
|
||||
toHaveLength,
|
||||
toHaveProp,
|
||||
toHaveText,
|
||||
toHaveTitle,
|
||||
toHaveURL,
|
||||
toHaveValue,
|
||||
toMatchSnapshot,
|
||||
});
|
||||
|
@ -14,49 +14,48 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import matchers from 'expect/build/matchers';
|
||||
import { Locator } from '../../..';
|
||||
import { Locator, Page } from '../../..';
|
||||
import type { Expect } from '../types';
|
||||
import { toBeTruthy } from './toBeTruthy';
|
||||
import { toEqual } from './toEqual';
|
||||
import { toMatchText } from './toMatchText';
|
||||
|
||||
export async function toBeChecked(
|
||||
export function toBeChecked(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeChecked', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', async timeout => {
|
||||
return await locator.isChecked({ timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeDisabled(
|
||||
export function toBeDisabled(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeDisabled', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeDisabled', locator, 'Locator', async timeout => {
|
||||
return await locator.isDisabled({ timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeEditable(
|
||||
export function toBeEditable(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeEditable', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeEditable', locator, 'Locator', async timeout => {
|
||||
return await locator.isEditable({ timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeEmpty(
|
||||
export function toBeEmpty(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeEmpty', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeEmpty', locator, 'Locator', async timeout => {
|
||||
return await locator.evaluate(element => {
|
||||
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA')
|
||||
return !(element as HTMLInputElement).value;
|
||||
@ -65,178 +64,212 @@ export async function toBeEmpty(
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeEnabled(
|
||||
export function toBeEnabled(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeEnabled', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeEnabled', locator, 'Locator', async timeout => {
|
||||
return await locator.isEnabled({ timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeFocused(
|
||||
export function toBeFocused(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeFocused', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeFocused', locator, 'Locator', async timeout => {
|
||||
return await locator.evaluate(element => {
|
||||
return document.activeElement === element;
|
||||
}, { timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeHidden(
|
||||
export function toBeHidden(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeHidden', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeHidden', locator, 'Locator', async timeout => {
|
||||
return await locator.isHidden({ timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeSelected(
|
||||
export function toBeSelected(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeSelected', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeSelected', locator, 'Locator', async timeout => {
|
||||
return await locator.evaluate(element => {
|
||||
return (element as HTMLOptionElement).selected;
|
||||
}, { timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toBeVisible(
|
||||
export function toBeVisible(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeVisible', locator, async timeout => {
|
||||
return toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', async timeout => {
|
||||
return await locator.isVisible({ timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
export async function toContainText(
|
||||
export function toContainText(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: string,
|
||||
options?: { timeout?: number, useInnerText?: boolean },
|
||||
) {
|
||||
return toMatchText.call(this, 'toContainText', locator, async timeout => {
|
||||
return toMatchText.call(this, 'toContainText', locator, 'Locator', async timeout => {
|
||||
if (options?.useInnerText)
|
||||
return await locator.innerText({ timeout });
|
||||
return await locator.textContent() || '';
|
||||
}, expected, { ...options, matchSubstring: true });
|
||||
}
|
||||
|
||||
export async function toHaveAttr(
|
||||
export function toHaveAttr(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
name: string,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveAttr', locator, async timeout => {
|
||||
return toMatchText.call(this, 'toHaveAttr', locator, 'Locator', async timeout => {
|
||||
return await locator.getAttribute(name, { timeout }) || '';
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export async function toHaveClass(
|
||||
export function toHaveClass(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: string,
|
||||
expected: string | RegExp | string[],
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveClass', locator, async timeout => {
|
||||
return await locator.evaluate(element => element.className, { timeout });
|
||||
}, expected, { ...options, matchSubstring: true });
|
||||
if (Array.isArray(expected)) {
|
||||
return toEqual.call(this, 'toHaveClass', locator, 'Locator', async () => {
|
||||
return await locator.evaluateAll(ee => ee.map(e => e.className));
|
||||
}, expected, options);
|
||||
} else {
|
||||
return toMatchText.call(this, 'toHaveClass', locator, 'Locator', async timeout => {
|
||||
return await locator.evaluate(element => element.className, { timeout });
|
||||
}, expected, options);
|
||||
}
|
||||
}
|
||||
|
||||
export async function toHaveCSS(
|
||||
export function toHaveCount(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: number,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toEqual.call(this, 'toHaveCount', locator, 'Locator', async timeout => {
|
||||
return await locator.count();
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export function toHaveCSS(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
name: string,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveCSS', locator, async timeout => {
|
||||
return toMatchText.call(this, 'toHaveCSS', locator, 'Locator', async timeout => {
|
||||
return await locator.evaluate(async (element, name) => {
|
||||
return (window.getComputedStyle(element) as any)[name];
|
||||
}, name, { timeout });
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export async function toHaveData(
|
||||
export function toHaveData(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
name: string,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveData', locator, async timeout => {
|
||||
return toMatchText.call(this, 'toHaveData', locator, 'Locator', async timeout => {
|
||||
return await locator.getAttribute('data-' + name, { timeout }) || '';
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export async function toHaveId(
|
||||
export function toHaveId(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveId', locator, async timeout => {
|
||||
return toMatchText.call(this, 'toHaveId', locator, 'Locator', async timeout => {
|
||||
return await locator.getAttribute('id', { timeout }) || '';
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export async function toHaveLength(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: number,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
if (typeof locator !== 'object' || locator.constructor.name !== 'Locator')
|
||||
return matchers.toHaveLength.call(this, locator, expected);
|
||||
return toEqual.call(this, 'toHaveLength', locator, async timeout => {
|
||||
return await locator.count();
|
||||
}, expected, { expectedType: 'number', ...options });
|
||||
}
|
||||
|
||||
export async function toHaveProp(
|
||||
export function toHaveProp(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
name: string,
|
||||
expected: number,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toEqual.call(this, 'toHaveProp', locator, async timeout => {
|
||||
return toEqual.call(this, 'toHaveProp', locator, 'Locator', async timeout => {
|
||||
return await locator.evaluate((element, name) => (element as any)[name], name, { timeout });
|
||||
}, expected, { expectedType: 'number', ...options });
|
||||
}
|
||||
|
||||
export async function toHaveText(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number, useInnerText?: boolean },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveText', locator, async timeout => {
|
||||
if (options?.useInnerText)
|
||||
return await locator.innerText({ timeout });
|
||||
return await locator.textContent() || '';
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export async function toHaveValue(
|
||||
export function toHaveText(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: string | RegExp | string[],
|
||||
options?: { timeout?: number, useInnerText?: boolean },
|
||||
) {
|
||||
if (Array.isArray(expected)) {
|
||||
return toEqual.call(this, 'toHaveText', locator, 'Locator', async () => {
|
||||
return locator.evaluateAll((ee, useInnerText) => {
|
||||
return ee.map(e => useInnerText ? (e as HTMLElement).innerText : e.textContent || '');
|
||||
}, options?.useInnerText);
|
||||
}, expected, options);
|
||||
} else {
|
||||
return toMatchText.call(this, 'toHaveText', locator, 'Locator', async timeout => {
|
||||
if (options?.useInnerText)
|
||||
return await locator.innerText({ timeout });
|
||||
return await locator.textContent() || '';
|
||||
}, expected, options);
|
||||
}
|
||||
}
|
||||
|
||||
export function toHaveTitle(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
page: Page,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveTitle', page, 'Page', async () => {
|
||||
return await page.title();
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export function toHaveURL(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
page: Page,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveURL', page, 'Page', async () => {
|
||||
return page.url();
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export function toHaveValue(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: Locator,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveValue', locator, async timeout => {
|
||||
return toMatchText.call(this, 'toHaveValue', locator, 'Locator', async timeout => {
|
||||
return await locator.inputValue({ timeout });
|
||||
}, expected, options);
|
||||
}
|
||||
|
@ -18,22 +18,22 @@ import {
|
||||
matcherHint,
|
||||
MatcherHintOptions
|
||||
} from 'jest-matcher-utils';
|
||||
import { Locator } from '../../..';
|
||||
import { currentTestInfo } from '../globals';
|
||||
import type { Expect } from '../types';
|
||||
import { expectLocator, monotonicTime, pollUntilDeadline } from '../util';
|
||||
import { expectType, monotonicTime, pollUntilDeadline } from '../util';
|
||||
|
||||
export async function toBeTruthy<T>(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
matcherName: string,
|
||||
locator: Locator,
|
||||
receiver: any,
|
||||
receiverType: string,
|
||||
query: (timeout: number) => Promise<T>,
|
||||
options: { timeout?: number } = {},
|
||||
) {
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo)
|
||||
throw new Error(`${matcherName} must be called during the test`);
|
||||
expectLocator(locator, matcherName);
|
||||
expectType(receiver, receiverType, matcherName);
|
||||
|
||||
const matcherOptions: MatcherHintOptions = {
|
||||
isNot: this.isNot,
|
||||
|
@ -25,10 +25,9 @@ import {
|
||||
printReceived,
|
||||
stringify
|
||||
} from 'jest-matcher-utils';
|
||||
import { Locator } from '../../..';
|
||||
import { currentTestInfo } from '../globals';
|
||||
import type { Expect } from '../types';
|
||||
import { expectLocator, monotonicTime, pollUntilDeadline } from '../util';
|
||||
import { expectType, monotonicTime, pollUntilDeadline } from '../util';
|
||||
|
||||
// Omit colon and one or more spaces, so can call getLabelPrinter.
|
||||
const EXPECTED_LABEL = 'Expected';
|
||||
@ -40,7 +39,8 @@ const isExpand = (expand?: boolean): boolean => expand !== false;
|
||||
export async function toEqual<T>(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
matcherName: string,
|
||||
locator: Locator,
|
||||
receiver: any,
|
||||
receiverType: string,
|
||||
query: (timeout: number) => Promise<T>,
|
||||
expected: T,
|
||||
options: { timeout?: number } = {},
|
||||
@ -48,7 +48,7 @@ export async function toEqual<T>(
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo)
|
||||
throw new Error(`${matcherName} must be called during the test`);
|
||||
expectLocator(locator, matcherName);
|
||||
expectType(receiver, receiverType, matcherName);
|
||||
|
||||
const matcherOptions: MatcherHintOptions = {
|
||||
comment: 'deep equality',
|
||||
|
@ -28,15 +28,15 @@ import {
|
||||
printReceived,
|
||||
printWithType,
|
||||
} from 'jest-matcher-utils';
|
||||
import { Locator } from '../../..';
|
||||
import { currentTestInfo } from '../globals';
|
||||
import type { Expect } from '../types';
|
||||
import { expectLocator, monotonicTime, pollUntilDeadline } from '../util';
|
||||
import { expectType, monotonicTime, pollUntilDeadline } from '../util';
|
||||
|
||||
export async function toMatchText(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
matcherName: string,
|
||||
locator: Locator,
|
||||
receiver: any,
|
||||
receiverType: string,
|
||||
query: (timeout: number) => Promise<string>,
|
||||
expected: string | RegExp,
|
||||
options: { timeout?: number, matchSubstring?: boolean } = {},
|
||||
@ -44,7 +44,7 @@ export async function toMatchText(
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo)
|
||||
throw new Error(`${matcherName} must be called during the test`);
|
||||
expectLocator(locator, matcherName);
|
||||
expectType(receiver, receiverType, matcherName);
|
||||
|
||||
const matcherOptions: MatcherHintOptions = {
|
||||
isNot: this.isNot,
|
||||
|
@ -187,7 +187,7 @@ export function errorWithLocation(location: Location, message: string) {
|
||||
return new Error(`${formatLocation(location)}: ${message}`);
|
||||
}
|
||||
|
||||
export function expectLocator(receiver: any, matcherName: string) {
|
||||
if (typeof receiver !== 'object' || receiver.constructor.name !== 'Locator')
|
||||
throw new Error(`${matcherName} can be only used with Locator object`);
|
||||
export function expectType(receiver: any, type: string, matcherName: string) {
|
||||
if (typeof receiver !== 'object' || receiver.constructor.name !== type)
|
||||
throw new Error(`${matcherName} can be only used with ${type} object`);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
|
||||
test('should support toHaveLength', async ({ runInlineTest }) => {
|
||||
test('should support toHaveCount', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
@ -24,8 +24,7 @@ test('should support toHaveLength', async ({ runInlineTest }) => {
|
||||
test('pass', async ({ page }) => {
|
||||
await page.setContent('<select><option>One</option><option>Two</option></select>');
|
||||
const locator = page.locator('option');
|
||||
await expect(locator).toHaveLength(2);
|
||||
await expect([1, 2]).toHaveLength(2);
|
||||
await expect(locator).toHaveCount(2);
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
@ -68,19 +67,93 @@ test('should support toHaveClass', async ({ runInlineTest }) => {
|
||||
test('pass', async ({ page }) => {
|
||||
await page.setContent('<div class="foo bar baz"></div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveClass('foo');
|
||||
await expect(locator).toHaveClass('foo bar baz');
|
||||
});
|
||||
|
||||
test('fail', async ({ page }) => {
|
||||
await page.setContent('<div class="bar baz"></div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveClass('foo', { timeout: 1000 });
|
||||
await expect(locator).toHaveClass('foo bar baz', { timeout: 1000 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
expect(output).toContain('expect(locator).toHaveClass');
|
||||
expect(output).toContain('Expected substring: \"foo\"');
|
||||
expect(output).toContain('Expected string: \"foo bar baz\"');
|
||||
expect(result.passed).toBe(1);
|
||||
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('<div class="foo"></div><div class="bar"></div><div class="baz"></div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveClass(['foo', 'bar', 'baz']);
|
||||
});
|
||||
|
||||
test('fail', async ({ page }) => {
|
||||
await page.setContent('<div class="foo"></div><div class="bar"></div><div class="bar"></div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveClass(['foo', 'bar', 'baz'], { timeout: 1000 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
expect(output).toContain('expect(received).toHaveClass(expected)');
|
||||
expect(output).toContain('- \"baz\",');
|
||||
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('<title>Hello</title>');
|
||||
await expect(page).toHaveTitle('Hello');
|
||||
});
|
||||
|
||||
test('fail', async ({ page }) => {
|
||||
await page.setContent('<title>Bye</title>');
|
||||
await expect(page).toHaveTitle('Hello', { timeout: 100 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(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,<div>A</div>');
|
||||
await expect(page).toHaveURL('data:text/html,<div>A</div>');
|
||||
});
|
||||
|
||||
test('fail', async ({ page }) => {
|
||||
await page.goto('data:text/html,<div>B</div>');
|
||||
await expect(page).toHaveURL('wrong', { timeout: 100 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(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);
|
||||
|
@ -78,6 +78,33 @@ test('should support toHaveText w/ text', async ({ runInlineTest }) => {
|
||||
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('<div>Text 1</div><div>Text 2</div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveText(['Text 1', 'Text 2']);
|
||||
});
|
||||
|
||||
test('fail', async ({ page }) => {
|
||||
await page.setContent('<div>Text 1</div><div>Text 3</div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveText(['Text 1', 'Text 2'], { timeout: 1000 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
expect(output).toContain('Error: expect(received).toHaveText(expected) // deep equality');
|
||||
expect(output).toContain('await expect(locator).toHaveText');
|
||||
expect(output).toContain('- \"Text 2\"');
|
||||
expect(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': `
|
||||
|
23
types/testExpect.d.ts
vendored
23
types/testExpect.d.ts
vendored
@ -127,7 +127,12 @@ declare global {
|
||||
/**
|
||||
* Asserts that DOM node has a given CSS class.
|
||||
*/
|
||||
toHaveClass(className: string, options?: { timeout?: number }): Promise<R>;
|
||||
toHaveClass(className: string | RegExp | string[], options?: { timeout?: number }): Promise<R>;
|
||||
|
||||
/**
|
||||
* Asserts number of DOM nodes matching given locator.
|
||||
*/
|
||||
toHaveCount(expected: number, options?: { timeout?: number }): Promise<R>;
|
||||
|
||||
/**
|
||||
* Asserts element's computed CSS property `name` matches expected value.
|
||||
@ -150,11 +155,21 @@ declare global {
|
||||
toHaveProp(name: string, value: any, options?: { timeout?: number }): Promise<R>;
|
||||
|
||||
/**
|
||||
* Asserts element's exact text content.
|
||||
* Asserts element's text content.
|
||||
*/
|
||||
toHaveText(expected: string | RegExp, options?: { timeout?: number, useInnerText?: boolean }): Promise<R>;
|
||||
|
||||
toHaveText(expected: string | RegExp | string[], options?: { timeout?: number, useInnerText?: boolean }): Promise<R>;
|
||||
|
||||
/**
|
||||
* Asserts page's title.
|
||||
*/
|
||||
toHaveTitle(expected: string | RegExp, options?: { timeout?: number }): Promise<R>;
|
||||
|
||||
/**
|
||||
* Asserts page's title.
|
||||
*/
|
||||
toHaveURL(expected: string | RegExp, options?: { timeout?: number }): Promise<R>;
|
||||
|
||||
/**
|
||||
* Asserts input element's value.
|
||||
*/
|
||||
toHaveValue(expected: string | RegExp, options?: { timeout?: number }): Promise<R>;
|
||||
|
Loading…
Reference in New Issue
Block a user