@smoothdeploy/playwright 1.58.4 → 1.60.1
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 +21 -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 +211 -0
- package/lib/common/index.js +2898 -0
- package/lib/common/index.js.txt +35 -0
- package/lib/errorContext.js +130 -0
- package/lib/index.js +258 -206
- 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 +12988 -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 +50 -33
- package/lib/mcp/test/testTools.js +15 -25
- package/lib/{internalsForTest.js → package.js} +13 -8
- package/lib/program.js +63 -263
- package/lib/runner/index.js +8339 -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 +5884 -30
- package/lib/transform/esmLoader.js.LICENSE +335 -0
- package/lib/transform/esmLoader.js.txt +55 -0
- package/lib/util.js +37 -34
- package/lib/worker/workerProcessEntry.js +3251 -0
- package/lib/worker/workerProcessEntry.js.txt +24 -0
- package/package.json +5 -16
- package/test.mjs +1 -0
- package/types/test.d.ts +172 -12
- 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,10 +393,12 @@ 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 });
|
|
@@ -405,18 +452,20 @@ const playwrightFixtures = {
|
|
|
405
452
|
const reuse = mode === "when-possible" && normalizeVideoMode(video) === "off";
|
|
406
453
|
await use(reuse);
|
|
407
454
|
}, { scope: "worker", title: "context", box: true }],
|
|
408
|
-
context: async ({ browser, _reuseContext, _contextFactory }, use,
|
|
455
|
+
context: async ({ browser, video, _reuseContext, _contextFactory }, use, testInfoPublic) => {
|
|
409
456
|
const browserImpl = browser;
|
|
457
|
+
const testInfo = testInfoPublic;
|
|
458
|
+
const show = typeof video === "string" ? void 0 : video.show;
|
|
410
459
|
attachConnectedHeaderIfNeeded(testInfo, browserImpl);
|
|
411
460
|
if (!_reuseContext) {
|
|
412
461
|
const { context: context2, close } = await _contextFactory();
|
|
413
|
-
|
|
462
|
+
await installScreencastTitleUpdater(testInfo, context2, show?.test);
|
|
414
463
|
await use(context2);
|
|
415
464
|
await close();
|
|
416
465
|
return;
|
|
417
466
|
}
|
|
418
467
|
const context = await browserImpl._wrapApiCall(() => browserImpl._newContextForReuse(), { internal: true });
|
|
419
|
-
|
|
468
|
+
await installScreencastTitleUpdater(testInfo, context, show?.test);
|
|
420
469
|
await use(context);
|
|
421
470
|
const closeReason = testInfo.status === "timedOut" ? "Test timeout of " + testInfo.timeout + "ms exceeded." : "Test ended.";
|
|
422
471
|
await browserImpl._wrapApiCall(() => browserImpl._disconnectFromReusedContext(closeReason), { internal: true });
|
|
@@ -430,54 +479,6 @@ const playwrightFixtures = {
|
|
|
430
479
|
if (!page)
|
|
431
480
|
page = await context.newPage();
|
|
432
481
|
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
482
|
}
|
|
482
483
|
};
|
|
483
484
|
function normalizeVideoMode(video) {
|
|
@@ -512,12 +513,12 @@ function attachConnectedHeaderIfNeeded(testInfo, browser) {
|
|
|
512
513
|
}
|
|
513
514
|
}
|
|
514
515
|
function resolveFileToConfig(file) {
|
|
515
|
-
const
|
|
516
|
-
if (!
|
|
516
|
+
const config2 = test.info().config.configFile;
|
|
517
|
+
if (!config2 || !file)
|
|
517
518
|
return file;
|
|
518
519
|
if (import_path.default.isAbsolute(file))
|
|
519
520
|
return file;
|
|
520
|
-
return import_path.default.resolve(import_path.default.dirname(
|
|
521
|
+
return import_path.default.resolve(import_path.default.dirname(config2), file);
|
|
521
522
|
}
|
|
522
523
|
function resolveClientCerticates(clientCertificates) {
|
|
523
524
|
for (const cert of clientCertificates) {
|
|
@@ -596,7 +597,7 @@ class SnapshotRecorder {
|
|
|
596
597
|
return;
|
|
597
598
|
page[this.testInfo._uniqueSymbol] = true;
|
|
598
599
|
try {
|
|
599
|
-
const path2 = temporary ? this._createTemporaryArtifact(
|
|
600
|
+
const path2 = temporary ? this._createTemporaryArtifact(createGuid() + this._extension) : this._createAttachmentPath();
|
|
600
601
|
await this._doSnapshot(page, path2);
|
|
601
602
|
if (temporary)
|
|
602
603
|
this._temporary.push(path2);
|
|
@@ -625,7 +626,7 @@ class ArtifactsRecorder {
|
|
|
625
626
|
}
|
|
626
627
|
async willStartTest(testInfo) {
|
|
627
628
|
this._testInfo = testInfo;
|
|
628
|
-
testInfo.
|
|
629
|
+
testInfo._onDidFinishTestFunctionCallbacks.add(() => this.didFinishTestFunction());
|
|
629
630
|
this._screenshotRecorder.fixOrdinal();
|
|
630
631
|
await Promise.all(this._playwright._allContexts().map((context) => this.didCreateBrowserContext(context)));
|
|
631
632
|
const existingApiRequests = Array.from(this._playwright.request._contexts);
|
|
@@ -644,6 +645,8 @@ class ArtifactsRecorder {
|
|
|
644
645
|
return;
|
|
645
646
|
if (this._testInfo.errors.length === 0)
|
|
646
647
|
return;
|
|
648
|
+
if (this._testInfo.errors.some((e) => e.errorContext))
|
|
649
|
+
return;
|
|
647
650
|
if (this._pageSnapshot)
|
|
648
651
|
return;
|
|
649
652
|
const page = context.pages()[0];
|
|
@@ -651,16 +654,16 @@ class ArtifactsRecorder {
|
|
|
651
654
|
return;
|
|
652
655
|
try {
|
|
653
656
|
await page._wrapApiCall(async () => {
|
|
654
|
-
this._pageSnapshot =
|
|
657
|
+
this._pageSnapshot = await page.ariaSnapshot({ mode: "ai", timeout: 5e3 });
|
|
655
658
|
}, { internal: true });
|
|
656
659
|
} catch {
|
|
657
660
|
}
|
|
658
661
|
}
|
|
659
662
|
async didCreateRequestContext(context) {
|
|
660
|
-
await this._startTraceChunkOnContextCreation(context, context.
|
|
663
|
+
await this._startTraceChunkOnContextCreation(context, context.tracing);
|
|
661
664
|
}
|
|
662
665
|
async willCloseRequestContext(context) {
|
|
663
|
-
await this._stopTracing(context, context.
|
|
666
|
+
await this._stopTracing(context, context.tracing);
|
|
664
667
|
}
|
|
665
668
|
async didFinishTestFunction() {
|
|
666
669
|
await this._screenshotRecorder.maybeCapture();
|
|
@@ -717,27 +720,29 @@ class ArtifactsRecorder {
|
|
|
717
720
|
await Promise.all(leftoverContexts.map(async (context2) => {
|
|
718
721
|
await this._stopTracing(context2, context2.tracing);
|
|
719
722
|
}).concat(leftoverApiRequests.map(async (context2) => {
|
|
720
|
-
await this._stopTracing(context2, context2.
|
|
723
|
+
await this._stopTracing(context2, context2.tracing);
|
|
721
724
|
})));
|
|
722
725
|
await this._screenshotRecorder.persistTemporary();
|
|
723
726
|
const context = leftoverContexts[0];
|
|
724
727
|
if (context)
|
|
725
728
|
await this._takePageSnapshot(context);
|
|
726
|
-
if (this.
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
this.
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
729
|
+
if (this._testInfo.errors.length > 0) {
|
|
730
|
+
const hasMatcherAriaSnapshot = this._testInfo.errors.some((e) => e.errorContext);
|
|
731
|
+
const errorContextContent = (0, import_errorContext.buildErrorContext)({
|
|
732
|
+
titlePath: this._testInfo.titlePath,
|
|
733
|
+
location: { file: this._testInfo.file, line: this._testInfo.line, column: this._testInfo.column },
|
|
734
|
+
errors: this._testInfo.errors,
|
|
735
|
+
pageSnapshot: hasMatcherAriaSnapshot ? void 0 : this._pageSnapshot
|
|
736
|
+
});
|
|
737
|
+
if (errorContextContent) {
|
|
738
|
+
const filePath = this._testInfo.outputPath("error-context.md");
|
|
739
|
+
await import_fs.default.promises.writeFile(filePath, errorContextContent, "utf8");
|
|
740
|
+
this._testInfo._attach({
|
|
741
|
+
name: "error-context",
|
|
742
|
+
contentType: "text/markdown",
|
|
743
|
+
path: filePath
|
|
744
|
+
}, void 0);
|
|
745
|
+
}
|
|
741
746
|
}
|
|
742
747
|
}
|
|
743
748
|
async _startTraceChunkOnContextCreation(channelOwner, tracing2) {
|
|
@@ -805,7 +810,7 @@ async function captureResultScreenshot(channel, data, testInfo) {
|
|
|
805
810
|
style: "x-pw-glass { display: none !important; }"
|
|
806
811
|
});
|
|
807
812
|
}, { internal: true });
|
|
808
|
-
const { full, cropped } =
|
|
813
|
+
const { full, cropped } = import_coreBundle.server.compositeHighlight(pngBuffer, box, cssViewport);
|
|
809
814
|
const safeTestId = testInfo.testId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
810
815
|
const safeStepId = (data.stepId ?? "unknown").replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
811
816
|
const screenshotDir = import_path.default.join(process.cwd(), ".smoothdeploy-output", "screenshots", safeTestId);
|
|
@@ -821,20 +826,67 @@ async function captureResultScreenshot(channel, data, testInfo) {
|
|
|
821
826
|
_capturingResultScreenshot = false;
|
|
822
827
|
}
|
|
823
828
|
}
|
|
829
|
+
async function installScreencastTitleUpdater(testInfo, context, testAnnotate) {
|
|
830
|
+
if (!testAnnotate)
|
|
831
|
+
return;
|
|
832
|
+
const testTitle = testAnnotate.level === "file" ? [testInfo.titlePath[0]] : testInfo.titlePath;
|
|
833
|
+
const stepStack = [];
|
|
834
|
+
const overlays = /* @__PURE__ */ new Map();
|
|
835
|
+
const position = testAnnotate.position ?? "top-left";
|
|
836
|
+
const fontSize = testAnnotate.fontSize ?? 14;
|
|
837
|
+
const level = testAnnotate.level ?? "step";
|
|
838
|
+
const updateOverlay = async () => {
|
|
839
|
+
const parts = level === "step" ? [...testTitle, ...stepStack] : testTitle;
|
|
840
|
+
const html = createTestOverlay(parts, position, fontSize);
|
|
841
|
+
for (const page of context.pages()) {
|
|
842
|
+
await overlays.get(page)?.dispose();
|
|
843
|
+
overlays.delete(page);
|
|
844
|
+
const disposable = await page.screencast.showOverlay(html);
|
|
845
|
+
overlays.set(page, disposable);
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
testInfo._onUserStepBegin = async (title) => {
|
|
849
|
+
stepStack.push(title);
|
|
850
|
+
await updateOverlay();
|
|
851
|
+
};
|
|
852
|
+
testInfo._onUserStepEnd = async () => {
|
|
853
|
+
stepStack.pop();
|
|
854
|
+
await updateOverlay();
|
|
855
|
+
};
|
|
856
|
+
context.on("page", async () => {
|
|
857
|
+
void updateOverlay();
|
|
858
|
+
});
|
|
859
|
+
await updateOverlay();
|
|
860
|
+
}
|
|
861
|
+
function createTestOverlay(parts, position, fontSize) {
|
|
862
|
+
const positionStyles = {
|
|
863
|
+
"top-left": "top: 6px; left: 6px;",
|
|
864
|
+
"top": "top: 6px; left: 50%; transform: translateX(-50%);",
|
|
865
|
+
"top-right": "top: 6px; right: 6px;",
|
|
866
|
+
"bottom-left": "bottom: 6px; left: 6px;",
|
|
867
|
+
"bottom": "bottom: 6px; left: 50%; transform: translateX(-50%);",
|
|
868
|
+
"bottom-right": "bottom: 6px; right: 6px;"
|
|
869
|
+
};
|
|
870
|
+
const posStyle = positionStyles[position] ?? positionStyles["top-left"];
|
|
871
|
+
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}">
|
|
872
|
+
${parts.map((p) => `<div>${escapeHTML(p)}</div>`).join("")}
|
|
873
|
+
</div>`;
|
|
874
|
+
}
|
|
824
875
|
function renderTitle(type, method, params, title) {
|
|
825
|
-
const prefix =
|
|
876
|
+
const prefix = renderTitleForCall({ title, type, method, params });
|
|
826
877
|
let selector;
|
|
827
878
|
if (params?.["selector"] && typeof params.selector === "string")
|
|
828
|
-
selector =
|
|
879
|
+
selector = asLocatorDescription("javascript", params.selector);
|
|
829
880
|
return prefix + (selector ? ` ${selector}` : "");
|
|
830
881
|
}
|
|
831
882
|
function tracing() {
|
|
832
883
|
return test.info()._tracing;
|
|
833
884
|
}
|
|
834
|
-
const test =
|
|
885
|
+
const test = _utilityTest.extend(playwrightFixtures);
|
|
835
886
|
// Annotate the CommonJS export names for ESM import in node:
|
|
836
887
|
0 && (module.exports = {
|
|
837
888
|
_baseTest,
|
|
889
|
+
_utilityTest,
|
|
838
890
|
defineConfig,
|
|
839
891
|
expect,
|
|
840
892
|
mergeExpects,
|