waygraph 0.12.6 → 0.12.8
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/README.md +2 -1
- package/dist/agent-dive.d.ts +19 -0
- package/dist/agent-dive.js +162 -0
- package/dist/auto-explore-run.d.ts +5 -0
- package/dist/auto-explore-run.js +15 -5
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +152 -1
- package/dist/traverse-run.d.ts +24 -0
- package/dist/traverse-run.js +227 -0
- package/package.json +1 -1
- package/templates/agents/waygraph-author.agent.md +36 -0
- package/templates/agents/waygraph-healer.agent.md +33 -0
- package/templates/agents/waygraph-planner.agent.md +35 -0
- package/templates/scaffold/README.md +11 -8
- package/templates/scaffold/STRUCTURE.md +42 -17
- package/templates/scaffold/package.json +13 -8
- package/templates/scaffold/playwright.config.ts +15 -0
- package/templates/scaffold/scripts/fixture-server.mjs +41 -0
- package/templates/scaffold/scripts/with-fixture.mjs +69 -0
- package/templates/scaffold/src/blocks/SITE-MAP.md +5 -6
- package/templates/scaffold/src/blocks/demo-web/NAV.md +17 -8
- package/templates/scaffold/src/blocks/demo-web/demo-sel.ts +14 -0
- package/templates/scaffold/src/blocks/demo-web/home.html +65 -0
- package/templates/scaffold/src/blocks/demo-web/home.page.block.ts +29 -0
- package/templates/scaffold/src/blocks/demo-web/methods/add-item.effect.block.ts +68 -0
- package/templates/scaffold/src/blocks/demo-web/methods/assert-hello.method.block.ts +11 -12
- package/templates/scaffold/src/blocks/demo-web/methods/clear-cart.method.block.ts +26 -0
- package/templates/scaffold/src/blocks/demo-web/methods/remove-item.effect.block.ts +63 -0
- package/templates/scaffold/src/blocks/demo-web/nav-home.block.ts +5 -8
- package/templates/scaffold/src/flows/example.flow.ts +3 -4
- package/templates/scaffold/src/flows/shop.flow.ts +42 -0
- package/templates/scaffold/src/states/demo.mem-keys.ts +7 -0
- package/templates/scaffold/src/states/demo.states.ts +2 -0
- package/templates/scaffold/tests/example.spec.ts +9 -0
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
# demo-web / (synthetic home)
|
|
2
2
|
|
|
3
|
-
**URL:** `
|
|
3
|
+
**URL:** `http://127.0.0.1:4177/home.html` (offline catalog via `scripts/fixture-server.mjs` - replace with your real `/`)
|
|
4
4
|
|
|
5
5
|
## Nav-to
|
|
6
6
|
|
|
7
7
|
| From | Block | How |
|
|
8
8
|
|------|-------|-----|
|
|
9
|
-
| start | `nav-home` | `defineNavBlock` `url` →
|
|
9
|
+
| start | `nav-home` | `defineNavBlock` `url` → home.html |
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Page hub
|
|
12
12
|
|
|
13
|
-
| Block |
|
|
14
|
-
|
|
15
|
-
| `
|
|
13
|
+
| Block | Checkpoint | Methods |
|
|
14
|
+
|-------|------------|---------|
|
|
15
|
+
| `page-home` | `Home` | add-item, remove-item, clear-cart, assert-hello |
|
|
16
|
+
|
|
17
|
+
## Methods / Effects
|
|
18
|
+
|
|
19
|
+
| Block | Kind | Auto |
|
|
20
|
+
|-------|------|------|
|
|
21
|
+
| `assert-hello` | Method | single row |
|
|
22
|
+
| `add-item` | Effect | one row per live Add button (`instanceOptions`) |
|
|
23
|
+
| `remove-item` | Effect | one row per live Remove button |
|
|
24
|
+
| `clear-cart` | Method | single row |
|
|
16
25
|
|
|
17
26
|
## States / mem-keys
|
|
18
27
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
28
|
+
- Checkpoints: `Home`, `HomeVerified`, `ItemInCart`, `CartEmpty`
|
|
29
|
+
- Mem: `demo.selectedItem` (`SelectedItem`) - filled by auto or `--data`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Offline catalog URL — HTTP fixture server (not file://; Flatpak Chromium blocks /tmp file URLs). */
|
|
2
|
+
export const HOME_URL =
|
|
3
|
+
process.env.WAYGRAPH_HOME_URL ??
|
|
4
|
+
`http://${process.env.WAYGRAPH_FIXTURE_HOST || "127.0.0.1"}:${process.env.WAYGRAPH_FIXTURE_PORT || "4177"}/home.html`;
|
|
5
|
+
|
|
6
|
+
export const DemoSel = {
|
|
7
|
+
title: "h1",
|
|
8
|
+
sub: "#sub",
|
|
9
|
+
cartCount: "[data-test=cart-count]",
|
|
10
|
+
catalog: "#catalog",
|
|
11
|
+
clear: "[data-test=clear-cart]",
|
|
12
|
+
addBtn: (id: string) => `[data-test=add-${id}]`,
|
|
13
|
+
removeBtn: (id: string) => `[data-test=remove-${id}]`,
|
|
14
|
+
} as const;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Waygraph scaffold demo</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; max-width: 32rem; }
|
|
8
|
+
h1 { margin: 0 0 0.5rem; }
|
|
9
|
+
#cart { margin: 1rem 0; font-weight: 600; }
|
|
10
|
+
.item { display: flex; gap: 0.75rem; align-items: center; margin: 0.5rem 0; }
|
|
11
|
+
button { cursor: pointer; }
|
|
12
|
+
</style>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<h1>Hello Waygraph</h1>
|
|
16
|
+
<p id="sub">Scaffold demo - Effect / auto / Page hub</p>
|
|
17
|
+
<div id="cart">Cart: <span data-test="cart-count">0</span></div>
|
|
18
|
+
<ul id="catalog">
|
|
19
|
+
<li class="item" data-id="alpha">
|
|
20
|
+
<span class="name">Alpha</span>
|
|
21
|
+
<button type="button" data-test="add-alpha" class="add">Add Alpha</button>
|
|
22
|
+
<button type="button" data-test="remove-alpha" class="remove" hidden>Remove Alpha</button>
|
|
23
|
+
</li>
|
|
24
|
+
<li class="item" data-id="beta">
|
|
25
|
+
<span class="name">Beta</span>
|
|
26
|
+
<button type="button" data-test="add-beta" class="add">Add Beta</button>
|
|
27
|
+
<button type="button" data-test="remove-beta" class="remove" hidden>Remove Beta</button>
|
|
28
|
+
</li>
|
|
29
|
+
<li class="item" data-id="gamma">
|
|
30
|
+
<span class="name">Gamma</span>
|
|
31
|
+
<button type="button" data-test="add-gamma" class="add">Add Gamma</button>
|
|
32
|
+
<button type="button" data-test="remove-gamma" class="remove" hidden>Remove Gamma</button>
|
|
33
|
+
</li>
|
|
34
|
+
</ul>
|
|
35
|
+
<button type="button" data-test="clear-cart" id="clear">Clear cart</button>
|
|
36
|
+
<script>
|
|
37
|
+
const countEl = document.querySelector("[data-test=cart-count]");
|
|
38
|
+
const sync = () => {
|
|
39
|
+
const n = document.querySelectorAll(".remove:not([hidden])").length;
|
|
40
|
+
countEl.textContent = String(n);
|
|
41
|
+
};
|
|
42
|
+
document.querySelectorAll(".item").forEach((row) => {
|
|
43
|
+
const add = row.querySelector(".add");
|
|
44
|
+
const remove = row.querySelector(".remove");
|
|
45
|
+
add.addEventListener("click", () => {
|
|
46
|
+
add.hidden = true;
|
|
47
|
+
remove.hidden = false;
|
|
48
|
+
sync();
|
|
49
|
+
});
|
|
50
|
+
remove.addEventListener("click", () => {
|
|
51
|
+
remove.hidden = true;
|
|
52
|
+
add.hidden = false;
|
|
53
|
+
sync();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
document.getElementById("clear").addEventListener("click", () => {
|
|
57
|
+
document.querySelectorAll(".item").forEach((row) => {
|
|
58
|
+
row.querySelector(".add").hidden = false;
|
|
59
|
+
row.querySelector(".remove").hidden = true;
|
|
60
|
+
});
|
|
61
|
+
sync();
|
|
62
|
+
});
|
|
63
|
+
</script>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { definePageBlock, Trait } from "waygraph";
|
|
2
|
+
import type { Home } from "../../states/demo.states.js";
|
|
3
|
+
import { DemoSel } from "./demo-sel.js";
|
|
4
|
+
import { AddItemBlock } from "./methods/add-item.effect.block.js";
|
|
5
|
+
import { RemoveItemBlock } from "./methods/remove-item.effect.block.js";
|
|
6
|
+
import { ClearCartBlock } from "./methods/clear-cart.method.block.js";
|
|
7
|
+
import { AssertHelloBlock } from "./methods/assert-hello.method.block.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Kind: Page
|
|
11
|
+
* Helper: definePageBlock
|
|
12
|
+
* Route: demo-web/
|
|
13
|
+
*
|
|
14
|
+
* Hub for the offline home screen. Methods hang here for docs/auto grouping;
|
|
15
|
+
* edges in flows still reference the method Blocks directly.
|
|
16
|
+
* Arrival-only (nav-home already landed on Home).
|
|
17
|
+
*/
|
|
18
|
+
export const HomePageBlock = definePageBlock<Home>({
|
|
19
|
+
name: "page-home",
|
|
20
|
+
description: "Offline home hub - add/remove effects, clear, assert-hello.",
|
|
21
|
+
checkpoint: "Home",
|
|
22
|
+
verify: [Trait.visible(DemoSel.catalog)],
|
|
23
|
+
methods: {
|
|
24
|
+
addItem: () => AddItemBlock,
|
|
25
|
+
removeItem: () => RemoveItemBlock,
|
|
26
|
+
clearCart: () => ClearCartBlock,
|
|
27
|
+
assertHello: () => AssertHelloBlock,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineEffectBlock,
|
|
3
|
+
checkpoint,
|
|
4
|
+
type Checkpoint,
|
|
5
|
+
type WaygraphInstanceOption,
|
|
6
|
+
} from "waygraph";
|
|
7
|
+
import type { ItemInCart } from "../../../states/demo.states.js";
|
|
8
|
+
import { SelectedItem } from "../../../states/demo.mem-keys.js";
|
|
9
|
+
import { DemoSel } from "../demo-sel.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Kind: Effect
|
|
13
|
+
* Helper: defineEffectBlock
|
|
14
|
+
* Route: demo-web/methods/
|
|
15
|
+
*
|
|
16
|
+
* Mutates one catalog row from mem. `waygraph auto` lists one "Add …" row per
|
|
17
|
+
* live Add button via instanceOptions.
|
|
18
|
+
*/
|
|
19
|
+
export const AddItemBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>({
|
|
20
|
+
name: "add-item",
|
|
21
|
+
description: "Adds the mem-selected catalog item to the cart.",
|
|
22
|
+
requires: [SelectedItem.key],
|
|
23
|
+
instruction: {
|
|
24
|
+
async act(page, _input, mem) {
|
|
25
|
+
const { id } = mem.get(SelectedItem.key);
|
|
26
|
+
await page.locator(DemoSel.addBtn(id)).click();
|
|
27
|
+
},
|
|
28
|
+
resolve: () => checkpoint("ItemInCart"),
|
|
29
|
+
verify: [
|
|
30
|
+
{
|
|
31
|
+
name: "item-added",
|
|
32
|
+
async check(page, mem) {
|
|
33
|
+
const { id } = mem.get(SelectedItem.key);
|
|
34
|
+
return page.locator(DemoSel.removeBtn(id)).isVisible();
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
stubBefore: {
|
|
39
|
+
add: { selector: DemoSel.catalog, label: "Catalog" },
|
|
40
|
+
},
|
|
41
|
+
stubAfter: {
|
|
42
|
+
cart: { selector: DemoSel.cartCount, label: "Cart count", duration: true },
|
|
43
|
+
},
|
|
44
|
+
stubOnError: {
|
|
45
|
+
cart: { selector: DemoSel.cartCount, label: "Cart at failure", tag: "FAIL" },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
async instanceOptions(page): Promise<readonly WaygraphInstanceOption[]> {
|
|
49
|
+
const buttons = page.locator("button.add:not([hidden])");
|
|
50
|
+
const n = await buttons.count();
|
|
51
|
+
const out: WaygraphInstanceOption[] = [];
|
|
52
|
+
for (let i = 0; i < n; i++) {
|
|
53
|
+
const btn = buttons.nth(i);
|
|
54
|
+
const label = ((await btn.textContent()) || "").trim();
|
|
55
|
+
const testId = await btn.getAttribute("data-test");
|
|
56
|
+
const id = (testId || "").replace(/^add-/, "");
|
|
57
|
+
if (!id) continue;
|
|
58
|
+
out.push({
|
|
59
|
+
id,
|
|
60
|
+
label: label || `Add ${id}`,
|
|
61
|
+
key: SelectedItem.key,
|
|
62
|
+
value: { id, name: id },
|
|
63
|
+
highlight: DemoSel.addBtn(id),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -1,48 +1,47 @@
|
|
|
1
1
|
import { defineMethodBlock, checkpoint, Trait } from "waygraph";
|
|
2
|
+
import { DemoSel } from "../demo-sel.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Kind: Method
|
|
5
6
|
* Helper: defineMethodBlock
|
|
6
7
|
* Route: demo-web/methods/
|
|
7
8
|
*
|
|
8
|
-
* Same-URL assert after nav -
|
|
9
|
+
* Same-URL assert after nav - stubs + fixtures + YAP slide.
|
|
9
10
|
*/
|
|
10
11
|
export const AssertHelloBlock = defineMethodBlock({
|
|
11
12
|
name: "assert-hello",
|
|
12
13
|
description: "Confirms the hello heading is still visible after arrival.",
|
|
13
14
|
instruction: {
|
|
14
|
-
async act(_page) {
|
|
15
|
-
// No DOM mutation - observe/verify own the proof.
|
|
16
|
-
},
|
|
15
|
+
async act(_page) {},
|
|
17
16
|
async observe(page) {
|
|
18
|
-
await page.locator(
|
|
17
|
+
await page.locator(DemoSel.title).first().waitFor({ state: "visible", timeout: 5_000 });
|
|
19
18
|
return "ok" as const;
|
|
20
19
|
},
|
|
21
20
|
resolve: () => checkpoint("HomeVerified"),
|
|
22
|
-
verify: [Trait.text(
|
|
21
|
+
verify: [Trait.text(DemoSel.title, "Hello Waygraph"), Trait.visible(DemoSel.sub)],
|
|
23
22
|
stubBefore: {
|
|
24
|
-
title: { selector:
|
|
23
|
+
title: { selector: DemoSel.title, label: "Heading about to check" },
|
|
25
24
|
},
|
|
26
25
|
stubAfter: {
|
|
27
26
|
title: {
|
|
28
|
-
selector:
|
|
27
|
+
selector: DemoSel.title,
|
|
29
28
|
label: "Hello confirmed",
|
|
30
29
|
detail: "Offline scaffold proof point.",
|
|
31
30
|
duration: true,
|
|
32
31
|
},
|
|
33
|
-
sub: { selector:
|
|
32
|
+
sub: { selector: DemoSel.sub, label: "Subcopy", duration: 1500 },
|
|
34
33
|
},
|
|
35
34
|
stubOnError: {
|
|
36
35
|
title: {
|
|
37
|
-
selector:
|
|
36
|
+
selector: DemoSel.title,
|
|
38
37
|
label: "Heading at failure",
|
|
39
38
|
tag: "FAIL",
|
|
40
39
|
},
|
|
41
40
|
},
|
|
42
41
|
slides: [
|
|
43
42
|
{
|
|
44
|
-
caption: "Scaffold
|
|
45
|
-
detail: "
|
|
43
|
+
caption: "Scaffold covers auto + narration",
|
|
44
|
+
detail: "Page hub, Effect instanceOptions, stubs, fixtures, YAP.",
|
|
46
45
|
tag: "YAP",
|
|
47
46
|
duration: true,
|
|
48
47
|
},
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineMethodBlock, checkpoint, Trait } from "waygraph";
|
|
2
|
+
import { DemoSel } from "../demo-sel.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Kind: Method
|
|
6
|
+
* Helper: defineMethodBlock
|
|
7
|
+
* Route: demo-web/methods/
|
|
8
|
+
*/
|
|
9
|
+
export const ClearCartBlock = defineMethodBlock({
|
|
10
|
+
name: "clear-cart",
|
|
11
|
+
description: "Clears every item via the Clear cart control.",
|
|
12
|
+
instruction: {
|
|
13
|
+
async act(page) {
|
|
14
|
+
await page.locator(DemoSel.clear).click();
|
|
15
|
+
},
|
|
16
|
+
resolve: () => checkpoint("CartEmpty"),
|
|
17
|
+
verify: [Trait.text(DemoSel.cartCount, "0")],
|
|
18
|
+
stubBefore: {
|
|
19
|
+
clear: { selector: DemoSel.clear, label: "Clear cart" },
|
|
20
|
+
},
|
|
21
|
+
stubAfter: {
|
|
22
|
+
cart: { selector: DemoSel.cartCount, label: "Empty cart", duration: true },
|
|
23
|
+
},
|
|
24
|
+
stubOnError: {},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineEffectBlock,
|
|
3
|
+
checkpoint,
|
|
4
|
+
type Checkpoint,
|
|
5
|
+
type WaygraphInstanceOption,
|
|
6
|
+
} from "waygraph";
|
|
7
|
+
import type { CartEmpty } from "../../../states/demo.states.js";
|
|
8
|
+
import { SelectedItem } from "../../../states/demo.mem-keys.js";
|
|
9
|
+
import { DemoSel } from "../demo-sel.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Kind: Effect
|
|
13
|
+
* Helper: defineEffectBlock
|
|
14
|
+
* Route: demo-web/methods/
|
|
15
|
+
*
|
|
16
|
+
* Removes one in-cart row. Auto menus one row per visible Remove button.
|
|
17
|
+
*/
|
|
18
|
+
export const RemoveItemBlock = defineEffectBlock<Checkpoint<string>, CartEmpty>({
|
|
19
|
+
name: "remove-item",
|
|
20
|
+
description: "Removes the mem-selected catalog item from 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(DemoSel.removeBtn(id)).click();
|
|
26
|
+
},
|
|
27
|
+
resolve: () => checkpoint("CartEmpty"),
|
|
28
|
+
verify: [
|
|
29
|
+
{
|
|
30
|
+
name: "item-removed",
|
|
31
|
+
async check(page, mem) {
|
|
32
|
+
const { id } = mem.get(SelectedItem.key);
|
|
33
|
+
return page.locator(DemoSel.addBtn(id)).isVisible();
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
stubBefore: {},
|
|
38
|
+
stubAfter: {
|
|
39
|
+
cart: { selector: DemoSel.cartCount, label: "Cart count", duration: true },
|
|
40
|
+
},
|
|
41
|
+
stubOnError: {},
|
|
42
|
+
},
|
|
43
|
+
async instanceOptions(page): Promise<readonly WaygraphInstanceOption[]> {
|
|
44
|
+
const buttons = page.locator("button.remove:not([hidden])");
|
|
45
|
+
const n = await buttons.count();
|
|
46
|
+
const out: WaygraphInstanceOption[] = [];
|
|
47
|
+
for (let i = 0; i < n; i++) {
|
|
48
|
+
const btn = buttons.nth(i);
|
|
49
|
+
const label = ((await btn.textContent()) || "").trim();
|
|
50
|
+
const testId = await btn.getAttribute("data-test");
|
|
51
|
+
const id = (testId || "").replace(/^remove-/, "");
|
|
52
|
+
if (!id) continue;
|
|
53
|
+
out.push({
|
|
54
|
+
id,
|
|
55
|
+
label: label || `Remove ${id}`,
|
|
56
|
+
key: SelectedItem.key,
|
|
57
|
+
value: { id, name: id },
|
|
58
|
+
highlight: DemoSel.removeBtn(id),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { defineNavBlock, Trait } from "waygraph";
|
|
2
|
-
|
|
3
|
-
/** Offline hello page - swap `url` for a real app route when you graduate. */
|
|
4
|
-
export const HOME_URL =
|
|
5
|
-
'data:text/html,<!doctype html><html><body><h1>Hello Waygraph</h1><p id="sub">Scaffold demo</p></body></html>';
|
|
2
|
+
import { DemoSel, HOME_URL } from "./demo-sel.js";
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
5
|
* Kind: Nav
|
|
@@ -11,15 +8,15 @@ export const HOME_URL =
|
|
|
11
8
|
*/
|
|
12
9
|
export const NavHomeBlock = defineNavBlock({
|
|
13
10
|
name: "nav-home",
|
|
14
|
-
description: "Opens the offline
|
|
11
|
+
description: "Opens the offline catalog page (HTTP fixture :4177).",
|
|
15
12
|
checkpoint: "Home",
|
|
16
13
|
url: HOME_URL,
|
|
17
|
-
verify: [Trait.text(
|
|
14
|
+
verify: [Trait.text(DemoSel.title, "Hello Waygraph"), Trait.visible(DemoSel.catalog)],
|
|
18
15
|
stubBefore: {},
|
|
19
16
|
stubAfter: {
|
|
20
|
-
title: { selector:
|
|
17
|
+
title: { selector: DemoSel.title, label: "Home heading", duration: true },
|
|
21
18
|
},
|
|
22
19
|
stubOnError: {
|
|
23
|
-
title: { selector:
|
|
20
|
+
title: { selector: DemoSel.title, label: "Home heading at failure" },
|
|
24
21
|
},
|
|
25
22
|
});
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Engine, start, end, withTitle, withHighlightFixtures } from "waygraph";
|
|
2
2
|
import { NavHomeBlock } from "../blocks/demo-web/nav-home.block.js";
|
|
3
3
|
import { AssertHelloBlock } from "../blocks/demo-web/methods/assert-hello.method.block.js";
|
|
4
|
+
// Page hub registered for auto/docs grouping (arrival-only after nav).
|
|
5
|
+
import "../blocks/demo-web/home.page.block.js";
|
|
4
6
|
|
|
5
7
|
const engine = new Engine();
|
|
6
8
|
|
|
7
|
-
/**
|
|
8
|
-
* Offline example: nav to data: hello page, then assert heading.
|
|
9
|
-
* Flow fixtures override ticket-style copy on stubs (demo narration).
|
|
10
|
-
*/
|
|
9
|
+
/** Smoke: nav + assert (no mem). Green offline. */
|
|
11
10
|
export const exampleFlow = withHighlightFixtures(
|
|
12
11
|
withTitle(
|
|
13
12
|
engine.defineFlow([start, NavHomeBlock, AssertHelloBlock, end]),
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Engine,
|
|
3
|
+
start,
|
|
4
|
+
end,
|
|
5
|
+
withTitle,
|
|
6
|
+
withHighlightFixtures,
|
|
7
|
+
withSessionReset,
|
|
8
|
+
} from "waygraph";
|
|
9
|
+
import { NavHomeBlock } from "../blocks/demo-web/nav-home.block.js";
|
|
10
|
+
import { AddItemBlock } from "../blocks/demo-web/methods/add-item.effect.block.js";
|
|
11
|
+
import { ClearCartBlock } from "../blocks/demo-web/methods/clear-cart.method.block.js";
|
|
12
|
+
import "../blocks/demo-web/home.page.block.js";
|
|
13
|
+
|
|
14
|
+
const engine = new Engine();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Effect + Method demo (set mem for add-item, or use `waygraph auto` which
|
|
18
|
+
* fills SelectedItem via instanceOptions).
|
|
19
|
+
*
|
|
20
|
+
* waygraph demo --blocks shopFlow --data '{"demo.selectedItem":{"id":"alpha","name":"Alpha"}}'
|
|
21
|
+
* waygraph auto # after landing on home, pick Add Alpha / Add Beta / …
|
|
22
|
+
*/
|
|
23
|
+
export const shopFlow = withHighlightFixtures(
|
|
24
|
+
withTitle(
|
|
25
|
+
withSessionReset(
|
|
26
|
+
engine.defineFlow([start, NavHomeBlock, AddItemBlock, ClearCartBlock, end]),
|
|
27
|
+
),
|
|
28
|
+
"Scaffold: Shop (Effect + clear)",
|
|
29
|
+
),
|
|
30
|
+
{
|
|
31
|
+
"add-item": {
|
|
32
|
+
stubAfter: {
|
|
33
|
+
cart: {
|
|
34
|
+
label: "AC · item in cart",
|
|
35
|
+
detail: "Cart count bumped after Add.",
|
|
36
|
+
tag: "AC",
|
|
37
|
+
duration: true,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { keyGroup } from "waygraph";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Which catalog row `add-item` / `remove-item` act on.
|
|
5
|
+
* `waygraph auto` fills this via Effect `instanceOptions` (one menu row per live button).
|
|
6
|
+
*/
|
|
7
|
+
export const SelectedItem = keyGroup<{ id: string; name: string }>("demo.selectedItem");
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { test, expect } from "@playwright/test";
|
|
2
2
|
import { MemPage, checkpoint } from "waygraph";
|
|
3
3
|
import { exampleFlow } from "../src/flows/example.flow.js";
|
|
4
|
+
import { shopFlow } from "../src/flows/shop.flow.js";
|
|
5
|
+
import { SelectedItem } from "../src/states/demo.mem-keys.js";
|
|
4
6
|
|
|
5
7
|
test("example flow reaches HomeVerified", async ({ context }) => {
|
|
6
8
|
const result = await exampleFlow.run(context, new MemPage());
|
|
7
9
|
expect(result).toEqual(checkpoint("HomeVerified"));
|
|
8
10
|
});
|
|
11
|
+
|
|
12
|
+
test("shop flow adds then clears (Effect + Method)", async ({ context }) => {
|
|
13
|
+
const mem = new MemPage();
|
|
14
|
+
mem.set(SelectedItem.key, { id: "alpha", name: "Alpha" });
|
|
15
|
+
const result = await shopFlow.run(context, mem);
|
|
16
|
+
expect(result).toEqual(checkpoint("CartEmpty"));
|
|
17
|
+
});
|