@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.
Files changed (182) hide show
  1. package/ThirdPartyNotices.txt +8 -5036
  2. package/lib/agents/agentParser.js +2 -2
  3. package/lib/agents/generateAgents.js +21 -22
  4. package/lib/agents/playwright-test-healer.agent.md +1 -0
  5. package/lib/agents/playwright-test-planner.agent.md +2 -1
  6. package/lib/cli/reportActions.js +78 -0
  7. package/lib/cli/testActions.js +211 -0
  8. package/lib/common/index.js +2898 -0
  9. package/lib/common/index.js.txt +35 -0
  10. package/lib/errorContext.js +130 -0
  11. package/lib/index.js +258 -206
  12. package/lib/{isomorphic/testServerConnection.js → isomorphic.js} +70 -35
  13. package/lib/isomorphic.js.txt +9 -0
  14. package/lib/loader/loaderProcessEntry.js +34 -0
  15. package/lib/loader/loaderProcessEntry.js.txt +9 -0
  16. package/lib/matchers/expect.js +12988 -249
  17. package/lib/matchers/expect.js.LICENSE +693 -0
  18. package/lib/matchers/expect.js.txt +72 -0
  19. package/lib/mcp/test/browserBackend.js +49 -22
  20. package/lib/mcp/test/generatorTools.js +16 -16
  21. package/lib/mcp/test/plannerTools.js +25 -20
  22. package/lib/mcp/test/seed.js +7 -7
  23. package/lib/mcp/test/testBackend.js +30 -30
  24. package/lib/mcp/test/testContext.js +50 -33
  25. package/lib/mcp/test/testTools.js +15 -25
  26. package/lib/{internalsForTest.js → package.js} +13 -8
  27. package/lib/program.js +63 -263
  28. package/lib/runner/index.js +8339 -0
  29. package/lib/runner/index.js.txt +60 -0
  30. package/lib/transform/babelBundle.js +71002 -18
  31. package/lib/transform/babelBundle.js.LICENSE +2359 -0
  32. package/lib/transform/babelBundle.js.txt +325 -0
  33. package/lib/transform/esmLoader.js +5884 -30
  34. package/lib/transform/esmLoader.js.LICENSE +335 -0
  35. package/lib/transform/esmLoader.js.txt +55 -0
  36. package/lib/util.js +37 -34
  37. package/lib/worker/workerProcessEntry.js +3251 -0
  38. package/lib/worker/workerProcessEntry.js.txt +24 -0
  39. package/package.json +5 -16
  40. package/test.mjs +1 -0
  41. package/types/test.d.ts +172 -12
  42. package/types/testReporter.d.ts +7 -5
  43. package/lib/common/config.js +0 -282
  44. package/lib/common/configLoader.js +0 -344
  45. package/lib/common/esmLoaderHost.js +0 -104
  46. package/lib/common/expectBundle.js +0 -28
  47. package/lib/common/expectBundleImpl.js +0 -407
  48. package/lib/common/fixtures.js +0 -302
  49. package/lib/common/ipc.js +0 -60
  50. package/lib/common/poolBuilder.js +0 -85
  51. package/lib/common/process.js +0 -132
  52. package/lib/common/suiteUtils.js +0 -140
  53. package/lib/common/test.js +0 -321
  54. package/lib/common/testLoader.js +0 -101
  55. package/lib/common/testType.js +0 -298
  56. package/lib/common/validators.js +0 -68
  57. package/lib/fsWatcher.js +0 -67
  58. package/lib/isomorphic/events.js +0 -77
  59. package/lib/isomorphic/folders.js +0 -30
  60. package/lib/isomorphic/stringInternPool.js +0 -69
  61. package/lib/isomorphic/teleReceiver.js +0 -521
  62. package/lib/isomorphic/teleSuiteUpdater.js +0 -157
  63. package/lib/isomorphic/testServerInterface.js +0 -16
  64. package/lib/isomorphic/testTree.js +0 -329
  65. package/lib/isomorphic/types.d.js +0 -16
  66. package/lib/loader/loaderMain.js +0 -59
  67. package/lib/matchers/matcherHint.js +0 -44
  68. package/lib/matchers/matchers.js +0 -383
  69. package/lib/matchers/toBeTruthy.js +0 -75
  70. package/lib/matchers/toEqual.js +0 -100
  71. package/lib/matchers/toHaveURL.js +0 -101
  72. package/lib/matchers/toMatchAriaSnapshot.js +0 -159
  73. package/lib/matchers/toMatchSnapshot.js +0 -342
  74. package/lib/matchers/toMatchText.js +0 -99
  75. package/lib/mcp/browser/browserContextFactory.js +0 -329
  76. package/lib/mcp/browser/browserServerBackend.js +0 -84
  77. package/lib/mcp/browser/config.js +0 -421
  78. package/lib/mcp/browser/context.js +0 -244
  79. package/lib/mcp/browser/response.js +0 -278
  80. package/lib/mcp/browser/sessionLog.js +0 -75
  81. package/lib/mcp/browser/tab.js +0 -343
  82. package/lib/mcp/browser/tools/common.js +0 -65
  83. package/lib/mcp/browser/tools/console.js +0 -46
  84. package/lib/mcp/browser/tools/dialogs.js +0 -60
  85. package/lib/mcp/browser/tools/evaluate.js +0 -61
  86. package/lib/mcp/browser/tools/files.js +0 -58
  87. package/lib/mcp/browser/tools/form.js +0 -63
  88. package/lib/mcp/browser/tools/install.js +0 -72
  89. package/lib/mcp/browser/tools/keyboard.js +0 -107
  90. package/lib/mcp/browser/tools/mouse.js +0 -107
  91. package/lib/mcp/browser/tools/navigate.js +0 -71
  92. package/lib/mcp/browser/tools/network.js +0 -63
  93. package/lib/mcp/browser/tools/open.js +0 -57
  94. package/lib/mcp/browser/tools/pdf.js +0 -49
  95. package/lib/mcp/browser/tools/runCode.js +0 -78
  96. package/lib/mcp/browser/tools/screenshot.js +0 -93
  97. package/lib/mcp/browser/tools/snapshot.js +0 -173
  98. package/lib/mcp/browser/tools/tabs.js +0 -67
  99. package/lib/mcp/browser/tools/tool.js +0 -47
  100. package/lib/mcp/browser/tools/tracing.js +0 -74
  101. package/lib/mcp/browser/tools/utils.js +0 -94
  102. package/lib/mcp/browser/tools/verify.js +0 -143
  103. package/lib/mcp/browser/tools/wait.js +0 -63
  104. package/lib/mcp/browser/tools.js +0 -84
  105. package/lib/mcp/browser/watchdog.js +0 -44
  106. package/lib/mcp/config.d.js +0 -16
  107. package/lib/mcp/extension/cdpRelay.js +0 -351
  108. package/lib/mcp/extension/extensionContextFactory.js +0 -76
  109. package/lib/mcp/extension/protocol.js +0 -28
  110. package/lib/mcp/index.js +0 -61
  111. package/lib/mcp/log.js +0 -35
  112. package/lib/mcp/program.js +0 -111
  113. package/lib/mcp/sdk/exports.js +0 -28
  114. package/lib/mcp/sdk/http.js +0 -152
  115. package/lib/mcp/sdk/inProcessTransport.js +0 -71
  116. package/lib/mcp/sdk/server.js +0 -223
  117. package/lib/mcp/sdk/tool.js +0 -47
  118. package/lib/mcp/terminal/cli.js +0 -296
  119. package/lib/mcp/terminal/command.js +0 -56
  120. package/lib/mcp/terminal/commands.js +0 -333
  121. package/lib/mcp/terminal/daemon.js +0 -129
  122. package/lib/mcp/terminal/help.json +0 -32
  123. package/lib/mcp/terminal/helpGenerator.js +0 -88
  124. package/lib/mcp/terminal/socketConnection.js +0 -80
  125. package/lib/plugins/gitCommitInfoPlugin.js +0 -198
  126. package/lib/plugins/index.js +0 -28
  127. package/lib/plugins/webServerPlugin.js +0 -237
  128. package/lib/reporters/base.js +0 -634
  129. package/lib/reporters/blob.js +0 -138
  130. package/lib/reporters/dot.js +0 -99
  131. package/lib/reporters/empty.js +0 -32
  132. package/lib/reporters/github.js +0 -128
  133. package/lib/reporters/html.js +0 -633
  134. package/lib/reporters/internalReporter.js +0 -138
  135. package/lib/reporters/json.js +0 -254
  136. package/lib/reporters/junit.js +0 -232
  137. package/lib/reporters/line.js +0 -131
  138. package/lib/reporters/list.js +0 -253
  139. package/lib/reporters/listModeReporter.js +0 -69
  140. package/lib/reporters/markdown.js +0 -144
  141. package/lib/reporters/merge.js +0 -558
  142. package/lib/reporters/multiplexer.js +0 -112
  143. package/lib/reporters/reporterV2.js +0 -102
  144. package/lib/reporters/smoothdeploy.js +0 -333
  145. package/lib/reporters/teleEmitter.js +0 -317
  146. package/lib/reporters/versions/blobV1.js +0 -16
  147. package/lib/runner/dispatcher.js +0 -531
  148. package/lib/runner/failureTracker.js +0 -72
  149. package/lib/runner/lastRun.js +0 -77
  150. package/lib/runner/loadUtils.js +0 -334
  151. package/lib/runner/loaderHost.js +0 -89
  152. package/lib/runner/processHost.js +0 -180
  153. package/lib/runner/projectUtils.js +0 -241
  154. package/lib/runner/rebase.js +0 -189
  155. package/lib/runner/reporters.js +0 -140
  156. package/lib/runner/sigIntWatcher.js +0 -96
  157. package/lib/runner/storage.js +0 -91
  158. package/lib/runner/taskRunner.js +0 -127
  159. package/lib/runner/tasks.js +0 -410
  160. package/lib/runner/testGroups.js +0 -125
  161. package/lib/runner/testRunner.js +0 -398
  162. package/lib/runner/testServer.js +0 -269
  163. package/lib/runner/uiModeReporter.js +0 -30
  164. package/lib/runner/vcs.js +0 -72
  165. package/lib/runner/watchMode.js +0 -396
  166. package/lib/runner/workerHost.js +0 -104
  167. package/lib/third_party/pirates.js +0 -62
  168. package/lib/third_party/tsconfig-loader.js +0 -103
  169. package/lib/transform/babelBundleImpl.js +0 -461
  170. package/lib/transform/compilationCache.js +0 -274
  171. package/lib/transform/md.js +0 -221
  172. package/lib/transform/portTransport.js +0 -67
  173. package/lib/transform/transform.js +0 -303
  174. package/lib/utilsBundle.js +0 -50
  175. package/lib/utilsBundleImpl.js +0 -103
  176. package/lib/worker/fixtureRunner.js +0 -262
  177. package/lib/worker/testInfo.js +0 -536
  178. package/lib/worker/testTracing.js +0 -345
  179. package/lib/worker/timeoutManager.js +0 -174
  180. package/lib/worker/util.js +0 -31
  181. package/lib/worker/workerMain.js +0 -530
  182. /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
- defineConfig: () => import_configLoader.defineConfig,
32
+ _utilityTest: () => _utilityTest,
33
+ defineConfig: () => import_common2.defineConfig,
33
34
  expect: () => import_expect.expect,
34
35
  mergeExpects: () => import_expect2.mergeExpects,
35
- mergeTests: () => import_testType2.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 import_utils = require("playwright-core/lib/utils");
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 import_globals = require("./common/globals");
46
- var import_testType = require("./common/testType");
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 import_configLoader = require("./common/configLoader");
50
- var import_testType2 = require("./common/testType");
51
+ var import_common2 = require("./common");
51
52
  var import_expect2 = require("./matchers/expect");
52
- const _baseTest = import_testType.rootTestType.test;
53
- (0, import_utils.setBoxedStackPrefixes)([import_path.default.dirname(require.resolve("../package.json"))]);
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 playwrightFixtures = {
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 ?? connectOptions._exposeNetwork,
242
+ exposeNetwork: connectOptions.exposeNetwork,
103
243
  headers: {
104
244
  // HTTP headers are ASCII only (not UTF-8).
105
- "x-playwright-launch-options": (0, import_utils.jsonStringifyForceASCII)(_browserOptions),
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, testInfo) => {
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
- if ((0, import_utils.debugMode)() === "inspector")
226
- testInfo._setDebugMode();
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, testInfo) => {
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
- testInfo._onCustomMessageCallback = (0, import_browserBackend.createCustomMessageHandler)(testInfo, context2);
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
- testInfo._onCustomMessageCallback = (0, import_browserBackend.createCustomMessageHandler)(testInfo, context);
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 config = test.info().config.configFile;
516
- if (!config || !file)
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(config), file);
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((0, import_utils.createGuid)() + this._extension) : this._createAttachmentPath();
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._onDidFinishTestFunctionCallback = () => this.didFinishTestFunction();
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 = (await page._snapshotForAI({ timeout: 5e3 })).full;
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._tracing);
663
+ await this._startTraceChunkOnContextCreation(context, context.tracing);
661
664
  }
662
665
  async willCloseRequestContext(context) {
663
- await this._stopTracing(context, context._tracing);
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._tracing);
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._pageSnapshot && this._testInfo.errors.length > 0 && !this._testInfo.attachments.some((a) => a.name === "error-context")) {
727
- const lines = [
728
- "# Page snapshot",
729
- "",
730
- "```yaml",
731
- this._pageSnapshot,
732
- "```"
733
- ];
734
- const filePath = this._testInfo.outputPath("error-context.md");
735
- await import_fs.default.promises.writeFile(filePath, lines.join("\n"), "utf8");
736
- this._testInfo._attach({
737
- name: "error-context",
738
- contentType: "text/markdown",
739
- path: filePath
740
- }, void 0);
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 } = (0, import_screenshotCompositor.compositeHighlight)(pngBuffer, box, cssViewport);
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 = (0, import_utils.renderTitleForCall)({ title, type, method, params });
876
+ const prefix = renderTitleForCall({ title, type, method, params });
826
877
  let selector;
827
878
  if (params?.["selector"] && typeof params.selector === "string")
828
- selector = (0, import_utils.asLocatorDescription)("javascript", params.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 = _baseTest.extend(playwrightFixtures);
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,