Skip to content

Commit e52434b

Browse files
committed
chore(testRunner): expose pauseAtEnd/pauseOnError
1 parent eed1f19 commit e52434b

File tree

17 files changed

+159
-43
lines changed

17 files changed

+159
-43
lines changed

packages/playwright/src/common/ipc.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ export type WorkerInitParams = {
6666
projectId: string;
6767
config: SerializedConfig;
6868
artifactsDir: string;
69-
pauseOnError: boolean;
70-
pauseAtEnd: boolean;
69+
pauseOnError: 'mcp' | 'notify' | 'off';
70+
pauseAtEnd: 'mcp' | 'notify' | 'off';
7171
};
7272

7373
export type TestBeginPayload = {
@@ -86,6 +86,10 @@ export type AttachmentPayload = {
8686

8787
export type TestInfoErrorImpl = TestInfoError;
8888

89+
export type TestPausedPayload = {
90+
errors: TestInfoErrorImpl[];
91+
};
92+
8993
export type TestEndPayload = {
9094
testId: string;
9195
duration: number;

packages/playwright/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
237237
(testInfo as TestInfoImpl)._setDebugMode();
238238

239239
playwright._defaultContextOptions = _combinedContextOptions;
240-
playwright._defaultContextTimeout = (testInfo as TestInfoImpl)._pauseOnError() ? 5000 : actionTimeout || 0;
240+
playwright._defaultContextTimeout = (testInfo as TestInfoImpl)._pauseOnError() === 'mcp' ? 5000 : actionTimeout || 0;
241241
playwright._defaultContextNavigationTimeout = navigationTimeout || 0;
242242
await use();
243243
playwright._defaultContextOptions = undefined;

packages/playwright/src/isomorphic/testServerConnection.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import * as events from './events';
1818

1919
import type { TestServerInterface, TestServerInterfaceEvents } from '@testIsomorphic/testServerInterface';
20+
import type * as reporterTypes from '../../types/testReporter';
2021

2122
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
2223

@@ -68,12 +69,14 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
6869
readonly onStdio: events.Event<{ type: 'stderr' | 'stdout'; text?: string | undefined; buffer?: string | undefined; }>;
6970
readonly onTestFilesChanged: events.Event<{ testFiles: string[] }>;
7071
readonly onLoadTraceRequested: events.Event<{ traceUrl: string }>;
72+
readonly onTestPaused: events.Event<{ errors: reporterTypes.TestError[] }>;
7173

7274
private _onCloseEmitter = new events.EventEmitter<void>();
7375
private _onReportEmitter = new events.EventEmitter<any>();
7476
private _onStdioEmitter = new events.EventEmitter<{ type: 'stderr' | 'stdout'; text?: string | undefined; buffer?: string | undefined; }>();
7577
private _onTestFilesChangedEmitter = new events.EventEmitter<{ testFiles: string[] }>();
7678
private _onLoadTraceRequestedEmitter = new events.EventEmitter<{ traceUrl: string }>();
79+
private _onTestPausedEmitter = new events.EventEmitter<{ errors: reporterTypes.TestError[] }>();
7780

7881
private _lastId = 0;
7982
private _transport: TestServerTransport;
@@ -87,6 +90,7 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
8790
this.onStdio = this._onStdioEmitter.event;
8891
this.onTestFilesChanged = this._onTestFilesChangedEmitter.event;
8992
this.onLoadTraceRequested = this._onLoadTraceRequestedEmitter.event;
93+
this.onTestPaused = this._onTestPausedEmitter.event;
9094

9195
this._transport = transport;
9296
this._transport.onmessage(data => {
@@ -147,6 +151,8 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
147151
this._onTestFilesChangedEmitter.fire(params);
148152
else if (method === 'loadTraceRequested')
149153
this._onLoadTraceRequestedEmitter.fire(params);
154+
else if (method === 'testPaused')
155+
this._onTestPausedEmitter.fire(params);
150156
}
151157

152158
async initialize(params: Parameters<TestServerInterface['initialize']>[0]): ReturnType<TestServerInterface['initialize']> {

packages/playwright/src/isomorphic/testServerInterface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export interface TestServerInterface {
103103
reuseContext?: boolean;
104104
connectWsEndpoint?: string;
105105
timeout?: number;
106+
pauseOnError?: boolean;
107+
pauseAtEnd?: boolean;
106108
}): Promise<{
107109
status: reporterTypes.FullResult['status'];
108110
}>;
@@ -121,11 +123,13 @@ export interface TestServerInterfaceEvents {
121123
onStdio: Event<{ type: 'stdout' | 'stderr', text?: string, buffer?: string }>;
122124
onTestFilesChanged: Event<{ testFiles: string[] }>;
123125
onLoadTraceRequested: Event<{ traceUrl: string }>;
126+
onTestPaused: Event<{ errors: reporterTypes.TestError[] }>;
124127
}
125128

126129
export interface TestServerInterfaceEventEmitters {
127130
dispatchEvent(event: 'report', params: ReportEntry): void;
128131
dispatchEvent(event: 'stdio', params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }): void;
129132
dispatchEvent(event: 'testFilesChanged', params: { testFiles: string[] }): void;
130133
dispatchEvent(event: 'loadTraceRequested', params: { traceUrl: string }): void;
134+
dispatchEvent(event: 'testPaused', params: { errors: reporterTypes.TestError[] }): void;
131135
}

packages/playwright/src/mcp/test/browserBackend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export async function runBrowserBackendAtEnd(context: playwright.BrowserContext,
3131
if (!testInfo)
3232
return;
3333

34-
const shouldPause = errorMessage ? testInfo?._pauseOnError() : testInfo?._pauseAtEnd();
34+
const shouldPause = errorMessage ? testInfo?._pauseOnError() === 'mcp' : testInfo?._pauseAtEnd() === 'mcp';
3535
if (!shouldPause)
3636
return;
3737

packages/playwright/src/mcp/test/testContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export class TestContext {
149149
projects: [projectName],
150150
timeout: 0,
151151
workers: 1,
152-
pauseAtEnd: true,
152+
pauseAtEnd: 'mcp',
153153
disableConfigReporters: true,
154154
failOnLoadErrors: true,
155155
});

packages/playwright/src/mcp/test/testTools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const debugTest = defineTestTool({
9090
// For automatic recovery
9191
timeout: 0,
9292
workers: 1,
93-
pauseOnError: true,
93+
pauseOnError: 'mcp',
9494
disableConfigReporters: true,
9595
});
9696

packages/playwright/src/reporters/internalReporter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export class InternalReporter implements ReporterV2 {
117117
}
118118
}
119119

120-
function addLocationAndSnippetToError(config: FullConfig, error: TestError, file?: string) {
120+
export function addLocationAndSnippetToError(config: FullConfig, error: TestError, file?: string) {
121121
if (error.stack && !error.location)
122122
error.location = prepareErrorStack(error.stack).location;
123123
const location = error.location;

packages/playwright/src/runner/dispatcher.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { colors } from 'playwright-core/lib/utils';
2020
import { addSuggestedRebaseline } from './rebase';
2121
import { WorkerHost } from './workerHost';
2222
import { serializeConfig } from '../common/ipc';
23+
import { addLocationAndSnippetToError } from '../reporters/internalReporter';
2324

2425
import type { FailureTracker } from './failureTracker';
2526
import type { ProcessExitData } from './processHost';
@@ -98,7 +99,7 @@ export class Dispatcher {
9899

99100
// 3. Claim both the job and the worker slot.
100101
this._queue.splice(jobIndex, 1);
101-
const jobDispatcher = new JobDispatcher(job, this._reporter, this._failureTracker, () => this.stop().catch(() => {}));
102+
const jobDispatcher = new JobDispatcher(job, this._config, this._reporter, this._failureTracker, () => this.stop().catch(() => {}));
102103
this._workerSlots[workerIndex].busy = true;
103104
this._workerSlots[workerIndex].jobDispatcher = jobDispatcher;
104105

@@ -278,6 +279,7 @@ class JobDispatcher {
278279
jobResult = new ManualPromise<{ newJob?: TestGroup, didFail: boolean }>();
279280

280281
readonly job: TestGroup;
282+
private _config: FullConfigInternal;
281283
private _reporter: ReporterV2;
282284
private _failureTracker: FailureTracker;
283285
private _stopCallback: () => void;
@@ -290,8 +292,9 @@ class JobDispatcher {
290292
private _workerIndex = 0;
291293
private _currentlyRunning: { test: TestCase, result: TestResult } | undefined;
292294

293-
constructor(job: TestGroup, reporter: ReporterV2, failureTracker: FailureTracker, stopCallback: () => void) {
295+
constructor(job: TestGroup, config: FullConfigInternal, reporter: ReporterV2, failureTracker: FailureTracker, stopCallback: () => void) {
294296
this.job = job;
297+
this._config = config;
295298
this._reporter = reporter;
296299
this._failureTracker = failureTracker;
297300
this._stopCallback = stopCallback;
@@ -574,6 +577,11 @@ class JobDispatcher {
574577
eventsHelper.addEventListener(worker, 'stepBegin', this._onStepBegin.bind(this)),
575578
eventsHelper.addEventListener(worker, 'stepEnd', this._onStepEnd.bind(this)),
576579
eventsHelper.addEventListener(worker, 'attach', this._onAttach.bind(this)),
580+
eventsHelper.addEventListener(worker, 'testPaused', params => {
581+
for (const error of params.errors)
582+
addLocationAndSnippetToError(this._config.config, error);
583+
this._failureTracker.onTestPaused?.(params);
584+
}),
577585
eventsHelper.addEventListener(worker, 'done', this._onDone.bind(this)),
578586
eventsHelper.addEventListener(worker, 'exit', this.onExit.bind(this)),
579587
];

packages/playwright/src/runner/failureTracker.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,24 @@
1414
* limitations under the License.
1515
*/
1616

17-
import type { TestResult } from '../../types/testReporter';
17+
import type { TestResult, TestError } from '../../types/testReporter';
1818
import type { FullConfigInternal, FullProjectInternal } from '../common/config';
1919
import type { Suite, TestCase } from '../common/test';
2020

2121
export class FailureTracker {
22+
private _config: FullConfigInternal;
2223
private _failureCount = 0;
2324
private _hasWorkerErrors = false;
2425
private _rootSuite: Suite | undefined;
2526
private _topLevelProjects: FullProjectInternal[] = [];
26-
private _pauseOnError: boolean;
27-
private _pauseAtEnd: boolean;
27+
private _pauseOnError: 'mcp' | 'notify' | 'off';
28+
private _pauseAtEnd: 'mcp' | 'notify' | 'off';
29+
onTestPaused?: (params: { errors: TestError[] }) => void;
2830

29-
constructor(private _config: FullConfigInternal, options?: { pauseOnError?: boolean, pauseAtEnd?: boolean }) {
30-
this._pauseOnError = options?.pauseOnError ?? false;
31-
this._pauseAtEnd = options?.pauseAtEnd ?? false;
31+
constructor(config: FullConfigInternal, options?: { pauseOnError?: 'mcp' | 'notify' | 'off', pauseAtEnd?: 'mcp' | 'notify' | 'off' }) {
32+
this._config = config;
33+
this._pauseOnError = options?.pauseOnError ?? 'off';
34+
this._pauseAtEnd = options?.pauseAtEnd ?? 'off';
3235
}
3336

3437
onRootSuite(rootSuite: Suite, topLevelProjects: FullProjectInternal[]) {
@@ -46,12 +49,12 @@ export class FailureTracker {
4649
this._hasWorkerErrors = true;
4750
}
4851

49-
pauseOnError(): boolean {
52+
pauseOnError() {
5053
return this._pauseOnError;
5154
}
5255

53-
pauseAtEnd(inProject: FullProjectInternal): boolean {
54-
return this._pauseAtEnd && this._topLevelProjects.includes(inProject);
56+
pauseAtEnd(inProject: FullProjectInternal) {
57+
return this._topLevelProjects.includes(inProject) ? this._pauseAtEnd : 'off';
5558
}
5659

5760
hasReachedMaxFailures() {

0 commit comments

Comments
 (0)