This commit is contained in:
Pavel Feldman 2023-10-11 13:56:27 -07:00 коммит произвёл GitHub
Родитель 3a43aaa700
Коммит 393bd36e0a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 71 добавлений и 49 удалений

Просмотреть файл

@ -8,46 +8,68 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
## Version 1.39
### Extending expect with custom matchers
### Add custom matchers to your expect
You can extend Playwright assertions by providing custom matchers. These matchers will be available on the expect object.
```js title=fixtures.ts
```js title="test.spec.ts"
import { expect as baseExpect } from '@playwright/test';
export const expect = baseExpect.extend({
async toHaveAmount(locator: Locator, expected: number, options?: { timeout?: number }) {
// Note: this matcher never passes, see the documentation for a full example.
// Return a "pass" flag and a message getter.
return { pass: false, message: () => `Expected ${expected} amount` };
// ... see documentation for how to write matchers.
},
});
test('pass', async ({ page }) => {
await expect(page.getByTestId('cart')).toHaveAmount(5);
});
```
See the documentation [for a full example](./test-configuration.md#add-custom-matchers-using-expectextend).
### Merging fixtures and expect matchers
### Merge test fixtures
You can combine fixtures and custom expect matchers from multiple files or modules.
You can now merge test fixtures from multiple files or modules:
```js title="fixtures.ts"
import { composedTest, composedExpect } from '@playwright/test';
import { mergeTests } from '@playwright/test';
import { test as dbTest } from 'database-test-utils';
import { test as a11yTest } from 'a11y-test-utils';
export const test = mergeTests(dbTest, a11yTest);
```
```js title="test.spec.ts"
import { test } from './fixtures';
test('passes', async ({ database, page, a11y }) => {
// use database and a11y fixtures.
});
```
### Merge custom expect matchers
You can now merge custom expect matchers from multiple files or modules:
```js title="fixtures.ts"
import { mergeTests, mergeExpects } from '@playwright/test';
import { test as dbTest, expect as dbExpect } from 'database-test-utils';
import { test as a11yTest, expect as a11yExpect } from 'a11y-test-utils';
export const expect = composedExpect(dbExpect, a11yExpect);
export const test = composedTest(dbTest, a11yTest);
export const test = mergeTests(dbTest, a11yTest);
export const expect = mergeExpects(dbExpect, a11yExpect);
```
```js title="test.spec.ts"
import { test, expect } from './fixtures';
test('passes', async ({ database, page }) => {
test('passes', async ({ page, database }) => {
await expect(database).toHaveDatabaseUser('admin');
await expect(page).toPassA11yAudit();
});
```
### Boxed test steps
### Hide implementation details: box test steps
You can mark a [`method: Test.step`] as "boxed" so that errors inside it point to the step call site.

Просмотреть файл

@ -217,12 +217,12 @@ Do not confuse Playwright's `expect` with the [`expect` library](https://jestjs.
You can combine custom matchers from multiple files or modules.
```js title="fixtures.ts"
import { composedTest, composedExpect } from '@playwright/test';
import { mergeTests, mergeExpects } from '@playwright/test';
import { test as dbTest, expect as dbExpect } from 'database-test-utils';
import { test as a11yTest, expect as a11yExpect } from 'a11y-test-utils';
export const expect = composedExpect(dbExpect, a11yExpect);
export const test = composedTest(dbTest, a11yTest);
export const expect = mergeExpects(dbExpect, a11yExpect);
export const test = mergeTests(dbTest, a11yTest);
```
```js title="test.spec.ts"

Просмотреть файл

@ -230,7 +230,7 @@ export class TestTypeImpl {
private _extend(location: Location, fixtures: Fixtures) {
if ((fixtures as any)[testTypeSymbol])
throw new Error(`test.extend() accepts fixtures object, not a test object.\nDid you mean to call composedTest()?`);
throw new Error(`test.extend() accepts fixtures object, not a test object.\nDid you mean to call mergeTests()?`);
const fixturesWithLocation: FixturesWithLocation = { fixtures, location };
return new TestTypeImpl([...this.fixtures, fixturesWithLocation]).test;
}
@ -249,12 +249,12 @@ function throwIfRunningInsideJest() {
export const rootTestType = new TestTypeImpl([]);
export function composedTest(...tests: TestType<any, any>[]) {
export function mergeTests(...tests: TestType<any, any>[]) {
let result = rootTestType;
for (const t of tests) {
const testTypeImpl = (t as any)[testTypeSymbol] as TestTypeImpl;
if (!testTypeImpl)
throw new Error(`composedTest() accepts "test" functions as parameters.\nDid you mean to call test.extend() with fixtures instead?`);
throw new Error(`mergeTests() accepts "test" functions as parameters.\nDid you mean to call test.extend() with fixtures instead?`);
// Filter out common ancestor fixtures.
const newFixtures = testTypeImpl.fixtures.filter(theirs => !result.fixtures.find(ours => ours.fixtures === theirs.fixtures));
result = new TestTypeImpl([...result.fixtures, ...newFixtures]);

Просмотреть файл

@ -744,7 +744,7 @@ function renderApiCall(apiName: string, params: any) {
export const test = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures);
export { defineConfig } from './common/configLoader';
export { composedTest } from './common/testType';
export { composedExpect } from './matchers/expect';
export { mergeTests } from './common/testType';
export { mergeExpects } from './matchers/expect';
export default test;

Просмотреть файл

@ -338,6 +338,6 @@ function computeArgsSuffix(matcherName: string, args: any[]) {
expectLibrary.extend(customMatchers);
export function composedExpect(...expects: any[]) {
export function mergeExpects(...expects: any[]) {
return expect;
}

4
packages/playwright/types/test.d.ts поставляемый
Просмотреть файл

@ -5328,7 +5328,7 @@ type MergedTestType<List> = TestType<MergedT<List>, MergedW<List>>;
/**
* Merges fixtures
*/
export function composedTest<List extends any[]>(...tests: List): MergedTestType<List>;
export function mergeTests<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>>;
@ -5336,7 +5336,7 @@ type MergedExpect<List> = Expect<MergedExpectMatchers<List>>;
/**
* Merges expects
*/
export function composedExpect<List extends any[]>(...expects: List): MergedExpect<List>;
export function mergeExpects<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 {};

Просмотреть файл

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { composedTest } from '@playwright/test';
import { mergeTests } from '@playwright/test';
import { test } from '@playwright/test';
import type { CommonFixtures, CommonWorkerFixtures } from './commonFixtures';
import { commonFixtures } from './commonFixtures';
@ -26,7 +26,7 @@ import { testModeTest } from './testModeFixtures';
export const base = test;
export const baseTest = composedTest(base, coverageTest, platformTest, testModeTest)
export const baseTest = mergeTests(base, coverageTest, platformTest, testModeTest)
.extend<CommonFixtures, CommonWorkerFixtures>(commonFixtures)
.extend<ServerFixtures, ServerWorkerOptions>(serverFixtures);

Просмотреть файл

@ -1,9 +1,9 @@
import { test as test1, expect as expect1, composedTest, composedExpect } from '@playwright/test';
import { test as test1, expect as expect1, mergeTests, mergeExpects } from '@playwright/test';
import type { Page } from '@playwright/test';
import { test as test2, expect as expect2 } from 'playwright-test-plugin';
const test = composedTest(test1, test2);
const expect = composedExpect(expect1, expect2);
const test = mergeTests(test1, test2);
const expect = mergeExpects(expect1, expect2);
test('sample test', async ({ page, plugin }) => {
type IsPage = (typeof page) extends Page ? true : never;

Просмотреть файл

@ -1,8 +1,8 @@
import { test as test1, expect as expect1, composedTest, composedExpect } from '@playwright/test';
import { test as test1, expect as expect1, mergeTests, mergeExpects } from '@playwright/test';
import { test as test2, expect as expect2 } from 'playwright-test-plugin';
const test = composedTest(test1, test2);
const expect = composedExpect(expect1, expect2);
const test = mergeTests(test1, test2);
const expect = mergeExpects(expect1, expect2);
test('sample test', async ({ page, plugin }) => {
await page.setContent(`<div>hello</div><span>world</span>`);

Просмотреть файл

@ -879,10 +879,10 @@ test('should suppport toHaveAttribute without optional value', async ({ runTSC }
expect(result.exitCode).toBe(0);
});
test('should support composedExpect (TSC)', async ({ runTSC }) => {
test('should support mergeExpects (TSC)', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
import { test, composedExpect, expect as baseExpect } from '@playwright/test';
import { test, mergeExpects, expect as baseExpect } from '@playwright/test';
import type { Page } from '@playwright/test';
const expect1 = baseExpect.extend({
@ -897,7 +897,7 @@ test('should support composedExpect (TSC)', async ({ runTSC }) => {
}
});
const expect = composedExpect(expect1, expect2);
const expect = mergeExpects(expect1, expect2);
test('custom matchers', async ({ page }) => {
await expect(page).toBeAGoodPage(123);
@ -914,10 +914,10 @@ test('should support composedExpect (TSC)', async ({ runTSC }) => {
expect(result.exitCode).toBe(0);
});
test('should support composedExpect', async ({ runInlineTest }) => {
test('should support mergeExpects', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test, composedExpect, expect as baseExpect } from '@playwright/test';
import { test, mergeExpects, expect as baseExpect } from '@playwright/test';
import type { Page } from '@playwright/test';
const expect1 = baseExpect.extend({
@ -932,7 +932,7 @@ test('should support composedExpect', async ({ runInlineTest }) => {
}
});
const expect = composedExpect(expect1, expect2);
const expect = mergeExpects(expect1, expect2);
test('custom matchers', async ({ page }) => {
await expect(page).toBeAGoodPage(123);

Просмотреть файл

@ -160,7 +160,7 @@ test('config should override options but not fixtures', async ({ runInlineTest }
expect(result.output).toContain('fixture-config-fixture');
});
test('composedTest should be able to merge', async ({ runInlineTest }) => {
test('mergeTests should be able to merge', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = {
@ -168,7 +168,7 @@ test('composedTest should be able to merge', async ({ runInlineTest }) => {
};
`,
'a.test.ts': `
import { test, expect, composedTest } from '@playwright/test';
import { test, expect, mergeTests } from '@playwright/test';
const base = test.extend({
myFixture: 'abc',
});
@ -184,7 +184,7 @@ test('composedTest should be able to merge', async ({ runInlineTest }) => {
fixture2: ({}, use) => use('fixture2'),
});
const test3 = composedTest(test1, test2);
const test3 = mergeTests(test1, test2);
test3('merged', async ({ param, fixture1, myFixture, fixture2 }) => {
console.log('param-' + param);
@ -202,7 +202,7 @@ test('composedTest should be able to merge', async ({ runInlineTest }) => {
expect(result.output).toContain('fixture2-fixture2');
});
test('test.extend should print nice message when used as composedTest', async ({ runInlineTest }) => {
test('test.extend should print nice message when used as mergeTests', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test as base, expect } from '@playwright/test';
@ -215,14 +215,14 @@ test('test.extend should print nice message when used as composedTest', async ({
});
expect(result.exitCode).toBe(1);
expect(result.passed).toBe(0);
expect(result.output).toContain('Did you mean to call composedTest()?');
expect(result.output).toContain('Did you mean to call mergeTests()?');
});
test('composedTest should print nice message when used as extend', async ({ runInlineTest }) => {
test('mergeTests should print nice message when used as extend', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test as base, expect, composedTest } from '@playwright/test';
const test3 = composedTest(base, {});
import { test as base, expect, mergeTests } from '@playwright/test';
const test3 = mergeTests(base, {});
test3('test', () => {});
`,
});

Просмотреть файл

@ -84,7 +84,7 @@ test('can return anything from hooks', async ({ runTSC }) => {
test('test.extend options should check types', async ({ runTSC }) => {
const result = await runTSC({
'helper.ts': `
import { test as base, expect, composedTest } from '@playwright/test';
import { test as base, expect, mergeTests } from '@playwright/test';
export type Params = { foo: string };
export const test = base;
export const test1 = test.extend<Params>({ foo: [ 'foo', { option: true } ] });
@ -100,7 +100,7 @@ test('test.extend options should check types', async ({ runTSC }) => {
// @ts-expect-error
bar: async ({ baz }, run) => { await run(42); }
});
export const test4 = composedTest(test1, testW);
export const test4 = mergeTests(test1, testW);
const test5 = test4.extend<{}, { hey: string, hey2: string }>({
// @ts-expect-error
hey: [async ({ foo }, use) => {

4
utils/generate_types/overrides-test.d.ts поставляемый
Просмотреть файл

@ -462,7 +462,7 @@ type MergedTestType<List> = TestType<MergedT<List>, MergedW<List>>;
/**
* Merges fixtures
*/
export function composedTest<List extends any[]>(...tests: List): MergedTestType<List>;
export function mergeTests<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>>;
@ -470,7 +470,7 @@ type MergedExpect<List> = Expect<MergedExpectMatchers<List>>;
/**
* Merges expects
*/
export function composedExpect<List extends any[]>(...expects: List): MergedExpect<List>;
export function mergeExpects<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 {};