waygraph 0.12.24 → 0.12.26

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
@@ -1139,18 +1139,40 @@ async function ensureSelectorInView(page, selector) {
1139
1139
  const loc = page.locator(selector).first();
1140
1140
  await loc.scrollIntoViewIfNeeded().catch(() => {});
1141
1141
  await page
1142
- .evaluate((sel) => {
1142
+ .evaluate(async (sel) => {
1143
1143
  const el = document.querySelector(sel);
1144
1144
  if (!el) return;
1145
+ const waitScroll = (node, ms) =>
1146
+ new Promise((resolve) => {
1147
+ let done = false;
1148
+ const finish = () => {
1149
+ if (done) return;
1150
+ done = true;
1151
+ node.removeEventListener("scrollend", finish);
1152
+ clearTimeout(t);
1153
+ resolve();
1154
+ };
1155
+ const t = setTimeout(finish, ms);
1156
+ try {
1157
+ node.addEventListener("scrollend", finish, { once: true });
1158
+ } catch {
1159
+ /* scrollend unsupported - timeout only */
1160
+ }
1161
+ });
1145
1162
  try {
1146
- el.scrollIntoView({ block: "center", inline: "center", behavior: "instant" });
1163
+ el.scrollIntoView({ block: "center", inline: "center", behavior: "smooth" });
1147
1164
  } catch {
1148
1165
  try {
1149
- el.scrollIntoView(true);
1166
+ el.scrollIntoView({ block: "center", inline: "center" });
1150
1167
  } catch {
1151
- /* ignore */
1168
+ try {
1169
+ el.scrollIntoView(true);
1170
+ } catch {
1171
+ /* ignore */
1172
+ }
1152
1173
  }
1153
1174
  }
1175
+ await waitScroll(document.scrollingElement || document.documentElement, 500);
1154
1176
  let p = el.parentElement;
1155
1177
  while (p && p !== document.documentElement && p !== document.body) {
1156
1178
  const st = getComputedStyle(p);
@@ -1160,8 +1182,15 @@ async function ensureSelectorInView(page, selector) {
1160
1182
  const er = el.getBoundingClientRect();
1161
1183
  const pr = p.getBoundingClientRect();
1162
1184
  if (er.left < pr.left || er.right > pr.right || er.top < pr.top || er.bottom > pr.bottom) {
1163
- p.scrollLeft += er.left + er.width / 2 - (pr.left + pr.width / 2);
1164
- p.scrollTop += er.top + er.height / 2 - (pr.top + pr.height / 2);
1185
+ const left = p.scrollLeft + (er.left + er.width / 2 - (pr.left + pr.width / 2));
1186
+ const top = p.scrollTop + (er.top + er.height / 2 - (pr.top + pr.height / 2));
1187
+ try {
1188
+ p.scrollTo({ left, top, behavior: "smooth" });
1189
+ } catch {
1190
+ p.scrollLeft = left;
1191
+ p.scrollTop = top;
1192
+ }
1193
+ await waitScroll(p, 500);
1165
1194
  }
1166
1195
  }
1167
1196
  p = p.parentElement;
@@ -17,7 +17,7 @@
17
17
  }
18
18
  },
19
19
  "../..": {
20
- "version": "0.12.4",
20
+ "version": "0.12.24",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
23
  "tsx": "^4.0.0"
@@ -26,7 +26,7 @@
26
26
  "waygraph": "bin/waygraph"
27
27
  },
28
28
  "devDependencies": {
29
- "@playwright/test": "^1.55.0",
29
+ "@playwright/test": "^1.63.0",
30
30
  "@types/node": "^26.0.0",
31
31
  "typescript": "^5.6.0"
32
32
  },
@@ -45,20 +45,36 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
45
45
  out.__state === "LoggedIn"
46
46
  ? [Trait.url({ pathname: "/inventory.html" })]
47
47
  : [Trait.visible('[data-test="error"]')],
48
- stubBefore: {
49
- username: { selector: "#user-name", label: "Username" },
50
- password: { selector: "#password", label: "Password" },
51
- submit: { selector: "#login-button", label: "Login" },
48
+ // Open lifecycle: todos + zoom; stubAfter rings a lower inventory item
49
+ // so smooth auto-scroll is visible after login.
50
+ stubBefore: (ctx) => {
51
+ ctx.todos(["Enter username", "Enter password", "Click Login"]);
52
+ ctx.todoIndex(0);
53
+ ctx.zoom(1.25);
54
+ ctx.highlights({
55
+ username: { selector: "#user-name", label: "Username", zoom: 1.3 },
56
+ password: { selector: "#password", label: "Password", zoom: 1.3 },
57
+ submit: { selector: "#login-button", label: "Login", zoom: 1.45 },
58
+ });
52
59
  },
53
- stubAfter: {},
54
- // Fail-path rings (demo step throw only) - locked-out / wrong-password
55
- // bug-repro narration; success never shows these.
56
- stubOnError: {
57
- error: {
60
+ stubAfter: (ctx) => {
61
+ ctx.todos(["Enter username", "Enter password", "Click Login"]);
62
+ ctx.todoIndex(2);
63
+ // Inventory is tall - fleece sits lower; demo smooth-scrolls + zooms it.
64
+ ctx.ring("fleece", {
65
+ selector: '[data-test="add-to-cart-sauce-labs-fleece-jacket"]',
66
+ label: "Fleece jacket",
67
+ detail: "Smooth scroll + zoom",
68
+ zoom: 1.4,
69
+ duration: true,
70
+ });
71
+ },
72
+ stubOnError: (ctx) => {
73
+ ctx.ring("error", {
58
74
  selector: '[data-test="error"]',
59
75
  label: "Login error banner",
60
76
  duration: true,
61
- },
77
+ });
62
78
  },
63
79
  },
64
80
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.12.24",
3
+ "version": "0.12.26",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",
@@ -45,18 +45,33 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
45
45
  out.__state === "LoggedIn"
46
46
  ? [Trait.url({ pathname: "/inventory.html" })]
47
47
  : [Trait.visible('[data-test="error"]')],
48
- stubBefore: {
49
- username: { selector: "#user-name", label: "Username" },
50
- password: { selector: "#password", label: "Password" },
51
- submit: { selector: "#login-button", label: "Login" },
48
+ stubBefore: (ctx) => {
49
+ ctx.todos(["Enter username", "Enter password", "Click Login"]);
50
+ ctx.todoIndex(0);
51
+ ctx.zoom(1.25);
52
+ ctx.highlights({
53
+ username: { selector: "#user-name", label: "Username", zoom: 1.3 },
54
+ password: { selector: "#password", label: "Password", zoom: 1.3 },
55
+ submit: { selector: "#login-button", label: "Login", zoom: 1.45 },
56
+ });
52
57
  },
53
- stubAfter: {},
54
- stubOnError: {
55
- error: {
58
+ stubAfter: (ctx) => {
59
+ ctx.todos(["Enter username", "Enter password", "Click Login"]);
60
+ ctx.todoIndex(2);
61
+ ctx.ring("fleece", {
62
+ selector: '[data-test="add-to-cart-sauce-labs-fleece-jacket"]',
63
+ label: "Fleece jacket",
64
+ detail: "Smooth scroll + zoom",
65
+ zoom: 1.4,
66
+ duration: true,
67
+ });
68
+ },
69
+ stubOnError: (ctx) => {
70
+ ctx.ring("error", {
56
71
  selector: '[data-test="error"]',
57
72
  label: "Login error banner",
58
73
  duration: true,
59
- },
74
+ });
60
75
  },
61
76
  },
62
77
  });