waygraph 0.13.4 → 0.13.7
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 +1707 -139
- package/dist/highlights.d.ts +35 -3
- package/dist/highlights.js +55 -17
- package/dist/index.d.ts +1 -1
- package/examples/saucedemo/package.json +1 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +16 -13
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +24 -16
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +36 -26
- package/package.json +9 -2
- package/examples/saucedemo/.tmp/device-demo/device-desktop.png +0 -0
- package/examples/saucedemo/.tmp/device-demo/device-mobile.png +0 -0
- package/examples/saucedemo/.tmp/device-demo/device-tablet.png +0 -0
- package/examples/saucedemo/.tmp/device-demo/orientation-gestures.webm +0 -0
- package/examples/saucedemo/.tmp/device-demo/page@fe3808e393870b60560762cb58982a73.webm +0 -0
- package/examples/saucedemo/.tmp/device-showcase/device-showcase.webm +0 -0
- package/examples/saucedemo/.tmp/device-showcase/page@10a295d8efbf9eae139eb931ef00363c.webm +0 -0
- package/examples/saucedemo/.waygraph-traverse/leases.json +0 -29
package/dist/highlights.d.ts
CHANGED
|
@@ -88,6 +88,22 @@ export type TodoDockState = {
|
|
|
88
88
|
groups: WaygraphTodoGroup[];
|
|
89
89
|
pos?: "left" | "right";
|
|
90
90
|
};
|
|
91
|
+
/** Options for compact {@link StubCtx.todos}(`id`, items, opts). */
|
|
92
|
+
export type WaygraphTodosOpts = {
|
|
93
|
+
/** Dock heading. */
|
|
94
|
+
title?: string;
|
|
95
|
+
/** Current item index (0-based). */
|
|
96
|
+
index?: number;
|
|
97
|
+
/** `sequential` | `checklist` | `bullets`. */
|
|
98
|
+
style?: TodoListStyle;
|
|
99
|
+
/** Dock side. */
|
|
100
|
+
pos?: "left" | "right";
|
|
101
|
+
/**
|
|
102
|
+
* Keep other docks with different ids (multi-todo). Default false —
|
|
103
|
+
* one checklist at a time (setting todos replaces the previous dock).
|
|
104
|
+
*/
|
|
105
|
+
parallel?: boolean;
|
|
106
|
+
};
|
|
91
107
|
/** Episode-level fixtures authored inside stubBefore/After/OnError(ctx). */
|
|
92
108
|
export type StubPhaseFixtures = {
|
|
93
109
|
todos?: readonly WaygraphTodoInput[];
|
|
@@ -242,16 +258,24 @@ export interface WaygraphHighlightStub extends FixtureDurationFields {
|
|
|
242
258
|
* for authored stub queues; set false to keep cursor hidden).
|
|
243
259
|
*/
|
|
244
260
|
cursor?: boolean;
|
|
261
|
+
/**
|
|
262
|
+
* Optional link to a todo item id. When set, Method fill/click advances that
|
|
263
|
+
* todo row instead of relying on stub array index (avoids password/submit/username
|
|
264
|
+
* alphabetical reorder bugs).
|
|
265
|
+
*/
|
|
266
|
+
todo?: string;
|
|
267
|
+
/** Slot key from stubBefore/After map (e.g. "username") - set by resolver. */
|
|
268
|
+
slotId?: string;
|
|
245
269
|
/**
|
|
246
270
|
* Pointer feel when touchMode is on (0.13+):
|
|
247
271
|
* - `tap` - short press via touchscreen (default in touch mode)
|
|
248
272
|
* - `hold` - long-press (~600ms) then release
|
|
249
273
|
* - `click` - mouse click even in touch mode
|
|
250
274
|
*/
|
|
251
|
-
gesture?: "tap" | "hold" | "click";
|
|
275
|
+
gesture?: "tap" | "hold" | "click" | "swipe";
|
|
252
276
|
}
|
|
253
277
|
/** Flow fixture patch: label required; selector optional (inherits from stub). */
|
|
254
|
-
export type WaygraphHighlightFixture = Partial<Pick<WaygraphHighlightStub, "selector">> & Required<Pick<WaygraphHighlightStub, "label">> & Pick<WaygraphHighlightStub, "detail" | "tag" | "tone" | "size" | "weight" | "zoom" | "zoomOut" | "focus" | "followMouse" | "follow" | "color" | "cursor"> & FixtureDurationFields;
|
|
278
|
+
export type WaygraphHighlightFixture = Partial<Pick<WaygraphHighlightStub, "selector">> & Required<Pick<WaygraphHighlightStub, "label">> & Pick<WaygraphHighlightStub, "detail" | "tag" | "tone" | "size" | "weight" | "zoom" | "zoomOut" | "focus" | "followMouse" | "follow" | "color" | "cursor" | "todo" | "gesture"> & FixtureDurationFields;
|
|
255
279
|
export type HighlightStubPhase = Record<string, WaygraphHighlightStub>;
|
|
256
280
|
/**
|
|
257
281
|
* Open stub lifecycle context. Authors set rings + episode fixtures freely —
|
|
@@ -283,8 +307,14 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
283
307
|
highlights(slots: HighlightStubPhase): void;
|
|
284
308
|
/** Set / overwrite one named ring. */
|
|
285
309
|
ring(id: string, stub: WaygraphHighlightStub): void;
|
|
286
|
-
/**
|
|
310
|
+
/**
|
|
311
|
+
* Floating checklist. Prefer the compact form:
|
|
312
|
+
* ctx.todos("login", ["Enter email", "Enter password"], { title: "Sign in", index: 0 })
|
|
313
|
+
* Legacy: ctx.todos([...]); ctx.todoId(...); ctx.todoIndex(...); …
|
|
314
|
+
* Default = one dock (setting todos replaces). Pass `{ parallel: true }` to keep others.
|
|
315
|
+
*/
|
|
287
316
|
todos(items: readonly WaygraphTodoInput[]): void;
|
|
317
|
+
todos(id: string, items: readonly WaygraphTodoInput[], opts?: WaygraphTodosOpts): void;
|
|
288
318
|
/** Current checklist index (bump for sequential plans). */
|
|
289
319
|
todoIndex(n: number): void;
|
|
290
320
|
/** Heading above the single {@link StubCtx.todos} list. */
|
|
@@ -392,6 +422,8 @@ export type StubPhaseResult = {
|
|
|
392
422
|
zoomOut?: boolean;
|
|
393
423
|
title?: string;
|
|
394
424
|
todoPos?: "left" | "right";
|
|
425
|
+
/** When true, keep other docks (multi-todo). Default false = replace. */
|
|
426
|
+
todoParallel?: boolean;
|
|
395
427
|
};
|
|
396
428
|
/**
|
|
397
429
|
* One yap/slide in a multi-step demo caption sequence.
|
package/dist/highlights.js
CHANGED
|
@@ -475,12 +475,19 @@ function pickFxFields(base, patch) {
|
|
|
475
475
|
const focus = patch?.focus !== undefined ? patch.focus : base?.focus;
|
|
476
476
|
const color = patch?.color !== undefined ? patch.color : base?.color;
|
|
477
477
|
const cursor = patch?.cursor !== undefined ? patch.cursor : base?.cursor;
|
|
478
|
+
const todo = patch?.todo !== undefined ? patch.todo : base?.todo;
|
|
479
|
+
const gesture = patch?.gesture !== undefined ? patch.gesture : base?.gesture;
|
|
478
480
|
if (focus !== undefined)
|
|
479
481
|
out.focus = !!focus;
|
|
480
482
|
if (color !== undefined && String(color).trim())
|
|
481
483
|
out.color = String(color).trim();
|
|
482
484
|
if (cursor !== undefined)
|
|
483
485
|
out.cursor = !!cursor;
|
|
486
|
+
if (todo !== undefined && String(todo).trim())
|
|
487
|
+
out.todo = String(todo).trim();
|
|
488
|
+
if (gesture === "tap" || gesture === "hold" || gesture === "click" || gesture === "swipe") {
|
|
489
|
+
out.gesture = gesture;
|
|
490
|
+
}
|
|
484
491
|
return out;
|
|
485
492
|
}
|
|
486
493
|
function mergeSlot(base, patch) {
|
|
@@ -634,9 +641,7 @@ export function buildTodoDock(opts) {
|
|
|
634
641
|
if (!items.length)
|
|
635
642
|
return undefined;
|
|
636
643
|
const group = { style, items };
|
|
637
|
-
|
|
638
|
-
group.title = String(opts.todoTitle).trim();
|
|
639
|
-
}
|
|
644
|
+
// Title once on the dock - not also on the lone group (avoids duplicate headings).
|
|
640
645
|
const dock = { style, groups: [group] };
|
|
641
646
|
if (opts.todoTitle && String(opts.todoTitle).trim()) {
|
|
642
647
|
dock.title = String(opts.todoTitle).trim();
|
|
@@ -738,13 +743,38 @@ function createStubCtx(bag, opts) {
|
|
|
738
743
|
ring(id, stub) {
|
|
739
744
|
bag.highlights = { ...bag.highlights, [id]: stub };
|
|
740
745
|
},
|
|
741
|
-
todos(
|
|
746
|
+
todos(a, b, c) {
|
|
742
747
|
bag.todoTouched = true;
|
|
743
748
|
bag.hideTodos = false;
|
|
744
|
-
bag.todos = items;
|
|
745
749
|
delete bag.todoGroups;
|
|
746
|
-
|
|
747
|
-
|
|
750
|
+
if (typeof a === "string") {
|
|
751
|
+
const id = String(a).trim();
|
|
752
|
+
if (id)
|
|
753
|
+
bag.todoId = id;
|
|
754
|
+
else
|
|
755
|
+
delete bag.todoId;
|
|
756
|
+
const items = (Array.isArray(b) ? b : []);
|
|
757
|
+
bag.todos = items;
|
|
758
|
+
const opts = (c && typeof c === "object" ? c : {});
|
|
759
|
+
if (opts.title != null)
|
|
760
|
+
bag.todoTitle = String(opts.title);
|
|
761
|
+
if (opts.index != null)
|
|
762
|
+
bag.todoIndex = Number(opts.index);
|
|
763
|
+
const st = normalizeTodoStyle(opts.style);
|
|
764
|
+
if (st)
|
|
765
|
+
bag.todoStyle = st;
|
|
766
|
+
const pos = normalizeTodoPos(opts.pos);
|
|
767
|
+
if (pos)
|
|
768
|
+
bag.todoPos = pos;
|
|
769
|
+
bag.todoParallel = opts.parallel === true;
|
|
770
|
+
if (!items.length)
|
|
771
|
+
bag.hideTodos = true;
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
bag.todos = a;
|
|
775
|
+
// Legacy list-only form: leave parallel unset (CLI treats as replace).
|
|
776
|
+
delete bag.todoParallel;
|
|
777
|
+
if (!a || !a.length)
|
|
748
778
|
bag.hideTodos = true;
|
|
749
779
|
},
|
|
750
780
|
todoIndex(n) {
|
|
@@ -977,19 +1007,26 @@ function applyDefaultZoomOut(slots, defaultZoomOut) {
|
|
|
977
1007
|
return slots.map((h) => h.zoomOut !== undefined ? h : { ...h, zoomOut: defaultZoomOut });
|
|
978
1008
|
}
|
|
979
1009
|
function mergePhaseMaps(phaseMap, fixturePhase) {
|
|
980
|
-
const
|
|
981
|
-
const
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1010
|
+
const phaseKeys = Object.keys(phaseMap);
|
|
1011
|
+
const fixtureKeys = Object.keys(fixturePhase || {});
|
|
1012
|
+
const allKeys = [...new Set([...phaseKeys, ...fixtureKeys])];
|
|
1013
|
+
const allNumeric = allKeys.length > 0 && allKeys.every((k) => Number.isFinite(Number(k)) && String(Number(k)) === k);
|
|
1014
|
+
// Numeric stub ids (legacy "0","1") sort by number. Named stubs keep author
|
|
1015
|
+
// insertion order - localeCompare used to reorder password/submit/username
|
|
1016
|
+
// and advance the wrong todo index on fill.
|
|
1017
|
+
const ordered = allNumeric
|
|
1018
|
+
? [...allKeys].sort((a, b) => Number(a) - Number(b))
|
|
1019
|
+
: [...phaseKeys, ...fixtureKeys.filter((k) => !phaseKeys.includes(k))];
|
|
988
1020
|
const resolved = [];
|
|
989
1021
|
for (const id of ordered) {
|
|
990
1022
|
const merged = mergeSlot(phaseMap[id], fixturePhase?.[id]);
|
|
991
|
-
if (merged)
|
|
992
|
-
resolved.push(
|
|
1023
|
+
if (merged) {
|
|
1024
|
+
resolved.push({
|
|
1025
|
+
...merged,
|
|
1026
|
+
slotId: id,
|
|
1027
|
+
...(merged.todo ? { todo: merged.todo } : {}),
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
993
1030
|
}
|
|
994
1031
|
return resolved;
|
|
995
1032
|
}
|
|
@@ -1138,6 +1175,7 @@ export async function runStubPhase(block, phase, opts) {
|
|
|
1138
1175
|
...(bag.zoomOut !== undefined ? { zoomOut: bag.zoomOut } : {}),
|
|
1139
1176
|
...(bag.title !== undefined && bag.title !== "" ? { title: bag.title } : {}),
|
|
1140
1177
|
...(bag.todoPos !== undefined ? { todoPos: bag.todoPos } : {}),
|
|
1178
|
+
...(bag.todoParallel === true ? { todoParallel: true } : {}),
|
|
1141
1179
|
};
|
|
1142
1180
|
}
|
|
1143
1181
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
|
|
|
4
4
|
export { Trait, urlMatches, textEquals, visible } from "./trait.js";
|
|
5
5
|
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, chainFlow, } from "./engine.js";
|
|
6
6
|
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
7
|
-
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, DevicePreset, DeviceViewport, DeviceState, DeviceOrientation, } from "./highlights.js";
|
|
7
|
+
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, WaygraphTodosOpts, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, DevicePreset, DeviceViewport, DeviceState, DeviceOrientation, } from "./highlights.js";
|
|
8
8
|
export type { Flow, EngineConfig, ComposedBlock, FastForwardComposedBlock, RunGraphOptions, BlockInfo, NavBlock, NavBlockOptions, NavClickBlock, NavClickBlockOptions, MethodBlock, ActionBlock, PageBlock, PageBlockOptions, PageMethodEntry, EffectBlock, MemEffectBlock, MemNavBlock, } from "./engine.js";
|
|
9
9
|
export type { ActionPage } from "./types.js";
|
|
10
10
|
export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"prove": "npm run prove:login-branch && npm run prove:graph && npm run prove:add-to-cart-menu && npm test",
|
|
27
27
|
"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",
|
|
28
28
|
"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\\\"}}\"",
|
|
29
|
-
"demo:device:video": "waygraph demo --blocks deviceShowcaseFlow --auto-
|
|
29
|
+
"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",
|
|
30
30
|
"demo:bulk": "waygraph demo --blocks cartBulkFlow --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\"",
|
|
31
31
|
"demo:bulk:auto-next": "waygraph demo --blocks cartBulkFlow --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --auto-next --title \"Cart bulk demo\"",
|
|
32
32
|
"demo:bulk:video": "waygraph demo --blocks cartBulkFlow --data \"{\\\"saucedemo.credentials\\\":{\\\"username\\\":\\\"standard_user\\\",\\\"password\\\":\\\"secret_sauce\\\"}}\" --auto-play-video --title \"Cart bulk demo\"",
|
|
@@ -18,25 +18,28 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
|
|
|
18
18
|
verify: [Trait.url({ pathname: "/cart.html" })],
|
|
19
19
|
stubBefore: (ctx: StubCtx) => {
|
|
20
20
|
ctx.title("Open cart · landscape");
|
|
21
|
-
// Device showcase: rotate tablet to landscape before the cart tap.
|
|
22
21
|
ctx.landscape();
|
|
23
|
-
ctx.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
ctx.todos(
|
|
23
|
+
"saucedemo",
|
|
24
|
+
[
|
|
25
|
+
{ id: "login-user", text: "Enter username", done: true },
|
|
26
|
+
{ id: "login-pass", text: "Enter password", done: true },
|
|
27
|
+
{ id: "login-submit", text: "Tap Login", done: true },
|
|
28
|
+
{ id: "cart-find", text: "Find the product", done: true },
|
|
29
|
+
{ id: "cart-add", text: "Add to cart", done: true },
|
|
30
|
+
{ id: "cart-open", text: "Open cart" },
|
|
31
|
+
],
|
|
32
|
+
{ title: "Device showcase", index: 5 },
|
|
33
|
+
);
|
|
30
34
|
ctx.zoom(1.4);
|
|
31
35
|
ctx.zoomOut(false);
|
|
32
36
|
ctx.highlights({
|
|
33
37
|
cart: {
|
|
34
38
|
selector: ".shopping_cart_link",
|
|
35
39
|
label: "Cart",
|
|
36
|
-
detail: "
|
|
40
|
+
detail: "Open cart",
|
|
37
41
|
focus: true,
|
|
38
42
|
color: "#c9a6ff",
|
|
39
|
-
zoom: 1.55,
|
|
40
43
|
zoomOut: false,
|
|
41
44
|
followMouse: false,
|
|
42
45
|
weight: "bold",
|
|
@@ -46,16 +49,16 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
|
|
|
46
49
|
},
|
|
47
50
|
stubAfter: (ctx: StubCtx) => {
|
|
48
51
|
ctx.banner("Your cart · desktop");
|
|
49
|
-
// Showcase finale: back to desktop.
|
|
50
52
|
ctx.clearDevice();
|
|
51
|
-
ctx.
|
|
53
|
+
ctx.hideTodos();
|
|
54
|
+
ctx.zoomOut(true);
|
|
52
55
|
ctx.ring("items", {
|
|
53
56
|
selector: InventorySel.cartItem,
|
|
54
57
|
label: "Cart items",
|
|
55
58
|
detail: "Product landed in cart",
|
|
56
59
|
focus: true,
|
|
57
60
|
color: "#86efac",
|
|
58
|
-
|
|
61
|
+
zoomOut: true,
|
|
59
62
|
followMouse: false,
|
|
60
63
|
duration: true,
|
|
61
64
|
});
|
package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts
CHANGED
|
@@ -37,16 +37,20 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
37
37
|
// Demo: zoom product -> add (stay zoomed), then badge after act.
|
|
38
38
|
stubBefore: (ctx: StubCtx) => {
|
|
39
39
|
ctx.title("Pick a product · tablet");
|
|
40
|
-
// Device showcase: tablet portrait (toast + lerp from mobile landscape).
|
|
41
40
|
ctx.device("tablet");
|
|
42
41
|
ctx.portrait();
|
|
43
|
-
ctx.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
+
);
|
|
50
54
|
ctx.zoom(1.35);
|
|
51
55
|
ctx.zoomOut(false);
|
|
52
56
|
ctx.highlights({
|
|
@@ -72,13 +76,18 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
72
76
|
},
|
|
73
77
|
stubAfter: (ctx: StubCtx) => {
|
|
74
78
|
ctx.banner("In the cart");
|
|
75
|
-
ctx.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
ctx.todos(
|
|
80
|
+
"saucedemo",
|
|
81
|
+
[
|
|
82
|
+
{ id: "login-user", text: "Enter username", done: true },
|
|
83
|
+
{ id: "login-pass", text: "Enter password", done: true },
|
|
84
|
+
{ id: "login-submit", text: "Tap Login", done: true },
|
|
85
|
+
{ id: "cart-find", text: "Find the product", done: true },
|
|
86
|
+
{ id: "cart-add", text: "Add to cart" },
|
|
87
|
+
{ id: "cart-open", text: "Open cart" },
|
|
88
|
+
],
|
|
89
|
+
{ title: "Device showcase", index: 4 },
|
|
90
|
+
);
|
|
82
91
|
ctx.zoomOut(false);
|
|
83
92
|
ctx.ring("badge", {
|
|
84
93
|
selector: InventorySel.cartBadge,
|
|
@@ -86,7 +95,6 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
86
95
|
detail: "Item count updated",
|
|
87
96
|
focus: true,
|
|
88
97
|
color: "#86efac",
|
|
89
|
-
zoom: 1.6,
|
|
90
98
|
zoomOut: false,
|
|
91
99
|
duration: true,
|
|
92
100
|
});
|
|
@@ -51,21 +51,26 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
51
51
|
// sequential highlight queue (appear / dwell / fade), focus + color.
|
|
52
52
|
stubBefore: (ctx: StubCtx) => {
|
|
53
53
|
ctx.title("Signing in · mobile");
|
|
54
|
-
// 0.13.3: mobile portrait + touch; tap Login.
|
|
55
54
|
ctx.device("mobile");
|
|
56
55
|
ctx.portrait();
|
|
57
|
-
ctx.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
ctx.todos(
|
|
57
|
+
"saucedemo",
|
|
58
|
+
[
|
|
59
|
+
{ id: "login-user", text: "Enter username" },
|
|
60
|
+
{ id: "login-pass", text: "Enter password" },
|
|
61
|
+
{ id: "login-submit", text: "Tap Login" },
|
|
62
|
+
{ id: "cart-find", text: "Find the product" },
|
|
63
|
+
{ id: "cart-add", text: "Add to cart" },
|
|
64
|
+
{ id: "cart-open", text: "Open cart" },
|
|
65
|
+
],
|
|
66
|
+
{ title: "Device showcase", index: 0 },
|
|
67
|
+
);
|
|
64
68
|
ctx.zoom(1.35);
|
|
65
69
|
ctx.highlights({
|
|
66
70
|
username: {
|
|
67
71
|
selector: "#user-name",
|
|
68
72
|
label: "Username",
|
|
73
|
+
todo: "login-user",
|
|
69
74
|
focus: true,
|
|
70
75
|
color: "#c9a6ff",
|
|
71
76
|
zoom: 1.4,
|
|
@@ -73,6 +78,7 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
73
78
|
password: {
|
|
74
79
|
selector: "#password",
|
|
75
80
|
label: "Password",
|
|
81
|
+
todo: "login-pass",
|
|
76
82
|
focus: true,
|
|
77
83
|
color: "#c9a6ff",
|
|
78
84
|
zoom: 1.4,
|
|
@@ -80,6 +86,7 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
80
86
|
submit: {
|
|
81
87
|
selector: "#login-button",
|
|
82
88
|
label: "Login",
|
|
89
|
+
todo: "login-submit",
|
|
83
90
|
focus: true,
|
|
84
91
|
color: "#86efac",
|
|
85
92
|
zoom: 1.55,
|
|
@@ -89,17 +96,17 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
89
96
|
});
|
|
90
97
|
},
|
|
91
98
|
stubAfter: (ctx: StubCtx) => {
|
|
92
|
-
// Fail branch lands on LoginPage with Epic sadface - do not ring the
|
|
93
|
-
// inventory shelf. Success (LoggedIn) hands off to shopFlow.
|
|
94
99
|
if (ctx.out && ctx.out.__state === "LoginPage") {
|
|
95
100
|
ctx.title("Login blocked");
|
|
96
|
-
ctx.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
ctx.todos(
|
|
102
|
+
"saucedemo",
|
|
103
|
+
[
|
|
104
|
+
{ id: "login-user", text: "Enter username", done: true },
|
|
105
|
+
{ id: "login-pass", text: "Enter password", done: true },
|
|
106
|
+
{ id: "login-submit", text: "Click Login", done: true },
|
|
107
|
+
],
|
|
108
|
+
{ title: "Sign in", style: "checklist" },
|
|
109
|
+
);
|
|
103
110
|
ctx.ring("error", {
|
|
104
111
|
selector: '[data-test="error"]',
|
|
105
112
|
label: "Login error banner",
|
|
@@ -111,16 +118,19 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
111
118
|
return;
|
|
112
119
|
}
|
|
113
120
|
ctx.banner("Inventory · landscape");
|
|
114
|
-
// Rotate mobile to landscape on the product shelf (toast + lerp).
|
|
115
121
|
ctx.landscape();
|
|
116
|
-
ctx.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
ctx.todos(
|
|
123
|
+
"saucedemo",
|
|
124
|
+
[
|
|
125
|
+
{ id: "login-user", text: "Enter username", done: true },
|
|
126
|
+
{ id: "login-pass", text: "Enter password", done: true },
|
|
127
|
+
{ id: "login-submit", text: "Tap Login", done: true },
|
|
128
|
+
{ id: "cart-find", text: "Find the product" },
|
|
129
|
+
{ id: "cart-add", text: "Add to cart" },
|
|
130
|
+
{ id: "cart-open", text: "Open cart" },
|
|
131
|
+
],
|
|
132
|
+
{ title: "Device showcase", index: 3 },
|
|
133
|
+
);
|
|
124
134
|
ctx.ring("shelf", {
|
|
125
135
|
selector: ".inventory_list",
|
|
126
136
|
label: "Product shelf",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "waygraph",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,7 +19,14 @@
|
|
|
19
19
|
"dist",
|
|
20
20
|
"bin",
|
|
21
21
|
"templates",
|
|
22
|
-
"examples"
|
|
22
|
+
"examples",
|
|
23
|
+
"!examples/**/.tmp",
|
|
24
|
+
"!examples/**/.tmp/**",
|
|
25
|
+
"!examples/**/.waygraph-traverse",
|
|
26
|
+
"!examples/**/.waygraph-traverse/**",
|
|
27
|
+
"!examples/**/.waygraph-videos",
|
|
28
|
+
"!examples/**/.waygraph-videos/**",
|
|
29
|
+
"!**/*.webm"
|
|
23
30
|
],
|
|
24
31
|
"repository": {
|
|
25
32
|
"type": "git",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"updatedAt": "2026-09-17T11:18:26.658Z",
|
|
3
|
-
"leases": {
|
|
4
|
-
"submit-login": {
|
|
5
|
-
"workerId": "traverse-2",
|
|
6
|
-
"claimedAt": 1789643826618
|
|
7
|
-
},
|
|
8
|
-
"add-all-to-cart": {
|
|
9
|
-
"workerId": "traverse-2",
|
|
10
|
-
"claimedAt": 1789643827327
|
|
11
|
-
},
|
|
12
|
-
"nav-cart": {
|
|
13
|
-
"workerId": "traverse-2",
|
|
14
|
-
"claimedAt": 1789643829570
|
|
15
|
-
},
|
|
16
|
-
"nav-continue-shopping": {
|
|
17
|
-
"workerId": "traverse-2",
|
|
18
|
-
"claimedAt": 1789643855282
|
|
19
|
-
},
|
|
20
|
-
"nav-item-detail::sauce-labs-backpack": {
|
|
21
|
-
"workerId": "traverse-2",
|
|
22
|
-
"claimedAt": 1789643857049
|
|
23
|
-
},
|
|
24
|
-
"nav-back-to-inventory": {
|
|
25
|
-
"workerId": "traverse-2",
|
|
26
|
-
"claimedAt": 1789643906658
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|