waygraph 0.15.52 → 0.15.54

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 (156) hide show
  1. package/README.md +8 -1
  2. package/dist/analysis/graph.d.ts +3 -0
  3. package/dist/analysis/map-check.d.ts +3 -2
  4. package/dist/auto-session/helpers.d.ts +10 -0
  5. package/dist/blocks-select.d.ts +3 -0
  6. package/dist/blocks-select.js +2 -0
  7. package/dist/cli/flags.js +3 -1
  8. package/dist/cli/usage.js +4 -1
  9. package/dist/cli.js +4 -1
  10. package/dist/commands/agent-dive-scaffold.d.ts +0 -20
  11. package/dist/commands/agent-dive-scaffold.js +3 -3
  12. package/dist/commands/init.js +1 -1
  13. package/dist/commands/upgrade.d.ts +1 -1
  14. package/dist/commands/upgrade.js +7 -1
  15. package/dist/config.d.ts +55 -19
  16. package/dist/config.js +55 -13
  17. package/dist/core/block.d.ts +7 -2
  18. package/dist/core/block.js +7 -3
  19. package/dist/core/blocks/api.d.ts +1 -0
  20. package/dist/core/blocks/method.d.ts +2 -0
  21. package/dist/core/blocks/method.js +1 -0
  22. package/dist/core/blocks/nav.d.ts +1 -0
  23. package/dist/core/blocks/page.d.ts +1 -0
  24. package/dist/core/branch-regression.d.ts +1 -0
  25. package/dist/core/compose.d.ts +1 -0
  26. package/dist/core/engine.d.ts +2 -0
  27. package/dist/core/engine.js +3 -1
  28. package/dist/core/flow.d.ts +11 -0
  29. package/dist/core/flow.js +32 -8
  30. package/dist/core/map-builder.d.ts +34 -1
  31. package/dist/core/map-builder.js +42 -4
  32. package/dist/core/run-graph.d.ts +2 -0
  33. package/dist/core/run-graph.js +1 -0
  34. package/dist/errors.d.ts +1 -0
  35. package/dist/extensions.d.ts +1 -0
  36. package/dist/extensions.js +1 -0
  37. package/dist/highlights/demo-pace.d.ts +4 -1
  38. package/dist/highlights/demo-pace.js +28 -3
  39. package/dist/highlights/device.d.ts +2 -0
  40. package/dist/highlights/device.js +2 -0
  41. package/dist/highlights/types.d.ts +6 -0
  42. package/dist/index.d.ts +2 -2
  43. package/dist/pilot-overlay/action-ring.d.ts +0 -9
  44. package/dist/pilot-overlay/action-ring.js +1 -1
  45. package/dist/pilot-overlay/panel.d.ts +2 -1
  46. package/dist/runner/interaction-patch.js +32 -1
  47. package/dist/runner/main.js +5 -2
  48. package/dist/runner/step-mode.js +10 -4
  49. package/dist/session/auto-explore-run.js +6 -0
  50. package/dist/session/auto-explore.d.ts +2 -1
  51. package/dist/session/auto-session-ipc.d.ts +1 -2
  52. package/dist/session/auto-session-ipc.js +1 -1
  53. package/dist/session/block-inject.d.ts +0 -13
  54. package/dist/session/block-inject.js +3 -3
  55. package/dist/session/overlay-beacon.d.ts +7 -0
  56. package/dist/session/overlay-beacon.js +5 -0
  57. package/dist/session/pilot.d.ts +1 -2
  58. package/dist/session/pilot.js +1 -2
  59. package/dist/traverse/coverage.d.ts +4 -4
  60. package/dist/traverse/coverage.js +4 -1
  61. package/dist/traverse/lease.d.ts +1 -0
  62. package/dist/traverse/lease.js +1 -0
  63. package/dist/traverse/run.d.ts +3 -0
  64. package/dist/types.d.ts +1 -1
  65. package/examples/routed-demo/src/flows/routed-demo-map.flow.ts +1 -1
  66. package/examples/routed-demo/src/flows/routed-demo.flow.ts +2 -2
  67. package/examples/saucedemo/README.md +11 -12
  68. package/examples/saucedemo/config/waygraph.config.ts +17 -0
  69. package/examples/saucedemo/docs/CONVENTION.md +1 -2
  70. package/examples/saucedemo/docs/HELPERS.md +1 -1
  71. package/examples/saucedemo/package-lock.json +10 -3
  72. package/examples/saucedemo/package.json +12 -29
  73. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +0 -49
  74. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +22 -25
  75. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.page.block.ts +0 -6
  76. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +38 -96
  77. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +1 -26
  78. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +1 -45
  79. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +14 -77
  80. package/examples/saucedemo/src/flows/api-demo.flow.ts +17 -11
  81. package/examples/saucedemo/src/flows/cart-bulk.flow.ts +19 -14
  82. package/examples/saucedemo/src/flows/cart-continue.flow.ts +10 -8
  83. package/examples/saucedemo/src/flows/cart-mem.flow.ts +7 -7
  84. package/examples/saucedemo/src/flows/catalog-logout.flow.ts +13 -8
  85. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +26 -48
  86. package/examples/saucedemo/src/flows/checkout.flow.ts +70 -86
  87. package/examples/saucedemo/src/flows/device-showcase.flow.ts +24 -25
  88. package/examples/saucedemo/src/flows/login.flow.ts +11 -18
  89. package/examples/saucedemo/src/flows/mem-stub-demo.flow.ts +9 -20
  90. package/examples/saucedemo/src/flows/shop.flow.ts +15 -20
  91. package/examples/saucedemo/src/flows/smoke.flow.ts +12 -27
  92. package/examples/saucedemo/src/flows/stub-on-error.flow.ts +24 -43
  93. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +26 -34
  94. package/examples/saucedemo/src/states/checkout.mem-stubs.ts +11 -0
  95. package/examples/saucedemo/src/stubs/device-showcase.ts +193 -0
  96. package/examples/saucedemo/src/stubs/login.ts +37 -0
  97. package/examples/saucedemo/tests/chain-flow-episodes.spec.ts +7 -9
  98. package/examples/saucedemo/tests/submit-login-branch.spec.ts +18 -5
  99. package/package.json +12 -5
  100. package/templates/quickstart/README.md +4 -7
  101. package/templates/quickstart/config/waygraph.config.ts +17 -0
  102. package/templates/quickstart/docs/CONVENTION.md +1 -2
  103. package/templates/quickstart/docs/HELPERS.md +1 -1
  104. package/templates/quickstart/package.json +14 -14
  105. package/templates/quickstart/src/blocks/SITE-MAP.md +14 -3
  106. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +0 -49
  107. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +22 -25
  108. package/templates/quickstart/src/blocks/saucedemo-web/inventory/inventory.page.block.ts +0 -6
  109. package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +38 -96
  110. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +1 -26
  111. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +1 -45
  112. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +14 -77
  113. package/templates/quickstart/src/flows/api-demo.flow.ts +43 -0
  114. package/templates/quickstart/src/flows/cart-bulk.flow.ts +19 -14
  115. package/templates/quickstart/src/flows/cart-continue.flow.ts +10 -8
  116. package/templates/quickstart/src/flows/cart-mem.flow.ts +7 -7
  117. package/templates/quickstart/src/flows/catalog-logout.flow.ts +13 -8
  118. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +26 -48
  119. package/templates/quickstart/src/flows/checkout.flow.ts +70 -63
  120. package/templates/quickstart/src/flows/device-showcase.flow.ts +24 -25
  121. package/templates/quickstart/src/flows/login.flow.ts +11 -18
  122. package/templates/quickstart/src/flows/mem-stub-demo.flow.ts +12 -0
  123. package/templates/quickstart/src/flows/shop.flow.ts +15 -20
  124. package/templates/quickstart/src/flows/smoke.flow.ts +12 -27
  125. package/templates/quickstart/src/flows/stub-on-error.flow.ts +31 -0
  126. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +26 -34
  127. package/templates/quickstart/src/states/checkout.mem-stubs.ts +11 -0
  128. package/templates/quickstart/src/stubs/device-showcase.ts +193 -0
  129. package/templates/quickstart/src/stubs/login.ts +37 -0
  130. package/templates/quickstart/tests/chain-flow.spec.ts +7 -10
  131. package/templates/scaffold/README.md +1 -1
  132. package/templates/scaffold/STRUCTURE.md +5 -2
  133. package/templates/scaffold/config/waygraph.config.ts +24 -5
  134. package/templates/scaffold/package.json +6 -6
  135. package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +36 -0
  136. package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +24 -0
  137. package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +34 -0
  138. package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +47 -0
  139. package/templates/scaffold/src/flows/README.md +35 -14
  140. package/templates/scaffold/src/flows/{shop.flow.ts → home/cart.flow.ts} +18 -17
  141. package/templates/scaffold/src/flows/{example.flow.ts → home/hello.flow.ts} +12 -13
  142. package/templates/scaffold/tests/example.spec.ts +5 -5
  143. package/templates/scaffold/tests/hoppscotch.spec.ts +3 -3
  144. package/templates/scaffold/tests/mail-verify.spec.ts +7 -7
  145. package/templates/skills/waygraph-convention.skill.md +14 -6
  146. package/examples/saucedemo/playwright.config.ts +0 -19
  147. package/examples/saucedemo/scripts/drive-auto-cli.mjs +0 -93
  148. package/examples/saucedemo/scripts/prove-add-to-cart-menu.mjs +0 -126
  149. package/examples/saucedemo/scripts/prove-auto-graph.mjs +0 -92
  150. package/examples/saucedemo/scripts/prove-wrong-login-runtime.mjs +0 -63
  151. package/templates/quickstart/playwright.config.ts +0 -14
  152. package/templates/quickstart/scripts/drive-auto-cli.mjs +0 -93
  153. package/templates/scaffold/src/flows/hoppscotch-auth.flow.ts +0 -35
  154. package/templates/scaffold/src/flows/hoppscotch-get.flow.ts +0 -23
  155. package/templates/scaffold/src/flows/hoppscotch-post.flow.ts +0 -33
  156. package/templates/scaffold/src/flows/mail-verify.flow.ts +0 -46
@@ -9,37 +9,20 @@
9
9
  "test": "waygraph test",
10
10
  "test:ui": "waygraph test ui",
11
11
  "flows": "waygraph list",
12
- "demo": "waygraph demo --blocks \"loginFlow then shopFlow\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"},\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}}\"",
13
- "demo:step": "waygraph demo --blocks \"loginFlow then shopFlow\" --title \"Shop demo\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"},\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}}\"",
14
- "demo:auto-next": "waygraph demo --blocks \"loginFlow then shopFlow\" --auto-next --title \"Product then cart\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"},\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}}\"",
15
- "auto": "waygraph auto",
16
- "auto:cli": "waygraph auto --cli --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\"",
17
- "prove:auto-cli": "node scripts/drive-auto-cli.mjs",
18
- "traverse": "waygraph traverse --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --max-steps 40",
19
- "traverse:parallel": "waygraph traverse --parallel 2 --session clone --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --max-steps 30",
20
- "traverse:coverage": "waygraph traverse --min-edge-coverage 10% --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --max-steps 40",
12
+ "check": "waygraph check",
21
13
  "graph": "waygraph graph",
22
14
  "graph:mermaid": "waygraph graph --mermaid",
23
- "demo:stub-on-error": "waygraph demo --blocks stubOnErrorFlow --auto-next --fast --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"locked_out_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --title \"stubOnError prove\"",
24
- "prove:login-branch": "node scripts/prove-wrong-login-runtime.mjs",
25
- "prove:graph": "node scripts/prove-auto-graph.mjs",
26
- "prove:add-to-cart-menu": "node scripts/prove-add-to-cart-menu.mjs",
27
- "prove": "npm run prove:login-branch && npm run prove:graph && npm run prove:add-to-cart-menu && npm test",
28
- "record": "waygraph run --blocks \"loginFlow then shopFlow({\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}})\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --video --no-step",
29
- "demo:device": "waygraph demo --blocks deviceShowcaseFlow --auto-next --title \"Device showcase\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"},\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}}\"",
30
- "demo:device:video": "waygraph demo --blocks deviceShowcaseFlow --auto-play-video --title \"Device showcase\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"},\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}}\" --video ./.tmp/device-showcase",
31
- "demo:bulk": "waygraph demo --blocks cartBulkFlow --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\"",
32
- "demo:bulk:auto-next": "waygraph demo --blocks cartBulkFlow --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --auto-next --title \"Cart bulk demo\"",
33
- "demo:bulk:video": "waygraph demo --blocks cartBulkFlow --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --auto-play-video --title \"Cart bulk demo\"",
34
- "demo:checkout": "waygraph demo --blocks checkoutFlow --auto-next --title \"Owner: Full Checkout\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"},\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}}\"",
35
- "demo:checkout:video": "waygraph demo --blocks checkoutFlow --auto-play-video --title \"Owner: Full Checkout\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"},\\\"saucedemo.selectedItem\\\":{\\\"id\\\":\\\"sauce-labs-backpack\\\",\\\"name\\\":\\\"Sauce Labs Backpack\\\"}}\"",
36
- "demo:api": "waygraph demo --blocks apiDemoFlow --auto-next --title \"FE + BE in one Flow\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\"",
37
- "demo:api:video": "waygraph demo --blocks apiDemoFlow --auto-play-video --title \"FE + BE in one Flow\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\"",
38
- "run:bulk": "waygraph run --blocks \"loginFlow then add-all-to-cart then remove-all-from-cart\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\"",
39
- "chain:bulk": "waygraph run --blocks \"loginFlow then add-all-to-cart then remove-all-from-cart\" --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --non-headless"
40
- },
41
- "waygraph": {
42
- "baseUrl": "https://www.saucedemo.com"
15
+ "auto": "waygraph auto",
16
+ "auto:cli": "waygraph auto --cli",
17
+ "demo": "waygraph demo --blocks \"loginFlow then shopFlow\"",
18
+ "demo:auto-next": "waygraph demo --blocks \"loginFlow then shopFlow\" --auto-next",
19
+ "demo:checkout": "waygraph demo --blocks checkoutFlow --auto-next",
20
+ "demo:device": "waygraph demo --blocks deviceShowcaseFlow --auto-next",
21
+ "demo:bulk": "waygraph demo --blocks cartBulkFlow --auto-next",
22
+ "demo:api": "waygraph demo --blocks apiDemoFlow --auto-next",
23
+ "demo:video": "waygraph demo --blocks deviceShowcaseFlow --auto-play-video",
24
+ "record": "waygraph run --blocks \"loginFlow then shopFlow\" --video --no-step",
25
+ "traverse": "waygraph traverse --max-steps 40"
43
26
  },
44
27
  "dependencies": {
45
28
  "waygraph": "file:../.."
@@ -1,7 +1,5 @@
1
1
  import { defineNavClickBlock, Trait } from "waygraph";
2
- import type { StubCtx } from "waygraph";
3
2
  import type { CartPage } from "../../../states/checkout.states.js";
4
- import { InventorySel } from "../inventory/methods/inventory-items.js";
5
3
  import { CartSel } from "./cart.sel.js";
6
4
 
7
5
  /**
@@ -17,51 +15,4 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
17
15
  checkpoint: "CartPage",
18
16
  click: CartSel.cartLink,
19
17
  verify: [Trait.url({ pathname: "/cart.html" })],
20
- stubBefore: (ctx: StubCtx) => {
21
- ctx.title("Open cart · landscape");
22
- ctx.landscape();
23
- ctx.todos(
24
- "saucedemo",
25
- [
26
- { id: "login-user", text: "Enter username", done: true },
27
- { id: "login-pass", text: "Enter password", done: true },
28
- { id: "login-submit", text: "Tap Login", done: true },
29
- { id: "cart-find", text: "Find the product", done: true },
30
- { id: "cart-add", text: "Add to cart", done: true },
31
- { id: "cart-open", text: "Open cart" },
32
- ],
33
- { title: "Device showcase", index: 5 },
34
- );
35
- ctx.zoom(1.4);
36
- ctx.zoomOut(false);
37
- ctx.highlights({
38
- cart: {
39
- selector: CartSel.cartLink,
40
- label: "Cart",
41
- detail: "tone: planned (purple) - open cart",
42
- tone: "planned",
43
- focus: true,
44
- zoomOut: false,
45
- followMouse: false,
46
- weight: "bold",
47
- gesture: "tap",
48
- },
49
- });
50
- },
51
- stubAfter: (ctx: StubCtx) => {
52
- ctx.banner("Your cart · desktop");
53
- ctx.clearDevice();
54
- ctx.hideTodos();
55
- ctx.zoomOut(true);
56
- ctx.ring("items", {
57
- selector: InventorySel.cartItem,
58
- label: "Cart items",
59
- detail: "tone: success (green) - product in cart",
60
- tone: "success",
61
- focus: true,
62
- zoomOut: true,
63
- followMouse: false,
64
- duration: true,
65
- });
66
- },
67
18
  });
@@ -1,5 +1,8 @@
1
- import { defineMethodBlock, checkpoint, Trait, narrate, type StubCtx } from "waygraph";
2
- import type { CheckoutOverviewPage, OrderComplete } from "../../../../states/checkout.states.js";
1
+ import { defineMethodBlock, checkpoint, Trait, narrate } from "waygraph";
2
+ import type {
3
+ CheckoutOverviewPage,
4
+ OrderComplete,
5
+ } from "../../../../states/checkout.states.js";
3
6
  import { CheckoutOverviewSel } from "./checkout-overview.sel.js";
4
7
 
5
8
  /**
@@ -10,34 +13,27 @@ import { CheckoutOverviewSel } from "./checkout-overview.sel.js";
10
13
  * Places the order on the overview page - form/finish click, not a NavBlock.
11
14
  * Stubs use open ctx lifecycles (preferred) - not static slot maps.
12
15
  */
13
- export const FinishOrderBlock = defineMethodBlock<CheckoutOverviewPage, OrderComplete>({
16
+ export const FinishOrderBlock = defineMethodBlock<
17
+ CheckoutOverviewPage,
18
+ OrderComplete
19
+ >({
14
20
  name: "finish-order",
15
- description: "Places the order and confirms the 'Thank you for your order!' completion page.",
21
+ description:
22
+ "Places the order and confirms the 'Thank you for your order!' completion page.",
16
23
  instruction: {
17
24
  async act(page) {
18
25
  const finishButton = page.locator(CheckoutOverviewSel.finishBtn);
19
- await narrate(finishButton, "Placing the order", () => finishButton.click());
26
+ await narrate(finishButton, "Placing the order", () =>
27
+ finishButton.click(),
28
+ );
20
29
  },
21
30
  resolve: () => checkpoint("OrderComplete"),
22
- verify: [Trait.text(CheckoutOverviewSel.completeHeader, "Thank you for your order!")],
23
- stubBefore: (ctx: StubCtx) => {
24
- ctx.ring("finish", {
25
- selector: CheckoutOverviewSel.finishBtn,
26
- label: "Finish",
27
- detail: "tone: warning (yellow) - place order",
28
- tone: "warning",
29
- weight: "bold",
30
- });
31
- },
32
- stubAfter: (ctx: StubCtx) => {
33
- ctx.ring("thanks", {
34
- selector: CheckoutOverviewSel.completeHeader,
35
- label: "Order confirmed",
36
- detail: "tone: success (green) - thank you!",
37
- tone: "success",
38
- duration: true,
39
- });
40
- },
31
+ verify: [
32
+ Trait.text(
33
+ CheckoutOverviewSel.completeHeader,
34
+ "Thank you for your order!",
35
+ ),
36
+ ],
41
37
  // Shown AFTER act (order already placed) - selectors must match the
42
38
  // completion page, not #finish on the overview.
43
39
  slides: [
@@ -50,7 +46,8 @@ export const FinishOrderBlock = defineMethodBlock<CheckoutOverviewPage, OrderCom
50
46
  },
51
47
  {
52
48
  caption: "Completion page is the durable proof",
53
- detail: "tone: success - the thank-you header is what verify asserts on.",
49
+ detail:
50
+ "tone: success - the thank-you header is what verify asserts on.",
54
51
  tag: "YAP",
55
52
  tone: "success",
56
53
  selector: CheckoutOverviewSel.completeHeader,
@@ -1,5 +1,4 @@
1
1
  import { definePageBlock, Trait } from "waygraph";
2
- import type { StubCtx } from "waygraph";
3
2
  import type { LoggedIn } from "../../../states/checkout.states.js";
4
3
  import { AddToCartBlock } from "./methods/add-to-cart.effect.block.js";
5
4
  import { RemoveFromCartBlock } from "./methods/remove-from-cart.effect.block.js";
@@ -28,11 +27,6 @@ export const InventoryPageBlock = definePageBlock<LoggedIn>({
28
27
  Trait.url({ pathname: "/inventory.html" }),
29
28
  Trait.visible(InventorySel.list),
30
29
  ],
31
- // Showcase responsive dashboard: tablet after mobile login (toast + lerp).
32
- stubBefore: (ctx: StubCtx) => {
33
- ctx.title("Inventory · tablet");
34
- ctx.device("tablet");
35
- },
36
30
  methods: {
37
31
  addToCart: () => AddToCartBlock,
38
32
  removeFromCart: () => RemoveFromCartBlock,
@@ -1,11 +1,13 @@
1
- import { defineEffectBlock, checkpoint, type Checkpoint, type WaygraphInstanceOption } from "waygraph";
2
- import type { StubCtx } from "waygraph";
1
+ import {
2
+ defineEffectBlock,
3
+ checkpoint,
4
+ type Checkpoint,
5
+ type WaygraphInstanceOption,
6
+ } from "waygraph";
3
7
  import type { ItemInCart } from "../../../../states/checkout.states.js";
4
8
  import { SelectedItem } from "../../../../states/checkout.mem-keys.js";
5
9
  import { collectAddableItems, InventorySel } from "./inventory-items.js";
6
10
 
7
- /** Showcase product for headed demos (matches shopFlow --data selectedItem). */
8
- const DEMO_PRODUCT_ID = "sauce-labs-backpack";
9
11
 
10
12
  /**
11
13
  * Kind: Effect
@@ -15,100 +17,40 @@ const DEMO_PRODUCT_ID = "sauce-labs-backpack";
15
17
  * Mutates cart for the mem-picked product. waygraph auto lists one
16
18
  * "Add ..." row per live Add button via instanceOptions.
17
19
  */
18
- export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>({
19
- name: "add-to-cart",
20
- description: "Adds the item named by mem (saucedemo.selectedItem) to the cart.",
21
- requires: [SelectedItem.key],
22
- instruction: {
23
- async act(page, _input, mem) {
24
- const { id } = mem.get(SelectedItem.key);
25
- await page.locator(InventorySel.addBtn(id)).click();
26
- },
27
- resolve: () => checkpoint("ItemInCart"),
28
- verify: [
29
- {
30
- name: "item-added-to-cart",
31
- async check(page, mem) {
32
- const { id } = mem.get(SelectedItem.key);
33
- return await page.locator(InventorySel.removeBtn(id)).first().isVisible();
34
- },
20
+ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
21
+ {
22
+ name: "add-to-cart",
23
+ description:
24
+ "Adds the item named by mem (saucedemo.selectedItem) to the cart.",
25
+ requires: [SelectedItem.key],
26
+ instruction: {
27
+ async act(page, _input, mem) {
28
+ const { id } = mem.get(SelectedItem.key);
29
+ await page.locator(InventorySel.addBtn(id)).click();
35
30
  },
36
- ],
37
- // Demo: zoom product -> add (stay zoomed), then badge after act.
38
- stubBefore: (ctx: StubCtx) => {
39
- ctx.title("Pick a product · tablet");
40
- ctx.device("tablet");
41
- ctx.portrait();
42
- ctx.todos(
43
- "saucedemo",
44
- [
45
- { id: "login-user", text: "Enter username", done: true },
46
- { id: "login-pass", text: "Enter password", done: true },
47
- { id: "login-submit", text: "Tap Login", done: true },
48
- { id: "cart-find", text: "Find the product" },
49
- { id: "cart-add", text: "Add to cart" },
50
- { id: "cart-open", text: "Open cart" },
51
- ],
52
- { title: "Device showcase", index: 3 },
53
- );
54
- ctx.zoom(1.35);
55
- ctx.zoomOut(false);
56
- ctx.highlights({
57
- product: {
58
- selector: InventorySel.title(DEMO_PRODUCT_ID),
59
- label: "Sauce Labs Backpack",
60
- detail: "tone: info (blue)",
61
- tone: "info",
62
- focus: true,
63
- zoom: 1.45,
64
- zoomOut: false,
31
+ resolve: () => checkpoint("ItemInCart"),
32
+ verify: [
33
+ {
34
+ name: "item-added-to-cart",
35
+ async check(page, mem) {
36
+ const { id } = mem.get(SelectedItem.key);
37
+ return await page
38
+ .locator(InventorySel.removeBtn(id))
39
+ .first()
40
+ .isVisible();
41
+ },
65
42
  },
66
- add: {
67
- selector: InventorySel.addBtn(DEMO_PRODUCT_ID),
68
- label: "Add to cart",
69
- detail: "tone: success (green)",
70
- tone: "success",
71
- focus: true,
72
- zoom: 1.5,
73
- zoomOut: false,
74
- weight: "bold",
75
- },
76
- });
43
+ ],
77
44
  },
78
- stubAfter: (ctx: StubCtx) => {
79
- ctx.banner("In the cart");
80
- ctx.todos(
81
- "saucedemo",
82
- [
83
- { id: "login-user", text: "Enter username", done: true },
84
- { id: "login-pass", text: "Enter password", done: true },
85
- { id: "login-submit", text: "Tap Login", done: true },
86
- { id: "cart-find", text: "Find the product", done: true },
87
- { id: "cart-add", text: "Add to cart" },
88
- { id: "cart-open", text: "Open cart" },
89
- ],
90
- { title: "Device showcase", index: 4 },
91
- );
92
- ctx.zoomOut(false);
93
- ctx.ring("badge", {
94
- selector: InventorySel.cartBadge,
95
- label: "Cart badge",
96
- detail: "tone: success (green) - count updated",
97
- tone: "success",
98
- focus: true,
99
- zoomOut: false,
100
- duration: true,
101
- });
45
+ async instanceOptions(page): Promise<readonly WaygraphInstanceOption[]> {
46
+ const items = await collectAddableItems(page);
47
+ return items.map((item) => ({
48
+ id: item.id,
49
+ label: `Add "${item.name}" to cart`,
50
+ key: SelectedItem.key,
51
+ value: item,
52
+ highlight: InventorySel.addBtn(item.id),
53
+ }));
102
54
  },
103
55
  },
104
- async instanceOptions(page): Promise<readonly WaygraphInstanceOption[]> {
105
- const items = await collectAddableItems(page);
106
- return items.map((item) => ({
107
- id: item.id,
108
- label: `Add "${item.name}" to cart`,
109
- key: SelectedItem.key,
110
- value: item,
111
- highlight: InventorySel.addBtn(item.id),
112
- }));
113
- },
114
- });
56
+ );
@@ -1,4 +1,4 @@
1
- import { defineMethodBlock, checkpoint, type StubCtx } from "waygraph";
1
+ import { defineMethodBlock, checkpoint } from "waygraph";
2
2
  import type { LoginPage } from "../../../states/checkout.states.js";
3
3
  import { LoginCreds } from "../../../states/checkout.mem-keys.js";
4
4
  import { LoginSel } from "./login.sel.js";
@@ -21,30 +21,5 @@ export const FillPasswordBlock = defineMethodBlock<LoginPage, LoginPage>({
21
21
  await page.locator(LoginSel.password).fill(password);
22
22
  },
23
23
  resolve: () => checkpoint("LoginPage"),
24
- stubBefore: (ctx: StubCtx) => {
25
- ctx.ring("password", {
26
- selector: LoginSel.password,
27
- label: "Password",
28
- detail: "tone: planned (purple)",
29
- todo: "login-pass",
30
- tone: "planned",
31
- focus: true,
32
- zoom: 1.4,
33
- });
34
- },
35
- stubAfter: (ctx: StubCtx) => {
36
- ctx.todos(
37
- "saucedemo",
38
- [
39
- { id: "login-user", text: "Enter username", done: true },
40
- { id: "login-pass", text: "Enter password", done: true },
41
- { id: "login-submit", text: "Tap Login" },
42
- { id: "cart-find", text: "Find the product" },
43
- { id: "cart-add", text: "Add to cart" },
44
- { id: "cart-open", text: "Open cart" },
45
- ],
46
- { title: "Device showcase", index: 2 },
47
- );
48
- },
49
24
  },
50
25
  });
@@ -1,4 +1,4 @@
1
- import { defineMethodBlock, checkpoint, type StubCtx } from "waygraph";
1
+ import { defineMethodBlock, checkpoint } from "waygraph";
2
2
  import type { LoginPage } from "../../../states/checkout.states.js";
3
3
  import { LoginCreds } from "../../../states/checkout.mem-keys.js";
4
4
  import { LoginSel } from "./login.sel.js";
@@ -23,49 +23,5 @@ export const FillUsernameBlock = defineMethodBlock<LoginPage, LoginPage>({
23
23
  await page.locator(LoginSel.username).fill(username);
24
24
  },
25
25
  resolve: () => checkpoint("LoginPage"),
26
- stubBefore: (ctx: StubCtx) => {
27
- ctx.title("Signing in · mobile");
28
- ctx.device("mobile");
29
- ctx.portrait();
30
- ctx.todos(
31
- "saucedemo",
32
- [
33
- { id: "login-user", text: "Enter username" },
34
- { id: "login-pass", text: "Enter password" },
35
- { id: "login-submit", text: "Tap Login" },
36
- { id: "cart-find", text: "Find the product" },
37
- { id: "cart-add", text: "Add to cart" },
38
- { id: "cart-open", text: "Open cart" },
39
- ],
40
- { title: "Device showcase", index: 0 },
41
- );
42
- ctx.zoom(1.35);
43
- const fromMem = ctx.mem?.get(LoginCreds.key)?.username;
44
- ctx.ring("username", {
45
- selector: LoginSel.username,
46
- label: "Username",
47
- detail: fromMem
48
- ? `tone: info (blue) - mem ${fromMem}`
49
- : "tone: info (blue)",
50
- tone: "info",
51
- todo: "login-user",
52
- focus: true,
53
- zoom: 1.4,
54
- });
55
- },
56
- stubAfter: (ctx: StubCtx) => {
57
- ctx.todos(
58
- "saucedemo",
59
- [
60
- { id: "login-user", text: "Enter username", done: true },
61
- { id: "login-pass", text: "Enter password" },
62
- { id: "login-submit", text: "Tap Login" },
63
- { id: "cart-find", text: "Find the product" },
64
- { id: "cart-add", text: "Add to cart" },
65
- { id: "cart-open", text: "Open cart" },
66
- ],
67
- { title: "Device showcase", index: 1 },
68
- );
69
- },
70
26
  },
71
27
  });
@@ -1,5 +1,8 @@
1
- import { defineMethodBlock, checkpoint, Trait, type StubCtx } from "waygraph";
2
- import type { LoginPage, LoginSubmitOutcome } from "../../../states/checkout.states.js";
1
+ import { defineMethodBlock, checkpoint, Trait } from "waygraph";
2
+ import type {
3
+ LoginPage,
4
+ LoginSubmitOutcome,
5
+ } from "../../../states/checkout.states.js";
3
6
  import { LoginSel } from "./login.sel.js";
4
7
 
5
8
  type LoginObserve = "success" | "failure";
@@ -14,7 +17,10 @@ type LoginObserve = "success" | "failure";
14
17
  * already did that). One Block, one action: submit.
15
18
  * Branches: good creds -> LoggedIn; bad/locked -> LoginPage with error banner.
16
19
  */
17
- export const SubmitLoginBlock = defineMethodBlock<LoginPage, LoginSubmitOutcome>({
20
+ export const SubmitLoginBlock = defineMethodBlock<
21
+ LoginPage,
22
+ LoginSubmitOutcome
23
+ >({
18
24
  name: "submit-login",
19
25
  description: "Submits the login form; stays on LoginPage when auth fails.",
20
26
  instruction: {
@@ -26,7 +32,9 @@ export const SubmitLoginBlock = defineMethodBlock<LoginPage, LoginSubmitOutcome>
26
32
  async observe(page): Promise<LoginObserve> {
27
33
  // Race inventory nav vs error banner - locked_out / wrong password show
28
34
  // Epic sadface immediately; do not burn the full inventory timeout.
29
- const inventory = page.waitForURL(/\/inventory\.html/, { timeout: 8_000 }).then(() => "success" as const);
35
+ const inventory = page
36
+ .waitForURL(/\/inventory\.html/, { timeout: 8_000 })
37
+ .then(() => "success" as const);
30
38
  const errorBanner = page
31
39
  .locator(LoginSel.errorBanner)
32
40
  .first()
@@ -38,82 +46,11 @@ export const SubmitLoginBlock = defineMethodBlock<LoginPage, LoginSubmitOutcome>
38
46
  return "failure";
39
47
  }
40
48
  },
41
- resolve: (observed) => checkpoint(observed === "success" ? "LoggedIn" : "LoginPage"),
49
+ resolve: (observed) =>
50
+ checkpoint(observed === "success" ? "LoggedIn" : "LoginPage"),
42
51
  verify: (out) =>
43
52
  out.__state === "LoggedIn"
44
53
  ? [Trait.url({ pathname: "/inventory.html" })]
45
54
  : [Trait.visible(LoginSel.errorBanner)],
46
- stubBefore: (ctx: StubCtx) => {
47
- ctx.highlights({
48
- submit: {
49
- selector: LoginSel.loginButton,
50
- label: "Login",
51
- detail: "tone: warning (yellow)",
52
- todo: "login-submit",
53
- tone: "warning",
54
- focus: true,
55
- zoom: 1.55,
56
- weight: "bold",
57
- gesture: "tap",
58
- },
59
- });
60
- },
61
- stubAfter: (ctx: StubCtx) => {
62
- if (ctx.out && ctx.out.__state === "LoginPage") {
63
- ctx.title("Login blocked");
64
- ctx.todos(
65
- "saucedemo",
66
- [
67
- { id: "login-user", text: "Enter username", done: true },
68
- { id: "login-pass", text: "Enter password", done: true },
69
- { id: "login-submit", text: "Click Login", done: true },
70
- ],
71
- { title: "Sign in", style: "checklist" },
72
- );
73
- ctx.ring("error", {
74
- selector: LoginSel.errorBanner,
75
- label: "Login error banner",
76
- detail: "tone: danger (red)",
77
- tone: "danger",
78
- focus: true,
79
- duration: true,
80
- });
81
- return;
82
- }
83
- ctx.banner("Inventory · landscape");
84
- ctx.landscape();
85
- ctx.todos(
86
- "saucedemo",
87
- [
88
- { id: "login-user", text: "Enter username", done: true },
89
- { id: "login-pass", text: "Enter password", done: true },
90
- { id: "login-submit", text: "Tap Login", done: true },
91
- { id: "cart-find", text: "Find the product" },
92
- { id: "cart-add", text: "Add to cart" },
93
- { id: "cart-open", text: "Open cart" },
94
- ],
95
- { title: "Device showcase", index: 3 },
96
- );
97
- ctx.ring("shelf", {
98
- selector: ".inventory_list",
99
- label: "Product shelf",
100
- detail: "tone: success (green) - ready to shop",
101
- tone: "success",
102
- focus: true,
103
- zoom: 1.25,
104
- duration: true,
105
- });
106
- },
107
- stubOnError: (ctx: StubCtx) => {
108
- ctx.title("Login failed");
109
- ctx.ring("error", {
110
- selector: LoginSel.errorBanner,
111
- label: "Login error banner",
112
- detail: "tone: danger (red)",
113
- tone: "danger",
114
- focus: true,
115
- duration: true,
116
- });
117
- },
118
55
  },
119
56
  });
@@ -1,5 +1,6 @@
1
- import { Engine, start, end, withTitle, chainFlow, checkpoint, defineApiBlock } from "waygraph";
1
+ import { checkpoint, defineApiBlock } from "waygraph";
2
2
  import type { Checkpoint, StubCtx } from "waygraph";
3
+ import { engine } from "../../config/waygraph.config.js";
3
4
  import { loginFlow } from "./login.flow.js";
4
5
 
5
6
  /**
@@ -17,21 +18,26 @@ import { loginFlow } from "./login.flow.js";
17
18
  export type BackendHealthy = Checkpoint<"BackendHealthy">;
18
19
  export type BackendDown = Checkpoint<"BackendDown">;
19
20
 
20
- const engine = new Engine();
21
-
22
21
  const CheckBackendHealth = defineApiBlock<any, BackendHealthy | BackendDown>({
23
22
  name: "check-backend-health",
24
- call: ({ request }) => request.get("https://jsonplaceholder.typicode.com/todos/1"),
25
- resolve: (result) => (result.ok ? checkpoint("BackendHealthy") : checkpoint("BackendDown")),
23
+ call: ({ request }) =>
24
+ request.get("https://jsonplaceholder.typicode.com/todos/1"),
25
+ resolve: (result) =>
26
+ result.ok ? checkpoint("BackendHealthy") : checkpoint("BackendDown"),
26
27
  stubBefore: (ctx: StubCtx<BackendHealthy | BackendDown>) => {
27
28
  ctx.title("Checking backend health · real HTTP request, no browser");
28
29
  },
29
30
  });
30
31
 
31
- export const apiDemoEpisode = withTitle(
32
- engine.defineFlow([start, CheckBackendHealth, end]),
33
- "Backend health check (defineApiBlock)",
34
- );
32
+ export const apiDemoEpisode = engine
33
+ .map()
34
+ .start()
35
+ .method(CheckBackendHealth)
36
+ .end();
35
37
 
36
- /** Full demo: Sign In (FE) -> backend health check (BE), one narrated Flow. */
37
- export const apiDemoFlow = chainFlow(loginFlow, apiDemoEpisode);
38
+ export const apiDemoFlow = engine
39
+ .map({ memStub: true })
40
+ .start()
41
+ .flow(loginFlow)
42
+ .flow(apiDemoEpisode)
43
+ .end();
@@ -1,19 +1,24 @@
1
- import { Engine, start, end, withTitle, chainFlow } from "waygraph";
1
+ import { engine } from "../../config/waygraph.config.js";
2
+ import "../states/checkout.mem-stubs.js";
2
3
  import { loginFlow } from "./login.flow.js";
3
4
  import { AddAllToCartBlock } from "../blocks/saucedemo-web/inventory/methods/add-all-to-cart.method.block.js";
4
5
  import { RemoveAllFromCartBlock } from "../blocks/saucedemo-web/inventory/methods/remove-all-from-cart.method.block.js";
5
6
 
6
- const engine = new Engine();
7
+ /** Add every item, then remove them all (the bulk Methods). */
8
+ export const cartBulkEpisode = engine
9
+ .map()
10
+ .start()
11
+ .method(
12
+ AddAllToCartBlock.stubBefore((ctx) =>
13
+ ctx.title("Cart bulk add/remove all"),
14
+ ),
15
+ )
16
+ .method(RemoveAllFromCartBlock)
17
+ .end();
7
18
 
8
- /**
9
- * Episode after login: drain every Add button, then every Remove button.
10
- * Proves bulk Methods + round-trip (LoggedIn <-> ItemInCart) without
11
- * instanceOptions. Pair with loginFlow via chainFlow for `waygraph demo`.
12
- */
13
- export const cartBulkEpisode = withTitle(
14
- engine.defineFlow([start, AddAllToCartBlock, RemoveAllFromCartBlock, end]),
15
- "Cart bulk add/remove all",
16
- );
17
-
18
- /** Full demo: Sign In -> Add all -> Remove all. */
19
- export const cartBulkFlow = chainFlow(loginFlow, cartBulkEpisode);
19
+ export const cartBulkFlow = engine
20
+ .map({ memStub: true })
21
+ .start()
22
+ .flow(loginFlow)
23
+ .flow(cartBulkEpisode)
24
+ .end();