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 { composedTest } from './common/testType';
|
||||
export { composedExpect } from './matchers/expect';
|
||||
|
||||
export default test;
|
||||
|
|
|
@ -337,3 +337,7 @@ function computeArgsSuffix(matcherName: string, args: any[]) {
|
|||
}
|
||||
|
||||
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>;
|
||||
|
||||
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
|
||||
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 { test as test2 } from 'playwright-test-plugin';
|
||||
import { test as test2, expect as expect2 } from 'playwright-test-plugin';
|
||||
|
||||
const test = composedTest(test1, test2);
|
||||
const expect = composedExpect(expect1, expect2);
|
||||
|
||||
test('sample test', async ({ page, plugin }) => {
|
||||
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;
|
||||
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 test2 } from 'playwright-test-plugin';
|
||||
import { test as test1, expect as expect1, composedTest, composedExpect } from '@playwright/test';
|
||||
import { test as test2, expect as expect2 } from 'playwright-test-plugin';
|
||||
|
||||
const test = composedTest(test1, test2);
|
||||
const expect = composedExpect(expect1, expect2);
|
||||
|
||||
test('sample test', async ({ page, plugin }) => {
|
||||
await page.setContent(`<div>hello</div><span>world</span>`);
|
||||
|
@ -9,4 +10,7 @@ test('sample test', async ({ page, plugin }) => {
|
|||
|
||||
console.log(`plugin value: ${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.
|
||||
*/
|
||||
|
||||
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) => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
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>;
|
||||
|
||||
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
|
||||
export {};
|
||||
|
|
Загрузка…
Ссылка в новой задаче