From a39a97f0eea9ba9a0e3f9052dba60d81365e0a7d Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 9 Jan 2023 11:21:48 -0800 Subject: [PATCH] chore: setup -> test.projectSetup (#19932) * Changed `setup` to `test.projectSetup` * Only `test.projectSetup.only` is supported on the new method * test.* methods except for before/after/Each/All hooks can be called inside the project setup files --- docs/src/test-api/class-test.md | 16 ++ packages/playwright-test/src/index.ts | 4 +- packages/playwright-test/src/testType.ts | 51 ++-- tests/playwright-test/project-setup.spec.ts | 243 ++++++++++---------- tests/playwright-test/store.spec.ts | 28 +-- 5 files changed, 184 insertions(+), 158 deletions(-) diff --git a/docs/src/test-api/class-test.md b/docs/src/test-api/class-test.md index a286ebab0a..3a50fe97bd 100644 --- a/docs/src/test-api/class-test.md +++ b/docs/src/test-api/class-test.md @@ -1174,6 +1174,22 @@ Test title. Test function that takes one or two arguments: an object with fixtures and optional [TestInfo]. +## method: Test.projectSetup +* since: v1.30 + +Declares a project setup function. The function will be run before all other tests in the same project and if it fails the project execution will be aborted. + +### param: Test.projectSetup.title +* since: v1.30 +- `title` <[string]> + +Project setup title. + +### param: Test.projectSetup.testFunction +* since: v1.30 +- `testFunction` <[function]\([Fixtures], [TestInfo]\)> + +Project setup function that takes one or two arguments: an object with fixtures and optional [TestInfo]. ## method: Test.setTimeout diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index abaadcbc2f..c9d72a10cc 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -23,7 +23,7 @@ import { removeFolders } from 'playwright-core/lib/utils/fileUtils'; import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, TraceMode, VideoMode } from '../types/test'; import { store as _baseStore } from './store'; import type { TestInfoImpl } from './testInfo'; -import { rootTestType, _setProjectSetup } from './testType'; +import { rootTestType } from './testType'; import { type ContextReuseMode } from './types'; export { expect } from './expect'; export { addRunnerPlugin as _addRunnerPlugin } from './plugins'; @@ -634,7 +634,5 @@ function normalizeScreenshotMode(screenshot: PlaywrightWorkerOptions['screenshot const kTracingStarted = Symbol('kTracingStarted'); export const test = _baseTest.extend(playwrightFixtures); -export const setup = _baseTest.extend(playwrightFixtures); -_setProjectSetup(setup, true); export default test; diff --git a/packages/playwright-test/src/testType.ts b/packages/playwright-test/src/testType.ts index 2a68e9de77..538d622d11 100644 --- a/packages/playwright-test/src/testType.ts +++ b/packages/playwright-test/src/testType.ts @@ -26,8 +26,6 @@ const testTypeSymbol = Symbol('testType'); export class TestTypeImpl { readonly fixtures: FixturesWithLocation[]; readonly test: TestType; - // Wether the test is 'setup' which should only be called inside project setup files. - _projectSetup: boolean = false; constructor(fixtures: FixturesWithLocation[]) { this.fixtures = fixtures; @@ -57,6 +55,8 @@ export class TestTypeImpl { test.step = wrapFunctionWithLocation(this._step.bind(this)); test.use = wrapFunctionWithLocation(this._use.bind(this)); test.extend = wrapFunctionWithLocation(this._extend.bind(this)); + test.projectSetup = wrapFunctionWithLocation(this._createTest.bind(this, 'projectSetup')); + (test.projectSetup as any).only = wrapFunctionWithLocation(this._createTest.bind(this, 'projectSetupOnly')); test._extendTest = wrapFunctionWithLocation(this._extendTest.bind(this)); test.info = () => { const result = currentTestInfo(); @@ -67,7 +67,7 @@ export class TestTypeImpl { this.test = test; } - private _currentSuite(location: Location, title: string): Suite | undefined { + private _currentSuite(location: Location, title: string, allowedContext: 'test' | 'projectSetup' | 'any'): Suite | undefined { const suite = currentlyLoadingFileSuite(); if (!suite) { addFatalError([ @@ -80,18 +80,28 @@ export class TestTypeImpl { ].join('\n'), location); return; } - if (this._projectSetup !== suite._isProjectSetup) { - if (this._projectSetup) - addFatalError(`${title} is called in a file which is not a part of project setup.`, location); - else - addFatalError(`${title} is called in a project setup file (use 'setup' instead of 'test').`, location); - } + if (allowedContext === 'projectSetup' && !suite._isProjectSetup) + addFatalError(`${title} is only allowed in a project setup file.`, location); + else if (allowedContext === 'test' && suite._isProjectSetup) + addFatalError(`${title} is not allowed in a project setup file.`, location); return suite; } - private _createTest(type: 'default' | 'only' | 'skip' | 'fixme', location: Location, title: string, fn: Function) { + private _createTest(type: 'default' | 'only' | 'skip' | 'fixme' | 'projectSetup' | 'projectSetupOnly', location: Location, title: string, fn: Function) { throwIfRunningInsideJest(); - const suite = this._currentSuite(location, this._projectSetup ? 'setup()' : 'test()'); + let functionTitle = 'test()'; + let allowedContext: 'test' | 'projectSetup' | 'any' = 'any'; + switch (type) { + case 'projectSetup': + case 'projectSetupOnly': + functionTitle = 'test.projectSetup()'; + allowedContext = 'projectSetup'; + break; + case 'default': + allowedContext = 'test'; + break; + } + const suite = this._currentSuite(location, functionTitle, allowedContext); if (!suite) return; const test = new TestCase(title, fn, this, location); @@ -99,7 +109,7 @@ export class TestTypeImpl { test._isProjectSetup = suite._isProjectSetup; suite._addTest(test); - if (type === 'only') + if (type === 'only' || type === 'projectSetupOnly') test._only = true; if (type === 'skip' || type === 'fixme') { test.annotations.push({ type }); @@ -113,7 +123,7 @@ export class TestTypeImpl { private _describe(type: 'default' | 'only' | 'serial' | 'serial.only' | 'parallel' | 'parallel.only' | 'skip' | 'fixme', location: Location, title: string | Function, fn?: Function) { throwIfRunningInsideJest(); - const suite = this._currentSuite(location, this._projectSetup ? 'setup.describe()' : 'test.describe()'); + const suite = this._currentSuite(location, 'test.describe()', 'any'); if (!suite) return; @@ -150,7 +160,7 @@ export class TestTypeImpl { } private _hook(name: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', location: Location, fn: Function) { - const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.${name}()`); + const suite = this._currentSuite(location, `test.${name}()`, 'test'); if (!suite) return; suite._hooks.push({ type: name, fn, location }); @@ -158,7 +168,7 @@ export class TestTypeImpl { private _configure(location: Location, options: { mode?: 'parallel' | 'serial', retries?: number, timeout?: number }) { throwIfRunningInsideJest(); - const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.describe.configure()`); + const suite = this._currentSuite(location, `test.describe.configure()`, 'any'); if (!suite) return; @@ -225,7 +235,7 @@ export class TestTypeImpl { } private _use(location: Location, fixtures: Fixtures) { - const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.use()`); + const suite = this._currentSuite(location, `test.use()`, 'any'); if (!suite) return; suite._use.push({ fixtures, location }); @@ -234,7 +244,7 @@ export class TestTypeImpl { private async _step(location: Location, title: string, body: () => Promise): Promise { const testInfo = currentTestInfo(); if (!testInfo) { - addFatalError(`${this._projectSetup ? 'setup' : 'test'}.step() can only be called from a test`, location); + addFatalError(`test.step() can only be called from a test`, location); return undefined as any; } const step = testInfo._addStep({ @@ -281,11 +291,4 @@ function throwIfRunningInsideJest() { } } -export function _setProjectSetup(test: TestType, projectSetup: boolean) { - const testTypeImpl = (test as any)[testTypeSymbol] as TestTypeImpl; - if (!testTypeImpl) - throw new Error(`Argument is not a TestType`); - testTypeImpl._projectSetup = projectSetup; -} - export const rootTestType = new TestTypeImpl([]); diff --git a/tests/playwright-test/project-setup.spec.ts b/tests/playwright-test/project-setup.spec.ts index c590b98679..8b747219e3 100644 --- a/tests/playwright-test/project-setup.spec.ts +++ b/tests/playwright-test/project-setup.spec.ts @@ -29,8 +29,8 @@ function createConfigWithProjects(names: string[], testInfo: TestInfo, projectTe await new Promise(f => setTimeout(f, 100)); });`; files[`${name}/${name}.setup.ts`] = ` - const { setup } = pwt; - setup('${name} setup', async () => { + const { test } = pwt; + test.projectSetup('${name} setup', async () => { await new Promise(f => setTimeout(f, 100)); });`; } @@ -142,8 +142,8 @@ test('should stop project if setup fails', async ({ runGroups }, testInfo) => { }; const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates); configWithFiles[`a/a.setup.ts`] = ` - const { setup, expect } = pwt; - setup('a setup', async () => { + const { test, expect } = pwt; + test.projectSetup('a setup', async () => { expect(1).toBe(2); });`; @@ -179,9 +179,9 @@ test('should run setup in each project shard', async ({ runGroups }, testInfo) = test('test2', async () => { }); `, 'c.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, }; @@ -233,14 +233,14 @@ test('should run setup only for projects that have tests in the shard', async ({ test('test2', async () => { }); `, 'p1.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, 'p2.setup.ts': ` - const { setup } = pwt; - setup('setup3', async () => { }); - setup('setup4', async () => { }); + const { test } = pwt; + test.projectSetup('setup3', async () => { }); + test.projectSetup('setup4', async () => { }); `, }; @@ -347,20 +347,20 @@ test('list-files should enumerate setup files in same group', async ({ runComman ] };`, 'a1.setup.ts': ` - const { setup } = pwt; - setup('test1', async () => { }); + const { test } = pwt; + test.projectSetup('test1', async () => { }); `, 'a2.setup.ts': ` - const { setup } = pwt; - setup('test1', async () => { }); + const { test } = pwt; + test.projectSetup('test1', async () => { }); `, 'a.test.ts': ` const { test } = pwt; test('test2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('test3', async () => { }); + const { test } = pwt; + test.projectSetup('test3', async () => { }); `, 'b.test.ts': ` const { test } = pwt; @@ -394,20 +394,20 @@ test('test --list should enumerate setup tests as regular ones', async ({ runCom ] };`, 'a1.setup.ts': ` - const { setup } = pwt; - setup('test1', async () => { }); + const { test } = pwt; + test.projectSetup('test1', async () => { }); `, 'a2.setup.ts': ` - const { setup } = pwt; - setup('test1', async () => { }); + const { test } = pwt; + test.projectSetup('test1', async () => { }); `, 'a.test.ts': ` const { test } = pwt; test('test2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('test3', async () => { }); + const { test } = pwt; + test.projectSetup('test3', async () => { }); `, 'b.test.ts': ` const { test } = pwt; @@ -419,9 +419,9 @@ test('test --list should enumerate setup tests as regular ones', async ({ runCom expect(exitCode).toBe(0); expect(output).toContain(`Listing tests: [p1] › a.test.ts:6:7 › test2 - [p1] › a1.setup.ts:5:7 › test1 - [p1] › a2.setup.ts:5:7 › test1 - [p2] › b.setup.ts:5:7 › test3 + [p1] › a1.setup.ts:5:12 › test1 + [p1] › a2.setup.ts:5:12 › test1 + [p2] › b.setup.ts:5:12 › test3 [p2] › b.test.ts:6:7 › test4 Total: 5 tests in 5 files`); }); @@ -445,17 +445,17 @@ test('should allow .only in setup files', async ({ runGroups }, testInfo) => { test('test4', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup.only('setup1', async () => { }); - setup('setup2', async () => { }); - setup.only('setup3', async () => { }); + const { test } = pwt; + test.projectSetup.only('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); + test.projectSetup.only('setup3', async () => { }); `, }; const { exitCode, passed, timeline, output } = await runGroups(files); expect(output).toContain('Running 2 tests using 1 worker'); - expect(output).toContain('[p1] › a.setup.ts:5:13 › setup1'); - expect(output).toContain('[p1] › a.setup.ts:7:13 › setup3'); + expect(output).toContain('[p1] › a.setup.ts:5:25 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:7:25 › setup3'); expect(fileNames(timeline)).toEqual(['a.setup.ts']); expect(exitCode).toBe(0); expect(passed).toBe(2); @@ -480,19 +480,19 @@ test('should allow describe.only in setup files', async ({ runGroups }, testInfo test('test4', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup.describe.only('main', () => { - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.describe.only('main', () => { + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); }); - setup('setup3', async () => { }); + test.projectSetup('setup3', async () => { }); `, }; const { exitCode, passed, timeline, output } = await runGroups(files); expect(output).toContain('Running 2 tests using 1 worker'); - expect(output).toContain('[p1] › a.setup.ts:6:9 › main › setup1'); - expect(output).toContain('[p1] › a.setup.ts:7:9 › main › setup2'); + expect(output).toContain('[p1] › a.setup.ts:6:14 › main › setup1'); + expect(output).toContain('[p1] › a.setup.ts:7:14 › main › setup2'); expect(fileNames(timeline)).toEqual(['a.setup.ts']); expect(exitCode).toBe(0); expect(passed).toBe(2); @@ -517,19 +517,19 @@ test('should filter describe line in setup files', async ({ runGroups }, testInf test('test4', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup.describe('main', () => { - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.describe('main', () => { + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); }); - setup('setup3', async () => { }); + test.projectSetup('setup3', async () => { }); `, }; const { exitCode, passed, timeline, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['a.setup.ts:5'] }); expect(output).toContain('Running 2 tests using 1 worker'); - expect(output).toContain('[p1] › a.setup.ts:6:9 › main › setup1'); - expect(output).toContain('[p1] › a.setup.ts:7:9 › main › setup2'); + expect(output).toContain('[p1] › a.setup.ts:6:14 › main › setup1'); + expect(output).toContain('[p1] › a.setup.ts:7:14 › main › setup2'); expect(fileNames(timeline)).toEqual(['a.setup.ts']); expect(exitCode).toBe(0); expect(passed).toBe(2); @@ -554,16 +554,16 @@ test('should allow .only in both setup and test files', async ({ runGroups }, te test('test4', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup.only('setup1', async () => { }); - setup('setup2', async () => { }); - setup('setup3', async () => { }); + const { test } = pwt; + test.projectSetup.only('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); + test.projectSetup('setup3', async () => { }); `, }; const { exitCode, output } = await runGroups(files); expect(exitCode).toBe(0); - expect(output).toContain('[p1] › a.setup.ts:5:13 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:5:25 › setup1'); expect(output).toContain('[p1] › a.test.ts:7:12 › test2'); }); @@ -586,13 +586,13 @@ test('should run full setup when there is test.only', async ({ runGroups }, test test('test4', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('setup3', async () => { }); + const { test } = pwt; + test.projectSetup('setup3', async () => { }); `, }; @@ -600,9 +600,9 @@ test('should run full setup when there is test.only', async ({ runGroups }, test expect(exitCode).toBe(0); expect(passed).toBe(4); expect(output).toContain('Running 4 tests using 2 workers'); - expect(output).toContain('[p1] › b.setup.ts:5:7 › setup3'); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); - expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2'); + expect(output).toContain('[p1] › b.setup.ts:5:12 › setup3'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:6:12 › setup2'); expect(output).toContain('[p1] › a.test.ts:6:12 › test1'); }); @@ -628,13 +628,13 @@ test('should allow filtering setup by file:line', async ({ runGroups }, testInfo test('test3', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); `, 'b.test.ts': ` const { test } = pwt; @@ -647,16 +647,16 @@ test('should allow filtering setup by file:line', async ({ runGroups }, testInfo { const { exitCode, passed, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*setup.ts$'] }); expect(output).toContain('Running 3 tests using 2 workers'); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); - expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2'); - expect(output).toContain('[p2] › b.setup.ts:5:7 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:6:12 › setup2'); + expect(output).toContain('[p2] › b.setup.ts:5:12 › setup1'); expect(exitCode).toBe(0); expect(passed).toBe(3); } { const { exitCode, passed, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a.setup.ts:5'] }); expect(output).toContain('Running 1 test using 1 worker'); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); expect(exitCode).toBe(0); expect(passed).toBe(1); } @@ -680,13 +680,13 @@ test('should support filters matching both setup and test', async ({ runGroups } test('test3', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); `, 'b.test.ts': ` const { test } = pwt; @@ -697,8 +697,8 @@ test('should support filters matching both setup and test', async ({ runGroups } const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a.(setup|test).ts$'] }); expect(exitCode).toBe(0); expect(output).toContain('Running 5 tests using 1 worker'); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); - expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:6:12 › setup2'); expect(output).toContain('[p1] › a.test.ts:6:7 › test1'); expect(output).toContain('[p1] › a.test.ts:7:7 › test2'); expect(output).toContain('[p1] › a.test.ts:8:7 › test3'); @@ -726,12 +726,12 @@ test('should run setup for a project if tests match only in another project', as test('test1', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); `, 'b.test.ts': ` const { test } = pwt; @@ -742,9 +742,9 @@ test('should run setup for a project if tests match only in another project', as const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a.test.ts$'] }); expect(exitCode).toBe(0); expect(output).toContain('Running 3 tests using 2 workers'); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); expect(output).toContain('[p1] › a.test.ts:6:7 › test1'); - expect(output).toContain('[p2] › b.setup.ts:5:7 › setup1'); + expect(output).toContain('[p2] › b.setup.ts:5:12 › setup1'); }); test('should run all setup files if only tests match filter', async ({ runGroups }, testInfo) => { @@ -765,22 +765,22 @@ test('should run all setup files if only tests match filter', async ({ runGroups test('test3', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); `, }; const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['a.test.ts:7'] }); expect(exitCode).toBe(0); expect(output).toContain('Running 4 tests using 2 workers'); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); - expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2'); - expect(output).toContain('[p1] › b.setup.ts:5:7 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:6:12 › setup2'); + expect(output).toContain('[p1] › b.setup.ts:5:12 › setup1'); expect(output).toContain('[p1] › a.test.ts:7:7 › test2'); }); @@ -802,22 +802,22 @@ test('should run all setup files if only tests match grep filter', async ({ runG test('test3', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); `, }; const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['--grep', '.*test2$'] }); expect(exitCode).toBe(0); expect(output).toContain('Running 4 tests using 2 workers'); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); - expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2'); - expect(output).toContain('[p1] › b.setup.ts:5:7 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:6:12 › setup2'); + expect(output).toContain('[p1] › b.setup.ts:5:12 › setup1'); expect(output).toContain('[p1] › a.test.ts:7:7 › test2'); }); @@ -840,21 +840,21 @@ test('should apply project.grep filter to both setup and tests', async ({ runGro test('foo', async () => { }); `, 'a.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('setup2', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('setup2', async () => { }); `, 'b.setup.ts': ` - const { setup } = pwt; - setup('setup1', async () => { }); - setup('foo', async () => { }); + const { test } = pwt; + test.projectSetup('setup1', async () => { }); + test.projectSetup('foo', async () => { }); `, }; const { exitCode, output } = await runGroups(files); expect(exitCode).toBe(0); - expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1'); - expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2'); + expect(output).toContain('[p1] › a.setup.ts:5:12 › setup1'); + expect(output).toContain('[p1] › a.setup.ts:6:12 › setup2'); expect(output).toContain('[p1] › a.test.ts:6:7 › test1'); expect(output).toContain('[p1] › a.test.ts:7:7 › test2'); }); @@ -862,28 +862,37 @@ test('should apply project.grep filter to both setup and tests', async ({ runGro test('should prohibit setup in test files', async ({ runGroups }, testInfo) => { const files = { 'a.test.ts': ` - const { setup, test } = pwt; - setup('test1', async () => { }); + const { test } = pwt; + test.projectSetup('test1', async () => { }); test('test2', async () => { }); `, }; const { exitCode, output } = await runGroups(files); expect(exitCode).toBe(1); - expect(output).toContain('setup() is called in a file which is not a part of project setup.'); + expect(output).toContain('test.projectSetup() is only allowed in a project setup file.'); }); -test('should prohibit setup hooks in test files', async ({ runGroups }, testInfo) => { +test('should prohibit beforeAll hooks in setup files', async ({ runGroups }, testInfo) => { const files = { - 'a.test.ts': ` - const { setup } = pwt; - setup.beforeAll(async () => { }); + 'playwright.config.ts': ` + module.exports = { + projects: [ + { + name: 'p1', + setupMatch: /.*.setup.ts/, + }, + ] + };`, + 'a.setup.ts': ` + const { test } = pwt; + test.beforeAll(async () => { }); `, }; const { exitCode, output } = await runGroups(files); expect(exitCode).toBe(1); - expect(output).toContain('setup.beforeAll() is called in a file which is not a part of project setup'); + expect(output).toContain('test.beforeAll() is not allowed in a project setup file'); }); test('should prohibit test in setup files', async ({ runGroups }, testInfo) => { @@ -905,7 +914,7 @@ test('should prohibit test in setup files', async ({ runGroups }, testInfo) => { const { exitCode, output } = await runGroups(files); expect(exitCode).toBe(1); - expect(output).toContain('test() is called in a project setup file'); + expect(output).toContain('test() is not allowed in a project setup file'); }); test('should prohibit test hooks in setup files', async ({ runGroups }, testInfo) => { @@ -927,5 +936,5 @@ test('should prohibit test hooks in setup files', async ({ runGroups }, testInfo const { exitCode, output } = await runGroups(files); expect(exitCode).toBe(1); - expect(output).toContain('test.beforeEach() is called in a project setup file'); + expect(output).toContain('test.beforeEach() is not allowed in a project setup file'); }); diff --git a/tests/playwright-test/store.spec.ts b/tests/playwright-test/store.spec.ts index 87a3e18b99..3e5330394c 100644 --- a/tests/playwright-test/store.spec.ts +++ b/tests/playwright-test/store.spec.ts @@ -54,8 +54,8 @@ test('should share store state between project setup and tests', async ({ runInl }; `, 'store.setup.ts': ` - const { setup, expect, store } = pwt; - setup('should initialize store', async ({ }) => { + const { test, expect, store } = pwt; + test.projectSetup('should initialize store', async ({ }) => { expect(await store.get('number')).toBe(undefined); await store.set('number', 2022) expect(await store.get('number')).toBe(2022); @@ -135,15 +135,15 @@ test('should isolate store state between projects', async ({ runInlineTest }) => }; `, 'store.setup.ts': ` - const { setup, expect, store } = pwt; - setup('should initialize store', async ({ }) => { + const { test, expect, store } = pwt; + test.projectSetup('should initialize store', async ({ }) => { expect(await store.get('number')).toBe(undefined); await store.set('number', 2022) expect(await store.get('number')).toBe(2022); expect(await store.get('name')).toBe(undefined); - await store.set('name', 'str-' + setup.info().project.name) - expect(await store.get('name')).toBe('str-' + setup.info().project.name); + await store.set('name', 'str-' + test.info().project.name) + expect(await store.get('name')).toBe('str-' + test.info().project.name); }); `, 'a.test.ts': ` @@ -182,8 +182,8 @@ test('should load context storageState from store', async ({ runInlineTest, serv }; `, 'store.setup.ts': ` - const { setup, expect, store } = pwt; - setup('should save storageState', async ({ page, context }) => { + const { test, expect, store } = pwt; + test.projectSetup('should save storageState', async ({ page, context }) => { expect(await store.get('user')).toBe(undefined); await page.goto('${server.PREFIX}/setcookie.html'); const state = await page.context().storageState(); @@ -234,11 +234,11 @@ test('should load storageStateName specified in the project config from store', }; `, 'store.setup.ts': ` - const { setup, expect, store } = pwt; - setup.use({ + const { test, expect, store } = pwt; + test.use({ storageStateName: ({}, use) => use(undefined), }) - setup('should save storageState', async ({ page, context }) => { + test.projectSetup('should save storageState', async ({ page, context }) => { expect(await store.get('stateInStorage')).toBe(undefined); await page.goto('${server.PREFIX}/setcookie.html'); const state = await page.context().storageState(); @@ -278,11 +278,11 @@ test('should load storageStateName specified in the global config from store', a }; `, 'store.setup.ts': ` - const { setup, expect, store } = pwt; - setup.use({ + const { test, expect, store } = pwt; + test.use({ storageStateName: ({}, use) => use(undefined), }) - setup('should save storageStateName', async ({ page, context }) => { + test.projectSetup('should save storageStateName', async ({ page, context }) => { expect(await store.get('stateInStorage')).toBe(undefined); await page.goto('${server.PREFIX}/setcookie.html'); const state = await page.context().storageState();