@playdrop/playdrop-cli 0.14.7 → 0.14.9
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.
- package/config/client-meta.json +2 -2
- package/dist/commands/worker.d.ts +1 -1
- package/dist/commands/worker.js +29 -8
- package/dist/index.js +1 -1
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playwright/mcp/LICENSE +201 -0
- package/node_modules/@playwright/mcp/README.md +1592 -0
- package/node_modules/@playwright/mcp/cli.js +32 -0
- package/node_modules/@playwright/mcp/config.d.ts +239 -0
- package/node_modules/@playwright/mcp/index.d.ts +23 -0
- package/node_modules/@playwright/mcp/index.js +19 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/LICENSE +202 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/NOTICE +5 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/README.md +318 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/ThirdPartyNotices.txt +14 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/cli.js +19 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/index.d.ts +17 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/index.js +17 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/index.mjs +18 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/jsx-runtime.js +42 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/jsx-runtime.mjs +21 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/agentParser.js +89 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/copilot-setup-steps.yml +34 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/generateAgents.js +395 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/playwright-test-coverage.prompt.md +31 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/playwright-test-generate.prompt.md +8 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/playwright-test-generator.agent.md +88 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/playwright-test-heal.prompt.md +6 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/playwright-test-healer.agent.md +56 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/playwright-test-plan.prompt.md +9 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/agents/playwright-test-planner.agent.md +74 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/cli/reportActions.js +78 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/cli/testActions.js +213 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/common/index.js +2966 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/common/index.js.txt +34 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/errorContext.js +129 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/globals.js +58 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/index.js +783 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/isomorphic.js +260 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/isomorphic.js.txt +9 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/loader/loaderProcessEntry.js +34 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/loader/loaderProcessEntry.js.txt +9 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/matchers/expect.js +13480 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/matchers/expect.js.LICENSE +640 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/matchers/expect.js.txt +67 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/browserBackend.js +125 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/generatorTools.js +122 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/plannerTools.js +150 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/seed.js +82 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/streams.js +44 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/testBackend.js +99 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/testContext.js +306 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/testTool.js +30 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/mcp/test/testTools.js +98 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/package.js +47 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/program.js +237 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/runner/index.js +8154 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/runner/index.js.txt +59 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/transform/babelBundle.js +72275 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +2384 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/transform/babelBundle.js.txt +325 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/transform/esmLoader.js +8060 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +355 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/transform/esmLoader.js.txt +60 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/util.js +373 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/worker/workerProcessEntry.js +1850 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +19 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/package.json +58 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/test.d.ts +18 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/test.js +24 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/test.mjs +35 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/types/test.d.ts +10709 -0
- package/node_modules/@playwright/mcp/node_modules/playwright/types/testReporter.d.ts +908 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/LICENSE +202 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/NOTICE +5 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/README.md +3 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/browsers.json +81 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/cli.js +21 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/index.d.ts +17 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/index.js +17 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/index.mjs +28 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/bootstrap.js +88 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/coreBundle.js +74495 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/entry/mcp.js +10 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/package.js +50 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/server/electron/loader.js +118 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/serverRegistry.js +7347 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +354 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/cli.js +6 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/help.json +707 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/output.js +343 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/program.js +404 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/registry.js +176 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/session.js +258 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/SKILL.md +420 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/element-attributes.md +23 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/playwright-tests.md +39 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/request-mocking.md +87 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/running-code.md +241 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/session-management.md +225 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/storage-state.md +275 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/test-generation.md +433 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/tracing.md +139 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/cli-client/skill/references/video-recording.md +143 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/trace/SKILL.md +171 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/utils/extension.js +78 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/utilsBundle.js +90764 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +2179 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-BVGIAA-i.js +32 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/recorder/assets/index-BQ57a3QC.js +129 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BSEO6m_i.js +32 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-CT5oRh4X.js +181 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-BN2nOuEV.js +7 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/index.DLSGjmWZ.js +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CZvydVOh.js +5 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/lib/xdg-open +1267 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/package.json +34 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/types/protocol.d.ts +24842 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/types/structs.d.ts +45 -0
- package/node_modules/@playwright/mcp/node_modules/playwright-core/types/types.d.ts +26088 -0
- package/node_modules/@playwright/mcp/package.json +51 -0
- package/package.json +3 -1
|
@@ -0,0 +1,1850 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// packages/playwright/src/worker/workerProcessEntry.ts
|
|
26
|
+
var import_common4 = require("../common");
|
|
27
|
+
|
|
28
|
+
// packages/playwright/src/worker/workerMain.ts
|
|
29
|
+
var import_common3 = require("../common");
|
|
30
|
+
var globals = __toESM(require("../globals"));
|
|
31
|
+
var import_expect = require("../matchers/expect");
|
|
32
|
+
|
|
33
|
+
// packages/playwright/src/util.ts
|
|
34
|
+
var import_fs = __toESM(require("fs"));
|
|
35
|
+
var import_path = __toESM(require("path"));
|
|
36
|
+
var import_util = __toESM(require("util"));
|
|
37
|
+
var debug = require("playwright-core/lib/utilsBundle").debug;
|
|
38
|
+
var mime = require("playwright-core/lib/utilsBundle").mime;
|
|
39
|
+
var { minimatch } = require("playwright-core/lib/utilsBundle");
|
|
40
|
+
var { calculateSha1 } = require("playwright-core/lib/coreBundle").utils;
|
|
41
|
+
var { sanitizeForFilePath } = require("playwright-core/lib/coreBundle").utils;
|
|
42
|
+
var { isRegExp } = require("playwright-core/lib/coreBundle").iso;
|
|
43
|
+
var { stringifyStackFrames, filteredStackTrace } = require("playwright-core/lib/coreBundle").utils;
|
|
44
|
+
var { ansiRegex, isString, stripAnsiEscapes } = require("playwright-core/lib/coreBundle").iso;
|
|
45
|
+
function filterStackTrace(e) {
|
|
46
|
+
const name = e.name ? e.name + ": " : "";
|
|
47
|
+
const cause = e.cause instanceof Error ? filterStackTrace(e.cause) : void 0;
|
|
48
|
+
if (process.env.PWDEBUGIMPL)
|
|
49
|
+
return { message: name + e.message, stack: e.stack || "", cause };
|
|
50
|
+
const stackLines = stringifyStackFrames(filteredStackTrace(e.stack?.split("\n") || []));
|
|
51
|
+
return {
|
|
52
|
+
message: name + e.message,
|
|
53
|
+
stack: `${name}${e.message}${stackLines.map((line) => "\n" + line).join("")}`,
|
|
54
|
+
cause
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function serializeError(error) {
|
|
58
|
+
if (error instanceof Error)
|
|
59
|
+
return filterStackTrace(error);
|
|
60
|
+
return {
|
|
61
|
+
value: import_util.default.inspect(error)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function relativeFilePath(file) {
|
|
65
|
+
if (!import_path.default.isAbsolute(file))
|
|
66
|
+
return file;
|
|
67
|
+
return import_path.default.relative(process.cwd(), file);
|
|
68
|
+
}
|
|
69
|
+
function formatLocation(location) {
|
|
70
|
+
return relativeFilePath(location.file) + ":" + location.line + ":" + location.column;
|
|
71
|
+
}
|
|
72
|
+
var windowsFilesystemFriendlyLength = 60;
|
|
73
|
+
function addSuffixToFilePath(filePath, suffix) {
|
|
74
|
+
const ext = import_path.default.extname(filePath);
|
|
75
|
+
const base = filePath.substring(0, filePath.length - ext.length);
|
|
76
|
+
return base + suffix + ext;
|
|
77
|
+
}
|
|
78
|
+
function sanitizeFilePathBeforeExtension(filePath, ext) {
|
|
79
|
+
ext ??= import_path.default.extname(filePath);
|
|
80
|
+
const base = filePath.substring(0, filePath.length - ext.length);
|
|
81
|
+
return sanitizeForFilePath(base) + ext;
|
|
82
|
+
}
|
|
83
|
+
function getContainedPath(parentPath, subPath = "") {
|
|
84
|
+
const resolvedPath = import_path.default.resolve(parentPath, subPath);
|
|
85
|
+
if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + import_path.default.sep))
|
|
86
|
+
return resolvedPath;
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
var debugTest = debug("pw:test");
|
|
90
|
+
async function normalizeAndSaveAttachment(outputPath, name, options = {}) {
|
|
91
|
+
if (options.path === void 0 && options.body === void 0)
|
|
92
|
+
return { name, contentType: "text/plain" };
|
|
93
|
+
if ((options.path !== void 0 ? 1 : 0) + (options.body !== void 0 ? 1 : 0) !== 1)
|
|
94
|
+
throw new Error(`Exactly one of "path" and "body" must be specified`);
|
|
95
|
+
if (options.path !== void 0) {
|
|
96
|
+
const hash = calculateSha1(options.path);
|
|
97
|
+
if (!isString(name))
|
|
98
|
+
throw new Error('"name" should be string.');
|
|
99
|
+
const sanitizedNamePrefix = sanitizeForFilePath(name) + "-";
|
|
100
|
+
const dest = import_path.default.join(outputPath, "attachments", sanitizedNamePrefix + hash + import_path.default.extname(options.path));
|
|
101
|
+
await import_fs.default.promises.mkdir(import_path.default.dirname(dest), { recursive: true });
|
|
102
|
+
await import_fs.default.promises.copyFile(options.path, dest);
|
|
103
|
+
const contentType = options.contentType ?? (mime.getType(import_path.default.basename(options.path)) || "application/octet-stream");
|
|
104
|
+
return { name, contentType, path: dest };
|
|
105
|
+
} else {
|
|
106
|
+
const contentType = options.contentType ?? (typeof options.body === "string" ? "text/plain" : "application/octet-stream");
|
|
107
|
+
return { name, contentType, body: typeof options.body === "string" ? Buffer.from(options.body) : options.body };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// packages/playwright/src/worker/fixtureRunner.ts
|
|
112
|
+
var import_common = require("../common");
|
|
113
|
+
var { ManualPromise } = require("playwright-core/lib/coreBundle").iso;
|
|
114
|
+
var { escapeWithQuotes } = require("playwright-core/lib/coreBundle").iso;
|
|
115
|
+
var { filterStackFile } = require("playwright-core/lib/coreBundle").utils;
|
|
116
|
+
var Fixture = class {
|
|
117
|
+
constructor(runner, registration) {
|
|
118
|
+
this.failed = false;
|
|
119
|
+
this._deps = /* @__PURE__ */ new Set();
|
|
120
|
+
this._usages = /* @__PURE__ */ new Set();
|
|
121
|
+
this.runner = runner;
|
|
122
|
+
this.registration = registration;
|
|
123
|
+
this.value = null;
|
|
124
|
+
const isUserFixture = this.registration.location && filterStackFile(this.registration.location.file);
|
|
125
|
+
const title = this.registration.customTitle || this.registration.name;
|
|
126
|
+
const location = isUserFixture ? this.registration.location : void 0;
|
|
127
|
+
this._stepInfo = { title: `Fixture ${escapeWithQuotes(title, '"')}`, category: "fixture", location };
|
|
128
|
+
if (this.registration.box === "self")
|
|
129
|
+
this._stepInfo = void 0;
|
|
130
|
+
else if (this.registration.box)
|
|
131
|
+
this._stepInfo.group = isUserFixture ? "configuration" : "internal";
|
|
132
|
+
this._setupDescription = {
|
|
133
|
+
title,
|
|
134
|
+
phase: "setup",
|
|
135
|
+
location,
|
|
136
|
+
slot: this.registration.timeout !== void 0 ? {
|
|
137
|
+
timeout: this.registration.timeout,
|
|
138
|
+
elapsed: 0
|
|
139
|
+
} : this.registration.scope === "worker" ? {
|
|
140
|
+
timeout: this.runner.workerFixtureTimeout,
|
|
141
|
+
elapsed: 0
|
|
142
|
+
} : void 0
|
|
143
|
+
};
|
|
144
|
+
this._teardownDescription = { ...this._setupDescription, phase: "teardown" };
|
|
145
|
+
}
|
|
146
|
+
async setup(testInfo, runnable) {
|
|
147
|
+
this.runner.instanceForId.set(this.registration.id, this);
|
|
148
|
+
if (typeof this.registration.fn !== "function") {
|
|
149
|
+
this.value = this.registration.fn;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const run = () => testInfo._runWithTimeout({ ...runnable, fixture: this._setupDescription }, () => this._setupInternal(testInfo));
|
|
153
|
+
if (this._stepInfo)
|
|
154
|
+
await testInfo._runAsStep(this._stepInfo, run);
|
|
155
|
+
else
|
|
156
|
+
await run();
|
|
157
|
+
}
|
|
158
|
+
async _setupInternal(testInfo) {
|
|
159
|
+
const params = {};
|
|
160
|
+
for (const name of this.registration.deps) {
|
|
161
|
+
const registration = this.runner.pool.resolve(name, this.registration);
|
|
162
|
+
const dep = this.runner.instanceForId.get(registration.id);
|
|
163
|
+
if (!dep) {
|
|
164
|
+
this.failed = true;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
dep._usages.add(this);
|
|
168
|
+
this._deps.add(dep);
|
|
169
|
+
params[name] = dep.value;
|
|
170
|
+
if (dep.failed) {
|
|
171
|
+
this.failed = true;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
let called = false;
|
|
176
|
+
const useFuncStarted = new ManualPromise();
|
|
177
|
+
const useFunc = async (value) => {
|
|
178
|
+
if (called)
|
|
179
|
+
throw new Error(`Cannot provide fixture value for the second time`);
|
|
180
|
+
called = true;
|
|
181
|
+
this.value = value;
|
|
182
|
+
this._useFuncFinished = new ManualPromise();
|
|
183
|
+
useFuncStarted.resolve();
|
|
184
|
+
await this._useFuncFinished;
|
|
185
|
+
};
|
|
186
|
+
const workerInfo = { config: testInfo.config, parallelIndex: testInfo.parallelIndex, workerIndex: testInfo.workerIndex, project: testInfo.project };
|
|
187
|
+
const info = this.registration.scope === "worker" ? workerInfo : testInfo;
|
|
188
|
+
this._selfTeardownComplete = (async () => {
|
|
189
|
+
try {
|
|
190
|
+
await this.registration.fn(params, useFunc, info);
|
|
191
|
+
if (!useFuncStarted.isDone())
|
|
192
|
+
throw new Error(`use() was not called in fixture "${this.registration.name}"`);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
this.failed = true;
|
|
195
|
+
if (!useFuncStarted.isDone())
|
|
196
|
+
useFuncStarted.reject(error);
|
|
197
|
+
else
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
await useFuncStarted;
|
|
202
|
+
}
|
|
203
|
+
async teardown(testInfo, runnable) {
|
|
204
|
+
try {
|
|
205
|
+
const fixtureRunnable = { ...runnable, fixture: this._teardownDescription };
|
|
206
|
+
if (!testInfo._timeoutManager.isTimeExhaustedFor(fixtureRunnable)) {
|
|
207
|
+
const run = () => testInfo._runWithTimeout(fixtureRunnable, () => this._teardownInternal());
|
|
208
|
+
if (this._stepInfo)
|
|
209
|
+
await testInfo._runAsStep(this._stepInfo, run);
|
|
210
|
+
else
|
|
211
|
+
await run();
|
|
212
|
+
}
|
|
213
|
+
} finally {
|
|
214
|
+
for (const dep of this._deps)
|
|
215
|
+
dep._usages.delete(this);
|
|
216
|
+
this.runner.instanceForId.delete(this.registration.id);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async _teardownInternal() {
|
|
220
|
+
if (typeof this.registration.fn !== "function")
|
|
221
|
+
return;
|
|
222
|
+
if (this._usages.size !== 0) {
|
|
223
|
+
console.error("Internal error: fixture integrity at", this._teardownDescription.title);
|
|
224
|
+
this._usages.clear();
|
|
225
|
+
}
|
|
226
|
+
if (this._useFuncFinished) {
|
|
227
|
+
this._useFuncFinished.resolve();
|
|
228
|
+
this._useFuncFinished = void 0;
|
|
229
|
+
await this._selfTeardownComplete;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
_collectFixturesInTeardownOrder(scope, collector) {
|
|
233
|
+
if (this.registration.scope !== scope)
|
|
234
|
+
return;
|
|
235
|
+
for (const fixture of this._usages)
|
|
236
|
+
fixture._collectFixturesInTeardownOrder(scope, collector);
|
|
237
|
+
collector.add(this);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
var FixtureRunner = class {
|
|
241
|
+
constructor() {
|
|
242
|
+
this.testScopeClean = true;
|
|
243
|
+
this.instanceForId = /* @__PURE__ */ new Map();
|
|
244
|
+
this.workerFixtureTimeout = 0;
|
|
245
|
+
}
|
|
246
|
+
setPool(pool) {
|
|
247
|
+
if (!this.testScopeClean)
|
|
248
|
+
throw new Error("Did not teardown test scope");
|
|
249
|
+
if (this.pool && pool.digest !== this.pool.digest) {
|
|
250
|
+
throw new Error([
|
|
251
|
+
`Playwright detected inconsistent test.use() options.`,
|
|
252
|
+
`Most common mistakes that lead to this issue:`,
|
|
253
|
+
` - Calling test.use() outside of the test file, for example in a common helper.`,
|
|
254
|
+
` - One test file imports from another test file.`
|
|
255
|
+
].join("\n"));
|
|
256
|
+
}
|
|
257
|
+
this.pool = pool;
|
|
258
|
+
}
|
|
259
|
+
_collectFixturesInSetupOrder(registration, collector) {
|
|
260
|
+
if (collector.has(registration))
|
|
261
|
+
return;
|
|
262
|
+
for (const name of registration.deps) {
|
|
263
|
+
const dep = this.pool.resolve(name, registration);
|
|
264
|
+
this._collectFixturesInSetupOrder(dep, collector);
|
|
265
|
+
}
|
|
266
|
+
collector.add(registration);
|
|
267
|
+
}
|
|
268
|
+
async teardownScope(scope, testInfo, runnable) {
|
|
269
|
+
const allFixtures = Array.from(this.instanceForId.values()).reverse();
|
|
270
|
+
const collector = /* @__PURE__ */ new Set();
|
|
271
|
+
for (const fixture of allFixtures)
|
|
272
|
+
fixture._collectFixturesInTeardownOrder(scope, collector);
|
|
273
|
+
let firstError;
|
|
274
|
+
for (const fixture of collector) {
|
|
275
|
+
try {
|
|
276
|
+
await fixture.teardown(testInfo, runnable);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
firstError = firstError ?? error;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (scope === "test")
|
|
282
|
+
this.testScopeClean = true;
|
|
283
|
+
if (firstError)
|
|
284
|
+
throw firstError;
|
|
285
|
+
}
|
|
286
|
+
async resolveParametersForFunction(fn, testInfo, autoFixtures, runnable) {
|
|
287
|
+
const collector = /* @__PURE__ */ new Set();
|
|
288
|
+
const auto = [];
|
|
289
|
+
for (const registration of this.pool.autoFixtures()) {
|
|
290
|
+
let shouldRun = true;
|
|
291
|
+
if (autoFixtures === "all-hooks-only")
|
|
292
|
+
shouldRun = registration.scope === "worker" || registration.auto === "all-hooks-included";
|
|
293
|
+
else if (autoFixtures === "worker")
|
|
294
|
+
shouldRun = registration.scope === "worker";
|
|
295
|
+
if (shouldRun)
|
|
296
|
+
auto.push(registration);
|
|
297
|
+
}
|
|
298
|
+
auto.sort((r1, r2) => (r1.scope === "worker" ? 0 : 1) - (r2.scope === "worker" ? 0 : 1));
|
|
299
|
+
for (const registration of auto)
|
|
300
|
+
this._collectFixturesInSetupOrder(registration, collector);
|
|
301
|
+
const names = getRequiredFixtureNames(fn);
|
|
302
|
+
for (const name of names)
|
|
303
|
+
this._collectFixturesInSetupOrder(this.pool.resolve(name), collector);
|
|
304
|
+
for (const registration of collector)
|
|
305
|
+
await this._setupFixtureForRegistration(registration, testInfo, runnable);
|
|
306
|
+
const params = {};
|
|
307
|
+
for (const name of names) {
|
|
308
|
+
const registration = this.pool.resolve(name);
|
|
309
|
+
const fixture = this.instanceForId.get(registration.id);
|
|
310
|
+
if (!fixture || fixture.failed)
|
|
311
|
+
return null;
|
|
312
|
+
params[name] = fixture.value;
|
|
313
|
+
}
|
|
314
|
+
return { result: params };
|
|
315
|
+
}
|
|
316
|
+
async resolveParametersAndRunFunction(fn, testInfo, autoFixtures, runnable) {
|
|
317
|
+
const params = await this.resolveParametersForFunction(fn, testInfo, autoFixtures, runnable);
|
|
318
|
+
if (params === null) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
await testInfo._runWithTimeout(runnable, () => fn(params.result, testInfo));
|
|
322
|
+
}
|
|
323
|
+
async _setupFixtureForRegistration(registration, testInfo, runnable) {
|
|
324
|
+
if (registration.scope === "test")
|
|
325
|
+
this.testScopeClean = false;
|
|
326
|
+
let fixture = this.instanceForId.get(registration.id);
|
|
327
|
+
if (fixture)
|
|
328
|
+
return fixture;
|
|
329
|
+
fixture = new Fixture(this, registration);
|
|
330
|
+
await fixture.setup(testInfo, runnable);
|
|
331
|
+
return fixture;
|
|
332
|
+
}
|
|
333
|
+
dependsOnWorkerFixturesOnly(fn, location) {
|
|
334
|
+
const names = getRequiredFixtureNames(fn, location);
|
|
335
|
+
for (const name of names) {
|
|
336
|
+
const registration = this.pool.resolve(name);
|
|
337
|
+
if (registration.scope !== "worker")
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
function getRequiredFixtureNames(fn, location) {
|
|
344
|
+
return import_common.fixtures.fixtureParameterNames(fn, location ?? { file: "<unknown>", line: 1, column: 1 }, (e) => {
|
|
345
|
+
throw new Error(`${formatLocation(e.location)}: ${e.message}`);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// packages/playwright/src/worker/testInfo.ts
|
|
350
|
+
var import_fs3 = __toESM(require("fs"));
|
|
351
|
+
var import_path3 = __toESM(require("path"));
|
|
352
|
+
|
|
353
|
+
// packages/playwright/src/worker/timeoutManager.ts
|
|
354
|
+
var colors = require("playwright-core/lib/utilsBundle").colors;
|
|
355
|
+
var { ManualPromise: ManualPromise2 } = require("playwright-core/lib/coreBundle").iso;
|
|
356
|
+
var { monotonicTime } = require("playwright-core/lib/coreBundle").iso;
|
|
357
|
+
var kMaxDeadline = 2147483647;
|
|
358
|
+
var TimeoutManager = class {
|
|
359
|
+
constructor(timeout) {
|
|
360
|
+
this._ignoreTimeouts = false;
|
|
361
|
+
this._slow = false;
|
|
362
|
+
this._defaultSlot = { timeout, elapsed: 0 };
|
|
363
|
+
}
|
|
364
|
+
setIgnoreTimeouts(ignoreTimeouts) {
|
|
365
|
+
if (this._ignoreTimeouts === ignoreTimeouts)
|
|
366
|
+
return;
|
|
367
|
+
this._ignoreTimeouts = ignoreTimeouts;
|
|
368
|
+
if (this._running) {
|
|
369
|
+
if (ignoreTimeouts)
|
|
370
|
+
this._running.slot.elapsed += monotonicTime() - this._running.start;
|
|
371
|
+
else
|
|
372
|
+
this._running.start = monotonicTime();
|
|
373
|
+
this._updateTimeout(this._running);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
interrupt() {
|
|
377
|
+
if (this._running)
|
|
378
|
+
this._running.timeoutPromise.reject(this._createTimeoutError(this._running));
|
|
379
|
+
}
|
|
380
|
+
isTimeExhaustedFor(runnable) {
|
|
381
|
+
const slot = runnable.fixture?.slot || runnable.slot || this._defaultSlot;
|
|
382
|
+
return slot.timeout > 0 && slot.elapsed >= slot.timeout - 1;
|
|
383
|
+
}
|
|
384
|
+
async withRunnable(runnable, cb) {
|
|
385
|
+
if (this._running)
|
|
386
|
+
throw new Error(`Internal error: duplicate runnable`);
|
|
387
|
+
const running = this._running = {
|
|
388
|
+
runnable,
|
|
389
|
+
slot: runnable.fixture?.slot || runnable.slot || this._defaultSlot,
|
|
390
|
+
start: monotonicTime(),
|
|
391
|
+
deadline: kMaxDeadline,
|
|
392
|
+
timer: void 0,
|
|
393
|
+
timeoutPromise: new ManualPromise2()
|
|
394
|
+
};
|
|
395
|
+
let debugTitle = "";
|
|
396
|
+
try {
|
|
397
|
+
if (debugTest.enabled) {
|
|
398
|
+
debugTitle = runnable.fixture ? `${runnable.fixture.phase} "${runnable.fixture.title}"` : runnable.type;
|
|
399
|
+
const location = runnable.location ? ` at "${formatLocation(runnable.location)}"` : ``;
|
|
400
|
+
debugTest(`started ${debugTitle}${location}`);
|
|
401
|
+
}
|
|
402
|
+
this._updateTimeout(running);
|
|
403
|
+
return await Promise.race([
|
|
404
|
+
cb(),
|
|
405
|
+
running.timeoutPromise
|
|
406
|
+
]);
|
|
407
|
+
} finally {
|
|
408
|
+
if (running.timer)
|
|
409
|
+
clearTimeout(running.timer);
|
|
410
|
+
running.timer = void 0;
|
|
411
|
+
running.slot.elapsed += monotonicTime() - running.start;
|
|
412
|
+
this._running = void 0;
|
|
413
|
+
if (debugTest.enabled)
|
|
414
|
+
debugTest(`finished ${debugTitle}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
_updateTimeout(running) {
|
|
418
|
+
if (running.timer)
|
|
419
|
+
clearTimeout(running.timer);
|
|
420
|
+
running.timer = void 0;
|
|
421
|
+
if (this._ignoreTimeouts || !running.slot.timeout) {
|
|
422
|
+
running.deadline = kMaxDeadline;
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
running.deadline = running.start + (running.slot.timeout - running.slot.elapsed);
|
|
426
|
+
const timeout = running.deadline - monotonicTime() + 1;
|
|
427
|
+
if (timeout <= 0)
|
|
428
|
+
running.timeoutPromise.reject(this._createTimeoutError(running));
|
|
429
|
+
else
|
|
430
|
+
running.timer = setTimeout(() => running.timeoutPromise.reject(this._createTimeoutError(running)), timeout);
|
|
431
|
+
}
|
|
432
|
+
defaultSlot() {
|
|
433
|
+
return this._defaultSlot;
|
|
434
|
+
}
|
|
435
|
+
slow() {
|
|
436
|
+
if (this._slow)
|
|
437
|
+
return;
|
|
438
|
+
this._slow = true;
|
|
439
|
+
const slot = this._running ? this._running.slot : this._defaultSlot;
|
|
440
|
+
slot.timeout = slot.timeout * 3;
|
|
441
|
+
if (this._running)
|
|
442
|
+
this._updateTimeout(this._running);
|
|
443
|
+
}
|
|
444
|
+
setTimeout(timeout) {
|
|
445
|
+
const slot = this._running ? this._running.slot : this._defaultSlot;
|
|
446
|
+
slot.timeout = timeout;
|
|
447
|
+
if (this._running)
|
|
448
|
+
this._updateTimeout(this._running);
|
|
449
|
+
}
|
|
450
|
+
currentSlotDeadline() {
|
|
451
|
+
return this._running ? this._running.deadline : kMaxDeadline;
|
|
452
|
+
}
|
|
453
|
+
currentSlotType() {
|
|
454
|
+
return this._running ? this._running.runnable.type : "test";
|
|
455
|
+
}
|
|
456
|
+
_createTimeoutError(running) {
|
|
457
|
+
let message = "";
|
|
458
|
+
const timeout = running.slot.timeout;
|
|
459
|
+
const runnable = running.runnable;
|
|
460
|
+
switch (runnable.type) {
|
|
461
|
+
case "test": {
|
|
462
|
+
if (runnable.fixture) {
|
|
463
|
+
if (runnable.fixture.phase === "setup")
|
|
464
|
+
message = `Test timeout of ${timeout}ms exceeded while setting up "${runnable.fixture.title}".`;
|
|
465
|
+
else
|
|
466
|
+
message = `Tearing down "${runnable.fixture.title}" exceeded the test timeout of ${timeout}ms.`;
|
|
467
|
+
} else {
|
|
468
|
+
message = `Test timeout of ${timeout}ms exceeded.`;
|
|
469
|
+
}
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
case "afterEach":
|
|
473
|
+
case "beforeEach":
|
|
474
|
+
message = `Test timeout of ${timeout}ms exceeded while running "${runnable.type}" hook.`;
|
|
475
|
+
break;
|
|
476
|
+
case "beforeAll":
|
|
477
|
+
case "afterAll":
|
|
478
|
+
message = `"${runnable.type}" hook timeout of ${timeout}ms exceeded.`;
|
|
479
|
+
break;
|
|
480
|
+
case "teardown": {
|
|
481
|
+
if (runnable.fixture)
|
|
482
|
+
message = `Worker teardown timeout of ${timeout}ms exceeded while ${runnable.fixture.phase === "setup" ? "setting up" : "tearing down"} "${runnable.fixture.title}".`;
|
|
483
|
+
else
|
|
484
|
+
message = `Worker teardown timeout of ${timeout}ms exceeded.`;
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
case "skip":
|
|
488
|
+
case "slow":
|
|
489
|
+
case "fixme":
|
|
490
|
+
case "fail":
|
|
491
|
+
message = `"${runnable.type}" modifier timeout of ${timeout}ms exceeded.`;
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
const fixtureWithSlot = runnable.fixture?.slot ? runnable.fixture : void 0;
|
|
495
|
+
if (fixtureWithSlot)
|
|
496
|
+
message = `Fixture "${fixtureWithSlot.title}" timeout of ${timeout}ms exceeded during ${fixtureWithSlot.phase}.`;
|
|
497
|
+
message = colors.red(message);
|
|
498
|
+
const location = (fixtureWithSlot || runnable).location;
|
|
499
|
+
const error = new TimeoutManagerError(message);
|
|
500
|
+
error.name = "";
|
|
501
|
+
error.stack = message + (location ? `
|
|
502
|
+
at ${location.file}:${location.line}:${location.column}` : "");
|
|
503
|
+
return error;
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
var TimeoutManagerError = class extends Error {
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// packages/playwright/src/worker/testTracing.ts
|
|
510
|
+
var import_fs2 = __toESM(require("fs"));
|
|
511
|
+
var import_path2 = __toESM(require("path"));
|
|
512
|
+
var import_coreBundle = require("playwright-core/lib/coreBundle");
|
|
513
|
+
var yazl = require("playwright-core/lib/utilsBundle").yazl;
|
|
514
|
+
var yauzl = require("playwright-core/lib/utilsBundle").yauzl;
|
|
515
|
+
var { ManualPromise: ManualPromise3 } = require("playwright-core/lib/coreBundle").iso;
|
|
516
|
+
var { monotonicTime: monotonicTime2 } = require("playwright-core/lib/coreBundle").iso;
|
|
517
|
+
var { calculateSha1: calculateSha12, createGuid } = require("playwright-core/lib/coreBundle").utils;
|
|
518
|
+
var { SerializedFS } = require("playwright-core/lib/coreBundle").utils;
|
|
519
|
+
var { filteredStackTrace: filteredStackTrace2 } = require("playwright-core/lib/coreBundle").utils;
|
|
520
|
+
var testTraceEntryName = "test.trace";
|
|
521
|
+
var version = 8;
|
|
522
|
+
var traceOrdinal = 0;
|
|
523
|
+
var TestTracing = class {
|
|
524
|
+
constructor(testInfo, artifactsDir) {
|
|
525
|
+
this._traceEvents = [];
|
|
526
|
+
this._temporaryTraceFiles = [];
|
|
527
|
+
this._didFinishTestFunctionAndAfterEachHooks = false;
|
|
528
|
+
this._testInfo = testInfo;
|
|
529
|
+
this._artifactsDir = artifactsDir;
|
|
530
|
+
this._tracesDir = import_path2.default.join(this._artifactsDir, "traces");
|
|
531
|
+
this._contextCreatedEvent = {
|
|
532
|
+
version,
|
|
533
|
+
type: "context-options",
|
|
534
|
+
origin: "testRunner",
|
|
535
|
+
browserName: "",
|
|
536
|
+
playwrightVersion: (0, import_coreBundle.getPlaywrightVersion)(),
|
|
537
|
+
options: {},
|
|
538
|
+
platform: process.platform,
|
|
539
|
+
wallTime: Date.now(),
|
|
540
|
+
monotonicTime: monotonicTime2(),
|
|
541
|
+
sdkLanguage: "javascript"
|
|
542
|
+
};
|
|
543
|
+
this._appendTraceEvent(this._contextCreatedEvent);
|
|
544
|
+
}
|
|
545
|
+
_shouldCaptureTrace() {
|
|
546
|
+
if (this._options?.mode === "on")
|
|
547
|
+
return true;
|
|
548
|
+
if (this._options?.mode === "retain-on-failure")
|
|
549
|
+
return true;
|
|
550
|
+
if (this._options?.mode === "on-first-retry" && this._testInfo.retry === 1)
|
|
551
|
+
return true;
|
|
552
|
+
if (this._options?.mode === "on-all-retries" && this._testInfo.retry > 0)
|
|
553
|
+
return true;
|
|
554
|
+
if (this._options?.mode === "retain-on-first-failure" && this._testInfo.retry === 0)
|
|
555
|
+
return true;
|
|
556
|
+
if (this._options?.mode === "retain-on-failure-and-retries")
|
|
557
|
+
return true;
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
async startIfNeeded(value) {
|
|
561
|
+
const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true, live: false, mode: "off" };
|
|
562
|
+
if (!value) {
|
|
563
|
+
this._options = defaultTraceOptions;
|
|
564
|
+
} else if (typeof value === "string") {
|
|
565
|
+
this._options = { ...defaultTraceOptions, mode: value === "retry-with-trace" ? "on-first-retry" : value };
|
|
566
|
+
} else {
|
|
567
|
+
const mode = value.mode || "off";
|
|
568
|
+
this._options = { ...defaultTraceOptions, ...value, mode: mode === "retry-with-trace" ? "on-first-retry" : mode };
|
|
569
|
+
}
|
|
570
|
+
if (!this._shouldCaptureTrace()) {
|
|
571
|
+
this._options = void 0;
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (!this._liveTraceFile && this._options.live) {
|
|
575
|
+
this._liveTraceFile = { file: import_path2.default.join(this._tracesDir, `${this._testInfo.testId}-test.trace`), fs: new SerializedFS() };
|
|
576
|
+
this._liveTraceFile.fs.mkdir(import_path2.default.dirname(this._liveTraceFile.file));
|
|
577
|
+
const data = this._traceEvents.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
578
|
+
this._liveTraceFile.fs.writeFile(this._liveTraceFile.file, data);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
didFinishTestFunctionAndAfterEachHooks() {
|
|
582
|
+
this._didFinishTestFunctionAndAfterEachHooks = true;
|
|
583
|
+
}
|
|
584
|
+
artifactsDir() {
|
|
585
|
+
return this._artifactsDir;
|
|
586
|
+
}
|
|
587
|
+
tracesDir() {
|
|
588
|
+
return this._tracesDir;
|
|
589
|
+
}
|
|
590
|
+
traceTitle() {
|
|
591
|
+
return [import_path2.default.relative(this._testInfo.project.testDir, this._testInfo.file) + ":" + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(" \u203A ");
|
|
592
|
+
}
|
|
593
|
+
generateNextTraceRecordingName() {
|
|
594
|
+
const ordinalSuffix = traceOrdinal ? `-recording${traceOrdinal}` : "";
|
|
595
|
+
++traceOrdinal;
|
|
596
|
+
const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : "";
|
|
597
|
+
return `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`;
|
|
598
|
+
}
|
|
599
|
+
_generateNextTraceRecordingPath() {
|
|
600
|
+
const file = import_path2.default.join(this._artifactsDir, createGuid() + ".zip");
|
|
601
|
+
this._temporaryTraceFiles.push(file);
|
|
602
|
+
return file;
|
|
603
|
+
}
|
|
604
|
+
traceOptions() {
|
|
605
|
+
return this._options;
|
|
606
|
+
}
|
|
607
|
+
maybeGenerateNextTraceRecordingPath() {
|
|
608
|
+
if (this._didFinishTestFunctionAndAfterEachHooks && this._shouldAbandonTrace())
|
|
609
|
+
return;
|
|
610
|
+
return this._generateNextTraceRecordingPath();
|
|
611
|
+
}
|
|
612
|
+
_shouldAbandonTrace() {
|
|
613
|
+
if (!this._options)
|
|
614
|
+
return true;
|
|
615
|
+
const testFailed = this._testInfo.status !== this._testInfo.expectedStatus;
|
|
616
|
+
if (this._options.mode === "retain-on-failure-and-retries")
|
|
617
|
+
return !testFailed && this._testInfo.retry === 0;
|
|
618
|
+
return !testFailed && (this._options.mode === "retain-on-failure" || this._options.mode === "retain-on-first-failure");
|
|
619
|
+
}
|
|
620
|
+
async stopIfNeeded() {
|
|
621
|
+
this._contextCreatedEvent.testTimeout = this._testInfo.timeout;
|
|
622
|
+
if (!this._options)
|
|
623
|
+
return;
|
|
624
|
+
const error = await this._liveTraceFile?.fs.syncAndGetError();
|
|
625
|
+
if (error)
|
|
626
|
+
throw error;
|
|
627
|
+
if (this._shouldAbandonTrace()) {
|
|
628
|
+
for (const file of this._temporaryTraceFiles)
|
|
629
|
+
await import_fs2.default.promises.unlink(file).catch(() => {
|
|
630
|
+
});
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const zipFile = new yazl.ZipFile();
|
|
634
|
+
if (!this._options?.attachments) {
|
|
635
|
+
for (const event of this._traceEvents) {
|
|
636
|
+
if (event.type === "after")
|
|
637
|
+
delete event.attachments;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (this._options?.sources) {
|
|
641
|
+
const sourceFiles = /* @__PURE__ */ new Set();
|
|
642
|
+
for (const event of this._traceEvents) {
|
|
643
|
+
if (event.type === "before") {
|
|
644
|
+
for (const frame of event.stack || [])
|
|
645
|
+
sourceFiles.add(frame.file);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
for (const sourceFile of sourceFiles) {
|
|
649
|
+
await import_fs2.default.promises.readFile(sourceFile, "utf8").then((source) => {
|
|
650
|
+
zipFile.addBuffer(Buffer.from(source), "resources/src@" + calculateSha12(sourceFile) + ".txt");
|
|
651
|
+
}).catch(() => {
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const sha1s = /* @__PURE__ */ new Set();
|
|
656
|
+
for (const event of this._traceEvents.filter((e) => e.type === "after")) {
|
|
657
|
+
for (const attachment of event.attachments || []) {
|
|
658
|
+
let contentPromise;
|
|
659
|
+
if (attachment.path)
|
|
660
|
+
contentPromise = import_fs2.default.promises.readFile(attachment.path).catch(() => void 0);
|
|
661
|
+
else if (attachment.base64)
|
|
662
|
+
contentPromise = Promise.resolve(Buffer.from(attachment.base64, "base64"));
|
|
663
|
+
const content = await contentPromise;
|
|
664
|
+
if (content === void 0)
|
|
665
|
+
continue;
|
|
666
|
+
const sha1 = calculateSha12(content);
|
|
667
|
+
attachment.sha1 = sha1;
|
|
668
|
+
delete attachment.path;
|
|
669
|
+
delete attachment.base64;
|
|
670
|
+
if (sha1s.has(sha1))
|
|
671
|
+
continue;
|
|
672
|
+
sha1s.add(sha1);
|
|
673
|
+
zipFile.addBuffer(content, "resources/" + sha1);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const traceContent = Buffer.from(this._traceEvents.map((e) => JSON.stringify(e)).join("\n"));
|
|
677
|
+
zipFile.addBuffer(traceContent, testTraceEntryName);
|
|
678
|
+
await new Promise((resolve, reject) => {
|
|
679
|
+
zipFile.end(void 0, () => {
|
|
680
|
+
zipFile.outputStream.pipe(import_fs2.default.createWriteStream(this._generateNextTraceRecordingPath())).on("close", resolve).on("error", reject);
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
const tracePath = this._testInfo.outputPath("trace.zip");
|
|
684
|
+
await mergeTraceFiles(tracePath, this._temporaryTraceFiles);
|
|
685
|
+
this._testInfo.attachments.push({ name: "trace", path: tracePath, contentType: "application/zip" });
|
|
686
|
+
}
|
|
687
|
+
appendForError(error) {
|
|
688
|
+
const rawStack = error.stack?.split("\n") || [];
|
|
689
|
+
const stack = rawStack ? filteredStackTrace2(rawStack) : [];
|
|
690
|
+
this._appendTraceEvent({
|
|
691
|
+
type: "error",
|
|
692
|
+
message: this._formatError(error),
|
|
693
|
+
stack
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
_formatError(error) {
|
|
697
|
+
const parts = [error.message || String(error.value)];
|
|
698
|
+
if (error.cause)
|
|
699
|
+
parts.push("[cause]: " + this._formatError(error.cause));
|
|
700
|
+
return parts.join("\n");
|
|
701
|
+
}
|
|
702
|
+
appendStdioToTrace(type, chunk) {
|
|
703
|
+
this._appendTraceEvent({
|
|
704
|
+
type,
|
|
705
|
+
timestamp: monotonicTime2(),
|
|
706
|
+
text: typeof chunk === "string" ? chunk : void 0,
|
|
707
|
+
base64: typeof chunk === "string" ? void 0 : chunk.toString("base64")
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
appendBeforeActionForStep(options) {
|
|
711
|
+
this._appendTraceEvent({
|
|
712
|
+
type: "before",
|
|
713
|
+
callId: options.stepId,
|
|
714
|
+
stepId: options.stepId,
|
|
715
|
+
parentId: options.parentId,
|
|
716
|
+
startTime: monotonicTime2(),
|
|
717
|
+
class: "Test",
|
|
718
|
+
method: options.category,
|
|
719
|
+
title: options.title,
|
|
720
|
+
params: Object.fromEntries(Object.entries(options.params || {}).map(([name, value]) => [name, generatePreview(value)])),
|
|
721
|
+
stack: options.stack,
|
|
722
|
+
group: options.group
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
appendAfterActionForStep(callId, error, attachments = [], annotations) {
|
|
726
|
+
this._appendTraceEvent({
|
|
727
|
+
type: "after",
|
|
728
|
+
callId,
|
|
729
|
+
endTime: monotonicTime2(),
|
|
730
|
+
attachments: serializeAttachments(attachments),
|
|
731
|
+
annotations,
|
|
732
|
+
error
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
_appendTraceEvent(event) {
|
|
736
|
+
this._traceEvents.push(event);
|
|
737
|
+
if (this._liveTraceFile)
|
|
738
|
+
this._liveTraceFile.fs.appendFile(this._liveTraceFile.file, JSON.stringify(event) + "\n", true);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
function serializeAttachments(attachments) {
|
|
742
|
+
if (attachments.length === 0)
|
|
743
|
+
return void 0;
|
|
744
|
+
return attachments.filter((a) => a.name !== "trace").map((a) => {
|
|
745
|
+
return {
|
|
746
|
+
name: a.name,
|
|
747
|
+
contentType: a.contentType,
|
|
748
|
+
path: a.path,
|
|
749
|
+
base64: a.body?.toString("base64")
|
|
750
|
+
};
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
function generatePreview(value, visited = /* @__PURE__ */ new Set()) {
|
|
754
|
+
if (visited.has(value))
|
|
755
|
+
return "";
|
|
756
|
+
visited.add(value);
|
|
757
|
+
if (typeof value === "string")
|
|
758
|
+
return value;
|
|
759
|
+
if (typeof value === "number")
|
|
760
|
+
return value.toString();
|
|
761
|
+
if (typeof value === "boolean")
|
|
762
|
+
return value.toString();
|
|
763
|
+
if (value === null)
|
|
764
|
+
return "null";
|
|
765
|
+
if (value === void 0)
|
|
766
|
+
return "undefined";
|
|
767
|
+
if (Array.isArray(value))
|
|
768
|
+
return "[" + value.map((v) => generatePreview(v, visited)).join(", ") + "]";
|
|
769
|
+
if (typeof value === "object")
|
|
770
|
+
return "Object";
|
|
771
|
+
return String(value);
|
|
772
|
+
}
|
|
773
|
+
async function mergeTraceFiles(fileName, temporaryTraceFiles) {
|
|
774
|
+
temporaryTraceFiles = temporaryTraceFiles.filter((file) => import_fs2.default.existsSync(file));
|
|
775
|
+
if (temporaryTraceFiles.length === 1) {
|
|
776
|
+
await import_fs2.default.promises.rename(temporaryTraceFiles[0], fileName);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const mergePromise = new ManualPromise3();
|
|
780
|
+
const zipFile = new yazl.ZipFile();
|
|
781
|
+
const entryNames = /* @__PURE__ */ new Set();
|
|
782
|
+
zipFile.on("error", (error) => mergePromise.reject(error));
|
|
783
|
+
for (let i = temporaryTraceFiles.length - 1; i >= 0; --i) {
|
|
784
|
+
const tempFile = temporaryTraceFiles[i];
|
|
785
|
+
const promise = new ManualPromise3();
|
|
786
|
+
yauzl.open(tempFile, (err, inZipFile) => {
|
|
787
|
+
if (err) {
|
|
788
|
+
promise.reject(err);
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
let pendingEntries = inZipFile.entryCount;
|
|
792
|
+
inZipFile.on("entry", (entry) => {
|
|
793
|
+
let entryName = entry.fileName;
|
|
794
|
+
if (entry.fileName === testTraceEntryName) {
|
|
795
|
+
} else if (entry.fileName.match(/trace\.[a-z]*$/)) {
|
|
796
|
+
entryName = i + "-" + entry.fileName;
|
|
797
|
+
}
|
|
798
|
+
if (entryNames.has(entryName)) {
|
|
799
|
+
if (--pendingEntries === 0)
|
|
800
|
+
promise.resolve();
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
entryNames.add(entryName);
|
|
804
|
+
inZipFile.openReadStream(entry, (err2, readStream) => {
|
|
805
|
+
if (err2) {
|
|
806
|
+
promise.reject(err2);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
zipFile.addReadStream(readStream, entryName);
|
|
810
|
+
if (--pendingEntries === 0)
|
|
811
|
+
promise.resolve();
|
|
812
|
+
});
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
await promise;
|
|
816
|
+
}
|
|
817
|
+
zipFile.end(void 0, () => {
|
|
818
|
+
zipFile.outputStream.pipe(import_fs2.default.createWriteStream(fileName)).on("close", () => {
|
|
819
|
+
void Promise.all(temporaryTraceFiles.map((tempFile) => import_fs2.default.promises.unlink(tempFile))).then(() => {
|
|
820
|
+
mergePromise.resolve();
|
|
821
|
+
}).catch((error) => mergePromise.reject(error));
|
|
822
|
+
}).on("error", (error) => mergePromise.reject(error));
|
|
823
|
+
});
|
|
824
|
+
await mergePromise;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// packages/playwright/src/worker/util.ts
|
|
828
|
+
function testInfoError(error) {
|
|
829
|
+
const result = serializeError(error);
|
|
830
|
+
const matcherResult = error instanceof Error ? error.matcherResult : void 0;
|
|
831
|
+
if (matcherResult?.ariaSnapshot !== void 0)
|
|
832
|
+
result.errorContext = matcherResult.ariaSnapshot;
|
|
833
|
+
return result;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// packages/playwright/src/worker/testInfo.ts
|
|
837
|
+
var import_common2 = require("../common");
|
|
838
|
+
var { ManualPromise: ManualPromise4 } = require("playwright-core/lib/coreBundle").iso;
|
|
839
|
+
var { captureRawStack, stringifyStackFrames: stringifyStackFrames2, filteredStackTrace: filteredStackTrace3 } = require("playwright-core/lib/coreBundle").utils;
|
|
840
|
+
var { escapeWithQuotes: escapeWithQuotes2 } = require("playwright-core/lib/coreBundle").iso;
|
|
841
|
+
var { monotonicTime: monotonicTime3 } = require("playwright-core/lib/coreBundle").iso;
|
|
842
|
+
var { createGuid: createGuid2 } = require("playwright-core/lib/coreBundle").utils;
|
|
843
|
+
var { sanitizeForFilePath: sanitizeForFilePath2, trimLongString } = require("playwright-core/lib/coreBundle").utils;
|
|
844
|
+
var { currentZone } = require("playwright-core/lib/coreBundle").utils;
|
|
845
|
+
var emtpyTestInfoCallbacks = {
|
|
846
|
+
onStepBegin: () => {
|
|
847
|
+
},
|
|
848
|
+
onStepEnd: () => {
|
|
849
|
+
},
|
|
850
|
+
onAttach: () => {
|
|
851
|
+
},
|
|
852
|
+
onTestPaused: () => Promise.reject(new Error("TestInfoImpl not initialized"))
|
|
853
|
+
};
|
|
854
|
+
var TestInfoImpl = class {
|
|
855
|
+
constructor(configInternal, projectInternal, workerParams, test, retry, callbacks) {
|
|
856
|
+
this._snapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} };
|
|
857
|
+
this._ariaSnapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} };
|
|
858
|
+
this._interruptedPromise = new ManualPromise4();
|
|
859
|
+
this._lastStepId = 0;
|
|
860
|
+
this._steps = [];
|
|
861
|
+
this._stepMap = /* @__PURE__ */ new Map();
|
|
862
|
+
this._onDidFinishTestFunctionCallbacks = /* @__PURE__ */ new Set();
|
|
863
|
+
this._hasNonRetriableError = false;
|
|
864
|
+
this._hasUnhandledError = false;
|
|
865
|
+
this._allowSkips = false;
|
|
866
|
+
this.duration = 0;
|
|
867
|
+
this.annotations = [];
|
|
868
|
+
this.attachments = [];
|
|
869
|
+
this.status = "passed";
|
|
870
|
+
this.snapshotSuffix = "";
|
|
871
|
+
this.errors = [];
|
|
872
|
+
this._ignoreTimeoutsCounter = 0;
|
|
873
|
+
this.testId = test?.id ?? "";
|
|
874
|
+
this._callbacks = callbacks;
|
|
875
|
+
this._startTime = monotonicTime3();
|
|
876
|
+
this._startWallTime = Date.now();
|
|
877
|
+
this._requireFile = test?._requireFile ?? "";
|
|
878
|
+
this._uniqueSymbol = /* @__PURE__ */ Symbol("testInfoUniqueSymbol");
|
|
879
|
+
this._workerParams = workerParams;
|
|
880
|
+
this.repeatEachIndex = workerParams.repeatEachIndex;
|
|
881
|
+
this.retry = retry;
|
|
882
|
+
this.workerIndex = workerParams.workerIndex;
|
|
883
|
+
this.parallelIndex = workerParams.parallelIndex;
|
|
884
|
+
this._projectInternal = projectInternal;
|
|
885
|
+
this.project = projectInternal.project;
|
|
886
|
+
this._configInternal = configInternal;
|
|
887
|
+
this.config = configInternal.config;
|
|
888
|
+
this.title = test?.title ?? "";
|
|
889
|
+
this.titlePath = test?.titlePath() ?? [];
|
|
890
|
+
this.file = test?.location.file ?? "";
|
|
891
|
+
this.line = test?.location.line ?? 0;
|
|
892
|
+
this.column = test?.location.column ?? 0;
|
|
893
|
+
this.tags = test?.tags ?? [];
|
|
894
|
+
this.fn = test?.fn ?? (() => {
|
|
895
|
+
});
|
|
896
|
+
this.expectedStatus = test?.expectedStatus ?? "skipped";
|
|
897
|
+
this._timeoutManager = new TimeoutManager(this.project.timeout);
|
|
898
|
+
if (configInternal.configCLIOverrides.debug === "inspector")
|
|
899
|
+
this._setIgnoreTimeouts(true);
|
|
900
|
+
this.outputDir = (() => {
|
|
901
|
+
const relativeTestFilePath = import_path3.default.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|jsx|tsx|mjs|mts|cjs|cts)$/, ""));
|
|
902
|
+
const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === "win32" ? new RegExp("\\\\", "g") : new RegExp("/", "g"), "-");
|
|
903
|
+
const fullTitleWithoutSpec = this.titlePath.slice(1).join(" ");
|
|
904
|
+
let testOutputDir = trimLongString(sanitizedRelativePath + "-" + sanitizeForFilePath2(fullTitleWithoutSpec), windowsFilesystemFriendlyLength);
|
|
905
|
+
if (projectInternal.id)
|
|
906
|
+
testOutputDir += "-" + sanitizeForFilePath2(projectInternal.id);
|
|
907
|
+
if (this.retry)
|
|
908
|
+
testOutputDir += "-retry" + this.retry;
|
|
909
|
+
if (this.repeatEachIndex)
|
|
910
|
+
testOutputDir += "-repeat" + this.repeatEachIndex;
|
|
911
|
+
return import_path3.default.join(this.project.outputDir, testOutputDir);
|
|
912
|
+
})();
|
|
913
|
+
this.snapshotDir = (() => {
|
|
914
|
+
const relativeTestFilePath = import_path3.default.relative(this.project.testDir, this._requireFile);
|
|
915
|
+
return import_path3.default.join(this.project.snapshotDir, relativeTestFilePath + "-snapshots");
|
|
916
|
+
})();
|
|
917
|
+
this._attachmentsPush = this.attachments.push.bind(this.attachments);
|
|
918
|
+
const attachmentsPush = (...attachments) => {
|
|
919
|
+
for (const a of attachments)
|
|
920
|
+
this._attach(a, this._parentStep()?.stepId);
|
|
921
|
+
return this.attachments.length;
|
|
922
|
+
};
|
|
923
|
+
Object.defineProperty(this.attachments, "push", {
|
|
924
|
+
value: attachmentsPush,
|
|
925
|
+
writable: true,
|
|
926
|
+
enumerable: false,
|
|
927
|
+
configurable: true
|
|
928
|
+
});
|
|
929
|
+
this._tracing = new TestTracing(this, workerParams.artifactsDir);
|
|
930
|
+
this.skip = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("skip", location, args));
|
|
931
|
+
this.fixme = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("fixme", location, args));
|
|
932
|
+
this.fail = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("fail", location, args));
|
|
933
|
+
this.slow = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("slow", location, args));
|
|
934
|
+
}
|
|
935
|
+
get error() {
|
|
936
|
+
return this.errors[0];
|
|
937
|
+
}
|
|
938
|
+
set error(e) {
|
|
939
|
+
if (e === void 0)
|
|
940
|
+
throw new Error("Cannot assign testInfo.error undefined value!");
|
|
941
|
+
this.errors[0] = e;
|
|
942
|
+
}
|
|
943
|
+
get timeout() {
|
|
944
|
+
return this._timeoutManager.defaultSlot().timeout;
|
|
945
|
+
}
|
|
946
|
+
set timeout(timeout) {
|
|
947
|
+
}
|
|
948
|
+
_deadline() {
|
|
949
|
+
return { deadline: this._timeoutManager.currentSlotDeadline(), timeout: this.timeout };
|
|
950
|
+
}
|
|
951
|
+
_modifier(type, location, modifierArgs) {
|
|
952
|
+
if (typeof modifierArgs[1] === "function") {
|
|
953
|
+
throw new Error([
|
|
954
|
+
"It looks like you are calling test.skip() inside the test and pass a callback.",
|
|
955
|
+
"Pass a condition instead and optional description instead:",
|
|
956
|
+
`test('my test', async ({ page, isMobile }) => {`,
|
|
957
|
+
` test.skip(isMobile, 'This test is not applicable on mobile');`,
|
|
958
|
+
`});`
|
|
959
|
+
].join("\n"));
|
|
960
|
+
}
|
|
961
|
+
if (modifierArgs.length >= 1 && !modifierArgs[0])
|
|
962
|
+
return;
|
|
963
|
+
const description = modifierArgs[1];
|
|
964
|
+
this.annotations.push({ type, description, location });
|
|
965
|
+
if (type === "slow") {
|
|
966
|
+
this._timeoutManager.slow();
|
|
967
|
+
} else if (type === "skip" || type === "fixme") {
|
|
968
|
+
this.expectedStatus = "skipped";
|
|
969
|
+
throw new TestSkipError("Test is skipped: " + (description || ""));
|
|
970
|
+
} else if (type === "fail") {
|
|
971
|
+
if (this.expectedStatus !== "skipped")
|
|
972
|
+
this.expectedStatus = "failed";
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
_findLastPredefinedStep(steps) {
|
|
976
|
+
for (let i = steps.length - 1; i >= 0; i--) {
|
|
977
|
+
const child = this._findLastPredefinedStep(steps[i].steps);
|
|
978
|
+
if (child)
|
|
979
|
+
return child;
|
|
980
|
+
if ((steps[i].category === "hook" || steps[i].category === "fixture") && !steps[i].endWallTime)
|
|
981
|
+
return steps[i];
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
_parentStep() {
|
|
985
|
+
return currentZone().data("stepZone") ?? this._findLastPredefinedStep(this._steps);
|
|
986
|
+
}
|
|
987
|
+
_addStep(data, parentStep) {
|
|
988
|
+
const stepId = `${data.category}@${++this._lastStepId}`;
|
|
989
|
+
if (data.category === "hook" || data.category === "fixture") {
|
|
990
|
+
parentStep = this._findLastPredefinedStep(this._steps);
|
|
991
|
+
} else {
|
|
992
|
+
if (!parentStep)
|
|
993
|
+
parentStep = this._parentStep();
|
|
994
|
+
}
|
|
995
|
+
const filteredStack = filteredStackTrace3(captureRawStack());
|
|
996
|
+
let boxedStack = parentStep?.boxedStack;
|
|
997
|
+
let location = data.location;
|
|
998
|
+
if (!boxedStack && data.box) {
|
|
999
|
+
boxedStack = filteredStack.slice(1);
|
|
1000
|
+
location = location || boxedStack[0];
|
|
1001
|
+
}
|
|
1002
|
+
location = location || filteredStack[0];
|
|
1003
|
+
const step = {
|
|
1004
|
+
...data,
|
|
1005
|
+
stepId,
|
|
1006
|
+
group: parentStep?.group ?? data.group,
|
|
1007
|
+
boxedStack,
|
|
1008
|
+
location,
|
|
1009
|
+
steps: [],
|
|
1010
|
+
attachmentIndices: [],
|
|
1011
|
+
info: new TestStepInfoImpl(this, stepId, data.title, parentStep?.info),
|
|
1012
|
+
complete: (result) => {
|
|
1013
|
+
if (step.endWallTime)
|
|
1014
|
+
return;
|
|
1015
|
+
step.endWallTime = Date.now();
|
|
1016
|
+
if (result.attachments) {
|
|
1017
|
+
for (const attachment of result.attachments)
|
|
1018
|
+
this._attach(attachment, stepId);
|
|
1019
|
+
}
|
|
1020
|
+
if (result.error) {
|
|
1021
|
+
if (typeof result.error === "object" && !result.error?.[stepSymbol])
|
|
1022
|
+
result.error[stepSymbol] = step;
|
|
1023
|
+
const error = testInfoError(result.error);
|
|
1024
|
+
if (step.boxedStack)
|
|
1025
|
+
error.stack = `${error.message}
|
|
1026
|
+
${stringifyStackFrames2(step.boxedStack).join("\n")}`;
|
|
1027
|
+
step.error = error;
|
|
1028
|
+
}
|
|
1029
|
+
if (result.softError) {
|
|
1030
|
+
step.infectParentStepsWithError = true;
|
|
1031
|
+
this._failWithError(result.softError);
|
|
1032
|
+
}
|
|
1033
|
+
if (result.shouldNotRetryTest)
|
|
1034
|
+
this._hasNonRetriableError = true;
|
|
1035
|
+
if (!step.error) {
|
|
1036
|
+
for (const childStep of step.steps) {
|
|
1037
|
+
if (childStep.error && childStep.infectParentStepsWithError) {
|
|
1038
|
+
step.error = childStep.error;
|
|
1039
|
+
step.infectParentStepsWithError = true;
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (!step.group) {
|
|
1045
|
+
const payload = {
|
|
1046
|
+
testId: this.testId,
|
|
1047
|
+
stepId,
|
|
1048
|
+
wallTime: step.endWallTime,
|
|
1049
|
+
error: step.error ? import_common2.ipc.toTestInfoErrorPayload(step.error) : void 0,
|
|
1050
|
+
suggestedRebaseline: result.suggestedRebaseline,
|
|
1051
|
+
annotations: step.info.annotations
|
|
1052
|
+
};
|
|
1053
|
+
this._callbacks.onStepEnd(payload);
|
|
1054
|
+
}
|
|
1055
|
+
if (step.group !== "internal") {
|
|
1056
|
+
const errorForTrace = step.error ? { name: "", message: step.error.message || "", stack: step.error.stack } : void 0;
|
|
1057
|
+
const attachments = step.attachmentIndices.map((i) => this.attachments[i]);
|
|
1058
|
+
this._tracing.appendAfterActionForStep(stepId, errorForTrace, attachments, step.info.annotations);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
const parentStepList = parentStep ? parentStep.steps : this._steps;
|
|
1063
|
+
parentStepList.push(step);
|
|
1064
|
+
this._stepMap.set(stepId, step);
|
|
1065
|
+
if (!step.group) {
|
|
1066
|
+
const payload = {
|
|
1067
|
+
testId: this.testId,
|
|
1068
|
+
stepId,
|
|
1069
|
+
parentStepId: parentStep ? parentStep.stepId : void 0,
|
|
1070
|
+
title: step.title,
|
|
1071
|
+
category: step.category,
|
|
1072
|
+
wallTime: Date.now(),
|
|
1073
|
+
location: step.location
|
|
1074
|
+
};
|
|
1075
|
+
this._callbacks.onStepBegin(payload);
|
|
1076
|
+
}
|
|
1077
|
+
if (step.group !== "internal") {
|
|
1078
|
+
this._tracing.appendBeforeActionForStep({
|
|
1079
|
+
stepId,
|
|
1080
|
+
parentId: parentStep?.stepId,
|
|
1081
|
+
title: step.shortTitle ?? step.title,
|
|
1082
|
+
category: step.category,
|
|
1083
|
+
params: step.params,
|
|
1084
|
+
stack: step.location ? [step.location] : [],
|
|
1085
|
+
group: step.group
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
return step;
|
|
1089
|
+
}
|
|
1090
|
+
_abort(location, message) {
|
|
1091
|
+
this.annotations.push({ type: "abort", description: message, location });
|
|
1092
|
+
throw new TestAbortError("Test aborted" + (message ? ": " + message : ""));
|
|
1093
|
+
}
|
|
1094
|
+
_interrupt() {
|
|
1095
|
+
this._interruptedPromise.resolve();
|
|
1096
|
+
this._timeoutManager.interrupt();
|
|
1097
|
+
if (this.status === "passed")
|
|
1098
|
+
this.status = "interrupted";
|
|
1099
|
+
}
|
|
1100
|
+
_failWithError(root) {
|
|
1101
|
+
if (this.status === "passed" || this.status === "skipped")
|
|
1102
|
+
this.status = root instanceof TimeoutManagerError ? "timedOut" : "failed";
|
|
1103
|
+
const visit = (error) => {
|
|
1104
|
+
const serialized = testInfoError(error);
|
|
1105
|
+
const step = error === root && typeof error === "object" ? error?.[stepSymbol] : void 0;
|
|
1106
|
+
if (step && step.boxedStack)
|
|
1107
|
+
serialized.stack = `${error.name}: ${error.message}
|
|
1108
|
+
${stringifyStackFrames2(step.boxedStack).join("\n")}`;
|
|
1109
|
+
this.errors.push(serialized);
|
|
1110
|
+
this._tracing.appendForError(serialized);
|
|
1111
|
+
const children = error?.errors;
|
|
1112
|
+
if (Array.isArray(children)) {
|
|
1113
|
+
for (const child of children)
|
|
1114
|
+
visit(child);
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
visit(root);
|
|
1118
|
+
}
|
|
1119
|
+
async _runAsStep(stepInfo, cb) {
|
|
1120
|
+
const step = this._addStep(stepInfo);
|
|
1121
|
+
try {
|
|
1122
|
+
await cb();
|
|
1123
|
+
step.complete({});
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
step.complete({ error });
|
|
1126
|
+
throw error;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
async _runWithTimeout(runnable, cb) {
|
|
1130
|
+
try {
|
|
1131
|
+
await this._timeoutManager.withRunnable(runnable, async () => {
|
|
1132
|
+
try {
|
|
1133
|
+
await cb();
|
|
1134
|
+
} catch (e) {
|
|
1135
|
+
if (this._allowSkips && e instanceof TestSkipError) {
|
|
1136
|
+
if (this.status === "passed")
|
|
1137
|
+
this.status = "skipped";
|
|
1138
|
+
} else {
|
|
1139
|
+
this._failWithError(e);
|
|
1140
|
+
}
|
|
1141
|
+
throw e;
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
if (!this._interruptedPromise.isDone() && error instanceof TimeoutManagerError)
|
|
1146
|
+
this._failWithError(error);
|
|
1147
|
+
throw error;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
_isFailure() {
|
|
1151
|
+
return this.status !== "skipped" && this.status !== this.expectedStatus;
|
|
1152
|
+
}
|
|
1153
|
+
_currentHookType() {
|
|
1154
|
+
const type = this._timeoutManager.currentSlotType();
|
|
1155
|
+
return ["beforeAll", "afterAll", "beforeEach", "afterEach"].includes(type) ? type : void 0;
|
|
1156
|
+
}
|
|
1157
|
+
_setIgnoreTimeouts(ignoreTimeouts) {
|
|
1158
|
+
this._ignoreTimeoutsCounter += ignoreTimeouts ? 1 : -1;
|
|
1159
|
+
this._timeoutManager.setIgnoreTimeouts(this._ignoreTimeoutsCounter > 0);
|
|
1160
|
+
}
|
|
1161
|
+
async _didFinishTestFunction() {
|
|
1162
|
+
const shouldPause = this._workerParams.pauseAtEnd && !this._isFailure() || this._workerParams.pauseOnError && this._isFailure();
|
|
1163
|
+
if (shouldPause) {
|
|
1164
|
+
await Promise.race([
|
|
1165
|
+
this._callbacks.onTestPaused({ testId: this.testId, errors: this._isFailure() ? this.errors.map(import_common2.ipc.toTestInfoErrorPayload) : [], status: this.status }),
|
|
1166
|
+
this._interruptedPromise
|
|
1167
|
+
]);
|
|
1168
|
+
}
|
|
1169
|
+
for (const cb of this._onDidFinishTestFunctionCallbacks)
|
|
1170
|
+
await cb();
|
|
1171
|
+
}
|
|
1172
|
+
// ------------ TestInfo methods ------------
|
|
1173
|
+
async attach(name, options = {}) {
|
|
1174
|
+
const step = this._addStep({
|
|
1175
|
+
title: `Attach ${escapeWithQuotes2(name, '"')}`,
|
|
1176
|
+
category: "test.attach"
|
|
1177
|
+
});
|
|
1178
|
+
this._attach(
|
|
1179
|
+
await normalizeAndSaveAttachment(this.outputPath(), name, options),
|
|
1180
|
+
step.stepId
|
|
1181
|
+
);
|
|
1182
|
+
step.complete({});
|
|
1183
|
+
}
|
|
1184
|
+
_attach(attachment, stepId) {
|
|
1185
|
+
const index = this._attachmentsPush(attachment) - 1;
|
|
1186
|
+
let step = stepId ? this._stepMap.get(stepId) : void 0;
|
|
1187
|
+
if (!!step?.group)
|
|
1188
|
+
step = void 0;
|
|
1189
|
+
if (step) {
|
|
1190
|
+
step.attachmentIndices.push(index);
|
|
1191
|
+
} else {
|
|
1192
|
+
const stepId2 = `attach@${createGuid2()}`;
|
|
1193
|
+
this._tracing.appendBeforeActionForStep({ stepId: stepId2, title: `Attach ${escapeWithQuotes2(attachment.name, '"')}`, category: "test.attach", stack: [] });
|
|
1194
|
+
this._tracing.appendAfterActionForStep(stepId2, void 0, [attachment]);
|
|
1195
|
+
}
|
|
1196
|
+
this._callbacks.onAttach({
|
|
1197
|
+
testId: this.testId,
|
|
1198
|
+
name: attachment.name,
|
|
1199
|
+
contentType: attachment.contentType,
|
|
1200
|
+
path: attachment.path,
|
|
1201
|
+
body: attachment.body?.toString("base64"),
|
|
1202
|
+
stepId: step?.stepId
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
outputPath(...pathSegments) {
|
|
1206
|
+
const outputPath = this._getOutputPath(...pathSegments);
|
|
1207
|
+
import_fs3.default.mkdirSync(this.outputDir, { recursive: true });
|
|
1208
|
+
return outputPath;
|
|
1209
|
+
}
|
|
1210
|
+
_getOutputPath(...pathSegments) {
|
|
1211
|
+
const joinedPath = import_path3.default.join(...pathSegments);
|
|
1212
|
+
const outputPath = getContainedPath(this.outputDir, joinedPath);
|
|
1213
|
+
if (outputPath)
|
|
1214
|
+
return outputPath;
|
|
1215
|
+
throw new Error(`The outputPath is not allowed outside of the parent directory. Please fix the defined path.
|
|
1216
|
+
|
|
1217
|
+
outputPath: ${joinedPath}`);
|
|
1218
|
+
}
|
|
1219
|
+
_fsSanitizedTestName() {
|
|
1220
|
+
const fullTitleWithoutSpec = this.titlePath.slice(1).join(" ");
|
|
1221
|
+
return sanitizeForFilePath2(trimLongString(fullTitleWithoutSpec));
|
|
1222
|
+
}
|
|
1223
|
+
_resolveSnapshotPaths(kind, name, updateSnapshotIndex, anonymousExtension) {
|
|
1224
|
+
const snapshotNames = kind === "aria" ? this._ariaSnapshotNames : this._snapshotNames;
|
|
1225
|
+
const defaultExtensions = { "aria": ".aria.yml", "screenshot": ".png", "snapshot": ".txt" };
|
|
1226
|
+
const ariaAwareExtname = (filePath) => kind === "aria" && filePath.endsWith(".aria.yml") ? ".aria.yml" : import_path3.default.extname(filePath);
|
|
1227
|
+
let subPath;
|
|
1228
|
+
let ext;
|
|
1229
|
+
let relativeOutputPath;
|
|
1230
|
+
if (!name) {
|
|
1231
|
+
const index = snapshotNames.lastAnonymousSnapshotIndex + 1;
|
|
1232
|
+
if (updateSnapshotIndex === "updateSnapshotIndex")
|
|
1233
|
+
snapshotNames.lastAnonymousSnapshotIndex = index;
|
|
1234
|
+
const fullTitleWithoutSpec = [...this.titlePath.slice(1), index].join(" ");
|
|
1235
|
+
ext = anonymousExtension ?? defaultExtensions[kind];
|
|
1236
|
+
subPath = sanitizeFilePathBeforeExtension(trimLongString(fullTitleWithoutSpec) + ext, ext);
|
|
1237
|
+
relativeOutputPath = sanitizeFilePathBeforeExtension(trimLongString(fullTitleWithoutSpec, windowsFilesystemFriendlyLength) + ext, ext);
|
|
1238
|
+
} else {
|
|
1239
|
+
if (Array.isArray(name)) {
|
|
1240
|
+
subPath = import_path3.default.join(...name);
|
|
1241
|
+
relativeOutputPath = import_path3.default.join(...name);
|
|
1242
|
+
ext = ariaAwareExtname(subPath);
|
|
1243
|
+
} else {
|
|
1244
|
+
ext = ariaAwareExtname(name);
|
|
1245
|
+
subPath = sanitizeFilePathBeforeExtension(name, ext);
|
|
1246
|
+
relativeOutputPath = sanitizeFilePathBeforeExtension(trimLongString(name, windowsFilesystemFriendlyLength), ext);
|
|
1247
|
+
}
|
|
1248
|
+
const index = (snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] || 0) + 1;
|
|
1249
|
+
if (updateSnapshotIndex === "updateSnapshotIndex")
|
|
1250
|
+
snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] = index;
|
|
1251
|
+
if (index > 1)
|
|
1252
|
+
relativeOutputPath = addSuffixToFilePath(relativeOutputPath, `-${index - 1}`);
|
|
1253
|
+
}
|
|
1254
|
+
const legacyTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}";
|
|
1255
|
+
let template;
|
|
1256
|
+
if (kind === "screenshot") {
|
|
1257
|
+
template = this._projectInternal.expect?.toHaveScreenshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || legacyTemplate;
|
|
1258
|
+
} else if (kind === "aria") {
|
|
1259
|
+
const ariaDefaultTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}";
|
|
1260
|
+
template = this._projectInternal.expect?.toMatchAriaSnapshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || ariaDefaultTemplate;
|
|
1261
|
+
} else {
|
|
1262
|
+
template = this._projectInternal.snapshotPathTemplate || legacyTemplate;
|
|
1263
|
+
}
|
|
1264
|
+
const nameArgument = import_path3.default.join(import_path3.default.dirname(subPath), import_path3.default.basename(subPath, ext));
|
|
1265
|
+
const absoluteSnapshotPath = this._applyPathTemplate(template, nameArgument, ext);
|
|
1266
|
+
return { absoluteSnapshotPath, relativeOutputPath };
|
|
1267
|
+
}
|
|
1268
|
+
_applyPathTemplate(template, nameArgument, ext) {
|
|
1269
|
+
const relativeTestFilePath = import_path3.default.relative(this.project.testDir, this._requireFile);
|
|
1270
|
+
const parsedRelativeTestFilePath = import_path3.default.parse(relativeTestFilePath);
|
|
1271
|
+
const projectNamePathSegment = sanitizeForFilePath2(this.project.name);
|
|
1272
|
+
const snapshotPath = template.replace(/\{(.)?testDir\}/g, "$1" + this.project.testDir).replace(/\{(.)?snapshotDir\}/g, "$1" + this.project.snapshotDir).replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? "$1" + this.snapshotSuffix : "").replace(/\{(.)?testFileDir\}/g, "$1" + parsedRelativeTestFilePath.dir).replace(/\{(.)?platform\}/g, "$1" + process.platform).replace(/\{(.)?projectName\}/g, projectNamePathSegment ? "$1" + projectNamePathSegment : "").replace(/\{(.)?testName\}/g, "$1" + this._fsSanitizedTestName()).replace(/\{(.)?testFileBaseName\}/g, "$1" + parsedRelativeTestFilePath.name).replace(/\{(.)?testFileName\}/g, "$1" + parsedRelativeTestFilePath.base).replace(/\{(.)?testFilePath\}/g, "$1" + relativeTestFilePath).replace(/\{(.)?arg\}/g, "$1" + nameArgument).replace(/\{(.)?ext\}/g, ext ? "$1" + ext : "");
|
|
1273
|
+
return import_path3.default.normalize(import_path3.default.resolve(this._configInternal.configDir, snapshotPath));
|
|
1274
|
+
}
|
|
1275
|
+
snapshotPath(...args) {
|
|
1276
|
+
let name = args;
|
|
1277
|
+
let kind = "snapshot";
|
|
1278
|
+
const options = args[args.length - 1];
|
|
1279
|
+
if (options && typeof options === "object") {
|
|
1280
|
+
kind = options.kind ?? kind;
|
|
1281
|
+
name = args.slice(0, -1);
|
|
1282
|
+
}
|
|
1283
|
+
if (!["snapshot", "screenshot", "aria"].includes(kind))
|
|
1284
|
+
throw new Error(`testInfo.snapshotPath: unknown kind "${kind}", must be one of "snapshot", "screenshot" or "aria"`);
|
|
1285
|
+
return this._resolveSnapshotPaths(kind, name.length <= 1 ? name[0] : name, "dontUpdateSnapshotIndex").absoluteSnapshotPath;
|
|
1286
|
+
}
|
|
1287
|
+
setTimeout(timeout) {
|
|
1288
|
+
this._timeoutManager.setTimeout(timeout);
|
|
1289
|
+
}
|
|
1290
|
+
artifactsDir() {
|
|
1291
|
+
return this._workerParams.artifactsDir;
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
var TestStepInfoImpl = class {
|
|
1295
|
+
constructor(testInfo, stepId, title, parentStep) {
|
|
1296
|
+
this.annotations = [];
|
|
1297
|
+
this._testInfo = testInfo;
|
|
1298
|
+
this._stepId = stepId;
|
|
1299
|
+
this._title = title;
|
|
1300
|
+
this._parentStep = parentStep;
|
|
1301
|
+
this.skip = import_common2.transform.wrapFunctionWithLocation((location, ...args) => {
|
|
1302
|
+
if (args.length > 0 && !args[0])
|
|
1303
|
+
return;
|
|
1304
|
+
const description = args[1];
|
|
1305
|
+
this.annotations.push({ type: "skip", description, location });
|
|
1306
|
+
throw new StepSkipError(description);
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
async _runStepBody(skip, body, location) {
|
|
1310
|
+
if (skip) {
|
|
1311
|
+
this.annotations.push({ type: "skip", location });
|
|
1312
|
+
return void 0;
|
|
1313
|
+
}
|
|
1314
|
+
try {
|
|
1315
|
+
return await body(this);
|
|
1316
|
+
} catch (e) {
|
|
1317
|
+
if (e instanceof StepSkipError)
|
|
1318
|
+
return void 0;
|
|
1319
|
+
throw e;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
async attach(name, options) {
|
|
1323
|
+
this._testInfo._attach(await normalizeAndSaveAttachment(this._testInfo.outputPath(), name, options), this._stepId);
|
|
1324
|
+
}
|
|
1325
|
+
get titlePath() {
|
|
1326
|
+
const parent = this._parentStep ?? this._testInfo;
|
|
1327
|
+
return [...parent.titlePath, this._title];
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
var TestSkipError = class extends Error {
|
|
1331
|
+
};
|
|
1332
|
+
var TestAbortError = class extends Error {
|
|
1333
|
+
};
|
|
1334
|
+
var StepSkipError = class extends Error {
|
|
1335
|
+
};
|
|
1336
|
+
var stepSymbol = /* @__PURE__ */ Symbol("step");
|
|
1337
|
+
|
|
1338
|
+
// packages/playwright/src/worker/workerMain.ts
|
|
1339
|
+
var colors2 = require("playwright-core/lib/utilsBundle").colors;
|
|
1340
|
+
var { ManualPromise: ManualPromise5 } = require("playwright-core/lib/coreBundle").iso;
|
|
1341
|
+
var { removeFolders } = require("playwright-core/lib/coreBundle").utils;
|
|
1342
|
+
var { gracefullyCloseAll } = require("playwright-core/lib/coreBundle").utils;
|
|
1343
|
+
var { filteredStackTrace: filteredStackTrace4 } = require("playwright-core/lib/coreBundle").utils;
|
|
1344
|
+
var WorkerMain = class extends import_common3.ProcessRunner {
|
|
1345
|
+
constructor(params) {
|
|
1346
|
+
super();
|
|
1347
|
+
// Accumulated fatal errors that cannot be attributed to a test.
|
|
1348
|
+
this._fatalErrors = [];
|
|
1349
|
+
// The stage of the full cleanup. Once "finished", we can safely stop running anything.
|
|
1350
|
+
this._didRunFullCleanup = false;
|
|
1351
|
+
// Whether the worker was stopped due to an unhandled error in a test marked with test.fail().
|
|
1352
|
+
// This should force dispatcher to use a new worker instead.
|
|
1353
|
+
this._stoppedDueToUnhandledErrorInTestFail = false;
|
|
1354
|
+
// Whether the worker was requested to stop.
|
|
1355
|
+
this._isStopped = false;
|
|
1356
|
+
// This promise resolves once the single "run test group" call finishes.
|
|
1357
|
+
this._runFinished = new ManualPromise5();
|
|
1358
|
+
this._currentTest = null;
|
|
1359
|
+
this._lastRunningTests = [];
|
|
1360
|
+
this._totalRunningTests = 0;
|
|
1361
|
+
// Suites that had their beforeAll hooks, but not afterAll hooks executed.
|
|
1362
|
+
// These suites still need afterAll hooks to be executed for the proper cleanup.
|
|
1363
|
+
// Contains dynamic annotations originated by modifiers with a callback, e.g. `test.skip(() => true)`.
|
|
1364
|
+
this._activeSuites = /* @__PURE__ */ new Map();
|
|
1365
|
+
process.env.TEST_WORKER_INDEX = String(params.workerIndex);
|
|
1366
|
+
process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex);
|
|
1367
|
+
globals.setIsWorkerProcess();
|
|
1368
|
+
this._params = params;
|
|
1369
|
+
this._fixtureRunner = new FixtureRunner();
|
|
1370
|
+
this._runFinished.resolve();
|
|
1371
|
+
process.on("unhandledRejection", (reason) => this.unhandledError(reason));
|
|
1372
|
+
process.on("uncaughtException", (error) => this.unhandledError(error));
|
|
1373
|
+
process.stdout.write = (chunk, cb) => {
|
|
1374
|
+
this.dispatchEvent("stdOut", import_common3.ipc.stdioChunkToParams(chunk));
|
|
1375
|
+
this._currentTest?._tracing.appendStdioToTrace("stdout", chunk);
|
|
1376
|
+
if (typeof cb === "function")
|
|
1377
|
+
process.nextTick(cb);
|
|
1378
|
+
return true;
|
|
1379
|
+
};
|
|
1380
|
+
if (!process.env.PW_RUNNER_DEBUG) {
|
|
1381
|
+
process.stderr.write = (chunk, cb) => {
|
|
1382
|
+
this.dispatchEvent("stdErr", import_common3.ipc.stdioChunkToParams(chunk));
|
|
1383
|
+
this._currentTest?._tracing.appendStdioToTrace("stderr", chunk);
|
|
1384
|
+
if (typeof cb === "function")
|
|
1385
|
+
process.nextTick(cb);
|
|
1386
|
+
return true;
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
_stop() {
|
|
1391
|
+
if (!this._isStopped) {
|
|
1392
|
+
this._isStopped = true;
|
|
1393
|
+
this._currentTest?._interrupt();
|
|
1394
|
+
}
|
|
1395
|
+
return this._runFinished;
|
|
1396
|
+
}
|
|
1397
|
+
async gracefullyClose() {
|
|
1398
|
+
try {
|
|
1399
|
+
await this._stop();
|
|
1400
|
+
if (!this._config) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
const fakeTestInfo = new TestInfoImpl(this._config, this._project, this._params, void 0, 0, emtpyTestInfoCallbacks);
|
|
1404
|
+
const runnable = { type: "teardown" };
|
|
1405
|
+
await fakeTestInfo._runWithTimeout(runnable, () => this._loadIfNeeded()).catch(() => {
|
|
1406
|
+
});
|
|
1407
|
+
await this._fixtureRunner.teardownScope("test", fakeTestInfo, runnable).catch(() => {
|
|
1408
|
+
});
|
|
1409
|
+
await this._fixtureRunner.teardownScope("worker", fakeTestInfo, runnable).catch(() => {
|
|
1410
|
+
});
|
|
1411
|
+
await fakeTestInfo._runWithTimeout(runnable, () => gracefullyCloseAll()).catch(() => {
|
|
1412
|
+
});
|
|
1413
|
+
this._fatalErrors.push(...fakeTestInfo.errors);
|
|
1414
|
+
} catch (e) {
|
|
1415
|
+
this._fatalErrors.push(testInfoError(e));
|
|
1416
|
+
}
|
|
1417
|
+
if (this._fatalErrors.length) {
|
|
1418
|
+
this._appendProcessTeardownDiagnostics(this._fatalErrors[this._fatalErrors.length - 1]);
|
|
1419
|
+
const payload = { fatalErrors: this._fatalErrors.map(import_common3.ipc.toTestInfoErrorPayload) };
|
|
1420
|
+
this.dispatchEvent("teardownErrors", payload);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
_appendProcessTeardownDiagnostics(error) {
|
|
1424
|
+
if (!this._lastRunningTests.length)
|
|
1425
|
+
return;
|
|
1426
|
+
const count = this._totalRunningTests === 1 ? "1 test" : `${this._totalRunningTests} tests`;
|
|
1427
|
+
let lastMessage = "";
|
|
1428
|
+
if (this._lastRunningTests.length < this._totalRunningTests)
|
|
1429
|
+
lastMessage = `, last ${this._lastRunningTests.length} tests were`;
|
|
1430
|
+
const message = [
|
|
1431
|
+
"",
|
|
1432
|
+
"",
|
|
1433
|
+
colors2.red(`Failed worker ran ${count}${lastMessage}:`),
|
|
1434
|
+
...this._lastRunningTests.map((test) => formatTestTitle(test, this._project.project.name))
|
|
1435
|
+
].join("\n");
|
|
1436
|
+
if (error.message) {
|
|
1437
|
+
if (error.stack) {
|
|
1438
|
+
let index = error.stack.indexOf(error.message);
|
|
1439
|
+
if (index !== -1) {
|
|
1440
|
+
index += error.message.length;
|
|
1441
|
+
error.stack = error.stack.substring(0, index) + message + error.stack.substring(index);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
error.message += message;
|
|
1445
|
+
} else if (error.value) {
|
|
1446
|
+
error.value += message;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
unhandledError(error) {
|
|
1450
|
+
if (!this._currentTest) {
|
|
1451
|
+
if (!this._fatalErrors.length)
|
|
1452
|
+
this._fatalErrors.push(testInfoError(error));
|
|
1453
|
+
void this._stop();
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
if (!this._currentTest._hasUnhandledError) {
|
|
1457
|
+
this._currentTest._hasUnhandledError = true;
|
|
1458
|
+
this._currentTest._failWithError(error);
|
|
1459
|
+
}
|
|
1460
|
+
const isExpectError = error instanceof Error && !!error.matcherResult;
|
|
1461
|
+
const shouldContinueInThisWorker = this._currentTest.expectedStatus === "failed" && isExpectError;
|
|
1462
|
+
if (!shouldContinueInThisWorker) {
|
|
1463
|
+
this._stoppedDueToUnhandledErrorInTestFail = true;
|
|
1464
|
+
void this._stop();
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
async _loadIfNeeded() {
|
|
1468
|
+
if (this._config)
|
|
1469
|
+
return;
|
|
1470
|
+
const config = await import_common3.configLoader.deserializeConfig(this._params.config);
|
|
1471
|
+
const project = config.projects.find((p) => p.id === this._params.projectId);
|
|
1472
|
+
if (!project)
|
|
1473
|
+
throw new Error(`Project "${this._params.projectId}" not found in the worker process. Make sure project name does not change.`);
|
|
1474
|
+
this._config = config;
|
|
1475
|
+
this._project = project;
|
|
1476
|
+
this._poolBuilder = import_common3.poolBuilder.PoolBuilder.createForWorker(this._project);
|
|
1477
|
+
this._fixtureRunner.workerFixtureTimeout = this._project.project.timeout;
|
|
1478
|
+
}
|
|
1479
|
+
async runTestGroup(runPayload) {
|
|
1480
|
+
this._runFinished = new ManualPromise5();
|
|
1481
|
+
const entries = new Map(runPayload.entries.map((e) => [e.testId, e]));
|
|
1482
|
+
let fatalUnknownTestIds;
|
|
1483
|
+
try {
|
|
1484
|
+
await this._loadIfNeeded();
|
|
1485
|
+
const fileSuite = await import_common3.testLoader.loadTestFile(runPayload.file, this._config);
|
|
1486
|
+
const suite = import_common3.suiteUtils.bindFileSuiteToProject(this._project, fileSuite);
|
|
1487
|
+
if (this._params.repeatEachIndex)
|
|
1488
|
+
import_common3.suiteUtils.applyRepeatEachIndex(this._project, suite, this._params.repeatEachIndex);
|
|
1489
|
+
import_common3.suiteUtils.filterTestsRemoveEmptySuites(suite, (test) => entries.has(test.id));
|
|
1490
|
+
const tests = suite.allTests();
|
|
1491
|
+
for (const test of tests)
|
|
1492
|
+
test.annotations.push(...entries.get(test.id).planAnnotations);
|
|
1493
|
+
const unknownTestIds = new Set(entries.keys());
|
|
1494
|
+
for (const test of tests)
|
|
1495
|
+
unknownTestIds.delete(test.id);
|
|
1496
|
+
if (unknownTestIds.size) {
|
|
1497
|
+
fatalUnknownTestIds = [...unknownTestIds];
|
|
1498
|
+
void this._stop();
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
this._poolBuilder.buildPools(suite);
|
|
1502
|
+
this._activeSuites = /* @__PURE__ */ new Map();
|
|
1503
|
+
this._didRunFullCleanup = false;
|
|
1504
|
+
for (let i = 0; i < tests.length; i++) {
|
|
1505
|
+
if (this._isStopped && this._didRunFullCleanup)
|
|
1506
|
+
break;
|
|
1507
|
+
const entry = entries.get(tests[i].id);
|
|
1508
|
+
entries.delete(tests[i].id);
|
|
1509
|
+
debugTest(`test started "${tests[i].title}"`);
|
|
1510
|
+
await this._runTest(tests[i], entry.retry, tests[i + 1]);
|
|
1511
|
+
debugTest(`test finished "${tests[i].title}"`);
|
|
1512
|
+
}
|
|
1513
|
+
} catch (e) {
|
|
1514
|
+
this._fatalErrors.push(testInfoError(e));
|
|
1515
|
+
void this._stop();
|
|
1516
|
+
} finally {
|
|
1517
|
+
const donePayload = {
|
|
1518
|
+
fatalErrors: this._fatalErrors.map(import_common3.ipc.toTestInfoErrorPayload),
|
|
1519
|
+
skipTestsDueToSetupFailure: [],
|
|
1520
|
+
fatalUnknownTestIds,
|
|
1521
|
+
stoppedDueToUnhandledErrorInTestFail: this._stoppedDueToUnhandledErrorInTestFail
|
|
1522
|
+
};
|
|
1523
|
+
for (const test of this._skipRemainingTestsInSuite?.allTests() || []) {
|
|
1524
|
+
if (entries.has(test.id))
|
|
1525
|
+
donePayload.skipTestsDueToSetupFailure.push(test.id);
|
|
1526
|
+
}
|
|
1527
|
+
this.dispatchEvent("done", donePayload);
|
|
1528
|
+
this._fatalErrors = [];
|
|
1529
|
+
this._skipRemainingTestsInSuite = void 0;
|
|
1530
|
+
this._runFinished.resolve();
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
async customMessage(payload) {
|
|
1534
|
+
try {
|
|
1535
|
+
if (this._currentTest?.testId !== payload.testId)
|
|
1536
|
+
throw new Error("Test has already stopped");
|
|
1537
|
+
const response = await this._currentTest._onCustomMessageCallback?.(payload.request);
|
|
1538
|
+
return { response };
|
|
1539
|
+
} catch (error) {
|
|
1540
|
+
return { response: {}, error: import_common3.ipc.toTestInfoErrorPayload(testInfoError(error)) };
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
resume(payload) {
|
|
1544
|
+
this._resumePromise?.resolve(payload);
|
|
1545
|
+
}
|
|
1546
|
+
async _runTest(test, retry, nextTest) {
|
|
1547
|
+
const testInfo = new TestInfoImpl(this._config, this._project, this._params, test, retry, {
|
|
1548
|
+
onStepBegin: (payload) => this.dispatchEvent("stepBegin", payload),
|
|
1549
|
+
onStepEnd: (payload) => this.dispatchEvent("stepEnd", payload),
|
|
1550
|
+
onAttach: (payload) => this.dispatchEvent("attach", payload),
|
|
1551
|
+
onTestPaused: (payload) => {
|
|
1552
|
+
this._resumePromise = new ManualPromise5();
|
|
1553
|
+
this.dispatchEvent("testPaused", payload);
|
|
1554
|
+
return this._resumePromise;
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
const processAnnotation = (annotation) => {
|
|
1558
|
+
testInfo.annotations.push(annotation);
|
|
1559
|
+
switch (annotation.type) {
|
|
1560
|
+
case "fixme":
|
|
1561
|
+
case "skip":
|
|
1562
|
+
testInfo.expectedStatus = "skipped";
|
|
1563
|
+
break;
|
|
1564
|
+
case "fail":
|
|
1565
|
+
if (testInfo.expectedStatus !== "skipped")
|
|
1566
|
+
testInfo.expectedStatus = "failed";
|
|
1567
|
+
break;
|
|
1568
|
+
case "slow":
|
|
1569
|
+
testInfo._timeoutManager.slow();
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
if (!this._isStopped)
|
|
1574
|
+
this._fixtureRunner.setPool(test._pool);
|
|
1575
|
+
const suites = getSuites(test);
|
|
1576
|
+
const reversedSuites = suites.slice().reverse();
|
|
1577
|
+
const nextSuites = new Set(getSuites(nextTest));
|
|
1578
|
+
testInfo._timeoutManager.setTimeout(test.timeout);
|
|
1579
|
+
for (const annotation of test.annotations)
|
|
1580
|
+
processAnnotation(annotation);
|
|
1581
|
+
for (const suite of suites) {
|
|
1582
|
+
const extraAnnotations = this._activeSuites.get(suite) || [];
|
|
1583
|
+
for (const annotation of extraAnnotations)
|
|
1584
|
+
processAnnotation(annotation);
|
|
1585
|
+
}
|
|
1586
|
+
this._currentTest = testInfo;
|
|
1587
|
+
globals.setCurrentTestInfo(testInfo);
|
|
1588
|
+
(0, import_expect.setExpectConfig)({
|
|
1589
|
+
testInfo,
|
|
1590
|
+
filteredStackTrace: filteredStackTrace4,
|
|
1591
|
+
ignoreSnapshots: testInfo._projectInternal.project.ignoreSnapshots,
|
|
1592
|
+
updateSnapshots: testInfo.config.updateSnapshots,
|
|
1593
|
+
timeout: testInfo._projectInternal.expect?.timeout,
|
|
1594
|
+
toHaveScreenshot: testInfo._projectInternal.expect?.toHaveScreenshot,
|
|
1595
|
+
toMatchSnapshot: testInfo._projectInternal.expect?.toMatchSnapshot,
|
|
1596
|
+
toMatchAriaSnapshot: testInfo._projectInternal.expect?.toMatchAriaSnapshot,
|
|
1597
|
+
toPass: testInfo._projectInternal.expect?.toPass
|
|
1598
|
+
});
|
|
1599
|
+
this.dispatchEvent("testBegin", buildTestBeginPayload(testInfo));
|
|
1600
|
+
const isSkipped = testInfo.expectedStatus === "skipped";
|
|
1601
|
+
const hasAfterAllToRunBeforeNextTest = reversedSuites.some((suite) => {
|
|
1602
|
+
return this._activeSuites.has(suite) && !nextSuites.has(suite) && suite._hooks.some((hook) => hook.type === "afterAll");
|
|
1603
|
+
});
|
|
1604
|
+
if (isSkipped && nextTest && !hasAfterAllToRunBeforeNextTest) {
|
|
1605
|
+
testInfo.status = "skipped";
|
|
1606
|
+
this.dispatchEvent("testEnd", buildTestEndPayload(testInfo));
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
this._totalRunningTests++;
|
|
1610
|
+
this._lastRunningTests.push(test);
|
|
1611
|
+
if (this._lastRunningTests.length > 10)
|
|
1612
|
+
this._lastRunningTests.shift();
|
|
1613
|
+
let shouldRunAfterEachHooks = false;
|
|
1614
|
+
testInfo._allowSkips = true;
|
|
1615
|
+
await (async () => {
|
|
1616
|
+
await testInfo._runWithTimeout({ type: "test" }, async () => {
|
|
1617
|
+
const traceFixtureRegistration = test._pool.resolve("trace");
|
|
1618
|
+
if (!traceFixtureRegistration)
|
|
1619
|
+
return;
|
|
1620
|
+
if (typeof traceFixtureRegistration.fn === "function")
|
|
1621
|
+
throw new Error(`"trace" option cannot be a function`);
|
|
1622
|
+
await testInfo._tracing.startIfNeeded(traceFixtureRegistration.fn);
|
|
1623
|
+
});
|
|
1624
|
+
if (this._isStopped || isSkipped) {
|
|
1625
|
+
testInfo.status = "skipped";
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
await removeFolders([testInfo.outputDir]);
|
|
1629
|
+
let testFunctionParams = null;
|
|
1630
|
+
await testInfo._runAsStep({ title: "Before Hooks", category: "hook" }, async () => {
|
|
1631
|
+
for (const suite of suites)
|
|
1632
|
+
await this._runBeforeAllHooksForSuite(suite, testInfo);
|
|
1633
|
+
shouldRunAfterEachHooks = true;
|
|
1634
|
+
await this._runEachHooksForSuites(suites, "beforeEach", testInfo);
|
|
1635
|
+
const params = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, "test", { type: "test" });
|
|
1636
|
+
if (params !== null)
|
|
1637
|
+
testFunctionParams = params.result;
|
|
1638
|
+
});
|
|
1639
|
+
if (testFunctionParams === null) {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
await testInfo._runWithTimeout({ type: "test" }, async () => {
|
|
1643
|
+
const fn = test.fn;
|
|
1644
|
+
await fn(testFunctionParams, testInfo);
|
|
1645
|
+
});
|
|
1646
|
+
})().catch(() => {
|
|
1647
|
+
});
|
|
1648
|
+
testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed | 0;
|
|
1649
|
+
testInfo._allowSkips = true;
|
|
1650
|
+
const afterHooksTimeout = calculateMaxTimeout(this._project.project.timeout, testInfo.timeout);
|
|
1651
|
+
const afterHooksSlot = { timeout: afterHooksTimeout, elapsed: 0 };
|
|
1652
|
+
await testInfo._runAsStep({ title: "After Hooks", category: "hook" }, async () => {
|
|
1653
|
+
let firstAfterHooksError;
|
|
1654
|
+
try {
|
|
1655
|
+
await testInfo._runWithTimeout({ type: "test", slot: afterHooksSlot }, () => testInfo._didFinishTestFunction());
|
|
1656
|
+
} catch (error) {
|
|
1657
|
+
firstAfterHooksError = firstAfterHooksError ?? error;
|
|
1658
|
+
}
|
|
1659
|
+
try {
|
|
1660
|
+
if (shouldRunAfterEachHooks)
|
|
1661
|
+
await this._runEachHooksForSuites(reversedSuites, "afterEach", testInfo, afterHooksSlot);
|
|
1662
|
+
} catch (error) {
|
|
1663
|
+
firstAfterHooksError = firstAfterHooksError ?? error;
|
|
1664
|
+
}
|
|
1665
|
+
testInfo._tracing.didFinishTestFunctionAndAfterEachHooks();
|
|
1666
|
+
try {
|
|
1667
|
+
await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: afterHooksSlot });
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
firstAfterHooksError = firstAfterHooksError ?? error;
|
|
1670
|
+
}
|
|
1671
|
+
for (const suite of reversedSuites) {
|
|
1672
|
+
if (!nextSuites.has(suite) || testInfo._isFailure()) {
|
|
1673
|
+
try {
|
|
1674
|
+
await this._runAfterAllHooksForSuite(suite, testInfo);
|
|
1675
|
+
} catch (error) {
|
|
1676
|
+
firstAfterHooksError = firstAfterHooksError ?? error;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
if (firstAfterHooksError)
|
|
1681
|
+
throw firstAfterHooksError;
|
|
1682
|
+
}).catch(() => {
|
|
1683
|
+
});
|
|
1684
|
+
if (testInfo._isFailure())
|
|
1685
|
+
this._isStopped = true;
|
|
1686
|
+
if (this._isStopped) {
|
|
1687
|
+
this._didRunFullCleanup = true;
|
|
1688
|
+
await testInfo._runAsStep({ title: "Worker Cleanup", category: "hook" }, async () => {
|
|
1689
|
+
let firstWorkerCleanupError;
|
|
1690
|
+
const teardownSlot = { timeout: this._project.project.timeout, elapsed: 0 };
|
|
1691
|
+
try {
|
|
1692
|
+
await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: teardownSlot });
|
|
1693
|
+
} catch (error) {
|
|
1694
|
+
firstWorkerCleanupError = firstWorkerCleanupError ?? error;
|
|
1695
|
+
}
|
|
1696
|
+
for (const suite of reversedSuites) {
|
|
1697
|
+
try {
|
|
1698
|
+
await this._runAfterAllHooksForSuite(suite, testInfo);
|
|
1699
|
+
} catch (error) {
|
|
1700
|
+
firstWorkerCleanupError = firstWorkerCleanupError ?? error;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
try {
|
|
1704
|
+
await this._fixtureRunner.teardownScope("worker", testInfo, { type: "teardown", slot: teardownSlot });
|
|
1705
|
+
} catch (error) {
|
|
1706
|
+
firstWorkerCleanupError = firstWorkerCleanupError ?? error;
|
|
1707
|
+
}
|
|
1708
|
+
if (firstWorkerCleanupError)
|
|
1709
|
+
throw firstWorkerCleanupError;
|
|
1710
|
+
}).catch(() => {
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
const tracingSlot = { timeout: this._project.project.timeout, elapsed: 0 };
|
|
1714
|
+
await testInfo._runWithTimeout({ type: "test", slot: tracingSlot }, async () => {
|
|
1715
|
+
await testInfo._tracing.stopIfNeeded();
|
|
1716
|
+
}).catch(() => {
|
|
1717
|
+
});
|
|
1718
|
+
testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed + afterHooksSlot.elapsed | 0;
|
|
1719
|
+
this._currentTest = null;
|
|
1720
|
+
globals.setCurrentTestInfo(null);
|
|
1721
|
+
(0, import_expect.setExpectConfig)({ testInfo: null, filteredStackTrace: filteredStackTrace4, ignoreSnapshots: false, updateSnapshots: "missing" });
|
|
1722
|
+
this.dispatchEvent("testEnd", buildTestEndPayload(testInfo));
|
|
1723
|
+
const preserveOutput = this._config.config.preserveOutput === "always" || this._config.config.preserveOutput === "failures-only" && testInfo._isFailure();
|
|
1724
|
+
if (!preserveOutput)
|
|
1725
|
+
await removeFolders([testInfo.outputDir]);
|
|
1726
|
+
}
|
|
1727
|
+
_collectHooksAndModifiers(suite, type, testInfo) {
|
|
1728
|
+
const runnables = [];
|
|
1729
|
+
for (const modifier of suite._modifiers) {
|
|
1730
|
+
const modifierType = this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location) ? "beforeAll" : "beforeEach";
|
|
1731
|
+
if (modifierType !== type)
|
|
1732
|
+
continue;
|
|
1733
|
+
const fn = async (fixtures3) => {
|
|
1734
|
+
const result = await modifier.fn(fixtures3);
|
|
1735
|
+
testInfo._modifier(modifier.type, modifier.location, [!!result, modifier.description]);
|
|
1736
|
+
};
|
|
1737
|
+
import_common3.fixtures.inheritFixtureNames(modifier.fn, fn);
|
|
1738
|
+
runnables.push({
|
|
1739
|
+
title: `${modifier.type} modifier`,
|
|
1740
|
+
location: modifier.location,
|
|
1741
|
+
type: modifier.type,
|
|
1742
|
+
fn
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
runnables.push(...suite._hooks.filter((hook) => hook.type === type));
|
|
1746
|
+
return runnables;
|
|
1747
|
+
}
|
|
1748
|
+
async _runBeforeAllHooksForSuite(suite, testInfo) {
|
|
1749
|
+
if (this._activeSuites.has(suite))
|
|
1750
|
+
return;
|
|
1751
|
+
const extraAnnotations = [];
|
|
1752
|
+
this._activeSuites.set(suite, extraAnnotations);
|
|
1753
|
+
await this._runAllHooksForSuite(suite, testInfo, "beforeAll", extraAnnotations);
|
|
1754
|
+
}
|
|
1755
|
+
async _runAllHooksForSuite(suite, testInfo, type, extraAnnotations) {
|
|
1756
|
+
let firstError;
|
|
1757
|
+
for (const hook of this._collectHooksAndModifiers(suite, type, testInfo)) {
|
|
1758
|
+
try {
|
|
1759
|
+
await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => {
|
|
1760
|
+
const timeSlot = { timeout: this._project.project.timeout, elapsed: 0 };
|
|
1761
|
+
const runnable = { type: hook.type, slot: timeSlot, location: hook.location };
|
|
1762
|
+
const existingAnnotations = new Set(testInfo.annotations);
|
|
1763
|
+
try {
|
|
1764
|
+
await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "all-hooks-only", runnable);
|
|
1765
|
+
} finally {
|
|
1766
|
+
if (extraAnnotations) {
|
|
1767
|
+
const newAnnotations = testInfo.annotations.filter((a) => !existingAnnotations.has(a));
|
|
1768
|
+
extraAnnotations.push(...newAnnotations);
|
|
1769
|
+
}
|
|
1770
|
+
await this._fixtureRunner.teardownScope("test", testInfo, runnable);
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
firstError = firstError ?? error;
|
|
1775
|
+
if (type === "beforeAll" && error instanceof TestSkipError)
|
|
1776
|
+
break;
|
|
1777
|
+
if (type === "beforeAll" && !this._skipRemainingTestsInSuite) {
|
|
1778
|
+
this._skipRemainingTestsInSuite = suite;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
if (firstError)
|
|
1783
|
+
throw firstError;
|
|
1784
|
+
}
|
|
1785
|
+
async _runAfterAllHooksForSuite(suite, testInfo) {
|
|
1786
|
+
if (!this._activeSuites.has(suite))
|
|
1787
|
+
return;
|
|
1788
|
+
this._activeSuites.delete(suite);
|
|
1789
|
+
await this._runAllHooksForSuite(suite, testInfo, "afterAll");
|
|
1790
|
+
}
|
|
1791
|
+
async _runEachHooksForSuites(suites, type, testInfo, slot) {
|
|
1792
|
+
let firstError;
|
|
1793
|
+
const hooks = suites.map((suite) => this._collectHooksAndModifiers(suite, type, testInfo)).flat();
|
|
1794
|
+
for (const hook of hooks) {
|
|
1795
|
+
const runnable = { type: hook.type, location: hook.location, slot };
|
|
1796
|
+
if (testInfo._timeoutManager.isTimeExhaustedFor(runnable)) {
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
try {
|
|
1800
|
+
await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => {
|
|
1801
|
+
await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "test", runnable);
|
|
1802
|
+
});
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
firstError = firstError ?? error;
|
|
1805
|
+
if (error instanceof TestSkipError)
|
|
1806
|
+
break;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
if (firstError)
|
|
1810
|
+
throw firstError;
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
function buildTestBeginPayload(testInfo) {
|
|
1814
|
+
return {
|
|
1815
|
+
testId: testInfo.testId,
|
|
1816
|
+
startWallTime: testInfo._startWallTime
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
function buildTestEndPayload(testInfo) {
|
|
1820
|
+
return {
|
|
1821
|
+
testId: testInfo.testId,
|
|
1822
|
+
duration: testInfo.duration,
|
|
1823
|
+
status: testInfo.status,
|
|
1824
|
+
errors: testInfo.errors.map(import_common3.ipc.toTestInfoErrorPayload),
|
|
1825
|
+
hasNonRetriableError: testInfo._hasNonRetriableError,
|
|
1826
|
+
expectedStatus: testInfo.expectedStatus,
|
|
1827
|
+
annotations: testInfo.annotations,
|
|
1828
|
+
timeout: testInfo.timeout
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
function getSuites(test) {
|
|
1832
|
+
const suites = [];
|
|
1833
|
+
for (let suite = test?.parent; suite; suite = suite.parent)
|
|
1834
|
+
suites.push(suite);
|
|
1835
|
+
suites.reverse();
|
|
1836
|
+
return suites;
|
|
1837
|
+
}
|
|
1838
|
+
function formatTestTitle(test, projectName) {
|
|
1839
|
+
const [, ...titles] = test.titlePath();
|
|
1840
|
+
const location = `${relativeFilePath(test.location.file)}:${test.location.line}:${test.location.column}`;
|
|
1841
|
+
const projectTitle = projectName ? `[${projectName}] \u203A ` : "";
|
|
1842
|
+
return `${projectTitle}${location} \u203A ${titles.join(" \u203A ")}`;
|
|
1843
|
+
}
|
|
1844
|
+
function calculateMaxTimeout(t1, t2) {
|
|
1845
|
+
return !t1 || !t2 ? 0 : Math.max(t1, t2);
|
|
1846
|
+
}
|
|
1847
|
+
var create = (params) => new WorkerMain(params);
|
|
1848
|
+
|
|
1849
|
+
// packages/playwright/src/worker/workerProcessEntry.ts
|
|
1850
|
+
(0, import_common4.startProcessRunner)(create);
|