@stablyai/internal-playwright 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +168 -0
  4. package/ThirdPartyNotices.txt +6277 -0
  5. package/cli.js +19 -0
  6. package/index.d.ts +17 -0
  7. package/index.js +17 -0
  8. package/index.mjs +18 -0
  9. package/jsx-runtime.js +42 -0
  10. package/jsx-runtime.mjs +21 -0
  11. package/lib/agents/generateAgents.js +265 -0
  12. package/lib/agents/generator.md +102 -0
  13. package/lib/agents/healer.md +78 -0
  14. package/lib/agents/planner.md +135 -0
  15. package/lib/cli.js +274 -0
  16. package/lib/common/config.js +274 -0
  17. package/lib/common/config.js.map +7 -0
  18. package/lib/common/configLoader.js +377 -0
  19. package/lib/common/configLoader.js.map +7 -0
  20. package/lib/common/esmLoaderHost.js +102 -0
  21. package/lib/common/esmLoaderHost.js.map +7 -0
  22. package/lib/common/expectBundle.js +52 -0
  23. package/lib/common/expectBundle.js.map +7 -0
  24. package/lib/common/expectBundleImpl.js +389 -0
  25. package/lib/common/expectBundleImpl.js.map +7 -0
  26. package/lib/common/fixtures.js +302 -0
  27. package/lib/common/fixtures.js.map +7 -0
  28. package/lib/common/globals.js +58 -0
  29. package/lib/common/globals.js.map +7 -0
  30. package/lib/common/ipc.js +60 -0
  31. package/lib/common/ipc.js.map +7 -0
  32. package/lib/common/poolBuilder.js +85 -0
  33. package/lib/common/poolBuilder.js.map +7 -0
  34. package/lib/common/process.js +104 -0
  35. package/lib/common/process.js.map +7 -0
  36. package/lib/common/suiteUtils.js +140 -0
  37. package/lib/common/suiteUtils.js.map +7 -0
  38. package/lib/common/test.js +321 -0
  39. package/lib/common/test.js.map +7 -0
  40. package/lib/common/testLoader.js +100 -0
  41. package/lib/common/testLoader.js.map +7 -0
  42. package/lib/common/testType.js +310 -0
  43. package/lib/common/testType.js.map +7 -0
  44. package/lib/fsWatcher.js +67 -0
  45. package/lib/fsWatcher.js.map +7 -0
  46. package/lib/index.js +696 -0
  47. package/lib/index.js.map +7 -0
  48. package/lib/internalsForTest.js +42 -0
  49. package/lib/internalsForTest.js.map +7 -0
  50. package/lib/isomorphic/events.js +77 -0
  51. package/lib/isomorphic/events.js.map +7 -0
  52. package/lib/isomorphic/folders.js +30 -0
  53. package/lib/isomorphic/folders.js.map +7 -0
  54. package/lib/isomorphic/stringInternPool.js +69 -0
  55. package/lib/isomorphic/stringInternPool.js.map +7 -0
  56. package/lib/isomorphic/teleReceiver.js +507 -0
  57. package/lib/isomorphic/teleReceiver.js.map +7 -0
  58. package/lib/isomorphic/teleSuiteUpdater.js +137 -0
  59. package/lib/isomorphic/teleSuiteUpdater.js.map +7 -0
  60. package/lib/isomorphic/testServerConnection.js +211 -0
  61. package/lib/isomorphic/testServerConnection.js.map +7 -0
  62. package/lib/isomorphic/testServerInterface.js +16 -0
  63. package/lib/isomorphic/testServerInterface.js.map +7 -0
  64. package/lib/isomorphic/testTree.js +334 -0
  65. package/lib/isomorphic/testTree.js.map +7 -0
  66. package/lib/isomorphic/types.d.js +16 -0
  67. package/lib/isomorphic/types.d.js.map +7 -0
  68. package/lib/loader/loaderMain.js +59 -0
  69. package/lib/loader/loaderMain.js.map +7 -0
  70. package/lib/matchers/expect.js +325 -0
  71. package/lib/matchers/expect.js.map +7 -0
  72. package/lib/matchers/matcherHint.js +87 -0
  73. package/lib/matchers/matcherHint.js.map +7 -0
  74. package/lib/matchers/matchers.js +366 -0
  75. package/lib/matchers/matchers.js.map +7 -0
  76. package/lib/matchers/toBeTruthy.js +73 -0
  77. package/lib/matchers/toBeTruthy.js.map +7 -0
  78. package/lib/matchers/toEqual.js +99 -0
  79. package/lib/matchers/toEqual.js.map +7 -0
  80. package/lib/matchers/toHaveURL.js +102 -0
  81. package/lib/matchers/toHaveURL.js.map +7 -0
  82. package/lib/matchers/toMatchAriaSnapshot.js +159 -0
  83. package/lib/matchers/toMatchAriaSnapshot.js.map +7 -0
  84. package/lib/matchers/toMatchSnapshot.js +359 -0
  85. package/lib/matchers/toMatchSnapshot.js.map +7 -0
  86. package/lib/matchers/toMatchText.js +99 -0
  87. package/lib/matchers/toMatchText.js.map +7 -0
  88. package/lib/mcp/browser/actions.d.js +16 -0
  89. package/lib/mcp/browser/backend.js +93 -0
  90. package/lib/mcp/browser/backend.js.map +7 -0
  91. package/lib/mcp/browser/browserContextFactory.js +296 -0
  92. package/lib/mcp/browser/browserServerBackend.js +76 -0
  93. package/lib/mcp/browser/codegen.js +66 -0
  94. package/lib/mcp/browser/config.js +385 -0
  95. package/lib/mcp/browser/context.js +287 -0
  96. package/lib/mcp/browser/response.js +228 -0
  97. package/lib/mcp/browser/sessionLog.js +160 -0
  98. package/lib/mcp/browser/tab.js +277 -0
  99. package/lib/mcp/browser/tool.js +30 -0
  100. package/lib/mcp/browser/tool.js.map +7 -0
  101. package/lib/mcp/browser/tools/common.js +63 -0
  102. package/lib/mcp/browser/tools/console.js +44 -0
  103. package/lib/mcp/browser/tools/dialogs.js +60 -0
  104. package/lib/mcp/browser/tools/evaluate.js +70 -0
  105. package/lib/mcp/browser/tools/files.js +58 -0
  106. package/lib/mcp/browser/tools/form.js +74 -0
  107. package/lib/mcp/browser/tools/install.js +69 -0
  108. package/lib/mcp/browser/tools/keyboard.js +85 -0
  109. package/lib/mcp/browser/tools/mouse.js +107 -0
  110. package/lib/mcp/browser/tools/navigate.js +62 -0
  111. package/lib/mcp/browser/tools/network.js +54 -0
  112. package/lib/mcp/browser/tools/pdf.js +59 -0
  113. package/lib/mcp/browser/tools/screenshot.js +88 -0
  114. package/lib/mcp/browser/tools/snapshot.js +182 -0
  115. package/lib/mcp/browser/tools/tabs.js +67 -0
  116. package/lib/mcp/browser/tools/tool.js +49 -0
  117. package/lib/mcp/browser/tools/tracing.js +74 -0
  118. package/lib/mcp/browser/tools/utils.js +100 -0
  119. package/lib/mcp/browser/tools/verify.js +154 -0
  120. package/lib/mcp/browser/tools/wait.js +63 -0
  121. package/lib/mcp/browser/tools.js +80 -0
  122. package/lib/mcp/browser/tools.js.map +7 -0
  123. package/lib/mcp/browser/watchdog.js +44 -0
  124. package/lib/mcp/config.d.js +16 -0
  125. package/lib/mcp/extension/cdpRelay.js +351 -0
  126. package/lib/mcp/extension/extensionContextFactory.js +75 -0
  127. package/lib/mcp/extension/protocol.js +28 -0
  128. package/lib/mcp/index.js +61 -0
  129. package/lib/mcp/log.js +35 -0
  130. package/lib/mcp/program.js +96 -0
  131. package/lib/mcp/sdk/bundle.js +81 -0
  132. package/lib/mcp/sdk/bundle.js.map +7 -0
  133. package/lib/mcp/sdk/call.js +49 -0
  134. package/lib/mcp/sdk/call.js.map +7 -0
  135. package/lib/mcp/sdk/exports.js +32 -0
  136. package/lib/mcp/sdk/exports.js.map +7 -0
  137. package/lib/mcp/sdk/http.js +187 -0
  138. package/lib/mcp/sdk/http.js.map +7 -0
  139. package/lib/mcp/sdk/inProcessTransport.js +71 -0
  140. package/lib/mcp/sdk/inProcessTransport.js.map +7 -0
  141. package/lib/mcp/sdk/mdb.js +206 -0
  142. package/lib/mcp/sdk/mdb.js.map +7 -0
  143. package/lib/mcp/sdk/proxyBackend.js +128 -0
  144. package/lib/mcp/sdk/proxyBackend.js.map +7 -0
  145. package/lib/mcp/sdk/server.js +189 -0
  146. package/lib/mcp/sdk/server.js.map +7 -0
  147. package/lib/mcp/sdk/tool.js +51 -0
  148. package/lib/mcp/sdk/tool.js.map +7 -0
  149. package/lib/mcp/test/backend.js +67 -0
  150. package/lib/mcp/test/backend.js.map +7 -0
  151. package/lib/mcp/test/browserBackend.js +98 -0
  152. package/lib/mcp/test/context.js +48 -0
  153. package/lib/mcp/test/context.js.map +7 -0
  154. package/lib/mcp/test/generatorTools.js +122 -0
  155. package/lib/mcp/test/plannerTools.js +46 -0
  156. package/lib/mcp/test/seed.js +72 -0
  157. package/lib/mcp/test/streams.js +39 -0
  158. package/lib/mcp/test/streams.js.map +7 -0
  159. package/lib/mcp/test/testBackend.js +97 -0
  160. package/lib/mcp/test/testContext.js +176 -0
  161. package/lib/mcp/test/testTool.js +30 -0
  162. package/lib/mcp/test/testTools.js +115 -0
  163. package/lib/mcp/test/tool.js +30 -0
  164. package/lib/mcp/test/tool.js.map +7 -0
  165. package/lib/mcp/test/tools.js +150 -0
  166. package/lib/mcp/test/tools.js.map +7 -0
  167. package/lib/mcp/vscode/host.js +187 -0
  168. package/lib/mcp/vscode/main.js +77 -0
  169. package/lib/mcpBundleImpl.js +41 -0
  170. package/lib/mcpBundleImpl.js.map +7 -0
  171. package/lib/plugins/gitCommitInfoPlugin.js +198 -0
  172. package/lib/plugins/gitCommitInfoPlugin.js.map +7 -0
  173. package/lib/plugins/index.js +28 -0
  174. package/lib/plugins/index.js.map +7 -0
  175. package/lib/plugins/webServerPlugin.js +209 -0
  176. package/lib/plugins/webServerPlugin.js.map +7 -0
  177. package/lib/program.js +412 -0
  178. package/lib/program.js.map +7 -0
  179. package/lib/reporters/base.js +609 -0
  180. package/lib/reporters/base.js.map +7 -0
  181. package/lib/reporters/blob.js +135 -0
  182. package/lib/reporters/blob.js.map +7 -0
  183. package/lib/reporters/dot.js +82 -0
  184. package/lib/reporters/dot.js.map +7 -0
  185. package/lib/reporters/empty.js +32 -0
  186. package/lib/reporters/empty.js.map +7 -0
  187. package/lib/reporters/github.js +128 -0
  188. package/lib/reporters/github.js.map +7 -0
  189. package/lib/reporters/html.js +644 -0
  190. package/lib/reporters/html.js.map +7 -0
  191. package/lib/reporters/internalReporter.js +130 -0
  192. package/lib/reporters/internalReporter.js.map +7 -0
  193. package/lib/reporters/json.js +254 -0
  194. package/lib/reporters/json.js.map +7 -0
  195. package/lib/reporters/junit.js +230 -0
  196. package/lib/reporters/junit.js.map +7 -0
  197. package/lib/reporters/line.js +113 -0
  198. package/lib/reporters/line.js.map +7 -0
  199. package/lib/reporters/list.js +235 -0
  200. package/lib/reporters/list.js.map +7 -0
  201. package/lib/reporters/listModeReporter.js +69 -0
  202. package/lib/reporters/listModeReporter.js.map +7 -0
  203. package/lib/reporters/markdown.js +144 -0
  204. package/lib/reporters/markdown.js.map +7 -0
  205. package/lib/reporters/merge.js +535 -0
  206. package/lib/reporters/merge.js.map +7 -0
  207. package/lib/reporters/multiplexer.js +104 -0
  208. package/lib/reporters/multiplexer.js.map +7 -0
  209. package/lib/reporters/reporterV2.js +102 -0
  210. package/lib/reporters/reporterV2.js.map +7 -0
  211. package/lib/reporters/teleEmitter.js +297 -0
  212. package/lib/reporters/teleEmitter.js.map +7 -0
  213. package/lib/reporters/versions/blobV1.js +16 -0
  214. package/lib/reporters/versions/blobV1.js.map +7 -0
  215. package/lib/runner/dispatcher.js +491 -0
  216. package/lib/runner/dispatcher.js.map +7 -0
  217. package/lib/runner/failureTracker.js +72 -0
  218. package/lib/runner/failureTracker.js.map +7 -0
  219. package/lib/runner/lastRun.js +77 -0
  220. package/lib/runner/lastRun.js.map +7 -0
  221. package/lib/runner/loadUtils.js +333 -0
  222. package/lib/runner/loadUtils.js.map +7 -0
  223. package/lib/runner/loaderHost.js +89 -0
  224. package/lib/runner/loaderHost.js.map +7 -0
  225. package/lib/runner/processHost.js +161 -0
  226. package/lib/runner/processHost.js.map +7 -0
  227. package/lib/runner/projectUtils.js +241 -0
  228. package/lib/runner/projectUtils.js.map +7 -0
  229. package/lib/runner/rebase.js +189 -0
  230. package/lib/runner/rebase.js.map +7 -0
  231. package/lib/runner/reporters.js +137 -0
  232. package/lib/runner/reporters.js.map +7 -0
  233. package/lib/runner/runner.js +173 -0
  234. package/lib/runner/sigIntWatcher.js +96 -0
  235. package/lib/runner/sigIntWatcher.js.map +7 -0
  236. package/lib/runner/taskRunner.js +127 -0
  237. package/lib/runner/taskRunner.js.map +7 -0
  238. package/lib/runner/tasks.js +410 -0
  239. package/lib/runner/tasks.js.map +7 -0
  240. package/lib/runner/testGroups.js +117 -0
  241. package/lib/runner/testGroups.js.map +7 -0
  242. package/lib/runner/testRunner.js +390 -0
  243. package/lib/runner/testRunner.js.map +7 -0
  244. package/lib/runner/testServer.js +264 -0
  245. package/lib/runner/testServer.js.map +7 -0
  246. package/lib/runner/uiMode.js +271 -0
  247. package/lib/runner/uiModeReporter.js +30 -0
  248. package/lib/runner/uiModeReporter.js.map +7 -0
  249. package/lib/runner/vcs.js +72 -0
  250. package/lib/runner/vcs.js.map +7 -0
  251. package/lib/runner/watchMode.js +395 -0
  252. package/lib/runner/watchMode.js.map +7 -0
  253. package/lib/runner/workerHost.js +95 -0
  254. package/lib/runner/workerHost.js.map +7 -0
  255. package/lib/store.js +98 -0
  256. package/lib/third_party/pirates.js +62 -0
  257. package/lib/third_party/pirates.js.map +7 -0
  258. package/lib/third_party/tsconfig-loader.js +103 -0
  259. package/lib/third_party/tsconfig-loader.js.map +7 -0
  260. package/lib/transform/babelBundle.js +43 -0
  261. package/lib/transform/babelBundle.js.map +7 -0
  262. package/lib/transform/babelBundleImpl.js +461 -0
  263. package/lib/transform/babelBundleImpl.js.map +7 -0
  264. package/lib/transform/compilationCache.js +272 -0
  265. package/lib/transform/compilationCache.js.map +7 -0
  266. package/lib/transform/esmLoader.js +104 -0
  267. package/lib/transform/esmLoader.js.map +7 -0
  268. package/lib/transform/esmUtils.js +32 -0
  269. package/lib/transform/portTransport.js +67 -0
  270. package/lib/transform/portTransport.js.map +7 -0
  271. package/lib/transform/transform.js +293 -0
  272. package/lib/transform/transform.js.map +7 -0
  273. package/lib/util.js +403 -0
  274. package/lib/util.js.map +7 -0
  275. package/lib/utilsBundle.js +43 -0
  276. package/lib/utilsBundle.js.map +7 -0
  277. package/lib/utilsBundleImpl.js +100 -0
  278. package/lib/utilsBundleImpl.js.map +7 -0
  279. package/lib/worker/fixtureRunner.js +258 -0
  280. package/lib/worker/fixtureRunner.js.map +7 -0
  281. package/lib/worker/stepContext.js +34 -0
  282. package/lib/worker/testInfo.js +508 -0
  283. package/lib/worker/testInfo.js.map +7 -0
  284. package/lib/worker/testTracing.js +344 -0
  285. package/lib/worker/testTracing.js.map +7 -0
  286. package/lib/worker/timeoutManager.js +174 -0
  287. package/lib/worker/timeoutManager.js.map +7 -0
  288. package/lib/worker/util.js +31 -0
  289. package/lib/worker/util.js.map +7 -0
  290. package/lib/worker/workerMain.js +520 -0
  291. package/lib/worker/workerMain.js.map +7 -0
  292. package/package.json +74 -0
  293. package/test.d.ts +18 -0
  294. package/test.js +24 -0
  295. package/test.mjs +33 -0
  296. package/types/test.d.ts +10217 -0
  297. package/types/testReporter.d.ts +816 -0
@@ -0,0 +1,359 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var toMatchSnapshot_exports = {};
30
+ __export(toMatchSnapshot_exports, {
31
+ toHaveScreenshot: () => toHaveScreenshot,
32
+ toHaveScreenshotStepTitle: () => toHaveScreenshotStepTitle,
33
+ toMatchSnapshot: () => toMatchSnapshot
34
+ });
35
+ module.exports = __toCommonJS(toMatchSnapshot_exports);
36
+ var import_fs = __toESM(require("fs"));
37
+ var import_path = __toESM(require("path"));
38
+ var import_utils = require("playwright-core/lib/utils");
39
+ var import_utils2 = require("playwright-core/lib/utils");
40
+ var import_utilsBundle = require("playwright-core/lib/utilsBundle");
41
+ var import_healingService = require("playwright-core/lib/server/stably/autohealing/healingService");
42
+ var import_util = require("../util");
43
+ var import_matcherHint = require("./matcherHint");
44
+ var import_globals = require("../common/globals");
45
+ const NonConfigProperties = [
46
+ "clip",
47
+ "fullPage",
48
+ "mask",
49
+ "maskColor",
50
+ "omitBackground",
51
+ "timeout",
52
+ "autoheal"
53
+ ];
54
+ class SnapshotHelper {
55
+ constructor(testInfo, matcherName, locator, anonymousSnapshotExtension, configOptions, nameOrOptions, optOptions) {
56
+ let name;
57
+ if (Array.isArray(nameOrOptions) || typeof nameOrOptions === "string") {
58
+ name = nameOrOptions;
59
+ this.options = { ...optOptions };
60
+ } else {
61
+ const { name: nameFromOptions, ...options } = nameOrOptions;
62
+ this.options = options;
63
+ name = nameFromOptions;
64
+ }
65
+ this.name = Array.isArray(name) ? name.join(import_path.default.sep) : name || "";
66
+ const resolvedPaths = testInfo._resolveSnapshotPaths(matcherName === "toHaveScreenshot" ? "screenshot" : "snapshot", name, "updateSnapshotIndex", anonymousSnapshotExtension);
67
+ this.expectedPath = resolvedPaths.absoluteSnapshotPath;
68
+ this.attachmentBaseName = resolvedPaths.relativeOutputPath;
69
+ const outputBasePath = testInfo._getOutputPath(resolvedPaths.relativeOutputPath);
70
+ this.legacyExpectedPath = (0, import_util.addSuffixToFilePath)(outputBasePath, "-expected");
71
+ this.previousPath = (0, import_util.addSuffixToFilePath)(outputBasePath, "-previous");
72
+ this.actualPath = (0, import_util.addSuffixToFilePath)(outputBasePath, "-actual");
73
+ this.diffPath = (0, import_util.addSuffixToFilePath)(outputBasePath, "-diff");
74
+ const filteredConfigOptions = { ...configOptions };
75
+ for (const prop of NonConfigProperties)
76
+ delete filteredConfigOptions[prop];
77
+ this.options = {
78
+ ...filteredConfigOptions,
79
+ ...this.options
80
+ };
81
+ if (this.options._comparator) {
82
+ this.options.comparator = this.options._comparator;
83
+ delete this.options._comparator;
84
+ }
85
+ if (this.options.maxDiffPixels !== void 0 && this.options.maxDiffPixels < 0)
86
+ throw new Error("`maxDiffPixels` option value must be non-negative integer");
87
+ if (this.options.maxDiffPixelRatio !== void 0 && (this.options.maxDiffPixelRatio < 0 || this.options.maxDiffPixelRatio > 1))
88
+ throw new Error("`maxDiffPixelRatio` option value must be between 0 and 1");
89
+ this.matcherName = matcherName;
90
+ this.locator = locator;
91
+ this.updateSnapshots = testInfo.config.updateSnapshots;
92
+ this.mimeType = import_utilsBundle.mime.getType(import_path.default.basename(this.expectedPath)) ?? "application/octet-stream";
93
+ this.comparator = (0, import_utils.getComparator)(this.mimeType);
94
+ this.testInfo = testInfo;
95
+ this.kind = this.mimeType.startsWith("image/") ? "Screenshot" : "Snapshot";
96
+ }
97
+ createMatcherResult(message, pass, log) {
98
+ const unfiltered = {
99
+ name: this.matcherName,
100
+ expected: this.expectedPath,
101
+ actual: this.actualPath,
102
+ diff: this.diffPath,
103
+ pass,
104
+ message: () => message,
105
+ log
106
+ };
107
+ return Object.fromEntries(Object.entries(unfiltered).filter(([_, v]) => v !== void 0));
108
+ }
109
+ handleMissingNegated() {
110
+ const isWriteMissingMode = this.updateSnapshots !== "none";
111
+ const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? `, matchers using ".not" won't write them automatically.` : "."}`;
112
+ return this.createMatcherResult(message, true);
113
+ }
114
+ handleDifferentNegated() {
115
+ return this.createMatcherResult("", false);
116
+ }
117
+ handleMatchingNegated() {
118
+ const message = [
119
+ import_utils2.colors.red(`${this.kind} comparison failed:`),
120
+ "",
121
+ indent("Expected result should be different from the actual one.", " ")
122
+ ].join("\n");
123
+ return this.createMatcherResult(message, true);
124
+ }
125
+ handleMissing(actual, step) {
126
+ const isWriteMissingMode = this.updateSnapshots !== "none";
127
+ if (isWriteMissingMode)
128
+ writeFileSync(this.expectedPath, actual);
129
+ step?._attachToStep({ name: (0, import_util.addSuffixToFilePath)(this.attachmentBaseName, "-expected"), contentType: this.mimeType, path: this.expectedPath });
130
+ writeFileSync(this.actualPath, actual);
131
+ step?._attachToStep({ name: (0, import_util.addSuffixToFilePath)(this.attachmentBaseName, "-actual"), contentType: this.mimeType, path: this.actualPath });
132
+ const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ", writing actual." : "."}`;
133
+ if (this.updateSnapshots === "all" || this.updateSnapshots === "changed") {
134
+ console.log(message);
135
+ return this.createMatcherResult(message, true);
136
+ }
137
+ if (this.updateSnapshots === "missing") {
138
+ this.testInfo._hasNonRetriableError = true;
139
+ this.testInfo._failWithError(new Error(message));
140
+ return this.createMatcherResult("", true);
141
+ }
142
+ return this.createMatcherResult(message, false);
143
+ }
144
+ handleDifferent(actual, expected, previous, diff, header, diffError, log, step) {
145
+ const output = [`${header}${indent(diffError, " ")}`];
146
+ if (this.name) {
147
+ output.push("");
148
+ output.push(` Snapshot: ${this.name}`);
149
+ }
150
+ if (expected !== void 0) {
151
+ writeFileSync(this.legacyExpectedPath, expected);
152
+ step?._attachToStep({ name: (0, import_util.addSuffixToFilePath)(this.attachmentBaseName, "-expected"), contentType: this.mimeType, path: this.expectedPath });
153
+ }
154
+ if (previous !== void 0) {
155
+ writeFileSync(this.previousPath, previous);
156
+ step?._attachToStep({ name: (0, import_util.addSuffixToFilePath)(this.attachmentBaseName, "-previous"), contentType: this.mimeType, path: this.previousPath });
157
+ }
158
+ if (actual !== void 0) {
159
+ writeFileSync(this.actualPath, actual);
160
+ step?._attachToStep({ name: (0, import_util.addSuffixToFilePath)(this.attachmentBaseName, "-actual"), contentType: this.mimeType, path: this.actualPath });
161
+ }
162
+ if (diff !== void 0) {
163
+ writeFileSync(this.diffPath, diff);
164
+ step?._attachToStep({ name: (0, import_util.addSuffixToFilePath)(this.attachmentBaseName, "-diff"), contentType: this.mimeType, path: this.diffPath });
165
+ }
166
+ if (log?.length)
167
+ output.push((0, import_matcherHint.callLogText)(log));
168
+ else
169
+ output.push("");
170
+ return this.createMatcherResult(output.join("\n"), false, log);
171
+ }
172
+ handleMatching() {
173
+ return this.createMatcherResult("", true);
174
+ }
175
+ }
176
+ function toMatchSnapshot(received, nameOrOptions = {}, optOptions = {}) {
177
+ const testInfo = (0, import_globals.currentTestInfo)();
178
+ if (!testInfo)
179
+ throw new Error(`toMatchSnapshot() must be called during the test`);
180
+ if (received instanceof Promise)
181
+ throw new Error("An unresolved Promise was passed to toMatchSnapshot(), make sure to resolve it by adding await to it.");
182
+ if (testInfo._projectInternal.ignoreSnapshots)
183
+ return { pass: !this.isNot, message: () => "", name: "toMatchSnapshot", expected: nameOrOptions };
184
+ const configOptions = testInfo._projectInternal.expect?.toMatchSnapshot || {};
185
+ const helper = new SnapshotHelper(
186
+ testInfo,
187
+ "toMatchSnapshot",
188
+ void 0,
189
+ "." + determineFileExtension(received),
190
+ configOptions,
191
+ nameOrOptions,
192
+ optOptions
193
+ );
194
+ if (this.isNot) {
195
+ if (!import_fs.default.existsSync(helper.expectedPath))
196
+ return helper.handleMissingNegated();
197
+ const isDifferent = !!helper.comparator(received, import_fs.default.readFileSync(helper.expectedPath), helper.options);
198
+ return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();
199
+ }
200
+ if (!import_fs.default.existsSync(helper.expectedPath))
201
+ return helper.handleMissing(received, this._stepInfo);
202
+ const expected = import_fs.default.readFileSync(helper.expectedPath);
203
+ if (helper.updateSnapshots === "all") {
204
+ if (!(0, import_utils.compareBuffersOrStrings)(received, expected))
205
+ return helper.handleMatching();
206
+ writeFileSync(helper.expectedPath, received);
207
+ console.log(helper.expectedPath + " is not the same, writing actual.");
208
+ return helper.createMatcherResult(helper.expectedPath + " running with --update-snapshots, writing actual.", true);
209
+ }
210
+ if (helper.updateSnapshots === "changed") {
211
+ const result2 = helper.comparator(received, expected, helper.options);
212
+ if (!result2)
213
+ return helper.handleMatching();
214
+ writeFileSync(helper.expectedPath, received);
215
+ console.log(helper.expectedPath + " does not match, writing actual.");
216
+ return helper.createMatcherResult(helper.expectedPath + " running with --update-snapshots, writing actual.", true);
217
+ }
218
+ const result = helper.comparator(received, expected, helper.options);
219
+ if (!result)
220
+ return helper.handleMatching();
221
+ const header = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toMatchSnapshot", receiver: (0, import_utils.isString)(received) ? "string" : "Buffer", expectation: "expected" });
222
+ return helper.handleDifferent(received, expected, void 0, result.diff, header, result.errorMessage, void 0, this._stepInfo);
223
+ }
224
+ function toHaveScreenshotStepTitle(nameOrOptions = {}, optOptions = {}) {
225
+ let name;
226
+ if (typeof nameOrOptions === "object" && !Array.isArray(nameOrOptions))
227
+ name = nameOrOptions.name;
228
+ else
229
+ name = nameOrOptions;
230
+ return Array.isArray(name) ? name.join(import_path.default.sep) : name || "";
231
+ }
232
+ async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions = {}) {
233
+ const testInfo = (0, import_globals.currentTestInfo)();
234
+ if (!testInfo)
235
+ throw new Error(`toHaveScreenshot() must be called during the test`);
236
+ if (testInfo._projectInternal.ignoreSnapshots)
237
+ return { pass: !this.isNot, message: () => "", name: "toHaveScreenshot", expected: nameOrOptions };
238
+ (0, import_util.expectTypes)(pageOrLocator, ["Page", "Locator"], "toHaveScreenshot");
239
+ const [page, locator] = pageOrLocator.constructor.name === "Page" ? [pageOrLocator, void 0] : [pageOrLocator.page(), pageOrLocator];
240
+ const configOptions = testInfo._projectInternal.expect?.toHaveScreenshot || {};
241
+ const helper = new SnapshotHelper(testInfo, "toHaveScreenshot", locator, void 0, configOptions, nameOrOptions, optOptions);
242
+ if (!helper.expectedPath.toLowerCase().endsWith(".png"))
243
+ throw new Error(`Screenshot name "${import_path.default.basename(helper.expectedPath)}" must have '.png' extension`);
244
+ (0, import_util.expectTypes)(pageOrLocator, ["Page", "Locator"], "toHaveScreenshot");
245
+ const style = await loadScreenshotStyles(helper.options.stylePath);
246
+ const timeout = helper.options.timeout ?? this.timeout;
247
+ const expectScreenshotOptions = {
248
+ locator,
249
+ animations: helper.options.animations ?? "disabled",
250
+ caret: helper.options.caret ?? "hide",
251
+ clip: helper.options.clip,
252
+ fullPage: helper.options.fullPage,
253
+ mask: helper.options.mask,
254
+ maskColor: helper.options.maskColor,
255
+ omitBackground: helper.options.omitBackground,
256
+ scale: helper.options.scale ?? "css",
257
+ style,
258
+ isNot: !!this.isNot,
259
+ timeout,
260
+ comparator: helper.options.comparator,
261
+ maxDiffPixels: helper.options.maxDiffPixels,
262
+ maxDiffPixelRatio: helper.options.maxDiffPixelRatio,
263
+ threshold: helper.options.threshold
264
+ };
265
+ const hasSnapshot = import_fs.default.existsSync(helper.expectedPath);
266
+ if (this.isNot) {
267
+ if (!hasSnapshot)
268
+ return helper.handleMissingNegated();
269
+ expectScreenshotOptions.expected = await import_fs.default.promises.readFile(helper.expectedPath);
270
+ const isDifferent = !(await page._expectScreenshot(expectScreenshotOptions)).errorMessage;
271
+ return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();
272
+ }
273
+ if (helper.updateSnapshots === "none" && !hasSnapshot)
274
+ return helper.createMatcherResult(`A snapshot doesn't exist at ${helper.expectedPath}.`, false);
275
+ if (!hasSnapshot) {
276
+ const { actual: actual2, previous: previous2, diff: diff2, errorMessage: errorMessage2, log: log2, timedOut: timedOut2 } = await page._expectScreenshot(expectScreenshotOptions);
277
+ if (errorMessage2) {
278
+ const header2 = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toHaveScreenshot", locator, expectation: "expected", timeout, timedOut: timedOut2 });
279
+ return helper.handleDifferent(actual2, void 0, previous2, diff2, header2, errorMessage2, log2, this._stepInfo);
280
+ }
281
+ return helper.handleMissing(actual2, this._stepInfo);
282
+ }
283
+ const expected = await import_fs.default.promises.readFile(helper.expectedPath);
284
+ expectScreenshotOptions.expected = helper.updateSnapshots === "all" ? void 0 : expected;
285
+ const { actual, previous, diff, errorMessage, log, timedOut } = await page._expectScreenshot(expectScreenshotOptions);
286
+ const writeFiles = (actualBuffer) => {
287
+ writeFileSync(helper.expectedPath, actualBuffer);
288
+ writeFileSync(helper.actualPath, actualBuffer);
289
+ console.log(helper.expectedPath + " is re-generated, writing actual.");
290
+ return helper.createMatcherResult(helper.expectedPath + " running with --update-snapshots, writing actual.", true);
291
+ };
292
+ if (!errorMessage) {
293
+ if (helper.updateSnapshots === "all" && actual && (0, import_utils.compareBuffersOrStrings)(actual, expected)) {
294
+ console.log(helper.expectedPath + " is re-generated, writing actual.");
295
+ return writeFiles(actual);
296
+ }
297
+ return helper.handleMatching();
298
+ }
299
+ if (helper.updateSnapshots === "changed" || helper.updateSnapshots === "all") {
300
+ if (actual)
301
+ return writeFiles(actual);
302
+ let header2 = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toHaveScreenshot", locator, expectation: "expected", timeout, timedOut });
303
+ header2 += " Failed to re-generate expected.\n";
304
+ return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header2, errorMessage, log, this._stepInfo);
305
+ }
306
+ if (helper.options.autoheal ?? (0, import_healingService.isHealingEnabled)()) {
307
+ if (actual && expected) {
308
+ try {
309
+ const aiScreenshotMatchJudgement = await (0, import_healingService.attemptToHaveScreenshotHealing)({
310
+ actualScreenshot: actual,
311
+ expectedScreenshot: expected
312
+ });
313
+ if (aiScreenshotMatchJudgement)
314
+ return helper.handleMatching();
315
+ } catch (healingError) {
316
+ const header2 = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toHaveScreenshot", locator, expectation: "expected", timeout, timedOut });
317
+ const healErrorMessage = `Screenshot healing failed: ${healingError instanceof Error ? healingError.message : String(healingError)}`;
318
+ return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header2, healErrorMessage, log, this._stepInfo);
319
+ }
320
+ }
321
+ }
322
+ const header = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toHaveScreenshot", locator, expectation: "expected", timeout, timedOut });
323
+ return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header, errorMessage, log, this._stepInfo);
324
+ }
325
+ function writeFileSync(aPath, content) {
326
+ import_fs.default.mkdirSync(import_path.default.dirname(aPath), { recursive: true });
327
+ import_fs.default.writeFileSync(aPath, content);
328
+ }
329
+ function indent(lines, tab) {
330
+ return lines.replace(/^(?=.+$)/gm, tab);
331
+ }
332
+ function determineFileExtension(file) {
333
+ if (typeof file === "string")
334
+ return "txt";
335
+ if (compareMagicBytes(file, [137, 80, 78, 71, 13, 10, 26, 10]))
336
+ return "png";
337
+ if (compareMagicBytes(file, [255, 216, 255]))
338
+ return "jpg";
339
+ return "dat";
340
+ }
341
+ function compareMagicBytes(file, magicBytes) {
342
+ return Buffer.compare(Buffer.from(magicBytes), file.slice(0, magicBytes.length)) === 0;
343
+ }
344
+ async function loadScreenshotStyles(stylePath) {
345
+ if (!stylePath)
346
+ return;
347
+ const stylePaths = Array.isArray(stylePath) ? stylePath : [stylePath];
348
+ const styles = await Promise.all(stylePaths.map(async (stylePath2) => {
349
+ const text = await import_fs.default.promises.readFile(stylePath2, "utf8");
350
+ return text.trim();
351
+ }));
352
+ return styles.join("\n").trim() || void 0;
353
+ }
354
+ // Annotate the CommonJS export names for ESM import in node:
355
+ 0 && (module.exports = {
356
+ toHaveScreenshot,
357
+ toHaveScreenshotStepTitle,
358
+ toMatchSnapshot
359
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/matchers/toMatchSnapshot.ts"],
4
+ "sourcesContent": ["/**\n * Copyright Microsoft Corporation. All rights reserved.\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';\n\nimport { compareBuffersOrStrings, getComparator, isString } from 'playwright-core/lib/utils';\nimport { colors } from 'playwright-core/lib/utils';\nimport { mime } from 'playwright-core/lib/utilsBundle';\n\nimport { addSuffixToFilePath, callLogText, expectTypes } from '../util';\nimport { matcherHint } from './matcherHint';\nimport { currentTestInfo } from '../common/globals';\n\nimport type { MatcherResult } from './matcherHint';\nimport type { ExpectMatcherStateInternal } from './matchers';\nimport type { FullProjectInternal } from '../common/config';\nimport type { TestInfoImpl, TestStepInfoImpl } from '../worker/testInfo';\nimport type { Locator, Page } from 'playwright-core';\nimport type { ExpectScreenshotOptions, Page as PageEx } from 'playwright-core/lib/client/page';\nimport type { Comparator, ImageComparatorOptions } from 'playwright-core/lib/utils';\n\ntype NameOrSegments = string | string[];\n\ntype ImageMatcherResult = MatcherResult<string, string> & { diff?: string };\n\ntype ToHaveScreenshotConfigOptions = NonNullable<NonNullable<FullProjectInternal['expect']>['toHaveScreenshot']> & {\n _comparator?: string;\n};\n\ntype ToHaveScreenshotOptions = ToHaveScreenshotConfigOptions & {\n clip?: {\n x: number;\n y: number;\n width: number;\n height: number;\n };\n fullPage?: boolean;\n mask?: Array<Locator>;\n maskColor?: string;\n omitBackground?: boolean;\n timeout?: number;\n};\n\n// Keep in sync with above (begin).\nconst NonConfigProperties: (keyof ToHaveScreenshotOptions)[] = [\n 'clip',\n 'fullPage',\n 'mask',\n 'maskColor',\n 'omitBackground',\n 'timeout',\n];\n// Keep in sync with above (end).\n\nclass SnapshotHelper {\n readonly testInfo: TestInfoImpl;\n readonly name: string;\n readonly attachmentBaseName: string;\n readonly legacyExpectedPath: string;\n readonly previousPath: string;\n readonly expectedPath: string;\n readonly actualPath: string;\n readonly diffPath: string;\n readonly mimeType: string;\n readonly kind: 'Screenshot'|'Snapshot';\n readonly updateSnapshots: 'all' | 'changed' | 'missing' | 'none';\n readonly comparator: Comparator;\n readonly options: Omit<ToHaveScreenshotOptions, '_comparator'> & { comparator?: string };\n readonly matcherName: string;\n readonly locator: Locator | undefined;\n\n constructor(\n testInfo: TestInfoImpl,\n matcherName: 'toMatchSnapshot' | 'toHaveScreenshot',\n locator: Locator | undefined,\n anonymousSnapshotExtension: string | undefined,\n configOptions: ToHaveScreenshotConfigOptions,\n nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions,\n optOptions: ToHaveScreenshotOptions,\n ) {\n let name: NameOrSegments | undefined;\n if (Array.isArray(nameOrOptions) || typeof nameOrOptions === 'string') {\n name = nameOrOptions;\n this.options = { ...optOptions };\n } else {\n const { name: nameFromOptions, ...options } = nameOrOptions;\n this.options = options;\n name = nameFromOptions;\n }\n\n this.name = Array.isArray(name) ? name.join(path.sep) : name || '';\n const resolvedPaths = testInfo._resolveSnapshotPaths(matcherName === 'toHaveScreenshot' ? 'screenshot' : 'snapshot', name, 'updateSnapshotIndex', anonymousSnapshotExtension);\n this.expectedPath = resolvedPaths.absoluteSnapshotPath;\n this.attachmentBaseName = resolvedPaths.relativeOutputPath;\n\n const outputBasePath = testInfo._getOutputPath(resolvedPaths.relativeOutputPath);\n this.legacyExpectedPath = addSuffixToFilePath(outputBasePath, '-expected');\n this.previousPath = addSuffixToFilePath(outputBasePath, '-previous');\n this.actualPath = addSuffixToFilePath(outputBasePath, '-actual');\n this.diffPath = addSuffixToFilePath(outputBasePath, '-diff');\n\n const filteredConfigOptions = { ...configOptions };\n for (const prop of NonConfigProperties)\n delete (filteredConfigOptions as any)[prop];\n this.options = {\n ...filteredConfigOptions,\n ...this.options,\n };\n\n // While comparator is not a part of the public API, it is translated here.\n if ((this.options as any)._comparator) {\n this.options.comparator = (this.options as any)._comparator;\n delete (this.options as any)._comparator;\n }\n\n if (this.options.maxDiffPixels !== undefined && this.options.maxDiffPixels < 0)\n throw new Error('`maxDiffPixels` option value must be non-negative integer');\n\n if (this.options.maxDiffPixelRatio !== undefined && (this.options.maxDiffPixelRatio < 0 || this.options.maxDiffPixelRatio > 1))\n throw new Error('`maxDiffPixelRatio` option value must be between 0 and 1');\n\n this.matcherName = matcherName;\n this.locator = locator;\n\n this.updateSnapshots = testInfo.config.updateSnapshots;\n this.mimeType = mime.getType(path.basename(this.expectedPath)) ?? 'application/octet-stream';\n this.comparator = getComparator(this.mimeType);\n\n this.testInfo = testInfo;\n this.kind = this.mimeType.startsWith('image/') ? 'Screenshot' : 'Snapshot';\n }\n\n createMatcherResult(message: string, pass: boolean, log?: string[]): ImageMatcherResult {\n const unfiltered: ImageMatcherResult = {\n name: this.matcherName,\n expected: this.expectedPath,\n actual: this.actualPath,\n diff: this.diffPath,\n pass,\n message: () => message,\n log,\n };\n return Object.fromEntries(Object.entries(unfiltered).filter(([_, v]) => v !== undefined)) as ImageMatcherResult;\n }\n\n handleMissingNegated(): ImageMatcherResult {\n const isWriteMissingMode = this.updateSnapshots !== 'none';\n const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', matchers using \".not\" won\\'t write them automatically.' : '.'}`;\n // NOTE: 'isNot' matcher implies inversed value.\n return this.createMatcherResult(message, true);\n }\n\n handleDifferentNegated(): ImageMatcherResult {\n // NOTE: 'isNot' matcher implies inversed value.\n return this.createMatcherResult('', false);\n }\n\n handleMatchingNegated(): ImageMatcherResult {\n const message = [\n colors.red(`${this.kind} comparison failed:`),\n '',\n indent('Expected result should be different from the actual one.', ' '),\n ].join('\\n');\n // NOTE: 'isNot' matcher implies inversed value.\n return this.createMatcherResult(message, true);\n }\n\n handleMissing(actual: Buffer | string, step: TestStepInfoImpl | undefined): ImageMatcherResult {\n const isWriteMissingMode = this.updateSnapshots !== 'none';\n if (isWriteMissingMode)\n writeFileSync(this.expectedPath, actual);\n step?._attachToStep({ name: addSuffixToFilePath(this.attachmentBaseName, '-expected'), contentType: this.mimeType, path: this.expectedPath });\n writeFileSync(this.actualPath, actual);\n step?._attachToStep({ name: addSuffixToFilePath(this.attachmentBaseName, '-actual'), contentType: this.mimeType, path: this.actualPath });\n const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', writing actual.' : '.'}`;\n if (this.updateSnapshots === 'all' || this.updateSnapshots === 'changed') {\n /* eslint-disable no-console */\n console.log(message);\n return this.createMatcherResult(message, true);\n }\n if (this.updateSnapshots === 'missing') {\n this.testInfo._hasNonRetriableError = true;\n this.testInfo._failWithError(new Error(message));\n return this.createMatcherResult('', true);\n }\n return this.createMatcherResult(message, false);\n }\n\n handleDifferent(\n actual: Buffer | string | undefined,\n expected: Buffer | string | undefined,\n previous: Buffer | string | undefined,\n diff: Buffer | string | undefined,\n header: string,\n diffError: string,\n log: string[] | undefined,\n step: TestStepInfoImpl | undefined): ImageMatcherResult {\n const output = [`${header}${indent(diffError, ' ')}`];\n if (this.name) {\n output.push('');\n output.push(` Snapshot: ${this.name}`);\n }\n if (expected !== undefined) {\n // Copy the expectation inside the `test-results/` folder for backwards compatibility,\n // so that one can upload `test-results/` directory and have all the data inside.\n writeFileSync(this.legacyExpectedPath, expected);\n step?._attachToStep({ name: addSuffixToFilePath(this.attachmentBaseName, '-expected'), contentType: this.mimeType, path: this.expectedPath });\n }\n if (previous !== undefined) {\n writeFileSync(this.previousPath, previous);\n step?._attachToStep({ name: addSuffixToFilePath(this.attachmentBaseName, '-previous'), contentType: this.mimeType, path: this.previousPath });\n }\n if (actual !== undefined) {\n writeFileSync(this.actualPath, actual);\n step?._attachToStep({ name: addSuffixToFilePath(this.attachmentBaseName, '-actual'), contentType: this.mimeType, path: this.actualPath });\n }\n if (diff !== undefined) {\n writeFileSync(this.diffPath, diff);\n step?._attachToStep({ name: addSuffixToFilePath(this.attachmentBaseName, '-diff'), contentType: this.mimeType, path: this.diffPath });\n }\n\n if (log?.length)\n output.push(callLogText(log));\n else\n output.push('');\n\n return this.createMatcherResult(output.join('\\n'), false, log);\n }\n\n handleMatching(): ImageMatcherResult {\n return this.createMatcherResult('', true);\n }\n}\n\nexport function toMatchSnapshot(\n this: ExpectMatcherStateInternal,\n received: Buffer | string,\n nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ImageComparatorOptions = {},\n optOptions: ImageComparatorOptions = {}\n): MatcherResult<NameOrSegments | { name?: NameOrSegments }, string> {\n const testInfo = currentTestInfo();\n if (!testInfo)\n throw new Error(`toMatchSnapshot() must be called during the test`);\n if (received instanceof Promise)\n throw new Error('An unresolved Promise was passed to toMatchSnapshot(), make sure to resolve it by adding await to it.');\n\n if (testInfo._projectInternal.ignoreSnapshots)\n return { pass: !this.isNot, message: () => '', name: 'toMatchSnapshot', expected: nameOrOptions };\n\n const configOptions = testInfo._projectInternal.expect?.toMatchSnapshot || {};\n const helper = new SnapshotHelper(\n testInfo, 'toMatchSnapshot', undefined, '.' + determineFileExtension(received),\n configOptions, nameOrOptions, optOptions);\n\n if (this.isNot) {\n if (!fs.existsSync(helper.expectedPath))\n return helper.handleMissingNegated();\n const isDifferent = !!helper.comparator(received, fs.readFileSync(helper.expectedPath), helper.options);\n return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();\n }\n\n if (!fs.existsSync(helper.expectedPath))\n return helper.handleMissing(received, this._stepInfo);\n\n const expected = fs.readFileSync(helper.expectedPath);\n\n if (helper.updateSnapshots === 'all') {\n if (!compareBuffersOrStrings(received, expected))\n return helper.handleMatching();\n writeFileSync(helper.expectedPath, received);\n /* eslint-disable no-console */\n console.log(helper.expectedPath + ' is not the same, writing actual.');\n return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);\n }\n\n if (helper.updateSnapshots === 'changed') {\n const result = helper.comparator(received, expected, helper.options);\n if (!result)\n return helper.handleMatching();\n writeFileSync(helper.expectedPath, received);\n /* eslint-disable no-console */\n console.log(helper.expectedPath + ' does not match, writing actual.');\n return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);\n }\n\n const result = helper.comparator(received, expected, helper.options);\n if (!result)\n return helper.handleMatching();\n\n const receiver = isString(received) ? 'string' : 'Buffer';\n const header = matcherHint(this, undefined, 'toMatchSnapshot', receiver, undefined, undefined, undefined);\n return helper.handleDifferent(received, expected, undefined, result.diff, header, result.errorMessage, undefined, this._stepInfo);\n}\n\nexport function toHaveScreenshotStepTitle(\n nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {},\n optOptions: ToHaveScreenshotOptions = {}\n): string {\n let name: NameOrSegments | undefined;\n if (typeof nameOrOptions === 'object' && !Array.isArray(nameOrOptions))\n name = nameOrOptions.name;\n else\n name = nameOrOptions;\n return Array.isArray(name) ? name.join(path.sep) : name || '';\n}\n\nexport async function toHaveScreenshot(\n this: ExpectMatcherStateInternal,\n pageOrLocator: Page | Locator,\n nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {},\n optOptions: ToHaveScreenshotOptions = {}\n): Promise<MatcherResult<NameOrSegments | { name?: NameOrSegments }, string>> {\n const testInfo = currentTestInfo();\n if (!testInfo)\n throw new Error(`toHaveScreenshot() must be called during the test`);\n\n if (testInfo._projectInternal.ignoreSnapshots)\n return { pass: !this.isNot, message: () => '', name: 'toHaveScreenshot', expected: nameOrOptions };\n\n expectTypes(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot');\n const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [(pageOrLocator as PageEx), undefined] : [(pageOrLocator as Locator).page() as PageEx, pageOrLocator as Locator];\n const configOptions = testInfo._projectInternal.expect?.toHaveScreenshot || {};\n const helper = new SnapshotHelper(testInfo, 'toHaveScreenshot', locator, undefined, configOptions, nameOrOptions, optOptions);\n if (!helper.expectedPath.toLowerCase().endsWith('.png'))\n throw new Error(`Screenshot name \"${path.basename(helper.expectedPath)}\" must have '.png' extension`);\n expectTypes(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot');\n const style = await loadScreenshotStyles(helper.options.stylePath);\n const timeout = helper.options.timeout ?? this.timeout;\n const expectScreenshotOptions: ExpectScreenshotOptions = {\n locator,\n animations: helper.options.animations ?? 'disabled',\n caret: helper.options.caret ?? 'hide',\n clip: helper.options.clip,\n fullPage: helper.options.fullPage,\n mask: helper.options.mask,\n maskColor: helper.options.maskColor,\n omitBackground: helper.options.omitBackground,\n scale: helper.options.scale ?? 'css',\n style,\n isNot: !!this.isNot,\n timeout,\n comparator: helper.options.comparator,\n maxDiffPixels: helper.options.maxDiffPixels,\n maxDiffPixelRatio: helper.options.maxDiffPixelRatio,\n threshold: helper.options.threshold,\n };\n\n const hasSnapshot = fs.existsSync(helper.expectedPath);\n if (this.isNot) {\n if (!hasSnapshot)\n return helper.handleMissingNegated();\n\n // Having `errorMessage` means we timed out while waiting\n // for screenshots not to match, so screenshots\n // are actually the same in the end.\n expectScreenshotOptions.expected = await fs.promises.readFile(helper.expectedPath);\n const isDifferent = !(await page._expectScreenshot(expectScreenshotOptions)).errorMessage;\n return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();\n }\n\n // Fast path: there's no screenshot and we don't intend to update it.\n if (helper.updateSnapshots === 'none' && !hasSnapshot)\n return helper.createMatcherResult(`A snapshot doesn't exist at ${helper.expectedPath}.`, false);\n\n const receiver = locator ? 'locator' : 'page';\n if (!hasSnapshot) {\n // Regenerate a new screenshot by waiting until two screenshots are the same.\n const { actual, previous, diff, errorMessage, log, timedOut } = await page._expectScreenshot(expectScreenshotOptions);\n // We tried re-generating new snapshot but failed.\n // This can be due to e.g. spinning animation, so we want to show it as a diff.\n if (errorMessage) {\n const header = matcherHint(this, locator, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined);\n return helper.handleDifferent(actual, undefined, previous, diff, header, errorMessage, log, this._stepInfo);\n }\n\n // We successfully generated new screenshot.\n return helper.handleMissing(actual!, this._stepInfo);\n }\n\n // General case:\n // - snapshot exists\n // - regular matcher (i.e. not a `.not`)\n const expected = await fs.promises.readFile(helper.expectedPath);\n expectScreenshotOptions.expected = helper.updateSnapshots === 'all' ? undefined : expected;\n\n const { actual, previous, diff, errorMessage, log, timedOut } = await page._expectScreenshot(expectScreenshotOptions);\n const writeFiles = (actualBuffer: Buffer) => {\n writeFileSync(helper.expectedPath, actualBuffer);\n writeFileSync(helper.actualPath, actualBuffer);\n /* eslint-disable no-console */\n console.log(helper.expectedPath + ' is re-generated, writing actual.');\n return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);\n };\n\n if (!errorMessage) {\n // Screenshot is matching, but is not necessarily the same as the expected.\n if (helper.updateSnapshots === 'all' && actual && compareBuffersOrStrings(actual, expected)) {\n console.log(helper.expectedPath + ' is re-generated, writing actual.');\n return writeFiles(actual);\n }\n return helper.handleMatching();\n }\n\n if (helper.updateSnapshots === 'changed' || helper.updateSnapshots === 'all') {\n if (actual)\n return writeFiles(actual);\n let header = matcherHint(this, undefined, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined);\n header += ' Failed to re-generate expected.\\n';\n return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header, errorMessage, log, this._stepInfo);\n }\n\n const header = matcherHint(this, undefined, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined);\n return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header, errorMessage, log, this._stepInfo);\n}\n\nfunction writeFileSync(aPath: string, content: Buffer | string) {\n fs.mkdirSync(path.dirname(aPath), { recursive: true });\n fs.writeFileSync(aPath, content);\n}\n\nfunction indent(lines: string, tab: string) {\n return lines.replace(/^(?=.+$)/gm, tab);\n}\n\nfunction determineFileExtension(file: string | Buffer): string {\n if (typeof file === 'string')\n return 'txt';\n if (compareMagicBytes(file, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))\n return 'png';\n if (compareMagicBytes(file, [0xff, 0xd8, 0xff]))\n return 'jpg';\n return 'dat';\n}\n\nfunction compareMagicBytes(file: Buffer, magicBytes: number[]): boolean {\n return Buffer.compare(Buffer.from(magicBytes), file.slice(0, magicBytes.length)) === 0;\n}\n\nasync function loadScreenshotStyles(stylePath?: string | string[]): Promise<string | undefined> {\n if (!stylePath)\n return;\n\n const stylePaths = Array.isArray(stylePath) ? stylePath : [stylePath];\n const styles = await Promise.all(stylePaths.map(async stylePath => {\n const text = await fs.promises.readFile(stylePath, 'utf8');\n return text.trim();\n }));\n return styles.join('\\n').trim() || undefined;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_utils", "path", "fs", "result", "actual", "previous", "diff", "errorMessage", "log", "timedOut", "header", "stylePath"]
7
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var toMatchText_exports = {};
20
+ __export(toMatchText_exports, {
21
+ toMatchText: () => toMatchText
22
+ });
23
+ module.exports = __toCommonJS(toMatchText_exports);
24
+ var import_util = require("../util");
25
+ var import_expect = require("./expect");
26
+ var import_matcherHint = require("./matcherHint");
27
+ var import_expectBundle = require("../common/expectBundle");
28
+ async function toMatchText(matcherName, receiver, receiverType, query, expected, options = {}) {
29
+ (0, import_util.expectTypes)(receiver, [receiverType], matcherName);
30
+ const locator = receiverType === "Locator" ? receiver : void 0;
31
+ if (!(typeof expected === "string") && !(expected && typeof expected.test === "function")) {
32
+ const errorMessage2 = `Error: ${(0, import_expectBundle.EXPECTED_COLOR)("expected")} value must be a string or regular expression
33
+ ${this.utils.printWithType("Expected", expected, this.utils.printExpected)}`;
34
+ throw new Error((0, import_matcherHint.formatMatcherMessage)(this, { locator, matcherName, expectation: "expected", errorMessage: errorMessage2 }));
35
+ }
36
+ const timeout = options.timeout ?? this.timeout;
37
+ const { matches: pass, received, log, timedOut, errorMessage } = await query(!!this.isNot, timeout);
38
+ if (pass === !this.isNot) {
39
+ return {
40
+ name: matcherName,
41
+ message: () => "",
42
+ pass,
43
+ expected
44
+ };
45
+ }
46
+ const expectedSuffix = typeof expected === "string" ? options.matchSubstring ? " substring" : "" : " pattern";
47
+ const receivedSuffix = typeof expected === "string" ? options.matchSubstring ? " string" : "" : " string";
48
+ const receivedString = received || "";
49
+ let printedReceived;
50
+ let printedExpected;
51
+ let printedDiff;
52
+ if (pass) {
53
+ if (typeof expected === "string") {
54
+ printedExpected = `Expected${expectedSuffix}: not ${this.utils.printExpected(expected)}`;
55
+ if (!errorMessage) {
56
+ const formattedReceived = (0, import_expect.printReceivedStringContainExpectedSubstring)(receivedString, receivedString.indexOf(expected), expected.length);
57
+ printedReceived = `Received${receivedSuffix}: ${formattedReceived}`;
58
+ }
59
+ } else {
60
+ printedExpected = `Expected${expectedSuffix}: not ${this.utils.printExpected(expected)}`;
61
+ if (!errorMessage) {
62
+ const formattedReceived = (0, import_expect.printReceivedStringContainExpectedResult)(receivedString, typeof expected.exec === "function" ? expected.exec(receivedString) : null);
63
+ printedReceived = `Received${receivedSuffix}: ${formattedReceived}`;
64
+ }
65
+ }
66
+ } else {
67
+ if (errorMessage)
68
+ printedExpected = `Expected${expectedSuffix}: ${this.utils.printExpected(expected)}`;
69
+ else
70
+ printedDiff = this.utils.printDiffOrStringify(expected, receivedString, `Expected${expectedSuffix}`, `Received${receivedSuffix}`, false);
71
+ }
72
+ const message = () => {
73
+ return (0, import_matcherHint.formatMatcherMessage)(this, {
74
+ matcherName,
75
+ expectation: "expected",
76
+ locator,
77
+ timeout,
78
+ timedOut,
79
+ printedExpected,
80
+ printedReceived,
81
+ printedDiff,
82
+ log,
83
+ errorMessage
84
+ });
85
+ };
86
+ return {
87
+ name: matcherName,
88
+ expected,
89
+ message,
90
+ pass,
91
+ actual: received,
92
+ log,
93
+ timeout: timedOut ? timeout : void 0
94
+ };
95
+ }
96
+ // Annotate the CommonJS export names for ESM import in node:
97
+ 0 && (module.exports = {
98
+ toMatchText
99
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/matchers/toMatchText.ts"],
4
+ "sourcesContent": ["/**\n * Copyright Microsoft Corporation. All rights reserved.\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\nimport { colors } from 'playwright-core/lib/utils';\n\nimport { callLogText, expectTypes } from '../util';\nimport {\n printReceivedStringContainExpectedResult,\n printReceivedStringContainExpectedSubstring\n} from './expect';\nimport { kNoElementsFoundError, matcherHint } from './matcherHint';\nimport { EXPECTED_COLOR } from '../common/expectBundle';\nimport { runBrowserBackendOnError } from '../mcp/browser/backend';\n\nimport type { MatcherResult } from './matcherHint';\nimport type { ExpectMatcherState } from '../../types/test';\nimport type { Page, Locator } from 'playwright-core';\n\nexport async function toMatchText(\n this: ExpectMatcherState,\n matcherName: string,\n receiver: Locator | Page,\n receiverType: 'Locator' | 'Page',\n query: (isNot: boolean, timeout: number) => Promise<{ matches: boolean, received?: string, log?: string[], timedOut?: boolean }>,\n expected: string | RegExp,\n options: { timeout?: number, matchSubstring?: boolean, receiverLabel?: string } = {},\n): Promise<MatcherResult<string | RegExp, string>> {\n expectTypes(receiver, [receiverType], matcherName);\n\n const matcherOptions = {\n isNot: this.isNot,\n promise: this.promise,\n };\n\n if (\n !(typeof expected === 'string') &&\n !(expected && typeof expected.test === 'function')\n ) {\n // Same format as jest's matcherErrorMessage\n throw new Error([\n matcherHint(this, receiverType === 'Locator' ? receiver as Locator : undefined, matcherName, options.receiverLabel ?? receiver, expected, matcherOptions, undefined, undefined, true),\n `${colors.bold('Matcher error')}: ${EXPECTED_COLOR('expected',)} value must be a string or regular expression`,\n this.utils.printWithType('Expected', expected, this.utils.printExpected)\n ].join('\\n\\n'));\n }\n\n const timeout = options.timeout ?? this.timeout;\n\n const { matches: pass, received, log, timedOut } = await query(!!this.isNot, timeout).catch(async error => {\n // FIXME: query should not throw, but it does for strict mode violations for example.\n if (receiverType === 'Locator')\n await runBrowserBackendOnError((receiver as Locator).page(), () => error.message);\n throw error;\n });\n\n if (pass === !this.isNot) {\n return {\n name: matcherName,\n message: () => '',\n pass,\n expected\n };\n }\n\n const stringSubstring = options.matchSubstring ? 'substring' : 'string';\n const receivedString = received || '';\n const notFound = received === kNoElementsFoundError;\n\n let printedReceived: string | undefined;\n let printedExpected: string | undefined;\n let printedDiff: string | undefined;\n if (pass) {\n if (typeof expected === 'string') {\n if (notFound) {\n printedExpected = `Expected ${stringSubstring}: not ${this.utils.printExpected(expected)}`;\n printedReceived = `Received: ${received}`;\n } else {\n printedExpected = `Expected ${stringSubstring}: not ${this.utils.printExpected(expected)}`;\n const formattedReceived = printReceivedStringContainExpectedSubstring(receivedString, receivedString.indexOf(expected), expected.length);\n printedReceived = `Received string: ${formattedReceived}`;\n }\n } else {\n if (notFound) {\n printedExpected = `Expected pattern: not ${this.utils.printExpected(expected)}`;\n printedReceived = `Received: ${received}`;\n } else {\n printedExpected = `Expected pattern: not ${this.utils.printExpected(expected)}`;\n const formattedReceived = printReceivedStringContainExpectedResult(receivedString, typeof expected.exec === 'function' ? expected.exec(receivedString) : null);\n printedReceived = `Received string: ${formattedReceived}`;\n }\n }\n } else {\n const labelExpected = `Expected ${typeof expected === 'string' ? stringSubstring : 'pattern'}`;\n if (notFound) {\n printedExpected = `${labelExpected}: ${this.utils.printExpected(expected)}`;\n printedReceived = `Received: ${received}`;\n } else {\n printedDiff = this.utils.printDiffOrStringify(expected, receivedString, labelExpected, 'Received string', false);\n }\n }\n\n const message = () => {\n const resultDetails = printedDiff ? printedDiff : printedExpected + '\\n' + printedReceived;\n const hints = matcherHint(this, receiverType === 'Locator' ? receiver as Locator : undefined, matcherName, options.receiverLabel ?? 'locator', undefined, matcherOptions, timedOut ? timeout : undefined, resultDetails, true);\n return hints + callLogText(log);\n };\n\n if (receiverType === 'Locator')\n await runBrowserBackendOnError((receiver as Locator).page(), message);\n\n return {\n name: matcherName,\n expected,\n message,\n pass,\n actual: received,\n log,\n timeout: timedOut ? timeout : undefined,\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var actions_d_exports = {};
16
+ module.exports = __toCommonJS(actions_d_exports);