fix(test runner): fix duplicate titles error when multiple issues are present (#14090)
This commit is contained in:
parent
c99ca17199
commit
305afcdacf
|
|
@ -44,6 +44,7 @@ import { SigIntWatcher } from './sigIntWatcher';
|
||||||
import type { TestRunnerPlugin } from './plugins';
|
import type { TestRunnerPlugin } from './plugins';
|
||||||
import { setRunnerToAddPluginsTo } from './plugins';
|
import { setRunnerToAddPluginsTo } from './plugins';
|
||||||
import { webServerPluginForConfig } from './plugins/webServerPlugin';
|
import { webServerPluginForConfig } from './plugins/webServerPlugin';
|
||||||
|
import { MultiMap } from 'playwright-core/lib/utils/multimap';
|
||||||
|
|
||||||
const removeFolderAsync = promisify(rimraf);
|
const removeFolderAsync = promisify(rimraf);
|
||||||
const readDirAsync = promisify(fs.readdir);
|
const readDirAsync = promisify(fs.readdir);
|
||||||
|
|
@ -287,9 +288,9 @@ export class Runner {
|
||||||
filterOnly(preprocessRoot);
|
filterOnly(preprocessRoot);
|
||||||
|
|
||||||
// 5. Complain about clashing.
|
// 5. Complain about clashing.
|
||||||
const clashingTests = getClashingTestsPerSuite(preprocessRoot);
|
const duplicateTitlesError = createDuplicateTitlesError(config, preprocessRoot);
|
||||||
if (clashingTests.size > 0)
|
if (duplicateTitlesError)
|
||||||
fatalErrors.push(createDuplicateTitlesError(config, clashingTests));
|
fatalErrors.push(duplicateTitlesError);
|
||||||
|
|
||||||
// 6. Generate projects.
|
// 6. Generate projects.
|
||||||
const fileSuites = new Map<string, Suite>();
|
const fileSuites = new Map<string, Suite>();
|
||||||
|
|
@ -607,28 +608,6 @@ async function collectFiles(testDir: string): Promise<string[]> {
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClashingTestsPerSuite(rootSuite: Suite): Map<string, TestCase[]> {
|
|
||||||
function visit(suite: Suite, clashingTests: Map<string, TestCase[]>) {
|
|
||||||
for (const childSuite of suite.suites)
|
|
||||||
visit(childSuite, clashingTests);
|
|
||||||
for (const test of suite.tests) {
|
|
||||||
const fullTitle = test.titlePath().slice(2).join(' ');
|
|
||||||
if (!clashingTests.has(fullTitle))
|
|
||||||
clashingTests.set(fullTitle, []);
|
|
||||||
clashingTests.set(fullTitle, clashingTests.get(fullTitle)!.concat(test));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const out = new Map<string, TestCase[]>();
|
|
||||||
for (const fileSuite of rootSuite.suites) {
|
|
||||||
const clashingTests = new Map<string, TestCase[]>();
|
|
||||||
visit(fileSuite, clashingTests);
|
|
||||||
for (const [title, tests] of clashingTests.entries()) {
|
|
||||||
if (tests.length > 1)
|
|
||||||
out.set(title, tests);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildItemLocation(rootDir: string, testOrSuite: Suite | TestCase) {
|
function buildItemLocation(rootDir: string, testOrSuite: Suite | TestCase) {
|
||||||
if (!testOrSuite.location)
|
if (!testOrSuite.location)
|
||||||
|
|
@ -768,18 +747,31 @@ function createForbidOnlyError(config: FullConfigInternal, onlyTestsAndSuites: (
|
||||||
return createStacklessError(errorMessage.join('\n'));
|
return createStacklessError(errorMessage.join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDuplicateTitlesError(config: FullConfigInternal, clashingTests: Map<string, TestCase[]>): TestError {
|
function createDuplicateTitlesError(config: FullConfigInternal, rootSuite: Suite): TestError | undefined {
|
||||||
const errorMessage = [
|
const lines: string[] = [];
|
||||||
|
for (const fileSuite of rootSuite.suites) {
|
||||||
|
const testsByFullTitle = new MultiMap<string, TestCase>();
|
||||||
|
for (const test of fileSuite.allTests()) {
|
||||||
|
const fullTitle = test.titlePath().slice(2).join(' ');
|
||||||
|
testsByFullTitle.set(fullTitle, test);
|
||||||
|
}
|
||||||
|
for (const fullTitle of testsByFullTitle.keys()) {
|
||||||
|
const tests = testsByFullTitle.get(fullTitle);
|
||||||
|
if (tests.length > 1) {
|
||||||
|
lines.push(` - title: ${fullTitle}`);
|
||||||
|
for (const test of tests)
|
||||||
|
lines.push(` - ${buildItemLocation(config.rootDir, test)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!lines.length)
|
||||||
|
return;
|
||||||
|
return createStacklessError([
|
||||||
'========================================',
|
'========================================',
|
||||||
' duplicate test titles are not allowed.',
|
' duplicate test titles are not allowed.',
|
||||||
];
|
...lines,
|
||||||
for (const [title, tests] of clashingTests.entries()) {
|
'========================================',
|
||||||
errorMessage.push(` - title: ${title}`);
|
].join('\n'));
|
||||||
for (const test of tests)
|
|
||||||
errorMessage.push(` - ${buildItemLocation(config.rootDir, test)}`);
|
|
||||||
}
|
|
||||||
errorMessage.push('========================================');
|
|
||||||
return createStacklessError(errorMessage.join('\n'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNoTestsError(): TestError {
|
function createNoTestsError(): TestError {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,29 @@ test('it should enforce unique test names based on the describe block name', asy
|
||||||
expect(result.output).toContain(` - tests${path.sep}example.spec.js:8`);
|
expect(result.output).toContain(` - tests${path.sep}example.spec.js:8`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it should not allow multiple tests with the same name in multiple files', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'tests/example1.spec.js': `
|
||||||
|
const { test } = pwt;
|
||||||
|
test('i-am-a-duplicate', async () => {});
|
||||||
|
test('i-am-a-duplicate', async () => {});
|
||||||
|
`,
|
||||||
|
'tests/example2.spec.js': `
|
||||||
|
const { test } = pwt;
|
||||||
|
test('i-am-a-duplicate', async () => {});
|
||||||
|
test('i-am-a-duplicate', async () => {});
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
expect(result.output).toContain('duplicate test titles are not allowed');
|
||||||
|
expect(result.output).toContain(`- title: i-am-a-duplicate`);
|
||||||
|
expect(result.output).toContain(` - tests${path.sep}example1.spec.js:6`);
|
||||||
|
expect(result.output).toContain(` - tests${path.sep}example1.spec.js:7`);
|
||||||
|
expect(result.output).toContain(`- title: i-am-a-duplicate`);
|
||||||
|
expect(result.output).toContain(` - tests${path.sep}example2.spec.js:6`);
|
||||||
|
expect(result.output).toContain(` - tests${path.sep}example2.spec.js:7`);
|
||||||
|
});
|
||||||
|
|
||||||
test('it should not allow a focused test when forbid-only is used', async ({ runInlineTest }) => {
|
test('it should not allow a focused test when forbid-only is used', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'tests/focused-test.spec.js': `
|
'tests/focused-test.spec.js': `
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue