feat: composedExpect (#27432)
Allows to merge multiple expects with custom matchers added by `expect.extend()`.
This commit is contained in:
Родитель
d426f2fd4e
Коммит
daba77644c
|
@ -766,5 +766,6 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFix
|
||||||
|
|
||||||
export { defineConfig } from './common/configLoader';
|
export { defineConfig } from './common/configLoader';
|
||||||
export { composedTest } from './common/testType';
|
export { composedTest } from './common/testType';
|
||||||
|
export { composedExpect } from './matchers/expect';
|
||||||
|
|
||||||
export default test;
|
export default test;
|
||||||
|
|
|
@ -337,3 +337,7 @@ function computeArgsSuffix(matcherName: string, args: any[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expectLibrary.extend(customMatchers);
|
expectLibrary.extend(customMatchers);
|
||||||
|
|
||||||
|
export function composedExpect(...expects: any[]) {
|
||||||
|
return expect;
|
||||||
|
}
|
||||||
|
|
|
@ -5207,6 +5207,14 @@ type MergedTestType<List> = TestType<MergedT<List>, MergedW<List>>;
|
||||||
*/
|
*/
|
||||||
export function composedTest<List extends any[]>(...tests: List): MergedTestType<List>;
|
export function composedTest<List extends any[]>(...tests: List): MergedTestType<List>;
|
||||||
|
|
||||||
|
type MergedExpectMatchers<List> = List extends [Expect<infer M>, ...(infer Rest)] ? M & MergedExpectMatchers<Rest> : {};
|
||||||
|
type MergedExpect<List> = Expect<MergedExpectMatchers<List>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges expects
|
||||||
|
*/
|
||||||
|
export function composedExpect<List extends any[]>(...expects: List): MergedExpect<List>;
|
||||||
|
|
||||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||||
export {};
|
export {};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { test as test1, composedTest } from '@playwright/test';
|
import { test as test1, expect as expect1, composedTest, composedExpect } from '@playwright/test';
|
||||||
import type { Page } from '@playwright/test';
|
import type { Page } from '@playwright/test';
|
||||||
import { test as test2 } from 'playwright-test-plugin';
|
import { test as test2, expect as expect2 } from 'playwright-test-plugin';
|
||||||
|
|
||||||
const test = composedTest(test1, test2);
|
const test = composedTest(test1, test2);
|
||||||
|
const expect = composedExpect(expect1, expect2);
|
||||||
|
|
||||||
test('sample test', async ({ page, plugin }) => {
|
test('sample test', async ({ page, plugin }) => {
|
||||||
type IsPage = (typeof page) extends Page ? true : never;
|
type IsPage = (typeof page) extends Page ? true : never;
|
||||||
|
@ -10,4 +11,9 @@ test('sample test', async ({ page, plugin }) => {
|
||||||
|
|
||||||
type IsString = (typeof plugin) extends string ? true : never;
|
type IsString = (typeof plugin) extends string ? true : never;
|
||||||
const isString: IsString = true;
|
const isString: IsString = true;
|
||||||
|
|
||||||
|
await page.setContent('<div>hello world</div>');
|
||||||
|
await expect(page).toContainText('hello');
|
||||||
|
// @ts-expect-error
|
||||||
|
await expect(page).toContainText(123);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { test as test1, expect, composedTest } from '@playwright/test';
|
import { test as test1, expect as expect1, composedTest, composedExpect } from '@playwright/test';
|
||||||
import { test as test2 } from 'playwright-test-plugin';
|
import { test as test2, expect as expect2 } from 'playwright-test-plugin';
|
||||||
|
|
||||||
const test = composedTest(test1, test2);
|
const test = composedTest(test1, test2);
|
||||||
|
const expect = composedExpect(expect1, expect2);
|
||||||
|
|
||||||
test('sample test', async ({ page, plugin }) => {
|
test('sample test', async ({ page, plugin }) => {
|
||||||
await page.setContent(`<div>hello</div><span>world</span>`);
|
await page.setContent(`<div>hello</div><span>world</span>`);
|
||||||
|
@ -9,4 +10,7 @@ test('sample test', async ({ page, plugin }) => {
|
||||||
|
|
||||||
console.log(`plugin value: ${plugin}`);
|
console.log(`plugin value: ${plugin}`);
|
||||||
expect(plugin).toBe('hello from plugin');
|
expect(plugin).toBe('hello from plugin');
|
||||||
|
|
||||||
|
await page.setContent('<div>hello world</div>');
|
||||||
|
await expect(page).toContainText('hello');
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,10 +14,36 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test as base } from '@playwright/test';
|
import { test as baseTest, expect as expectBase } from '@playwright/test';
|
||||||
|
import type { Page } from '@playwright/test';
|
||||||
|
|
||||||
export const test = base.extend<{ plugin: string }>({
|
export const test = baseTest.extend<{ plugin: string }>({
|
||||||
plugin: async ({}, use) => {
|
plugin: async ({}, use) => {
|
||||||
await use('hello from plugin');
|
await use('hello from plugin');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const expect = expectBase.extend({
|
||||||
|
async toContainText(page: Page, expected: string) {
|
||||||
|
const locator = page.getByText(expected);
|
||||||
|
|
||||||
|
let pass: boolean;
|
||||||
|
let matcherResult: any;
|
||||||
|
try {
|
||||||
|
await expectBase(locator).toBeVisible();
|
||||||
|
pass = true;
|
||||||
|
} catch (e: any) {
|
||||||
|
matcherResult = e.matcherResult;
|
||||||
|
pass = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'toContainText',
|
||||||
|
expected,
|
||||||
|
message: () => matcherResult.message,
|
||||||
|
pass,
|
||||||
|
actual: matcherResult?.actual,
|
||||||
|
log: matcherResult?.log,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -878,4 +878,69 @@ test('should suppport toHaveAttribute without optional value', async ({ runTSC }
|
||||||
`
|
`
|
||||||
});
|
});
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should support composedExpect (TSC)', async ({ runTSC }) => {
|
||||||
|
const result = await runTSC({
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test, composedExpect, expect as baseExpect } from '@playwright/test';
|
||||||
|
import type { Page } from '@playwright/test';
|
||||||
|
|
||||||
|
const expect1 = baseExpect.extend({
|
||||||
|
async toBeAGoodPage(page: Page, x: number) {
|
||||||
|
return { pass: true, message: () => '' };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const expect2 = baseExpect.extend({
|
||||||
|
async toBeABadPage(page: Page, y: string) {
|
||||||
|
return { pass: true, message: () => '' };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const expect = composedExpect(expect1, expect2);
|
||||||
|
|
||||||
|
test('custom matchers', async ({ page }) => {
|
||||||
|
await expect(page).toBeAGoodPage(123);
|
||||||
|
await expect(page).toBeABadPage('123');
|
||||||
|
// @ts-expect-error
|
||||||
|
await expect(page).toBeAMedicorePage();
|
||||||
|
// @ts-expect-error
|
||||||
|
await expect(page).toBeABadPage(123);
|
||||||
|
// @ts-expect-error
|
||||||
|
await expect(page).toBeAGoodPage('123');
|
||||||
|
});
|
||||||
|
`
|
||||||
|
});
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should support composedExpect', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test, composedExpect, expect as baseExpect } from '@playwright/test';
|
||||||
|
import type { Page } from '@playwright/test';
|
||||||
|
|
||||||
|
const expect1 = baseExpect.extend({
|
||||||
|
async toBeAGoodPage(page: Page, x: number) {
|
||||||
|
return { pass: true, message: () => '' };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const expect2 = baseExpect.extend({
|
||||||
|
async toBeABadPage(page: Page, y: string) {
|
||||||
|
return { pass: true, message: () => '' };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const expect = composedExpect(expect1, expect2);
|
||||||
|
|
||||||
|
test('custom matchers', async ({ page }) => {
|
||||||
|
await expect(page).toBeAGoodPage(123);
|
||||||
|
await expect(page).toBeABadPage('123');
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { workers: 1 });
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
|
@ -464,5 +464,13 @@ type MergedTestType<List> = TestType<MergedT<List>, MergedW<List>>;
|
||||||
*/
|
*/
|
||||||
export function composedTest<List extends any[]>(...tests: List): MergedTestType<List>;
|
export function composedTest<List extends any[]>(...tests: List): MergedTestType<List>;
|
||||||
|
|
||||||
|
type MergedExpectMatchers<List> = List extends [Expect<infer M>, ...(infer Rest)] ? M & MergedExpectMatchers<Rest> : {};
|
||||||
|
type MergedExpect<List> = Expect<MergedExpectMatchers<List>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges expects
|
||||||
|
*/
|
||||||
|
export function composedExpect<List extends any[]>(...expects: List): MergedExpect<List>;
|
||||||
|
|
||||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||||
export {};
|
export {};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче