Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: composed->merge #27555

Merged
merged 1 commit into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions docs/src/release-notes-js.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 now merge test fixtures from multiple files or modules:

```js title="fixtures.ts"
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 combine fixtures and custom expect matchers from multiple files or modules.
You can now merge custom expect 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 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.

Expand Down
6 changes: 3 additions & 3 deletions docs/src/test-configuration-js.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright/src/common/testType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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]);
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
2 changes: 1 addition & 1 deletion packages/playwright/src/matchers/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 2 additions & 2 deletions packages/playwright/types/test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5328,15 +5328,15 @@ 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>>;

/**
* 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 {};
Expand Down
4 changes: 2 additions & 2 deletions tests/config/baseTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
6 changes: 3 additions & 3 deletions tests/installation/fixture-scripts/plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -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>`);
Expand Down
12 changes: 6 additions & 6 deletions tests/playwright-test/expect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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);
Expand All @@ -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({
Expand All @@ -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);
Expand Down
16 changes: 8 additions & 8 deletions tests/playwright-test/test-extend.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,15 @@ 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 = {
use: { param: 'from-config' },
};
`,
'a.test.ts': `
import { test, expect, composedTest } from '@playwright/test';
import { test, expect, mergeTests } from '@playwright/test';
const base = test.extend({
myFixture: 'abc',
});
Expand All @@ -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);
Expand All @@ -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';
Expand All @@ -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', () => {});
`,
});
Expand Down
4 changes: 2 additions & 2 deletions tests/playwright-test/types-2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } ] });
Expand All @@ -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) => {
Expand Down
4 changes: 2 additions & 2 deletions utils/generate_types/overrides-test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,15 +462,15 @@ 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>>;

/**
* 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 {};
Loading