@smoothdeploy/playwright 1.58.4 → 1.61.0
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/ThirdPartyNotices.txt +8 -5036
- package/lib/agents/agentParser.js +2 -2
- package/lib/agents/generateAgents.js +69 -22
- package/lib/agents/playwright-test-healer.agent.md +1 -0
- package/lib/agents/playwright-test-planner.agent.md +2 -1
- package/lib/cli/reportActions.js +78 -0
- package/lib/cli/testActions.js +213 -0
- package/lib/common/index.js +2931 -0
- package/lib/common/index.js.txt +35 -0
- package/lib/errorContext.js +130 -0
- package/lib/index.js +276 -219
- package/lib/{isomorphic/testServerConnection.js → isomorphic.js} +70 -35
- package/lib/isomorphic.js.txt +9 -0
- package/lib/loader/loaderProcessEntry.js +34 -0
- package/lib/loader/loaderProcessEntry.js.txt +9 -0
- package/lib/matchers/expect.js +12990 -249
- package/lib/matchers/expect.js.LICENSE +693 -0
- package/lib/matchers/expect.js.txt +72 -0
- package/lib/mcp/test/browserBackend.js +49 -22
- package/lib/mcp/test/generatorTools.js +16 -16
- package/lib/mcp/test/plannerTools.js +25 -20
- package/lib/mcp/test/seed.js +7 -7
- package/lib/mcp/test/testBackend.js +30 -30
- package/lib/mcp/test/testContext.js +54 -33
- package/lib/mcp/test/testTools.js +15 -25
- package/lib/{internalsForTest.js → package.js} +13 -8
- package/lib/program.js +71 -265
- package/lib/runner/index.js +8389 -0
- package/lib/runner/index.js.txt +60 -0
- package/lib/transform/babelBundle.js +71002 -18
- package/lib/transform/babelBundle.js.LICENSE +2359 -0
- package/lib/transform/babelBundle.js.txt +325 -0
- package/lib/transform/esmLoader.js +5994 -33
- package/lib/transform/esmLoader.js.LICENSE +335 -0
- package/lib/transform/esmLoader.js.txt +57 -0
- package/lib/util.js +48 -48
- package/lib/worker/workerProcessEntry.js +1868 -0
- package/lib/worker/workerProcessEntry.js.txt +20 -0
- package/package.json +5 -16
- package/test.mjs +1 -0
- package/types/test.d.ts +238 -28
- package/types/testReporter.d.ts +7 -5
- package/lib/common/config.js +0 -282
- package/lib/common/configLoader.js +0 -344
- package/lib/common/esmLoaderHost.js +0 -104
- package/lib/common/expectBundle.js +0 -28
- package/lib/common/expectBundleImpl.js +0 -407
- package/lib/common/fixtures.js +0 -302
- package/lib/common/ipc.js +0 -60
- package/lib/common/poolBuilder.js +0 -85
- package/lib/common/process.js +0 -132
- package/lib/common/suiteUtils.js +0 -140
- package/lib/common/test.js +0 -321
- package/lib/common/testLoader.js +0 -101
- package/lib/common/testType.js +0 -298
- package/lib/common/validators.js +0 -68
- package/lib/fsWatcher.js +0 -67
- package/lib/isomorphic/events.js +0 -77
- package/lib/isomorphic/folders.js +0 -30
- package/lib/isomorphic/stringInternPool.js +0 -69
- package/lib/isomorphic/teleReceiver.js +0 -521
- package/lib/isomorphic/teleSuiteUpdater.js +0 -157
- package/lib/isomorphic/testServerInterface.js +0 -16
- package/lib/isomorphic/testTree.js +0 -329
- package/lib/isomorphic/types.d.js +0 -16
- package/lib/loader/loaderMain.js +0 -59
- package/lib/matchers/matcherHint.js +0 -44
- package/lib/matchers/matchers.js +0 -383
- package/lib/matchers/toBeTruthy.js +0 -75
- package/lib/matchers/toEqual.js +0 -100
- package/lib/matchers/toHaveURL.js +0 -101
- package/lib/matchers/toMatchAriaSnapshot.js +0 -159
- package/lib/matchers/toMatchSnapshot.js +0 -342
- package/lib/matchers/toMatchText.js +0 -99
- package/lib/mcp/browser/browserContextFactory.js +0 -329
- package/lib/mcp/browser/browserServerBackend.js +0 -84
- package/lib/mcp/browser/config.js +0 -421
- package/lib/mcp/browser/context.js +0 -244
- package/lib/mcp/browser/response.js +0 -278
- package/lib/mcp/browser/sessionLog.js +0 -75
- package/lib/mcp/browser/tab.js +0 -343
- package/lib/mcp/browser/tools/common.js +0 -65
- package/lib/mcp/browser/tools/console.js +0 -46
- package/lib/mcp/browser/tools/dialogs.js +0 -60
- package/lib/mcp/browser/tools/evaluate.js +0 -61
- package/lib/mcp/browser/tools/files.js +0 -58
- package/lib/mcp/browser/tools/form.js +0 -63
- package/lib/mcp/browser/tools/install.js +0 -72
- package/lib/mcp/browser/tools/keyboard.js +0 -107
- package/lib/mcp/browser/tools/mouse.js +0 -107
- package/lib/mcp/browser/tools/navigate.js +0 -71
- package/lib/mcp/browser/tools/network.js +0 -63
- package/lib/mcp/browser/tools/open.js +0 -57
- package/lib/mcp/browser/tools/pdf.js +0 -49
- package/lib/mcp/browser/tools/runCode.js +0 -78
- package/lib/mcp/browser/tools/screenshot.js +0 -93
- package/lib/mcp/browser/tools/snapshot.js +0 -173
- package/lib/mcp/browser/tools/tabs.js +0 -67
- package/lib/mcp/browser/tools/tool.js +0 -47
- package/lib/mcp/browser/tools/tracing.js +0 -74
- package/lib/mcp/browser/tools/utils.js +0 -94
- package/lib/mcp/browser/tools/verify.js +0 -143
- package/lib/mcp/browser/tools/wait.js +0 -63
- package/lib/mcp/browser/tools.js +0 -84
- package/lib/mcp/browser/watchdog.js +0 -44
- package/lib/mcp/config.d.js +0 -16
- package/lib/mcp/extension/cdpRelay.js +0 -351
- package/lib/mcp/extension/extensionContextFactory.js +0 -76
- package/lib/mcp/extension/protocol.js +0 -28
- package/lib/mcp/index.js +0 -61
- package/lib/mcp/log.js +0 -35
- package/lib/mcp/program.js +0 -111
- package/lib/mcp/sdk/exports.js +0 -28
- package/lib/mcp/sdk/http.js +0 -152
- package/lib/mcp/sdk/inProcessTransport.js +0 -71
- package/lib/mcp/sdk/server.js +0 -223
- package/lib/mcp/sdk/tool.js +0 -47
- package/lib/mcp/terminal/cli.js +0 -296
- package/lib/mcp/terminal/command.js +0 -56
- package/lib/mcp/terminal/commands.js +0 -333
- package/lib/mcp/terminal/daemon.js +0 -129
- package/lib/mcp/terminal/help.json +0 -32
- package/lib/mcp/terminal/helpGenerator.js +0 -88
- package/lib/mcp/terminal/socketConnection.js +0 -80
- package/lib/plugins/gitCommitInfoPlugin.js +0 -198
- package/lib/plugins/index.js +0 -28
- package/lib/plugins/webServerPlugin.js +0 -237
- package/lib/reporters/base.js +0 -634
- package/lib/reporters/blob.js +0 -138
- package/lib/reporters/dot.js +0 -99
- package/lib/reporters/empty.js +0 -32
- package/lib/reporters/github.js +0 -128
- package/lib/reporters/html.js +0 -633
- package/lib/reporters/internalReporter.js +0 -138
- package/lib/reporters/json.js +0 -254
- package/lib/reporters/junit.js +0 -232
- package/lib/reporters/line.js +0 -131
- package/lib/reporters/list.js +0 -253
- package/lib/reporters/listModeReporter.js +0 -69
- package/lib/reporters/markdown.js +0 -144
- package/lib/reporters/merge.js +0 -558
- package/lib/reporters/multiplexer.js +0 -112
- package/lib/reporters/reporterV2.js +0 -102
- package/lib/reporters/smoothdeploy.js +0 -333
- package/lib/reporters/teleEmitter.js +0 -317
- package/lib/reporters/versions/blobV1.js +0 -16
- package/lib/runner/dispatcher.js +0 -531
- package/lib/runner/failureTracker.js +0 -72
- package/lib/runner/lastRun.js +0 -77
- package/lib/runner/loadUtils.js +0 -334
- package/lib/runner/loaderHost.js +0 -89
- package/lib/runner/processHost.js +0 -180
- package/lib/runner/projectUtils.js +0 -241
- package/lib/runner/rebase.js +0 -189
- package/lib/runner/reporters.js +0 -140
- package/lib/runner/sigIntWatcher.js +0 -96
- package/lib/runner/storage.js +0 -91
- package/lib/runner/taskRunner.js +0 -127
- package/lib/runner/tasks.js +0 -410
- package/lib/runner/testGroups.js +0 -125
- package/lib/runner/testRunner.js +0 -398
- package/lib/runner/testServer.js +0 -269
- package/lib/runner/uiModeReporter.js +0 -30
- package/lib/runner/vcs.js +0 -72
- package/lib/runner/watchMode.js +0 -396
- package/lib/runner/workerHost.js +0 -104
- package/lib/third_party/pirates.js +0 -62
- package/lib/third_party/tsconfig-loader.js +0 -103
- package/lib/transform/babelBundleImpl.js +0 -461
- package/lib/transform/compilationCache.js +0 -274
- package/lib/transform/md.js +0 -221
- package/lib/transform/portTransport.js +0 -67
- package/lib/transform/transform.js +0 -303
- package/lib/utilsBundle.js +0 -50
- package/lib/utilsBundleImpl.js +0 -103
- package/lib/worker/fixtureRunner.js +0 -262
- package/lib/worker/testInfo.js +0 -536
- package/lib/worker/testTracing.js +0 -345
- package/lib/worker/timeoutManager.js +0 -174
- package/lib/worker/util.js +0 -31
- package/lib/worker/workerMain.js +0 -530
- /package/lib/{common/globals.js → globals.js} +0 -0
package/lib/index.js
CHANGED
|
@@ -29,28 +29,37 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
29
|
var index_exports = {};
|
|
30
30
|
__export(index_exports, {
|
|
31
31
|
_baseTest: () => _baseTest,
|
|
32
|
-
|
|
32
|
+
_utilityTest: () => _utilityTest,
|
|
33
|
+
defineConfig: () => import_common2.defineConfig,
|
|
33
34
|
expect: () => import_expect.expect,
|
|
34
35
|
mergeExpects: () => import_expect2.mergeExpects,
|
|
35
|
-
mergeTests: () =>
|
|
36
|
+
mergeTests: () => import_common2.mergeTests,
|
|
36
37
|
test: () => test
|
|
37
38
|
});
|
|
38
39
|
module.exports = __toCommonJS(index_exports);
|
|
39
40
|
var import_fs = __toESM(require("fs"));
|
|
40
41
|
var import_path = __toESM(require("path"));
|
|
41
42
|
var playwrightLibrary = __toESM(require("playwright-core"));
|
|
42
|
-
var
|
|
43
|
-
var import_screenshotCompositor = require("playwright-core/lib/server/screenshotCompositor");
|
|
43
|
+
var import_coreBundle = require("playwright-core/lib/coreBundle");
|
|
44
44
|
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
45
|
-
var
|
|
46
|
-
var
|
|
45
|
+
var import_errorContext = require("./errorContext");
|
|
46
|
+
var import_common = require("./common");
|
|
47
|
+
var globals = __toESM(require("./globals"));
|
|
48
|
+
var import_package = require("./package");
|
|
47
49
|
var import_browserBackend = require("./mcp/test/browserBackend");
|
|
48
50
|
var import_expect = require("./matchers/expect");
|
|
49
|
-
var
|
|
50
|
-
var import_testType2 = require("./common/testType");
|
|
51
|
+
var import_common2 = require("./common");
|
|
51
52
|
var import_expect2 = require("./matchers/expect");
|
|
52
|
-
const
|
|
53
|
-
|
|
53
|
+
const { asLocatorDescription } = require("playwright-core/lib/coreBundle").iso;
|
|
54
|
+
const { getActionGroup, renderTitleForCall } = require("playwright-core/lib/coreBundle").iso;
|
|
55
|
+
const { escapeHTML } = require("playwright-core/lib/coreBundle").iso;
|
|
56
|
+
const { jsonStringifyForceASCII } = require("playwright-core/lib/coreBundle").utils;
|
|
57
|
+
const { createGuid } = require("playwright-core/lib/coreBundle").utils;
|
|
58
|
+
const { debugMode } = require("playwright-core/lib/coreBundle").utils;
|
|
59
|
+
const { setBoxedStackPrefixes } = require("playwright-core/lib/coreBundle").utils;
|
|
60
|
+
const { currentZone } = require("playwright-core/lib/coreBundle").utils;
|
|
61
|
+
const _baseTest = import_common.testType.rootTestType.test;
|
|
62
|
+
setBoxedStackPrefixes([import_package.packageRoot]);
|
|
54
63
|
if (process["__pw_initiator__"]) {
|
|
55
64
|
const originalStackTraceLimit = Error.stackTraceLimit;
|
|
56
65
|
Error.stackTraceLimit = 200;
|
|
@@ -62,28 +71,159 @@ if (process["__pw_initiator__"]) {
|
|
|
62
71
|
} else {
|
|
63
72
|
process["__pw_initiator__"] = new Error().stack;
|
|
64
73
|
}
|
|
65
|
-
const
|
|
66
|
-
defaultBrowserType: ["chromium", { scope: "worker", option: true, box: true }],
|
|
67
|
-
browserName: [({ defaultBrowserType }, use) => use(defaultBrowserType), { scope: "worker", option: true, box: true }],
|
|
74
|
+
const utilityFixtures = {
|
|
68
75
|
playwright: [async ({}, use) => {
|
|
69
76
|
await use(require("playwright-core"));
|
|
70
77
|
}, { scope: "worker", box: true }],
|
|
78
|
+
screenshot: ["off", { scope: "worker", option: true, box: true }],
|
|
79
|
+
trace: ["off", { scope: "worker", option: true, box: true }],
|
|
80
|
+
launchOptions: [{}, { scope: "worker", option: true, box: true }],
|
|
71
81
|
headless: [({ launchOptions }, use) => use(launchOptions.headless ?? true), { scope: "worker", option: true, box: true }],
|
|
82
|
+
resultScreenshots: [false, { scope: "worker", option: true, box: true }],
|
|
83
|
+
pauseOnFailure: [false, { scope: "worker", option: true, box: true }],
|
|
84
|
+
testIdAttribute: ["data-testid", { option: true, box: true }],
|
|
85
|
+
_combinedContextOptions: [{}, { box: true }],
|
|
86
|
+
_setupArtifacts: [async ({ playwright, headless, screenshot, resultScreenshots, pauseOnFailure, _combinedContextOptions }, use, testInfo) => {
|
|
87
|
+
testInfo.setTimeout(testInfo.project.timeout);
|
|
88
|
+
const artifactsRecorder = new ArtifactsRecorder(playwright, tracing().artifactsDir(), screenshot, resultScreenshots, pauseOnFailure && !headless);
|
|
89
|
+
await artifactsRecorder.willStartTest(testInfo);
|
|
90
|
+
const tracingGroupSteps = [];
|
|
91
|
+
const pausedContexts = /* @__PURE__ */ new Set();
|
|
92
|
+
const csiListener = {
|
|
93
|
+
onApiCallBegin: (data, channel) => {
|
|
94
|
+
const testInfo2 = globals.currentTestInfo();
|
|
95
|
+
if (!testInfo2 || data.apiName.includes("setTestIdAttribute") || data.apiName === "tracing.groupEnd")
|
|
96
|
+
return;
|
|
97
|
+
const zone = currentZone().data("stepZone");
|
|
98
|
+
const isExpectCall = data.apiName === "locator._expect" || data.apiName === "frame._expect" || data.apiName === "page._expectScreenshot";
|
|
99
|
+
if (zone && zone.category === "expect" && isExpectCall) {
|
|
100
|
+
if (zone.apiName)
|
|
101
|
+
data.apiName = zone.apiName;
|
|
102
|
+
if (zone.shortTitle || zone.title)
|
|
103
|
+
data.title = zone.shortTitle ?? zone.title;
|
|
104
|
+
data.stepId = zone.stepId;
|
|
105
|
+
if (resultScreenshots && channel.params?.selector)
|
|
106
|
+
data._channel = channel;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const step = testInfo2._addStep({
|
|
110
|
+
location: data.frames[0],
|
|
111
|
+
category: "pw:api",
|
|
112
|
+
title: renderTitle(channel.type, channel.method, channel.params, data.title),
|
|
113
|
+
apiName: data.apiName,
|
|
114
|
+
params: channel.params,
|
|
115
|
+
group: getActionGroup({ type: channel.type, method: channel.method })
|
|
116
|
+
}, tracingGroupSteps[tracingGroupSteps.length - 1]);
|
|
117
|
+
data.userData = step;
|
|
118
|
+
data.stepId = step.stepId;
|
|
119
|
+
if (data.apiName === "tracing.group")
|
|
120
|
+
tracingGroupSteps.push(step);
|
|
121
|
+
if (resultScreenshots && channel.params?.selector)
|
|
122
|
+
data._channel = channel;
|
|
123
|
+
},
|
|
124
|
+
onApiCallEnd: async (data) => {
|
|
125
|
+
if (data.apiName === "tracing.group")
|
|
126
|
+
return;
|
|
127
|
+
if (data.apiName === "tracing.groupEnd") {
|
|
128
|
+
const step2 = tracingGroupSteps.pop();
|
|
129
|
+
step2?.complete({ error: data.error });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const channel = data._channel;
|
|
133
|
+
if (channel && !data.error) {
|
|
134
|
+
const testInfo2 = globals.currentTestInfo();
|
|
135
|
+
if (testInfo2) {
|
|
136
|
+
try {
|
|
137
|
+
await captureResultScreenshot(channel, data, testInfo2);
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const step = data.userData;
|
|
143
|
+
step?.complete({ error: data.error });
|
|
144
|
+
},
|
|
145
|
+
onWillPause: ({ keepTestTimeout }) => {
|
|
146
|
+
if (!keepTestTimeout)
|
|
147
|
+
globals.currentTestInfo()?._setIgnoreTimeouts(true);
|
|
148
|
+
},
|
|
149
|
+
runBeforeCreateBrowserContext: async (options) => {
|
|
150
|
+
for (const [key, value] of Object.entries(_combinedContextOptions)) {
|
|
151
|
+
if (!(key in options))
|
|
152
|
+
options[key] = value;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
runBeforeCreateRequestContext: async (options) => {
|
|
156
|
+
for (const [key, value] of Object.entries(_combinedContextOptions)) {
|
|
157
|
+
if (!(key in options))
|
|
158
|
+
options[key] = value;
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
runAfterCreateBrowserContext: async (context) => {
|
|
162
|
+
context.debugger.on("pausedstatechanged", () => {
|
|
163
|
+
const paused = !!context.debugger.pausedDetails();
|
|
164
|
+
if (pausedContexts.has(context) && !paused) {
|
|
165
|
+
pausedContexts.delete(context);
|
|
166
|
+
testInfo._setIgnoreTimeouts(false);
|
|
167
|
+
} else if (!pausedContexts.has(context) && paused) {
|
|
168
|
+
pausedContexts.add(context);
|
|
169
|
+
testInfo._setIgnoreTimeouts(true);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
await artifactsRecorder.didCreateBrowserContext(context);
|
|
173
|
+
const currentTestInfo = globals.currentTestInfo();
|
|
174
|
+
if (currentTestInfo) {
|
|
175
|
+
attachConnectedHeaderIfNeeded(currentTestInfo, context.browser());
|
|
176
|
+
currentTestInfo._onCustomMessageCallback = (0, import_browserBackend.createCustomMessageHandler)(currentTestInfo, context);
|
|
177
|
+
await (0, import_browserBackend.runDaemonForContext)(currentTestInfo, context);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
runAfterCreateRequestContext: async (context) => {
|
|
181
|
+
await artifactsRecorder.didCreateRequestContext(context);
|
|
182
|
+
},
|
|
183
|
+
runBeforeCloseBrowserContext: async (context) => {
|
|
184
|
+
await artifactsRecorder.willCloseBrowserContext(context);
|
|
185
|
+
},
|
|
186
|
+
runBeforeCloseRequestContext: async (context) => {
|
|
187
|
+
await artifactsRecorder.willCloseRequestContext(context);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const clientInstrumentation = playwright._instrumentation;
|
|
191
|
+
clientInstrumentation.addListener(csiListener);
|
|
192
|
+
await use();
|
|
193
|
+
clientInstrumentation.removeListener(csiListener);
|
|
194
|
+
await artifactsRecorder.didFinishTest();
|
|
195
|
+
}, { auto: "all-hooks-included", title: "trace recording", box: true, timeout: 0 }],
|
|
196
|
+
request: async ({ playwright }, use) => {
|
|
197
|
+
const request = await playwright.request.newContext();
|
|
198
|
+
await use(request);
|
|
199
|
+
const hook = test.info()._currentHookType();
|
|
200
|
+
if (hook === "beforeAll") {
|
|
201
|
+
await request.dispose({ reason: [
|
|
202
|
+
`Fixture { request } from beforeAll cannot be reused in a test.`,
|
|
203
|
+
` - Recommended fix: use a separate { request } in the test.`,
|
|
204
|
+
` - Alternatively, manually create APIRequestContext in beforeAll and dispose it in afterAll.`,
|
|
205
|
+
`See https://playwright.dev/docs/api-testing#sending-api-requests-from-ui-tests for more details.`
|
|
206
|
+
].join("\n") });
|
|
207
|
+
} else {
|
|
208
|
+
await request.dispose();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const _utilityTest = _baseTest.extend(utilityFixtures);
|
|
213
|
+
const playwrightFixtures = {
|
|
214
|
+
defaultBrowserType: ["chromium", { scope: "worker", option: true, box: true }],
|
|
215
|
+
browserName: [({ defaultBrowserType }, use) => use(defaultBrowserType), { scope: "worker", option: true, box: true }],
|
|
72
216
|
channel: [({ launchOptions }, use) => use(launchOptions.channel), { scope: "worker", option: true, box: true }],
|
|
73
|
-
launchOptions: [{}, { scope: "worker", option: true, box: true }],
|
|
74
217
|
connectOptions: [async ({ _optionConnectOptions }, use) => {
|
|
75
218
|
await use(connectOptionsFromEnv() || _optionConnectOptions);
|
|
76
219
|
}, { scope: "worker", option: true, box: true }],
|
|
77
|
-
screenshot: ["off", { scope: "worker", option: true, box: true }],
|
|
78
|
-
resultScreenshots: [false, { scope: "worker", option: true, box: true }],
|
|
79
|
-
pauseOnFailure: [false, { scope: "worker", option: true, box: true }],
|
|
80
220
|
video: ["off", { scope: "worker", option: true, box: true }],
|
|
81
|
-
trace: ["off", { scope: "worker", option: true, box: true }],
|
|
82
221
|
_browserOptions: [async ({ playwright, headless, channel, launchOptions }, use) => {
|
|
83
222
|
const options = {
|
|
84
223
|
handleSIGINT: false,
|
|
85
224
|
...launchOptions,
|
|
86
|
-
tracesDir: tracing().tracesDir()
|
|
225
|
+
tracesDir: tracing().tracesDir(),
|
|
226
|
+
artifactsDir: tracing().artifactsDir()
|
|
87
227
|
};
|
|
88
228
|
if (headless !== void 0)
|
|
89
229
|
options.headless = headless;
|
|
@@ -93,16 +233,16 @@ const playwrightFixtures = {
|
|
|
93
233
|
await use(options);
|
|
94
234
|
playwright._defaultLaunchOptions = void 0;
|
|
95
235
|
}, { scope: "worker", auto: true, box: true }],
|
|
96
|
-
browser: [async ({ playwright, browserName, _browserOptions, connectOptions }, use) => {
|
|
236
|
+
browser: [async ({ playwright, browserName, _browserOptions, connectOptions }, use, workerInfo) => {
|
|
97
237
|
if (!["chromium", "firefox", "webkit"].includes(browserName))
|
|
98
238
|
throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`);
|
|
99
239
|
if (connectOptions) {
|
|
100
|
-
const browser2 = await playwright[browserName].connect({
|
|
240
|
+
const browser2 = await playwright[browserName].connect(connectOptions.wsEndpoint, {
|
|
101
241
|
...connectOptions,
|
|
102
|
-
exposeNetwork: connectOptions.exposeNetwork
|
|
242
|
+
exposeNetwork: connectOptions.exposeNetwork,
|
|
103
243
|
headers: {
|
|
104
244
|
// HTTP headers are ASCII only (not UTF-8).
|
|
105
|
-
"x-playwright-launch-options":
|
|
245
|
+
"x-playwright-launch-options": jsonStringifyForceASCII(_browserOptions),
|
|
106
246
|
...connectOptions.headers
|
|
107
247
|
}
|
|
108
248
|
});
|
|
@@ -111,6 +251,8 @@ const playwrightFixtures = {
|
|
|
111
251
|
return;
|
|
112
252
|
}
|
|
113
253
|
const browser = await playwright[browserName].launch();
|
|
254
|
+
if (process.env.PLAYWRIGHT_DASHBOARD)
|
|
255
|
+
await browser.bind(`worker-${workerInfo.parallelIndex}`);
|
|
114
256
|
await use(browser);
|
|
115
257
|
await browser.close({ reason: "Test ended." });
|
|
116
258
|
}, { scope: "worker", timeout: 0 }],
|
|
@@ -135,14 +277,12 @@ const playwrightFixtures = {
|
|
|
135
277
|
userAgent: [({ contextOptions }, use) => use(contextOptions.userAgent), { option: true, box: true }],
|
|
136
278
|
viewport: [({ contextOptions }, use) => use(contextOptions.viewport === void 0 ? null : contextOptions.viewport), { option: true, box: true }],
|
|
137
279
|
actionTimeout: [0, { option: true, box: true }],
|
|
138
|
-
testIdAttribute: ["data-testid", { option: true, box: true }],
|
|
139
280
|
navigationTimeout: [0, { option: true, box: true }],
|
|
140
281
|
baseURL: [async ({}, use) => {
|
|
141
282
|
await use(process.env.PLAYWRIGHT_TEST_BASE_URL);
|
|
142
283
|
}, { option: true, box: true }],
|
|
143
284
|
serviceWorkers: [({ contextOptions }, use) => use(contextOptions.serviceWorkers ?? "allow"), { option: true, box: true }],
|
|
144
285
|
contextOptions: [{}, { option: true, box: true }],
|
|
145
|
-
agentOptions: [void 0, { option: true, box: true }],
|
|
146
286
|
_combinedContextOptions: [async ({
|
|
147
287
|
acceptDownloads,
|
|
148
288
|
bypassCSP,
|
|
@@ -217,116 +357,21 @@ const playwrightFixtures = {
|
|
|
217
357
|
...contextOptions,
|
|
218
358
|
...options
|
|
219
359
|
});
|
|
220
|
-
}, { box: true }],
|
|
221
|
-
_setupContextOptions: [async ({ playwright, actionTimeout, navigationTimeout, testIdAttribute }, use,
|
|
360
|
+
}, { scope: "test", box: true }],
|
|
361
|
+
_setupContextOptions: [async ({ playwright, actionTimeout, navigationTimeout, testIdAttribute }, use, _testInfo) => {
|
|
362
|
+
const testInfo = _testInfo;
|
|
222
363
|
if (testIdAttribute)
|
|
223
364
|
playwrightLibrary.selectors.setTestIdAttribute(testIdAttribute);
|
|
224
365
|
testInfo.snapshotSuffix = process.platform;
|
|
225
|
-
|
|
226
|
-
|
|
366
|
+
testInfo._onCustomMessageCallback = () => Promise.reject(new Error("Only tests that use default Playwright context or page fixture support test_debug"));
|
|
367
|
+
if (debugMode() === "inspector")
|
|
368
|
+
testInfo._setIgnoreTimeouts(true);
|
|
227
369
|
playwright._defaultContextTimeout = actionTimeout || 0;
|
|
228
370
|
playwright._defaultContextNavigationTimeout = navigationTimeout || 0;
|
|
229
371
|
await use();
|
|
230
372
|
playwright._defaultContextTimeout = void 0;
|
|
231
373
|
playwright._defaultContextNavigationTimeout = void 0;
|
|
232
374
|
}, { auto: "all-hooks-included", title: "context configuration", box: true }],
|
|
233
|
-
_setupArtifacts: [async ({ playwright, headless, screenshot, resultScreenshots, pauseOnFailure, _combinedContextOptions }, use, testInfo) => {
|
|
234
|
-
testInfo.setTimeout(testInfo.project.timeout);
|
|
235
|
-
const artifactsRecorder = new ArtifactsRecorder(playwright, tracing().artifactsDir(), screenshot, resultScreenshots, pauseOnFailure && !headless);
|
|
236
|
-
await artifactsRecorder.willStartTest(testInfo);
|
|
237
|
-
const tracingGroupSteps = [];
|
|
238
|
-
const csiListener = {
|
|
239
|
-
onApiCallBegin: (data, channel) => {
|
|
240
|
-
const testInfo2 = (0, import_globals.currentTestInfo)();
|
|
241
|
-
if (!testInfo2 || data.apiName.includes("setTestIdAttribute") || data.apiName === "tracing.groupEnd")
|
|
242
|
-
return;
|
|
243
|
-
const zone = (0, import_utils.currentZone)().data("stepZone");
|
|
244
|
-
const isExpectCall = data.apiName === "locator._expect" || data.apiName === "frame._expect" || data.apiName === "page._expectScreenshot";
|
|
245
|
-
if (zone && zone.category === "expect" && isExpectCall) {
|
|
246
|
-
if (zone.apiName)
|
|
247
|
-
data.apiName = zone.apiName;
|
|
248
|
-
if (zone.shortTitle || zone.title)
|
|
249
|
-
data.title = zone.shortTitle ?? zone.title;
|
|
250
|
-
data.stepId = zone.stepId;
|
|
251
|
-
if (resultScreenshots && channel.params?.selector)
|
|
252
|
-
data._channel = channel;
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
const stepTitle = renderTitle(channel.type, channel.method, channel.params, data.title);
|
|
256
|
-
const step = testInfo2._addStep({
|
|
257
|
-
location: data.frames[0],
|
|
258
|
-
category: "pw:api",
|
|
259
|
-
title: stepTitle,
|
|
260
|
-
apiName: data.apiName,
|
|
261
|
-
params: channel.params,
|
|
262
|
-
group: (0, import_utils.getActionGroup)({ type: channel.type, method: channel.method })
|
|
263
|
-
}, tracingGroupSteps[tracingGroupSteps.length - 1]);
|
|
264
|
-
data.userData = step;
|
|
265
|
-
data.stepId = step.stepId;
|
|
266
|
-
if (data.apiName === "tracing.group")
|
|
267
|
-
tracingGroupSteps.push(step);
|
|
268
|
-
if (resultScreenshots && channel.params?.selector)
|
|
269
|
-
data._channel = channel;
|
|
270
|
-
},
|
|
271
|
-
onApiCallEnd: async (data) => {
|
|
272
|
-
if (data.apiName === "tracing.group")
|
|
273
|
-
return;
|
|
274
|
-
if (data.apiName === "tracing.groupEnd") {
|
|
275
|
-
const step2 = tracingGroupSteps.pop();
|
|
276
|
-
step2?.complete({ error: data.error });
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
const channel = data._channel;
|
|
280
|
-
if (channel && !data.error) {
|
|
281
|
-
const testInfo2 = (0, import_globals.currentTestInfo)();
|
|
282
|
-
if (testInfo2) {
|
|
283
|
-
try {
|
|
284
|
-
await captureResultScreenshot(channel, data, testInfo2);
|
|
285
|
-
} catch {
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
const step = data.userData;
|
|
290
|
-
step?.complete({ error: data.error });
|
|
291
|
-
},
|
|
292
|
-
onWillPause: ({ keepTestTimeout }) => {
|
|
293
|
-
if (!keepTestTimeout)
|
|
294
|
-
(0, import_globals.currentTestInfo)()?._setDebugMode();
|
|
295
|
-
},
|
|
296
|
-
runBeforeCreateBrowserContext: async (options) => {
|
|
297
|
-
for (const [key, value] of Object.entries(_combinedContextOptions)) {
|
|
298
|
-
if (!(key in options))
|
|
299
|
-
options[key] = value;
|
|
300
|
-
}
|
|
301
|
-
},
|
|
302
|
-
runBeforeCreateRequestContext: async (options) => {
|
|
303
|
-
for (const [key, value] of Object.entries(_combinedContextOptions)) {
|
|
304
|
-
if (!(key in options))
|
|
305
|
-
options[key] = value;
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
runAfterCreateBrowserContext: async (context) => {
|
|
309
|
-
await artifactsRecorder.didCreateBrowserContext(context);
|
|
310
|
-
const testInfo2 = (0, import_globals.currentTestInfo)();
|
|
311
|
-
if (testInfo2)
|
|
312
|
-
attachConnectedHeaderIfNeeded(testInfo2, context.browser());
|
|
313
|
-
},
|
|
314
|
-
runAfterCreateRequestContext: async (context) => {
|
|
315
|
-
await artifactsRecorder.didCreateRequestContext(context);
|
|
316
|
-
},
|
|
317
|
-
runBeforeCloseBrowserContext: async (context) => {
|
|
318
|
-
await artifactsRecorder.willCloseBrowserContext(context);
|
|
319
|
-
},
|
|
320
|
-
runBeforeCloseRequestContext: async (context) => {
|
|
321
|
-
await artifactsRecorder.willCloseRequestContext(context);
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
const clientInstrumentation = playwright._instrumentation;
|
|
325
|
-
clientInstrumentation.addListener(csiListener);
|
|
326
|
-
await use();
|
|
327
|
-
clientInstrumentation.removeListener(csiListener);
|
|
328
|
-
await artifactsRecorder.didFinishTest();
|
|
329
|
-
}, { auto: "all-hooks-included", title: "trace recording", box: true, timeout: 0 }],
|
|
330
375
|
_contextFactory: [async ({
|
|
331
376
|
browser,
|
|
332
377
|
video,
|
|
@@ -348,23 +393,15 @@ const playwrightFixtures = {
|
|
|
348
393
|
`If you would like to configure your page before each test, do that in beforeEach hook instead.`
|
|
349
394
|
].join("\n"));
|
|
350
395
|
}
|
|
396
|
+
const show = typeof video === "string" ? void 0 : video.show;
|
|
351
397
|
const videoOptions = captureVideo ? {
|
|
352
398
|
recordVideo: {
|
|
353
399
|
dir: tracing().artifactsDir(),
|
|
354
|
-
size: typeof video === "string" ? void 0 : video.size
|
|
400
|
+
size: typeof video === "string" ? void 0 : video.size,
|
|
401
|
+
showActions: show?.actions
|
|
355
402
|
}
|
|
356
403
|
} : {};
|
|
357
404
|
const context = await browser.newContext({ ...videoOptions, ...options });
|
|
358
|
-
if (process.env.PW_CLOCK === "frozen") {
|
|
359
|
-
await context._wrapApiCall(async () => {
|
|
360
|
-
await context.clock.install({ time: 0 });
|
|
361
|
-
await context.clock.pauseAt(1e3);
|
|
362
|
-
}, { internal: true });
|
|
363
|
-
} else if (process.env.PW_CLOCK === "realtime") {
|
|
364
|
-
await context._wrapApiCall(async () => {
|
|
365
|
-
await context.clock.install({ time: 0 });
|
|
366
|
-
}, { internal: true });
|
|
367
|
-
}
|
|
368
405
|
let closed = false;
|
|
369
406
|
const close = async () => {
|
|
370
407
|
if (closed)
|
|
@@ -372,8 +409,7 @@ const playwrightFixtures = {
|
|
|
372
409
|
closed = true;
|
|
373
410
|
const closeReason = testInfo.status === "timedOut" ? "Test timeout of " + testInfo.timeout + "ms exceeded." : "Test ended.";
|
|
374
411
|
await context.close({ reason: closeReason });
|
|
375
|
-
const
|
|
376
|
-
const preserveVideo = captureVideo && (videoMode === "on" || testFailed && videoMode === "retain-on-failure" || videoMode === "on-first-retry" && testInfo.retry === 1);
|
|
412
|
+
const preserveVideo = captureVideo && shouldPreserveVideo(videoMode, testInfo);
|
|
377
413
|
if (preserveVideo) {
|
|
378
414
|
const { pagesWithVideo: pagesForVideo } = contexts.get(context);
|
|
379
415
|
const videos = pagesForVideo.map((p) => p.video()).filter((video2) => !!video2);
|
|
@@ -405,18 +441,20 @@ const playwrightFixtures = {
|
|
|
405
441
|
const reuse = mode === "when-possible" && normalizeVideoMode(video) === "off";
|
|
406
442
|
await use(reuse);
|
|
407
443
|
}, { scope: "worker", title: "context", box: true }],
|
|
408
|
-
context: async ({ browser, _reuseContext, _contextFactory }, use,
|
|
444
|
+
context: async ({ browser, video, _reuseContext, _contextFactory }, use, testInfoPublic) => {
|
|
409
445
|
const browserImpl = browser;
|
|
446
|
+
const testInfo = testInfoPublic;
|
|
447
|
+
const show = typeof video === "string" ? void 0 : video.show;
|
|
410
448
|
attachConnectedHeaderIfNeeded(testInfo, browserImpl);
|
|
411
449
|
if (!_reuseContext) {
|
|
412
450
|
const { context: context2, close } = await _contextFactory();
|
|
413
|
-
|
|
451
|
+
await installScreencastTitleUpdater(testInfo, context2, show?.test);
|
|
414
452
|
await use(context2);
|
|
415
453
|
await close();
|
|
416
454
|
return;
|
|
417
455
|
}
|
|
418
456
|
const context = await browserImpl._wrapApiCall(() => browserImpl._newContextForReuse(), { internal: true });
|
|
419
|
-
|
|
457
|
+
await installScreencastTitleUpdater(testInfo, context, show?.test);
|
|
420
458
|
await use(context);
|
|
421
459
|
const closeReason = testInfo.status === "timedOut" ? "Test timeout of " + testInfo.timeout + "ms exceeded." : "Test ended.";
|
|
422
460
|
await browserImpl._wrapApiCall(() => browserImpl._disconnectFromReusedContext(closeReason), { internal: true });
|
|
@@ -430,54 +468,6 @@ const playwrightFixtures = {
|
|
|
430
468
|
if (!page)
|
|
431
469
|
page = await context.newPage();
|
|
432
470
|
await use(page);
|
|
433
|
-
},
|
|
434
|
-
agent: async ({ page, agentOptions }, use, testInfo) => {
|
|
435
|
-
const testInfoImpl = testInfo;
|
|
436
|
-
const cachePathTemplate = agentOptions?.cachePathTemplate ?? "{testDir}/{testFilePath}-cache.json";
|
|
437
|
-
const resolvedCacheFile = testInfoImpl._applyPathTemplate(cachePathTemplate, "", ".json");
|
|
438
|
-
const cacheFile = testInfoImpl.config.runAgents === "all" ? void 0 : await testInfoImpl._cloneStorage(resolvedCacheFile);
|
|
439
|
-
const cacheOutFile = import_path.default.join(testInfoImpl.artifactsDir(), "agent-cache-" + (0, import_utils.createGuid)() + ".json");
|
|
440
|
-
const provider = agentOptions?.provider && testInfo.config.runAgents !== "none" ? agentOptions.provider : void 0;
|
|
441
|
-
if (provider)
|
|
442
|
-
testInfo.setTimeout(0);
|
|
443
|
-
const cache = {
|
|
444
|
-
cacheFile,
|
|
445
|
-
cacheOutFile
|
|
446
|
-
};
|
|
447
|
-
const agent = await page.agent({
|
|
448
|
-
provider,
|
|
449
|
-
cache,
|
|
450
|
-
limits: agentOptions?.limits,
|
|
451
|
-
secrets: agentOptions?.secrets,
|
|
452
|
-
systemPrompt: agentOptions?.systemPrompt,
|
|
453
|
-
expect: {
|
|
454
|
-
timeout: testInfoImpl._projectInternal.expect?.timeout
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
await use(agent);
|
|
458
|
-
const usage = await agent.usage();
|
|
459
|
-
if (usage.turns > 0)
|
|
460
|
-
await testInfoImpl.attach("agent-usage", { contentType: "application/json", body: Buffer.from(JSON.stringify(usage, null, 2)) });
|
|
461
|
-
if (!resolvedCacheFile || !cacheOutFile)
|
|
462
|
-
return;
|
|
463
|
-
if (testInfo.status !== "passed")
|
|
464
|
-
return;
|
|
465
|
-
await testInfoImpl._upstreamStorage(resolvedCacheFile, cacheOutFile);
|
|
466
|
-
},
|
|
467
|
-
request: async ({ playwright }, use) => {
|
|
468
|
-
const request = await playwright.request.newContext();
|
|
469
|
-
await use(request);
|
|
470
|
-
const hook = test.info()._currentHookType();
|
|
471
|
-
if (hook === "beforeAll") {
|
|
472
|
-
await request.dispose({ reason: [
|
|
473
|
-
`Fixture { request } from beforeAll cannot be reused in a test.`,
|
|
474
|
-
` - Recommended fix: use a separate { request } in the test.`,
|
|
475
|
-
` - Alternatively, manually create APIRequestContext in beforeAll and dispose it in afterAll.`,
|
|
476
|
-
`See https://playwright.dev/docs/api-testing#sending-api-requests-from-ui-tests for more details.`
|
|
477
|
-
].join("\n") });
|
|
478
|
-
} else {
|
|
479
|
-
await request.dispose();
|
|
480
|
-
}
|
|
481
471
|
}
|
|
482
472
|
};
|
|
483
473
|
function normalizeVideoMode(video) {
|
|
@@ -489,7 +479,23 @@ function normalizeVideoMode(video) {
|
|
|
489
479
|
return videoMode;
|
|
490
480
|
}
|
|
491
481
|
function shouldCaptureVideo(videoMode, testInfo) {
|
|
492
|
-
return videoMode === "on" || videoMode === "retain-on-failure" || videoMode === "on-first-retry" && testInfo.retry === 1;
|
|
482
|
+
return videoMode === "on" || videoMode === "retain-on-failure" || videoMode === "retain-on-failure-and-retries" || videoMode === "on-first-retry" && testInfo.retry === 1 || videoMode === "on-all-retries" && testInfo.retry > 0 || videoMode === "retain-on-first-failure" && testInfo.retry === 0;
|
|
483
|
+
}
|
|
484
|
+
function shouldPreserveVideo(videoMode, testInfo) {
|
|
485
|
+
const testFailed = testInfo.status !== testInfo.expectedStatus;
|
|
486
|
+
switch (videoMode) {
|
|
487
|
+
case "on":
|
|
488
|
+
case "on-first-retry":
|
|
489
|
+
case "on-all-retries":
|
|
490
|
+
return true;
|
|
491
|
+
case "retain-on-failure":
|
|
492
|
+
case "retain-on-first-failure":
|
|
493
|
+
return testFailed;
|
|
494
|
+
case "retain-on-failure-and-retries":
|
|
495
|
+
return testFailed || testInfo.retry > 0;
|
|
496
|
+
default:
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
493
499
|
}
|
|
494
500
|
function normalizeScreenshotMode(screenshot) {
|
|
495
501
|
if (!screenshot)
|
|
@@ -512,12 +518,12 @@ function attachConnectedHeaderIfNeeded(testInfo, browser) {
|
|
|
512
518
|
}
|
|
513
519
|
}
|
|
514
520
|
function resolveFileToConfig(file) {
|
|
515
|
-
const
|
|
516
|
-
if (!
|
|
521
|
+
const config2 = test.info().config.configFile;
|
|
522
|
+
if (!config2 || !file)
|
|
517
523
|
return file;
|
|
518
524
|
if (import_path.default.isAbsolute(file))
|
|
519
525
|
return file;
|
|
520
|
-
return import_path.default.resolve(import_path.default.dirname(
|
|
526
|
+
return import_path.default.resolve(import_path.default.dirname(config2), file);
|
|
521
527
|
}
|
|
522
528
|
function resolveClientCerticates(clientCertificates) {
|
|
523
529
|
for (const cert of clientCertificates) {
|
|
@@ -596,7 +602,7 @@ class SnapshotRecorder {
|
|
|
596
602
|
return;
|
|
597
603
|
page[this.testInfo._uniqueSymbol] = true;
|
|
598
604
|
try {
|
|
599
|
-
const path2 = temporary ? this._createTemporaryArtifact(
|
|
605
|
+
const path2 = temporary ? this._createTemporaryArtifact(createGuid() + this._extension) : this._createAttachmentPath();
|
|
600
606
|
await this._doSnapshot(page, path2);
|
|
601
607
|
if (temporary)
|
|
602
608
|
this._temporary.push(path2);
|
|
@@ -625,7 +631,7 @@ class ArtifactsRecorder {
|
|
|
625
631
|
}
|
|
626
632
|
async willStartTest(testInfo) {
|
|
627
633
|
this._testInfo = testInfo;
|
|
628
|
-
testInfo.
|
|
634
|
+
testInfo._onDidFinishTestFunctionCallbacks.add(() => this.didFinishTestFunction());
|
|
629
635
|
this._screenshotRecorder.fixOrdinal();
|
|
630
636
|
await Promise.all(this._playwright._allContexts().map((context) => this.didCreateBrowserContext(context)));
|
|
631
637
|
const existingApiRequests = Array.from(this._playwright.request._contexts);
|
|
@@ -644,6 +650,8 @@ class ArtifactsRecorder {
|
|
|
644
650
|
return;
|
|
645
651
|
if (this._testInfo.errors.length === 0)
|
|
646
652
|
return;
|
|
653
|
+
if (this._testInfo.errors.some((e) => e.errorContext))
|
|
654
|
+
return;
|
|
647
655
|
if (this._pageSnapshot)
|
|
648
656
|
return;
|
|
649
657
|
const page = context.pages()[0];
|
|
@@ -651,16 +659,16 @@ class ArtifactsRecorder {
|
|
|
651
659
|
return;
|
|
652
660
|
try {
|
|
653
661
|
await page._wrapApiCall(async () => {
|
|
654
|
-
this._pageSnapshot =
|
|
662
|
+
this._pageSnapshot = await page.ariaSnapshot({ mode: "ai", timeout: 5e3 });
|
|
655
663
|
}, { internal: true });
|
|
656
664
|
} catch {
|
|
657
665
|
}
|
|
658
666
|
}
|
|
659
667
|
async didCreateRequestContext(context) {
|
|
660
|
-
await this._startTraceChunkOnContextCreation(context, context.
|
|
668
|
+
await this._startTraceChunkOnContextCreation(context, context.tracing);
|
|
661
669
|
}
|
|
662
670
|
async willCloseRequestContext(context) {
|
|
663
|
-
await this._stopTracing(context, context.
|
|
671
|
+
await this._stopTracing(context, context.tracing);
|
|
664
672
|
}
|
|
665
673
|
async didFinishTestFunction() {
|
|
666
674
|
await this._screenshotRecorder.maybeCapture();
|
|
@@ -717,27 +725,29 @@ class ArtifactsRecorder {
|
|
|
717
725
|
await Promise.all(leftoverContexts.map(async (context2) => {
|
|
718
726
|
await this._stopTracing(context2, context2.tracing);
|
|
719
727
|
}).concat(leftoverApiRequests.map(async (context2) => {
|
|
720
|
-
await this._stopTracing(context2, context2.
|
|
728
|
+
await this._stopTracing(context2, context2.tracing);
|
|
721
729
|
})));
|
|
722
730
|
await this._screenshotRecorder.persistTemporary();
|
|
723
731
|
const context = leftoverContexts[0];
|
|
724
732
|
if (context)
|
|
725
733
|
await this._takePageSnapshot(context);
|
|
726
|
-
if (this.
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
this.
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
734
|
+
if (this._testInfo.errors.length > 0) {
|
|
735
|
+
const hasMatcherAriaSnapshot = this._testInfo.errors.some((e) => e.errorContext);
|
|
736
|
+
const errorContextContent = (0, import_errorContext.buildErrorContext)({
|
|
737
|
+
titlePath: this._testInfo.titlePath,
|
|
738
|
+
location: { file: this._testInfo.file, line: this._testInfo.line, column: this._testInfo.column },
|
|
739
|
+
errors: this._testInfo.errors,
|
|
740
|
+
pageSnapshot: hasMatcherAriaSnapshot ? void 0 : this._pageSnapshot
|
|
741
|
+
});
|
|
742
|
+
if (errorContextContent) {
|
|
743
|
+
const filePath = this._testInfo.outputPath("error-context.md");
|
|
744
|
+
await import_fs.default.promises.writeFile(filePath, errorContextContent, "utf8");
|
|
745
|
+
this._testInfo._attach({
|
|
746
|
+
name: "error-context",
|
|
747
|
+
contentType: "text/markdown",
|
|
748
|
+
path: filePath
|
|
749
|
+
}, void 0);
|
|
750
|
+
}
|
|
741
751
|
}
|
|
742
752
|
}
|
|
743
753
|
async _startTraceChunkOnContextCreation(channelOwner, tracing2) {
|
|
@@ -805,7 +815,7 @@ async function captureResultScreenshot(channel, data, testInfo) {
|
|
|
805
815
|
style: "x-pw-glass { display: none !important; }"
|
|
806
816
|
});
|
|
807
817
|
}, { internal: true });
|
|
808
|
-
const { full, cropped } =
|
|
818
|
+
const { full, cropped } = import_coreBundle.server.compositeHighlight(pngBuffer, box, cssViewport);
|
|
809
819
|
const safeTestId = testInfo.testId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
810
820
|
const safeStepId = (data.stepId ?? "unknown").replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
811
821
|
const screenshotDir = import_path.default.join(process.cwd(), ".smoothdeploy-output", "screenshots", safeTestId);
|
|
@@ -821,20 +831,67 @@ async function captureResultScreenshot(channel, data, testInfo) {
|
|
|
821
831
|
_capturingResultScreenshot = false;
|
|
822
832
|
}
|
|
823
833
|
}
|
|
834
|
+
async function installScreencastTitleUpdater(testInfo, context, testAnnotate) {
|
|
835
|
+
if (!testAnnotate)
|
|
836
|
+
return;
|
|
837
|
+
const testTitle = testAnnotate.level === "file" ? [testInfo.titlePath[0]] : testInfo.titlePath;
|
|
838
|
+
const stepStack = [];
|
|
839
|
+
const overlays = /* @__PURE__ */ new Map();
|
|
840
|
+
const position = testAnnotate.position ?? "top-left";
|
|
841
|
+
const fontSize = testAnnotate.fontSize ?? 14;
|
|
842
|
+
const level = testAnnotate.level ?? "step";
|
|
843
|
+
const updateOverlay = async () => {
|
|
844
|
+
const parts = level === "step" ? [...testTitle, ...stepStack] : testTitle;
|
|
845
|
+
const html = createTestOverlay(parts, position, fontSize);
|
|
846
|
+
for (const page of context.pages()) {
|
|
847
|
+
await overlays.get(page)?.dispose();
|
|
848
|
+
overlays.delete(page);
|
|
849
|
+
const disposable = await page.screencast.showOverlay(html);
|
|
850
|
+
overlays.set(page, disposable);
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
testInfo._onUserStepBegin = async (title) => {
|
|
854
|
+
stepStack.push(title);
|
|
855
|
+
await updateOverlay();
|
|
856
|
+
};
|
|
857
|
+
testInfo._onUserStepEnd = async () => {
|
|
858
|
+
stepStack.pop();
|
|
859
|
+
await updateOverlay();
|
|
860
|
+
};
|
|
861
|
+
context.on("page", async () => {
|
|
862
|
+
void updateOverlay();
|
|
863
|
+
});
|
|
864
|
+
await updateOverlay();
|
|
865
|
+
}
|
|
866
|
+
function createTestOverlay(parts, position, fontSize) {
|
|
867
|
+
const positionStyles = {
|
|
868
|
+
"top-left": "top: 6px; left: 6px;",
|
|
869
|
+
"top": "top: 6px; left: 50%; transform: translateX(-50%);",
|
|
870
|
+
"top-right": "top: 6px; right: 6px;",
|
|
871
|
+
"bottom-left": "bottom: 6px; left: 6px;",
|
|
872
|
+
"bottom": "bottom: 6px; left: 50%; transform: translateX(-50%);",
|
|
873
|
+
"bottom-right": "bottom: 6px; right: 6px;"
|
|
874
|
+
};
|
|
875
|
+
const posStyle = positionStyles[position] ?? positionStyles["top-left"];
|
|
876
|
+
return `<div style="white-space: nowrap; font-size: ${fontSize}px; padding: 3px 6px; background: rgba(0,0,0,0.5); color: white; border-radius: 4px; position: absolute; ${posStyle}">
|
|
877
|
+
${parts.map((p) => `<div>${escapeHTML(p)}</div>`).join("")}
|
|
878
|
+
</div>`;
|
|
879
|
+
}
|
|
824
880
|
function renderTitle(type, method, params, title) {
|
|
825
|
-
const prefix =
|
|
881
|
+
const prefix = renderTitleForCall({ title, type, method, params });
|
|
826
882
|
let selector;
|
|
827
883
|
if (params?.["selector"] && typeof params.selector === "string")
|
|
828
|
-
selector =
|
|
884
|
+
selector = asLocatorDescription("javascript", params.selector);
|
|
829
885
|
return prefix + (selector ? ` ${selector}` : "");
|
|
830
886
|
}
|
|
831
887
|
function tracing() {
|
|
832
888
|
return test.info()._tracing;
|
|
833
889
|
}
|
|
834
|
-
const test =
|
|
890
|
+
const test = _utilityTest.extend(playwrightFixtures);
|
|
835
891
|
// Annotate the CommonJS export names for ESM import in node:
|
|
836
892
|
0 && (module.exports = {
|
|
837
893
|
_baseTest,
|
|
894
|
+
_utilityTest,
|
|
838
895
|
defineConfig,
|
|
839
896
|
expect,
|
|
840
897
|
mergeExpects,
|