diff --git a/docs/src/test-api/class-test.md b/docs/src/test-api/class-test.md index fe277d4347..bcf62e3add 100644 --- a/docs/src/test-api/class-test.md +++ b/docs/src/test-api/class-test.md @@ -1083,51 +1083,6 @@ Test function that takes one or two arguments: an object with fixtures and optio -## method: Test.reset -* since: v1.28 - -Resets options that were set up in the configuration file or with [`method: Test.use`] to their default or config-specified value. - -```js tab=js-js -const { test, expect } = require('@playwright/test'); - -test.reset({ - // Reset storage state to the default empty value. - storageStage: 'default', - - // Reset locale to the value specified in the config file. - locale: 'config', -}); - -test('example', async ({ page }) => { - // ... -}); -``` - -```js tab=js-ts -import { test, expect } from '@playwright/test'; - -test.reset({ - // Reset storage state to the default empty value. - storageStage: 'default', - - // Reset locale to the value specified in the config file. - locale: 'config', -}); - -test('example', async ({ page }) => { - // ... -}); -``` - -### param: Test.reset.fixtures -* since: v1.28 -- `options` <[Object]> - -An object with options set to either `'config'` or `'default'`. - - - ## method: Test.setTimeout * since: v1.10 diff --git a/packages/playwright-test/src/fixtures.ts b/packages/playwright-test/src/fixtures.ts index 5d486f6032..2aa919ed5a 100644 --- a/packages/playwright-test/src/fixtures.ts +++ b/packages/playwright-test/src/fixtures.ts @@ -48,13 +48,8 @@ type FixtureRegistration = { id: string; // A fixture override can use the previous version of the fixture. super?: FixtureRegistration; - // Whether this fixture is an option value set from the config. - fromConfig?: boolean; }; -export const kResetToConfig = Symbol('reset-to-config'); -export const kResetToDefault = Symbol('reset-to-default'); - class Fixture { runner: FixtureRunner; registration: FixtureRegistration; @@ -193,7 +188,7 @@ export class FixturePool { constructor(fixturesList: FixturesWithLocation[], parentPool?: FixturePool, disallowWorkerFixtures?: boolean) { this.registrations = new Map(parentPool ? parentPool.registrations : []); - for (const { fixtures, location, fromConfig } of fixturesList) { + for (const { fixtures, location } of fixturesList) { for (const entry of Object.entries(fixtures)) { const name = entry[0]; let value = entry[1]; @@ -227,30 +222,17 @@ export class FixturePool { if (options.scope === 'worker' && disallowWorkerFixtures) throw errorWithLocations(`Cannot use({ ${name} }) in a describe group, because it forces a new worker.\nMake it top-level in the test file or put in the configuration file.`, { location, name }); - if (fn === undefined && options.option) { - // Overriding option with "undefined" value means setting it to the config value. - fn = kResetToConfig; - } - if (fn === kResetToConfig || fn === kResetToDefault) { - // Find the target fixture to copy the reset value from. - // It is either the original definition, or "fromConfig" one. - // - // Note that "reset to config" behaves like "reset to default" - // if no value is set in the config. - let targetFixture = previous; - while (targetFixture && targetFixture.super) { - if (fn === kResetToConfig && targetFixture.fromConfig) - break; - targetFixture = targetFixture.super; - } - if (targetFixture) - fn = targetFixture.fn; - else - fn = undefined; + // Overriding option with "undefined" value means setting it to the default value + // from the original declaration of the option. + if (fn === undefined && options.option && previous) { + let original = previous; + while (original.super) + original = original.super; + fn = original.fn; } const deps = fixtureParameterNames(fn, location); - const registration: FixtureRegistration = { id: '', name, location, scope: options.scope, fn, auto: options.auto, option: options.option, timeout: options.timeout, customTitle: options.customTitle, deps, super: previous, fromConfig }; + const registration: FixtureRegistration = { id: '', name, location, scope: options.scope, fn, auto: options.auto, option: options.option, timeout: options.timeout, customTitle: options.customTitle, deps, super: previous }; registrationId(registration); this.registrations.set(name, registration); } diff --git a/packages/playwright-test/src/loader.ts b/packages/playwright-test/src/loader.ts index aa0137d195..e81a936367 100644 --- a/packages/playwright-test/src/loader.ts +++ b/packages/playwright-test/src/loader.ts @@ -434,17 +434,18 @@ class ProjectSuiteBuilder { return testType.fixtures; const result: FixturesWithLocation[] = []; for (const f of testType.fixtures) { - result.push(f); const optionsFromConfig: Fixtures = {}; + const originalFixtures: Fixtures = {}; for (const [key, value] of Object.entries(f.fixtures)) { if (isFixtureOption(value) && configKeys.has(key)) (optionsFromConfig as any)[key] = [(configUse as any)[key], value[1]]; + else + (originalFixtures as any)[key] = value; } - if (Object.entries(optionsFromConfig).length) { - // Add config options immediately after original option definition, - // so that any test.use() override it. - result.push({ fixtures: optionsFromConfig, location: { file: `project#${this._project._id}`, line: 1, column: 1 }, fromConfig: true }); - } + if (Object.entries(optionsFromConfig).length) + result.push({ fixtures: optionsFromConfig, location: { file: `project#${this._project._id}`, line: 1, column: 1 } }); + if (Object.entries(originalFixtures).length) + result.push({ fixtures: originalFixtures, location: f.location }); } return result; } diff --git a/packages/playwright-test/src/testType.ts b/packages/playwright-test/src/testType.ts index f8db73b7c0..2dc003f378 100644 --- a/packages/playwright-test/src/testType.ts +++ b/packages/playwright-test/src/testType.ts @@ -15,7 +15,6 @@ */ import { expect } from './expect'; -import { kResetToConfig, kResetToDefault } from './fixtures'; import { currentlyLoadingFileSuite, currentTestInfo, setCurrentlyLoadingFileSuite } from './globals'; import { TestCase, Suite } from './test'; import { wrapFunctionWithLocation } from './transform'; @@ -55,7 +54,6 @@ export class TestTypeImpl { test.setTimeout = wrapFunctionWithLocation(this._setTimeout.bind(this)); test.step = wrapFunctionWithLocation(this._step.bind(this)); test.use = wrapFunctionWithLocation(this._use.bind(this)); - test.reset = wrapFunctionWithLocation(this._reset.bind(this)); test.extend = wrapFunctionWithLocation(this._extend.bind(this)); test._extendTest = wrapFunctionWithLocation(this._extendTest.bind(this)); test.info = () => { @@ -208,20 +206,6 @@ export class TestTypeImpl { suite._use.push({ fixtures, location }); } - private _reset(location: Location, resets: Fixtures) { - const suite = this._ensureCurrentSuite(location, `test.reset()`); - const fixtures: any = {}; - for (const [key, value] of Object.entries(resets)) { - if (value === 'config') - fixtures[key] = kResetToConfig; - else if (value === 'default') - fixtures[key] = kResetToDefault; - else - throw errorWithLocation(location, `test.reset() supports "config" or "default", got unexpected value "${value}"`); - } - suite._use.push({ fixtures, location }); - } - private async _step(location: Location, title: string, body: () => Promise): Promise { const testInfo = currentTestInfo(); if (!testInfo) diff --git a/packages/playwright-test/src/types.ts b/packages/playwright-test/src/types.ts index d405a43cf1..69a390f77d 100644 --- a/packages/playwright-test/src/types.ts +++ b/packages/playwright-test/src/types.ts @@ -24,7 +24,6 @@ export type { Location } from '../types/testReporter'; export type FixturesWithLocation = { fixtures: Fixtures; location: Location; - fromConfig?: boolean; }; export type Annotation = { type: string, description?: string }; diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index b8f6c5f6c0..cfcd50fe38 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -2612,29 +2612,6 @@ export interface TestType): void; - /** - * Resets options that were set up in the configuration file or with - * [test.use(options)](https://playwright.dev/docs/api/class-test#test-use) to their default or config-specified value. - * - * ```js - * import { test, expect } from '@playwright/test'; - * - * test.reset({ - * // Reset storage state to the default empty value. - * storageStage: 'default', - * - * // Reset locale to the value specified in the config file. - * locale: 'config', - * }); - * - * test('example', async ({ page }) => { - * // ... - * }); - * ``` - * - * @param options An object with options set to either `'config'` or `'default'`. - */ - reset(options: ResetOptions): void; /** * Declares a test step. * @@ -2759,7 +2736,6 @@ export type Fixtures | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined }]; }; -type ResetOptions = { [K in keyof T]?: 'config' | 'default' }; type BrowserName = 'chromium' | 'firefox' | 'webkit'; type BrowserChannel = Exclude; diff --git a/tests/playwright-test/storage.spec.ts b/tests/playwright-test/storage.spec.ts index 91b07dd895..28f2f5b51a 100644 --- a/tests/playwright-test/storage.spec.ts +++ b/tests/playwright-test/storage.spec.ts @@ -246,8 +246,8 @@ test('should load storageStateName specified in the project config from storage' `, 'storage.setup.ts': ` const { test, expect } = pwt; - test.reset({ - storageStateName: 'default' + test.use({ + storageStateName: ({}, use) => use(undefined), }) test('should save storageState', async ({ page, context }) => { const storage = test.info().storage(); @@ -291,8 +291,8 @@ test('should load storageStateName specified in the global config from storage', `, 'storage.setup.ts': ` const { test, expect } = pwt; - test.reset({ - storageStateName: 'default' + test.use({ + storageStateName: ({}, use) => use(undefined), }) test('should save storageStateName', async ({ page, context }) => { const storage = test.info().storage(); diff --git a/tests/playwright-test/test-extend.spec.ts b/tests/playwright-test/test-extend.spec.ts index 85704980f2..8fd3b0af5a 100644 --- a/tests/playwright-test/test-extend.spec.ts +++ b/tests/playwright-test/test-extend.spec.ts @@ -225,3 +225,54 @@ test('test._extendTest should print nice message when used as extend', async ({ expect(result.passed).toBe(0); expect(result.output).toContain('Did you mean to call test.extend() with fixtures instead?'); }); + +test('test.use() with undefined should not be ignored', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { + use: { option1: 'config' }, + }; + `, + 'a.test.js': ` + const test = pwt.test.extend({ + option1: [ 'default', { option: true } ], + option2: [ 'default', { option: true } ], + }); + test('test1', async ({ option1, option2 }) => { + console.log('test1: option1=' + option1); + console.log('test1: option2=' + option2); + }); + + test.describe('', () => { + test.use({ option1: 'foo', option2: 'foo' }); + test('test2', async ({ option1, option2 }) => { + console.log('test2: option1=' + option1); + console.log('test2: option2=' + option2); + }); + + test.describe('', () => { + test.use({ option1: undefined, option2: undefined }); + test('test3', async ({ option1, option2 }) => { + console.log('test3: option1=' + option1); + console.log('test3: option2=' + option2); + }); + }); + }); + + test.extend({ option1: undefined, option2: undefined })('test4', async ({ option1, option2 }) => { + console.log('test4: option1=' + option1); + console.log('test4: option2=' + option2); + }); + `, + }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(4); + expect(result.output).toContain('test1: option1=config'); + expect(result.output).toContain('test1: option2=default'); + expect(result.output).toContain('test2: option1=foo'); + expect(result.output).toContain('test2: option2=foo'); + expect(result.output).toContain('test3: option1=config'); + expect(result.output).toContain('test3: option2=default'); + expect(result.output).toContain('test4: option1=config'); + expect(result.output).toContain('test4: option2=default'); +}); diff --git a/tests/playwright-test/test-use.spec.ts b/tests/playwright-test/test-use.spec.ts index 1698449ec7..9a058c3b7b 100644 --- a/tests/playwright-test/test-use.spec.ts +++ b/tests/playwright-test/test-use.spec.ts @@ -179,109 +179,3 @@ test('test.use() should throw if called from beforeAll ', async ({ runInlineTest expect(result.output).toContain('Playwright Test did not expect test.use() to be called here'); }); -test('test.use() with undefined should not be ignored', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'playwright.config.ts': ` - module.exports = { - use: { option1: 'config' }, - }; - `, - 'a.test.js': ` - const test = pwt.test.extend({ - option1: [ 'default', { option: true } ], - option2: [ 'default', { option: true } ], - }); - test('test1', async ({ option1, option2 }) => { - console.log('test1: option1=' + option1); - console.log('test1: option2=' + option2); - }); - - test.describe('', () => { - test.use({ option1: 'foo', option2: 'foo' }); - test('test2', async ({ option1, option2 }) => { - console.log('test2: option1=' + option1); - console.log('test2: option2=' + option2); - }); - - test.describe('', () => { - test.use({ option1: undefined, option2: undefined }); - test('test3', async ({ option1, option2 }) => { - console.log('test3: option1=' + option1); - console.log('test3: option2=' + option2); - }); - }); - }); - - test.extend({ option1: undefined, option2: undefined })('test4', async ({ option1, option2 }) => { - console.log('test4: option1=' + option1); - console.log('test4: option2=' + option2); - }); - `, - }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(4); - expect(result.output).toContain('test1: option1=config'); - expect(result.output).toContain('test1: option2=default'); - expect(result.output).toContain('test2: option1=foo'); - expect(result.output).toContain('test2: option2=foo'); - expect(result.output).toContain('test3: option1=config'); - expect(result.output).toContain('test3: option2=default'); - expect(result.output).toContain('test4: option1=config'); - expect(result.output).toContain('test4: option2=default'); -}); - -test('test.reset() should reset to default or config', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'playwright.config.ts': ` - module.exports = { - use: { option1: 'config-value' }, - }; - `, - 'a.test.js': ` - const test = pwt.test.extend({ - option1: [ 'default-value', { option: true } ], - option2: [ 'default-value', { option: true } ], - }); - - test.use({ option1: 'use-value', option2: 'use-value' }); - test('test1', async ({ option1, option2 }) => { - console.log('test1: option1=' + option1); - console.log('test1: option2=' + option2); - }); - - test.describe(() => { - test.reset({ option1: 'default', option2: 'default' }); - test('test2', async ({ option1, option2 }) => { - console.log('test2: option1=' + option1); - console.log('test2: option2=' + option2); - }); - }); - - test.describe(() => { - test.reset({ option1: 'config', option2: 'config' }); - test('test3', async ({ option1, option2 }) => { - console.log('test3: option1=' + option1); - console.log('test3: option2=' + option2); - }); - }); - `, - }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(3); - expect(result.output).toContain('test1: option1=use-value'); - expect(result.output).toContain('test1: option2=use-value'); - expect(result.output).toContain('test2: option1=default-value'); - expect(result.output).toContain('test2: option2=default-value'); - expect(result.output).toContain('test3: option1=config-value'); - expect(result.output).toContain('test3: option2=default-value'); -}); - -test('test.reset() throws for unsupported value', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.js': ` - pwt.test.reset({ option: 'foo' }); - `, - }); - expect(result.exitCode).toBe(1); - expect(result.output).toContain('test.reset() supports "config" or "default", got unexpected value "foo"'); -}); diff --git a/tests/playwright-test/types.spec.ts b/tests/playwright-test/types.spec.ts index b0972f4522..1ef96945f9 100644 --- a/tests/playwright-test/types.spec.ts +++ b/tests/playwright-test/types.spec.ts @@ -136,13 +136,6 @@ test('should check types of fixtures', async ({ runTSC }) => { // @ts-expect-error test.use({ baz: 'baz' }); - test.reset({ foo: 'default' }); - test.reset({ foo: 'config' }); - // @ts-expect-error - test.reset({ unknown: 'config' }); - // @ts-expect-error - test.reset({ foo: 'unknown' }); - test('my test', async ({ foo, bar }) => { bar += parseInt(foo); }); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 68de70bc65..ad666c606a 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -151,7 +151,6 @@ export interface TestType Promise | any): void; afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | any): void; use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void; - reset(options: ResetOptions): void; step(title: string, body: () => Promise): Promise; expect: Expect; extend(fixtures: Fixtures): TestType; @@ -172,7 +171,6 @@ export type Fixtures | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined }]; }; -type ResetOptions = { [K in keyof T]?: 'config' | 'default' }; type BrowserName = 'chromium' | 'firefox' | 'webkit'; type BrowserChannel = Exclude;