Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 10 additions & 16 deletions docs/src/test-reporter-api/class-reporter.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,25 +298,19 @@ Result of the test run.

Whether this reporter uses stdio for reporting. When it does not, Playwright Test could add some output to enhance user experience. If your reporter does not print to the terminal, it is strongly recommended to return `false`.

## optional async method: Reporter.preprocessSuite
* since: v1.61
- `result` ?<[Object]>
- `implementsSharding` ?<[boolean]> When `true`, Playwright skips its built-in shard filter for this run, leaving sharding to the reporter (typically implemented by calling [`method: TestCase.exclude`] on out-of-shard tests).
## optional async method: Reporter.preprocess
* since: v1.62

Called after the configuration has been resolved and before [`method: Reporter.onBegin`]. Allows a reporter to mark individual tests as skipped, excluded, fixed or failing.

### param: Reporter.preprocessSuite.config
* since: v1.61
- `config` <[FullConfig]>

Resolved configuration.

### param: Reporter.preprocessSuite.suite
* since: v1.61
- `suite` <[Suite]>
### param: Reporter.preprocess.params
* since: v1.62
- `params` <[Object]>
- `config` <[FullConfig]> Resolved configuration.
- `suite` <[Suite]> The root suite that contains the projects, files and test cases that will run.
- `testRun` <[TestRun]> Control which tests will run and their expected status.

The root suite that contains the projects, files and test cases that will run.

The suite reflects `--project`, `--grep`/`--grep-invert` and `.only` filtering, so it only contains tests that match the current invocation. Setup and dependency projects are readonly and cannot be excluded from here.
The suite reflects `--project`, `--grep`/`--grep-invert` and `.only` filtering, so it only contains tests that match the current invocation. Setup and dependency projects are readonly and cannot be changed through [TestRun].

The suite ignores the `--shard` argument: it always contains the full, un-sharded corpus. Playwright applies its built-in sharding after [`method: Reporter.preprocessSuite`] returns, unless the returned `implementsSharding` is `true`.
The suite ignores the `--shard` argument: it always contains the full, un-sharded corpus. Playwright applies its built-in sharding after [`method: Reporter.preprocess`] returns, unless the reporter calls [`method: TestRun.skipSharding`].
38 changes: 0 additions & 38 deletions docs/src/test-reporter-api/class-suite.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,41 +85,3 @@ Returns a list of titles from the root down to this suite.

Returns the type of the suite. The Suites form the following hierarchy:
`root` -> `project` -> `file` -> `describe` -> ...`describe` -> `test`.

## method: Suite.skip
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`]. Mark every [TestCase] of this suite as skipped, see [`method: TestCase.skip`].

### param: Suite.skip.reason
* since: v1.61
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: Suite.fixme
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`]. Mark every [TestCase] of this suite as fixme, see [`method: TestCase.fixme`].

### param: Suite.fixme.reason
* since: v1.61
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: Suite.fail
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`]. Mark every [TestCase] of this suite as expected-to-fail, see [`method: TestCase.fail`].

### param: Suite.fail.reason
* since: v1.61
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: Suite.exclude
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`], exclude this suite from the run. Excluded tests do not appear in the report and their body is not executed.
38 changes: 0 additions & 38 deletions docs/src/test-reporter-api/class-testcase.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,41 +107,3 @@ Returns a list of titles from the root down to this test.
- returns: <[TestCaseType]<"test">>

Returns "test". Useful for detecting test cases in [`method: Suite.entries`].

## method: TestCase.skip
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`], skip this test. The test body is not executed and the test is reported as skipped.

### param: TestCase.skip.reason
* since: v1.61
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: TestCase.fixme
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`], mark this test as fixme. The test body is not executed and the test is reported as skipped, with the intention to fix it.

### param: TestCase.fixme.reason
* since: v1.61
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: TestCase.fail
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`], mark this test as "should fail". Playwright runs the test and ensures it is actually failing, useful for documenting broken functionality until it is fixed.

### param: TestCase.fail.reason
* since: v1.61
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: TestCase.exclude
* since: v1.61

Must be called from inside [`method: Reporter.preprocessSuite`], exclude this test from the run. Excluded tests do not appear in the report and their body is not executed.
72 changes: 72 additions & 0 deletions docs/src/test-reporter-api/class-testrun.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# class: TestRun
* since: v1.62
* langs: js

Controls which tests will run and their expected status. A [TestRun] is available during [`method: Reporter.preprocess`]. Setup and teardown projects cannot be changed.

## method: TestRun.exclude
* since: v1.62

Excludes a test or suite from the run. Excluded tests do not appear in the report and their bodies are not executed.

### param: TestRun.exclude.test
* since: v1.62
- `test` <[TestCase]|[Suite]>

Test or suite to exclude. The root suite cannot be excluded.

## method: TestRun.fail
* since: v1.62

Marks a test or every test in a suite as "should fail". Playwright runs the tests and ensures they are actually failing, useful for documenting broken functionality until it is fixed.

### param: TestRun.fail.test
* since: v1.62
- `test` <[TestCase]|[Suite]>

Test or suite to mark as expected-to-fail.

### param: TestRun.fail.reason
* since: v1.62
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: TestRun.fixme
* since: v1.62

Marks a test or every test in a suite as fixme. The test bodies are not executed and the tests are reported as skipped, with the intention to fix them.

### param: TestRun.fixme.test
* since: v1.62
- `test` <[TestCase]|[Suite]>

Test or suite to mark as fixme.

### param: TestRun.fixme.reason
* since: v1.62
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: TestRun.skip
* since: v1.62

Skips a test or every test in a suite. The test bodies are not executed and the tests are reported as skipped.

### param: TestRun.skip.test
* since: v1.62
- `test` <[TestCase]|[Suite]>

Test or suite to skip.

### param: TestRun.skip.reason
* since: v1.62
- `reason` ?<[string]>

Optional explanation surfaced as the annotation description.

## method: TestRun.skipSharding
* since: v1.62

Disables Playwright's built-in shard filter for this run, leaving sharding to the reporter. Reporters typically implement their own sharding by calling [`method: TestRun.exclude`] on out-of-shard tests.
2 changes: 1 addition & 1 deletion docs/src/test-reporter-api/class-teststep.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* since: v1.10
* langs: js

Represents a step in the [TestRun].
Represents a step in a [TestResult].

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow!


## property: TestStep.category
* since: v1.10
Expand Down
58 changes: 0 additions & 58 deletions packages/playwright/src/common/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import { rootTestType } from './testType';
import { computeTestCaseOutcome } from '../isomorphic/teleReceiver';
import { wrapFunctionWithLocation } from '../transform/transform';
import type { FixturesWithLocation, FullProjectInternal } from './config';
import type { FixturePool } from './fixtures';
import type { TestTypeImpl } from './testType';
Expand Down Expand Up @@ -58,19 +57,11 @@ export class Suite extends Base {
_parallelMode: 'none' | 'default' | 'serial' | 'parallel' = 'none';
_fullProject: FullProjectInternal | undefined;
_fileId: string | undefined;
_preprocessMode: 'editable' | 'readonly' | undefined = undefined;
readonly _type: 'root' | 'project' | 'file' | 'describe';

skip: (reason?: string) => void;
fixme: (reason?: string) => void;
fail: (reason?: string) => void;

constructor(title: string, type: 'root' | 'project' | 'file' | 'describe') {
super(title);
this._type = type;
this.skip = wrapFunctionWithLocation((location, reason?: string) => this._modifier('skip', location, reason));
this.fixme = wrapFunctionWithLocation((location, reason?: string) => this._modifier('fixme', location, reason));
this.fail = wrapFunctionWithLocation((location, reason?: string) => this._modifier('fail', location, reason));
}

get type(): 'root' | 'project' | 'file' | 'describe' {
Expand Down Expand Up @@ -269,30 +260,6 @@ export class Suite extends Base {
return this._fullProject?.project || this.parent?.project();
}

private _modifier(type: 'skip' | 'fixme' | 'fail', location: Location, reason: string | undefined): void {
const mode = this._resolvePreprocessMode();
if (!mode)
throw new Error(`Suite.${type}() can only be called from Reporter.preprocessSuite().`);
if (mode === 'readonly')
throw new Error(`Suite.${type}() cannot be called on a setup or teardown project; these always run in full.`);
for (const test of this.allTests())
test._applyPlanAnnotation({ type, description: reason, location });
}

exclude(): void {
const mode = this._resolvePreprocessMode();
if (!mode)
throw new Error(`Suite.exclude() can only be called from Reporter.preprocessSuite().`);
if (!this.parent)
throw new Error(`Suite.exclude() cannot be called on the root suite.`);
if (mode === 'readonly')
throw new Error(`Suite.exclude() cannot be called on a setup or teardown project; these always run in full.`);
this.parent._detach(this);
}

_resolvePreprocessMode(): 'editable' | 'readonly' | undefined {
return this._preprocessMode ?? this.parent?._resolvePreprocessMode();
}
}

export class TestCase extends Base implements reporterTypes.TestCase {
Expand All @@ -318,18 +285,11 @@ export class TestCase extends Base implements reporterTypes.TestCase {
_tags: string[] = [];
_planAnnotations: TestAnnotation[] = [];

skip: (reason?: string) => void;
fixme: (reason?: string) => void;
fail: (reason?: string) => void;

constructor(title: string, fn: Function, testType: TestTypeImpl, location: Location) {
super(title);
this.fn = fn;
this._testType = testType;
this.location = location;
this.skip = wrapFunctionWithLocation((location, reason?: string) => this._modifier('skip', location, reason));
this.fixme = wrapFunctionWithLocation((location, reason?: string) => this._modifier('fixme', location, reason));
this.fail = wrapFunctionWithLocation((location, reason?: string) => this._modifier('fail', location, reason));
}

titlePath(): string[] {
Expand Down Expand Up @@ -358,15 +318,6 @@ export class TestCase extends Base implements reporterTypes.TestCase {
];
}

private _modifier(type: 'skip' | 'fixme' | 'fail', location: Location, reason: string | undefined): void {
const mode = this.parent._resolvePreprocessMode();
if (!mode)
throw new Error(`TestCase.${type}() can only be called from Reporter.preprocessSuite().`);
if (mode === 'readonly')
throw new Error(`TestCase.${type}() cannot be called on a setup or teardown project test; these always run in full.`);
this._applyPlanAnnotation({ type, description: reason, location });
}

_applyPlanAnnotation(annotation: TestAnnotation): void {
this.annotations.push(annotation);
this._planAnnotations.push(annotation);
Expand All @@ -376,15 +327,6 @@ export class TestCase extends Base implements reporterTypes.TestCase {
this.expectedStatus = 'failed';
}

exclude(): void {
const mode = this.parent._resolvePreprocessMode();
if (!mode)
throw new Error(`TestCase.exclude() can only be called from Reporter.preprocessSuite().`);
if (mode === 'readonly')
throw new Error(`TestCase.exclude() cannot be called on a setup or teardown project test; these always run in full.`);
this.parent._detach(this);
}

_serialize(): any {
return {
kind: 'test',
Expand Down
7 changes: 3 additions & 4 deletions packages/playwright/src/reporters/internalReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ import { test as testNs } from '../common';
import * as babel from '../transform/babelBundle';
import { wrapReporterAsV2 } from './reporterV2';

import type { AnyReporter, ReporterV2 } from './reporterV2';
import type { AnyReporter, ReporterPreprocessParams, ReporterV2 } from './reporterV2';
import type { FullConfig, FullResult, TestCase, TestError, TestResult, TestStep, WorkerInfo } from '../../types/testReporter';


export class InternalReporter implements ReporterV2 {
private _reporter: Multiplexer;
private _didBegin = false;
Expand All @@ -54,8 +53,8 @@ export class InternalReporter implements ReporterV2 {
this._reporter.onConfigure?.(config);
}

async preprocessSuite(config: FullConfig, suite: testNs.Suite) {
return await this._reporter.preprocessSuite?.(config, suite);
async preprocess(params: ReporterPreprocessParams) {
await this._reporter.preprocess(params);
}

onBegin(suite: testNs.Suite) {
Expand Down
19 changes: 6 additions & 13 deletions packages/playwright/src/reporters/multiplexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { serializeError } from '../util';

import type { ReportConfigureParams, ReportEndParams, ReporterV2 } from './reporterV2';
import type { ReportConfigureParams, ReportEndParams, ReporterPreprocessParams, ReporterV2 } from './reporterV2';
import type { FullConfig, FullResult, TestCase, TestError, TestResult, TestStep, WorkerInfo } from '../../types/testReporter';
import type { test } from '../common';

Expand All @@ -41,20 +41,13 @@ export class Multiplexer implements ReporterV2 {
this._wrap(() => reporter.onConfigure?.(config));
}

async preprocessSuite(config: FullConfig, suite: test.Suite) {
// Unlike other reporter callbacks, `preprocessSuite` errors are NOT swallowed —
// they propagate so the run aborts before onBegin. Reporters use preprocessSuite
async preprocess(params: ReporterPreprocessParams) {
// Unlike other reporter callbacks, `preprocess` errors are NOT swallowed —
// they propagate so the run aborts before onBegin. Reporters use preprocess
// to mutate the corpus; silently dropping a planning error would let
// an inconsistent (partial-mutation) state reach the workers.
const shardingReporters: ReporterV2[] = [];
for (const reporter of this._reporters) {
const result = await reporter.preprocessSuite?.(config, suite);
if (result?.implementsSharding)
shardingReporters.push(reporter);
}
if (shardingReporters.length > 1)
throw new Error(`Multiple reporters declare 'implementsSharding': ${shardingReporters.map(r => r.constructor?.name ?? 'reporter').join(', ')}. Only one reporter may handle sharding.`);
return { implementsSharding: shardingReporters.length > 0 };
for (const reporter of this._reporters)
await reporter.preprocess?.(params);
}

onBegin(suite: test.Suite) {
Expand Down
Loading