diff --git a/packages/playwright-test/src/dispatcher.ts b/packages/playwright-test/src/dispatcher.ts index 2730d04bee..734ed1aa52 100644 --- a/packages/playwright-test/src/dispatcher.ts +++ b/packages/playwright-test/src/dispatcher.ts @@ -219,7 +219,7 @@ export class Dispatcher { })); result.status = params.status; test.expectedStatus = params.expectedStatus; - test._annotateWithInheritence(params.annotations); + test.annotations = params.annotations; test.timeout = params.timeout; const isFailure = result.status !== 'skipped' && result.status !== test.expectedStatus; if (isFailure) diff --git a/packages/playwright-test/src/suiteUtils.ts b/packages/playwright-test/src/suiteUtils.ts index de1bbcb22e..8b2ee6f925 100644 --- a/packages/playwright-test/src/suiteUtils.ts +++ b/packages/playwright-test/src/suiteUtils.ts @@ -55,13 +55,13 @@ export function buildFileSuiteForProject(project: FullProjectInternal, suite: Su test.id = testId; test.repeatEachIndex = repeatEachIndex; test._projectId = project._id; - test.retries = project.retries; + let inheritedRetries: number | undefined; for (let parentSuite: Suite | undefined = suite; parentSuite; parentSuite = parentSuite.parent) { - if (parentSuite._retries !== undefined) { - test.retries = parentSuite._retries; - break; - } + test._staticAnnotations.push(...parentSuite._staticAnnotations); + if (inheritedRetries === undefined && parentSuite._retries !== undefined) + inheritedRetries = parentSuite._retries; } + test.retries = inheritedRetries ?? project.retries; // We only compute / set digest in the runner. if (test._poolDigest) test._workerHash = `${project._id}-${test._poolDigest}-${repeatEachIndex}`; diff --git a/packages/playwright-test/src/test.ts b/packages/playwright-test/src/test.ts index 1a5648d20f..c7353885e4 100644 --- a/packages/playwright-test/src/test.ts +++ b/packages/playwright-test/src/test.ts @@ -46,7 +46,7 @@ export class Suite extends Base implements reporterTypes.Suite { _hooks: { type: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', fn: Function, location: Location }[] = []; _timeout: number | undefined; _retries: number | undefined; - _annotations: Annotation[] = []; + _staticAnnotations: Annotation[] = []; _modifiers: Modifier[] = []; _parallelMode: 'default' | 'serial' | 'parallel' = 'default'; _projectConfig: FullProjectInternal | undefined; @@ -161,7 +161,7 @@ export class Suite extends Base implements reporterTypes.Suite { requireFile: this._requireFile, timeout: this._timeout, retries: this._retries, - annotations: this._annotations.slice(), + staticAnnotations: this._staticAnnotations.slice(), modifiers: this._modifiers.slice(), parallelMode: this._parallelMode, skipped: this._skipped, @@ -176,7 +176,7 @@ export class Suite extends Base implements reporterTypes.Suite { suite._requireFile = data.requireFile; suite._timeout = data.timeout; suite._retries = data.retries; - suite._annotations = data.annotations; + suite._staticAnnotations = data.staticAnnotations; suite._modifiers = data.modifiers; suite._parallelMode = data.parallelMode; suite._skipped = data.skipped; @@ -216,9 +216,8 @@ export class TestCase extends Base implements reporterTypes.TestCase { _poolDigest = ''; _workerHash = ''; _projectId = ''; - // Annotations that are not added from within a test (like fixme and skip), should not - // be re-added each time we retry a test. - _alreadyInheritedAnnotations: boolean = false; + // Annotations known statically before running the test, e.g. `test.skip()` or `test.describe.skip()`. + _staticAnnotations: Annotation[] = []; constructor(title: string, fn: Function, testType: TestTypeImpl, location: Location) { super(title); @@ -258,7 +257,7 @@ export class TestCase extends Base implements reporterTypes.TestCase { requireFile: this._requireFile, poolDigest: this._poolDigest, expectedStatus: this.expectedStatus, - annotations: this.annotations.slice(), + staticAnnotations: this._staticAnnotations.slice(), }; } @@ -268,7 +267,7 @@ export class TestCase extends Base implements reporterTypes.TestCase { test._requireFile = data.requireFile; test._poolDigest = data.poolDigest; test.expectedStatus = data.expectedStatus; - test.annotations = data.annotations; + test._staticAnnotations = data.staticAnnotations; return test; } @@ -280,15 +279,6 @@ export class TestCase extends Base implements reporterTypes.TestCase { return test; } - _annotateWithInheritence(annotations: Annotation[]) { - if (this._alreadyInheritedAnnotations) { - this.annotations = annotations; - } else { - this._alreadyInheritedAnnotations = true; - this.annotations = [...this.annotations, ...annotations]; - } - } - _appendTestResult(): reporterTypes.TestResult { const result: reporterTypes.TestResult = { retry: this.results.length, diff --git a/packages/playwright-test/src/testType.ts b/packages/playwright-test/src/testType.ts index d67e88b309..7f05388453 100644 --- a/packages/playwright-test/src/testType.ts +++ b/packages/playwright-test/src/testType.ts @@ -93,7 +93,7 @@ export class TestTypeImpl { if (type === 'only') test._only = true; if (type === 'skip' || type === 'fixme') { - test.annotations.push({ type }); + test._staticAnnotations.push({ type }); test.expectedStatus = 'skipped'; } for (let parent: Suite | undefined = suite; parent; parent = parent.parent) { @@ -126,7 +126,7 @@ export class TestTypeImpl { child._parallelMode = 'parallel'; if (type === 'skip' || type === 'fixme') { child._skipped = true; - child._annotations.push({ type }); + child._staticAnnotations.push({ type }); } for (let parent: Suite | undefined = suite; parent; parent = parent.parent) { @@ -184,7 +184,7 @@ export class TestTypeImpl { if (modifierArgs.length >= 1 && !modifierArgs[0]) return; const description = modifierArgs[1]; - suite._annotations.push({ type, description }); + suite._staticAnnotations.push({ type, description }); } return; } diff --git a/packages/playwright-test/src/workerMain.ts b/packages/playwright-test/src/workerMain.ts index 559c7bed93..f7a5687ebf 100644 --- a/packages/playwright-test/src/workerMain.ts +++ b/packages/playwright-test/src/workerMain.ts @@ -287,10 +287,10 @@ export class WorkerMain extends ProcessRunner { } } - // Process existing annotations defined on parent suites. + for (const annotation of test._staticAnnotations) + processAnnotation(annotation); + // Process existing annotations dynamically set for parent suites. for (const suite of suites) { - for (const annotation of suite._annotations) - processAnnotation(annotation); const extraAnnotations = this._extraSuiteAnnotations.get(suite) || []; for (const annotation of extraAnnotations) processAnnotation(annotation);