@rehearsal-labs/retest 0.1.0 → 0.2.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 (315) hide show
  1. package/README.md +6 -0
  2. package/dist/agent/session.d.ts +21 -2
  3. package/dist/agent/session.d.ts.map +1 -1
  4. package/dist/agent/session.js +73 -11
  5. package/dist/api/app-page.d.ts +39 -3
  6. package/dist/api/app-page.d.ts.map +1 -1
  7. package/dist/api/app-page.js +108 -0
  8. package/dist/api/dialog.d.ts +38 -0
  9. package/dist/api/dialog.d.ts.map +1 -0
  10. package/dist/api/dialog.js +78 -0
  11. package/dist/api/drag.d.ts +32 -0
  12. package/dist/api/drag.d.ts.map +1 -0
  13. package/dist/api/drag.js +26 -0
  14. package/dist/api/files.d.ts +49 -0
  15. package/dist/api/files.d.ts.map +1 -0
  16. package/dist/api/files.js +168 -0
  17. package/dist/api/inbox.d.ts +97 -0
  18. package/dist/api/inbox.d.ts.map +1 -0
  19. package/dist/api/inbox.js +191 -0
  20. package/dist/api/locator-recipes.d.ts +1 -1
  21. package/dist/api/locator-recipes.d.ts.map +1 -1
  22. package/dist/api/locator-recipes.js +4 -3
  23. package/dist/api/page.d.ts +214 -4
  24. package/dist/api/page.d.ts.map +1 -1
  25. package/dist/api/route.d.ts +21 -0
  26. package/dist/api/route.d.ts.map +1 -0
  27. package/dist/api/route.js +99 -0
  28. package/dist/api/test-run.d.ts +127 -1
  29. package/dist/api/test-run.d.ts.map +1 -1
  30. package/dist/api/test-run.js +293 -0
  31. package/dist/assertions/expect.d.ts +91 -2
  32. package/dist/assertions/expect.d.ts.map +1 -1
  33. package/dist/assertions/expect.js +98 -0
  34. package/dist/assertions/look-until.d.ts +5 -0
  35. package/dist/assertions/look-until.d.ts.map +1 -1
  36. package/dist/assertions/look-until.js +1 -1
  37. package/dist/assertions/poll-layout.d.ts +24 -0
  38. package/dist/assertions/poll-layout.d.ts.map +1 -0
  39. package/dist/assertions/poll-layout.js +63 -0
  40. package/dist/assertions/screenshot.d.ts +36 -0
  41. package/dist/assertions/screenshot.d.ts.map +1 -0
  42. package/dist/assertions/screenshot.js +125 -0
  43. package/dist/browser/accessibility.d.ts.map +1 -1
  44. package/dist/browser/accessibility.js +22 -1
  45. package/dist/browser/actionability.d.ts +9 -6
  46. package/dist/browser/actionability.d.ts.map +1 -1
  47. package/dist/browser/actionability.js +17 -8
  48. package/dist/browser/browser.d.ts.map +1 -1
  49. package/dist/browser/browser.js +15 -4
  50. package/dist/browser/cdp/connection.d.ts +6 -0
  51. package/dist/browser/cdp/connection.d.ts.map +1 -1
  52. package/dist/browser/cdp/connection.js +8 -0
  53. package/dist/browser/checked-state.d.ts +2 -2
  54. package/dist/browser/checked-state.d.ts.map +1 -1
  55. package/dist/browser/chromium-process.d.ts +9 -2
  56. package/dist/browser/chromium-process.d.ts.map +1 -1
  57. package/dist/browser/chromium-process.js +17 -4
  58. package/dist/browser/contract.d.ts +119 -5
  59. package/dist/browser/contract.d.ts.map +1 -1
  60. package/dist/browser/dialogs.d.ts +157 -0
  61. package/dist/browser/dialogs.d.ts.map +1 -0
  62. package/dist/browser/dialogs.js +271 -0
  63. package/dist/browser/downloads.d.ts +71 -0
  64. package/dist/browser/downloads.d.ts.map +1 -0
  65. package/dist/browser/downloads.js +253 -0
  66. package/dist/browser/drag.d.ts +50 -0
  67. package/dist/browser/drag.d.ts.map +1 -0
  68. package/dist/browser/drag.js +584 -0
  69. package/dist/browser/electron-window-watch.d.ts +32 -0
  70. package/dist/browser/electron-window-watch.d.ts.map +1 -0
  71. package/dist/browser/electron-window-watch.js +93 -0
  72. package/dist/browser/electron.d.ts +13 -2
  73. package/dist/browser/electron.d.ts.map +1 -1
  74. package/dist/browser/electron.js +53 -8
  75. package/dist/browser/element-queries.d.ts +51 -15
  76. package/dist/browser/element-queries.d.ts.map +1 -1
  77. package/dist/browser/element-queries.js +92 -13
  78. package/dist/browser/emulation.d.ts.map +1 -1
  79. package/dist/browser/emulation.js +7 -5
  80. package/dist/browser/file-chooser.d.ts +75 -0
  81. package/dist/browser/file-chooser.d.ts.map +1 -0
  82. package/dist/browser/file-chooser.js +449 -0
  83. package/dist/browser/firefox/input.d.ts +5 -0
  84. package/dist/browser/firefox/input.d.ts.map +1 -1
  85. package/dist/browser/firefox/input.js +6 -3
  86. package/dist/browser/firefox/page.d.ts +11 -1
  87. package/dist/browser/firefox/page.d.ts.map +1 -1
  88. package/dist/browser/firefox/page.js +66 -7
  89. package/dist/browser/frames.d.ts +205 -0
  90. package/dist/browser/frames.d.ts.map +1 -0
  91. package/dist/browser/frames.js +657 -0
  92. package/dist/browser/input-guard.d.ts +18 -5
  93. package/dist/browser/input-guard.d.ts.map +1 -1
  94. package/dist/browser/input-guard.js +65 -6
  95. package/dist/browser/isolated-world.d.ts +8 -1
  96. package/dist/browser/isolated-world.d.ts.map +1 -1
  97. package/dist/browser/layout.d.ts +120 -0
  98. package/dist/browser/layout.d.ts.map +1 -0
  99. package/dist/browser/layout.js +213 -0
  100. package/dist/browser/locate.d.ts +10 -0
  101. package/dist/browser/locate.d.ts.map +1 -1
  102. package/dist/browser/locate.js +24 -6
  103. package/dist/browser/origin-document.d.ts +4 -0
  104. package/dist/browser/origin-document.d.ts.map +1 -1
  105. package/dist/browser/origin-document.js +9 -1
  106. package/dist/browser/page-scripts.d.ts +48 -0
  107. package/dist/browser/page-scripts.d.ts.map +1 -1
  108. package/dist/browser/page-scripts.js +515 -39
  109. package/dist/browser/page.d.ts +60 -1
  110. package/dist/browser/page.d.ts.map +1 -1
  111. package/dist/browser/page.js +275 -32
  112. package/dist/browser/popups.d.ts +166 -0
  113. package/dist/browser/popups.d.ts.map +1 -0
  114. package/dist/browser/popups.js +317 -0
  115. package/dist/browser/routes.d.ts +97 -0
  116. package/dist/browser/routes.d.ts.map +1 -0
  117. package/dist/browser/routes.js +226 -0
  118. package/dist/browser/screenshot.d.ts +44 -0
  119. package/dist/browser/screenshot.d.ts.map +1 -0
  120. package/dist/browser/screenshot.js +51 -0
  121. package/dist/browser/shadow-candidates.d.ts +20 -0
  122. package/dist/browser/shadow-candidates.d.ts.map +1 -0
  123. package/dist/browser/shadow-candidates.js +77 -0
  124. package/dist/browser/webkit/page.d.ts +11 -1
  125. package/dist/browser/webkit/page.d.ts.map +1 -1
  126. package/dist/browser/webkit/page.js +53 -2
  127. package/dist/browser/webkit/relay.d.ts.map +1 -1
  128. package/dist/browser/webkit/relay.js +5 -1
  129. package/dist/cli/commands/run.d.ts.map +1 -1
  130. package/dist/cli/commands/run.js +2 -0
  131. package/dist/cli/inspect/test-timeline.d.ts.map +1 -1
  132. package/dist/cli/inspect/test-timeline.js +38 -4
  133. package/dist/config/loaded.d.ts +12 -2
  134. package/dist/config/loaded.d.ts.map +1 -1
  135. package/dist/config/read-inboxes.d.ts +10 -0
  136. package/dist/config/read-inboxes.d.ts.map +1 -0
  137. package/dist/config/read-inboxes.js +78 -0
  138. package/dist/config/register.d.ts +5 -1
  139. package/dist/config/register.d.ts.map +1 -1
  140. package/dist/config/types.d.ts +36 -0
  141. package/dist/config/types.d.ts.map +1 -1
  142. package/dist/config/validate.d.ts.map +1 -1
  143. package/dist/config/validate.js +4 -1
  144. package/dist/diagnostics/artifact.d.ts.map +1 -1
  145. package/dist/diagnostics/artifact.js +10 -2
  146. package/dist/diagnostics/attempt.d.ts +5 -0
  147. package/dist/diagnostics/attempt.d.ts.map +1 -1
  148. package/dist/diagnostics/attempt.js +10 -0
  149. package/dist/diagnostics/chromium-collector.d.ts +9 -1
  150. package/dist/diagnostics/chromium-collector.d.ts.map +1 -1
  151. package/dist/diagnostics/chromium-collector.js +19 -4
  152. package/dist/diagnostics/observations.d.ts +8 -0
  153. package/dist/diagnostics/observations.d.ts.map +1 -1
  154. package/dist/diagnostics/session-capture.d.ts.map +1 -1
  155. package/dist/diagnostics/session-capture.js +8 -0
  156. package/dist/index.d.ts +9 -4
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +1 -0
  159. package/dist/media/policy.d.ts +2 -2
  160. package/dist/media/policy.d.ts.map +1 -1
  161. package/dist/media/policy.js +1 -1
  162. package/dist/playwright/not-yet.d.ts.map +1 -1
  163. package/dist/playwright/not-yet.js +5 -2
  164. package/dist/protocol/commands.d.ts +122 -13
  165. package/dist/protocol/commands.d.ts.map +1 -1
  166. package/dist/protocol/commands.js +85 -6
  167. package/dist/protocol/diagnostics.d.ts +10 -0
  168. package/dist/protocol/diagnostics.d.ts.map +1 -1
  169. package/dist/protocol/diagnostics.js +5 -0
  170. package/dist/protocol/dialogs.d.ts +104 -0
  171. package/dist/protocol/dialogs.d.ts.map +1 -0
  172. package/dist/protocol/dialogs.js +101 -0
  173. package/dist/protocol/drag.d.ts +45 -0
  174. package/dist/protocol/drag.d.ts.map +1 -0
  175. package/dist/protocol/drag.js +31 -0
  176. package/dist/protocol/events.d.ts +153 -1
  177. package/dist/protocol/events.d.ts.map +1 -1
  178. package/dist/protocol/events.js +97 -0
  179. package/dist/protocol/files.d.ts +83 -0
  180. package/dist/protocol/files.d.ts.map +1 -0
  181. package/dist/protocol/files.js +68 -0
  182. package/dist/protocol/inbox.d.ts +87 -0
  183. package/dist/protocol/inbox.d.ts.map +1 -0
  184. package/dist/protocol/inbox.js +51 -0
  185. package/dist/protocol/index.d.ts +4 -1
  186. package/dist/protocol/index.d.ts.map +1 -1
  187. package/dist/protocol/layout-checks.d.ts +83 -0
  188. package/dist/protocol/layout-checks.d.ts.map +1 -0
  189. package/dist/protocol/layout-checks.js +214 -0
  190. package/dist/protocol/layout.d.ts +135 -0
  191. package/dist/protocol/layout.d.ts.map +1 -0
  192. package/dist/protocol/layout.js +124 -0
  193. package/dist/protocol/locator-checks.d.ts +19 -2
  194. package/dist/protocol/locator-checks.d.ts.map +1 -1
  195. package/dist/protocol/locator-checks.js +23 -1
  196. package/dist/protocol/locator.d.ts +55 -7
  197. package/dist/protocol/locator.d.ts.map +1 -1
  198. package/dist/protocol/locator.js +99 -18
  199. package/dist/protocol/messages.d.ts +95 -0
  200. package/dist/protocol/messages.d.ts.map +1 -1
  201. package/dist/protocol/messages.js +63 -0
  202. package/dist/protocol/popups.d.ts +43 -0
  203. package/dist/protocol/popups.d.ts.map +1 -0
  204. package/dist/protocol/popups.js +27 -0
  205. package/dist/protocol/result.d.ts +5 -0
  206. package/dist/protocol/result.d.ts.map +1 -1
  207. package/dist/protocol/result.js +1 -0
  208. package/dist/protocol/routes.d.ts +136 -0
  209. package/dist/protocol/routes.d.ts.map +1 -0
  210. package/dist/protocol/routes.js +191 -0
  211. package/dist/protocol/screenshot.d.ts +219 -0
  212. package/dist/protocol/screenshot.d.ts.map +1 -0
  213. package/dist/protocol/screenshot.js +335 -0
  214. package/dist/reporters/actions.d.ts +6 -4
  215. package/dist/reporters/actions.d.ts.map +1 -1
  216. package/dist/reporters/actions.js +24 -6
  217. package/dist/reporters/agent.d.ts.map +1 -1
  218. package/dist/reporters/agent.js +4 -2
  219. package/dist/reporters/dialogs-popups.d.ts +22 -0
  220. package/dist/reporters/dialogs-popups.d.ts.map +1 -0
  221. package/dist/reporters/dialogs-popups.js +30 -0
  222. package/dist/reporters/failure-card.d.ts +10 -1
  223. package/dist/reporters/failure-card.d.ts.map +1 -1
  224. package/dist/reporters/failure-card.js +5 -1
  225. package/dist/reporters/format.d.ts +33 -0
  226. package/dist/reporters/format.d.ts.map +1 -1
  227. package/dist/reporters/format.js +42 -0
  228. package/dist/reporters/html/comparison-view.d.ts +12 -0
  229. package/dist/reporters/html/comparison-view.d.ts.map +1 -0
  230. package/dist/reporters/html/comparison-view.js +21 -0
  231. package/dist/reporters/html/diagnostics-view.js +8 -0
  232. package/dist/reporters/html/downloads-view.d.ts +10 -0
  233. package/dist/reporters/html/downloads-view.d.ts.map +1 -0
  234. package/dist/reporters/html/downloads-view.js +29 -0
  235. package/dist/reporters/html/style.d.ts.map +1 -1
  236. package/dist/reporters/html/style.js +3 -0
  237. package/dist/reporters/html/summary-view.d.ts.map +1 -1
  238. package/dist/reporters/html/summary-view.js +3 -2
  239. package/dist/reporters/html/test-view.d.ts.map +1 -1
  240. package/dist/reporters/html/test-view.js +6 -3
  241. package/dist/reporters/html/timeline-view.d.ts.map +1 -1
  242. package/dist/reporters/html/timeline-view.js +26 -2
  243. package/dist/reporters/human-card.js +10 -0
  244. package/dist/reporters/human.d.ts.map +1 -1
  245. package/dist/reporters/human.js +3 -1
  246. package/dist/reporters/routes.d.ts +9 -0
  247. package/dist/reporters/routes.d.ts.map +1 -0
  248. package/dist/reporters/routes.js +17 -0
  249. package/dist/runner/child-program.js +10 -0
  250. package/dist/runner/contract.d.ts +5 -0
  251. package/dist/runner/contract.d.ts.map +1 -1
  252. package/dist/runner/dialogs.d.ts +71 -0
  253. package/dist/runner/dialogs.d.ts.map +1 -0
  254. package/dist/runner/dialogs.js +189 -0
  255. package/dist/runner/files.d.ts +79 -0
  256. package/dist/runner/files.d.ts.map +1 -0
  257. package/dist/runner/files.js +312 -0
  258. package/dist/runner/inbox-links.d.ts +19 -0
  259. package/dist/runner/inbox-links.d.ts.map +1 -0
  260. package/dist/runner/inbox-links.js +63 -0
  261. package/dist/runner/inbox.d.ts +87 -0
  262. package/dist/runner/inbox.d.ts.map +1 -0
  263. package/dist/runner/inbox.js +340 -0
  264. package/dist/runner/index.d.ts +2 -1
  265. package/dist/runner/index.d.ts.map +1 -1
  266. package/dist/runner/native-pool.d.ts.map +1 -1
  267. package/dist/runner/native-pool.js +2 -0
  268. package/dist/runner/observations.d.ts +15 -2
  269. package/dist/runner/observations.d.ts.map +1 -1
  270. package/dist/runner/observations.js +109 -6
  271. package/dist/runner/popups.d.ts +82 -0
  272. package/dist/runner/popups.d.ts.map +1 -0
  273. package/dist/runner/popups.js +236 -0
  274. package/dist/runner/redactor.d.ts.map +1 -1
  275. package/dist/runner/redactor.js +10 -6
  276. package/dist/runner/routes.d.ts +23 -0
  277. package/dist/runner/routes.d.ts.map +1 -0
  278. package/dist/runner/routes.js +128 -0
  279. package/dist/runner/run-media.d.ts +6 -0
  280. package/dist/runner/run-media.d.ts.map +1 -1
  281. package/dist/runner/run-media.js +21 -0
  282. package/dist/runner/run-session.d.ts.map +1 -1
  283. package/dist/runner/run-session.js +93 -8
  284. package/dist/runner/running-test.d.ts +35 -0
  285. package/dist/runner/running-test.d.ts.map +1 -1
  286. package/dist/runner/running-test.js +333 -22
  287. package/dist/runner/screenshot-baselines.d.ts +159 -0
  288. package/dist/runner/screenshot-baselines.d.ts.map +1 -0
  289. package/dist/runner/screenshot-baselines.js +357 -0
  290. package/dist/runner/secrets.d.ts +13 -0
  291. package/dist/runner/secrets.d.ts.map +1 -1
  292. package/dist/runner/secrets.js +6 -4
  293. package/dist/runner/test-pages.d.ts +3 -1
  294. package/dist/runner/test-pages.d.ts.map +1 -1
  295. package/dist/runner/test-pages.js +4 -3
  296. package/dist/schemas/event-v1.schema.json +118873 -6311
  297. package/dist/schemas/result-v1.schema.json +4 -0
  298. package/dist/shared/image-diff.d.ts +36 -0
  299. package/dist/shared/image-diff.d.ts.map +1 -0
  300. package/dist/shared/image-diff.js +242 -0
  301. package/dist/shared/metadata-process.d.ts +4 -1
  302. package/dist/shared/metadata-process.d.ts.map +1 -1
  303. package/dist/shared/metadata-process.js +51 -9
  304. package/dist/shared/png.d.ts +35 -0
  305. package/dist/shared/png.d.ts.map +1 -0
  306. package/dist/shared/png.js +251 -0
  307. package/dist/shared/process-ownership.d.ts +15 -4
  308. package/dist/shared/process-ownership.d.ts.map +1 -1
  309. package/dist/shared/process-ownership.js +123 -34
  310. package/dist/store/artifacts.d.ts +23 -2
  311. package/dist/store/artifacts.d.ts.map +1 -1
  312. package/dist/store/artifacts.js +40 -1
  313. package/dist/store/rebuild-result.d.ts.map +1 -1
  314. package/dist/store/rebuild-result.js +6 -0
  315. package/package.json +1 -1
@@ -0,0 +1,128 @@
1
+ import { originAndPath } from "../browser/page-url.js";
2
+ import { smallestBudget } from "../protocol/deadline.js";
3
+ import { formatSessionId } from "../protocol/evidence.js";
4
+ import { errorMessage, failure, withLocation } from "../protocol/failures.js";
5
+ import { describeRoutePattern, routeRequestProblem, summarizeRouteAnswer } from "../protocol/routes.js";
6
+ import { shorten } from "../protocol/text.js";
7
+ import { NativePageAdapter } from "./native-pool.js";
8
+ /**
9
+ * The parent's side of a test's routes. A change goes to the app's page, which answers once Chrome holds what the
10
+ * change asks for, so the test's next command meets it; the answer goes back to the test process, and every change,
11
+ * made or refused, is written down as a `route` event. The routes end with the page: a test's own pages close as it
12
+ * ends, and Retest removes nothing before then. A refused change is the parent's own failure, as a refused wait for a
13
+ * file is, which fails the test whatever its process claims; so is a request whose route's answer Chrome refused, which
14
+ * the page failed in its place. A pattern is redacted whole before it is cut, so no part of a secret is left at the cut.
15
+ */
16
+ export class TestRoutes {
17
+ #host;
18
+ #running = new Set();
19
+ constructor(host) {
20
+ this.#host = host;
21
+ }
22
+ /** Hears each request of an app's page whose route's answer Chrome refused. Returns a function that stops listening. */
23
+ watch(app, page) {
24
+ return page.onRouteRefused?.((refusal) => this.#refused(app, refusal)) ?? (() => undefined);
25
+ }
26
+ receive(message) {
27
+ if (message.testId !== this.#host.testId || message.attemptId !== this.#host.attemptId)
28
+ return this.#host.violation('sent a route for an inactive test attempt');
29
+ if (this.#running.has(message.id))
30
+ return this.#host.violation(`sent route ${message.id} again while the first was still running`);
31
+ const page = this.#host.page(message.app);
32
+ if (page === undefined)
33
+ return this.#host.violation(`sent a route for the app ${JSON.stringify(message.app)}, which this test does not use`);
34
+ this.#running.add(message.id);
35
+ void this.#change(message, page)
36
+ .catch((error) => ({ ok: false, failure: failure('setup_failed', `Retest could not change the routes: ${errorMessage(error)}`) }))
37
+ .then((reply) => {
38
+ this.#running.delete(message.id);
39
+ this.#settle(message, reply);
40
+ });
41
+ }
42
+ async #change({ app, request, timeoutMs, location }, page) {
43
+ const refusal = this.#host.refusal();
44
+ if (refusal !== undefined)
45
+ return { ok: false, failure: withLocation(refusal, location) };
46
+ const problem = routeRequestProblem(request);
47
+ if (problem !== undefined)
48
+ return { ok: false, failure: problem };
49
+ const remaining = this.#host.remainingMs();
50
+ const budget = Math.max(1, remaining === undefined ? timeoutMs : smallestBudget(timeoutMs, remaining));
51
+ if (request.kind === 'add') {
52
+ if (page.addRoute === undefined)
53
+ return { ok: false, failure: noRoutes(app, page) };
54
+ const added = await page.addRoute(request.pattern, request.answer, budget);
55
+ return added.ok ? { ok: true, kind: 'added' } : added;
56
+ }
57
+ if (page.removeRoute === undefined)
58
+ return { ok: false, failure: noRoutes(app, page) };
59
+ const removed = await page.removeRoute(request.pattern, budget);
60
+ if (!removed.ok)
61
+ return removed;
62
+ if (removed.removed === 0)
63
+ return { ok: false, failure: { ...failure('usage', `page.unroute(${describeRoutePattern(request.pattern)}) matches no route of this page. Pass the pattern the route was added with, as it was written.`), details: { removed: 0 } } };
64
+ return { ok: true, kind: 'removed', removed: removed.removed };
65
+ }
66
+ // A change is written down whether it was made or refused, then answered, its failure redacted, if anyone still waits.
67
+ #settle(message, reply) {
68
+ const placed = reply.ok ? reply : { ok: false, failure: withLocation(reply.failure, message.location) };
69
+ this.#emit(message, placed);
70
+ if (!this.#host.answerable())
71
+ return;
72
+ if (!placed.ok)
73
+ this.#host.observe(placed.failure);
74
+ this.#host.send({ type: 'route-result', id: message.id, answer: placed.ok ? placed : { ok: false, failure: redacted(placed.failure, this.#host.redact) } });
75
+ }
76
+ #emit({ app, request, stepId, location }, reply) {
77
+ const { testId, attemptId } = this.#host;
78
+ this.#host.emit({
79
+ type: 'route',
80
+ testId,
81
+ attemptId,
82
+ ...(stepId === undefined ? {} : { stepId }),
83
+ session: app,
84
+ sessionId: formatSessionId(attemptId, app),
85
+ operation: request.kind,
86
+ pattern: this.#written(describeRoutePattern(request.pattern)),
87
+ ...answerOf(request),
88
+ ...(reply.ok && reply.kind === 'removed' ? { removed: reply.removed } : {}),
89
+ ...(location === undefined ? {} : { location }),
90
+ ...(reply.ok ? {} : { failure: redacted(reply.failure, this.#host.redact) }),
91
+ });
92
+ }
93
+ // A request the route matched, whose answer Chrome would not take: the page failed it instead, so the test's own answer
94
+ // never reached the page, and the test fails for it.
95
+ #refused(app, { pattern, url, reason }) {
96
+ const { testId, attemptId } = this.#host;
97
+ const parsed = URL.parse(url);
98
+ const address = this.#host.redact(parsed === null ? url : originAndPath(parsed));
99
+ const written = this.#written(pattern);
100
+ const problem = {
101
+ ...failure('setup_failed', this.#host.redact(`Chrome refused the answer page.route(${pattern}) gave a request for ${address}: ${reason}. Retest failed the request in its place, so the page saw a network error.`)),
102
+ details: { url: address },
103
+ };
104
+ this.#host.emit({ type: 'route', testId, attemptId, ...stepOf(this.#host.stepId()), session: app, sessionId: formatSessionId(attemptId, app), operation: 'answer', pattern: written, url: address, failure: problem });
105
+ if (this.#host.answerable())
106
+ this.#host.observe(problem);
107
+ }
108
+ // A pattern as an event records it: redacted whole, then cut as messages cut text.
109
+ #written(pattern) {
110
+ return shorten(this.#host.redact(pattern));
111
+ }
112
+ }
113
+ function stepOf(stepId) {
114
+ return stepId === undefined ? {} : { stepId };
115
+ }
116
+ function answerOf(request) {
117
+ return request.kind === 'add' ? { answer: summarizeRouteAnswer(request.answer) } : {};
118
+ }
119
+ function redacted(problem, redact) {
120
+ return { ...problem, message: redact(problem.message) };
121
+ }
122
+ // A native app has no network a route can answer for; any other page without routes belongs to an engine that refuses
123
+ // them, and says so itself.
124
+ function noRoutes(app, page) {
125
+ if (page instanceof NativePageAdapter)
126
+ return failure('unsupported', `${app} is a native app, and page.route() answers the requests of a web page.`);
127
+ return failure('unsupported', `The page of ${app} cannot answer its requests with routes.`);
128
+ }
@@ -129,6 +129,12 @@ export declare class AttemptRecorder implements AttemptRecordings {
129
129
  * to start, within the start budget, so no action comes before its recording began.
130
130
  */
131
131
  start(pages: readonly AppPage[]): Promise<void>;
132
+ /**
133
+ * Notes a session the run cannot record, such as a page a test's page opened: when the app it comes from, `root`, is
134
+ * one the run records, it gets a recording that never began, with `reason`, so the attempt's evidence never counts it
135
+ * as seen. Before `finish` only.
136
+ */
137
+ notRecorded(page: AppPage, root: string, reason: string): void;
132
138
  /** The session's recording for AI checks of its frames. */
133
139
  recording(app: string): AttemptRecording | undefined;
134
140
  step(name: string): StepSpan | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"run-media.d.ts","sourceRoot":"","sources":["../../src/runner/run-media.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-media.d.ts","sourceRoot":"","sources":["../../src/runner/run-media.ts"],"names":[],"mappings":"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"}
@@ -320,6 +320,8 @@ export class AttemptRecorder {
320
320
  #steps = new Map();
321
321
  #latest = new Map();
322
322
  #finishing;
323
+ #pageCount = 0;
324
+ #unrecorded = 0;
323
325
  constructor(options) {
324
326
  this.#options = options;
325
327
  }
@@ -328,8 +330,27 @@ export class AttemptRecorder {
328
330
  * to start, within the start budget, so no action comes before its recording began.
329
331
  */
330
332
  async start(pages) {
333
+ this.#pageCount = pages.length;
331
334
  await Promise.all(pages.map((page, index) => this.#startOne(page, index)));
332
335
  }
336
+ /**
337
+ * Notes a session the run cannot record, such as a page a test's page opened: when the app it comes from, `root`, is
338
+ * one the run records, it gets a recording that never began, with `reason`, so the attempt's evidence never counts it
339
+ * as seen. Before `finish` only.
340
+ */
341
+ notRecorded(page, root, reason) {
342
+ const { app, session } = page;
343
+ const { testId, attemptId, records, sequences } = this.#options;
344
+ if (this.#finishing !== undefined || !records(root) || this.#order.includes(app))
345
+ return;
346
+ const identity = { testId, attemptId, app, sessionId: session.sessionId };
347
+ const sequence = sequences.next(identity, 'recording');
348
+ // Numbered after every page the attempt began with, as each of those is by its place.
349
+ this.#unrecorded += 1;
350
+ const recordingId = `${attemptId}-${this.#pageCount + this.#unrecorded}-${sequence}`;
351
+ this.#order.push(app);
352
+ this.#unstarted.set(app, unstartedRecording({ identity, sequence, recordingId }, 'no_frame_source', reason));
353
+ }
333
354
  /** The session's recording for AI checks of its frames. */
334
355
  recording(app) {
335
356
  const withheld = this.#options.withheldFrom(app);
@@ -1 +1 @@
1
- {"version":3,"file":"run-session.d.ts","sourceRoot":"","sources":["../../src/runner/run-session.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAkC,SAAS,EAAc,MAAM,uBAAuB,CAAA;AAGlG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAGxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAiC,MAAM,mBAAmB,CAAA;AACrH,OAAO,KAAK,EAAe,UAAU,EAAyB,MAAM,eAAe,CAAA;AAWnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AA+BhD,OAAO,KAAK,EAAE,cAAc,EAAqB,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAiCtF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,CAAA;IACnB,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC9B,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,cAAc,CAAA;IAC9B,KAAK,EAAE,QAAQ,CAAA;IACf,oFAAoF;IACpF,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,wIAAwI;IACxI,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;KAAE,CAAA;IACnF,sHAAsH;IACtH,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,gBAAgB,EAAE,aAAa,CAAA;CACvD,CAAA;AA6FD;;;;;;GAMG;AACH,qBAAa,UAAU;;gBA6FT,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,iBAAiB;IAiHjI,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;CAq4ChC"}
1
+ {"version":3,"file":"run-session.d.ts","sourceRoot":"","sources":["../../src/runner/run-session.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAkC,SAAS,EAAc,MAAM,uBAAuB,CAAA;AAGlG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAGxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAiC,MAAM,mBAAmB,CAAA;AACrH,OAAO,KAAK,EAAe,UAAU,EAAyB,MAAM,eAAe,CAAA;AAWnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAgChD,OAAO,KAAK,EAAE,cAAc,EAAqB,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAqCtF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,CAAA;IACnB,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC9B,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,cAAc,CAAA;IAC9B,KAAK,EAAE,QAAQ,CAAA;IACf,oFAAoF;IACpF,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,wIAAwI;IACxI,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;KAAE,CAAA;IACnF,sHAAsH;IACtH,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,gBAAgB,EAAE,aAAa,CAAA;CACvD,CAAA;AAgGD;;;;;;GAMG;AACH,qBAAa,UAAU;;gBAmGT,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,iBAAiB;IAkHjI,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;CA88ChC"}
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { existsSync } from 'node:fs';
3
- import { resolve } from 'node:path';
3
+ import { join, resolve } from 'node:path';
4
4
  import { isDeepStrictEqual } from 'node:util';
5
5
  import { AttemptDiagnostics } from "../diagnostics/attempt.js";
6
6
  import { defaultRecordingSettings, recordsApp, resolveRecording } from "../config/read-recording.js";
@@ -19,7 +19,7 @@ import { appLogFile, browserLogFile, childLogFile, stateFile, targetBrowserLogFi
19
19
  import { originOf, withoutCredentials } from "../protocol/url.js";
20
20
  import { variantKey } from "../protocol/variant.js";
21
21
  import { relativePosixPath } from "../shared/posix-path.js";
22
- import { applyRetention, recordingArtifactReferences, ArtifactSequences, inventoryArtifacts, planRetention } from "../store/artifacts.js";
22
+ import { applyRetention, recordingArtifactReferences, ArtifactSequences, downloadsFolder, inventoryArtifacts, planRetention } from "../store/artifacts.js";
23
23
  import { retestVersion } from "../version.js";
24
24
  import { AppServers } from "./app-servers.js";
25
25
  import { newAttemptId } from "./attempt-id.js";
@@ -40,12 +40,15 @@ import { interruptionOf, runOutcome, stopReasonOf, testStatus } from "./outcome.
40
40
  import { collectedTest, planTests } from "./plan.js";
41
41
  import { declaredBackend, preparationsScopeProblem, preparationsShapeProblem, prepareAttempt, readPreparations, testPreparations } from "./preparation.js";
42
42
  import { endedBeforeTest, fileProcessFailure, laterTestsReason, reportedErrors } from "./process-failures.js";
43
+ import { AttemptFiles } from "./files.js";
44
+ import { AttemptInbox } from "./inbox.js";
43
45
  import { Redactor } from "./redactor.js";
44
46
  import { runConfig } from "./run-config.js";
45
47
  import { runHostChecks } from "./run-host-checks.js";
46
48
  import { AttemptRecorder, discoverMediaLocation, RunMedia, startMediaProcess } from "./run-media.js";
47
49
  import { abortGraceMs, RunningTest } from "./running-test.js";
48
50
  import { attemptKey, phasesOf, scheduleRun } from "./schedule.js";
51
+ import { AttemptScreenshots, pictureApp, ScreenshotBaselines } from "./screenshot-baselines.js";
49
52
  import { SecretFiller, secretValuesProblem, secretVariables } from "./secrets.js";
50
53
  import { emptySelectionFailure, selectionProblem, testSelected, variantSelected } from "./selection.js";
51
54
  import { acquireResources, hostResources, leasePart, partFree, resourceNeeds, sessionResource } from "./resources.js";
@@ -55,9 +58,12 @@ import { skippedCheckFailures } from "./skipped-checks.js";
55
58
  import { attemptRefusal, targetDriver } from "./target-drivers.js";
56
59
  import { TestFileProcess } from "./test-file-process.js";
57
60
  import { timerMs } from "./timer.js";
61
+ import { AttemptPopups } from "./popups.js";
58
62
  import { captureFailure, disposePages, openPage, saveState } from "./test-pages.js";
59
63
  // How much longer than the media process's own deadline the run waits for a recording's end.
60
64
  const finishMarginMs = 5000;
65
+ // Why a page a test's page opened has no diagnostics: Retest watches only the pages a test begins with.
66
+ const notCaptured = 'popups are not captured';
61
67
  // A target's first browser keeps the log's name; each further one adds its number, as logs/browser-2.log.
62
68
  function instanceLogFile(file, instance) {
63
69
  return instance === 0 ? file : file.replace(/\.log$/, `-${instance + 1}.log`);
@@ -76,6 +82,8 @@ export class RunSession {
76
82
  #redactor = new Redactor();
77
83
  #reporterNames;
78
84
  #rootDir;
85
+ /** Where each screenshot's baseline is, and, in a run that updates them, what the run wrote. */
86
+ #baselines;
79
87
  #files;
80
88
  #runId = randomUUID();
81
89
  #startedAt = new Date();
@@ -149,9 +157,13 @@ export class RunSession {
149
157
  /** Each running attempt's recordings, by attempt id, and each attempt by its id for the policy's records. */
150
158
  #recorders = new Map();
151
159
  #attempts = new Map();
160
+ /** When each running attempt started, in milliseconds since 1970: a message its inboxes received before then is not its. */
161
+ #attemptStarts = new Map();
152
162
  #sequences = new ArtifactSequences();
153
163
  /** Each attempt's lost or withheld failure screenshots, for its evidence. */
154
164
  #screenshotGaps = new Map();
165
+ /** The app of the test's own that each popup came from, by the popup's app name, whose capture rules it follows. */
166
+ #popupRoots = new Map();
155
167
  /** Every recording the run wrote down, and every partial file a record names, as retention reads them. */
156
168
  #recordingReferences = [];
157
169
  /** Recordings retention removed, by path, and why. */
@@ -168,6 +180,7 @@ export class RunSession {
168
180
  this.#browserCount = options.browsers ?? defaultBrowsers(this.#workers);
169
181
  this.#store = store;
170
182
  this.#rootDir = resolve(options.rootDir);
183
+ this.#baselines = new ScreenshotBaselines({ rootDir: this.#rootDir, update: options.updateScreenshots === true });
171
184
  this.#files = options.files.map((file) => relativePosixPath(this.#rootDir, resolve(this.#rootDir, file)));
172
185
  this.#reporterNames = describeReporters(reporters);
173
186
  this.#events = new EventLog({
@@ -267,7 +280,7 @@ export class RunSession {
267
280
  this.#recording = recording.ok ? recording.settings : defaultRecordingSettings;
268
281
  this.#recordsApps = appNames.some((app) => recordsApp(this.#recording, app, pixels));
269
282
  this.#clockUs = microsecondsSince(this.#start);
270
- this.#policy = this.#recordsApps || pixels.size > 0 || declared.size > 0 ? new PixelCapturePolicy({ rules: (app) => pixels.get(app) ?? defaultAppPixelRules, clock: this.#clockUs, record: (record) => this.#policyRecord(record) }) : undefined;
283
+ this.#policy = this.#recordsApps || pixels.size > 0 || declared.size > 0 ? new PixelCapturePolicy({ rules: (app) => pixels.get(this.#popupRoots.get(app) ?? app) ?? defaultAppPixelRules, clock: this.#clockUs, record: (record) => this.#policyRecord(record) }) : undefined;
271
284
  this.#media = this.#recordsApps
272
285
  ? new RunMedia({
273
286
  location: startMedia !== undefined && discoverMedia === undefined && options.media !== undefined ? options.media : signal => (discoverMedia ?? discoverMediaLocation)(options.media, process.env, timeouts.setup, signal),
@@ -816,6 +829,8 @@ export class RunSession {
816
829
  const { test } = described;
817
830
  const execution = this.#executionRecord(described, planned, prepared, hashModules(this.#rootDir, modules.collected, modules.hashes));
818
831
  const shown = { name: test.registered.name, file: test.file, location: test.registered.location, ...describePath(test), ...setupMark(test) };
832
+ // A host's preparation can send mail the test then reads, so the attempt's inboxes count from here.
833
+ this.#attemptStarts.set(described.attemptId, Date.now());
819
834
  this.#emitFor(described, { type: 'test.started', testId: described.testId, attemptId: described.attemptId, ...shown, execution });
820
835
  const finished = { ...described, startedAt, failure: undefined, assertionCount: 0, evidence: [], cleanupFailures: [], execution };
821
836
  const { variant } = described;
@@ -833,6 +848,7 @@ export class RunSession {
833
848
  ? await this.#runPrepared(child, described, planned, prepared, finished)
834
849
  : { kind: 'finished', finished: { ...finished, failure: preparing.failure, seen: [preparing.failure] } };
835
850
  // An app launched for the attempt is gone before the host cleans its state up, so it cannot touch that state again.
851
+ this.#attemptStarts.delete(described.attemptId);
836
852
  const quitFailures = await this.#quitLaunches(planned.config, described, prepared, ran.leftOpen ?? []);
837
853
  const cleaned = await preparing.cleanUp(ran.kind === 'not_run' || ran.finished.failure !== undefined);
838
854
  const records = { execution, preparations: preparing.records, cleanups: cleaned.records };
@@ -931,7 +947,7 @@ export class RunSession {
931
947
  async #runRecorded(child, described, planned, finished, opened) {
932
948
  const { context, pages, diagnostics, recorder } = opened;
933
949
  const evaluations = this.#evaluations.attempt({ context, pages, hostChecks: described.hostEvaluations, runSignal: this.#options.signal, diagnostics: Object.freeze({ snapshot: (app) => diagnostics.snapshot(app) }), ...(recorder === undefined ? {} : { recordings: recorder }) });
934
- const { report, running } = await this.#runBody(child, pages, described, planned.config, evaluations);
950
+ const { report, running, popups } = await this.#runBody(child, pages, described, planned.config, evaluations);
935
951
  let checked;
936
952
  try {
937
953
  if (report.endedBeforeStart !== undefined)
@@ -953,6 +969,11 @@ export class RunSession {
953
969
  running.close();
954
970
  }
955
971
  const observed = [...(report.observed ?? []), ...(checked?.failure === undefined ? [] : [checked.failure])];
972
+ // A page a test's page opened is neither recorded nor watched for diagnostics, and its records say so.
973
+ for (const popup of popups.all()) {
974
+ diagnostics.notCaptured(popup.app, popup.session, notCaptured);
975
+ recorder?.notRecorded(popup, this.#popupRoots.get(popup.app) ?? popup.app, 'Popups are not captured, so Retest did not record this page.');
976
+ }
956
977
  // Capture ends once the body, its dispatched commands and the parent's checks are over, before the screenshot and
957
978
  // before any app is closed. A diagnostics policy's failure follows any failure the test already had, and never
958
979
  // replaces it.
@@ -960,10 +981,13 @@ export class RunSession {
960
981
  // Recordings end after the last check and before the screenshot, so their frames stop before anything is closed.
961
982
  const recordings = recorder === undefined ? {} : { recordings: await recorder.finish() };
962
983
  const verdict = withDiagnosticsFailure(withEvaluationFailures({ reported: report.failure ?? checked?.failure, recorded: await evaluations.failures(), observed }), diagnosed.failure);
963
- const evidence = verdict === undefined ? [] : await captureFailure(context, pages);
984
+ // The popups still open are pictured and closed with the pages, each before the page that opened it.
985
+ const evidence = verdict === undefined ? [] : await captureFailure(context, [...pages, ...popups.open()]);
964
986
  const unsaved = verdict === undefined ? await this.#saveSetupState(context, described, pages) : undefined;
965
987
  const browserLost = pages.some((page) => !this.#connected(page.browser));
966
- const { cleanupFailures, leftOpen } = await this.#closePages(context, pages);
988
+ const { cleanupFailures, leftOpen } = await this.#closePages(context, [...popups.open(), ...pages]);
989
+ for (const { session } of popups.all())
990
+ this.#policy?.pageLeft(recordIdentity(described, session.owner.app, session.sessionId), 'session_ended');
967
991
  const problem = verdict ?? unsaved;
968
992
  const hostChecks = checked === undefined ? {} : { checked: checked.results };
969
993
  const crashed = report.crashed === true ? { crashed: true } : {};
@@ -1085,11 +1109,13 @@ export class RunSession {
1085
1109
  return state;
1086
1110
  }
1087
1111
  const baseUrl = runtime.kind === 'web' ? config.apps.get(app)?.baseUrl : undefined;
1112
+ const downloads = this.#downloadsOf(described, app, runtime, config);
1088
1113
  const options = {
1089
1114
  ...(baseUrl === undefined ? {} : { baseUrl }),
1090
1115
  ...(emulation === undefined ? {} : { emulation }),
1091
1116
  ...(state === undefined ? {} : { storageState: state.value.storage }),
1092
1117
  ...(proxy === undefined ? {} : { proxy }),
1118
+ ...(downloads === undefined ? {} : { downloads: downloads.folder }),
1093
1119
  };
1094
1120
  const { testId: id, attemptId } = described;
1095
1121
  const session = { sessionId: formatSessionId(attemptId, app), owner: { runId: this.#runId, testId: id, attemptId, app }, runtime };
@@ -1172,6 +1198,26 @@ export class RunSession {
1172
1198
  // An Electron app shows whatever origin it serves, so a fill on its window takes only the origins `secretOrigins` lists.
1173
1199
  const electron = new Set(test.apps.filter((app) => isElectronTarget(targetOf(config, app, described.targets[app]))));
1174
1200
  const fillSecret = (command, context) => this.#secrets.resolve(command, { ...context, appOrigins, ...(electron.has(context.app) ? { target: 'electron' } : {}) });
1201
+ const inbox = this.#inboxAccess(described, appOrigins, electron);
1202
+ const popups = new AttemptPopups({ pages, runId: this.#runId, rooted: (popup, app) => this.#popupRoots.set(popup, app) });
1203
+ const files = new AttemptFiles({
1204
+ pages: new Map(pages.map((page) => [page.app, this.#filesPage(described, config, page)])),
1205
+ runFolder: this.#store.directory,
1206
+ testId: id,
1207
+ attemptId,
1208
+ redactor: this.#redactor,
1209
+ emit: (body) => this.#emitFor(described, body),
1210
+ });
1211
+ const screenshots = new AttemptScreenshots({
1212
+ baselines: this.#baselines,
1213
+ context: this.#pagesContext(described),
1214
+ // A popup is one of the test's pages once it opens, so a page's matchers reach it; it follows the rules of its root.
1215
+ app: (name) => {
1216
+ const found = pages.find((page) => page.app === name) ?? popups.all().find((page) => page.app === name);
1217
+ return found === undefined ? undefined : pictureApp(found.page, found.session.runtime, electron.has(this.#popupRoots.get(name) ?? name));
1218
+ },
1219
+ testFile: test.file,
1220
+ });
1175
1221
  const running = new RunningTest({
1176
1222
  process: child,
1177
1223
  pages: new Map(pages.map(({ app, page }) => [app, page])),
@@ -1184,7 +1230,12 @@ export class RunSession {
1184
1230
  redactor: this.#redactor,
1185
1231
  touch: new Set(pages.filter((page) => page.touch).map(({ app }) => app)),
1186
1232
  evaluations,
1187
- ...(this.#policy === undefined ? {} : { secretEntry: (app, secret) => this.#secretEntry(described, pages, app, secret) }),
1233
+ popups,
1234
+ ...(inbox === undefined ? {} : { inbox }),
1235
+ files,
1236
+ ...(this.#options.playwright === true ? { playwright: true } : {}),
1237
+ ...(this.#policy === undefined ? {} : { secretEntry: (app, secret) => this.#secretEntry(described, [...pages, ...popups.open()], app, secret) }),
1238
+ screenshots,
1188
1239
  });
1189
1240
  // The test is known as running only while its body runs, as before: a browser lost while it settles is the
1190
1241
  // next test's problem, not this one's.
@@ -1198,7 +1249,40 @@ export class RunSession {
1198
1249
  this.#running.delete(active);
1199
1250
  }
1200
1251
  await running.settle(this.#settleBudget());
1201
- return { report, running };
1252
+ return { report, running, popups };
1253
+ }
1254
+ // The attempt's reads of the config's inboxes, which bind a code and a link to the origins of the test's apps' base URLs
1255
+ // and the inbox's own, as a secret is bound. None in a run whose config declares no inbox.
1256
+ #inboxAccess(described, appOrigins, electron) {
1257
+ const { apps } = this.#options;
1258
+ const inboxes = apps.kind === 'config' ? apps.config.inboxes : undefined;
1259
+ if (inboxes === undefined)
1260
+ return undefined;
1261
+ const attempt = new AttemptInbox({ inboxes, redactor: this.#redactor, startedAt: this.#attemptStarts.get(described.attemptId) ?? Date.now() });
1262
+ return {
1263
+ read: (request, options) => attempt.read(request, options),
1264
+ fillCode: (command, context) => attempt.resolveCode(command, { ...context, appOrigins, ...(electron.has(context.app) ? { target: 'electron' } : {}) }),
1265
+ openLink: (reference) => attempt.resolveLink(reference, { appOrigins }),
1266
+ };
1267
+ }
1268
+ // A Chromium page saves its downloads in its session's folder of the run. An Electron app saves them its own way, and
1269
+ // another engine's page and a native app have none Retest keeps.
1270
+ #downloadsOf(described, app, runtime, config) {
1271
+ if (runtime.kind !== 'web' || runtime.engine !== 'chromium' || isElectronTarget(targetOf(config, app, described.targets[app])))
1272
+ return undefined;
1273
+ const reference = downloadsFolder({ testId: described.testId, attemptId: described.attemptId, app, sessionId: formatSessionId(described.attemptId, app) });
1274
+ return { folder: join(this.#store.directory, reference), reference };
1275
+ }
1276
+ // An app's page as the attempt's files see it, with where it runs, for a refusal that names it.
1277
+ #filesPage(described, config, { app, page, session }) {
1278
+ const { runtime } = session;
1279
+ const downloads = this.#downloadsOf(described, app, runtime, config);
1280
+ const where = runtime.kind !== 'web' ? 'in a native app'
1281
+ : isElectronTarget(targetOf(config, app, described.targets[app])) ? 'in an Electron app'
1282
+ : runtime.engine === 'firefox' ? 'on Firefox'
1283
+ : runtime.engine === 'webkit' ? 'on WebKit'
1284
+ : 'on this browser';
1285
+ return { files: page.files, where, sessionId: session.sessionId, ...(downloads === undefined ? {} : { downloads }) };
1202
1286
  }
1203
1287
  // An interrupted run waits for nothing more.
1204
1288
  #settleBudget() {
@@ -1705,6 +1789,7 @@ export class RunSession {
1705
1789
  ...(this.#nativeStarts.length === 0 ? {} : { natives: this.#nativeStarts }),
1706
1790
  ...(this.#options.apps.kind === 'config' ? { browsers } : {}),
1707
1791
  ...(this.#narrowed === undefined ? {} : { narrowed: this.#narrowed }),
1792
+ ...(this.#baselines.written === 0 ? {} : { baselinesWritten: this.#baselines.written }),
1708
1793
  ...evidence,
1709
1794
  files,
1710
1795
  };
@@ -2,11 +2,16 @@ import type { OwnedPage } from '../browser/contract.ts';
2
2
  import type { EvaluationRequests } from '../evaluation/attempt.ts';
3
3
  import type { EventBody, EventOrigin } from '../protocol/events.ts';
4
4
  import type { Failure } from '../protocol/failures.ts';
5
+ import type { InboxLinkRef, InboxRequest } from '../protocol/inbox.ts';
5
6
  import type { Timeouts } from '../protocol/timeouts.ts';
6
7
  import type { Variant } from '../protocol/variant.ts';
7
8
  import type { ProcessExit } from '../shared/process-exit.ts';
8
9
  import type { Redactor } from './redactor.ts';
10
+ import type { ScreenshotAccess } from './screenshot-baselines.ts';
11
+ import type { InboxCodeFill, InboxRead, InboxReadBudget, LinkResolution } from './inbox.ts';
12
+ import type { PopupRegistry } from './popups.ts';
9
13
  import type { FillResolution, SecretFill } from './secrets.ts';
14
+ import type { AttemptFiles } from './files.ts';
10
15
  import type { TestFileProcess } from './test-file-process.ts';
11
16
  /** How long a stopped test's process has to answer, and a page that lost its browser has to say so. */
12
17
  export declare const abortGraceMs = 1000;
@@ -31,12 +36,40 @@ export type RunningTestOptions = {
31
36
  touch?: ReadonlySet<string>;
32
37
  /** Runs the test's AI checks in this process. Without it, every check is refused. */
33
38
  evaluations?: EvaluationRequests;
39
+ /** Reads the run's inboxes for the test, and resolves the codes and links they gave it. Without it, every read is refused. */
40
+ inbox?: InboxAccess;
41
+ /**
42
+ * Makes the pages a test's pages open sessions of the attempt, which `page.popup()` hands the test. Without it, every
43
+ * wait for a popup is refused.
44
+ */
45
+ popups?: PopupRegistry;
46
+ /**
47
+ * The attempt's files: what a test gives a file input, the pickers and downloads it waits for, and every download its
48
+ * pages make. Without it, every command and request about files is refused.
49
+ */
50
+ files?: FilesAccess;
51
+ /** The test file is a Playwright test file, whose page has no `fileChooser()` and whose locators no `setInputFiles()`. */
52
+ playwright?: true;
34
53
  /**
35
54
  * Told just before a secret's keys go to an app's page, with the secret's name, and given how far the keys got once
36
55
  * the fill has answered; the pixel capture policy withholds captures of that session in between. Absent in a run with
37
56
  * no capture policy.
38
57
  */
39
58
  secretEntry?: (app: string, secret: string) => SecretEntryEnd;
59
+ /**
60
+ * Takes the pictures of the test's screenshot checks and keeps them, and leaves what a judged comparison needs in
61
+ * the run folder or the baseline. Without it, every screenshot check is refused.
62
+ */
63
+ screenshots?: ScreenshotAccess;
64
+ };
65
+ /**
66
+ * What a running test needs of the run's inboxes: to read one, to turn a code it gave into the text a fill types, bound
67
+ * to the origins it may be typed on, and to turn a link it gave into the address a goto opens, when its origin allows.
68
+ */
69
+ export type InboxAccess = {
70
+ read: (request: InboxRequest, budget: InboxReadBudget) => Promise<InboxRead>;
71
+ fillCode: (command: InboxCodeFill, context: SecretFillContext) => Promise<FillResolution>;
72
+ openLink: (reference: InboxLinkRef) => LinkResolution;
40
73
  };
41
74
  /** Ends a secret's entry once its fill has answered: whether its keys were sent, not sent, or may have been. */
42
75
  export type SecretEntryEnd = (input: 'not_sent' | 'sent' | 'unknown') => void;
@@ -80,6 +113,8 @@ export type BodyReport = {
80
113
  */
81
114
  observed?: readonly Failure[];
82
115
  };
116
+ /** What a running test needs of its attempt's files. */
117
+ export type FilesAccess = Pick<AttemptFiles, 'stage' | 'chooser' | 'download' | 'saved' | 'close'>;
83
118
  /**
84
119
  * The parent's side of one test body. It forwards the child's page commands to the browser, reports
85
120
  * each action, writes down each look it serves, judges each assertion against the look it names, and
@@ -1 +1 @@
1
- {"version":3,"file":"running-test.d.ts","sourceRoot":"","sources":["../../src/runner/running-test.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,wBAAwB,CAAA;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAElE,OAAO,KAAK,EAAc,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,yBAAyB,CAAA;AAIpF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAG5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,KAAK,EAAiC,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAkB5F,uGAAuG;AACvG,eAAO,MAAM,YAAY,OAAO,CAAA;AAEhC,uDAAuD;AACvD,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,CAAA;AAE/F,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,WAAW,CAAA;IACpB,6GAA6G;IAC7G,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wCAAwC;IACxC,QAAQ,EAAE,QAAQ,CAAA;IAClB,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,IAAI,CAAA;IACrD,mHAAmH;IACnH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,cAAc,CAAC,CAAA;IACzF,iGAAiG;IACjG,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,kGAAkG;IAClG,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC3B,qFAAqF;IACrF,WAAW,CAAC,EAAE,kBAAkB,CAAA;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,cAAc,CAAA;CAC9D,CAAA;AAED,gHAAgH;AAChH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,KAAK,IAAI,CAAA;AAE7E;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAA;AAE9K,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,WAAW,CAAA;IAC9B,wFAAwF;IACxF,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B;;;OAGG;IACH,aAAa,CAAC,EAAE,IAAI,CAAA;IACpB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,IAAI,CAAA;IACd,2GAA2G;IAC3G,QAAQ,EAAE,OAAO,CAAA;IACjB,0HAA0H;IAC1H,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CAC9B,CAAA;AA+CD;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;;gBAwCV,OAAO,EAAE,kBAAkB;IAKvC,yHAAyH;IACzH,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;IAe1B,qFAAqF;IACrF,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAuC9C;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAkBjC;;;;OAIG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9C;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD;;;OAGG;IACH,KAAK,IAAI,IAAI;CAidd"}
1
+ {"version":3,"file":"running-test.d.ts","sourceRoot":"","sources":["../../src/runner/running-test.ts"],"names":[],"mappings":"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"}