feat(test-runner): pass config via this to test group filters

This commit is contained in:
Mathias Leppich 2024-10-11 16:26:38 +02:00
parent d1e7b19c93
commit a605c65e46
4 changed files with 91 additions and 9 deletions

View file

@ -218,11 +218,12 @@ async function filterTestGroups(config: FullConfigInternal, testGroups: TestGrou
let filteredTestGroups = testGroups.map(group => ({ tests: group.tests.map(test => test as reporterTypes.TestCase) })); let filteredTestGroups = testGroups.map(group => ({ tests: group.tests.map(test => test as reporterTypes.TestCase) }));
const allTests = new Set(filteredTestGroups.flatMap(group => group.tests)); const allTests = new Set(filteredTestGroups.flatMap(group => group.tests));
for (const filter of filters) { for (const filter of filters) {
const filterThis = { config: config.config };
if ('filterTestGroups' in filter) { if ('filterTestGroups' in filter) {
const result = filter.filterTestGroups(filteredTestGroups, config.config); const result = filter.filterTestGroups.call(filterThis, filteredTestGroups);
filteredTestGroups = result instanceof Promise ? await result : result; filteredTestGroups = result instanceof Promise ? await result : result;
} else if ('filterTests' in filter) { } else if ('filterTests' in filter) {
const result = filter.filterTests(filteredTestGroups.flatMap(group => group.tests), config.config); const result = filter.filterTests.call(filterThis, filteredTestGroups.flatMap(group => group.tests), config.config);
const filteredTests = result instanceof Promise ? await result : result; const filteredTests = result instanceof Promise ? await result : result;
if (!Array.isArray(filteredTests)) if (!Array.isArray(filteredTests))
throw new Error('Invalid filter result: tests should be an array'); throw new Error('Invalid filter result: tests should be an array');
@ -236,7 +237,7 @@ async function filterTestGroups(config: FullConfigInternal, testGroups: TestGrou
filteredTestGroups = filteredTestGroups.map(group => { filteredTestGroups = filteredTestGroups.map(group => {
return { return {
tests: group.tests.filter(test => { tests: group.tests.filter(test => {
const result = filter(test); const result = filter.call(filterThis, test);
if (typeof result !== 'boolean') if (typeof result !== 'boolean')
throw new Error('Invalid filter result: filter function should return a boolean'); throw new Error('Invalid filter result: filter function should return a boolean');
return result; return result;

View file

@ -1885,9 +1885,10 @@ export type TestDetails = {
annotation?: TestDetailsAnnotation | TestDetailsAnnotation[]; annotation?: TestDetailsAnnotation | TestDetailsAnnotation[];
} }
type TestFilterFunction = (test: TestCase) => boolean; type TestFilterThis = { config: FullConfig };
type TestsFilter = { filterTests(tests: TestCase[], config: FullConfig): Promise<TestCase[]> | TestCase[] }; type TestFilterFunction = (this: TestFilterThis, test: TestCase) => boolean;
type TestGroupsFilter = { filterTestGroups(testGroups: { tests: TestCase[] }[], config: FullConfig): Promise<{ tests: TestCase[] }[]> | { tests: TestCase[] }[] }; type TestsFilter = { filterTests(this: TestFilterThis, tests: TestCase[]): Promise<TestCase[]> | TestCase[] };
type TestGroupsFilter = { filterTestGroups(this: TestFilterThis, testGroups: { tests: TestCase[] }[]): Promise<{ tests: TestCase[] }[]> | { tests: TestCase[] }[] };
export type TestFilter = TestFilterFunction | TestsFilter | TestGroupsFilter; export type TestFilter = TestFilterFunction | TestsFilter | TestGroupsFilter;
interface SuiteFunction { interface SuiteFunction {

View file

@ -138,6 +138,85 @@ test('config.filter async filterTestGroups should work', async ({ runInlineTest
]); ]);
}); });
test('config.filter function should have access to config', async ({ runInlineTest }) => {
const result = await runInlineTest({
...testFiles,
'playwright.config.ts': `
module.exports = {
filter(test) {
console.log('\\n%% .config.workers: '+this.config.workers);
return test.title === 'a2-test2'
},
};
`,
}, { workers: 2 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
result.outputLines.sort();
expect(result.outputLines).toEqual([
// filter function is called once per test...
'.config.workers: 2',
'.config.workers: 2',
'.config.workers: 2',
'.config.workers: 2',
'.config.workers: 2',
'.config.workers: 2',
'a2-test2',
]);
});
test('config.filter filterTests should have access to config', async ({ runInlineTest }) => {
const result = await runInlineTest({
...testFiles,
'playwright.config.ts': `
module.exports = {
filter: {
filterTests(tests) {
console.log('\\n%% .config.workers: '+this.config.workers);
return tests.filter((test, index) => index % 2 === 0)
},
},
};
`,
}, { workers: 2 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(3);
result.outputLines.sort();
expect(result.outputLines).toEqual([
// filterTests is only called once...
'.config.workers: 2',
'a1-test1',
'a2-test1',
'a3-test1',
]);
});
test('config.filter filterTestGroups should have access to config', async ({ runInlineTest }) => {
const result = await runInlineTest({
...testFiles,
'playwright.config.ts': `
module.exports = {
filter: {
filterTestGroups(testgroups) {
console.log('\\n%% .config.workers: '+this.config.workers);
return testgroups.filter((testgroup, index) => index % 2 === 1);
},
},
};
`,
}, { workers: 2 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(3);
result.outputLines.sort(); // Due to parallel execution, the order of output lines is not deterministic.
expect(result.outputLines).toEqual([
// filterTestGroups is only called once...
'.config.workers: 2',
'a2-test1',
'a2-test2',
'a4-test1',
]);
});
test('config.filter invalid function should throw', async ({ runInlineTest }) => { test('config.filter invalid function should throw', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({
...testFiles, ...testFiles,

View file

@ -77,9 +77,10 @@ export type TestDetails = {
annotation?: TestDetailsAnnotation | TestDetailsAnnotation[]; annotation?: TestDetailsAnnotation | TestDetailsAnnotation[];
} }
type TestFilterFunction = (test: TestCase) => boolean; type TestFilterThis = { config: FullConfig };
type TestsFilter = { filterTests(tests: TestCase[], config: FullConfig): Promise<TestCase[]> | TestCase[] }; type TestFilterFunction = (this: TestFilterThis, test: TestCase) => boolean;
type TestGroupsFilter = { filterTestGroups(testGroups: { tests: TestCase[] }[], config: FullConfig): Promise<{ tests: TestCase[] }[]> | { tests: TestCase[] }[] }; type TestsFilter = { filterTests(this: TestFilterThis, tests: TestCase[]): Promise<TestCase[]> | TestCase[] };
type TestGroupsFilter = { filterTestGroups(this: TestFilterThis, testGroups: { tests: TestCase[] }[]): Promise<{ tests: TestCase[] }[]> | { tests: TestCase[] }[] };
export type TestFilter = TestFilterFunction | TestsFilter | TestGroupsFilter; export type TestFilter = TestFilterFunction | TestsFilter | TestGroupsFilter;
interface SuiteFunction { interface SuiteFunction {