@smoothdeploy/playwright 1.57.1 → 1.58.1-beta-1770383926000

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @smoothdeploy/playwright might be problematic. Click here for more details.

Files changed (264) hide show
  1. package/ThirdPartyNotices.txt +1188 -65
  2. package/lib/agents/agentParser.js +89 -0
  3. package/lib/agents/generateAgents.js +27 -74
  4. package/lib/agents/generateAgents.js.map +7 -0
  5. package/lib/agents/playwright-test-planner.agent.md +1 -0
  6. package/lib/common/config.js +6 -4
  7. package/lib/common/config.js.map +7 -0
  8. package/lib/common/configLoader.js.map +7 -0
  9. package/lib/common/esmLoaderHost.js +2 -0
  10. package/lib/common/esmLoaderHost.js.map +7 -0
  11. package/lib/common/expectBundle.js +2 -17
  12. package/lib/common/expectBundle.js.map +7 -0
  13. package/lib/common/expectBundleImpl.js +132 -132
  14. package/lib/common/expectBundleImpl.js.map +7 -0
  15. package/lib/common/fixtures.js.map +7 -0
  16. package/lib/common/globals.js.map +7 -0
  17. package/lib/common/ipc.js.map +7 -0
  18. package/lib/common/poolBuilder.js.map +7 -0
  19. package/lib/common/process.js +28 -0
  20. package/lib/common/process.js.map +7 -0
  21. package/lib/common/suiteUtils.js.map +7 -0
  22. package/lib/common/test.js.map +7 -0
  23. package/lib/common/testLoader.js.map +7 -0
  24. package/lib/common/testType.js.map +7 -0
  25. package/lib/common/validators.js +10 -10
  26. package/lib/common/validators.js.map +7 -0
  27. package/lib/fsWatcher.js.map +7 -0
  28. package/lib/index.js +205 -12
  29. package/lib/index.js.map +7 -0
  30. package/lib/internalsForTest.js.map +7 -0
  31. package/lib/isomorphic/events.js.map +7 -0
  32. package/lib/isomorphic/folders.js.map +7 -0
  33. package/lib/isomorphic/stringInternPool.js.map +7 -0
  34. package/lib/isomorphic/teleReceiver.js +15 -2
  35. package/lib/isomorphic/teleReceiver.js.map +7 -0
  36. package/lib/isomorphic/teleSuiteUpdater.js +24 -4
  37. package/lib/isomorphic/teleSuiteUpdater.js.map +7 -0
  38. package/lib/isomorphic/testServerConnection.js.map +7 -0
  39. package/lib/isomorphic/testServerInterface.js.map +7 -0
  40. package/lib/isomorphic/testTree.js +13 -18
  41. package/lib/isomorphic/testTree.js.map +7 -0
  42. package/lib/isomorphic/types.d.js.map +7 -0
  43. package/lib/loader/loaderMain.js.map +7 -0
  44. package/lib/matchers/expect.js +2 -15
  45. package/lib/matchers/expect.js.map +7 -0
  46. package/lib/matchers/matcherHint.js +1 -44
  47. package/lib/matchers/matcherHint.js.map +7 -0
  48. package/lib/matchers/matchers.js +3 -2
  49. package/lib/matchers/matchers.js.map +7 -0
  50. package/lib/matchers/toBeTruthy.js +5 -3
  51. package/lib/matchers/toBeTruthy.js.map +7 -0
  52. package/lib/matchers/toEqual.js +4 -3
  53. package/lib/matchers/toEqual.js.map +7 -0
  54. package/lib/matchers/toHaveURL.js +5 -6
  55. package/lib/matchers/toHaveURL.js.map +7 -0
  56. package/lib/matchers/toMatchAriaSnapshot.js +5 -5
  57. package/lib/matchers/toMatchAriaSnapshot.js.map +7 -0
  58. package/lib/matchers/toMatchSnapshot.js +9 -8
  59. package/lib/matchers/toMatchSnapshot.js.map +7 -0
  60. package/lib/matchers/toMatchText.js +9 -9
  61. package/lib/matchers/toMatchText.js.map +7 -0
  62. package/lib/mcp/browser/actions.d.js.map +7 -0
  63. package/lib/mcp/browser/browserContextFactory.js +62 -29
  64. package/lib/mcp/browser/browserContextFactory.js.map +7 -0
  65. package/lib/mcp/browser/browserServerBackend.js +17 -9
  66. package/lib/mcp/browser/browserServerBackend.js.map +7 -0
  67. package/lib/mcp/browser/codegen.js.map +7 -0
  68. package/lib/mcp/browser/config.js +65 -12
  69. package/lib/mcp/browser/config.js.map +7 -0
  70. package/lib/mcp/browser/context.js +71 -94
  71. package/lib/mcp/browser/context.js.map +7 -0
  72. package/lib/mcp/browser/response.js +172 -131
  73. package/lib/mcp/browser/response.js.map +7 -0
  74. package/lib/mcp/browser/sessionLog.js +19 -104
  75. package/lib/mcp/browser/sessionLog.js.map +7 -0
  76. package/lib/mcp/browser/tab.js +92 -41
  77. package/lib/mcp/browser/tab.js.map +7 -0
  78. package/lib/mcp/browser/tools/common.js +8 -6
  79. package/lib/mcp/browser/tools/common.js.map +7 -0
  80. package/lib/mcp/browser/tools/console.js +7 -5
  81. package/lib/mcp/browser/tools/console.js.map +7 -0
  82. package/lib/mcp/browser/tools/dialogs.js +4 -4
  83. package/lib/mcp/browser/tools/dialogs.js.map +7 -0
  84. package/lib/mcp/browser/tools/evaluate.js +11 -19
  85. package/lib/mcp/browser/tools/evaluate.js.map +7 -0
  86. package/lib/mcp/browser/tools/files.js +3 -3
  87. package/lib/mcp/browser/tools/files.js.map +7 -0
  88. package/lib/mcp/browser/tools/form.js +9 -19
  89. package/lib/mcp/browser/tools/form.js.map +7 -0
  90. package/lib/mcp/browser/tools/install.js +6 -3
  91. package/lib/mcp/browser/tools/install.js.map +7 -0
  92. package/lib/mcp/browser/tools/keyboard.js +34 -11
  93. package/lib/mcp/browser/tools/keyboard.js.map +7 -0
  94. package/lib/mcp/browser/tools/mouse.js +11 -11
  95. package/lib/mcp/browser/tools/mouse.js.map +7 -0
  96. package/lib/mcp/browser/tools/navigate.js +14 -5
  97. package/lib/mcp/browser/tools/navigate.js.map +7 -0
  98. package/lib/mcp/browser/tools/network.js +20 -11
  99. package/lib/mcp/browser/tools/network.js.map +7 -0
  100. package/lib/mcp/browser/tools/open.js +57 -0
  101. package/lib/mcp/browser/tools/pdf.js +9 -19
  102. package/lib/mcp/browser/tools/pdf.js.map +7 -0
  103. package/lib/mcp/browser/tools/runCode.js +11 -8
  104. package/lib/mcp/browser/tools/runCode.js.map +7 -0
  105. package/lib/mcp/browser/tools/screenshot.js +16 -29
  106. package/lib/mcp/browser/tools/screenshot.js.map +7 -0
  107. package/lib/mcp/browser/tools/snapshot.js +21 -29
  108. package/lib/mcp/browser/tools/snapshot.js.map +7 -0
  109. package/lib/mcp/browser/tools/tabs.js +12 -12
  110. package/lib/mcp/browser/tools/tabs.js.map +7 -0
  111. package/lib/mcp/browser/tools/tool.js +2 -4
  112. package/lib/mcp/browser/tools/tool.js.map +7 -0
  113. package/lib/mcp/browser/tools/tracing.js +6 -6
  114. package/lib/mcp/browser/tools/tracing.js.map +7 -0
  115. package/lib/mcp/browser/tools/utils.js +49 -44
  116. package/lib/mcp/browser/tools/utils.js.map +7 -0
  117. package/lib/mcp/browser/tools/verify.js +24 -34
  118. package/lib/mcp/browser/tools/verify.js.map +7 -0
  119. package/lib/mcp/browser/tools/wait.js +6 -6
  120. package/lib/mcp/browser/tools/wait.js.map +7 -0
  121. package/lib/mcp/browser/tools.js +3 -1
  122. package/lib/mcp/browser/tools.js.map +7 -0
  123. package/lib/mcp/browser/watchdog.js.map +7 -0
  124. package/lib/mcp/config.d.js.map +7 -0
  125. package/lib/mcp/extension/cdpRelay.js +1 -1
  126. package/lib/mcp/extension/cdpRelay.js.map +7 -0
  127. package/lib/mcp/extension/extensionContextFactory.js +6 -5
  128. package/lib/mcp/extension/extensionContextFactory.js.map +7 -0
  129. package/lib/mcp/extension/protocol.js.map +7 -0
  130. package/lib/mcp/index.js.map +7 -0
  131. package/lib/mcp/log.js.map +7 -0
  132. package/lib/mcp/program.js +15 -20
  133. package/lib/mcp/program.js.map +7 -0
  134. package/lib/mcp/sdk/bundle.js.map +7 -0
  135. package/lib/mcp/sdk/exports.js +0 -2
  136. package/lib/mcp/sdk/exports.js.map +7 -0
  137. package/lib/mcp/sdk/http.js +20 -55
  138. package/lib/mcp/sdk/http.js.map +7 -0
  139. package/lib/mcp/sdk/inProcessTransport.js.map +7 -0
  140. package/lib/mcp/sdk/proxyBackend.js.map +7 -0
  141. package/lib/mcp/sdk/server.js +29 -4
  142. package/lib/mcp/sdk/server.js.map +7 -0
  143. package/lib/mcp/sdk/tool.js +2 -2
  144. package/lib/mcp/sdk/tool.js.map +7 -0
  145. package/lib/mcp/terminal/cli.js +296 -0
  146. package/lib/mcp/terminal/command.js +56 -0
  147. package/lib/mcp/terminal/commands.js +333 -0
  148. package/lib/mcp/terminal/daemon.js +129 -0
  149. package/lib/mcp/terminal/help.json +32 -0
  150. package/lib/mcp/terminal/helpGenerator.js +88 -0
  151. package/lib/mcp/terminal/socketConnection.js +80 -0
  152. package/lib/mcp/test/browserBackend.js +3 -13
  153. package/lib/mcp/test/browserBackend.js.map +7 -0
  154. package/lib/mcp/test/generatorTools.js +9 -9
  155. package/lib/mcp/test/generatorTools.js.map +7 -0
  156. package/lib/mcp/test/plannerTools.js +23 -22
  157. package/lib/mcp/test/plannerTools.js.map +7 -0
  158. package/lib/mcp/test/seed.js.map +7 -0
  159. package/lib/mcp/test/streams.js.map +7 -0
  160. package/lib/mcp/test/testBackend.js +6 -6
  161. package/lib/mcp/test/testBackend.js.map +7 -0
  162. package/lib/mcp/test/testContext.js +9 -3
  163. package/lib/mcp/test/testContext.js.map +7 -0
  164. package/lib/mcp/test/testTool.js.map +7 -0
  165. package/lib/mcp/test/testTools.js +12 -10
  166. package/lib/mcp/test/testTools.js.map +7 -0
  167. package/lib/mcpBundleImpl.js.map +7 -0
  168. package/lib/plugins/gitCommitInfoPlugin.js.map +7 -0
  169. package/lib/plugins/index.js.map +7 -0
  170. package/lib/plugins/webServerPlugin.js.map +7 -0
  171. package/lib/program.js +18 -4
  172. package/lib/program.js.map +7 -0
  173. package/lib/reporters/base.js +29 -4
  174. package/lib/reporters/base.js.map +7 -0
  175. package/lib/reporters/blob.js +3 -0
  176. package/lib/reporters/blob.js.map +7 -0
  177. package/lib/reporters/dot.js +17 -0
  178. package/lib/reporters/dot.js.map +7 -0
  179. package/lib/reporters/empty.js.map +7 -0
  180. package/lib/reporters/github.js.map +7 -0
  181. package/lib/reporters/html.js +13 -3
  182. package/lib/reporters/html.js.map +7 -0
  183. package/lib/reporters/internalReporter.js +6 -0
  184. package/lib/reporters/internalReporter.js.map +7 -0
  185. package/lib/reporters/json.js.map +7 -0
  186. package/lib/reporters/junit.js.map +7 -0
  187. package/lib/reporters/line.js +18 -0
  188. package/lib/reporters/line.js.map +7 -0
  189. package/lib/reporters/list.js +22 -0
  190. package/lib/reporters/list.js.map +7 -0
  191. package/lib/reporters/listModeReporter.js.map +7 -0
  192. package/lib/reporters/markdown.js.map +7 -0
  193. package/lib/reporters/merge.js +25 -8
  194. package/lib/reporters/merge.js.map +7 -0
  195. package/lib/reporters/multiplexer.js +8 -0
  196. package/lib/reporters/multiplexer.js.map +7 -0
  197. package/lib/reporters/reporterV2.js.map +7 -0
  198. package/lib/reporters/smoothdeploy.js +191 -0
  199. package/lib/reporters/teleEmitter.js +22 -4
  200. package/lib/reporters/teleEmitter.js.map +7 -0
  201. package/lib/reporters/versions/blobV1.js.map +7 -0
  202. package/lib/runner/dispatcher.js +20 -4
  203. package/lib/runner/dispatcher.js.map +7 -0
  204. package/lib/runner/failureTracker.js.map +7 -0
  205. package/lib/runner/lastRun.js.map +7 -0
  206. package/lib/runner/loadUtils.js +2 -2
  207. package/lib/runner/loadUtils.js.map +7 -0
  208. package/lib/runner/loaderHost.js.map +7 -0
  209. package/lib/runner/processHost.js +19 -0
  210. package/lib/runner/processHost.js.map +7 -0
  211. package/lib/runner/projectUtils.js +1 -1
  212. package/lib/runner/projectUtils.js.map +7 -0
  213. package/lib/runner/rebase.js.map +7 -0
  214. package/lib/runner/reporters.js +3 -1
  215. package/lib/runner/reporters.js.map +7 -0
  216. package/lib/runner/sigIntWatcher.js.map +7 -0
  217. package/lib/runner/storage.js +91 -0
  218. package/lib/runner/taskRunner.js.map +7 -0
  219. package/lib/runner/tasks.js.map +7 -0
  220. package/lib/runner/testGroups.js +14 -6
  221. package/lib/runner/testGroups.js.map +7 -0
  222. package/lib/runner/testRunner.js +13 -4
  223. package/lib/runner/testRunner.js.map +7 -0
  224. package/lib/runner/testServer.js +2 -2
  225. package/lib/runner/testServer.js.map +7 -0
  226. package/lib/runner/uiModeReporter.js.map +7 -0
  227. package/lib/runner/vcs.js.map +7 -0
  228. package/lib/runner/watchMode.js +2 -1
  229. package/lib/runner/watchMode.js.map +7 -0
  230. package/lib/runner/workerHost.js +6 -0
  231. package/lib/runner/workerHost.js.map +7 -0
  232. package/lib/third_party/pirates.js.map +7 -0
  233. package/lib/third_party/tsconfig-loader.js.map +7 -0
  234. package/lib/transform/babelBundle.js +3 -0
  235. package/lib/transform/babelBundle.js.map +7 -0
  236. package/lib/transform/babelBundleImpl.js +134 -134
  237. package/lib/transform/babelBundleImpl.js.map +7 -0
  238. package/lib/transform/compilationCache.js +2 -0
  239. package/lib/transform/compilationCache.js.map +7 -0
  240. package/lib/transform/esmLoader.js +10 -11
  241. package/lib/transform/esmLoader.js.map +7 -0
  242. package/lib/transform/md.js +221 -0
  243. package/lib/transform/portTransport.js.map +7 -0
  244. package/lib/transform/transform.js +18 -8
  245. package/lib/transform/transform.js.map +7 -0
  246. package/lib/util.js +3 -6
  247. package/lib/util.js.map +7 -0
  248. package/lib/utilsBundle.js +7 -0
  249. package/lib/utilsBundle.js.map +7 -0
  250. package/lib/utilsBundleImpl.js +51 -48
  251. package/lib/utilsBundleImpl.js.map +7 -0
  252. package/lib/worker/fixtureRunner.js +6 -2
  253. package/lib/worker/fixtureRunner.js.map +7 -0
  254. package/lib/worker/testInfo.js +39 -19
  255. package/lib/worker/testInfo.js.map +7 -0
  256. package/lib/worker/testTracing.js.map +7 -0
  257. package/lib/worker/timeoutManager.js.map +7 -0
  258. package/lib/worker/util.js.map +7 -0
  259. package/lib/worker/workerMain.js +17 -16
  260. package/lib/worker/workerMain.js.map +7 -0
  261. package/package.json +2 -2
  262. package/test.mjs +1 -0
  263. package/types/test.d.ts +26 -6
  264. package/types/testReporter.d.ts +1 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/plugins/gitCommitInfoPlugin.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as fs from 'fs';\n\nimport { monotonicTime, spawnAsync } from 'playwright-core/lib/utils';\n\nimport type { TestRunnerPlugin } from './';\nimport type { FullConfig } from '../../types/testReporter';\nimport type { FullConfigInternal } from '../common/config';\nimport type { GitCommitInfo, CIInfo, MetadataWithCommitInfo } from '../isomorphic/types';\n\nconst GIT_OPERATIONS_TIMEOUT_MS = 3000;\n\nexport const addGitCommitInfoPlugin = (fullConfig: FullConfigInternal) => {\n fullConfig.plugins.push({ factory: gitCommitInfoPlugin.bind(null, fullConfig) });\n};\n\nfunction print(s: string, ...args: any[]) {\n // eslint-disable-next-line no-console\n console.log('GitCommitInfo: ' + s, ...args);\n}\n\nfunction debug(s: string, ...args: any[]) {\n if (!process.env.DEBUG_GIT_COMMIT_INFO)\n return;\n print(s, ...args);\n}\n\nconst gitCommitInfoPlugin = (fullConfig: FullConfigInternal): TestRunnerPlugin => {\n return {\n name: 'playwright:git-commit-info',\n\n setup: async (config: FullConfig, configDir: string) => {\n const metadata = config.metadata as MetadataWithCommitInfo;\n const ci = await ciInfo();\n if (!metadata.ci && ci) {\n debug('ci info', ci);\n metadata.ci = ci;\n }\n\n if (fullConfig.captureGitInfo?.commit || (fullConfig.captureGitInfo?.commit === undefined && ci)) {\n const git = await gitCommitInfo(configDir).catch(e => print('failed to get git commit info', e));\n if (git) {\n debug('commit info', git);\n metadata.gitCommit = git;\n }\n }\n\n if (fullConfig.captureGitInfo?.diff || (fullConfig.captureGitInfo?.diff === undefined && ci)) {\n const diffResult = await gitDiff(configDir, ci).catch(e => print('failed to get git diff', e));\n if (diffResult) {\n debug(`diff length ${diffResult.length}`);\n metadata.gitDiff = diffResult;\n }\n }\n },\n };\n};\n\nasync function ciInfo(): Promise<CIInfo | undefined> {\n if (process.env.GITHUB_ACTIONS) {\n let pr: { title: string, number: number, baseHash: string } | undefined;\n try {\n const json = JSON.parse(await fs.promises.readFile(process.env.GITHUB_EVENT_PATH!, 'utf8'));\n pr = { title: json.pull_request.title, number: json.pull_request.number, baseHash: json.pull_request.base.sha };\n } catch {\n }\n\n return {\n commitHref: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/commit/${process.env.GITHUB_SHA}`,\n commitHash: process.env.GITHUB_SHA,\n prHref: pr ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/pull/${pr.number}` : undefined,\n prTitle: pr?.title,\n prBaseHash: pr?.baseHash,\n buildHref: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`,\n };\n }\n\n if (process.env.GITLAB_CI) {\n return {\n commitHref: `${process.env.CI_PROJECT_URL}/-/commit/${process.env.CI_COMMIT_SHA}`,\n commitHash: process.env.CI_COMMIT_SHA,\n buildHref: process.env.CI_JOB_URL,\n branch: process.env.CI_COMMIT_REF_NAME,\n };\n }\n\n if (process.env.JENKINS_URL && process.env.BUILD_URL) {\n return {\n commitHref: process.env.BUILD_URL,\n commitHash: process.env.GIT_COMMIT,\n branch: process.env.GIT_BRANCH,\n };\n }\n\n // Open to PRs.\n}\n\nasync function gitCommitInfo(gitDir: string): Promise<GitCommitInfo | undefined> {\n const separator = `---786eec917292---`;\n const tokens = [\n '%H', // commit hash\n '%h', // abbreviated commit hash\n '%s', // subject\n '%B', // raw body (unwrapped subject and body)\n '%an', // author name\n '%ae', // author email\n '%at', // author date, UNIX timestamp\n '%cn', // committer name\n '%ce', // committer email\n '%ct', // committer date, UNIX timestamp\n '', // branch\n ];\n const output = await runGit(`git log -1 --pretty=format:\"${tokens.join(separator)}\" && git rev-parse --abbrev-ref HEAD`, gitDir);\n if (!output)\n return undefined;\n const [hash, shortHash, subject, body, authorName, authorEmail, authorTime, committerName, committerEmail, committerTime, branch] = output.split(separator);\n\n return {\n shortHash,\n hash,\n subject,\n body,\n author: {\n name: authorName,\n email: authorEmail,\n time: +authorTime * 1000,\n },\n committer: {\n name: committerName,\n email: committerEmail,\n time: +committerTime * 1000,\n },\n branch: branch.trim(),\n };\n}\n\nasync function gitDiff(gitDir: string, ci?: CIInfo): Promise<string | undefined> {\n const diffLimit = 100_000;\n if (ci?.prBaseHash) {\n // https://git-scm.com/docs/git-fetch\n await runGit(`git fetch origin ${ci.prBaseHash} --depth=1 --no-auto-maintenance --no-auto-gc --no-tags --no-recurse-submodules`, gitDir);\n const diff = await runGit(`git diff ${ci.prBaseHash} HEAD`, gitDir);\n if (diff)\n return diff.substring(0, diffLimit);\n }\n\n // Do not attempt to diff on CI commit.\n if (ci)\n return;\n\n // Check dirty state first.\n const uncommitted = await runGit('git diff', gitDir);\n if (uncommitted === undefined) {\n // Failed to run git diff.\n return;\n }\n if (uncommitted)\n return uncommitted.substring(0, diffLimit);\n\n // Assume non-shallow checkout on local.\n const diff = await runGit('git diff HEAD~1', gitDir);\n return diff?.substring(0, diffLimit);\n}\n\nasync function runGit(command: string, cwd: string): Promise<string | undefined> {\n debug(`running \"${command}\"`);\n const start = monotonicTime();\n const result = await spawnAsync(\n command,\n [],\n { stdio: 'pipe', cwd, timeout: GIT_OPERATIONS_TIMEOUT_MS, shell: true }\n );\n if (monotonicTime() - start > GIT_OPERATIONS_TIMEOUT_MS) {\n print(`timeout of ${GIT_OPERATIONS_TIMEOUT_MS}ms exceeded while running \"${command}\"`);\n return;\n }\n if (result.code)\n debug(`failure, code=${result.code}\\n\\n${result.stderr}`);\n else\n debug(`success`);\n return result.code ? undefined : result.stdout.trim();\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["diff"]
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/plugins/index.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { FullConfig, Suite } from '../../types/testReporter';\nimport type { ReporterV2 } from '../reporters/reporterV2';\n\nexport interface TestRunnerPlugin {\n name: string;\n setup?(config: FullConfig, configDir: string, reporter: ReporterV2): Promise<void>;\n populateDependencies?(): Promise<void>;\n startDevServer?(): Promise<() => Promise<void>>;\n clearCache?(): Promise<void>;\n begin?(suite: Suite): Promise<void>;\n end?(): Promise<void>;\n teardown?(): Promise<void>;\n}\n\nexport type TestRunnerPluginRegistration = {\n factory: TestRunnerPlugin | (() => TestRunnerPlugin | Promise<TestRunnerPlugin>);\n instance?: TestRunnerPlugin;\n devServerCleanup?: any;\n};\n\nexport { webServer } from './webServerPlugin';\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCA,6BAA0B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/plugins/webServerPlugin.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport net from 'net';\nimport path from 'path';\n\nimport { launchProcess, isURLAvailable, monotonicTime, raceAgainstDeadline, ManualPromise } from 'playwright-core/lib/utils';\nimport { colors } from 'playwright-core/lib/utils';\nimport { debug } from 'playwright-core/lib/utilsBundle';\n\nimport type { TestRunnerPlugin } from '.';\nimport type { FullConfig } from '../../types/testReporter';\nimport type { FullConfigInternal } from '../common/config';\nimport type { ReporterV2 } from '../reporters/reporterV2';\n\nexport type WebServerPluginOptions = {\n command: string;\n url?: string;\n wait?: { stdout?: RegExp, stderr?: RegExp };\n ignoreHTTPSErrors?: boolean;\n timeout?: number;\n gracefulShutdown?: { signal: 'SIGINT' | 'SIGTERM', timeout?: number };\n reuseExistingServer?: boolean;\n cwd?: string;\n env?: { [key: string]: string; };\n stdout?: 'pipe' | 'ignore';\n stderr?: 'pipe' | 'ignore';\n name?: string;\n};\n\nconst DEFAULT_ENVIRONMENT_VARIABLES = {\n 'BROWSER': 'none', // Disable that create-react-app will open the page in the browser\n 'FORCE_COLOR': '1',\n 'DEBUG_COLORS': '1',\n};\n\nconst debugWebServer = debug('pw:webserver');\n\nexport class WebServerPlugin implements TestRunnerPlugin {\n private _isAvailableCallback?: () => Promise<boolean>;\n private _killProcess?: () => Promise<void>;\n private _processExitedPromise!: Promise<any>;\n private _options: WebServerPluginOptions;\n private _checkPortOnly: boolean;\n private _reporter?: ReporterV2;\n private _waitForStdioPromise: ManualPromise | undefined;\n\n name = 'playwright:webserver';\n\n constructor(options: WebServerPluginOptions, checkPortOnly: boolean) {\n this._options = options;\n this._checkPortOnly = checkPortOnly;\n }\n\n public async setup(config: FullConfig, configDir: string, reporter: ReporterV2) {\n this._reporter = reporter;\n this._isAvailableCallback = this._options.url ? getIsAvailableFunction(this._options.url, this._checkPortOnly, !!this._options.ignoreHTTPSErrors, this._reporter.onStdErr?.bind(this._reporter)) : undefined;\n this._options.cwd = this._options.cwd ? path.resolve(configDir, this._options.cwd) : configDir;\n try {\n await this._startProcess();\n await this._waitForProcess();\n } catch (error) {\n await this.teardown();\n throw error;\n }\n }\n\n public async teardown() {\n debugWebServer(`Terminating the WebServer`);\n await this._killProcess?.();\n debugWebServer(`Terminated the WebServer`);\n }\n\n private async _startProcess(): Promise<void> {\n let processExitedReject = (error: Error) => { };\n this._processExitedPromise = new Promise((_, reject) => processExitedReject = reject);\n\n const isAlreadyAvailable = await this._isAvailableCallback?.();\n if (isAlreadyAvailable) {\n debugWebServer(`WebServer is already available`);\n if (this._options.reuseExistingServer)\n return;\n const port = new URL(this._options.url!).port;\n throw new Error(`${this._options.url ?? `http://localhost${port ? ':' + port : ''}`} is already used, make sure that nothing is running on the port/url or set reuseExistingServer:true in config.webServer.`);\n }\n\n if (!this._options.command)\n throw new Error('config.webServer.command cannot be empty');\n\n debugWebServer(`Starting WebServer process ${this._options.command}...`);\n const { launchedProcess, gracefullyClose } = await launchProcess({\n command: this._options.command,\n env: {\n ...DEFAULT_ENVIRONMENT_VARIABLES,\n ...process.env,\n ...this._options.env,\n },\n cwd: this._options.cwd,\n stdio: 'stdin',\n shell: true,\n attemptToGracefullyClose: async () => {\n if (process.platform === 'win32')\n throw new Error('Graceful shutdown is not supported on Windows');\n if (!this._options.gracefulShutdown)\n throw new Error('skip graceful shutdown');\n\n const { signal, timeout = 0 } = this._options.gracefulShutdown;\n\n // proper usage of SIGINT is to send it to the entire process group, see https://www.cons.org/cracauer/sigint.html\n // there's no such convention for SIGTERM, so we decide what we want. signaling the process group for consistency.\n process.kill(-launchedProcess.pid!, signal);\n\n return new Promise<void>((resolve, reject) => {\n const timer = timeout !== 0\n ? setTimeout(() => reject(new Error(`process didn't close gracefully within timeout`)), timeout)\n : undefined;\n launchedProcess.once('close', (...args) => {\n clearTimeout(timer);\n resolve();\n });\n });\n },\n log: () => {},\n onExit: code => processExitedReject(new Error(code ? `Process from config.webServer was not able to start. Exit code: ${code}` : 'Process from config.webServer exited early.')),\n tempDirectories: [],\n });\n this._killProcess = gracefullyClose;\n\n debugWebServer(`Process started`);\n\n if (this._options.wait?.stdout || this._options.wait?.stderr)\n this._waitForStdioPromise = new ManualPromise();\n const stdioWaitCollectors = {\n stdout: this._options.wait?.stdout ? '' : undefined,\n stderr: this._options.wait?.stderr ? '' : undefined,\n };\n\n launchedProcess.stdout!.on('data', data => {\n if (debugWebServer.enabled || this._options.stdout === 'pipe')\n this._reporter!.onStdOut?.(prefixOutputLines(data.toString(), this._options.name));\n });\n\n launchedProcess.stderr!.on('data', data => {\n if (debugWebServer.enabled || (this._options.stderr === 'pipe' || !this._options.stderr))\n this._reporter!.onStdErr?.(prefixOutputLines(data.toString(), this._options.name));\n });\n\n const resolveStdioPromise = () => {\n stdioWaitCollectors.stdout = undefined;\n stdioWaitCollectors.stderr = undefined;\n this._waitForStdioPromise?.resolve();\n };\n\n for (const stdio of ['stdout', 'stderr'] as const) {\n launchedProcess[stdio]!.on('data', data => {\n if (!this._options.wait?.[stdio] || stdioWaitCollectors[stdio] === undefined)\n return;\n stdioWaitCollectors[stdio] += data.toString();\n this._options.wait[stdio].lastIndex = 0;\n const result = this._options.wait[stdio].exec(stdioWaitCollectors[stdio]);\n if (result) {\n for (const [key, value] of Object.entries(result.groups || {}))\n process.env[key.toUpperCase()] = value;\n resolveStdioPromise();\n }\n });\n }\n }\n\n private async _waitForProcess() {\n if (!this._isAvailableCallback && !this._waitForStdioPromise) {\n this._processExitedPromise.catch(() => {});\n return;\n }\n\n debugWebServer(`Waiting for availability...`);\n const launchTimeout = this._options.timeout || 60 * 1000;\n const cancellationToken = { canceled: false };\n const deadline = monotonicTime() + launchTimeout;\n\n const racingPromises = [this._processExitedPromise];\n if (this._isAvailableCallback)\n racingPromises.push(raceAgainstDeadline(() => waitFor(this._isAvailableCallback!, cancellationToken), deadline));\n if (this._waitForStdioPromise)\n racingPromises.push(raceAgainstDeadline(() => this._waitForStdioPromise!, deadline));\n\n const { timedOut } = await Promise.race(racingPromises);\n cancellationToken.canceled = true;\n if (timedOut)\n throw new Error(`Timed out waiting ${launchTimeout}ms from config.webServer.`);\n debugWebServer(`WebServer available`);\n }\n}\n\nasync function isPortUsed(port: number): Promise<boolean> {\n const innerIsPortUsed = (host: string) => new Promise<boolean>(resolve => {\n const conn = net\n .connect(port, host)\n .on('error', () => {\n resolve(false);\n })\n .on('connect', () => {\n conn.end();\n resolve(true);\n });\n });\n return await innerIsPortUsed('127.0.0.1') || await innerIsPortUsed('::1');\n}\n\nasync function waitFor(waitFn: () => Promise<boolean>, cancellationToken: { canceled: boolean }) {\n const logScale = [100, 250, 500];\n while (!cancellationToken.canceled) {\n const connected = await waitFn();\n if (connected)\n return;\n const delay = logScale.shift() || 1000;\n debugWebServer(`Waiting ${delay}ms`);\n await new Promise(x => setTimeout(x, delay));\n }\n}\n\nfunction getIsAvailableFunction(url: string, checkPortOnly: boolean, ignoreHTTPSErrors: boolean, onStdErr: ReporterV2['onStdErr']) {\n const urlObject = new URL(url);\n if (!checkPortOnly)\n return () => isURLAvailable(urlObject, ignoreHTTPSErrors, debugWebServer, onStdErr);\n const port = urlObject.port;\n return () => isPortUsed(+port);\n}\n\nexport const webServer = (options: WebServerPluginOptions): TestRunnerPlugin => {\n return new WebServerPlugin(options, false);\n};\n\nexport const webServerPluginsForConfig = (config: FullConfigInternal): TestRunnerPlugin[] => {\n const shouldSetBaseUrl = !!config.config.webServer;\n const webServerPlugins = [];\n for (const webServerConfig of config.webServers) {\n if (webServerConfig.port && webServerConfig.url)\n throw new Error(`Either 'port' or 'url' should be specified in config.webServer.`);\n\n let url: string | undefined;\n if (webServerConfig.port || webServerConfig.url) {\n url = webServerConfig.url || `http://localhost:${webServerConfig.port}`;\n\n // We only set base url when only the port is given. That's a legacy mode we have regrets about.\n if (shouldSetBaseUrl && !webServerConfig.url)\n process.env.PLAYWRIGHT_TEST_BASE_URL = url;\n }\n webServerPlugins.push(new WebServerPlugin({ ...webServerConfig, url }, webServerConfig.port !== undefined));\n }\n\n return webServerPlugins;\n};\n\nfunction prefixOutputLines(output: string, prefixName: string = 'WebServer'): string {\n const lastIsNewLine = output[output.length - 1] === '\\n';\n let lines = output.split('\\n');\n if (lastIsNewLine)\n lines.pop();\n lines = lines.map(line => colors.dim(`[${prefixName}] `) + line);\n if (lastIsNewLine)\n lines.push('');\n return lines.join('\\n');\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_utils", "path", "net"]
7
+ }
package/lib/program.js CHANGED
@@ -245,8 +245,8 @@ async function runTests(args, opts) {
245
245
  (0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
246
246
  }
247
247
  async function runTestServer(opts) {
248
- const host = opts.host || "localhost";
249
- const port = opts.port ? +opts.port : 0;
248
+ const host = opts.host;
249
+ const port = opts.port ? +opts.port : void 0;
250
250
  const status = await testServer.runTestServer(opts.config, {}, { host, port });
251
251
  const exitCode = status === "interrupted" ? 130 : status === "passed" ? 0 : 1;
252
252
  (0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
@@ -282,12 +282,15 @@ function overridesFromOptions(options) {
282
282
  retries: options.retries ? parseInt(options.retries, 10) : void 0,
283
283
  reporter: resolveReporterOption(options.reporter),
284
284
  shard: resolveShardOption(options.shard),
285
+ shardWeights: resolveShardWeightsOption(),
285
286
  timeout: options.timeout ? parseInt(options.timeout, 10) : void 0,
286
287
  tsconfig: options.tsconfig ? import_path.default.resolve(process.cwd(), options.tsconfig) : void 0,
287
288
  ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : void 0,
288
289
  updateSnapshots: options.updateSnapshots,
289
290
  updateSourceMethod: options.updateSourceMethod,
290
- workers: options.workers
291
+ runAgents: options.runAgents,
292
+ workers: options.workers,
293
+ pause: process.env.PWPAUSE ? true : void 0
291
294
  };
292
295
  if (options.browser) {
293
296
  const browserOpt = options.browser.toLowerCase();
@@ -301,7 +304,7 @@ function overridesFromOptions(options) {
301
304
  };
302
305
  });
303
306
  }
304
- if (options.headed || options.debug)
307
+ if (options.headed || options.debug || overrides.pause)
305
308
  overrides.use = { headless: false };
306
309
  if (!options.ui && options.debug) {
307
310
  overrides.debug = true;
@@ -340,6 +343,17 @@ function resolveShardOption(shard) {
340
343
  }
341
344
  return { current, total };
342
345
  }
346
+ function resolveShardWeightsOption() {
347
+ const shardWeights = process.env.PWTEST_SHARD_WEIGHTS;
348
+ if (!shardWeights)
349
+ return void 0;
350
+ return shardWeights.split(":").map((w) => {
351
+ const weight = parseInt(w, 10);
352
+ if (isNaN(weight) || weight < 0)
353
+ throw new Error(`PWTEST_SHARD_WEIGHTS="${shardWeights}" weights must be non-negative numbers`);
354
+ return weight;
355
+ });
356
+ }
343
357
  function resolveReporter(id) {
344
358
  if (import_config.builtInReporters.includes(id))
345
359
  return id;
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/program.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-disable no-console */\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { program } from 'playwright-core/lib/cli/program';\nimport { gracefullyProcessExitDoNotHang, startProfiling, stopProfiling } from 'playwright-core/lib/utils';\n\nimport { builtInReporters, defaultReporter, defaultTimeout } from './common/config';\nimport { loadConfigFromFile, loadEmptyConfigForMergeReports, resolveConfigLocation } from './common/configLoader';\nexport { program } from 'playwright-core/lib/cli/program';\nimport { terminalScreen } from './reporters/base';\nimport { showHTMLReport } from './reporters/html';\nimport { createMergedReport } from './reporters/merge';\nimport { filterProjects } from './runner/projectUtils';\nimport * as testServer from './runner/testServer';\nimport { runWatchModeLoop } from './runner/watchMode';\nimport { runAllTestsWithConfig, TestRunner } from './runner/testRunner';\nimport { createErrorCollectingReporter } from './runner/reporters';\nimport * as mcp from './mcp/sdk/exports';\nimport { TestServerBackend } from './mcp/test/testBackend';\nimport { decorateCommand } from './mcp/program';\nimport { setupExitWatchdog } from './mcp/browser/watchdog';\nimport { ClaudeGenerator, OpencodeGenerator, VSCodeGenerator, CopilotGenerator } from './agents/generateAgents';\n\nimport type { ConfigCLIOverrides } from './common/ipc';\nimport type { TraceMode } from '../types/test';\nimport type { ReporterDescription } from '../types/test';\nimport type { Command } from 'playwright-core/lib/utilsBundle';\n\nconst packageJSON = require('../package.json');\n\nfunction addTestCommand(program: Command) {\n const command = program.command('test [test-filter...]');\n command.description('run tests with Playwright Test');\n const options = testOptions.sort((a, b) => a[0].replace(/-/g, '').localeCompare(b[0].replace(/-/g, '')));\n options.forEach(([name, { description, choices, preset }]) => {\n const option = command.createOption(name, description);\n if (choices)\n option.choices(choices);\n if (preset)\n option.preset(preset);\n // We don't set the default value here, because we want not specified options to\n // fall back to the user config, which we haven't parsed yet.\n command.addOption(option);\n return command;\n });\n command.action(async (args, opts) => {\n try {\n await runTests(args, opts);\n } catch (e) {\n console.error(e);\n gracefullyProcessExitDoNotHang(1);\n }\n });\n command.addHelpText('afterAll', `\nArguments [test-filter...]:\n Pass arguments to filter test files. Each argument is treated as a regular expression. Matching is performed against the absolute file paths.\n\nExamples:\n $ npx playwright test my.spec.ts\n $ npx playwright test some.spec.ts:42\n $ npx playwright test --headed\n $ npx playwright test --project=webkit`);\n}\n\nfunction addClearCacheCommand(program: Command) {\n const command = program.command('clear-cache');\n command.description('clears build and test caches');\n command.option('-c, --config <file>', `Configuration file, or a test directory with optional \"playwright.config.{m,c}?{js,ts}\"`);\n command.action(async opts => {\n const runner = new TestRunner(resolveConfigLocation(opts.config), {});\n const { status } = await runner.clearCache(createErrorCollectingReporter(terminalScreen));\n const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);\n gracefullyProcessExitDoNotHang(exitCode);\n });\n}\n\nfunction addDevServerCommand(program: Command) {\n const command = program.command('dev-server', { hidden: true });\n command.description('start dev server');\n command.option('-c, --config <file>', `Configuration file, or a test directory with optional \"playwright.config.{m,c}?{js,ts}\"`);\n command.action(async options => {\n const runner = new TestRunner(resolveConfigLocation(options.config), {});\n await runner.startDevServer(createErrorCollectingReporter(terminalScreen), 'in-process');\n });\n}\n\nfunction addTestServerCommand(program: Command) {\n const command = program.command('test-server', { hidden: true });\n command.description('start test server');\n command.option('-c, --config <file>', `Configuration file, or a test directory with optional \"playwright.config.{m,c}?{js,ts}\"`);\n command.option('--host <host>', 'Host to start the server on', 'localhost');\n command.option('--port <port>', 'Port to start the server on', '0');\n command.action(opts => runTestServer(opts));\n}\n\nfunction addShowReportCommand(program: Command) {\n const command = program.command('show-report [report]');\n command.description('show HTML report');\n command.action((report, options) => showHTMLReport(report, options.host, +options.port));\n command.option('--host <host>', 'Host to serve report on', 'localhost');\n command.option('--port <port>', 'Port to serve report on', '9323');\n command.addHelpText('afterAll', `\nArguments [report]:\n When specified, opens given report, otherwise opens last generated report.\n\nExamples:\n $ npx playwright show-report\n $ npx playwright show-report playwright-report`);\n}\n\nfunction addMergeReportsCommand(program: Command) {\n const command = program.command('merge-reports [dir]');\n command.description('merge multiple blob reports (for sharded tests) into a single report');\n command.action(async (dir, options) => {\n try {\n await mergeReports(dir, options);\n } catch (e) {\n console.error(e);\n gracefullyProcessExitDoNotHang(1);\n }\n });\n command.option('-c, --config <file>', `Configuration file. Can be used to specify additional configuration for the output report.`);\n command.option('--reporter <reporter>', `Reporter to use, comma-separated, can be ${builtInReporters.map(name => `\"${name}\"`).join(', ')} (default: \"${defaultReporter}\")`);\n command.addHelpText('afterAll', `\nArguments [dir]:\n Directory containing blob reports.\n\nExamples:\n $ npx playwright merge-reports playwright-report`);\n}\n\nfunction addBrowserMCPServerCommand(program: Command) {\n const command = program.command('run-mcp-server', { hidden: true });\n command.description('Interact with the browser over MCP');\n decorateCommand(command, packageJSON.version);\n}\n\nfunction addTestMCPServerCommand(program: Command) {\n const command = program.command('run-test-mcp-server', { hidden: true });\n command.description('Interact with the test runner over MCP');\n command.option('--headless', 'run browser in headless mode, headed by default');\n command.option('-c, --config <file>', `Configuration file, or a test directory with optional \"playwright.config.{m,c}?{js,ts}\"`);\n command.option('--host <host>', 'host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.');\n command.option('--port <port>', 'port to listen on for SSE transport.');\n command.action(async options => {\n setupExitWatchdog();\n const factory: mcp.ServerBackendFactory = {\n name: 'Playwright Test Runner',\n nameInConfig: 'playwright-test-runner',\n version: packageJSON.version,\n create: () => new TestServerBackend(options.config, { muteConsole: options.port === undefined, headless: options.headless }),\n };\n // TODO: add all options from mcp.startHttpServer.\n await mcp.start(factory, { port: options.port === undefined ? undefined : +options.port, host: options.host });\n });\n}\n\nfunction addInitAgentsCommand(program: Command) {\n const command = program.command('init-agents');\n command.description('Initialize repository agents');\n const option = command.createOption('--loop <loop>', 'Agentic loop provider');\n option.choices(['claude', 'copilot', 'opencode', 'vscode', 'vscode-legacy']);\n command.addOption(option);\n command.option('-c, --config <file>', `Configuration file to find a project to use for seed test`);\n command.option('--project <project>', 'Project to use for seed test');\n command.option('--prompts', 'Whether to include prompts in the agent initialization');\n command.action(async opts => {\n const config = await loadConfigFromFile(opts.config);\n if (opts.loop === 'opencode') {\n await OpencodeGenerator.init(config, opts.project, opts.prompts);\n } else if (opts.loop === 'vscode-legacy') {\n await VSCodeGenerator.init(config, opts.project);\n } else if (opts.loop === 'claude') {\n await ClaudeGenerator.init(config, opts.project, opts.prompts);\n } else {\n await CopilotGenerator.init(config, opts.project, opts.prompts);\n return;\n }\n });\n}\n\nasync function runTests(args: string[], opts: { [key: string]: any }) {\n await startProfiling();\n const cliOverrides = overridesFromOptions(opts);\n\n const config = await loadConfigFromFile(opts.config, cliOverrides, opts.deps === false);\n config.cliArgs = args;\n config.cliGrep = opts.grep as string | undefined;\n config.cliOnlyChanged = opts.onlyChanged === true ? 'HEAD' : opts.onlyChanged;\n config.cliGrepInvert = opts.grepInvert as string | undefined;\n config.cliListOnly = !!opts.list;\n config.cliProjectFilter = opts.project || undefined;\n config.cliPassWithNoTests = !!opts.passWithNoTests;\n config.cliLastFailed = !!opts.lastFailed;\n config.cliTestList = opts.testList ? path.resolve(process.cwd(), opts.testList) : undefined;\n config.cliTestListInvert = opts.testListInvert ? path.resolve(process.cwd(), opts.testListInvert) : undefined;\n\n // Evaluate project filters against config before starting execution. This enables a consistent error message across run modes\n filterProjects(config.projects, config.cliProjectFilter);\n\n if (opts.ui || opts.uiHost || opts.uiPort) {\n if (opts.onlyChanged)\n throw new Error(`--only-changed is not supported in UI mode. If you'd like that to change, see https://github.com/microsoft/playwright/issues/15075 for more details.`);\n\n const status = await testServer.runUIMode(opts.config, cliOverrides, {\n host: opts.uiHost,\n port: opts.uiPort ? +opts.uiPort : undefined,\n args,\n grep: opts.grep as string | undefined,\n grepInvert: opts.grepInvert as string | undefined,\n project: opts.project || undefined,\n reporter: Array.isArray(opts.reporter) ? opts.reporter : opts.reporter ? [opts.reporter] : undefined,\n });\n await stopProfiling('runner');\n const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);\n gracefullyProcessExitDoNotHang(exitCode);\n return;\n }\n\n if (process.env.PWTEST_WATCH) {\n if (opts.onlyChanged)\n throw new Error(`--only-changed is not supported in watch mode. If you'd like that to change, file an issue and let us know about your usecase for it.`);\n\n const status = await runWatchModeLoop(\n resolveConfigLocation(opts.config),\n {\n projects: opts.project,\n files: args,\n grep: opts.grep\n }\n );\n await stopProfiling('runner');\n const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);\n gracefullyProcessExitDoNotHang(exitCode);\n return;\n }\n\n const status = await runAllTestsWithConfig(config);\n await stopProfiling('runner');\n const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);\n gracefullyProcessExitDoNotHang(exitCode);\n}\n\nasync function runTestServer(opts: { [key: string]: any }) {\n const host = opts.host || 'localhost';\n const port = opts.port ? +opts.port : 0;\n const status = await testServer.runTestServer(opts.config, { }, { host, port });\n const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);\n gracefullyProcessExitDoNotHang(exitCode);\n}\n\nasync function mergeReports(reportDir: string | undefined, opts: { [key: string]: any }) {\n const configFile = opts.config;\n const config = configFile ? await loadConfigFromFile(configFile) : await loadEmptyConfigForMergeReports();\n\n const dir = path.resolve(process.cwd(), reportDir || '');\n const dirStat = await fs.promises.stat(dir).catch(e => null);\n if (!dirStat)\n throw new Error('Directory does not exist: ' + dir);\n if (!dirStat.isDirectory())\n throw new Error(`\"${dir}\" is not a directory`);\n let reporterDescriptions: ReporterDescription[] | undefined = resolveReporterOption(opts.reporter);\n if (!reporterDescriptions && configFile)\n reporterDescriptions = config.config.reporter;\n if (!reporterDescriptions)\n reporterDescriptions = [[defaultReporter]];\n const rootDirOverride = configFile ? config.config.rootDir : undefined;\n await createMergedReport(config, dir, reporterDescriptions!, rootDirOverride);\n gracefullyProcessExitDoNotHang(0);\n}\n\nfunction overridesFromOptions(options: { [key: string]: any }): ConfigCLIOverrides {\n const overrides: ConfigCLIOverrides = {\n failOnFlakyTests: options.failOnFlakyTests ? true : undefined,\n forbidOnly: options.forbidOnly ? true : undefined,\n fullyParallel: options.fullyParallel ? true : undefined,\n globalTimeout: options.globalTimeout ? parseInt(options.globalTimeout, 10) : undefined,\n maxFailures: options.x ? 1 : (options.maxFailures ? parseInt(options.maxFailures, 10) : undefined),\n outputDir: options.output ? path.resolve(process.cwd(), options.output) : undefined,\n quiet: options.quiet ? options.quiet : undefined,\n repeatEach: options.repeatEach ? parseInt(options.repeatEach, 10) : undefined,\n retries: options.retries ? parseInt(options.retries, 10) : undefined,\n reporter: resolveReporterOption(options.reporter),\n shard: resolveShardOption(options.shard),\n timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,\n tsconfig: options.tsconfig ? path.resolve(process.cwd(), options.tsconfig) : undefined,\n ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : undefined,\n updateSnapshots: options.updateSnapshots,\n updateSourceMethod: options.updateSourceMethod,\n workers: options.workers,\n };\n\n if (options.browser) {\n const browserOpt = options.browser.toLowerCase();\n if (!['all', 'chromium', 'firefox', 'webkit'].includes(browserOpt))\n throw new Error(`Unsupported browser \"${options.browser}\", must be one of \"all\", \"chromium\", \"firefox\" or \"webkit\"`);\n const browserNames = browserOpt === 'all' ? ['chromium', 'firefox', 'webkit'] : [browserOpt];\n overrides.projects = browserNames.map(browserName => {\n return {\n name: browserName,\n use: { browserName },\n };\n });\n }\n\n if (options.headed || options.debug)\n overrides.use = { headless: false };\n if (!options.ui && options.debug) {\n overrides.debug = true;\n process.env.PWDEBUG = '1';\n }\n if (!options.ui && options.trace) {\n overrides.use = overrides.use || {};\n overrides.use.trace = options.trace;\n }\n if (overrides.tsconfig && !fs.existsSync(overrides.tsconfig))\n throw new Error(`--tsconfig \"${options.tsconfig}\" does not exist`);\n\n return overrides;\n}\n\nfunction resolveReporterOption(reporter?: string): ReporterDescription[] | undefined {\n if (!reporter || !reporter.length)\n return undefined;\n return reporter.split(',').map((r: string) => [resolveReporter(r)]);\n}\n\nfunction resolveShardOption(shard?: string): ConfigCLIOverrides['shard'] {\n if (!shard)\n return undefined;\n\n const shardPair = shard.split('/');\n\n if (shardPair.length !== 2) {\n throw new Error(\n `--shard \"${shard}\", expected format is \"current/all\", 1-based, for example \"3/5\".`,\n );\n }\n\n const current = parseInt(shardPair[0], 10);\n const total = parseInt(shardPair[1], 10);\n\n if (isNaN(total) || total < 1)\n throw new Error(`--shard \"${shard}\" total must be a positive number`);\n\n\n if (isNaN(current) || current < 1 || current > total) {\n throw new Error(\n `--shard \"${shard}\" current must be a positive number, not greater than shard total`,\n );\n }\n\n return { current, total };\n}\n\nfunction resolveReporter(id: string) {\n if (builtInReporters.includes(id as any))\n return id;\n const localPath = path.resolve(process.cwd(), id);\n if (fs.existsSync(localPath))\n return localPath;\n return require.resolve(id, { paths: [process.cwd()] });\n}\n\nconst kTraceModes: TraceMode[] = ['on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure', 'retain-on-first-failure'];\n\n// Note: update docs/src/test-cli-js.md when you update this, program is the source of truth.\n\nconst testOptions: [string, { description: string, choices?: string[], preset?: string }][] = [\n /* deprecated */ ['--browser <browser>', { description: `Browser to use for tests, one of \"all\", \"chromium\", \"firefox\" or \"webkit\" (default: \"chromium\")` }],\n ['-c, --config <file>', { description: `Configuration file, or a test directory with optional \"playwright.config.{m,c}?{js,ts}\"` }],\n ['--debug', { description: `Run tests with Playwright Inspector. Shortcut for \"PWDEBUG=1\" environment variable and \"--timeout=0 --max-failures=1 --headed --workers=1\" options` }],\n ['--fail-on-flaky-tests', { description: `Fail if any test is flagged as flaky (default: false)` }],\n ['--forbid-only', { description: `Fail if test.only is called (default: false)` }],\n ['--fully-parallel', { description: `Run all tests in parallel (default: false)` }],\n ['--global-timeout <timeout>', { description: `Maximum time this test suite can run in milliseconds (default: unlimited)` }],\n ['-g, --grep <grep>', { description: `Only run tests matching this regular expression (default: \".*\")` }],\n ['--grep-invert <grep>', { description: `Only run tests that do not match this regular expression` }],\n ['--headed', { description: `Run tests in headed browsers (default: headless)` }],\n ['--ignore-snapshots', { description: `Ignore screenshot and snapshot expectations` }],\n ['--last-failed', { description: `Only re-run the failures` }],\n ['--list', { description: `Collect all the tests and report them, but do not run` }],\n ['--max-failures <N>', { description: `Stop after the first N failures` }],\n ['--no-deps', { description: `Do not run project dependencies` }],\n ['--output <dir>', { description: `Folder for output artifacts (default: \"test-results\")` }],\n ['--only-changed [ref]', { description: `Only run test files that have been changed between 'HEAD' and 'ref'. Defaults to running all uncommitted changes. Only supports Git.` }],\n ['--pass-with-no-tests', { description: `Makes test run succeed even if no tests were found` }],\n ['--project <project-name...>', { description: `Only run tests from the specified list of projects, supports '*' wildcard (default: run all projects)` }],\n ['--quiet', { description: `Suppress stdio` }],\n ['--repeat-each <N>', { description: `Run each test N times (default: 1)` }],\n ['--reporter <reporter>', { description: `Reporter to use, comma-separated, can be ${builtInReporters.map(name => `\"${name}\"`).join(', ')} (default: \"${defaultReporter}\")` }],\n ['--retries <retries>', { description: `Maximum retry count for flaky tests, zero for no retries (default: no retries)` }],\n ['--shard <shard>', { description: `Shard tests and execute only the selected shard, specify in the form \"current/all\", 1-based, for example \"3/5\"` }],\n ['--test-list <file>', { description: `Path to a file containing a list of tests to run. See https://playwright.dev/docs/test-cli for more details.` }],\n ['--test-list-invert <file>', { description: `Path to a file containing a list of tests to skip. See https://playwright.dev/docs/test-cli for more details.` }],\n ['--timeout <timeout>', { description: `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${defaultTimeout})` }],\n ['--trace <mode>', { description: `Force tracing mode`, choices: kTraceModes as string[] }],\n ['--tsconfig <path>', { description: `Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately)` }],\n ['--ui', { description: `Run tests in interactive UI mode` }],\n ['--ui-host <host>', { description: `Host to serve UI on; specifying this option opens UI in a browser tab` }],\n ['--ui-port <port>', { description: `Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab` }],\n ['-u, --update-snapshots [mode]', { description: `Update snapshots with actual results. Running tests without the flag defaults to \"missing\"`, choices: ['all', 'changed', 'missing', 'none'], preset: 'changed' }],\n ['--update-source-method <method>', { description: `Chooses the way source is updated (default: \"patch\")`, choices: ['overwrite', '3way', 'patch'] }],\n ['-j, --workers <workers>', { description: `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)` }],\n ['-x', { description: `Stop after the first failure` }],\n];\n\naddTestCommand(program);\naddShowReportCommand(program);\naddMergeReportsCommand(program);\naddClearCacheCommand(program);\naddBrowserMCPServerCommand(program);\naddTestMCPServerCommand(program);\naddDevServerCommand(program);\naddTestServerCommand(program);\naddInitAgentsCommand(program);\n"],
5
+ "mappings": "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",
6
+ "names": ["import_program", "program", "path", "status", "exitCode", "fs"]
7
+ }
@@ -36,6 +36,7 @@ __export(base_exports, {
36
36
  formatRetry: () => formatRetry,
37
37
  internalScreen: () => internalScreen,
38
38
  kOutputSymbol: () => kOutputSymbol,
39
+ markErrorsAsReported: () => markErrorsAsReported,
39
40
  nonTerminalScreen: () => nonTerminalScreen,
40
41
  prepareErrorStack: () => prepareErrorStack,
41
42
  relativeFilePath: () => relativeFilePath,
@@ -296,19 +297,37 @@ class TerminalReporter {
296
297
  formatError(error) {
297
298
  return formatError(this.screen, error);
298
299
  }
300
+ formatResultErrors(test, result) {
301
+ return formatResultErrors(this.screen, test, result);
302
+ }
299
303
  writeLine(line) {
300
304
  this.screen.stdout?.write(line ? line + "\n" : "\n");
301
305
  }
302
306
  }
307
+ function formatResultErrors(screen, test, result) {
308
+ const lines = [];
309
+ if (test.outcome() === "unexpected") {
310
+ const errorDetails = formatResultFailure(screen, test, result, " ");
311
+ if (errorDetails.length > 0)
312
+ lines.push("");
313
+ for (const error of errorDetails)
314
+ lines.push(error.message, "");
315
+ }
316
+ return lines.join("\n");
317
+ }
303
318
  function formatFailure(screen, config, test, index, options) {
304
319
  const lines = [];
305
- const header = formatTestHeader(screen, config, test, { indent: " ", index, mode: "error", includeTestId: options?.includeTestId });
306
- lines.push(screen.colors.red(header));
320
+ let printedHeader = false;
307
321
  for (const result of test.results) {
308
322
  const resultLines = [];
309
323
  const errors = formatResultFailure(screen, test, result, " ");
310
324
  if (!errors.length)
311
325
  continue;
326
+ if (!printedHeader) {
327
+ const header = formatTestHeader(screen, config, test, { indent: " ", index, mode: "error", includeTestId: options?.includeTestId });
328
+ lines.push(screen.colors.red(header));
329
+ printedHeader = true;
330
+ }
312
331
  if (result.retry) {
313
332
  resultLines.push("");
314
333
  resultLines.push(screen.colors.gray(separator(screen, ` Retry #${result.retry}`)));
@@ -383,6 +402,10 @@ function quotePathIfNeeded(path2) {
383
402
  return `"${path2}"`;
384
403
  return path2;
385
404
  }
405
+ const kReportedSymbol = Symbol("reported");
406
+ function markErrorsAsReported(result) {
407
+ result[kReportedSymbol] = result.errors.length;
408
+ }
386
409
  function formatResultFailure(screen, test, result, initialIndent) {
387
410
  const errorDetails = [];
388
411
  if (result.status === "passed" && test.expectedStatus === "failed") {
@@ -395,7 +418,8 @@ function formatResultFailure(screen, test, result, initialIndent) {
395
418
  message: indent(screen.colors.red(`Test was interrupted.`), initialIndent)
396
419
  });
397
420
  }
398
- for (const error of result.errors) {
421
+ const reportedIndex = result[kReportedSymbol] || 0;
422
+ for (const error of result.errors.slice(reportedIndex)) {
399
423
  const formattedError = formatError(screen, error);
400
424
  errorDetails.push({
401
425
  message: indent(formattedError.message, initialIndent),
@@ -423,7 +447,7 @@ function formatTestTitle(screen, config, test, step, options = {}) {
423
447
  const projectLabel = options.includeTestId ? `project=` : "";
424
448
  const projectTitle = projectName ? `[${projectLabel}${projectName}] \u203A ` : "";
425
449
  const testTitle = `${testId}${projectTitle}${location} \u203A ${titles.join(" \u203A ")}`;
426
- const extraTags = test.tags.filter((t) => !testTitle.includes(t));
450
+ const extraTags = test.tags.filter((t) => !testTitle.includes(t) && !config.tags.includes(t));
427
451
  return `${testTitle}${stepSuffix(step)}${extraTags.length ? " " + extraTags.join(" ") : ""}`;
428
452
  }
429
453
  function formatTestHeader(screen, config, test, options = {}) {
@@ -599,6 +623,7 @@ function groupAttachments(attachments) {
599
623
  formatRetry,
600
624
  internalScreen,
601
625
  kOutputSymbol,
626
+ markErrorsAsReported,
602
627
  nonTerminalScreen,
603
628
  prepareErrorStack,
604
629
  relativeFilePath,
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/reporters/base.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport path from 'path';\n\nimport { getPackageManagerExecCommand, parseErrorStack } from 'playwright-core/lib/utils';\nimport { ms as milliseconds } from 'playwright-core/lib/utilsBundle';\nimport { colors as realColors, noColors } from 'playwright-core/lib/utils';\n\nimport { ansiRegex, resolveReporterOutputPath, stripAnsiEscapes } from '../util';\nimport { getEastAsianWidth } from '../utilsBundle';\n\nimport type { ReporterV2 } from './reporterV2';\nimport type { FullConfig, FullResult, Location, Suite, TestCase, TestError, TestResult, TestStep } from '../../types/testReporter';\nimport type { Colors } from '@isomorphic/colors';\n\nexport type TestResultOutput = { chunk: string | Buffer, type: 'stdout' | 'stderr' };\nexport const kOutputSymbol = Symbol('output');\n\ntype ErrorDetails = {\n message: string;\n location?: Location;\n};\n\ntype TestSummary = {\n didNotRun: number;\n skipped: number;\n expected: number;\n interrupted: TestCase[];\n unexpected: TestCase[];\n flaky: TestCase[];\n failuresToPrint: TestCase[];\n fatalErrors: TestError[];\n};\n\nexport type CommonReporterOptions = {\n configDir: string,\n _mode?: 'list' | 'test' | 'merge',\n _commandHash?: string,\n};\n\nexport type Screen = {\n resolveFiles: 'cwd' | 'rootDir';\n colors: Colors;\n isTTY: boolean;\n ttyWidth: number;\n ttyHeight: number;\n stdout?: NodeJS.WriteStream;\n stderr?: NodeJS.WriteStream;\n};\n\nexport type TerminalScreen = Screen & {\n stdout: NodeJS.WriteStream;\n stderr: NodeJS.WriteStream;\n};\n\nconst DEFAULT_TTY_WIDTH = 100;\nconst DEFAULT_TTY_HEIGHT = 40;\n\n// eslint-disable-next-line no-restricted-properties\nconst originalProcessStdout = process.stdout;\n// eslint-disable-next-line no-restricted-properties\nconst originalProcessStderr = process.stderr;\n\n// Output goes to terminal.\nexport const terminalScreen: TerminalScreen = (() => {\n let isTTY = !!originalProcessStdout.isTTY;\n let ttyWidth = originalProcessStdout.columns || 0;\n let ttyHeight = originalProcessStdout.rows || 0;\n if (process.env.PLAYWRIGHT_FORCE_TTY === 'false' || process.env.PLAYWRIGHT_FORCE_TTY === '0') {\n isTTY = false;\n ttyWidth = 0;\n ttyHeight = 0;\n } else if (process.env.PLAYWRIGHT_FORCE_TTY === 'true' || process.env.PLAYWRIGHT_FORCE_TTY === '1') {\n isTTY = true;\n ttyWidth = originalProcessStdout.columns || DEFAULT_TTY_WIDTH;\n ttyHeight = originalProcessStdout.rows || DEFAULT_TTY_HEIGHT;\n } else if (process.env.PLAYWRIGHT_FORCE_TTY) {\n isTTY = true;\n const sizeMatch = process.env.PLAYWRIGHT_FORCE_TTY.match(/^(\\d+)x(\\d+)$/);\n if (sizeMatch) {\n ttyWidth = +sizeMatch[1];\n ttyHeight = +sizeMatch[2];\n } else {\n ttyWidth = +process.env.PLAYWRIGHT_FORCE_TTY;\n ttyHeight = DEFAULT_TTY_HEIGHT;\n }\n if (isNaN(ttyWidth))\n ttyWidth = DEFAULT_TTY_WIDTH;\n if (isNaN(ttyHeight))\n ttyHeight = DEFAULT_TTY_HEIGHT;\n }\n\n let useColors = isTTY;\n if (process.env.DEBUG_COLORS === '0' || process.env.DEBUG_COLORS === 'false' ||\n process.env.FORCE_COLOR === '0' || process.env.FORCE_COLOR === 'false')\n useColors = false;\n else if (process.env.DEBUG_COLORS || process.env.FORCE_COLOR)\n useColors = true;\n\n const colors = useColors ? realColors : noColors;\n return {\n resolveFiles: 'cwd',\n isTTY,\n ttyWidth,\n ttyHeight,\n colors,\n stdout: originalProcessStdout,\n stderr: originalProcessStderr,\n };\n})();\n\n// Output does not go to terminal, but colors are controlled with terminal env vars.\nexport const nonTerminalScreen: Screen = {\n colors: terminalScreen.colors,\n isTTY: false,\n ttyWidth: 0,\n ttyHeight: 0,\n resolveFiles: 'rootDir',\n};\n\n// Internal output for post-processing, should always contain real colors.\nexport const internalScreen: Screen = {\n colors: realColors,\n isTTY: false,\n ttyWidth: 0,\n ttyHeight: 0,\n resolveFiles: 'rootDir',\n};\n\nexport type TerminalReporterOptions = {\n screen?: TerminalScreen;\n omitFailures?: boolean;\n includeTestId?: boolean;\n};\n\nexport class TerminalReporter implements ReporterV2 {\n screen: TerminalScreen;\n config!: FullConfig;\n suite!: Suite;\n totalTestCount = 0;\n result!: FullResult;\n private fileDurations = new Map<string, { duration: number, workers: Set<number> }>();\n private _options: TerminalReporterOptions;\n private _fatalErrors: TestError[] = [];\n private _failureCount: number = 0;\n\n constructor(options: TerminalReporterOptions = {}) {\n this.screen = options.screen ?? terminalScreen;\n this._options = options;\n }\n\n version(): 'v2' {\n return 'v2';\n }\n\n onConfigure(config: FullConfig) {\n this.config = config;\n }\n\n onBegin(suite: Suite) {\n this.suite = suite;\n this.totalTestCount = suite.allTests().length;\n }\n\n onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {\n this._appendOutput({ chunk, type: 'stdout' }, result);\n }\n\n onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {\n this._appendOutput({ chunk, type: 'stderr' }, result);\n }\n\n private _appendOutput(output: TestResultOutput, result: TestResult | undefined) {\n if (!result)\n return;\n (result as any)[kOutputSymbol] = (result as any)[kOutputSymbol] || [];\n (result as any)[kOutputSymbol].push(output);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n if (result.status !== 'skipped' && result.status !== test.expectedStatus)\n ++this._failureCount;\n const projectName = test.titlePath()[1];\n const relativePath = relativeTestPath(this.screen, this.config, test);\n const fileAndProject = (projectName ? `[${projectName}] \u203A ` : '') + relativePath;\n const entry = this.fileDurations.get(fileAndProject) || { duration: 0, workers: new Set() };\n entry.duration += result.duration;\n entry.workers.add(result.workerIndex);\n this.fileDurations.set(fileAndProject, entry);\n }\n\n onError(error: TestError) {\n this._fatalErrors.push(error);\n }\n\n async onEnd(result: FullResult) {\n this.result = result;\n }\n\n protected fitToScreen(line: string, prefix?: string): string {\n if (!this.screen.ttyWidth) {\n // Guard against the case where we cannot determine available width.\n return line;\n }\n return fitToWidth(line, this.screen.ttyWidth, prefix);\n }\n\n protected generateStartingMessage() {\n const jobs = this.config.metadata.actualWorkers ?? this.config.workers;\n const shardDetails = this.config.shard ? `, shard ${this.config.shard.current} of ${this.config.shard.total}` : '';\n if (!this.totalTestCount)\n return '';\n return '\\n' + this.screen.colors.dim('Running ') + this.totalTestCount + this.screen.colors.dim(` test${this.totalTestCount !== 1 ? 's' : ''} using `) + jobs + this.screen.colors.dim(` worker${jobs !== 1 ? 's' : ''}${shardDetails}`);\n }\n\n protected getSlowTests(): [string, number][] {\n if (!this.config.reportSlowTests)\n return [];\n // Only pick durations that were served by single worker.\n const fileDurations = [...this.fileDurations.entries()].filter(([key, value]) => value.workers.size === 1).map(([key, value]) => [key, value.duration]) as [string, number][];\n fileDurations.sort((a, b) => b[1] - a[1]);\n const count = Math.min(fileDurations.length, this.config.reportSlowTests.max || Number.POSITIVE_INFINITY);\n const threshold = this.config.reportSlowTests.threshold;\n return fileDurations.filter(([, duration]) => duration > threshold).slice(0, count);\n }\n\n protected generateSummaryMessage({ didNotRun, skipped, expected, interrupted, unexpected, flaky, fatalErrors }: TestSummary) {\n const tokens: string[] = [];\n if (unexpected.length) {\n tokens.push(this.screen.colors.red(` ${unexpected.length} failed`));\n for (const test of unexpected)\n tokens.push(this.screen.colors.red(this.formatTestHeader(test, { indent: ' ' })));\n }\n if (interrupted.length) {\n tokens.push(this.screen.colors.yellow(` ${interrupted.length} interrupted`));\n for (const test of interrupted)\n tokens.push(this.screen.colors.yellow(this.formatTestHeader(test, { indent: ' ' })));\n }\n if (flaky.length) {\n tokens.push(this.screen.colors.yellow(` ${flaky.length} flaky`));\n for (const test of flaky)\n tokens.push(this.screen.colors.yellow(this.formatTestHeader(test, { indent: ' ' })));\n }\n if (skipped)\n tokens.push(this.screen.colors.yellow(` ${skipped} skipped`));\n if (didNotRun)\n tokens.push(this.screen.colors.yellow(` ${didNotRun} did not run`));\n if (expected)\n tokens.push(this.screen.colors.green(` ${expected} passed`) + this.screen.colors.dim(` (${milliseconds(this.result.duration)})`));\n if (fatalErrors.length && expected + unexpected.length + interrupted.length + flaky.length > 0)\n tokens.push(this.screen.colors.red(` ${fatalErrors.length === 1 ? '1 error was not a part of any test' : fatalErrors.length + ' errors were not a part of any test'}, see above for details`));\n\n return tokens.join('\\n');\n }\n\n protected generateSummary(): TestSummary {\n let didNotRun = 0;\n let skipped = 0;\n let expected = 0;\n const interrupted: TestCase[] = [];\n const interruptedToPrint: TestCase[] = [];\n const unexpected: TestCase[] = [];\n const flaky: TestCase[] = [];\n\n this.suite.allTests().forEach(test => {\n switch (test.outcome()) {\n case 'skipped': {\n if (test.results.some(result => result.status === 'interrupted')) {\n if (test.results.some(result => !!result.error))\n interruptedToPrint.push(test);\n interrupted.push(test);\n } else if (!test.results.length || test.expectedStatus !== 'skipped') {\n ++didNotRun;\n } else {\n ++skipped;\n }\n break;\n }\n case 'expected': ++expected; break;\n case 'unexpected': unexpected.push(test); break;\n case 'flaky': flaky.push(test); break;\n }\n });\n\n const failuresToPrint = [...unexpected, ...flaky, ...interruptedToPrint];\n return {\n didNotRun,\n skipped,\n expected,\n interrupted,\n unexpected,\n flaky,\n failuresToPrint,\n fatalErrors: this._fatalErrors,\n };\n }\n\n epilogue(full: boolean) {\n const summary = this.generateSummary();\n const summaryMessage = this.generateSummaryMessage(summary);\n if (full && summary.failuresToPrint.length && !this._options.omitFailures)\n this._printFailures(summary.failuresToPrint);\n this._printSlowTests();\n this._printSummary(summaryMessage);\n }\n\n private _printFailures(failures: TestCase[]) {\n this.writeLine('');\n failures.forEach((test, index) => {\n this.writeLine(this.formatFailure(test, index + 1));\n });\n }\n\n private _printSlowTests() {\n const slowTests = this.getSlowTests();\n slowTests.forEach(([file, duration]) => {\n this.writeLine(this.screen.colors.yellow(' Slow test file: ') + file + this.screen.colors.yellow(` (${milliseconds(duration)})`));\n });\n if (slowTests.length)\n this.writeLine(this.screen.colors.yellow(' Consider running tests from slow files in parallel. See: https://playwright.dev/docs/test-parallel'));\n }\n\n private _printSummary(summary: string) {\n if (summary.trim())\n this.writeLine(summary);\n }\n\n willRetry(test: TestCase): boolean {\n return test.outcome() === 'unexpected' && test.results.length <= test.retries;\n }\n\n formatTestTitle(test: TestCase, step?: TestStep): string {\n return formatTestTitle(this.screen, this.config, test, step, this._options);\n }\n\n formatTestHeader(test: TestCase, options: { indent?: string, index?: number, mode?: 'default' | 'error' } = {}): string {\n return formatTestHeader(this.screen, this.config, test, { ...options, includeTestId: this._options.includeTestId });\n }\n\n formatFailure(test: TestCase, index?: number): string {\n return formatFailure(this.screen, this.config, test, index, this._options);\n }\n\n formatError(error: TestError): ErrorDetails {\n return formatError(this.screen, error);\n }\n\n writeLine(line?: string) {\n this.screen.stdout?.write(line ? line + '\\n' : '\\n');\n }\n}\n\nexport function formatFailure(screen: Screen, config: FullConfig, test: TestCase, index?: number, options?: TerminalReporterOptions): string {\n const lines: string[] = [];\n const header = formatTestHeader(screen, config, test, { indent: ' ', index, mode: 'error', includeTestId: options?.includeTestId });\n lines.push(screen.colors.red(header));\n for (const result of test.results) {\n const resultLines: string[] = [];\n const errors = formatResultFailure(screen, test, result, ' ');\n if (!errors.length)\n continue;\n if (result.retry) {\n resultLines.push('');\n resultLines.push(screen.colors.gray(separator(screen, ` Retry #${result.retry}`)));\n }\n resultLines.push(...errors.map(error => '\\n' + error.message));\n const attachmentGroups = groupAttachments(result.attachments);\n for (let i = 0; i < attachmentGroups.length; ++i) {\n const attachment = attachmentGroups[i];\n if (attachment.name === 'error-context' && attachment.path) {\n resultLines.push('');\n resultLines.push(screen.colors.dim(` Error Context: ${relativeFilePath(screen, config, attachment.path)}`));\n continue;\n }\n\n if (attachment.name.startsWith('_'))\n continue;\n\n const hasPrintableContent = attachment.contentType.startsWith('text/');\n if (!attachment.path && !hasPrintableContent)\n continue;\n\n resultLines.push('');\n resultLines.push(screen.colors.dim(separator(screen, ` attachment #${i + 1}: ${screen.colors.bold(attachment.name)} (${attachment.contentType})`)));\n\n if (attachment.actual?.path) {\n if (attachment.expected?.path) {\n const expectedPath = relativeFilePath(screen, config, attachment.expected.path);\n resultLines.push(screen.colors.dim(` Expected: ${expectedPath}`));\n }\n const actualPath = relativeFilePath(screen, config, attachment.actual.path);\n resultLines.push(screen.colors.dim(` Received: ${actualPath}`));\n if (attachment.previous?.path) {\n const previousPath = relativeFilePath(screen, config, attachment.previous.path);\n resultLines.push(screen.colors.dim(` Previous: ${previousPath}`));\n }\n if (attachment.diff?.path) {\n const diffPath = relativeFilePath(screen, config, attachment.diff.path);\n resultLines.push(screen.colors.dim(` Diff: ${diffPath}`));\n }\n } else if (attachment.path) {\n const relativePath = relativeFilePath(screen, config, attachment.path);\n resultLines.push(screen.colors.dim(` ${relativePath}`));\n // Make this extensible\n if (attachment.name === 'trace') {\n const packageManagerCommand = getPackageManagerExecCommand();\n resultLines.push(screen.colors.dim(` Usage:`));\n resultLines.push('');\n resultLines.push(screen.colors.dim(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`));\n resultLines.push('');\n }\n } else {\n if (attachment.contentType.startsWith('text/') && attachment.body) {\n let text = attachment.body.toString();\n if (text.length > 300)\n text = text.slice(0, 300) + '...';\n for (const line of text.split('\\n'))\n resultLines.push(screen.colors.dim(` ${line}`));\n }\n }\n resultLines.push(screen.colors.dim(separator(screen, ' ')));\n }\n lines.push(...resultLines);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\nexport function formatRetry(screen: Screen, result: TestResult) {\n const retryLines = [];\n if (result.retry) {\n retryLines.push('');\n retryLines.push(screen.colors.gray(separator(screen, ` Retry #${result.retry}`)));\n }\n return retryLines;\n}\n\nfunction quotePathIfNeeded(path: string): string {\n if (/\\s/.test(path))\n return `\"${path}\"`;\n return path;\n}\n\nexport function formatResultFailure(screen: Screen, test: TestCase, result: TestResult, initialIndent: string): ErrorDetails[] {\n const errorDetails: ErrorDetails[] = [];\n\n if (result.status === 'passed' && test.expectedStatus === 'failed') {\n errorDetails.push({\n message: indent(screen.colors.red(`Expected to fail, but passed.`), initialIndent),\n });\n }\n if (result.status === 'interrupted') {\n errorDetails.push({\n message: indent(screen.colors.red(`Test was interrupted.`), initialIndent),\n });\n }\n\n for (const error of result.errors) {\n const formattedError = formatError(screen, error);\n errorDetails.push({\n message: indent(formattedError.message, initialIndent),\n location: formattedError.location,\n });\n }\n return errorDetails;\n}\n\nexport function relativeFilePath(screen: Screen, config: FullConfig, file: string): string {\n if (screen.resolveFiles === 'cwd')\n return path.relative(process.cwd(), file);\n return path.relative(config.rootDir, file);\n}\n\nfunction relativeTestPath(screen: Screen, config: FullConfig, test: TestCase): string {\n return relativeFilePath(screen, config, test.location.file);\n}\n\nexport function stepSuffix(step: TestStep | undefined) {\n const stepTitles = step ? step.titlePath() : [];\n return stepTitles.map(t => t.split('\\n')[0]).map(t => ' \u203A ' + t).join('');\n}\n\nfunction formatTestTitle(screen: Screen, config: FullConfig, test: TestCase, step?: TestStep, options: { includeTestId?: boolean } = {}): string {\n // root, project, file, ...describes, test\n const [, projectName, , ...titles] = test.titlePath();\n const location = `${relativeTestPath(screen, config, test)}:${test.location.line}:${test.location.column}`;\n const testId = options.includeTestId ? `[id=${test.id}] ` : '';\n const projectLabel = options.includeTestId ? `project=` : '';\n const projectTitle = projectName ? `[${projectLabel}${projectName}] \u203A ` : '';\n const testTitle = `${testId}${projectTitle}${location} \u203A ${titles.join(' \u203A ')}`;\n const extraTags = test.tags.filter(t => !testTitle.includes(t));\n return `${testTitle}${stepSuffix(step)}${extraTags.length ? ' ' + extraTags.join(' ') : ''}`;\n}\n\nfunction formatTestHeader(screen: Screen, config: FullConfig, test: TestCase, options: { indent?: string, index?: number, mode?: 'default' | 'error', includeTestId?: boolean } = {}): string {\n const title = formatTestTitle(screen, config, test, undefined, options);\n const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`;\n let fullHeader = header;\n\n // Render the path to the deepest failing test.step.\n if (options.mode === 'error') {\n const stepPaths = new Set<string>();\n for (const result of test.results.filter(r => !!r.errors.length)) {\n const stepPath: string[] = [];\n const visit = (steps: TestStep[]) => {\n const errors = steps.filter(s => s.error);\n if (errors.length > 1)\n return;\n if (errors.length === 1 && errors[0].category === 'test.step') {\n stepPath.push(errors[0].title);\n visit(errors[0].steps);\n }\n };\n visit(result.steps);\n stepPaths.add(['', ...stepPath].join(' \u203A '));\n }\n fullHeader = header + (stepPaths.size === 1 ? stepPaths.values().next().value : '');\n }\n return separator(screen, fullHeader);\n}\n\nexport function formatError(screen: Screen, error: TestError): ErrorDetails {\n const message = error.message || error.value || '';\n const stack = error.stack;\n if (!stack && !error.location)\n return { message };\n\n const tokens = [];\n\n // Now that we filter out internals from our stack traces, we can safely render\n // the helper / original exception locations.\n const parsedStack = stack ? prepareErrorStack(stack) : undefined;\n tokens.push(parsedStack?.message || message);\n\n if (error.snippet) {\n let snippet = error.snippet;\n if (!screen.colors.enabled)\n snippet = stripAnsiEscapes(snippet);\n tokens.push('');\n tokens.push(snippet);\n }\n\n if (parsedStack && parsedStack.stackLines.length)\n tokens.push(screen.colors.dim(parsedStack.stackLines.join('\\n')));\n\n let location = error.location;\n if (parsedStack && !location)\n location = parsedStack.location;\n\n if (error.cause)\n tokens.push(screen.colors.dim('[cause]: ') + formatError(screen, error.cause).message);\n\n return {\n location,\n message: tokens.join('\\n'),\n };\n}\n\nexport function separator(screen: Screen, text: string = ''): string {\n if (text)\n text += ' ';\n const columns = Math.min(100, screen.ttyWidth || 100);\n return text + screen.colors.dim('\u2500'.repeat(Math.max(0, columns - stripAnsiEscapes(text).length)));\n}\n\nfunction indent(lines: string, tab: string) {\n return lines.replace(/^(?=.+$)/gm, tab);\n}\n\nexport function prepareErrorStack(stack: string): {\n message: string;\n stackLines: string[];\n location?: Location;\n} {\n return parseErrorStack(stack, path.sep, !!process.env.PWDEBUGIMPL);\n}\n\nfunction characterWidth(c: string) {\n return getEastAsianWidth.eastAsianWidth(c.codePointAt(0)!);\n}\n\nfunction stringWidth(v: string) {\n let width = 0;\n for (const { segment } of new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(v))\n width += characterWidth(segment);\n return width;\n}\n\nfunction suffixOfWidth(v: string, width: number) {\n const segments = [...new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(v)];\n let suffixBegin = v.length;\n for (const { segment, index } of segments.reverse()) {\n const segmentWidth = stringWidth(segment);\n if (segmentWidth > width)\n break;\n width -= segmentWidth;\n suffixBegin = index;\n }\n return v.substring(suffixBegin);\n}\n\n// Leaves enough space for the \"prefix\" to also fit.\nexport function fitToWidth(line: string, width: number, prefix?: string): string {\n const prefixLength = prefix ? stripAnsiEscapes(prefix).length : 0;\n width -= prefixLength;\n if (stringWidth(line) <= width)\n return line;\n\n // Even items are plain text, odd items are control sequences.\n const parts = line.split(ansiRegex);\n const taken: string[] = [];\n for (let i = parts.length - 1; i >= 0; i--) {\n if (i % 2) {\n // Include all control sequences to preserve formatting.\n taken.push(parts[i]);\n } else {\n let part = suffixOfWidth(parts[i], width);\n const wasTruncated = part.length < parts[i].length;\n if (wasTruncated && parts[i].length > 0) {\n // Add ellipsis if we are truncating.\n part = '\\u2026' + suffixOfWidth(parts[i], width - 1);\n }\n taken.push(part);\n width -= stringWidth(part);\n }\n }\n return taken.reverse().join('');\n}\n\nfunction resolveFromEnv(name: string): string | undefined {\n const value = process.env[name];\n if (value)\n return path.resolve(process.cwd(), value);\n return undefined;\n}\n\n// In addition to `outputFile` the function returns `outputDir` which should\n// be cleaned up if present by some reporters contract.\nexport function resolveOutputFile(reporterName: string, options: {\n configDir: string,\n outputDir?: string,\n fileName?: string,\n outputFile?: string,\n default?: {\n fileName: string,\n outputDir: string,\n }\n}): { outputFile: string, outputDir?: string } | undefined {\n const name = reporterName.toUpperCase();\n let outputFile = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_FILE`);\n if (!outputFile && options.outputFile)\n outputFile = path.resolve(options.configDir, options.outputFile);\n if (outputFile)\n return { outputFile };\n\n let outputDir = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_DIR`);\n if (!outputDir && options.outputDir)\n outputDir = path.resolve(options.configDir, options.outputDir);\n if (!outputDir && options.default)\n outputDir = resolveReporterOutputPath(options.default.outputDir, options.configDir, undefined);\n if (!outputDir)\n outputDir = options.configDir;\n\n const reportName = process.env[`PLAYWRIGHT_${name}_OUTPUT_NAME`] ?? options.fileName ?? options.default?.fileName;\n if (!reportName)\n return undefined;\n outputFile = path.resolve(outputDir, reportName);\n\n return { outputFile, outputDir };\n}\n\ntype TestAttachment = TestResult['attachments'][number];\n\ntype TestAttachmentGroup = TestAttachment & {\n expected?: TestAttachment;\n actual?: TestAttachment;\n diff?: TestAttachment;\n previous?: TestAttachment;\n};\n\nfunction groupAttachments(attachments: TestResult['attachments']): TestAttachmentGroup[] {\n const result: TestAttachmentGroup[] = [];\n const attachmentsByPrefix = new Map<string, TestAttachment>();\n for (const attachment of attachments) {\n if (!attachment.path) {\n result.push(attachment);\n continue;\n }\n\n const match = attachment.name.match(/^(.*)-(expected|actual|diff|previous)(\\.[^.]+)?$/);\n if (!match) {\n result.push(attachment);\n continue;\n }\n\n const [, name, category] = match;\n let group: TestAttachmentGroup | undefined = attachmentsByPrefix.get(name);\n if (!group) {\n group = { ...attachment, name };\n attachmentsByPrefix.set(name, group);\n result.push(group);\n }\n if (category === 'expected')\n group.expected = attachment;\n else if (category === 'actual')\n group.actual = attachment;\n else if (category === 'diff')\n group.diff = attachment;\n else if (category === 'previous')\n group.previous = attachment;\n }\n return result;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_utils", "import_utilsBundle", "realColors", "milliseconds", "path"]
7
+ }
@@ -56,6 +56,7 @@ class BlobReporter extends import_teleEmitter.TeleReporterEmitter {
56
56
  const metadata = {
57
57
  version: currentBlobReportVersion,
58
58
  userAgent: (0, import_utils2.getUserAgent)(),
59
+ // TODO: remove after some time, recommend config.tag instead.
59
60
  name: process.env.PWTEST_BOT_NAME,
60
61
  shard: config.shard ?? void 0,
61
62
  pathSeparator: import_path.default.sep
@@ -67,6 +68,8 @@ class BlobReporter extends import_teleEmitter.TeleReporterEmitter {
67
68
  this._config = config;
68
69
  super.onConfigure(config);
69
70
  }
71
+ async onTestPaused(test, result) {
72
+ }
70
73
  async onEnd(result) {
71
74
  await super.onEnd(result);
72
75
  const zipFileName = await this._prepareOutputFile();
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/reporters/blob.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport { Readable } from 'stream';\n\nimport { removeFolders, sanitizeForFilePath } from 'playwright-core/lib/utils';\nimport { ManualPromise, calculateSha1, createGuid, getUserAgent } from 'playwright-core/lib/utils';\nimport { mime } from 'playwright-core/lib/utilsBundle';\nimport { yazl } from 'playwright-core/lib/zipBundle';\n\nimport { resolveOutputFile, CommonReporterOptions } from './base';\nimport { TeleReporterEmitter } from './teleEmitter';\n\nimport type { BlobReporterOptions } from '../../types/test';\nimport type { FullConfig, FullResult, TestResult } from '../../types/testReporter';\nimport type { BlobReportMetadata, JsonAttachment, JsonEvent } from '../isomorphic/teleReceiver';\nimport type { EventEmitter } from 'events';\n\nexport const currentBlobReportVersion = 2;\n\nexport class BlobReporter extends TeleReporterEmitter {\n private readonly _messages: JsonEvent[] = [];\n private readonly _attachments: { originalPath: string, zipEntryPath: string }[] = [];\n private readonly _options: BlobReporterOptions & CommonReporterOptions;\n private readonly _salt: string;\n private _config!: FullConfig;\n\n constructor(options: BlobReporterOptions & CommonReporterOptions) {\n super(message => this._messages.push(message));\n this._options = options;\n if (this._options.fileName && !this._options.fileName.endsWith('.zip'))\n throw new Error(`Blob report file name must end with .zip extension: ${this._options.fileName}`);\n this._salt = createGuid();\n }\n\n override onConfigure(config: FullConfig) {\n const metadata: BlobReportMetadata = {\n version: currentBlobReportVersion,\n userAgent: getUserAgent(),\n name: process.env.PWTEST_BOT_NAME,\n shard: config.shard ?? undefined,\n pathSeparator: path.sep,\n };\n this._messages.push({\n method: 'onBlobReportMetadata',\n params: metadata\n });\n\n this._config = config;\n super.onConfigure(config);\n }\n\n override async onEnd(result: FullResult): Promise<void> {\n await super.onEnd(result);\n\n const zipFileName = await this._prepareOutputFile();\n\n const zipFile = new yazl.ZipFile();\n const zipFinishPromise = new ManualPromise<undefined>();\n const finishPromise = zipFinishPromise.catch(e => {\n throw new Error(`Failed to write report ${zipFileName}: ` + e.message);\n });\n\n (zipFile as any as EventEmitter).on('error', error => zipFinishPromise.reject(error));\n zipFile.outputStream.pipe(fs.createWriteStream(zipFileName)).on('close', () => {\n zipFinishPromise.resolve(undefined);\n }).on('error', error => zipFinishPromise.reject(error));\n\n for (const { originalPath, zipEntryPath } of this._attachments) {\n if (!fs.statSync(originalPath, { throwIfNoEntry: false })?.isFile())\n continue;\n zipFile.addFile(originalPath, zipEntryPath);\n }\n\n const lines = this._messages.map(m => JSON.stringify(m) + '\\n');\n const content = Readable.from(lines);\n zipFile.addReadStream(content, 'report.jsonl');\n zipFile.end();\n\n await finishPromise;\n }\n\n private async _prepareOutputFile() {\n const { outputFile, outputDir } = resolveOutputFile('BLOB', {\n ...this._options,\n default: {\n fileName: this._defaultReportName(this._config),\n outputDir: 'blob-report',\n }\n })!;\n if (!process.env.PWTEST_BLOB_DO_NOT_REMOVE)\n await removeFolders([outputDir!]);\n await fs.promises.mkdir(path.dirname(outputFile), { recursive: true });\n return outputFile;\n }\n\n private _defaultReportName(config: FullConfig) {\n let reportName = 'report';\n if (this._options._commandHash)\n reportName += '-' + sanitizeForFilePath(this._options._commandHash);\n if (config.shard) {\n const paddedNumber = `${config.shard.current}`.padStart(`${config.shard.total}`.length, '0');\n reportName = `${reportName}-${paddedNumber}`;\n }\n return `${reportName}.zip`;\n }\n\n override _serializeAttachments(attachments: TestResult['attachments']): JsonAttachment[] {\n return super._serializeAttachments(attachments).map(attachment => {\n if (!attachment.path)\n return attachment;\n // Add run guid to avoid clashes between shards.\n const sha1 = calculateSha1(attachment.path + this._salt);\n const extension = mime.getExtension(attachment.contentType) || 'dat';\n const newPath = `resources/${sha1}.${extension}`;\n this._attachments.push({ originalPath: attachment.path, zipEntryPath: newPath });\n return {\n ...attachment,\n path: newPath,\n };\n });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_utils", "path", "fs"]
7
+ }
@@ -73,6 +73,23 @@ class DotReporter extends import_base.TerminalReporter {
73
73
  this.writeLine("\n" + this.formatError(error).message);
74
74
  this._counter = 0;
75
75
  }
76
+ async onTestPaused(test, result) {
77
+ if (!process.stdin.isTTY && !process.env.PW_TEST_DEBUG_REPORTERS)
78
+ return;
79
+ this.screen.stdout.write("\n");
80
+ if (test.outcome() === "unexpected") {
81
+ this.writeLine(this.screen.colors.red(this.formatTestHeader(test, { indent: " " })));
82
+ this.writeLine(this.formatResultErrors(test, result));
83
+ (0, import_base.markErrorsAsReported)(result);
84
+ this.writeLine(this.screen.colors.yellow(" Paused on error. Press Ctrl+C to end.") + "\n");
85
+ } else {
86
+ this.writeLine(this.screen.colors.yellow(this.formatTestHeader(test, { indent: " " })));
87
+ this.writeLine(this.screen.colors.yellow(" Paused at test end. Press Ctrl+C to end.") + "\n");
88
+ }
89
+ this._counter = 0;
90
+ await new Promise(() => {
91
+ });
92
+ }
76
93
  async onEnd(result) {
77
94
  await super.onEnd(result);
78
95
  this.screen.stdout.write("\n");
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/reporters/dot.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { TerminalReporter } from './base';\n\nimport type { FullResult, Suite, TestCase, TestError, TestResult } from '../../types/testReporter';\n\nclass DotReporter extends TerminalReporter {\n private _counter = 0;\n\n override onBegin(suite: Suite) {\n super.onBegin(suite);\n this.writeLine(this.generateStartingMessage());\n }\n\n override onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {\n super.onStdOut(chunk, test, result);\n if (!this.config.quiet)\n this.screen.stdout.write(chunk);\n }\n\n override onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {\n super.onStdErr(chunk, test, result);\n if (!this.config.quiet)\n this.screen.stderr.write(chunk);\n }\n\n override onTestEnd(test: TestCase, result: TestResult) {\n super.onTestEnd(test, result);\n if (this._counter === 80) {\n this.screen.stdout.write('\\n');\n this._counter = 0;\n }\n ++this._counter;\n if (result.status === 'skipped') {\n this.screen.stdout.write(this.screen.colors.yellow('\u00B0'));\n return;\n }\n if (this.willRetry(test)) {\n this.screen.stdout.write(this.screen.colors.gray('\u00D7'));\n return;\n }\n switch (test.outcome()) {\n case 'expected': this.screen.stdout.write(this.screen.colors.green('\u00B7')); break;\n case 'unexpected': this.screen.stdout.write(this.screen.colors.red(result.status === 'timedOut' ? 'T' : 'F')); break;\n case 'flaky': this.screen.stdout.write(this.screen.colors.yellow('\u00B1')); break;\n }\n }\n\n override onError(error: TestError): void {\n super.onError(error);\n this.writeLine('\\n' + this.formatError(error).message);\n this._counter = 0;\n }\n\n override async onEnd(result: FullResult) {\n await super.onEnd(result);\n this.screen.stdout.write('\\n');\n this.epilogue(true);\n }\n}\n\nexport default DotReporter;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,kBAAiC;AAIjC,MAAM,oBAAoB,6BAAiB;AAAA,EAA3C;AAAA;AACE,SAAQ,WAAW;AAAA;AAAA,EAEV,QAAQ,OAAc;AAC7B,UAAM,QAAQ,KAAK;AACnB,SAAK,UAAU,KAAK,wBAAwB,CAAC;AAAA,EAC/C;AAAA,EAES,SAAS,OAAwB,MAAiB,QAAqB;AAC9E,UAAM,SAAS,OAAO,MAAM,MAAM;AAClC,QAAI,CAAC,KAAK,OAAO;AACf,WAAK,OAAO,OAAO,MAAM,KAAK;AAAA,EAClC;AAAA,EAES,SAAS,OAAwB,MAAiB,QAAqB;AAC9E,UAAM,SAAS,OAAO,MAAM,MAAM;AAClC,QAAI,CAAC,KAAK,OAAO;AACf,WAAK,OAAO,OAAO,MAAM,KAAK;AAAA,EAClC;AAAA,EAES,UAAU,MAAgB,QAAoB;AACrD,UAAM,UAAU,MAAM,MAAM;AAC5B,QAAI,KAAK,aAAa,IAAI;AACxB,WAAK,OAAO,OAAO,MAAM,IAAI;AAC7B,WAAK,WAAW;AAAA,IAClB;AACA,MAAE,KAAK;AACP,QAAI,OAAO,WAAW,WAAW;AAC/B,WAAK,OAAO,OAAO,MAAM,KAAK,OAAO,OAAO,OAAO,MAAG,CAAC;AACvD;AAAA,IACF;AACA,QAAI,KAAK,UAAU,IAAI,GAAG;AACxB,WAAK,OAAO,OAAO,MAAM,KAAK,OAAO,OAAO,KAAK,MAAG,CAAC;AACrD;AAAA,IACF;AACA,YAAQ,KAAK,QAAQ,GAAG;AAAA,MACtB,KAAK;AAAY,aAAK,OAAO,OAAO,MAAM,KAAK,OAAO,OAAO,MAAM,MAAG,CAAC;AAAG;AAAA,MAC1E,KAAK;AAAc,aAAK,OAAO,OAAO,MAAM,KAAK,OAAO,OAAO,IAAI,OAAO,WAAW,aAAa,MAAM,GAAG,CAAC;AAAG;AAAA,MAC/G,KAAK;AAAS,aAAK,OAAO,OAAO,MAAM,KAAK,OAAO,OAAO,OAAO,MAAG,CAAC;AAAG;AAAA,IAC1E;AAAA,EACF;AAAA,EAES,QAAQ,OAAwB;AACvC,UAAM,QAAQ,KAAK;AACnB,SAAK,UAAU,OAAO,KAAK,YAAY,KAAK,EAAE,OAAO;AACrD,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,MAAe,MAAM,QAAoB;AACvC,UAAM,MAAM,MAAM,MAAM;AACxB,SAAK,OAAO,OAAO,MAAM,IAAI;AAC7B,SAAK,SAAS,IAAI;AAAA,EACpB;AACF;AAEA,IAAO,cAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/reporters/empty.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ReporterV2 } from './reporterV2';\n\nclass EmptyReporter implements ReporterV2 {\n version(): 'v2' {\n return 'v2';\n }\n\n printsToStdio() {\n return false;\n }\n}\n\nexport default EmptyReporter;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA,MAAM,cAAoC;AAAA,EACxC,UAAgB;AACd,WAAO;AAAA,EACT;AAAA,EAEA,gBAAgB;AACd,WAAO;AAAA,EACT;AACF;AAEA,IAAO,gBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/reporters/github.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport path from 'path';\n\nimport { noColors } from 'playwright-core/lib/utils';\nimport { ms as milliseconds } from 'playwright-core/lib/utilsBundle';\n\nimport { TerminalReporter, formatResultFailure, formatRetry } from './base';\nimport { stripAnsiEscapes } from '../util';\n\nimport type { FullResult, TestCase, TestError } from '../../types/testReporter';\n\ntype GitHubLogType = 'debug' | 'notice' | 'warning' | 'error';\n\ntype GitHubLogOptions = Partial<{\n title: string;\n file: string;\n col: number;\n endColumn: number;\n line: number;\n endLine: number;\n}>;\n\nclass GitHubLogger {\n private _log(message: string, type: GitHubLogType = 'notice', options: GitHubLogOptions = {}) {\n message = message.replace(/\\n/g, '%0A');\n const configs = Object.entries(options)\n .map(([key, option]) => `${key}=${option}`)\n .join(',');\n // eslint-disable-next-line no-restricted-properties\n process.stdout.write(stripAnsiEscapes(`::${type} ${configs}::${message}\\n`));\n }\n\n debug(message: string, options?: GitHubLogOptions) {\n this._log(message, 'debug', options);\n }\n\n error(message: string, options?: GitHubLogOptions) {\n this._log(message, 'error', options);\n }\n\n notice(message: string, options?: GitHubLogOptions) {\n this._log(message, 'notice', options);\n }\n\n warning(message: string, options?: GitHubLogOptions) {\n this._log(message, 'warning', options);\n }\n}\n\nexport class GitHubReporter extends TerminalReporter {\n githubLogger = new GitHubLogger();\n\n constructor(options: { omitFailures?: boolean } = {}) {\n super(options);\n this.screen = { ...this.screen, colors: noColors };\n }\n\n printsToStdio() {\n return false;\n }\n\n override async onEnd(result: FullResult) {\n await super.onEnd(result);\n this._printAnnotations();\n }\n\n override onError(error: TestError) {\n const errorMessage = this.formatError(error).message;\n this.githubLogger.error(errorMessage);\n }\n\n private _printAnnotations() {\n const summary = this.generateSummary();\n const summaryMessage = this.generateSummaryMessage(summary);\n if (summary.failuresToPrint.length)\n this._printFailureAnnotations(summary.failuresToPrint);\n this._printSlowTestAnnotations();\n this._printSummaryAnnotation(summaryMessage);\n }\n\n private _printSlowTestAnnotations() {\n this.getSlowTests().forEach(([file, duration]) => {\n const filePath = workspaceRelativePath(path.join(process.cwd(), file));\n this.githubLogger.warning(`${filePath} took ${milliseconds(duration)}`, {\n title: 'Slow Test',\n file: filePath,\n });\n });\n }\n\n private _printSummaryAnnotation(summary: string){\n this.githubLogger.notice(summary, {\n title: '\uD83C\uDFAD Playwright Run Summary'\n });\n }\n\n private _printFailureAnnotations(failures: TestCase[]) {\n failures.forEach((test, index) => {\n const title = this.formatTestTitle(test);\n const header = this.formatTestHeader(test, { indent: ' ', index: index + 1, mode: 'error' });\n for (const result of test.results) {\n const errors = formatResultFailure(this.screen, test, result, ' ');\n for (const error of errors) {\n const options: GitHubLogOptions = {\n file: workspaceRelativePath(error.location?.file || test.location.file),\n title,\n };\n if (error.location) {\n options.line = error.location.line;\n options.col = error.location.column;\n }\n const message = [header, ...formatRetry(this.screen, result), error.message].join('\\n');\n this.githubLogger.error(message, options);\n }\n }\n });\n }\n}\n\nfunction workspaceRelativePath(filePath: string): string {\n return path.relative(process.env['GITHUB_WORKSPACE'] ?? '', filePath);\n}\n\nexport default GitHubReporter;\n"],
5
+ "mappings": "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",
6
+ "names": ["path", "milliseconds"]
7
+ }
@@ -51,6 +51,7 @@ const isHtmlReportOption = (type) => {
51
51
  class HtmlReporter {
52
52
  constructor(options) {
53
53
  this._topLevelErrors = [];
54
+ this._machines = [];
54
55
  this._options = options;
55
56
  }
56
57
  version() {
@@ -104,6 +105,9 @@ class HtmlReporter {
104
105
  onError(error) {
105
106
  this._topLevelErrors.push(error);
106
107
  }
108
+ onMachineEnd(result) {
109
+ this._machines.push(result);
110
+ }
107
111
  async onEnd(result) {
108
112
  const projectSuites = this.suite.suites;
109
113
  await (0, import_utils.removeFolders)([this._outputFolder]);
@@ -124,7 +128,7 @@ class HtmlReporter {
124
128
  noSnippets,
125
129
  noCopyPrompt
126
130
  });
127
- this._buildResult = await builder.build(this.config.metadata, projectSuites, result, this._topLevelErrors);
131
+ this._buildResult = await builder.build(this.config.metadata, projectSuites, result, this._topLevelErrors, this._machines);
128
132
  }
129
133
  async onExit() {
130
134
  if (process.env.CI || !this._buildResult)
@@ -215,7 +219,7 @@ class HtmlBuilder {
215
219
  this._dataZipFile = new import_zipBundle.yazl.ZipFile();
216
220
  this._attachmentsBaseURL = attachmentsBaseURL;
217
221
  }
218
- async build(metadata, projectSuites, result, topLevelErrors) {
222
+ async build(metadata, projectSuites, result, topLevelErrors, machines) {
219
223
  const data = /* @__PURE__ */ new Map();
220
224
  for (const projectSuite of projectSuites) {
221
225
  const projectName = projectSuite.project().name;
@@ -259,7 +263,13 @@ class HtmlBuilder {
259
263
  projectNames: projectSuites.map((r) => r.project().name),
260
264
  stats: { ...[...data.values()].reduce((a, e) => addStats(a, e.testFileSummary.stats), emptyStats()) },
261
265
  errors: topLevelErrors.map((error) => (0, import_base.formatError)(import_base.internalScreen, error).message),
262
- options: this._options
266
+ options: this._options,
267
+ machines: machines.map((s) => ({
268
+ duration: s.duration,
269
+ startTime: s.startTime.getTime(),
270
+ tag: s.tag,
271
+ shardIndex: s.shardIndex
272
+ }))
263
273
  };
264
274
  htmlReport.files.sort((f1, f2) => {
265
275
  const w1 = f1.stats.unexpected * 1e3 + f1.stats.flaky;