fix(test-runner): rely on test title paths instead of ordinal (#12414)
Fixes #11904
This commit is contained in:
parent
cb41e668fe
commit
d744a87aee
|
|
@ -18,6 +18,7 @@ import type { FullProject, Fixtures, FixturesWithLocation } from './types';
|
||||||
import { Suite, TestCase } from './test';
|
import { Suite, TestCase } from './test';
|
||||||
import { FixturePool, isFixtureOption } from './fixtures';
|
import { FixturePool, isFixtureOption } from './fixtures';
|
||||||
import { TestTypeImpl } from './testType';
|
import { TestTypeImpl } from './testType';
|
||||||
|
import { calculateSha1 } from 'playwright-core/lib/utils/utils';
|
||||||
|
|
||||||
export class ProjectImpl {
|
export class ProjectImpl {
|
||||||
config: FullProject;
|
config: FullProject;
|
||||||
|
|
@ -64,19 +65,21 @@ export class ProjectImpl {
|
||||||
return this.testPools.get(test)!;
|
return this.testPools.get(test)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _cloneEntries(from: Suite, to: Suite, repeatEachIndex: number, filter: (test: TestCase) => boolean): boolean {
|
private _cloneEntries(from: Suite, to: Suite, repeatEachIndex: number, filter: (test: TestCase) => boolean, relativeTitlePath: string): boolean {
|
||||||
for (const entry of from._entries) {
|
for (const entry of from._entries) {
|
||||||
if (entry instanceof Suite) {
|
if (entry instanceof Suite) {
|
||||||
const suite = entry._clone();
|
const suite = entry._clone();
|
||||||
to._addSuite(suite);
|
to._addSuite(suite);
|
||||||
if (!this._cloneEntries(entry, suite, repeatEachIndex, filter)) {
|
if (!this._cloneEntries(entry, suite, repeatEachIndex, filter, relativeTitlePath + ' ' + suite.title)) {
|
||||||
to._entries.pop();
|
to._entries.pop();
|
||||||
to.suites.pop();
|
to.suites.pop();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const test = entry._clone();
|
const test = entry._clone();
|
||||||
test.retries = this.config.retries;
|
test.retries = this.config.retries;
|
||||||
test._id = `${entry._ordinalInFile}@${entry._requireFile}#run${this.index}-repeat${repeatEachIndex}`;
|
// We rely upon relative paths being unique.
|
||||||
|
// See `getClashingTestsPerSuite()` in `runner.ts`.
|
||||||
|
test._id = `${calculateSha1(relativeTitlePath + ' ' + entry.title)}@${entry._requireFile}#run${this.index}-repeat${repeatEachIndex}`;
|
||||||
test.repeatEachIndex = repeatEachIndex;
|
test.repeatEachIndex = repeatEachIndex;
|
||||||
test._projectIndex = this.index;
|
test._projectIndex = this.index;
|
||||||
to._addTest(test);
|
to._addTest(test);
|
||||||
|
|
@ -97,7 +100,7 @@ export class ProjectImpl {
|
||||||
|
|
||||||
cloneFileSuite(suite: Suite, repeatEachIndex: number, filter: (test: TestCase) => boolean): Suite | undefined {
|
cloneFileSuite(suite: Suite, repeatEachIndex: number, filter: (test: TestCase) => boolean): Suite | undefined {
|
||||||
const result = suite._clone();
|
const result = suite._clone();
|
||||||
return this._cloneEntries(suite, result, repeatEachIndex, filter) ? result : undefined;
|
return this._cloneEntries(suite, result, repeatEachIndex, filter, '') ? result : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveFixtures(testType: TestTypeImpl, configUse: Fixtures): FixturesWithLocation[] {
|
private resolveFixtures(testType: TestTypeImpl, configUse: Fixtures): FixturesWithLocation[] {
|
||||||
|
|
|
||||||
|
|
@ -128,17 +128,15 @@ export class TestCase extends Base implements reporterTypes.TestCase {
|
||||||
retries = 0;
|
retries = 0;
|
||||||
repeatEachIndex = 0;
|
repeatEachIndex = 0;
|
||||||
|
|
||||||
_ordinalInFile: number;
|
|
||||||
_testType: TestTypeImpl;
|
_testType: TestTypeImpl;
|
||||||
_id = '';
|
_id = '';
|
||||||
_workerHash = '';
|
_workerHash = '';
|
||||||
_pool: FixturePool | undefined;
|
_pool: FixturePool | undefined;
|
||||||
_projectIndex = 0;
|
_projectIndex = 0;
|
||||||
|
|
||||||
constructor(title: string, fn: Function, ordinalInFile: number, testType: TestTypeImpl, location: Location) {
|
constructor(title: string, fn: Function, testType: TestTypeImpl, location: Location) {
|
||||||
super(title);
|
super(title);
|
||||||
this.fn = fn;
|
this.fn = fn;
|
||||||
this._ordinalInFile = ordinalInFile;
|
|
||||||
this._testType = testType;
|
this._testType = testType;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +164,7 @@ export class TestCase extends Base implements reporterTypes.TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
_clone(): TestCase {
|
_clone(): TestCase {
|
||||||
const test = new TestCase(this.title, this.fn, this._ordinalInFile, this._testType, this.location);
|
const test = new TestCase(this.title, this.fn, this._testType, this.location);
|
||||||
test._only = this._only;
|
test._only = this._only;
|
||||||
test._requireFile = this._requireFile;
|
test._requireFile = this._requireFile;
|
||||||
test.expectedStatus = this.expectedStatus;
|
test.expectedStatus = this.expectedStatus;
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export class TestTypeImpl {
|
||||||
private _createTest(type: 'default' | 'only' | 'skip' | 'fixme', location: Location, title: string, fn: Function) {
|
private _createTest(type: 'default' | 'only' | 'skip' | 'fixme', location: Location, title: string, fn: Function) {
|
||||||
throwIfRunningInsideJest();
|
throwIfRunningInsideJest();
|
||||||
const suite = this._ensureCurrentSuite(location, 'test()');
|
const suite = this._ensureCurrentSuite(location, 'test()');
|
||||||
const test = new TestCase(title, fn, nextOrdinalInFile(suite._requireFile), this, location);
|
const test = new TestCase(title, fn, this, location);
|
||||||
test._requireFile = suite._requireFile;
|
test._requireFile = suite._requireFile;
|
||||||
suite._addTest(test);
|
suite._addTest(test);
|
||||||
|
|
||||||
|
|
@ -243,11 +243,4 @@ function throwIfRunningInsideJest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const countByFile = new Map<string, number>();
|
|
||||||
function nextOrdinalInFile(file: string) {
|
|
||||||
const ordinalInFile = countByFile.get(file) || 0;
|
|
||||||
countByFile.set(file, ordinalInFile + 1);
|
|
||||||
return ordinalInFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const rootTestType = new TestTypeImpl([]);
|
export const rootTestType = new TestTypeImpl([]);
|
||||||
|
|
|
||||||
|
|
@ -407,3 +407,52 @@ test('should filter stack even without default Error.prepareStackTrace', async (
|
||||||
expect(stackLines.length).toBe(1);
|
expect(stackLines.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should work with cross-imports - 1', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'test1.spec.ts': `
|
||||||
|
const { test } = pwt;
|
||||||
|
test('test 1', async ({}) => {
|
||||||
|
await new Promise(x => setTimeout(x, 500));
|
||||||
|
console.log('running TEST-1');
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'test2.spec.ts': `
|
||||||
|
import * as _ from './test1.spec';
|
||||||
|
const { test } = pwt;
|
||||||
|
test('test 2', async ({}) => {
|
||||||
|
await new Promise(x => setTimeout(x, 500));
|
||||||
|
console.log('running TEST-2');
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { workers: 2 });
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.passed).toBe(2);
|
||||||
|
expect(result.failed).toBe(0);
|
||||||
|
expect(result.output).toContain('TEST-1');
|
||||||
|
expect(result.output).toContain('TEST-2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should work with cross-imports - 2', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'test1.spec.ts': `
|
||||||
|
const { test } = pwt;
|
||||||
|
import * as _ from './test2.spec';
|
||||||
|
test('test 1', async ({}) => {
|
||||||
|
await new Promise(x => setTimeout(x, 500));
|
||||||
|
console.log('running TEST-1');
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'test2.spec.ts': `
|
||||||
|
const { test } = pwt;
|
||||||
|
test('test 2', async ({}) => {
|
||||||
|
await new Promise(x => setTimeout(x, 500));
|
||||||
|
console.log('running TEST-2');
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { workers: 2, reporter: 'list' });
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.passed).toBe(2);
|
||||||
|
expect(result.failed).toBe(0);
|
||||||
|
expect(result.output).toContain('TEST-1');
|
||||||
|
expect(result.output).toContain('TEST-2');
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue