feat: expose `composedTest()` instead of `test._extendTest()` (#27414)

This commit is contained in:
Dmitry Gozman 2023-10-03 13:26:30 -07:00 коммит произвёл GitHub
Родитель 47b0255b89
Коммит 65ce4cd213
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 74 добавлений и 45 удалений

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

@ -57,7 +57,6 @@ export class TestTypeImpl {
test.step = this._step.bind(this);
test.use = wrapFunctionWithLocation(this._use.bind(this));
test.extend = wrapFunctionWithLocation(this._extend.bind(this));
test._extendTest = wrapFunctionWithLocation(this._extendTest.bind(this));
test.info = () => {
const result = currentTestInfo();
if (!result)
@ -231,19 +230,10 @@ 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 test._extendTest()?`);
throw new Error(`test.extend() accepts fixtures object, not a test object.\nDid you mean to call composedTest()?`);
const fixturesWithLocation: FixturesWithLocation = { fixtures, location };
return new TestTypeImpl([...this.fixtures, fixturesWithLocation]).test;
}
private _extendTest(location: Location, test: TestType<any, any>) {
const testTypeImpl = (test as any)[testTypeSymbol] as TestTypeImpl;
if (!testTypeImpl)
throw new Error(`test._extendTest() accepts a single "test" parameter.\nDid you mean to call test.extend() with fixtures instead?`);
// Filter out common ancestor fixtures.
const newFixtures = testTypeImpl.fixtures.filter(theirs => !this.fixtures.find(ours => ours.fixtures === theirs.fixtures));
return new TestTypeImpl([...this.fixtures, ...newFixtures]).test;
}
}
function throwIfRunningInsideJest() {
@ -258,3 +248,16 @@ function throwIfRunningInsideJest() {
}
export const rootTestType = new TestTypeImpl([]);
export function composedTest(...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?`);
// 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]);
}
return result.test;
}

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

@ -764,4 +764,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 default test;

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

@ -15,12 +15,10 @@
*/
const pwt = require('./lib/index');
const { defineConfig } = require('./lib/common/configLoader');
const playwright = require('./index');
const combinedExports = {
...playwright,
...pwt,
defineConfig,
};
Object.defineProperty(combinedExports, '__esModule', { value: true });

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

@ -5198,6 +5198,15 @@ export function defineConfig(config: PlaywrightTestConfig, ...configs: Playwrigh
export function defineConfig<T>(config: PlaywrightTestConfig<T>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T>;
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T, W>;
type MergedT<List> = List extends [TestType<infer T, any>, ...(infer Rest)] ? T & MergedT<Rest> : {};
type MergedW<List> = List extends [TestType<any, infer W>, ...(infer Rest)] ? W & MergedW<Rest> : {};
type MergedTestType<List> = TestType<MergedT<List>, MergedW<List>>;
/**
* Merges fixtures
*/
export function composedTest<List extends any[]>(...tests: List): MergedTestType<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 type { TestType, Fixtures } from '@playwright/test';
import { composedTest } from '@playwright/test';
import { test } from '@playwright/test';
import type { CommonFixtures, CommonWorkerFixtures } from './commonFixtures';
import { commonFixtures } from './commonFixtures';
@ -24,18 +24,9 @@ import { coverageTest } from './coverageFixtures';
import { platformTest } from './platformFixtures';
import { testModeTest } from './testModeFixtures';
interface TestTypeEx<TestArgs, WorkerArgs> extends TestType<TestArgs, WorkerArgs> {
extend<T, W = {}>(fixtures: Fixtures<T, W, TestArgs, WorkerArgs>): TestTypeEx<TestArgs & T, WorkerArgs & W>;
_extendTest<T, W>(other: TestType<T, W>): TestTypeEx<TestArgs & T, WorkerArgs & W>;
}
type BaseT = (typeof test) extends TestType<infer T, infer W> ? T : never; // eslint-disable-line
type BaseW = (typeof test) extends TestType<infer T, infer W> ? W : never; // eslint-disable-line
export const base = test as TestTypeEx<BaseT, BaseW>;
export const base = test;
export const baseTest = base
._extendTest(coverageTest)
._extendTest(platformTest)
._extendTest(testModeTest)
export const baseTest = composedTest(base, coverageTest, platformTest, testModeTest)
.extend<CommonFixtures, CommonWorkerFixtures>(commonFixtures)
.extend<ServerFixtures, ServerWorkerOptions>(serverFixtures);

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

@ -1,8 +1,8 @@
import { test as test1 } from '@playwright/test';
import { test as test1, composedTest } from '@playwright/test';
import type { Page } from '@playwright/test';
import { test as test2 } from 'playwright-test-plugin';
const test = (test1 as any)._extendTest(test2);
const test = composedTest(test1, test2);
test('sample test', async ({ page, plugin }) => {
type IsPage = (typeof page) extends Page ? true : never;

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

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

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

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

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

@ -84,11 +84,11 @@ 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 } from '@playwright/test';
import { test as base, expect, composedTest } from '@playwright/test';
export type Params = { foo: string };
export const test = base;
export const test1 = test.extend<Params>({ foo: [ 'foo', { option: true } ] });
export const test1b = test.extend<{ bar: string }>({ bar: [ 'bar', { option: true } ] });
export const testW = test.extend<{}, { bar: string }>({ bar: ['bar', { scope: 'worker' }] });
export const testerror = test.extend<{ foo: string }>({
// @ts-expect-error
foo: 123
@ -100,9 +100,21 @@ test('test.extend options should check types', async ({ runTSC }) => {
// @ts-expect-error
bar: async ({ baz }, run) => { await run(42); }
});
// TODO: enable when _extendTest is out of experiment.
// export const test4 = test1._extendTest(test1b);
export const test4 = test1;
export const test4 = composedTest(test1, testW);
const test5 = test4.extend<{}, { hey: string, hey2: string }>({
// @ts-expect-error
hey: [async ({ foo }, use) => {
await use(foo);
}, { scope: 'worker' }],
hey2: [async ({ bar }, use) => {
await use(bar);
}, { scope: 'worker' }],
});
export const test6 = test4.extend<{ hey: string }>({
hey: async ({ foo }, use) => {
await use(foo);
},
});
`,
'playwright.config.ts': `
import { Params } from './helper';
@ -127,7 +139,7 @@ test('test.extend options should check types', async ({ runTSC }) => {
module.exports = configs;
`,
'a.spec.ts': `
import { test, test1, test2, test3, test4 } from './helper';
import { test, test1, test2, test3, test4, test6 } from './helper';
// @ts-expect-error
test('my test', async ({ foo }) => {});
test1('my test', async ({ foo }) => {});
@ -136,8 +148,12 @@ test('test.extend options should check types', async ({ runTSC }) => {
test2('my test', async ({ foo, bar }) => {});
// @ts-expect-error
test2('my test', async ({ foo, baz }) => {});
// TODO: enable when _extendTest is out of experiment.
// test4('my test', async ({ foo, bar }) => {});
test4('my test', async ({ foo, bar }) => {});
// @ts-expect-error
test4('my test', async ({ foo, qux }) => {});
test6('my test', async ({ bar, hey }) => {});
// @ts-expect-error
test6('my test', async ({ qux }) => {});
`
});
expect(result.exitCode).toBe(0);

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

@ -455,5 +455,14 @@ export function defineConfig(config: PlaywrightTestConfig, ...configs: Playwrigh
export function defineConfig<T>(config: PlaywrightTestConfig<T>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T>;
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T, W>;
type MergedT<List> = List extends [TestType<infer T, any>, ...(infer Rest)] ? T & MergedT<Rest> : {};
type MergedW<List> = List extends [TestType<any, infer W>, ...(infer Rest)] ? W & MergedW<Rest> : {};
type MergedTestType<List> = TestType<MergedT<List>, MergedW<List>>;
/**
* Merges fixtures
*/
export function composedTest<List extends any[]>(...tests: List): MergedTestType<List>;
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
export {};