playwright 1.55.0-alpha-2025-08-13 → 1.55.0-alpha-2025-08-15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of playwright might be problematic. Click here for more details.
- package/README.md +2 -2
- package/lib/index.js +5 -0
- package/lib/isomorphic/testServerConnection.js +7 -0
- package/lib/matchers/expect.js +21 -8
- package/lib/program.js +9 -59
- package/lib/reporters/base.js +18 -11
- package/lib/reporters/dot.js +11 -11
- package/lib/reporters/github.js +2 -1
- package/lib/reporters/html.js +14 -11
- package/lib/reporters/internalReporter.js +2 -1
- package/lib/reporters/line.js +15 -15
- package/lib/reporters/list.js +15 -15
- package/lib/reporters/listModeReporter.js +66 -0
- package/lib/reporters/markdown.js +3 -3
- package/lib/runner/dispatcher.js +22 -1
- package/lib/runner/failureTracker.js +14 -0
- package/lib/runner/reporters.js +7 -32
- package/lib/runner/tasks.js +0 -9
- package/lib/runner/testRunner.js +402 -0
- package/lib/runner/testServer.js +57 -273
- package/lib/runner/watchMode.js +57 -1
- package/lib/runner/workerHost.js +6 -2
- package/lib/transform/transform.js +1 -1
- package/lib/util.js +0 -18
- package/lib/worker/testInfo.js +24 -1
- package/lib/worker/workerMain.js +5 -0
- package/package.json +3 -2
- package/lib/runner/runner.js +0 -110
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🎭 Playwright
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
|
4
4
|
|
|
5
5
|
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
|
|
8
8
|
|
|
9
9
|
| | Linux | macOS | Windows |
|
|
10
10
|
| :--- | :---: | :---: | :---: |
|
|
11
|
-
| Chromium <!-- GEN:chromium-version -->140.0.7339.
|
|
11
|
+
| Chromium <!-- GEN:chromium-version -->140.0.7339.16<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
|
12
12
|
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
|
13
13
|
| Firefox <!-- GEN:firefox-version -->141.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
|
14
14
|
|
package/lib/index.js
CHANGED
|
@@ -258,6 +258,11 @@ const playwrightFixtures = {
|
|
|
258
258
|
if (data.apiName === "tracing.group")
|
|
259
259
|
tracingGroupSteps.push(step);
|
|
260
260
|
},
|
|
261
|
+
onApiCallRecovery: (data, error, recoveryHandlers) => {
|
|
262
|
+
const step = data.userData;
|
|
263
|
+
if (step)
|
|
264
|
+
recoveryHandlers.push(() => step.recoverFromStepError(error));
|
|
265
|
+
},
|
|
261
266
|
onApiCallEnd: (data) => {
|
|
262
267
|
if (data.apiName === "tracing.group")
|
|
263
268
|
return;
|
|
@@ -63,6 +63,7 @@ class TestServerConnection {
|
|
|
63
63
|
this._onStdioEmitter = new events.EventEmitter();
|
|
64
64
|
this._onTestFilesChangedEmitter = new events.EventEmitter();
|
|
65
65
|
this._onLoadTraceRequestedEmitter = new events.EventEmitter();
|
|
66
|
+
this._onRecoverFromStepErrorEmitter = new events.EventEmitter();
|
|
66
67
|
this._lastId = 0;
|
|
67
68
|
this._callbacks = /* @__PURE__ */ new Map();
|
|
68
69
|
this._isClosed = false;
|
|
@@ -71,6 +72,7 @@ class TestServerConnection {
|
|
|
71
72
|
this.onStdio = this._onStdioEmitter.event;
|
|
72
73
|
this.onTestFilesChanged = this._onTestFilesChangedEmitter.event;
|
|
73
74
|
this.onLoadTraceRequested = this._onLoadTraceRequestedEmitter.event;
|
|
75
|
+
this.onRecoverFromStepError = this._onRecoverFromStepErrorEmitter.event;
|
|
74
76
|
this._transport = transport;
|
|
75
77
|
this._transport.onmessage((data) => {
|
|
76
78
|
const message = JSON.parse(data);
|
|
@@ -127,6 +129,8 @@ class TestServerConnection {
|
|
|
127
129
|
this._onTestFilesChangedEmitter.fire(params);
|
|
128
130
|
else if (method === "loadTraceRequested")
|
|
129
131
|
this._onLoadTraceRequestedEmitter.fire(params);
|
|
132
|
+
else if (method === "recoverFromStepError")
|
|
133
|
+
this._onRecoverFromStepErrorEmitter.fire(params);
|
|
130
134
|
}
|
|
131
135
|
async initialize(params) {
|
|
132
136
|
await this._sendMessage("initialize", params);
|
|
@@ -197,6 +201,9 @@ class TestServerConnection {
|
|
|
197
201
|
async closeGracefully(params) {
|
|
198
202
|
await this._sendMessage("closeGracefully", params);
|
|
199
203
|
}
|
|
204
|
+
async resumeAfterStepError(params) {
|
|
205
|
+
await this._sendMessage("resumeAfterStepError", params);
|
|
206
|
+
}
|
|
200
207
|
close() {
|
|
201
208
|
try {
|
|
202
209
|
this._transport.close();
|
package/lib/matchers/expect.js
CHANGED
|
@@ -234,18 +234,31 @@ class ExpectMetaInfoProxyHandler {
|
|
|
234
234
|
infectParentStepsWithError: this._info.isSoft
|
|
235
235
|
};
|
|
236
236
|
const step = testInfo._addStep(stepInfo);
|
|
237
|
-
const reportStepError = (e) => {
|
|
237
|
+
const reportStepError = (isAsync, e) => {
|
|
238
238
|
const jestError = (0, import_matcherHint.isJestError)(e) ? e : null;
|
|
239
|
-
const
|
|
239
|
+
const expectError = jestError ? new import_matcherHint.ExpectError(jestError, customMessage, stackFrames) : void 0;
|
|
240
240
|
if (jestError?.matcherResult.suggestedRebaseline) {
|
|
241
241
|
step.complete({ suggestedRebaseline: jestError?.matcherResult.suggestedRebaseline });
|
|
242
242
|
return;
|
|
243
243
|
}
|
|
244
|
+
const error = expectError ?? e;
|
|
244
245
|
step.complete({ error });
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
if (!isAsync || !expectError) {
|
|
247
|
+
if (this._info.isSoft)
|
|
248
|
+
testInfo._failWithError(error);
|
|
249
|
+
else
|
|
250
|
+
throw error;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
return (async () => {
|
|
254
|
+
const recoveryResult = await step.recoverFromStepError(expectError);
|
|
255
|
+
if (recoveryResult.status === "recovered")
|
|
256
|
+
return recoveryResult.value;
|
|
257
|
+
if (this._info.isSoft)
|
|
258
|
+
testInfo._failWithError(expectError);
|
|
259
|
+
else
|
|
260
|
+
throw expectError;
|
|
261
|
+
})();
|
|
249
262
|
};
|
|
250
263
|
const finalizer = () => {
|
|
251
264
|
step.complete({});
|
|
@@ -255,11 +268,11 @@ class ExpectMetaInfoProxyHandler {
|
|
|
255
268
|
const callback = () => matcher.call(target, ...args);
|
|
256
269
|
const result = (0, import_utils.currentZone)().with("stepZone", step).run(callback);
|
|
257
270
|
if (result instanceof Promise)
|
|
258
|
-
return result.then(finalizer).catch(reportStepError);
|
|
271
|
+
return result.then(finalizer).catch(reportStepError.bind(null, true));
|
|
259
272
|
finalizer();
|
|
260
273
|
return result;
|
|
261
274
|
} catch (e) {
|
|
262
|
-
reportStepError(e);
|
|
275
|
+
void reportStepError(false, e);
|
|
263
276
|
}
|
|
264
277
|
};
|
|
265
278
|
}
|
package/lib/program.js
CHANGED
|
@@ -28,8 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
var program_exports = {};
|
|
30
30
|
__export(program_exports, {
|
|
31
|
-
program: () => import_program2.program
|
|
32
|
-
withRunnerAndMutedWrite: () => withRunnerAndMutedWrite
|
|
31
|
+
program: () => import_program2.program
|
|
33
32
|
});
|
|
34
33
|
module.exports = __toCommonJS(program_exports);
|
|
35
34
|
var import_fs = __toESM(require("fs"));
|
|
@@ -43,10 +42,10 @@ var import_base = require("./reporters/base");
|
|
|
43
42
|
var import_html = require("./reporters/html");
|
|
44
43
|
var import_merge = require("./reporters/merge");
|
|
45
44
|
var import_projectUtils = require("./runner/projectUtils");
|
|
46
|
-
var import_runner = require("./runner/runner");
|
|
47
45
|
var testServer = __toESM(require("./runner/testServer"));
|
|
48
46
|
var import_watchMode = require("./runner/watchMode");
|
|
49
|
-
var
|
|
47
|
+
var import_testRunner = require("./runner/testRunner");
|
|
48
|
+
var import_reporters = require("./runner/reporters");
|
|
50
49
|
function addTestCommand(program3) {
|
|
51
50
|
const command = program3.command("test [test-filter...]");
|
|
52
51
|
command.description("run tests with Playwright Test");
|
|
@@ -78,44 +77,24 @@ Examples:
|
|
|
78
77
|
$ npx playwright test --headed
|
|
79
78
|
$ npx playwright test --project=webkit`);
|
|
80
79
|
}
|
|
81
|
-
function addListFilesCommand(program3) {
|
|
82
|
-
const command = program3.command("list-files [file-filter...]", { hidden: true });
|
|
83
|
-
command.description("List files with Playwright Test tests");
|
|
84
|
-
command.option("-c, --config <file>", `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
|
85
|
-
command.option("--project <project-name...>", `Only run tests from the specified list of projects, supports '*' wildcard (default: list all projects)`);
|
|
86
|
-
command.action(async (args, opts) => listTestFiles(opts));
|
|
87
|
-
}
|
|
88
80
|
function addClearCacheCommand(program3) {
|
|
89
81
|
const command = program3.command("clear-cache");
|
|
90
82
|
command.description("clears build and test caches");
|
|
91
83
|
command.option("-c, --config <file>", `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
|
92
84
|
command.action(async (opts) => {
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const { status } = await runner.clearCache();
|
|
85
|
+
const runner = new import_testRunner.TestRunner((0, import_configLoader.resolveConfigLocation)(opts.config), {});
|
|
86
|
+
const { status } = await runner.clearCache((0, import_reporters.createErrorCollectingReporter)(import_base.terminalScreen));
|
|
96
87
|
const exitCode = status === "interrupted" ? 130 : status === "passed" ? 0 : 1;
|
|
97
88
|
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
|
|
98
89
|
});
|
|
99
90
|
}
|
|
100
|
-
function addFindRelatedTestFilesCommand(program3) {
|
|
101
|
-
const command = program3.command("find-related-test-files [source-files...]", { hidden: true });
|
|
102
|
-
command.description("Returns the list of related tests to the given files");
|
|
103
|
-
command.option("-c, --config <file>", `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
|
104
|
-
command.action(async (files, options) => {
|
|
105
|
-
const resolvedFiles = files.map((file) => import_path.default.resolve(process.cwd(), file));
|
|
106
|
-
await withRunnerAndMutedWrite(options.config, (runner) => runner.findRelatedTestFiles(resolvedFiles));
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
91
|
function addDevServerCommand(program3) {
|
|
110
92
|
const command = program3.command("dev-server", { hidden: true });
|
|
111
93
|
command.description("start dev server");
|
|
112
94
|
command.option("-c, --config <file>", `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
|
113
95
|
command.action(async (options) => {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const { status } = await runner.runDevServer();
|
|
117
|
-
const exitCode = status === "interrupted" ? 130 : status === "passed" ? 0 : 1;
|
|
118
|
-
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
|
|
96
|
+
const runner = new import_testRunner.TestRunner((0, import_configLoader.resolveConfigLocation)(options.config), {});
|
|
97
|
+
await runner.startDevServer((0, import_reporters.createErrorCollectingReporter)(import_base.terminalScreen), "in-process");
|
|
119
98
|
});
|
|
120
99
|
}
|
|
121
100
|
function addTestServerCommand(program3) {
|
|
@@ -172,7 +151,6 @@ async function runTests(args, opts) {
|
|
|
172
151
|
config.cliProjectFilter = opts.project || void 0;
|
|
173
152
|
config.cliPassWithNoTests = !!opts.passWithNoTests;
|
|
174
153
|
config.cliLastFailed = !!opts.lastFailed;
|
|
175
|
-
config.cliFilterFile = opts.filter ? import_path.default.resolve(process.cwd(), opts.filter) : void 0;
|
|
176
154
|
(0, import_projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
|
|
177
155
|
if (opts.ui || opts.uiHost || opts.uiPort) {
|
|
178
156
|
if (opts.onlyChanged)
|
|
@@ -207,8 +185,7 @@ async function runTests(args, opts) {
|
|
|
207
185
|
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode2);
|
|
208
186
|
return;
|
|
209
187
|
}
|
|
210
|
-
const
|
|
211
|
-
const status = await runner.runAllTests();
|
|
188
|
+
const status = await (0, import_testRunner.runAllTestsWithConfig)(config);
|
|
212
189
|
await (0, import_utils.stopProfiling)("runner");
|
|
213
190
|
const exitCode = status === "interrupted" ? 130 : status === "passed" ? 0 : 1;
|
|
214
191
|
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
|
|
@@ -220,29 +197,6 @@ async function runTestServer(opts) {
|
|
|
220
197
|
const exitCode = status === "interrupted" ? 130 : status === "passed" ? 0 : 1;
|
|
221
198
|
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
|
|
222
199
|
}
|
|
223
|
-
async function withRunnerAndMutedWrite(configFile, callback) {
|
|
224
|
-
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
|
225
|
-
process.stdout.write = (a, b, c) => process.stderr.write(a, b, c);
|
|
226
|
-
try {
|
|
227
|
-
const config = await (0, import_configLoader.loadConfigFromFile)(configFile);
|
|
228
|
-
const runner = new import_runner.Runner(config);
|
|
229
|
-
const result = await callback(runner);
|
|
230
|
-
stdoutWrite(JSON.stringify(result, void 0, 2), () => {
|
|
231
|
-
(0, import_utils.gracefullyProcessExitDoNotHang)(0);
|
|
232
|
-
});
|
|
233
|
-
} catch (e) {
|
|
234
|
-
const error = (0, import_util.serializeError)(e);
|
|
235
|
-
error.location = (0, import_base.prepareErrorStack)(e.stack).location;
|
|
236
|
-
stdoutWrite(JSON.stringify({ error }, void 0, 2), () => {
|
|
237
|
-
(0, import_utils.gracefullyProcessExitDoNotHang)(0);
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
async function listTestFiles(opts) {
|
|
242
|
-
await withRunnerAndMutedWrite(opts.config, async (runner) => {
|
|
243
|
-
return await runner.listTestFiles();
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
200
|
async function mergeReports(reportDir, opts) {
|
|
247
201
|
const configFile = opts.config;
|
|
248
202
|
const config = configFile ? await (0, import_configLoader.loadConfigFromFile)(configFile) : await (0, import_configLoader.loadEmptyConfigForMergeReports)();
|
|
@@ -347,7 +301,6 @@ const testOptions = [
|
|
|
347
301
|
["-c, --config <file>", { description: `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"` }],
|
|
348
302
|
["--debug", { description: `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options` }],
|
|
349
303
|
["--fail-on-flaky-tests", { description: `Fail if any test is flagged as flaky (default: false)` }],
|
|
350
|
-
["--filter <file>", { description: `Path to a test filter file. See documentation for details.` }],
|
|
351
304
|
["--forbid-only", { description: `Fail if test.only is called (default: false)` }],
|
|
352
305
|
["--fully-parallel", { description: `Run all tests in parallel (default: false)` }],
|
|
353
306
|
["--global-timeout <timeout>", { description: `Maximum time this test suite can run in milliseconds (default: unlimited)` }],
|
|
@@ -381,14 +334,11 @@ const testOptions = [
|
|
|
381
334
|
];
|
|
382
335
|
addTestCommand(import_program.program);
|
|
383
336
|
addShowReportCommand(import_program.program);
|
|
384
|
-
addListFilesCommand(import_program.program);
|
|
385
337
|
addMergeReportsCommand(import_program.program);
|
|
386
338
|
addClearCacheCommand(import_program.program);
|
|
387
|
-
addFindRelatedTestFilesCommand(import_program.program);
|
|
388
339
|
addDevServerCommand(import_program.program);
|
|
389
340
|
addTestServerCommand(import_program.program);
|
|
390
341
|
// Annotate the CommonJS export names for ESM import in node:
|
|
391
342
|
0 && (module.exports = {
|
|
392
|
-
program
|
|
393
|
-
withRunnerAndMutedWrite
|
|
343
|
+
program
|
|
394
344
|
});
|
package/lib/reporters/base.js
CHANGED
|
@@ -54,18 +54,20 @@ var import_utilsBundle2 = require("../utilsBundle");
|
|
|
54
54
|
const kOutputSymbol = Symbol("output");
|
|
55
55
|
const DEFAULT_TTY_WIDTH = 100;
|
|
56
56
|
const DEFAULT_TTY_HEIGHT = 40;
|
|
57
|
+
const originalProcessStdout = process.stdout;
|
|
58
|
+
const originalProcessStderr = process.stderr;
|
|
57
59
|
const terminalScreen = (() => {
|
|
58
|
-
let isTTY = !!
|
|
59
|
-
let ttyWidth =
|
|
60
|
-
let ttyHeight =
|
|
60
|
+
let isTTY = !!originalProcessStdout.isTTY;
|
|
61
|
+
let ttyWidth = originalProcessStdout.columns || 0;
|
|
62
|
+
let ttyHeight = originalProcessStdout.rows || 0;
|
|
61
63
|
if (process.env.PLAYWRIGHT_FORCE_TTY === "false" || process.env.PLAYWRIGHT_FORCE_TTY === "0") {
|
|
62
64
|
isTTY = false;
|
|
63
65
|
ttyWidth = 0;
|
|
64
66
|
ttyHeight = 0;
|
|
65
67
|
} else if (process.env.PLAYWRIGHT_FORCE_TTY === "true" || process.env.PLAYWRIGHT_FORCE_TTY === "1") {
|
|
66
68
|
isTTY = true;
|
|
67
|
-
ttyWidth =
|
|
68
|
-
ttyHeight =
|
|
69
|
+
ttyWidth = originalProcessStdout.columns || DEFAULT_TTY_WIDTH;
|
|
70
|
+
ttyHeight = originalProcessStdout.rows || DEFAULT_TTY_HEIGHT;
|
|
69
71
|
} else if (process.env.PLAYWRIGHT_FORCE_TTY) {
|
|
70
72
|
isTTY = true;
|
|
71
73
|
const sizeMatch = process.env.PLAYWRIGHT_FORCE_TTY.match(/^(\d+)x(\d+)$/);
|
|
@@ -92,7 +94,9 @@ const terminalScreen = (() => {
|
|
|
92
94
|
isTTY,
|
|
93
95
|
ttyWidth,
|
|
94
96
|
ttyHeight,
|
|
95
|
-
colors
|
|
97
|
+
colors,
|
|
98
|
+
stdout: originalProcessStdout,
|
|
99
|
+
stderr: originalProcessStderr
|
|
96
100
|
};
|
|
97
101
|
})();
|
|
98
102
|
const nonTerminalScreen = {
|
|
@@ -260,22 +264,22 @@ class TerminalReporter {
|
|
|
260
264
|
this._printSummary(summaryMessage);
|
|
261
265
|
}
|
|
262
266
|
_printFailures(failures) {
|
|
263
|
-
|
|
267
|
+
this.writeLine("");
|
|
264
268
|
failures.forEach((test, index) => {
|
|
265
|
-
|
|
269
|
+
this.writeLine(this.formatFailure(test, index + 1));
|
|
266
270
|
});
|
|
267
271
|
}
|
|
268
272
|
_printSlowTests() {
|
|
269
273
|
const slowTests = this.getSlowTests();
|
|
270
274
|
slowTests.forEach(([file, duration]) => {
|
|
271
|
-
|
|
275
|
+
this.writeLine(this.screen.colors.yellow(" Slow test file: ") + file + this.screen.colors.yellow(` (${(0, import_utilsBundle.ms)(duration)})`));
|
|
272
276
|
});
|
|
273
277
|
if (slowTests.length)
|
|
274
|
-
|
|
278
|
+
this.writeLine(this.screen.colors.yellow(" Consider running tests from slow files in parallel. See: https://playwright.dev/docs/test-parallel"));
|
|
275
279
|
}
|
|
276
280
|
_printSummary(summary) {
|
|
277
281
|
if (summary.trim())
|
|
278
|
-
|
|
282
|
+
this.writeLine(summary);
|
|
279
283
|
}
|
|
280
284
|
willRetry(test) {
|
|
281
285
|
return test.outcome() === "unexpected" && test.results.length <= test.retries;
|
|
@@ -292,6 +296,9 @@ class TerminalReporter {
|
|
|
292
296
|
formatError(error) {
|
|
293
297
|
return formatError(this.screen, error);
|
|
294
298
|
}
|
|
299
|
+
writeLine(line) {
|
|
300
|
+
this.screen.stdout?.write(line ? line + "\n" : "\n");
|
|
301
|
+
}
|
|
295
302
|
}
|
|
296
303
|
function formatFailure(screen, config, test, index) {
|
|
297
304
|
const lines = [];
|
package/lib/reporters/dot.js
CHANGED
|
@@ -29,53 +29,53 @@ class DotReporter extends import_base.TerminalReporter {
|
|
|
29
29
|
}
|
|
30
30
|
onBegin(suite) {
|
|
31
31
|
super.onBegin(suite);
|
|
32
|
-
|
|
32
|
+
this.writeLine(this.generateStartingMessage());
|
|
33
33
|
}
|
|
34
34
|
onStdOut(chunk, test, result) {
|
|
35
35
|
super.onStdOut(chunk, test, result);
|
|
36
36
|
if (!this.config.quiet)
|
|
37
|
-
|
|
37
|
+
this.screen.stdout.write(chunk);
|
|
38
38
|
}
|
|
39
39
|
onStdErr(chunk, test, result) {
|
|
40
40
|
super.onStdErr(chunk, test, result);
|
|
41
41
|
if (!this.config.quiet)
|
|
42
|
-
|
|
42
|
+
this.screen.stderr.write(chunk);
|
|
43
43
|
}
|
|
44
44
|
onTestEnd(test, result) {
|
|
45
45
|
super.onTestEnd(test, result);
|
|
46
46
|
if (this._counter === 80) {
|
|
47
|
-
|
|
47
|
+
this.screen.stdout.write("\n");
|
|
48
48
|
this._counter = 0;
|
|
49
49
|
}
|
|
50
50
|
++this._counter;
|
|
51
51
|
if (result.status === "skipped") {
|
|
52
|
-
|
|
52
|
+
this.screen.stdout.write(this.screen.colors.yellow("\xB0"));
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
if (this.willRetry(test)) {
|
|
56
|
-
|
|
56
|
+
this.screen.stdout.write(this.screen.colors.gray("\xD7"));
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
59
|
switch (test.outcome()) {
|
|
60
60
|
case "expected":
|
|
61
|
-
|
|
61
|
+
this.screen.stdout.write(this.screen.colors.green("\xB7"));
|
|
62
62
|
break;
|
|
63
63
|
case "unexpected":
|
|
64
|
-
|
|
64
|
+
this.screen.stdout.write(this.screen.colors.red(result.status === "timedOut" ? "T" : "F"));
|
|
65
65
|
break;
|
|
66
66
|
case "flaky":
|
|
67
|
-
|
|
67
|
+
this.screen.stdout.write(this.screen.colors.yellow("\xB1"));
|
|
68
68
|
break;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
onError(error) {
|
|
72
72
|
super.onError(error);
|
|
73
|
-
|
|
73
|
+
this.writeLine("\n" + this.formatError(error).message);
|
|
74
74
|
this._counter = 0;
|
|
75
75
|
}
|
|
76
76
|
async onEnd(result) {
|
|
77
77
|
await super.onEnd(result);
|
|
78
|
-
|
|
78
|
+
this.screen.stdout.write("\n");
|
|
79
79
|
this.epilogue(true);
|
|
80
80
|
}
|
|
81
81
|
}
|
package/lib/reporters/github.js
CHANGED
|
@@ -41,7 +41,8 @@ class GitHubLogger {
|
|
|
41
41
|
_log(message, type = "notice", options = {}) {
|
|
42
42
|
message = message.replace(/\n/g, "%0A");
|
|
43
43
|
const configs = Object.entries(options).map(([key, option]) => `${key}=${option}`).join(",");
|
|
44
|
-
|
|
44
|
+
process.stdout.write((0, import_util.stripAnsiEscapes)(`::${type} ${configs}::${message}
|
|
45
|
+
`));
|
|
45
46
|
}
|
|
46
47
|
debug(message, options) {
|
|
47
48
|
this._log(message, "debug", options);
|
package/lib/reporters/html.js
CHANGED
|
@@ -76,12 +76,12 @@ class HtmlReporter {
|
|
|
76
76
|
if (reportedWarnings.has(key))
|
|
77
77
|
continue;
|
|
78
78
|
reportedWarnings.add(key);
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
writeLine(import_utils2.colors.red(`Configuration Error: HTML reporter output folder clashes with the tests output folder:`));
|
|
80
|
+
writeLine(`
|
|
81
81
|
html reporter folder: ${import_utils2.colors.bold(outputFolder)}
|
|
82
82
|
test results folder: ${import_utils2.colors.bold(project.outputDir)}`);
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
writeLine("");
|
|
84
|
+
writeLine(`HTML reporter will clear its output directory prior to being generated, which will lead to the artifact loss.
|
|
85
85
|
`);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -128,9 +128,9 @@ class HtmlReporter {
|
|
|
128
128
|
const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? "" : " " + import_path.default.relative(process.cwd(), this._outputFolder);
|
|
129
129
|
const hostArg = this._host ? ` --host ${this._host}` : "";
|
|
130
130
|
const portArg = this._port ? ` --port ${this._port}` : "";
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
writeLine("");
|
|
132
|
+
writeLine("To open last HTML report run:");
|
|
133
|
+
writeLine(import_utils2.colors.cyan(`
|
|
134
134
|
${packageManagerCommand} playwright show-report${relativeReportPath}${hostArg}${portArg}
|
|
135
135
|
`));
|
|
136
136
|
}
|
|
@@ -145,7 +145,7 @@ function getHtmlReportOptionProcessEnv() {
|
|
|
145
145
|
if (!htmlOpenEnv)
|
|
146
146
|
return void 0;
|
|
147
147
|
if (!isHtmlReportOption(htmlOpenEnv)) {
|
|
148
|
-
|
|
148
|
+
writeLine(import_utils2.colors.red(`Configuration Error: HTML reporter Invalid value for PLAYWRIGHT_HTML_OPEN: ${htmlOpenEnv}. Valid values are: ${htmlReportOptions.join(", ")}`));
|
|
149
149
|
return void 0;
|
|
150
150
|
}
|
|
151
151
|
return htmlOpenEnv;
|
|
@@ -158,15 +158,15 @@ async function showHTMLReport(reportFolder, host = "localhost", port, testId) {
|
|
|
158
158
|
try {
|
|
159
159
|
(0, import_utils.assert)(import_fs.default.statSync(folder).isDirectory());
|
|
160
160
|
} catch (e) {
|
|
161
|
-
|
|
161
|
+
writeLine(import_utils2.colors.red(`No report found at "${folder}"`));
|
|
162
162
|
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
const server = startHtmlReportServer(folder);
|
|
166
166
|
await server.start({ port, host, preferredPort: port ? void 0 : 9323 });
|
|
167
167
|
let url = server.urlPrefix("human-readable");
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
writeLine("");
|
|
169
|
+
writeLine(import_utils2.colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
|
|
170
170
|
if (testId)
|
|
171
171
|
url += `#?testId=${testId}`;
|
|
172
172
|
url = url.replace("0.0.0.0", "localhost");
|
|
@@ -620,6 +620,9 @@ function createErrorCodeframe(message, location) {
|
|
|
620
620
|
}
|
|
621
621
|
);
|
|
622
622
|
}
|
|
623
|
+
function writeLine(line) {
|
|
624
|
+
process.stdout.write(line + "\n");
|
|
625
|
+
}
|
|
623
626
|
var html_default = HtmlReporter;
|
|
624
627
|
// Annotate the CommonJS export names for ESM import in node:
|
|
625
628
|
0 && (module.exports = {
|
|
@@ -37,10 +37,11 @@ var import_base = require("./base");
|
|
|
37
37
|
var import_multiplexer = require("./multiplexer");
|
|
38
38
|
var import_test = require("../common/test");
|
|
39
39
|
var import_babelBundle = require("../transform/babelBundle");
|
|
40
|
+
var import_reporterV2 = require("./reporterV2");
|
|
40
41
|
class InternalReporter {
|
|
41
42
|
constructor(reporters) {
|
|
42
43
|
this._didBegin = false;
|
|
43
|
-
this._reporter = new import_multiplexer.Multiplexer(reporters);
|
|
44
|
+
this._reporter = new import_multiplexer.Multiplexer(reporters.map(import_reporterV2.wrapReporterAsV2));
|
|
44
45
|
}
|
|
45
46
|
version() {
|
|
46
47
|
return "v2";
|
package/lib/reporters/line.js
CHANGED
|
@@ -33,18 +33,18 @@ class LineReporter extends import_base.TerminalReporter {
|
|
|
33
33
|
super.onBegin(suite);
|
|
34
34
|
const startingMessage = this.generateStartingMessage();
|
|
35
35
|
if (startingMessage) {
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
this.writeLine(startingMessage);
|
|
37
|
+
this.writeLine();
|
|
38
38
|
}
|
|
39
39
|
this._didBegin = true;
|
|
40
40
|
}
|
|
41
41
|
onStdOut(chunk, test, result) {
|
|
42
42
|
super.onStdOut(chunk, test, result);
|
|
43
|
-
this._dumpToStdio(test, chunk,
|
|
43
|
+
this._dumpToStdio(test, chunk, this.screen.stdout);
|
|
44
44
|
}
|
|
45
45
|
onStdErr(chunk, test, result) {
|
|
46
46
|
super.onStdErr(chunk, test, result);
|
|
47
|
-
this._dumpToStdio(test, chunk,
|
|
47
|
+
this._dumpToStdio(test, chunk, this.screen.stderr);
|
|
48
48
|
}
|
|
49
49
|
_dumpToStdio(test, chunk, stream) {
|
|
50
50
|
if (this.config.quiet)
|
|
@@ -59,8 +59,8 @@ class LineReporter extends import_base.TerminalReporter {
|
|
|
59
59
|
}
|
|
60
60
|
stream.write(chunk);
|
|
61
61
|
if (chunk[chunk.length - 1] !== "\n")
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
this.writeLine();
|
|
63
|
+
this.writeLine();
|
|
64
64
|
}
|
|
65
65
|
onTestBegin(test, result) {
|
|
66
66
|
++this._current;
|
|
@@ -78,9 +78,9 @@ class LineReporter extends import_base.TerminalReporter {
|
|
|
78
78
|
super.onTestEnd(test, result);
|
|
79
79
|
if (!this.willRetry(test) && (test.outcome() === "flaky" || test.outcome() === "unexpected" || result.status === "interrupted")) {
|
|
80
80
|
if (!process.env.PW_TEST_DEBUG_REPORTERS)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
this.screen.stdout.write(`\x1B[1A\x1B[2K`);
|
|
82
|
+
this.writeLine(this.formatFailure(test, ++this._failures));
|
|
83
|
+
this.writeLine();
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
_updateLine(test, result, step) {
|
|
@@ -89,23 +89,23 @@ class LineReporter extends import_base.TerminalReporter {
|
|
|
89
89
|
const currentRetrySuffix = result.retry ? this.screen.colors.yellow(` (retry #${result.retry})`) : "";
|
|
90
90
|
const title = this.formatTestTitle(test, step) + currentRetrySuffix;
|
|
91
91
|
if (process.env.PW_TEST_DEBUG_REPORTERS)
|
|
92
|
-
|
|
92
|
+
this.screen.stdout.write(`${prefix + title}
|
|
93
93
|
`);
|
|
94
94
|
else
|
|
95
|
-
|
|
95
|
+
this.screen.stdout.write(`\x1B[1A\x1B[2K${prefix + this.fitToScreen(title, prefix)}
|
|
96
96
|
`);
|
|
97
97
|
}
|
|
98
98
|
onError(error) {
|
|
99
99
|
super.onError(error);
|
|
100
100
|
const message = this.formatError(error).message + "\n";
|
|
101
101
|
if (!process.env.PW_TEST_DEBUG_REPORTERS && this._didBegin)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
this.screen.stdout.write(`\x1B[1A\x1B[2K`);
|
|
103
|
+
this.screen.stdout.write(message);
|
|
104
|
+
this.writeLine();
|
|
105
105
|
}
|
|
106
106
|
async onEnd(result) {
|
|
107
107
|
if (!process.env.PW_TEST_DEBUG_REPORTERS && this._didBegin)
|
|
108
|
-
|
|
108
|
+
this.screen.stdout.write(`\x1B[1A\x1B[2K`);
|
|
109
109
|
await super.onEnd(result);
|
|
110
110
|
this.epilogue(false);
|
|
111
111
|
}
|
package/lib/reporters/list.js
CHANGED
|
@@ -44,8 +44,8 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
44
44
|
super.onBegin(suite);
|
|
45
45
|
const startingMessage = this.generateStartingMessage();
|
|
46
46
|
if (startingMessage) {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
this.writeLine(startingMessage);
|
|
48
|
+
this.writeLine("");
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
onTestBegin(test, result) {
|
|
@@ -61,11 +61,11 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
61
61
|
}
|
|
62
62
|
onStdOut(chunk, test, result) {
|
|
63
63
|
super.onStdOut(chunk, test, result);
|
|
64
|
-
this._dumpToStdio(test, chunk,
|
|
64
|
+
this._dumpToStdio(test, chunk, this.screen.stdout);
|
|
65
65
|
}
|
|
66
66
|
onStdErr(chunk, test, result) {
|
|
67
67
|
super.onStdErr(chunk, test, result);
|
|
68
|
-
this._dumpToStdio(test, chunk,
|
|
68
|
+
this._dumpToStdio(test, chunk, this.screen.stderr);
|
|
69
69
|
}
|
|
70
70
|
getStepIndex(testIndex, result, step) {
|
|
71
71
|
if (this._stepIndex.has(step))
|
|
@@ -115,7 +115,7 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
115
115
|
_maybeWriteNewLine() {
|
|
116
116
|
if (this._needNewLine) {
|
|
117
117
|
this._needNewLine = false;
|
|
118
|
-
|
|
118
|
+
this.screen.stdout.write("\n");
|
|
119
119
|
++this._lastRow;
|
|
120
120
|
this._lastColumn = 0;
|
|
121
121
|
}
|
|
@@ -183,10 +183,10 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
183
183
|
_appendLine(text, prefix) {
|
|
184
184
|
const line = prefix + this.fitToScreen(text, prefix);
|
|
185
185
|
if (process.env.PW_TEST_DEBUG_REPORTERS) {
|
|
186
|
-
|
|
186
|
+
this.screen.stdout.write("#" + this._lastRow + " : " + line + "\n");
|
|
187
187
|
} else {
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
this.screen.stdout.write(line);
|
|
189
|
+
this.screen.stdout.write("\n");
|
|
190
190
|
}
|
|
191
191
|
++this._lastRow;
|
|
192
192
|
this._lastColumn = 0;
|
|
@@ -194,17 +194,17 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
194
194
|
_updateLine(row, text, prefix) {
|
|
195
195
|
const line = prefix + this.fitToScreen(text, prefix);
|
|
196
196
|
if (process.env.PW_TEST_DEBUG_REPORTERS)
|
|
197
|
-
|
|
197
|
+
this.screen.stdout.write("#" + row + " : " + line + "\n");
|
|
198
198
|
else
|
|
199
199
|
this._updateLineForTTY(row, line);
|
|
200
200
|
}
|
|
201
201
|
_updateLineForTTY(row, line) {
|
|
202
202
|
if (row !== this._lastRow)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
this.screen.stdout.write(`\x1B[${this._lastRow - row}A`);
|
|
204
|
+
this.screen.stdout.write("\x1B[2K\x1B[0G");
|
|
205
|
+
this.screen.stdout.write(line);
|
|
206
206
|
if (row !== this._lastRow)
|
|
207
|
-
|
|
207
|
+
this.screen.stdout.write(`\x1B[${this._lastRow - row}E`);
|
|
208
208
|
}
|
|
209
209
|
_testPrefix(index, statusMark) {
|
|
210
210
|
const statusMarkLength = (0, import_util.stripAnsiEscapes)(statusMark).length;
|
|
@@ -219,11 +219,11 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
219
219
|
this._maybeWriteNewLine();
|
|
220
220
|
const message = this.formatError(error).message + "\n";
|
|
221
221
|
this._updateLineCountAndNewLineFlagForOutput(message);
|
|
222
|
-
|
|
222
|
+
this.screen.stdout.write(message);
|
|
223
223
|
}
|
|
224
224
|
async onEnd(result) {
|
|
225
225
|
await super.onEnd(result);
|
|
226
|
-
|
|
226
|
+
this.screen.stdout.write("\n");
|
|
227
227
|
this.epilogue(true);
|
|
228
228
|
}
|
|
229
229
|
}
|