@itmetsam/samurai-framework 0.10.0 → 0.12.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 (326) hide show
  1. package/README.md +3 -4
  2. package/dist/assert/assertion-error.d.ts +1 -1
  3. package/dist/assert/expect.d.ts +5 -50
  4. package/dist/assert/expect.js +5 -182
  5. package/dist/assert/locator-assertions.d.ts +22 -0
  6. package/dist/assert/locator-assertions.js +61 -0
  7. package/dist/assert/pending-assertions.d.ts +7 -0
  8. package/dist/assert/pending-assertions.js +40 -0
  9. package/dist/assert/text-match.d.ts +4 -0
  10. package/dist/assert/text-match.js +11 -0
  11. package/dist/assert/value-assertions.d.ts +23 -0
  12. package/dist/assert/value-assertions.js +82 -0
  13. package/dist/browser/browser-context.d.ts +0 -1
  14. package/dist/browser/browser-context.js +13 -5
  15. package/dist/browser/browser-finder.js +5 -9
  16. package/dist/browser/browser.d.ts +3 -31
  17. package/dist/browser/browser.js +36 -219
  18. package/dist/browser/chrome-launch.d.ts +2 -1
  19. package/dist/browser/dialog-handling.d.ts +32 -0
  20. package/dist/browser/dialog-handling.js +85 -0
  21. package/dist/browser/document-watcher.d.ts +44 -0
  22. package/dist/browser/document-watcher.js +160 -0
  23. package/dist/browser/download-folders.d.ts +0 -1
  24. package/dist/browser/download-folders.js +7 -15
  25. package/dist/browser/download.js +5 -10
  26. package/dist/browser/downloads-dir.js +2 -2
  27. package/dist/browser/emit.d.ts +7 -0
  28. package/dist/browser/emit.js +16 -0
  29. package/dist/browser/emulation.d.ts +1 -1
  30. package/dist/browser/emulation.js +54 -18
  31. package/dist/browser/file-chooser.js +4 -11
  32. package/dist/browser/history.d.ts +6 -0
  33. package/dist/browser/history.js +89 -0
  34. package/dist/browser/index.d.ts +2 -0
  35. package/dist/browser/index.js +2 -0
  36. package/dist/browser/init-script.d.ts +2 -0
  37. package/dist/browser/init-script.js +16 -0
  38. package/dist/browser/launch-firefox.d.ts +20 -0
  39. package/dist/browser/launch-firefox.js +66 -0
  40. package/dist/browser/launch-flags.d.ts +18 -0
  41. package/dist/browser/launch-flags.js +100 -0
  42. package/dist/browser/lazy.d.ts +21 -0
  43. package/dist/browser/lazy.js +44 -0
  44. package/dist/browser/page-events.d.ts +44 -0
  45. package/dist/browser/page-events.js +58 -0
  46. package/dist/browser/page-logs.js +2 -9
  47. package/dist/browser/page-navigation.d.ts +58 -0
  48. package/dist/browser/page-navigation.js +108 -0
  49. package/dist/browser/page-network.d.ts +69 -0
  50. package/dist/browser/page-network.js +163 -0
  51. package/dist/browser/page-settings.d.ts +35 -0
  52. package/dist/browser/page-settings.js +51 -0
  53. package/dist/browser/page-transfers.d.ts +36 -0
  54. package/dist/browser/page-transfers.js +50 -0
  55. package/dist/browser/page.d.ts +39 -154
  56. package/dist/browser/page.js +134 -758
  57. package/dist/browser/pdf.d.ts +1 -1
  58. package/dist/browser/session.d.ts +16 -0
  59. package/dist/browser/session.js +32 -0
  60. package/dist/browser/viewport.d.ts +19 -0
  61. package/dist/browser/viewport.js +72 -0
  62. package/dist/browser/warm-up.d.ts +1 -1
  63. package/dist/browser/warm-up.js +2 -1
  64. package/dist/browsers/index.d.ts +1 -1
  65. package/dist/browsers/index.js +1 -1
  66. package/dist/browsers/pins.d.ts +0 -2
  67. package/dist/browsers/pins.js +0 -2
  68. package/dist/browsers/store.d.ts +0 -2
  69. package/dist/browsers/store.js +1 -2
  70. package/dist/cli/args.d.ts +13 -6
  71. package/dist/cli/args.js +13 -10
  72. package/dist/cli/browsers.d.ts +1 -2
  73. package/dist/cli/browsers.js +2 -2
  74. package/dist/cli/console-reporter.js +3 -2
  75. package/dist/cli/flow-reporter.js +2 -1
  76. package/dist/cli/flow.d.ts +1 -2
  77. package/dist/cli/flow.js +25 -22
  78. package/dist/cli/init.js +2 -7
  79. package/dist/cli/io.d.ts +10 -0
  80. package/dist/cli/io.js +1 -0
  81. package/dist/cli/main.d.ts +3 -10
  82. package/dist/cli/main.js +10 -143
  83. package/dist/cli/paint.d.ts +2 -0
  84. package/dist/cli/paint.js +4 -0
  85. package/dist/cli/record.d.ts +4 -0
  86. package/dist/cli/record.js +93 -0
  87. package/dist/cli/usage.d.ts +1 -0
  88. package/dist/cli/usage.js +58 -0
  89. package/dist/cli/version.d.ts +2 -0
  90. package/dist/cli/version.js +7 -0
  91. package/dist/config/browsers.d.ts +11 -0
  92. package/dist/config/browsers.js +32 -0
  93. package/dist/config/config.js +4 -1
  94. package/dist/config/mask.d.ts +1 -1
  95. package/dist/config/mask.js +7 -2
  96. package/dist/config/run-settings.d.ts +1 -2
  97. package/dist/config/run-settings.js +1 -6
  98. package/dist/config/timeouts.d.ts +2 -0
  99. package/dist/config/timeouts.js +13 -0
  100. package/dist/config/workers.d.ts +14 -0
  101. package/dist/config/workers.js +29 -0
  102. package/dist/flows/run/branch-context.d.ts +14 -0
  103. package/dist/flows/run/branch-context.js +35 -0
  104. package/dist/flows/run/handlers/condition.d.ts +1 -1
  105. package/dist/flows/run/handlers/end.d.ts +1 -1
  106. package/dist/flows/run/handlers/group.d.ts +1 -1
  107. package/dist/flows/run/handlers/index.d.ts +2 -2
  108. package/dist/flows/run/handlers/parallel.d.ts +1 -1
  109. package/dist/flows/run/handlers/start.d.ts +1 -1
  110. package/dist/flows/run/handlers/support.d.ts +3 -27
  111. package/dist/flows/run/handlers/test.d.ts +1 -1
  112. package/dist/flows/run/handlers/types.d.ts +27 -0
  113. package/dist/flows/run/handlers/types.js +1 -0
  114. package/dist/flows/run/index.d.ts +1 -1
  115. package/dist/flows/run/prepare-flow.d.ts +14 -0
  116. package/dist/flows/run/prepare-flow.js +44 -0
  117. package/dist/flows/run/project.d.ts +33 -2
  118. package/dist/flows/run/project.js +139 -57
  119. package/dist/flows/run/run.d.ts +3 -4
  120. package/dist/flows/run/run.js +23 -193
  121. package/dist/flows/run/walker.d.ts +56 -0
  122. package/dist/flows/run/walker.js +225 -0
  123. package/dist/flows/steps/catalog.d.ts +1 -1
  124. package/dist/flows/steps/handler.d.ts +1 -1
  125. package/dist/flows/steps/set.d.ts +1 -1
  126. package/dist/index.js +1 -1
  127. package/dist/locator/actionability.d.ts +33 -0
  128. package/dist/locator/actionability.js +104 -0
  129. package/dist/locator/chain-resolver.d.ts +20 -0
  130. package/dist/locator/chain-resolver.js +102 -0
  131. package/dist/locator/document-swap.d.ts +1 -0
  132. package/dist/locator/document-swap.js +11 -0
  133. package/dist/locator/element-state.d.ts +3 -1
  134. package/dist/locator/element-state.js +11 -3
  135. package/dist/locator/file-actions.d.ts +35 -0
  136. package/dist/locator/file-actions.js +55 -0
  137. package/dist/{script/helpers.js → locator/helper-realm.js} +2 -2
  138. package/dist/locator/input-actions.d.ts +18 -0
  139. package/dist/locator/input-actions.js +80 -0
  140. package/dist/locator/label-locator.d.ts +1 -1
  141. package/dist/locator/label-locator.js +5 -12
  142. package/dist/locator/locator.d.ts +14 -37
  143. package/dist/locator/locator.js +68 -424
  144. package/dist/locator/page-scripts.d.ts +16 -0
  145. package/dist/locator/page-scripts.js +54 -0
  146. package/dist/locator/page-search.d.ts +10 -0
  147. package/dist/locator/page-search.js +19 -0
  148. package/dist/locator/selector-errors.d.ts +0 -4
  149. package/dist/locator/selector-errors.js +0 -7
  150. package/dist/locator/selector.d.ts +14 -13
  151. package/dist/locator/selector.js +10 -16
  152. package/dist/locator/text-locator.d.ts +1 -1
  153. package/dist/locator/text-locator.js +5 -9
  154. package/dist/logger/index.d.ts +6 -0
  155. package/dist/logger/index.js +14 -7
  156. package/dist/network/bidi-wire.d.ts +10 -0
  157. package/dist/network/bidi-wire.js +38 -0
  158. package/dist/network/intercept.d.ts +30 -0
  159. package/dist/network/intercept.js +130 -0
  160. package/dist/network/network-tracker.d.ts +10 -47
  161. package/dist/network/network-tracker.js +34 -68
  162. package/dist/network/network-types.d.ts +33 -0
  163. package/dist/network/network-types.js +1 -0
  164. package/dist/network/network-wait.d.ts +7 -2
  165. package/dist/network/network-wait.js +14 -4
  166. package/dist/network/response.d.ts +1 -1
  167. package/dist/network/response.js +1 -1
  168. package/dist/network/route.d.ts +92 -0
  169. package/dist/network/route.js +142 -0
  170. package/dist/network/router.d.ts +6 -91
  171. package/dist/network/router.js +25 -258
  172. package/dist/recorder/expectation.d.ts +5 -0
  173. package/dist/recorder/expectation.js +20 -0
  174. package/dist/recorder/files-recorder.d.ts +30 -0
  175. package/dist/recorder/files-recorder.js +88 -0
  176. package/dist/recorder/normalise.d.ts +17 -6
  177. package/dist/recorder/normalise.js +96 -91
  178. package/dist/recorder/options.d.ts +56 -0
  179. package/dist/recorder/options.js +1 -0
  180. package/dist/recorder/page-messages.d.ts +35 -0
  181. package/dist/recorder/page-messages.js +26 -0
  182. package/dist/recorder/ranking.js +1 -0
  183. package/dist/recorder/realms.d.ts +6 -0
  184. package/dist/recorder/realms.js +13 -0
  185. package/dist/recorder/recorder.d.ts +10 -74
  186. package/dist/recorder/recorder.js +39 -199
  187. package/dist/recorder/session-browser.d.ts +22 -0
  188. package/dist/recorder/session-browser.js +21 -0
  189. package/dist/recorder/session-secrets.d.ts +8 -0
  190. package/dist/recorder/session-secrets.js +45 -0
  191. package/dist/recorder/session.d.ts +5 -14
  192. package/dist/recorder/session.js +22 -120
  193. package/dist/recorder/spec-source.d.ts +39 -0
  194. package/dist/recorder/spec-source.js +84 -0
  195. package/dist/recorder/toolbar/action-guard.d.ts +21 -0
  196. package/dist/recorder/toolbar/action-guard.js +43 -0
  197. package/dist/recorder/toolbar/actions/assert.d.ts +4 -0
  198. package/dist/recorder/toolbar/actions/assert.js +52 -0
  199. package/dist/recorder/toolbar/actions/insert.d.ts +6 -0
  200. package/dist/recorder/toolbar/actions/insert.js +93 -0
  201. package/dist/recorder/toolbar/actions/pick.d.ts +8 -0
  202. package/dist/recorder/toolbar/actions/pick.js +26 -0
  203. package/dist/recorder/toolbar/actions/reply.d.ts +7 -0
  204. package/dist/recorder/toolbar/actions/reply.js +10 -0
  205. package/dist/recorder/toolbar/controller-fixture.d.ts +47 -0
  206. package/dist/recorder/toolbar/controller-fixture.js +129 -0
  207. package/dist/recorder/toolbar/controller.d.ts +12 -78
  208. package/dist/recorder/toolbar/controller.js +35 -247
  209. package/dist/recorder/toolbar/inspect.d.ts +1 -3
  210. package/dist/recorder/toolbar/inspect.js +2 -2
  211. package/dist/recorder/toolbar/page-scope.d.ts +19 -0
  212. package/dist/recorder/toolbar/page-scope.js +43 -0
  213. package/dist/recorder/toolbar/protocol.d.ts +2 -1
  214. package/dist/recorder/toolbar/source/attach.d.ts +2 -0
  215. package/dist/recorder/toolbar/source/attach.js +41 -0
  216. package/dist/recorder/toolbar/source/dom.d.ts +2 -0
  217. package/dist/recorder/toolbar/source/dom.js +47 -0
  218. package/dist/recorder/toolbar/source/drag.d.ts +2 -0
  219. package/dist/recorder/toolbar/source/drag.js +32 -0
  220. package/dist/recorder/toolbar/source/events-page.d.ts +2 -0
  221. package/dist/recorder/toolbar/source/events-page.js +67 -0
  222. package/dist/recorder/toolbar/source/events-toolbar.d.ts +2 -0
  223. package/dist/recorder/toolbar/source/events-toolbar.js +35 -0
  224. package/dist/recorder/toolbar/source/fields.d.ts +2 -0
  225. package/dist/recorder/toolbar/source/fields.js +44 -0
  226. package/dist/recorder/toolbar/source/handlers.d.ts +2 -0
  227. package/dist/recorder/toolbar/source/handlers.js +134 -0
  228. package/dist/recorder/toolbar/source/names.d.ts +2 -0
  229. package/dist/recorder/toolbar/source/names.js +12 -0
  230. package/dist/recorder/toolbar/source/picking.d.ts +2 -0
  231. package/dist/recorder/toolbar/source/picking.js +72 -0
  232. package/dist/recorder/toolbar/source/placement.d.ts +2 -0
  233. package/dist/recorder/toolbar/source/placement.js +45 -0
  234. package/dist/recorder/toolbar/source/recorder-side.d.ts +2 -0
  235. package/dist/recorder/toolbar/source/recorder-side.js +54 -0
  236. package/dist/recorder/toolbar/source/setup.d.ts +2 -0
  237. package/dist/recorder/toolbar/source/setup.js +50 -0
  238. package/dist/recorder/toolbar/source/views-panels.d.ts +2 -0
  239. package/dist/recorder/toolbar/source/views-panels.js +150 -0
  240. package/dist/recorder/toolbar/source/views-rail.d.ts +2 -0
  241. package/dist/recorder/toolbar/source/views-rail.js +40 -0
  242. package/dist/recorder/toolbar/source/views-render.d.ts +2 -0
  243. package/dist/recorder/toolbar/source/views-render.js +48 -0
  244. package/dist/recorder/toolbar/source.js +30 -856
  245. package/dist/recorder/toolbar/types.d.ts +70 -0
  246. package/dist/recorder/toolbar/types.js +1 -0
  247. package/dist/recorder/toolbar-bridge.d.ts +43 -0
  248. package/dist/recorder/toolbar-bridge.js +106 -0
  249. package/dist/runner/browsers.d.ts +0 -12
  250. package/dist/runner/browsers.js +1 -33
  251. package/dist/runner/options.d.ts +31 -0
  252. package/dist/runner/options.js +1 -0
  253. package/dist/runner/pooled-run.d.ts +43 -0
  254. package/dist/runner/pooled-run.js +75 -0
  255. package/dist/runner/ports.d.ts +2 -0
  256. package/dist/runner/ports.js +2 -0
  257. package/dist/runner/registry.d.ts +1 -1
  258. package/dist/runner/registry.js +1 -1
  259. package/dist/runner/reporter.d.ts +31 -10
  260. package/dist/runner/reporter.js +39 -6
  261. package/dist/runner/run-test.d.ts +16 -0
  262. package/dist/runner/run-test.js +178 -0
  263. package/dist/runner/run.d.ts +5 -3
  264. package/dist/runner/run.js +28 -29
  265. package/dist/runner/select.d.ts +24 -0
  266. package/dist/runner/select.js +77 -0
  267. package/dist/runner/test-dir.d.ts +13 -0
  268. package/dist/runner/test-dir.js +27 -0
  269. package/dist/runner/test-runner.d.ts +25 -56
  270. package/dist/runner/test-runner.js +81 -270
  271. package/dist/runner/worker-pool.d.ts +67 -0
  272. package/dist/runner/worker-pool.js +275 -0
  273. package/dist/runner/worker-process.d.ts +6 -0
  274. package/dist/runner/worker-process.js +54 -0
  275. package/dist/runner/worker-protocol.d.ts +67 -0
  276. package/dist/runner/worker-protocol.js +1 -0
  277. package/dist/runner/worker-session.d.ts +7 -0
  278. package/dist/runner/worker-session.js +60 -0
  279. package/dist/runner/worker.d.ts +26 -0
  280. package/dist/runner/worker.js +121 -0
  281. package/dist/runner/workers-option.d.ts +15 -0
  282. package/dist/runner/workers-option.js +22 -0
  283. package/dist/script/call-function.d.ts +1 -9
  284. package/dist/script/call-function.js +2 -19
  285. package/dist/script/element-handle.d.ts +1 -2
  286. package/dist/script/element-handle.js +1 -2
  287. package/dist/script/serialize.d.ts +3 -12
  288. package/dist/script/serialize.js +53 -62
  289. package/dist/steps/parse/call.d.ts +3 -0
  290. package/dist/steps/parse/call.js +81 -0
  291. package/dist/steps/parse/expectation.d.ts +5 -0
  292. package/dist/steps/parse/expectation.js +71 -0
  293. package/dist/steps/parse/file-entry.d.ts +4 -0
  294. package/dist/steps/parse/file-entry.js +74 -0
  295. package/dist/steps/parse/literals.d.ts +4 -0
  296. package/dist/steps/parse/literals.js +28 -0
  297. package/dist/steps/parse/step.d.ts +4 -0
  298. package/dist/steps/parse/step.js +81 -0
  299. package/dist/steps/parse.js +2 -323
  300. package/dist/transport/bidi-connection.d.ts +4 -15
  301. package/dist/transport/bidi-connection.js +9 -63
  302. package/dist/transport/log-redaction.d.ts +13 -0
  303. package/dist/transport/log-redaction.js +51 -0
  304. package/dist/transport/subscriptions.js +18 -13
  305. package/dist/transport/unsupported.d.ts +7 -0
  306. package/dist/transport/unsupported.js +13 -0
  307. package/dist/types/bidi-modules/browser.d.ts +1 -12
  308. package/dist/types/bidi-modules/browsing-context.d.ts +8 -18
  309. package/dist/types/bidi-modules/emulation.d.ts +29 -21
  310. package/dist/types/bidi-modules/input.d.ts +1 -9
  311. package/dist/types/bidi-modules/network.d.ts +29 -24
  312. package/dist/types/bidi-modules/script.d.ts +0 -9
  313. package/dist/types/bidi-modules/session.d.ts +0 -8
  314. package/dist/types/bidi-modules/storage.d.ts +0 -5
  315. package/dist/types/bidi.d.ts +7 -37
  316. package/dist/types/config.d.ts +12 -0
  317. package/dist/types/test.d.ts +8 -3
  318. package/dist/wait/wait-until.d.ts +1 -5
  319. package/dist/wait/wait-until.js +7 -16
  320. package/package.json +3 -2
  321. package/dist/config/default.d.ts +0 -2
  322. package/dist/config/default.js +0 -3
  323. package/dist/types/bidi-modules/web-extensions.d.ts +0 -9
  324. package/dist/types/bidi-protocols/command.d.ts +0 -7
  325. package/dist/types/bidi-protocols/event.d.ts +0 -17
  326. /package/dist/{script/helpers.d.ts → locator/helper-realm.d.ts} +0 -0
@@ -1,4 +1,3 @@
1
- import { BiDiError } from "../transport/bidi-error.js";
2
1
  import { fromRemoteValue, toLocalValue } from "./serialize.js";
3
2
  /** How much of a function's source `ScriptError` keeps */
4
3
  const SOURCE_PREVIEW = 200;
@@ -37,10 +36,8 @@ export function toFunctionDeclaration(fn) {
37
36
  /** Calls `fn` in the page with serialized arguments and returns its deserialized result */
38
37
  export async function callFunction(connector, context, fn, args = [], options = {}) {
39
38
  const functionDeclaration = toFunctionDeclaration(fn);
40
- const ownership = options.ownership ?? "none";
41
39
  // Serialize first: a bad argument must fail before anything is sent
42
40
  const serialized = args.map(toLocalValue);
43
- const thisValue = "thisArg" in options ? toLocalValue(options.thisArg) : undefined;
44
41
  const result = await connector.send("script.callFunction", {
45
42
  functionDeclaration,
46
43
  awaitPromise: options.awaitPromise ?? true,
@@ -48,24 +45,10 @@ export async function callFunction(connector, context, fn, args = [], options =
48
45
  ? { context }
49
46
  : { context, sandbox: options.sandbox },
50
47
  ...(serialized.length > 0 && { arguments: serialized }),
51
- resultOwnership: ownership,
52
- ...(thisValue !== undefined && { this: thisValue }),
48
+ resultOwnership: "none",
53
49
  });
54
50
  if (result.type === "exception") {
55
51
  throw new ScriptError(result.exceptionDetails.text, functionDeclaration, result.exceptionDetails.stackTrace);
56
52
  }
57
- return fromRemoteValue(result.result, { keepHandles: ownership === "root" });
58
- }
59
- /** Releases handles obtained with `ownership: "root"`; a handle that is already gone is fine */
60
- export async function disown(connector, context, handles) {
61
- if (handles.length === 0)
62
- return;
63
- try {
64
- await connector.send("script.disown", { handles, target: { context } });
65
- }
66
- catch (err) {
67
- if (err instanceof BiDiError && err.code === "no such handle")
68
- return;
69
- throw err;
70
- }
53
+ return fromRemoteValue(result.result);
71
54
  }
@@ -5,6 +5,5 @@
5
5
  */
6
6
  export declare class ElementHandle {
7
7
  readonly sharedId: string;
8
- readonly handle?: string | undefined;
9
- constructor(sharedId: string, handle?: string | undefined);
8
+ constructor(sharedId: string);
10
9
  }
@@ -4,8 +4,7 @@
4
4
  * Internal: user code keeps using `Locator`.
5
5
  */
6
6
  export class ElementHandle {
7
- constructor(sharedId, handle) {
7
+ constructor(sharedId) {
8
8
  this.sharedId = sharedId;
9
- this.handle = handle;
10
9
  }
11
10
  }
@@ -1,15 +1,10 @@
1
1
  import type { ArgumentValue, RemoteValue } from "../types/bidi-modules/script.js";
2
2
  /** A value passed to a page function */
3
3
  export type Arg = unknown;
4
- /** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by type and handle */
4
+ /** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by its type */
5
5
  export declare class RemoteObject {
6
6
  readonly type: string;
7
- readonly handle?: string | undefined;
8
- /** The deserialized value, when the browser sent one and handles were kept */
9
- readonly value?: unknown | undefined;
10
- constructor(type: string, handle?: string | undefined,
11
- /** The deserialized value, when the browser sent one and handles were kept */
12
- value?: unknown | undefined);
7
+ constructor(type: string);
13
8
  }
14
9
  /**
15
10
  * A channel the page function can call as `send(value)`; the browser delivers each value as a
@@ -21,9 +16,5 @@ export declare class ChannelArg {
21
16
  }
22
17
  /** Serializes `value` for `script.callFunction` arguments; throws TypeError for what cannot cross the wire */
23
18
  export declare function toLocalValue(value: Arg): ArgumentValue;
24
- export interface DeserializeOptions {
25
- /** Return objects that carry a handle as `RemoteObject`s (with `.value`), so the caller can disown them later */
26
- keepHandles?: boolean;
27
- }
28
19
  /** Converts a remote value to plain JS; what cannot be copied becomes a `RemoteObject` */
29
- export declare function fromRemoteValue(value: RemoteValue, options?: DeserializeOptions): unknown;
20
+ export declare function fromRemoteValue(value: RemoteValue): unknown;
@@ -1,14 +1,10 @@
1
1
  import { ElementHandle } from "./element-handle.js";
2
2
  /** Deepest nesting `toLocalValue` accepts; a second guard next to cycle detection */
3
3
  const MAX_DEPTH = 100;
4
- /** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by type and handle */
4
+ /** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by its type */
5
5
  export class RemoteObject {
6
- constructor(type, handle,
7
- /** The deserialized value, when the browser sent one and handles were kept */
8
- value) {
6
+ constructor(type) {
9
7
  this.type = type;
10
- this.handle = handle;
11
- this.value = value;
12
8
  }
13
9
  }
14
10
  /**
@@ -55,11 +51,8 @@ function serialize(value, ancestors, depth) {
55
51
  },
56
52
  };
57
53
  }
58
- if (value instanceof ElementHandle) {
59
- return value.handle === undefined
60
- ? { sharedId: value.sharedId }
61
- : { sharedId: value.sharedId, handle: value.handle };
62
- }
54
+ if (value instanceof ElementHandle)
55
+ return { sharedId: value.sharedId };
63
56
  if (value instanceof Date)
64
57
  return { type: "date", value: value.toISOString() };
65
58
  if (value instanceof RegExp) {
@@ -112,10 +105,10 @@ function numberValue(value) {
112
105
  return value;
113
106
  }
114
107
  /** Converts a remote value to plain JS; what cannot be copied becomes a `RemoteObject` */
115
- export function fromRemoteValue(value, options = {}) {
116
- return deserialize(value, new Map(), options);
108
+ export function fromRemoteValue(value) {
109
+ return deserialize(value, new Map());
117
110
  }
118
- function deserialize(value, known, options) {
111
+ function deserialize(value, known) {
119
112
  const loose = value;
120
113
  switch (value.type) {
121
114
  case "undefined":
@@ -136,60 +129,58 @@ function deserialize(value, known, options) {
136
129
  return new RegExp(pattern, flags);
137
130
  }
138
131
  case "node":
139
- return new ElementHandle(loose.sharedId ?? "", loose.handle);
140
- }
141
- const nested = (item) => deserialize(item, known, options);
142
- const remember = (result) => {
143
- if (loose.internalId !== undefined)
144
- known.set(loose.internalId, result);
145
- return result;
146
- };
147
- const backReference = () => loose.internalId !== undefined && known.has(loose.internalId)
148
- ? { found: true, value: known.get(loose.internalId) }
149
- : undefined;
150
- let result;
151
- switch (value.type) {
132
+ return new ElementHandle(loose.sharedId ?? "");
152
133
  case "array":
153
134
  case "set":
154
135
  case "object":
136
+ case "map":
137
+ // A collection cut off by the serialization depth has no copy to make
138
+ return (deserializeCollection(value, known) ?? new RemoteObject(value.type));
139
+ }
140
+ return new RemoteObject(value.type);
141
+ }
142
+ /**
143
+ * An array, set, map or object. A back reference (a value seen before, by `internalId`) gives the copy made
144
+ * earlier; `undefined` means the browser cut the collection off.
145
+ */
146
+ function deserializeCollection(value, known) {
147
+ const { value: items, internalId } = value;
148
+ if (items === undefined)
149
+ return internalId !== undefined && known.has(internalId)
150
+ ? known.get(internalId)
151
+ : undefined;
152
+ // Registered before the items are read, so an item that points back at its container finds it
153
+ const remember = (copy) => {
154
+ if (internalId !== undefined)
155
+ known.set(internalId, copy);
156
+ return copy;
157
+ };
158
+ const nested = (item) => deserialize(item, known);
159
+ const key = (raw) => typeof raw === "string" ? raw : nested(raw);
160
+ switch (value.type) {
161
+ case "array": {
162
+ const list = remember([]);
163
+ items.forEach((item) => list.push(nested(item)));
164
+ return list;
165
+ }
166
+ case "set": {
167
+ const set = remember(new Set());
168
+ items.forEach((item) => set.add(nested(item)));
169
+ return set;
170
+ }
155
171
  case "map": {
156
- const reference = backReference();
157
- if (reference && loose.value === undefined)
158
- return reference.value;
159
- if (loose.value === undefined)
160
- break; // cut off by the serialization depth
161
- if (value.type === "array") {
162
- const list = remember([]);
163
- loose.value.forEach((item) => list.push(nested(item)));
164
- result = list;
165
- }
166
- else if (value.type === "set") {
167
- const set = remember(new Set());
168
- loose.value.forEach((item) => set.add(nested(item)));
169
- result = set;
170
- }
171
- else {
172
- const entries = loose.value;
173
- const key = (raw) => typeof raw === "string" ? raw : nested(raw);
174
- if (value.type === "map") {
175
- const map = remember(new Map());
176
- entries.forEach(([k, item]) => map.set(key(k), nested(item)));
177
- result = map;
178
- }
179
- else {
180
- const object = remember({});
181
- entries.forEach(([k, item]) => {
182
- object[String(key(k))] = nested(item);
183
- });
184
- result = object;
185
- }
186
- }
187
- return options.keepHandles && loose.handle !== undefined
188
- ? new RemoteObject(value.type, loose.handle, result)
189
- : result;
172
+ const map = remember(new Map());
173
+ items.forEach(([k, item]) => map.set(key(k), nested(item)));
174
+ return map;
175
+ }
176
+ default: {
177
+ const object = remember({});
178
+ items.forEach(([k, item]) => {
179
+ object[String(key(k))] = nested(item);
180
+ });
181
+ return object;
190
182
  }
191
183
  }
192
- return new RemoteObject(value.type, loose.handle);
193
184
  }
194
185
  function specialNumber(value) {
195
186
  switch (value) {
@@ -0,0 +1,3 @@
1
+ import ts from "typescript";
2
+ import type { LocatorSpec } from "../model.js";
3
+ export declare function readLocator(node: ts.Expression): LocatorSpec | undefined;
@@ -0,0 +1,81 @@
1
+ import ts from "typescript";
2
+ import { readObject, stringOf } from "./literals.js";
3
+ function readCall(method, args) {
4
+ const first = stringOf(args[0]);
5
+ if (first === undefined)
6
+ return undefined;
7
+ switch (method) {
8
+ case "locator":
9
+ return args.length === 1 ? { method, xpath: first } : undefined;
10
+ case "getByCss":
11
+ return args.length === 1 ? { method, css: first } : undefined;
12
+ case "getByTestId":
13
+ return args.length === 1 ? { method, testId: first } : undefined;
14
+ case "getByText":
15
+ case "getByLabel": {
16
+ if (args.length > 2)
17
+ return undefined;
18
+ const options = args.length === 2 ? readObject(args[1], ["match", "ignoreCase"]) : {};
19
+ if (!options)
20
+ return undefined;
21
+ const { match, ignoreCase } = options;
22
+ if ((match !== undefined && match !== "full" && match !== "partial") ||
23
+ (ignoreCase !== undefined && typeof ignoreCase !== "boolean")) {
24
+ return undefined;
25
+ }
26
+ return {
27
+ method,
28
+ text: first,
29
+ ...(match !== undefined && { match: match }),
30
+ ...(ignoreCase !== undefined && { ignoreCase: ignoreCase }),
31
+ };
32
+ }
33
+ case "getByRole": {
34
+ if (args.length > 2)
35
+ return undefined;
36
+ const options = args.length === 2 ? readObject(args[1], ["name"]) : {};
37
+ if (!options ||
38
+ (options.name !== undefined && typeof options.name !== "string"))
39
+ return undefined;
40
+ return {
41
+ method,
42
+ role: first,
43
+ ...(options.name !== undefined && { name: options.name }),
44
+ };
45
+ }
46
+ default:
47
+ return undefined;
48
+ }
49
+ }
50
+ /** `page.a(…).b(…)` as calls in order; undefined when it is anything else */
51
+ function readChain(node) {
52
+ const calls = [];
53
+ let current = node;
54
+ while (ts.isCallExpression(current)) {
55
+ if (!ts.isPropertyAccessExpression(current.expression))
56
+ return undefined;
57
+ const call = readCall(current.expression.name.text, current.arguments);
58
+ if (!call)
59
+ return undefined;
60
+ calls.unshift(call);
61
+ current = current.expression.expression;
62
+ }
63
+ return ts.isIdentifier(current) && current.text === "page" && calls.length > 0
64
+ ? calls
65
+ : undefined;
66
+ }
67
+ export function readLocator(node) {
68
+ if (ts.isCallExpression(node) &&
69
+ ts.isPropertyAccessExpression(node.expression) &&
70
+ node.expression.name.text === "withFallbacks") {
71
+ const chain = readChain(node.expression.expression);
72
+ const fallbacks = node.arguments.map(readChain);
73
+ if (!chain ||
74
+ node.arguments.length === 0 ||
75
+ fallbacks.some((fallback) => !fallback))
76
+ return undefined;
77
+ return { chain, fallbacks: fallbacks };
78
+ }
79
+ const chain = readChain(node);
80
+ return chain && { chain, fallbacks: [] };
81
+ }
@@ -0,0 +1,5 @@
1
+ import ts from "typescript";
2
+ import type { Expectation, TextValue } from "../model.js";
3
+ /** `env.NAME`, `env["NAME"]`, `secrets.NAME`, `secrets["NAME"]` or a string literal */
4
+ export declare function readValue(node: ts.Expression | undefined): TextValue | undefined;
5
+ export declare function readExpectation(matcher: string, args: readonly ts.Expression[]): Expectation | undefined;
@@ -0,0 +1,71 @@
1
+ import ts from "typescript";
2
+ import { stringOf } from "./literals.js";
3
+ const TEXT_MATCHERS = ["toHaveText", "toContainText", "toHaveValue"];
4
+ /** `env.NAME`, `env["NAME"]`, `secrets.NAME`, `secrets["NAME"]` or a string literal */
5
+ export function readValue(node) {
6
+ if (!node)
7
+ return undefined;
8
+ const literal = stringOf(node);
9
+ if (literal !== undefined)
10
+ return { kind: "literal", value: literal };
11
+ let object;
12
+ let name;
13
+ if (ts.isPropertyAccessExpression(node)) {
14
+ object = node.expression;
15
+ name = node.name.text;
16
+ }
17
+ else if (ts.isElementAccessExpression(node)) {
18
+ object = node.expression;
19
+ name = stringOf(node.argumentExpression);
20
+ }
21
+ else
22
+ return undefined;
23
+ if (name === undefined || !ts.isIdentifier(object))
24
+ return undefined;
25
+ if (object.text === "env")
26
+ return { kind: "env", name };
27
+ if (object.text === "secrets")
28
+ return { kind: "secret", name };
29
+ return undefined;
30
+ }
31
+ function readTextMatcher(node) {
32
+ if (!node)
33
+ return undefined;
34
+ const literal = stringOf(node);
35
+ if (literal !== undefined)
36
+ return literal;
37
+ if (ts.isRegularExpressionLiteral(node)) {
38
+ const end = node.text.lastIndexOf("/");
39
+ return {
40
+ regex: {
41
+ source: node.text.slice(1, end),
42
+ flags: node.text.slice(end + 1),
43
+ },
44
+ };
45
+ }
46
+ return undefined;
47
+ }
48
+ export function readExpectation(matcher, args) {
49
+ if (matcher === "toBeVisible")
50
+ return args.length === 0 ? { matcher } : undefined;
51
+ if (matcher === "toHaveCount") {
52
+ const [count] = args;
53
+ return args.length === 1 && count && ts.isNumericLiteral(count)
54
+ ? { matcher, expected: Number(count.text) }
55
+ : undefined;
56
+ }
57
+ if (matcher === "toHaveAttribute") {
58
+ const name = stringOf(args[0]);
59
+ const expected = readTextMatcher(args[1]);
60
+ return args.length === 2 && name !== undefined && expected !== undefined
61
+ ? { matcher, name, expected }
62
+ : undefined;
63
+ }
64
+ if (TEXT_MATCHERS.includes(matcher)) {
65
+ const expected = readTextMatcher(args[0]);
66
+ return args.length === 1 && expected !== undefined
67
+ ? { matcher: matcher, expected }
68
+ : undefined;
69
+ }
70
+ return undefined;
71
+ }
@@ -0,0 +1,4 @@
1
+ import ts from "typescript";
2
+ import type { FileEntry } from "../model.js";
3
+ /** One file entry, or an array of them */
4
+ export declare function readFiles(node: ts.Expression | undefined): FileEntry[] | undefined;
@@ -0,0 +1,74 @@
1
+ import ts from "typescript";
2
+ import { parseSize } from "../files.js";
3
+ import { stringOf } from "./literals.js";
4
+ /** `"path"` or `generatedFile({ name, size, type? })`; undefined for anything else */
5
+ function readFileEntry(node) {
6
+ const path = stringOf(node);
7
+ if (path !== undefined)
8
+ return { path };
9
+ if (!ts.isCallExpression(node) ||
10
+ !ts.isIdentifier(node.expression) ||
11
+ node.expression.text !== "generatedFile" ||
12
+ node.arguments.length !== 1 ||
13
+ !ts.isObjectLiteralExpression(node.arguments[0]))
14
+ return undefined;
15
+ let name;
16
+ let type;
17
+ let size;
18
+ let sizeSource;
19
+ for (const property of node.arguments[0].properties) {
20
+ if (!ts.isPropertyAssignment(property))
21
+ return undefined;
22
+ const value = property.initializer;
23
+ switch (property.name.getText()) {
24
+ case "name":
25
+ name = stringOf(value);
26
+ if (name === undefined)
27
+ return undefined;
28
+ break;
29
+ case "type":
30
+ type = stringOf(value);
31
+ if (type === undefined)
32
+ return undefined;
33
+ break;
34
+ case "size": {
35
+ const text = stringOf(value);
36
+ size =
37
+ text !== undefined
38
+ ? parseSize(text)
39
+ : ts.isNumericLiteral(value)
40
+ ? parseSize(Number(value.text))
41
+ : undefined;
42
+ if (size === undefined)
43
+ return undefined;
44
+ sizeSource = value.getText();
45
+ break;
46
+ }
47
+ default:
48
+ return undefined;
49
+ }
50
+ }
51
+ if (name === undefined || size === undefined)
52
+ return undefined;
53
+ return {
54
+ generate: {
55
+ name,
56
+ size,
57
+ ...(type !== undefined && { type }),
58
+ ...(sizeSource !== undefined && { sizeSource }),
59
+ },
60
+ };
61
+ }
62
+ /** One file entry, or an array of them */
63
+ export function readFiles(node) {
64
+ if (!node)
65
+ return undefined;
66
+ if (ts.isArrayLiteralExpression(node)) {
67
+ const entries = node.elements.map(readFileEntry);
68
+ return entries.every((entry) => entry !== undefined)
69
+ ? entries
70
+ : undefined;
71
+ }
72
+ const entry = readFileEntry(node);
73
+ return entry && [entry];
74
+ }
@@ -0,0 +1,4 @@
1
+ import ts from "typescript";
2
+ export declare function stringOf(node: ts.Node | undefined): string | undefined;
3
+ /** `{ key: literal, … }` with only the allowed keys, or undefined when it has anything else */
4
+ export declare function readObject(node: ts.Node | undefined, allowed: readonly string[]): Record<string, string | boolean> | undefined;
@@ -0,0 +1,28 @@
1
+ import ts from "typescript";
2
+ export function stringOf(node) {
3
+ return node &&
4
+ (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
5
+ ? node.text
6
+ : undefined;
7
+ }
8
+ /** `{ key: literal, … }` with only the allowed keys, or undefined when it has anything else */
9
+ export function readObject(node, allowed) {
10
+ if (!node || !ts.isObjectLiteralExpression(node))
11
+ return undefined;
12
+ const result = {};
13
+ for (const property of node.properties) {
14
+ if (!ts.isPropertyAssignment(property) ||
15
+ !allowed.includes(property.name.getText()))
16
+ return undefined;
17
+ const value = stringOf(property.initializer);
18
+ if (value !== undefined)
19
+ result[property.name.getText()] = value;
20
+ else if (property.initializer.kind === ts.SyntaxKind.TrueKeyword)
21
+ result[property.name.getText()] = true;
22
+ else if (property.initializer.kind === ts.SyntaxKind.FalseKeyword)
23
+ result[property.name.getText()] = false;
24
+ else
25
+ return undefined;
26
+ }
27
+ return result;
28
+ }
@@ -0,0 +1,4 @@
1
+ import ts from "typescript";
2
+ import type { Step } from "../model.js";
3
+ /** The step a statement stands for, or undefined when it is outside the recorder's set */
4
+ export declare function readStep(statement: ts.Statement): Step | undefined;
@@ -0,0 +1,81 @@
1
+ import ts from "typescript";
2
+ import { readLocator } from "./call.js";
3
+ import { readExpectation, readValue } from "./expectation.js";
4
+ import { readFiles } from "./file-entry.js";
5
+ import { readObject, stringOf } from "./literals.js";
6
+ /** The step a statement stands for, or undefined when it is outside the recorder's set */
7
+ export function readStep(statement) {
8
+ if (!ts.isExpressionStatement(statement) ||
9
+ !ts.isAwaitExpression(statement.expression))
10
+ return undefined;
11
+ const call = statement.expression.expression;
12
+ if (!ts.isCallExpression(call) ||
13
+ !ts.isPropertyAccessExpression(call.expression))
14
+ return undefined;
15
+ const { expression: target, name } = call.expression;
16
+ const args = call.arguments;
17
+ if (ts.isIdentifier(target) && target.text === "page") {
18
+ if (name.text === "goto" && args.length === 1) {
19
+ const url = stringOf(args[0]);
20
+ return url === undefined ? undefined : { kind: "goto", url };
21
+ }
22
+ if (name.text === "waitForNetworkIdle" && args.length === 0)
23
+ return { kind: "waitForNetworkIdle" };
24
+ if (name.text === "waitForTimeout" &&
25
+ args.length === 1 &&
26
+ ts.isNumericLiteral(args[0])) {
27
+ const ms = Number(args[0].text);
28
+ return Number.isInteger(ms) ? { kind: "waitForTimeout", ms } : undefined;
29
+ }
30
+ if (name.text === "screenshot" && args.length === 1) {
31
+ const options = readObject(args[0], ["name", "fullPage"]);
32
+ if (!options || typeof options.name !== "string")
33
+ return undefined;
34
+ if (options.fullPage !== undefined &&
35
+ typeof options.fullPage !== "boolean")
36
+ return undefined;
37
+ return {
38
+ kind: "screenshot",
39
+ name: options.name,
40
+ fullPage: options.fullPage === true,
41
+ };
42
+ }
43
+ return undefined;
44
+ }
45
+ // expect(locator).matcher(…) and expect(locator).not.matcher(…)
46
+ const negated = ts.isPropertyAccessExpression(target) && target.name.text === "not";
47
+ const subject = negated
48
+ ? target.expression
49
+ : target;
50
+ if (ts.isCallExpression(subject) &&
51
+ ts.isIdentifier(subject.expression) &&
52
+ subject.expression.text === "expect" &&
53
+ subject.arguments.length === 1) {
54
+ const locator = readLocator(subject.arguments[0]);
55
+ const expectation = readExpectation(name.text, args);
56
+ return locator && expectation
57
+ ? { kind: "expect", locator, not: negated, expectation }
58
+ : undefined;
59
+ }
60
+ const locator = readLocator(target);
61
+ if (!locator)
62
+ return undefined;
63
+ if (name.text === "click" && args.length === 0)
64
+ return { kind: "click", locator };
65
+ if (name.text === "press" && args.length === 1) {
66
+ const key = stringOf(args[0]);
67
+ return key === undefined ? undefined : { kind: "press", locator, key };
68
+ }
69
+ if (name.text === "fill" && args.length === 1) {
70
+ const value = readValue(args[0]);
71
+ return value ? { kind: "fill", locator, value } : undefined;
72
+ }
73
+ if ((name.text === "setInputFiles" || name.text === "dropFiles") &&
74
+ args.length === 1) {
75
+ const files = readFiles(args[0]);
76
+ return files
77
+ ? { kind: name.text, locator, files }
78
+ : undefined;
79
+ }
80
+ return undefined;
81
+ }