@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,2 +1,2 @@
1
- import { type Handler } from "./support.js";
1
+ import type { Handler } from "./types.js";
2
2
  export declare const group: Handler;
@@ -1,4 +1,4 @@
1
- import type { Handler } from "./support.js";
2
- export type { Handler, HandlerRun, Outcome } from "./support.js";
1
+ import type { Handler } from "./types.js";
2
+ export type { Handler, HandlerRun, Outcome } from "./types.js";
3
3
  /** The kinds a run can execute, by kind: the closed ones and every built-in step */
4
4
  export declare const HANDLERS: Readonly<Record<string, Handler>>;
@@ -1,3 +1,3 @@
1
- import type { Handler } from "./support.js";
1
+ import type { Handler } from "./types.js";
2
2
  /** Its branches are walked by the walker; the node itself just opens them */
3
3
  export declare const parallel: Handler;
@@ -1,2 +1,2 @@
1
- import type { Handler } from "./support.js";
1
+ import type { Handler } from "./types.js";
2
2
  export declare const start: Handler;
@@ -1,30 +1,6 @@
1
- import { type AnyNodeDefinition, type ExpressionContext, type FlowFile, type FlowNodeModel } from "../../core/index.js";
2
- import type { RunEvent } from "../../../runner/reporter.js";
3
- import type { SupportedBrowser } from "../../../types/browser.js";
4
- import type { NodeResult, NodeTest } from "../events.js";
5
- /** What a handler can use of the run */
6
- export interface HandlerRun {
7
- flow: FlowFile;
8
- environment: {
9
- name: string;
10
- variables: Record<string, unknown>;
11
- };
12
- context: ExpressionContext;
13
- nodeTests: Record<string, NodeTest[]>;
14
- /** The browsers of the nodes that have their own (a Group node's group); the others use the project's */
15
- nodeBrowsers?: Record<string, SupportedBrowser[]>;
16
- signal: AbortSignal;
17
- runTests: (tests: NodeTest[], onEvent: (e: RunEvent) => void, browsers?: SupportedBrowser[]) => Promise<"passed" | "failed" | "cancelled">;
18
- sleep: (ms: number, signal: AbortSignal) => Promise<void>;
19
- onTestEvent: (nodeId: string, e: RunEvent) => void;
20
- /** Definitions of kinds core doesn't have (steps), for expression scopes */
21
- nodes: readonly AnyNodeDefinition[];
22
- log: (nodeId: string, level: "info" | "warn" | "error", text: string) => void;
23
- }
24
- /** What a handler decides; the walker adds timing */
25
- export type Outcome = Omit<NodeResult, "durationMs"> | "cancelled";
26
- /** Runs one kind of node. A `branches` node names the branch it takes in `branch` */
27
- export type Handler = (node: FlowNodeModel, run: HandlerRun) => Promise<Outcome>;
1
+ import { type FlowNodeModel } from "../../core/index.js";
2
+ import type { NodeTest } from "../events.js";
3
+ import type { HandlerRun, Outcome } from "./types.js";
28
4
  export declare const fail: (e: unknown) => Outcome;
29
5
  /** Runs `list` for the node and turns the result into an outcome */
30
6
  export declare function runNodeTests(node: FlowNodeModel, run: HandlerRun, list: NodeTest[]): Promise<Outcome>;
@@ -1,2 +1,2 @@
1
- import { type Handler } from "./support.js";
1
+ import type { Handler } from "./types.js";
2
2
  export declare const test: Handler;
@@ -0,0 +1,27 @@
1
+ import type { AnyNodeDefinition, ExpressionContext, FlowFile, FlowNodeModel } from "../../core/index.js";
2
+ import type { RunEvent } from "../../../runner/reporter.js";
3
+ import type { SupportedBrowser } from "../../../types/browser.js";
4
+ import type { NodeResult, NodeTest } from "../events.js";
5
+ /** What a handler can use of the run */
6
+ export interface HandlerRun {
7
+ flow: FlowFile;
8
+ environment: {
9
+ name: string;
10
+ variables: Record<string, unknown>;
11
+ };
12
+ context: ExpressionContext;
13
+ nodeTests: Record<string, NodeTest[]>;
14
+ /** The browsers of the nodes that have their own (a Group node's group); the others use the project's */
15
+ nodeBrowsers?: Record<string, SupportedBrowser[]>;
16
+ signal: AbortSignal;
17
+ runTests: (tests: NodeTest[], onEvent: (e: RunEvent) => void, browsers?: SupportedBrowser[]) => Promise<"passed" | "failed" | "cancelled">;
18
+ sleep: (ms: number, signal: AbortSignal) => Promise<void>;
19
+ onTestEvent: (nodeId: string, e: RunEvent) => void;
20
+ /** Definitions of kinds core doesn't have (steps), for expression scopes */
21
+ nodes: readonly AnyNodeDefinition[];
22
+ log: (nodeId: string, level: "info" | "warn" | "error", text: string) => void;
23
+ }
24
+ /** What a handler decides; the walker adds timing */
25
+ export type Outcome = Omit<NodeResult, "durationMs"> | "cancelled";
26
+ /** Runs one kind of node. A `branches` node names the branch it takes in `branch` */
27
+ export type Handler = (node: FlowNodeModel, run: HandlerRun) => Promise<Outcome>;
@@ -0,0 +1 @@
1
+ export {};
@@ -6,4 +6,4 @@ export { runFlow, type RunFlowOptions } from "./run.js";
6
6
  export { FLOW_SUFFIX, flowsDir, listFlows, readFlow, readFlowPath, readFlows, } from "./files.js";
7
7
  export * from "./report.js";
8
8
  export { projectSteps, type StepSet } from "../steps/set.js";
9
- export { FlowCheckError, checkFlowInProject, chooseEnvironment, runFlowInProject, type RunFlowInProjectOptions, } from "./project.js";
9
+ export { FlowCheckError, checkFlowInProject, chooseEnvironment, runFlowInProject, type FlowRunOptions, type RunFlowInProjectOptions, } from "./project.js";
@@ -0,0 +1,14 @@
1
+ import { type AnyNodeDefinition, type FlowFile } from "../core/index.js";
2
+ import type { Handler } from "./handlers/index.js";
3
+ /** The flow's shape can't be walked (no Start, a node reached twice, a branch that never meets): the run ends with this message */
4
+ export declare class ShapeError extends Error {
5
+ }
6
+ export declare const shapeError: (message: string) => ShapeError;
7
+ /** Waits `ms`, or rejects at once when `signal` aborts */
8
+ export declare const defaultSleep: (ms: number, signal: AbortSignal) => Promise<void>;
9
+ /** Whether a node of this kind has a list of branches that is only known at run time (a Parallel node) */
10
+ export declare const dynamicBranches: (def: AnyNodeDefinition) => boolean;
11
+ /** The definition of a kind: the project's own steps first, then the built-in kinds; undefined for a kind nobody knows */
12
+ export declare function definitionOf(nodes: readonly AnyNodeDefinition[], kind: string): AnyNodeDefinition | undefined;
13
+ /** Throws when the flow has a step or branching node whose kind has no handler: the run would not know what to do */
14
+ export declare function assertRunnable(flow: FlowFile, nodes: readonly AnyNodeDefinition[], handlers: Readonly<Record<string, Handler>>): void;
@@ -0,0 +1,44 @@
1
+ import { nodeDef, } from "../core/index.js";
2
+ /** The flow's shape can't be walked (no Start, a node reached twice, a branch that never meets): the run ends with this message */
3
+ export class ShapeError extends Error {
4
+ }
5
+ export const shapeError = (message) => new ShapeError(`This flow's shape can't run: ${message}`);
6
+ /** Waits `ms`, or rejects at once when `signal` aborts */
7
+ export const defaultSleep = (ms, signal) => new Promise((resolve, reject) => {
8
+ if (signal.aborted)
9
+ return reject(new Error("aborted"));
10
+ const onAbort = () => {
11
+ clearTimeout(timer);
12
+ reject(new Error("aborted"));
13
+ };
14
+ const timer = setTimeout(() => {
15
+ signal.removeEventListener("abort", onAbort);
16
+ resolve();
17
+ }, ms);
18
+ signal.addEventListener("abort", onAbort, { once: true });
19
+ });
20
+ /** Whether a node of this kind has a list of branches that is only known at run time (a Parallel node) */
21
+ export const dynamicBranches = (def) => def.shape === "branches" && !!def.branches && !Array.isArray(def.branches);
22
+ /** The definition of a kind: the project's own steps first, then the built-in kinds; undefined for a kind nobody knows */
23
+ export function definitionOf(nodes, kind) {
24
+ const own = nodes.find((d) => d.kind === kind);
25
+ if (own)
26
+ return own;
27
+ try {
28
+ return nodeDef(kind);
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ /** Throws when the flow has a step or branching node whose kind has no handler: the run would not know what to do */
35
+ export function assertRunnable(flow, nodes, handlers) {
36
+ const problems = flow.nodes
37
+ .map((n) => ({ n, def: definitionOf(nodes, n.kind) }))
38
+ .filter(({ n, def }) => def &&
39
+ (def.shape === "step" || def.shape === "branches") &&
40
+ !handlers[n.kind])
41
+ .map(({ def }) => `${def.label} can't run yet.`);
42
+ if (problems.length)
43
+ throw new Error(`This flow can't run: ${[...new Set(problems)].join(" ")}`);
44
+ }
@@ -1,8 +1,11 @@
1
1
  import { type FlowFile, type FlowProblem } from "../core/index.js";
2
+ import { type ResolvedGroup } from "../../runner/groups.js";
2
3
  import { type ProjectOptions } from "../../runner/project.js";
3
4
  import { runTests } from "../../runner/run.js";
5
+ import { WorkerPool, type PoolOptions } from "../../runner/worker-pool.js";
4
6
  import type { SamuraiTestConfig } from "../../types/config.js";
5
- import type { FlowEvent, FlowSummary } from "./events.js";
7
+ import type { FlowEvent, FlowSummary, NodeTest } from "./events.js";
8
+ import type { SupportedBrowser } from "../../types/browser.js";
6
9
  /** The flow has errors; nothing was run */
7
10
  export declare class FlowCheckError extends Error {
8
11
  readonly problems: FlowProblem[];
@@ -20,13 +23,39 @@ export interface RunFlowInProjectOptions extends ProjectOptions {
20
23
  /** `"fail"` (default): a flow with a node that can't run is refused. `"skip"`: those nodes are skipped. */
21
24
  unsupported?: "fail" | "skip";
22
25
  port?: number;
26
+ /**
27
+ * How many worker processes the tests of the whole run share: a number, or a percentage like `"50%"`. Unset
28
+ * takes `SAMURAI_WORKERS` (`workersEnv`), then the config's `workers`, then half the CPUs. 1 runs each Test
29
+ * and Group node through `runTests` in this process; more starts one pool that every node, and every branch
30
+ * of a Parallel node, takes its workers from
31
+ */
32
+ workers?: number | string;
33
+ /** The value of `SAMURAI_WORKERS`, when set */
34
+ workersEnv?: string;
35
+ /** @internal Replaces the worker pool (tests) */
36
+ createPool?: (options: PoolOptions) => Pick<WorkerPool, "run" | "dispose">;
23
37
  /** @internal Replaces the runner of each Test and Group node (tests) */
24
38
  testRunner?: typeof runTests;
25
39
  /** @internal */
26
40
  sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
27
41
  }
42
+ /** What `runFlowInProject` takes when the flow itself is given separately: everything but `flow` and `flowId` */
43
+ export type FlowRunOptions = Omit<RunFlowInProjectOptions, "flow" | "flowId">;
28
44
  /** The environment: the option, else the flow's `envDefault` (when the project has it), else the config's default, else the first, else "default" */
29
45
  export declare function chooseEnvironment(config: SamuraiTestConfig, flow: FlowFile, option?: string): string;
46
+ /** A test of the project as flows see it */
47
+ interface ProjectTest {
48
+ id: string;
49
+ file: string;
50
+ name: string;
51
+ /** The spec file relative to `srcDir`, with `/` separators */
52
+ relative: string;
53
+ }
54
+ /** The tests, and the browsers, of each Test and Group node of a flow; a Test node uses the project's browsers */
55
+ export declare function nodeTestsOf(flow: FlowFile, tests: Pick<ProjectTest, "id" | "file" | "name">[], groups: Pick<ResolvedGroup, "name" | "testIds" | "browsers">[]): {
56
+ nodeTests: Record<string, NodeTest[]>;
57
+ nodeBrowsers: Record<string, SupportedBrowser[]>;
58
+ };
30
59
  /**
31
60
  * Checks a flow against the project without running it: its tests, groups and environment
32
61
  * variables, and which kinds can run. Returns the flow (with keys) and every problem, warnings included.
@@ -38,6 +67,8 @@ export declare function checkFlowInProject(options: Pick<RunFlowInProjectOptions
38
67
  /**
39
68
  * Checks and runs a flow of a project: loads its config, lists its tests and groups, refuses to start
40
69
  * when `checkFlow` finds errors (`FlowCheckError`), then walks the flow; each Test and Group node runs
41
- * its tests through `runTests`, one after the other. One at a time per process.
70
+ * its tests through `runTests`, one after the other, or on one shared worker pool when `workers` is above 1.
71
+ * One at a time per process.
42
72
  */
43
73
  export declare function runFlowInProject(options: RunFlowInProjectOptions): Promise<FlowSummary>;
74
+ export {};
@@ -2,9 +2,15 @@ import path from "node:path";
2
2
  import { projectConfig } from "../../config/config.js";
3
3
  import { checkFlow, withKeys, } from "../core/index.js";
4
4
  import { DEFAULT_SRC_DIR, realPath, resolveGroups, testId, toPosix, } from "../../runner/groups.js";
5
+ import logger from "../../logger/index.js";
6
+ import { resolveWorkers } from "../../runner/workers-option.js";
5
7
  import { withProject } from "../../runner/project.js";
8
+ import { browsersFor } from "../../runner/browsers.js";
9
+ import { DEFAULT_PORT } from "../../runner/ports.js";
6
10
  import { runTests } from "../../runner/run.js";
7
11
  import TestRunner from "../../runner/test-runner.js";
12
+ import { WorkerPool } from "../../runner/worker-pool.js";
13
+ import { processWorkerFactory } from "../../runner/worker-process.js";
8
14
  import { readFlow } from "./files.js";
9
15
  import { projectSteps } from "../steps/set.js";
10
16
  import { HANDLERS } from "./handlers/index.js";
@@ -17,16 +23,66 @@ export class FlowCheckError extends Error {
17
23
  this.name = "FlowCheckError";
18
24
  }
19
25
  }
26
+ /** Whether the project's config defines an environment of this name */
27
+ function hasEnvironment(config, name) {
28
+ return Object.prototype.hasOwnProperty.call(config.environments ?? {}, name);
29
+ }
20
30
  /** The environment: the option, else the flow's `envDefault` (when the project has it), else the config's default, else the first, else "default" */
21
31
  export function chooseEnvironment(config, flow, option) {
22
- const names = Object.keys(config.environments ?? {});
23
32
  if (option)
24
33
  return option;
34
+ const names = Object.keys(config.environments ?? {});
25
35
  if (flow.envDefault && names.includes(flow.envDefault))
26
36
  return flow.envDefault;
27
37
  return config.defaultEnvironment ?? names[0] ?? "default";
28
38
  }
29
- /** What the project says about a flow: its environment, tests and groups */
39
+ /** The work items of a node: each test in each browser, the browsers one after the other */
40
+ function itemsOf(tests, browsers) {
41
+ return browsers
42
+ .flatMap((browser) => tests.map((t) => ({ file: t.file, testName: t.name, browser })))
43
+ .map((item, index) => ({ ...item, index }));
44
+ }
45
+ /** Runs the tests of a node on the pool and reports them as a run would */
46
+ async function runOnPool(pool, items, onEvent, signal) {
47
+ const results = await pool.run(items, {
48
+ signal,
49
+ onEvent: (event) => onEvent({
50
+ type: event.type,
51
+ name: event.item.testName,
52
+ file: event.item.file,
53
+ browser: event.item.browser,
54
+ ...(event.type === "test-end" && { result: event.result }),
55
+ }),
56
+ });
57
+ if (signal.aborted)
58
+ return "cancelled";
59
+ return results.every((r) => r.status === "success") ? "passed" : "failed";
60
+ }
61
+ /** The tests, and the browsers, of each Test and Group node of a flow; a Test node uses the project's browsers */
62
+ export function nodeTestsOf(flow, tests, groups) {
63
+ const byId = new Map(tests.map((t) => [t.id, t]));
64
+ const nodeTests = {};
65
+ const nodeBrowsers = {};
66
+ for (const node of flow.nodes) {
67
+ const group = node.kind === "group"
68
+ ? groups.find((g) => g.name === node.ref?.group)
69
+ : undefined;
70
+ const ids = node.kind === "test"
71
+ ? [node.ref?.testId ?? ""]
72
+ : node.kind === "group"
73
+ ? (group?.testIds ?? [])
74
+ : undefined;
75
+ if (!ids)
76
+ continue;
77
+ if (group)
78
+ nodeBrowsers[node.id] = group.browsers;
79
+ nodeTests[node.id] = ids.flatMap((id) => {
80
+ const t = byId.get(id);
81
+ return t ? [{ file: t.file, name: t.name }] : [];
82
+ });
83
+ }
84
+ return { nodeTests, nodeBrowsers };
85
+ }
30
86
  async function prepare(options) {
31
87
  const projectDir = path.resolve(options.projectDir ?? process.cwd());
32
88
  if (!options.flow && !options.flowId)
@@ -40,7 +96,7 @@ async function prepare(options) {
40
96
  const environment = {
41
97
  name,
42
98
  variables: {
43
- ...(Object.prototype.hasOwnProperty.call(config.environments ?? {}, name)
99
+ ...(hasEnvironment(config, name)
44
100
  ? config.environments[name].variables
45
101
  : {}),
46
102
  },
@@ -99,72 +155,98 @@ export async function checkFlowInProject(options) {
99
155
  /**
100
156
  * Checks and runs a flow of a project: loads its config, lists its tests and groups, refuses to start
101
157
  * when `checkFlow` finds errors (`FlowCheckError`), then walks the flow; each Test and Group node runs
102
- * its tests through `runTests`, one after the other. One at a time per process.
158
+ * its tests through `runTests`, one after the other, or on one shared worker pool when `workers` is above 1.
159
+ * One at a time per process.
103
160
  */
104
161
  export async function runFlowInProject(options) {
105
162
  const { projectDir, flow, unsupported, config, steps, name, environment, tests, groups, problems, } = await prepare(options);
106
163
  const errors = problems.filter((p) => p.level === "error");
107
164
  if (errors.length)
108
165
  throw new FlowCheckError(errors);
109
- // Phase 3: walk it
110
- const byId = new Map(tests.map((t) => [t.id, t]));
111
- const nodeTests = {};
112
- const nodeBrowsers = {};
113
- for (const node of flow.nodes) {
114
- const ids = node.kind === "test"
115
- ? [node.ref?.testId ?? ""]
116
- : node.kind === "group"
117
- ? (groups.find((g) => g.name === node.ref?.group)?.testIds ?? [])
118
- : undefined;
119
- if (!ids)
120
- continue;
121
- if (node.kind === "group") {
122
- const browsers = groups.find((g) => g.name === node.ref?.group)?.browsers;
123
- if (browsers)
124
- nodeBrowsers[node.id] = browsers;
125
- }
126
- nodeTests[node.id] = ids.flatMap((id) => {
127
- const t = byId.get(id);
128
- return t ? [{ file: t.file, name: t.name }] : [];
166
+ const { nodeTests, nodeBrowsers } = nodeTestsOf(flow, tests, groups);
167
+ const run = options.testRunner ?? runTests;
168
+ // An injected runner (tests) stays in this process unless the workers are asked for
169
+ const asked = options.workers !== undefined ||
170
+ Boolean(options.workersEnv) ||
171
+ config.workers !== undefined;
172
+ let workers = options.testRunner && options.workers === undefined
173
+ ? 1
174
+ : resolveWorkers({
175
+ flag: options.workers,
176
+ env: options.workersEnv,
177
+ config: config.workers,
129
178
  });
179
+ // A worker reads `samurai.config.ts` from the project folder: a config given as an object stays in this process
180
+ if (options.config !== undefined && !options.createPool && workers > 1) {
181
+ if (asked)
182
+ logger.warn("workers needs samurai.config.ts in the project folder; running on one worker");
183
+ workers = 1;
130
184
  }
131
- const run = options.testRunner ?? runTests;
132
- return runFlow({
133
- flow,
134
- nodeTests,
135
- nodeBrowsers,
136
- environment,
137
- ...(options.signal && { signal: options.signal }),
138
- ...(options.onEvent && { onEvent: options.onEvent }),
139
- ...(options.sleep && { sleep: options.sleep }),
140
- unsupported,
141
- handlers: { ...HANDLERS, ...steps.handlers },
142
- nodes: steps.nodes,
143
- runTests: async ({ tests, onEvent, signal, browsers }) => {
144
- const summary = await run({
185
+ const files = [
186
+ ...new Set(Object.values(nodeTests).flatMap((list) => list.map((t) => t.file))),
187
+ ];
188
+ // One pool for the whole run: the nodes, and the branches of a Parallel node, share its workers
189
+ const pool = workers > 1
190
+ ? (options.createPool ?? ((o) => new WorkerPool(o)))({
191
+ workers,
192
+ factory: processWorkerFactory(),
193
+ init: {
145
194
  projectDir,
146
- config,
147
195
  ...(options.dataDir !== undefined && { dataDir: options.dataDir }),
148
- // The environment the expressions see, whenever the project defines it
149
- ...(Object.prototype.hasOwnProperty.call(config.environments ?? {}, name)
150
- ? { environment: name }
151
- : {}),
152
- ...(options.headless !== undefined && { headless: options.headless }),
196
+ ...(hasEnvironment(config, name) && { environment: name }),
153
197
  ...(options.timeout !== undefined && { timeout: options.timeout }),
154
198
  ...(options.expectTimeout !== undefined && {
155
199
  expectTimeout: options.expectTimeout,
156
200
  }),
157
- ...(options.port !== undefined && { port: options.port }),
158
- ...(browsers && { browsers }),
159
- files: [...new Set(tests.map((t) => t.file))],
160
- testNames: tests.map((t) => t.name),
161
- reportPath: false,
162
- signal,
163
- onEvent,
164
- });
165
- if (signal.aborted)
166
- return "cancelled";
167
- return summary.status === "success" ? "passed" : "failed";
168
- },
169
- });
201
+ ...(options.headless !== undefined && {
202
+ headless: options.headless,
203
+ }),
204
+ files,
205
+ basePort: options.port ?? DEFAULT_PORT,
206
+ },
207
+ })
208
+ : undefined;
209
+ try {
210
+ return await runFlow({
211
+ flow,
212
+ nodeTests,
213
+ nodeBrowsers,
214
+ environment,
215
+ ...(options.signal && { signal: options.signal }),
216
+ ...(options.onEvent && { onEvent: options.onEvent }),
217
+ ...(options.sleep && { sleep: options.sleep }),
218
+ unsupported,
219
+ handlers: { ...HANDLERS, ...steps.handlers },
220
+ nodes: steps.nodes,
221
+ runTests: async ({ tests, onEvent, signal, browsers }) => {
222
+ if (pool)
223
+ return runOnPool(pool, itemsOf(tests, browsers ?? browsersFor({ project: config.browsers })), onEvent, signal);
224
+ const summary = await run({
225
+ projectDir,
226
+ config,
227
+ ...(options.dataDir !== undefined && { dataDir: options.dataDir }),
228
+ // The environment the expressions see, whenever the project defines it
229
+ ...(hasEnvironment(config, name) ? { environment: name } : {}),
230
+ ...(options.headless !== undefined && { headless: options.headless }),
231
+ ...(options.timeout !== undefined && { timeout: options.timeout }),
232
+ ...(options.expectTimeout !== undefined && {
233
+ expectTimeout: options.expectTimeout,
234
+ }),
235
+ ...(options.port !== undefined && { port: options.port }),
236
+ ...(browsers && { browsers }),
237
+ files: [...new Set(tests.map((t) => t.file))],
238
+ testNames: tests.map((t) => t.name),
239
+ reportPath: false,
240
+ signal,
241
+ onEvent,
242
+ });
243
+ if (signal.aborted)
244
+ return "cancelled";
245
+ return summary.status === "success" ? "passed" : "failed";
246
+ },
247
+ });
248
+ }
249
+ finally {
250
+ await pool?.dispose();
251
+ }
170
252
  }
@@ -39,9 +39,8 @@ export interface RunFlowOptions {
39
39
  nodes?: readonly AnyNodeDefinition[];
40
40
  }
41
41
  /**
42
- * Walks the flow from Start by the shape of each node's kind: a step runs and
43
- * follows its one way out; a node with a fixed list of branches follows the one its
44
- * handler names and skips the others; a node with a dynamic list walks every branch in
45
- * fill order; an end stops. Branches meet again at `meetOf`.
42
+ * Runs a flow: sets up what its nodes share (expression context, handler access to the tests), walks it from
43
+ * Start (see `FlowWalker`) and reports how it ended. A flow whose shape can't run ends with an `error`; a flow
44
+ * with a kind that has no handler throws before it starts, unless `unsupported` is `"skip"`.
46
45
  */
47
46
  export declare function runFlow(options: RunFlowOptions): Promise<FlowSummary>;