mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(test runner): make sure undefined options in config result in default values (#19632)
Note: this keeps existing behavior of `undefined` in `test.use()` reverting to the config value and not to the original default value. References #19615.
This commit is contained in:
parent
eaae8ebbf3
commit
b8848fb499
@ -48,6 +48,8 @@ type FixtureRegistration = {
|
|||||||
id: string;
|
id: string;
|
||||||
// A fixture override can use the previous version of the fixture.
|
// A fixture override can use the previous version of the fixture.
|
||||||
super?: FixtureRegistration;
|
super?: FixtureRegistration;
|
||||||
|
// Whether this fixture is an option value set from the config.
|
||||||
|
fromConfig?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Fixture {
|
class Fixture {
|
||||||
@ -188,7 +190,7 @@ export class FixturePool {
|
|||||||
constructor(fixturesList: FixturesWithLocation[], parentPool?: FixturePool, disallowWorkerFixtures?: boolean) {
|
constructor(fixturesList: FixturesWithLocation[], parentPool?: FixturePool, disallowWorkerFixtures?: boolean) {
|
||||||
this.registrations = new Map(parentPool ? parentPool.registrations : []);
|
this.registrations = new Map(parentPool ? parentPool.registrations : []);
|
||||||
|
|
||||||
for (const { fixtures, location } of fixturesList) {
|
for (const { fixtures, location, fromConfig } of fixturesList) {
|
||||||
for (const entry of Object.entries(fixtures)) {
|
for (const entry of Object.entries(fixtures)) {
|
||||||
const name = entry[0];
|
const name = entry[0];
|
||||||
let value = entry[1];
|
let value = entry[1];
|
||||||
@ -223,16 +225,16 @@ export class FixturePool {
|
|||||||
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 });
|
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 });
|
||||||
|
|
||||||
// Overriding option with "undefined" value means setting it to the default value
|
// Overriding option with "undefined" value means setting it to the default value
|
||||||
// from the original declaration of the option.
|
// from the config or from the original declaration of the option.
|
||||||
if (fn === undefined && options.option && previous) {
|
if (fn === undefined && options.option && previous) {
|
||||||
let original = previous;
|
let original = previous;
|
||||||
while (original.super)
|
while (!original.fromConfig && original.super)
|
||||||
original = original.super;
|
original = original.super;
|
||||||
fn = original.fn;
|
fn = original.fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deps = fixtureParameterNames(fn, location);
|
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 };
|
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 };
|
||||||
registrationId(registration);
|
registrationId(registration);
|
||||||
this.registrations.set(name, registration);
|
this.registrations.set(name, registration);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -435,18 +435,17 @@ class ProjectSuiteBuilder {
|
|||||||
return testType.fixtures;
|
return testType.fixtures;
|
||||||
const result: FixturesWithLocation[] = [];
|
const result: FixturesWithLocation[] = [];
|
||||||
for (const f of testType.fixtures) {
|
for (const f of testType.fixtures) {
|
||||||
|
result.push(f);
|
||||||
const optionsFromConfig: Fixtures = {};
|
const optionsFromConfig: Fixtures = {};
|
||||||
const originalFixtures: Fixtures = {};
|
|
||||||
for (const [key, value] of Object.entries(f.fixtures)) {
|
for (const [key, value] of Object.entries(f.fixtures)) {
|
||||||
if (isFixtureOption(value) && configKeys.has(key))
|
if (isFixtureOption(value) && configKeys.has(key))
|
||||||
(optionsFromConfig as any)[key] = [(configUse as any)[key], value[1]];
|
(optionsFromConfig as any)[key] = [(configUse as any)[key], value[1]];
|
||||||
else
|
|
||||||
(originalFixtures as any)[key] = value;
|
|
||||||
}
|
}
|
||||||
if (Object.entries(optionsFromConfig).length)
|
if (Object.entries(optionsFromConfig).length) {
|
||||||
result.push({ fixtures: optionsFromConfig, location: { file: `project#${this._project._id}`, line: 1, column: 1 } });
|
// Add config options immediately after original option definition,
|
||||||
if (Object.entries(originalFixtures).length)
|
// so that any test.use() override it.
|
||||||
result.push({ fixtures: originalFixtures, location: f.location });
|
result.push({ fixtures: optionsFromConfig, location: { file: `project#${this._project._id}`, line: 1, column: 1 }, fromConfig: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export type { Location } from '../types/testReporter';
|
|||||||
export type FixturesWithLocation = {
|
export type FixturesWithLocation = {
|
||||||
fixtures: Fixtures;
|
fixtures: Fixtures;
|
||||||
location: Location;
|
location: Location;
|
||||||
|
fromConfig?: boolean;
|
||||||
};
|
};
|
||||||
export type Annotation = { type: string, description?: string };
|
export type Annotation = { type: string, description?: string };
|
||||||
|
|
||||||
|
|||||||
@ -605,6 +605,7 @@ test('should not throw with many fixtures set to undefined', async ({ runInlineT
|
|||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
module.exports = { use: {
|
module.exports = { use: {
|
||||||
|
browserName: undefined,
|
||||||
headless: undefined,
|
headless: undefined,
|
||||||
channel: undefined,
|
channel: undefined,
|
||||||
launchOptions: undefined,
|
launchOptions: undefined,
|
||||||
@ -642,6 +643,7 @@ test('should not throw with many fixtures set to undefined', async ({ runInlineT
|
|||||||
'a.spec.ts': `
|
'a.spec.ts': `
|
||||||
const { test } = pwt;
|
const { test } = pwt;
|
||||||
test.use({
|
test.use({
|
||||||
|
browserName: undefined,
|
||||||
headless: undefined,
|
headless: undefined,
|
||||||
channel: undefined,
|
channel: undefined,
|
||||||
launchOptions: undefined,
|
launchOptions: undefined,
|
||||||
@ -676,6 +678,7 @@ test('should not throw with many fixtures set to undefined', async ({ runInlineT
|
|||||||
contextOptions: undefined,
|
contextOptions: undefined,
|
||||||
});
|
});
|
||||||
test('passes', async ({ page }) => {
|
test('passes', async ({ page }) => {
|
||||||
|
await page.setContent('text');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { workers: 1 });
|
}, { workers: 1 });
|
||||||
|
|||||||
@ -276,3 +276,31 @@ test('test.use() with undefined should not be ignored', async ({ runInlineTest }
|
|||||||
expect(result.output).toContain('test4: option1=config');
|
expect(result.output).toContain('test4: option1=config');
|
||||||
expect(result.output).toContain('test4: option2=default');
|
expect(result.output).toContain('test4: option2=default');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('undefined values in config and test.use should be reverted to default', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.ts': `
|
||||||
|
module.exports = {
|
||||||
|
use: { option1: undefined, option2: undefined },
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
'a.test.js': `
|
||||||
|
const test = pwt.test.extend({
|
||||||
|
option1: [ 'default1', { option: true } ],
|
||||||
|
option2: [ 'default2', { option: true } ],
|
||||||
|
option3: [ 'default3', { option: true } ],
|
||||||
|
});
|
||||||
|
test.use({ option2: undefined, option3: undefined });
|
||||||
|
test('my test', async ({ option1, option2, option3 }) => {
|
||||||
|
console.log('option1=' + option1);
|
||||||
|
console.log('option2=' + option2);
|
||||||
|
console.log('option3=' + option3);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.output).toContain('option1=default1');
|
||||||
|
expect(result.output).toContain('option2=default2');
|
||||||
|
expect(result.output).toContain('option3=default3');
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user