waygraph 0.12.45 → 0.12.47

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.
package/dist/cli.js CHANGED
@@ -1202,9 +1202,9 @@ async function installOverlay(page, title) {
1202
1202
  }
1203
1203
  const rowId = t.id || t.name || "";
1204
1204
  return (
1205
- "<li class=\"" +
1205
+ '<li class="' +
1206
1206
  cls +
1207
- "\"" +
1207
+ '"' +
1208
1208
  attrId(rowId) +
1209
1209
  '><span class="wg-todo-mark">' +
1210
1210
  mark +
@@ -1724,6 +1724,22 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1724
1724
  // rings stay visible ~2s before the error panel (PIA stubOnError RFC).
1725
1725
  const defaultHoldMs = opts && opts.defaultHoldMs != null ? opts.defaultHoldMs : null;
1726
1726
  const pace = opts && opts.pace !== undefined ? opts.pace : undefined;
1727
+ let dock = opts && opts.todoDock;
1728
+ // Push stubAfter/before dock before the first ring so Method after-panels
1729
+ // show the updated checklist immediately (not only after the ring cycle).
1730
+ if (dock) {
1731
+ await page
1732
+ .evaluate((d) => {
1733
+ if (window.__wgSyncTodos) window.__wgSyncTodos({ sync: "set", dock: d });
1734
+ }, dock)
1735
+ .catch(() => {});
1736
+ } else if (opts && opts.todoSync === "clear") {
1737
+ await page
1738
+ .evaluate(() => {
1739
+ if (window.__wgSyncTodos) window.__wgSyncTodos({ sync: "clear" });
1740
+ })
1741
+ .catch(() => {});
1742
+ }
1727
1743
  for (let i = 0; i < list.length; i++) {
1728
1744
  const h = list[i];
1729
1745
  try {
@@ -1735,7 +1751,15 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1735
1751
  size: h.size,
1736
1752
  weight: h.weight,
1737
1753
  });
1738
- if ((opts && opts.todos && opts.todos.length) || (h.todos && h.todos.length)) {
1754
+ if (dock) {
1755
+ dock = advanceTodoDock(dock, i);
1756
+ if (opts && opts.todoDockRef) opts.todoDockRef.current = dock;
1757
+ await page
1758
+ .evaluate((d) => {
1759
+ if (window.__wgSyncTodos) window.__wgSyncTodos({ sync: "set", dock: d });
1760
+ }, dock)
1761
+ .catch(() => {});
1762
+ } else if ((opts && opts.todos && opts.todos.length) || (h.todos && h.todos.length)) {
1739
1763
  const rows =
1740
1764
  opts && opts.todos && opts.todos.length
1741
1765
  ? opts.todos
@@ -1743,7 +1767,7 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1743
1767
  await page
1744
1768
  .evaluate((todos) => {
1745
1769
  if (window.__wgSyncTodos) {
1746
- window.__wgSyncTodos(todos);
1770
+ window.__wgSyncTodos({ sync: "set", list: todos });
1747
1771
  return;
1748
1772
  }
1749
1773
  // Fallback for pages that never got installOverlay sync helper.
@@ -1836,6 +1860,9 @@ async function renderAfterStep(page, info) {
1836
1860
  await cycleHighlightRings(page, highlights, gatesFast, {
1837
1861
  pace: info.pace,
1838
1862
  stepLabel: miniStepLabel(info),
1863
+ todoDock: info.todoDock,
1864
+ todos: info.todos,
1865
+ todoSync: info.todoSync,
1839
1866
  });
1840
1867
  const afterPayload = { ...info, stepLabel: miniStepLabel(info) };
1841
1868
  await page
@@ -2246,6 +2273,46 @@ async function matchStubForLocator(page, locator, stubs) {
2246
2273
  return null;
2247
2274
  }
2248
2275
 
2276
+ /** 0-based stub index for Method fill/click todo advance; -1 if none. */
2277
+ async function matchStubIndexForLocator(page, locator, stubs) {
2278
+ if (!stubs || stubs.length === 0) return -1;
2279
+ const box = await locator.boundingBox().catch(() => null);
2280
+ if (!box) return -1;
2281
+ for (let i = 0; i < stubs.length; i++) {
2282
+ const s = stubs[i];
2283
+ try {
2284
+ const b = await page.locator(s.selector).first().boundingBox();
2285
+ if (
2286
+ b &&
2287
+ Math.abs(b.x - box.x) < 4 &&
2288
+ Math.abs(b.y - box.y) < 4 &&
2289
+ Math.abs(b.width - box.width) < 8
2290
+ ) {
2291
+ return i;
2292
+ }
2293
+ } catch {
2294
+ /* skip */
2295
+ }
2296
+ }
2297
+ return -1;
2298
+ }
2299
+
2300
+ /** Advance sequential todo dock to stubIndex and push to the page (Method act). */
2301
+ async function syncTodoDockAdvance(page, todoDockRef, stubIndex) {
2302
+ if (!todoDockRef || stubIndex < 0) return;
2303
+ const prev = todoDockRef.current;
2304
+ if (!prev) return;
2305
+ const advanced = advanceTodoDock(prev, stubIndex);
2306
+ todoDockRef.current = advanced;
2307
+ await page
2308
+ .evaluate((dock) => {
2309
+ if (window.__wgSyncTodos) {
2310
+ window.__wgSyncTodos({ sync: "set", dock });
2311
+ }
2312
+ }, advanced)
2313
+ .catch(() => {});
2314
+ }
2315
+
2249
2316
  async function captionForLocator(page, locator, stubs, fallback) {
2250
2317
  const matched = await matchStubForLocator(page, locator, stubs);
2251
2318
  if (matched) {
@@ -2619,7 +2686,7 @@ async function wasJustNarrated(page) {
2619
2686
  .catch(() => false);
2620
2687
  }
2621
2688
 
2622
- function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef) {
2689
+ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef, todoDockRef) {
2623
2690
  // Playwright's own slowMo ALREADY pauses after every single low-level
2624
2691
  // action it dispatches - and pressSequentially() fires one such action
2625
2692
  // PER CHARACTER. Also giving pressSequentially its own fixed delay
@@ -2675,6 +2742,8 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
2675
2742
  : "writing from mem";
2676
2743
  const stubs = (stubBeforeRef && stubBeforeRef.current) || [];
2677
2744
  const cap = await captionForLocator(page, this, stubs, fallback);
2745
+ const stubIdx = await matchStubIndexForLocator(page, this, stubs);
2746
+ await syncTodoDockAdvance(page, todoDockRef, stubIdx);
2678
2747
  await moveCursorTo(page, box, cursorMs(500));
2679
2748
  await showRing(page, box, cap.label, cap.tone, {
2680
2749
  size: cap.size,
@@ -2749,6 +2818,8 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
2749
2818
  }
2750
2819
  const stubs = (stubBeforeRef && stubBeforeRef.current) || [];
2751
2820
  const cap = await captionForLocator(page, this, stubs, fallback);
2821
+ const stubIdx = await matchStubIndexForLocator(page, this, stubs);
2822
+ await syncTodoDockAdvance(page, todoDockRef, stubIdx);
2752
2823
  clickTone = cap.tone;
2753
2824
  clickPoint = await moveCursorTo(page, box, cursorMs(600));
2754
2825
  await showRing(page, box, cap.label, cap.tone, {
@@ -2887,7 +2958,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
2887
2958
  const stepperMode = process.env.WAYGRAPH_STEPPER === "full" ? "full" : "carousel";
2888
2959
  const pacing = { gatesFast: demoFast, gatesSlow: false, skipTheater: false, demoPace: "normal" };
2889
2960
  const stubBeforeRef = { current: [] };
2890
- instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef);
2961
+ /** Live todo dock for Method fill/click advance (same carry as lastTodoDock). */
2962
+ const todoDockRef = { current: undefined };
2963
+ instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef, todoDockRef);
2891
2964
  // Force the panel checkbox from this process's flags/env at run start.
2892
2965
  // installOverlay only seeds localStorage when the key is null (so mid-run
2893
2966
  // checkbox clicks survive navigations). Without this, a prior --autoplay
@@ -3108,6 +3181,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
3108
3181
  todoPos: stubBeforePhase.todoPos,
3109
3182
  });
3110
3183
  lastTodoDock = appliedBefore.dock;
3184
+ todoDockRef.current = lastTodoDock;
3111
3185
  const stubBeforeTodos =
3112
3186
  (lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
3113
3187
  stubBeforePhase.todos ||
@@ -3341,6 +3415,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
3341
3415
  todoPos: afterPhase.todoPos,
3342
3416
  });
3343
3417
  lastTodoDock = appliedAfter.dock;
3418
+ todoDockRef.current = lastTodoDock;
3344
3419
  stubAfterTodos =
3345
3420
  (lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
3346
3421
  afterPhase.todos ||
@@ -17,7 +17,7 @@
17
17
  }
18
18
  },
19
19
  "../..": {
20
- "version": "0.12.43",
20
+ "version": "0.12.47",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
23
  "tsx": "^4.0.0"
@@ -1,4 +1,5 @@
1
1
  import { defineNavClickBlock, Trait } from "waygraph";
2
+ import type { StubCtx } from "waygraph";
2
3
  import type { CartPage } from "../../../states/checkout.states.js";
3
4
  import { InventorySel } from "../inventory/methods/inventory-items.js";
4
5
 
@@ -15,7 +16,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
15
16
  checkpoint: "CartPage",
16
17
  click: ".shopping_cart_link",
17
18
  verify: [Trait.url({ pathname: "/cart.html" })],
18
- stubBefore: (ctx) => {
19
+ stubBefore: (ctx: StubCtx) => {
19
20
  ctx.title("Open cart");
20
21
  ctx.todoId("saucedemo-cart");
21
22
  ctx.todos([
@@ -40,7 +41,7 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
40
41
  },
41
42
  });
42
43
  },
43
- stubAfter: (ctx) => {
44
+ stubAfter: (ctx: StubCtx) => {
44
45
  ctx.banner("Your cart");
45
46
  ctx.zoomOut(false);
46
47
  ctx.ring("items", {
@@ -1,4 +1,5 @@
1
1
  import { defineEffectBlock, checkpoint, type Checkpoint, type WaygraphInstanceOption } from "waygraph";
2
+ import type { StubCtx } from "waygraph";
2
3
  import type { ItemInCart } from "../../../../states/checkout.states.js";
3
4
  import { SelectedItem } from "../../../../states/checkout.mem-keys.js";
4
5
  import { collectAddableItems, InventorySel } from "./inventory-items.js";
@@ -34,7 +35,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
34
35
  },
35
36
  ],
36
37
  // Demo: zoom product -> add (stay zoomed), then badge after act.
37
- stubBefore: (ctx) => {
38
+ stubBefore: (ctx: StubCtx) => {
38
39
  ctx.title("Pick a product");
39
40
  ctx.todoId("saucedemo-cart");
40
41
  ctx.todos([
@@ -66,7 +67,7 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
66
67
  },
67
68
  });
68
69
  },
69
- stubAfter: (ctx) => {
70
+ stubAfter: (ctx: StubCtx) => {
70
71
  ctx.banner("In the cart");
71
72
  ctx.todoId("saucedemo-cart");
72
73
  ctx.todos([
@@ -1,4 +1,4 @@
1
- import { defineMethodBlock, checkpoint, Trait } from "waygraph";
1
+ import { defineMethodBlock, checkpoint, Trait, type StubCtx } from "waygraph";
2
2
  import type { LoginPage, LoginSubmitOutcome } from "../../../states/checkout.states.js";
3
3
  import { LoginCreds } from "../../../states/checkout.mem-keys.js";
4
4
 
@@ -9,8 +9,10 @@ type LoginObserve = "success" | "failure";
9
9
  * Helper: defineMethodBlock
10
10
  * Route: saucedemo-web/methods/ (page URL /)
11
11
  *
12
- * Fills credentials and submits - never page.goto (nav-login owns that).
12
+ * Fills credentials and submits - never page.goto (nav-login Page hub owns that).
13
13
  * Branches: good creds -> LoggedIn; bad/locked -> LoginPage with error banner.
14
+ * stubBefore/stubAfter drive the floating todo dock (Method fill/click advances
15
+ * todoIndex mid-act; stubAfter marks complete / fail).
14
16
  */
15
17
  export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOutcome>({
16
18
  name: "submit-login",
@@ -47,7 +49,7 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
47
49
  : [Trait.visible('[data-test="error"]')],
48
50
  // Open lifecycle: banner title, todos, Screen Studio camera zoom,
49
51
  // sequential highlight queue (appear / dwell / fade), focus + color.
50
- stubBefore: (ctx) => {
52
+ stubBefore: (ctx: StubCtx) => {
51
53
  ctx.title("Signing in");
52
54
  ctx.todoId("saucedemo-login");
53
55
  ctx.todos([
@@ -82,18 +84,18 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
82
84
  },
83
85
  });
84
86
  },
85
- stubAfter: (ctx) => {
87
+ stubAfter: (ctx: StubCtx) => {
86
88
  // Fail branch lands on LoginPage with Epic sadface - do not ring the
87
89
  // inventory shelf. Success (LoggedIn) hands off to shopFlow.
88
90
  if (ctx.out && ctx.out.__state === "LoginPage") {
89
91
  ctx.title("Login blocked");
90
92
  ctx.todoId("saucedemo-login");
91
93
  ctx.todos([
92
- { id: "login-user", text: "Enter username" },
93
- { id: "login-pass", text: "Enter password" },
94
- { id: "login-submit", text: "Click Login" },
94
+ { id: "login-user", text: "Enter username", done: true },
95
+ { id: "login-pass", text: "Enter password", done: true },
96
+ { id: "login-submit", text: "Click Login", done: true },
95
97
  ]);
96
- ctx.todoIndex(2);
98
+ ctx.todoStyle("checklist");
97
99
  ctx.ring("error", {
98
100
  selector: '[data-test="error"]',
99
101
  label: "Login error banner",
@@ -123,7 +125,7 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
123
125
  duration: true,
124
126
  });
125
127
  },
126
- stubOnError: (ctx) => {
128
+ stubOnError: (ctx: StubCtx) => {
127
129
  ctx.title("Login failed");
128
130
  ctx.ring("error", {
129
131
  selector: '[data-test="error"]',
@@ -1,17 +1,23 @@
1
- import { defineNavBlock, Trait } from "waygraph";
1
+ import { definePageBlock, Trait } from "waygraph";
2
2
  import type { LoginPage } from "../../states/checkout.states.js";
3
+ import { SubmitLoginActionBlock } from "./methods/submit-login.method.block.js";
3
4
 
4
5
  /**
5
- * Kind: Nav
6
- * Helper: defineNavBlock
6
+ * Kind: Page
7
+ * Helper: definePageBlock
7
8
  * Route: saucedemo-web/ (= /)
8
9
  *
9
- * page.goto to the login form lives here only. submit-login Action never navigates.
10
+ * Login screen hub. Deep-link via url; method submit-login hangs off this
11
+ * page (stubBefore/stubAfter todos live on the Method). Name stays
12
+ * `nav-login` so existing flows / chain specs keep working.
10
13
  */
11
- export const NavLoginBlock = defineNavBlock<LoginPage>({
14
+ export const NavLoginBlock = definePageBlock<LoginPage>({
12
15
  name: "nav-login",
13
16
  description: "Goes to the Swag Labs login page.",
14
17
  checkpoint: "LoginPage",
15
18
  url: "/",
16
19
  verify: [Trait.visible("#login-button")],
20
+ methods: {
21
+ submitLogin: () => SubmitLoginActionBlock,
22
+ },
17
23
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.12.45",
3
+ "version": "0.12.47",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",
@@ -17,7 +17,12 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
17
17
  verify: [Trait.url({ pathname: "/cart.html" })],
18
18
  stubBefore: (ctx) => {
19
19
  ctx.title("Open cart");
20
- ctx.todos(["Find the product", "Add to cart", "Open cart"]);
20
+ ctx.todoId("saucedemo-cart");
21
+ ctx.todos([
22
+ { id: "cart-find", text: "Find the product" },
23
+ { id: "cart-add", text: "Add to cart" },
24
+ { id: "cart-open", text: "Open cart" },
25
+ ]);
21
26
  ctx.todoIndex(2);
22
27
  ctx.zoom(1.4);
23
28
  ctx.zoomOut(false);
@@ -36,7 +36,12 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
36
36
  // Demo: zoom product -> add (stay zoomed), then badge after act.
37
37
  stubBefore: (ctx) => {
38
38
  ctx.title("Pick a product");
39
- ctx.todos(["Find the product", "Add to cart", "Open cart"]);
39
+ ctx.todoId("saucedemo-cart");
40
+ ctx.todos([
41
+ { id: "cart-find", text: "Find the product" },
42
+ { id: "cart-add", text: "Add to cart" },
43
+ { id: "cart-open", text: "Open cart" },
44
+ ]);
40
45
  ctx.todoIndex(0);
41
46
  ctx.zoom(1.35);
42
47
  ctx.zoomOut(false);
@@ -63,7 +68,12 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
63
68
  },
64
69
  stubAfter: (ctx) => {
65
70
  ctx.banner("In the cart");
66
- ctx.todos(["Find the product", "Add to cart", "Open cart"]);
71
+ ctx.todoId("saucedemo-cart");
72
+ ctx.todos([
73
+ { id: "cart-find", text: "Find the product" },
74
+ { id: "cart-add", text: "Add to cart" },
75
+ { id: "cart-open", text: "Open cart" },
76
+ ]);
67
77
  ctx.todoIndex(1);
68
78
  ctx.zoomOut(false);
69
79
  ctx.ring("badge", {
@@ -1,4 +1,4 @@
1
- import { defineMethodBlock, checkpoint, Trait } from "waygraph";
1
+ import { defineMethodBlock, checkpoint, Trait, type StubCtx } from "waygraph";
2
2
  import type { LoginPage, LoginSubmitOutcome } from "../../../states/checkout.states.js";
3
3
  import { LoginCreds } from "../../../states/checkout.mem-keys.js";
4
4
 
@@ -9,8 +9,10 @@ type LoginObserve = "success" | "failure";
9
9
  * Helper: defineMethodBlock
10
10
  * Route: saucedemo-web/methods/ (page URL /)
11
11
  *
12
- * Fills credentials and submits - never page.goto (nav-login owns that).
12
+ * Fills credentials and submits - never page.goto (nav-login Page hub owns that).
13
13
  * Branches: good creds -> LoggedIn; bad/locked -> LoginPage with error banner.
14
+ * stubBefore/stubAfter drive the floating todo dock (Method fill/click advances
15
+ * todoIndex mid-act; stubAfter marks complete / fail).
14
16
  */
15
17
  export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOutcome>({
16
18
  name: "submit-login",
@@ -47,9 +49,14 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
47
49
  : [Trait.visible('[data-test="error"]')],
48
50
  // Open lifecycle: banner title, todos, Screen Studio camera zoom,
49
51
  // sequential highlight queue (appear / dwell / fade), focus + color.
50
- stubBefore: (ctx) => {
52
+ stubBefore: (ctx: StubCtx) => {
51
53
  ctx.title("Signing in");
52
- ctx.todos(["Enter username", "Enter password", "Click Login"]);
54
+ ctx.todoId("saucedemo-login");
55
+ ctx.todos([
56
+ { id: "login-user", text: "Enter username" },
57
+ { id: "login-pass", text: "Enter password" },
58
+ { id: "login-submit", text: "Click Login" },
59
+ ]);
53
60
  ctx.todoIndex(0);
54
61
  ctx.zoom(1.35);
55
62
  ctx.highlights({
@@ -77,13 +84,18 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
77
84
  },
78
85
  });
79
86
  },
80
- stubAfter: (ctx) => {
87
+ stubAfter: (ctx: StubCtx) => {
81
88
  // Fail branch lands on LoginPage with Epic sadface - do not ring the
82
89
  // inventory shelf. Success (LoggedIn) hands off to shopFlow.
83
90
  if (ctx.out && ctx.out.__state === "LoginPage") {
84
91
  ctx.title("Login blocked");
85
- ctx.todos(["Enter username", "Enter password", "Click Login"]);
86
- ctx.todoIndex(2);
92
+ ctx.todoId("saucedemo-login");
93
+ ctx.todos([
94
+ { id: "login-user", text: "Enter username", done: true },
95
+ { id: "login-pass", text: "Enter password", done: true },
96
+ { id: "login-submit", text: "Click Login", done: true },
97
+ ]);
98
+ ctx.todoStyle("checklist");
87
99
  ctx.ring("error", {
88
100
  selector: '[data-test="error"]',
89
101
  label: "Login error banner",
@@ -95,7 +107,12 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
95
107
  return;
96
108
  }
97
109
  ctx.banner("Inventory");
98
- ctx.todos(["Enter username", "Enter password", "Click Login"]);
110
+ ctx.todoId("saucedemo-login");
111
+ ctx.todos([
112
+ { id: "login-user", text: "Enter username" },
113
+ { id: "login-pass", text: "Enter password" },
114
+ { id: "login-submit", text: "Click Login" },
115
+ ]);
99
116
  ctx.todoIndex(2);
100
117
  // Landed on inventory - hand off to shopFlow for product -> cart zoom.
101
118
  ctx.ring("shelf", {
@@ -108,7 +125,7 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
108
125
  duration: true,
109
126
  });
110
127
  },
111
- stubOnError: (ctx) => {
128
+ stubOnError: (ctx: StubCtx) => {
112
129
  ctx.title("Login failed");
113
130
  ctx.ring("error", {
114
131
  selector: '[data-test="error"]',
@@ -1,17 +1,23 @@
1
- import { defineNavBlock, Trait } from "waygraph";
1
+ import { definePageBlock, Trait } from "waygraph";
2
2
  import type { LoginPage } from "../../states/checkout.states.js";
3
+ import { SubmitLoginActionBlock } from "./methods/submit-login.method.block.js";
3
4
 
4
5
  /**
5
- * Kind: Nav
6
- * Helper: defineNavBlock
6
+ * Kind: Page
7
+ * Helper: definePageBlock
7
8
  * Route: saucedemo-web/ (= /)
8
9
  *
9
- * page.goto to the login form lives here only. submit-login Action never navigates.
10
+ * Login screen hub. Deep-link via url; method submit-login hangs off this
11
+ * page (stubBefore/stubAfter todos live on the Method). Name stays
12
+ * `nav-login` so existing flows / chain specs keep working.
10
13
  */
11
- export const NavLoginBlock = defineNavBlock<LoginPage>({
14
+ export const NavLoginBlock = definePageBlock<LoginPage>({
12
15
  name: "nav-login",
13
16
  description: "Goes to the Swag Labs login page.",
14
17
  checkpoint: "LoginPage",
15
18
  url: "/",
16
19
  verify: [Trait.visible("#login-button")],
20
+ methods: {
21
+ submitLogin: () => SubmitLoginActionBlock,
22
+ },
17
23
  });