waygraph 0.15.51 → 0.15.53
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 +8 -1
- package/dist/analysis/graph.d.ts +1 -1
- package/dist/analysis/graph.js +10 -15
- package/dist/analysis/map-check.d.ts +3 -2
- package/dist/analysis/practices-check.d.ts +0 -1
- package/dist/analysis/practices-check.js +0 -13
- package/dist/auto-session/session.d.ts +1 -1
- package/dist/auto-session/session.js +13 -8
- package/dist/blocks-select.d.ts +7 -4
- package/dist/blocks-select.js +0 -14
- package/dist/browser.d.ts +27 -0
- package/dist/browser.js +62 -0
- package/dist/cli/flags.js +3 -1
- package/dist/cli/run-chain.js +4 -0
- package/dist/cli/usage.js +17 -1
- package/dist/cli.js +13 -1
- package/dist/commands/agent-dive-scaffold.d.ts +5 -20
- package/dist/commands/agent-dive-scaffold.js +10 -3
- package/dist/commands/extensions.d.ts +1 -0
- package/dist/commands/extensions.js +22 -0
- package/dist/commands/init.js +43 -5
- package/dist/commands/test.js +16 -2
- package/dist/commands/try.js +2 -1
- package/dist/commands/upgrade.d.ts +6 -0
- package/dist/commands/upgrade.js +113 -0
- package/dist/config.d.ts +81 -3
- package/dist/config.js +91 -16
- package/dist/core/block.d.ts +19 -2
- package/dist/core/block.js +43 -3
- package/dist/core/blocks/nav.d.ts +11 -0
- package/dist/core/blocks/nav.js +8 -0
- package/dist/core/config.d.ts +5 -0
- package/dist/core/engine.d.ts +19 -16
- package/dist/core/engine.js +6 -4
- package/dist/core/flow.d.ts +27 -0
- package/dist/core/flow.js +75 -28
- package/dist/core/map-builder.d.ts +30 -4
- package/dist/core/map-builder.js +45 -7
- package/dist/env-file.d.ts +7 -7
- package/dist/env-file.js +29 -16
- package/dist/extensions.d.ts +21 -0
- package/dist/extensions.js +178 -0
- package/dist/highlights/demo-pace.d.ts +0 -1
- package/dist/highlights/demo-pace.js +24 -3
- package/dist/index.d.ts +7 -5
- package/dist/index.js +3 -2
- package/dist/pilot-overlay/action-ring.d.ts +0 -9
- package/dist/pilot-overlay/action-ring.js +1 -1
- package/dist/pilot-overlay/overlay-fixtures.d.ts +0 -7
- package/dist/pilot-overlay/overlay-fixtures.js +0 -45
- package/dist/pilot-overlay/panel.d.ts +2 -1
- package/dist/playwright-config.d.ts +4 -0
- package/dist/playwright-config.js +15 -0
- package/dist/playwright.config.d.ts +2 -0
- package/dist/playwright.config.js +6 -0
- package/dist/runner/demo-log.js +1 -7
- package/dist/runner/inpage/core.d.ts +1 -1
- package/dist/runner/inpage/core.js +8 -7
- package/dist/runner/interaction-patch.js +32 -1
- package/dist/runner/main.js +22 -11
- package/dist/runner/rings.js +0 -49
- package/dist/runner/step-mode.js +10 -4
- package/dist/session/auto-explore-run.js +9 -5
- package/dist/session/auto-explore.d.ts +2 -1
- package/dist/session/auto-explore.js +6 -4
- package/dist/session/auto-session-ipc.d.ts +1 -2
- package/dist/session/auto-session-ipc.js +1 -1
- package/dist/session/block-inject.d.ts +0 -13
- package/dist/session/block-inject.js +3 -3
- package/dist/session/pilot.d.ts +0 -2
- package/dist/session/pilot.js +1 -2
- package/dist/session/step-overlay.js +3 -1
- package/dist/traverse/coverage.d.ts +0 -4
- package/dist/traverse/coverage.js +1 -1
- package/dist/traverse/run.js +3 -1
- package/dist/types.d.ts +1 -1
- package/dist/ui/components/ring.js +1 -0
- package/dist/ui/overlay.css +1 -0
- package/dist/ui/shadow.d.ts +1 -3
- package/dist/ui/shadow.js +0 -4
- package/dist/ui/tokens.d.ts +0 -11
- package/dist/ui/tokens.js +0 -5
- package/dist/video-dir.d.ts +8 -0
- package/dist/video-dir.js +60 -0
- package/dist/webserver.js +5 -1
- package/package.json +11 -5
- package/templates/scaffold/README.md +5 -3
- package/templates/scaffold/STRUCTURE.md +10 -4
- package/templates/scaffold/config/waygraph.config.ts +45 -0
- package/templates/scaffold/package.json +7 -7
- package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +36 -0
- package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +24 -0
- package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +34 -0
- package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +47 -0
- package/templates/scaffold/src/flows/README.md +35 -14
- package/templates/scaffold/src/flows/home/cart.flow.ts +51 -0
- package/templates/scaffold/src/flows/home/hello.flow.ts +42 -0
- package/templates/scaffold/src/map/(app_base)/docs/_nav.block.ts +0 -9
- package/templates/scaffold/src/map/(app_base)/docs/_page.block.ts +0 -5
- package/templates/scaffold/src/map/(app_base)/docs/_sel.ts +1 -1
- package/templates/scaffold/src/map/(app_base)/home/_methods/add-item.effect.block.ts +3 -19
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +2 -25
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts +2 -19
- package/templates/scaffold/src/map/(app_base)/home/_methods/clear-cart.method.block.ts +1 -17
- package/templates/scaffold/src/map/(app_base)/home/_methods/remove-item.effect.block.ts +1 -10
- package/templates/scaffold/src/map/(app_base)/home/_nav.block.ts +1 -12
- package/templates/scaffold/src/map/(app_base)/home/_page.block.ts +0 -9
- package/templates/scaffold/src/map/(app_base)/home/_sel.ts +3 -1
- package/templates/scaffold/src/map/(app_base)/home/nav-verification-link.block.ts +1 -23
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-response-body.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-response-header.method.block.ts +19 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-status.method.block.ts +20 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/pick-extension-interceptor.method.block.ts +18 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/read-response-body.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/read-response-headers.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/send-request.method.block.ts +15 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-bearer-token.method.block.ts +20 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-headers.method.block.ts +23 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-json-body.method.block.ts +36 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-method.method.block.ts +17 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-query-params.method.block.ts +22 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-url.method.block.ts +18 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_nav.block.ts +11 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_page.block.ts +36 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_sel.ts +27 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/settings/_nav.block.ts +10 -0
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-content.method.block.ts +2 -27
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-received.method.block.ts +2 -21
- package/templates/scaffold/src/map/(external)/mailpit/_methods/extract-email-link.method.block.ts +1 -19
- package/templates/scaffold/src/map/(external)/mailpit/_methods/open-message.method.block.ts +1 -11
- package/templates/scaffold/src/map/(external)/mailpit/_nav.block.ts +1 -9
- package/templates/scaffold/src/map/(external)/mailpit/_page.block.ts +0 -11
- package/templates/scaffold/src/states/demo.mem-keys.ts +20 -19
- package/templates/scaffold/src/states/demo.mem-stubs.ts +4 -0
- package/templates/scaffold/src/states/demo.states.ts +5 -1
- package/templates/scaffold/tests/example.spec.ts +6 -7
- package/templates/scaffold/tests/hoppscotch.spec.ts +97 -0
- package/templates/scaffold/tests/mail-verify.spec.ts +8 -17
- package/templates/scaffold/tsconfig.json +1 -1
- package/templates/skills/waygraph-convention.skill.md +40 -3
- package/templates/skills/waygraph-pilot.skill.md +4 -5
- package/dist/browser-path.d.ts +0 -5
- package/dist/browser-path.js +0 -18
- package/dist/runner/overlay-present.d.ts +0 -8
- package/dist/runner/overlay-present.js +0 -57
- package/dist/ui/components/banner.d.ts +0 -2
- package/dist/ui/components/banner.js +0 -10
- package/dist/ui/components/cursor.d.ts +0 -2
- package/dist/ui/components/cursor.js +0 -19
- package/templates/scaffold/playwright.config.ts +0 -12
- package/templates/scaffold/src/flows/example.flow.ts +0 -29
- package/templates/scaffold/src/flows/mail-verify.flow.ts +0 -85
- package/templates/scaffold/src/flows/shop.flow.ts +0 -74
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "waygraph",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.53",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,16 +48,18 @@
|
|
|
48
48
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
49
49
|
"build": "npm run clean && tsc -p tsconfig.build.json && node scripts/copy-runner.mjs && node scripts/copy-ui-css.mjs",
|
|
50
50
|
"typecheck": "node bin/waygraph typecheck .",
|
|
51
|
-
"test": "npm run check:structure && npm run test:unit && playwright test",
|
|
51
|
+
"test": "npm run check:structure && npm run knip && npm run test:unit && playwright test",
|
|
52
52
|
"test:ui": "playwright test --ui",
|
|
53
53
|
"test:unit": "node --import tsx/esm --test tests/unit/*.spec.ts",
|
|
54
54
|
"example:saucedemo:install": "npm --prefix examples/saucedemo install",
|
|
55
55
|
"example:saucedemo:test": "npm --prefix examples/saucedemo test",
|
|
56
56
|
"example:saucedemo:auto": "npm --prefix examples/saucedemo run auto",
|
|
57
57
|
"check:structure": "node scripts/check-structure.mjs",
|
|
58
|
-
"docs:dev": "vitepress dev docs",
|
|
59
|
-
"docs:build": "vitepress build docs",
|
|
60
|
-
"docs:preview": "vitepress preview docs"
|
|
58
|
+
"docs:dev": "npm run docs:api && vitepress dev docs",
|
|
59
|
+
"docs:build": "npm run docs:api && vitepress build docs",
|
|
60
|
+
"docs:preview": "vitepress preview docs",
|
|
61
|
+
"docs:api": "typedoc && node scripts/gen-deprecated.mjs",
|
|
62
|
+
"knip": "knip"
|
|
61
63
|
},
|
|
62
64
|
"peerDependencies": {
|
|
63
65
|
"@playwright/test": "^1.40.0"
|
|
@@ -70,6 +72,10 @@
|
|
|
70
72
|
"devDependencies": {
|
|
71
73
|
"@playwright/test": "^1.63.0",
|
|
72
74
|
"@types/node": "^26.0.0",
|
|
75
|
+
"knip": "^6.42.1",
|
|
76
|
+
"typedoc": "^0.28.20",
|
|
77
|
+
"typedoc-plugin-markdown": "^4.14.1",
|
|
78
|
+
"typedoc-vitepress-theme": "^1.1.4",
|
|
73
79
|
"typescript": "^5.6.0",
|
|
74
80
|
"vitepress": "^1.6.4"
|
|
75
81
|
}
|
|
@@ -20,13 +20,15 @@ More: `npm run check` (lint your Blocks), `npm run graph` (see the whole graph),
|
|
|
20
20
|
## Where things live
|
|
21
21
|
|
|
22
22
|
See [STRUCTURE.md](./STRUCTURE.md). Short version: one folder per page under `src/map/`,
|
|
23
|
-
one Block per action,
|
|
23
|
+
one Block per action, one flow per use case in `src/flows/` (a folder per page: its success, its failures, its bad inputs).
|
|
24
24
|
|
|
25
25
|
## Point it at your real app
|
|
26
26
|
|
|
27
27
|
Change each `_nav.block.ts`'s `url` from the local `file://` path to a real one, and set a real
|
|
28
|
-
`baseUrl` if your Nav Blocks use relative paths (`waygraph.config.ts`, a `.env`, or
|
|
29
|
-
`package.json`'s `"waygraph"` field - see the main docs' "Project config" section).
|
|
28
|
+
`baseUrl` if your Nav Blocks use relative paths (`config/waygraph.config.ts`, a `.env`, or
|
|
29
|
+
`package.json`'s `"waygraph"` field - see the main docs' "Project config" section). That file also
|
|
30
|
+
holds the test settings (`testDir`, `webServer`, `playwright: { workers, timeout, ... }`), so the
|
|
31
|
+
project needs no `playwright.config.ts`. Keep each
|
|
30
32
|
`src/map/` folder matching the URL it represents (`waygraph map` enforces this).
|
|
31
33
|
|
|
32
34
|
Docs: https://deviate-dv8.github.io/waygraph/
|
|
@@ -7,6 +7,8 @@ Next.js route groups) and never appear in a URL or Checkpoint name.
|
|
|
7
7
|
```text
|
|
8
8
|
.
|
|
9
9
|
├── package.json
|
|
10
|
+
├── .agents/skills/ # waygraph skills for coding agents
|
|
11
|
+
├── config/waygraph.config.ts # baseUrl, tests, extensions, webServer - no playwright.config
|
|
10
12
|
├── src/
|
|
11
13
|
│ ├── map/
|
|
12
14
|
│ │ ├── (app_base)/ # your app (opened via file:// here)
|
|
@@ -22,9 +24,13 @@ Next.js route groups) and never appear in a URL or Checkpoint name.
|
|
|
22
24
|
│ │ │ │ ├── assert-hello.method.block.ts # + stubs + YAP slide
|
|
23
25
|
│ │ │ │ └── assert-item-added.method.block.ts
|
|
24
26
|
│ │ │ └── docs/ # a second page, same shape
|
|
25
|
-
│ │
|
|
27
|
+
│ │ ├── (external)/mailpit/ # a different origin (mail catcher)
|
|
28
|
+
│ │ └── (external)/hoppscotch/ # API client, REST client, needs the hoppscotch extension
|
|
26
29
|
│ ├── states/ # Checkpoint types + mem keys
|
|
27
|
-
│ └── flows/ #
|
|
30
|
+
│ └── flows/ # use cases, one file per scenario (see flows/README.md)
|
|
31
|
+
│ ├── home/ # a page: hello, cart
|
|
32
|
+
│ ├── (user-stories)/ # flows that cross pages
|
|
33
|
+
│ └── (external)/hoppscotch/ # a tool on another origin
|
|
28
34
|
└── tests/example.spec.ts
|
|
29
35
|
```
|
|
30
36
|
|
|
@@ -38,8 +44,8 @@ page folder uses, so any agent or teammate knows where to look without asking.
|
|
|
38
44
|
| Add a page | copy a folder under `src/map/`, keep the `_nav` / `_page` / `_sel` names |
|
|
39
45
|
| Add an action on a page | one file in that page's `_methods/` |
|
|
40
46
|
| Verify a state (button disabled, error shown) | `defineAssertBlock` + `Trait.disabled` / `Trait.enabled` |
|
|
41
|
-
|
|
|
42
|
-
| Give a flow fake data | `registerMemStub(key, fake)` + `
|
|
47
|
+
| Define a use case (success, failed, bad input) | one `*.flow.ts` in `src/flows/<page>/`, or `(user-stories)/` for one that crosses pages |
|
|
48
|
+
| Give a flow fake data | `registerMemStub(key, fake)` + `memStub: true` in the flow's options (docs: docs/REFERENCE.md, "memStub") |
|
|
43
49
|
| Explore by hand | `npm run auto` (headed) or `npm run auto:cli` |
|
|
44
50
|
| Check your work | `npm run check`, `npm run graph`, `waygraph map` |
|
|
45
51
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Engine, defineConfig } from "waygraph";
|
|
2
|
+
|
|
3
|
+
const config = defineConfig(({ env }) => ({
|
|
4
|
+
// Where the app under test lives. BASE_URL comes from .env / .env.<mode>: waygraph test --env staging
|
|
5
|
+
baseUrl: env.BASE_URL ?? "__APP_BASE_URL__",
|
|
6
|
+
|
|
7
|
+
// ── waygraph: uncomment what you need ─────────────────────────────────────────────────────────
|
|
8
|
+
// extensions: ["hoppscotch"], // browser extensions to load (a waygraph feature, not Playwright's)
|
|
9
|
+
// video: { // recordings made with `waygraph run|demo --video`
|
|
10
|
+
// dir: "~/Downloads/waygraph-videos", // where they are saved (default: Downloads/waygraph-videos/<project>)
|
|
11
|
+
// viewport: "1920x1080", // recording size: demo defaults to 1920x1080, run to 1280x720
|
|
12
|
+
// },
|
|
13
|
+
// slowMo: 250, // shared by run and demo: ms to pause around each browser action
|
|
14
|
+
// headed: true, // shared: show the browser window (demo and auto already do)
|
|
15
|
+
// run: { // `waygraph run` only; overrides the shared values above
|
|
16
|
+
// slowMo: 0,
|
|
17
|
+
// video: { viewport: "1280x720" },
|
|
18
|
+
// },
|
|
19
|
+
// demo: { // `waygraph demo` only; overrides the shared values above
|
|
20
|
+
// pace: "fast", // default pace for steps that set none: "fast" | "slow" | "blitz" | 0.5 | 2
|
|
21
|
+
// speeds: { blitz: 0.05, fast: 0.33, slow: 2 }, // what each pace means, x normal dwell (lower = faster)
|
|
22
|
+
// typingMs: 30, // typing animation, ms per character (0 types instantly)
|
|
23
|
+
// slowMo: 350, // pause after each action, once per action (a step with pace "blitz" skips it)
|
|
24
|
+
// autoNext: true, // advance steps without clicking Next
|
|
25
|
+
// fast: false, // shorter Next / auto-next gates
|
|
26
|
+
// video: { viewport: "1920x1080" },
|
|
27
|
+
// },
|
|
28
|
+
// webServer: { command: "npm start", port: 3000 }, // started around test / run / demo / auto
|
|
29
|
+
|
|
30
|
+
// Another config for another environment: config/waygraph.staging.config.ts, chosen with `waygraph test --config staging`.
|
|
31
|
+
|
|
32
|
+
// ── test runner (Playwright) ──────────────────────────────────────────────────────────────────
|
|
33
|
+
// testDir: "tests",
|
|
34
|
+
// playwright: { // any Playwright test option, merged over waygraph's defaults
|
|
35
|
+
// workers: 1,
|
|
36
|
+
// retries: 1,
|
|
37
|
+
// timeout: 60_000,
|
|
38
|
+
// use: { video: "on", trace: "on", screenshot: "only-on-failure", viewport: { width: 1280, height: 720 } },
|
|
39
|
+
// },
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
export default config;
|
|
43
|
+
|
|
44
|
+
// Flows import this, so they run with the config above already applied.
|
|
45
|
+
export const engine = new Engine(config);
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
"graph:mermaid": "waygraph graph --mermaid",
|
|
14
14
|
"auto": "waygraph auto",
|
|
15
15
|
"auto:cli": "waygraph auto --cli",
|
|
16
|
-
"demo": "waygraph demo --blocks
|
|
17
|
-
"demo:auto-next": "waygraph demo --blocks
|
|
18
|
-
"demo:shop": "waygraph demo --blocks
|
|
19
|
-
"demo:shop:auto-next": "waygraph demo --blocks
|
|
20
|
-
"run": "waygraph run --blocks
|
|
21
|
-
"run:shop": "waygraph run --blocks
|
|
16
|
+
"demo": "waygraph demo --blocks homeHelloFlow",
|
|
17
|
+
"demo:auto-next": "waygraph demo --blocks homeHelloFlow --auto-next",
|
|
18
|
+
"demo:shop": "waygraph demo --blocks homeCartFlow",
|
|
19
|
+
"demo:shop:auto-next": "waygraph demo --blocks homeCartFlow --auto-next",
|
|
20
|
+
"run": "waygraph run --blocks homeHelloFlow",
|
|
21
|
+
"run:shop": "waygraph run --blocks homeCartFlow"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"waygraph": "
|
|
24
|
+
"waygraph": "latest"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@playwright/test": "^1.55.0",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { engine } from "../../../../config/waygraph.config.js";
|
|
2
|
+
import { NavHoppscotchSettingsBlock } from "../../../map/(external)/hoppscotch/settings/_nav.block.js";
|
|
3
|
+
import { PickExtensionInterceptorBlock } from "../../../map/(external)/hoppscotch/_methods/pick-extension-interceptor.method.block.js";
|
|
4
|
+
import { NavHoppscotchRestBlock } from "../../../map/(external)/hoppscotch/_nav.block.js";
|
|
5
|
+
import { SetQueryParamsBlock } from "../../../map/(external)/hoppscotch/_methods/set-query-params.method.block.js";
|
|
6
|
+
import { SetUrlBlock } from "../../../map/(external)/hoppscotch/_methods/set-url.method.block.js";
|
|
7
|
+
import { SetBearerTokenBlock } from "../../../map/(external)/hoppscotch/_methods/set-bearer-token.method.block.js";
|
|
8
|
+
import { SendRequestBlock } from "../../../map/(external)/hoppscotch/_methods/send-request.method.block.js";
|
|
9
|
+
import { AssertStatusBlock } from "../../../map/(external)/hoppscotch/_methods/assert-status.method.block.js";
|
|
10
|
+
import { ReadResponseHeadersBlock } from "../../../map/(external)/hoppscotch/_methods/read-response-headers.method.block.js";
|
|
11
|
+
import { AssertResponseHeaderBlock } from "../../../map/(external)/hoppscotch/_methods/assert-response-header.method.block.js";
|
|
12
|
+
import { ReadResponseBodyBlock } from "../../../map/(external)/hoppscotch/_methods/read-response-body.method.block.js";
|
|
13
|
+
import { AssertResponseBodyBlock } from "../../../map/(external)/hoppscotch/_methods/assert-response-body.method.block.js";
|
|
14
|
+
import "../../../map/(external)/hoppscotch/_page.block.js";
|
|
15
|
+
|
|
16
|
+
/** GET with query params and a bearer token, check the status, that the API received them, and a response header. Needs `extensions: ["hoppscotch"]`. */
|
|
17
|
+
export const hoppscotchAuthFlow = engine
|
|
18
|
+
.map()
|
|
19
|
+
.start()
|
|
20
|
+
.gotoExternal(
|
|
21
|
+
NavHoppscotchSettingsBlock.stubBefore((ctx) => {
|
|
22
|
+
ctx.title("Scaffold: Hoppscotch GET with query params and bearer token");
|
|
23
|
+
}),
|
|
24
|
+
)
|
|
25
|
+
.method(PickExtensionInterceptorBlock)
|
|
26
|
+
.gotoExternal(NavHoppscotchRestBlock)
|
|
27
|
+
.method(SetUrlBlock)
|
|
28
|
+
.method(SetQueryParamsBlock)
|
|
29
|
+
.method(SetBearerTokenBlock)
|
|
30
|
+
.method(SendRequestBlock)
|
|
31
|
+
.assert(AssertStatusBlock)
|
|
32
|
+
.method(ReadResponseBodyBlock)
|
|
33
|
+
.assert(AssertResponseBodyBlock)
|
|
34
|
+
.method(ReadResponseHeadersBlock)
|
|
35
|
+
.assert(AssertResponseHeaderBlock)
|
|
36
|
+
.end();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { engine } from "../../../../config/waygraph.config.js";
|
|
2
|
+
import { NavHoppscotchSettingsBlock } from "../../../map/(external)/hoppscotch/settings/_nav.block.js";
|
|
3
|
+
import { PickExtensionInterceptorBlock } from "../../../map/(external)/hoppscotch/_methods/pick-extension-interceptor.method.block.js";
|
|
4
|
+
import { NavHoppscotchRestBlock } from "../../../map/(external)/hoppscotch/_nav.block.js";
|
|
5
|
+
import { SetUrlBlock } from "../../../map/(external)/hoppscotch/_methods/set-url.method.block.js";
|
|
6
|
+
import { SendRequestBlock } from "../../../map/(external)/hoppscotch/_methods/send-request.method.block.js";
|
|
7
|
+
import { AssertStatusBlock } from "../../../map/(external)/hoppscotch/_methods/assert-status.method.block.js";
|
|
8
|
+
import "../../../map/(external)/hoppscotch/_page.block.js";
|
|
9
|
+
|
|
10
|
+
/** GET a URL and check the status. Needs `extensions: ["hoppscotch"]` in waygraph.config.ts. */
|
|
11
|
+
export const hoppscotchGetFlow = engine
|
|
12
|
+
.map()
|
|
13
|
+
.start()
|
|
14
|
+
.gotoExternal(
|
|
15
|
+
NavHoppscotchSettingsBlock.stubBefore((ctx) => {
|
|
16
|
+
ctx.title("Scaffold: Hoppscotch GET");
|
|
17
|
+
}),
|
|
18
|
+
)
|
|
19
|
+
.method(PickExtensionInterceptorBlock)
|
|
20
|
+
.gotoExternal(NavHoppscotchRestBlock)
|
|
21
|
+
.method(SetUrlBlock)
|
|
22
|
+
.method(SendRequestBlock)
|
|
23
|
+
.assert(AssertStatusBlock)
|
|
24
|
+
.end();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { engine } from "../../../../config/waygraph.config.js";
|
|
2
|
+
import { NavHoppscotchSettingsBlock } from "../../../map/(external)/hoppscotch/settings/_nav.block.js";
|
|
3
|
+
import { PickExtensionInterceptorBlock } from "../../../map/(external)/hoppscotch/_methods/pick-extension-interceptor.method.block.js";
|
|
4
|
+
import { NavHoppscotchRestBlock } from "../../../map/(external)/hoppscotch/_nav.block.js";
|
|
5
|
+
import { SetMethodBlock } from "../../../map/(external)/hoppscotch/_methods/set-method.method.block.js";
|
|
6
|
+
import { SetUrlBlock } from "../../../map/(external)/hoppscotch/_methods/set-url.method.block.js";
|
|
7
|
+
import { SetHeadersBlock } from "../../../map/(external)/hoppscotch/_methods/set-headers.method.block.js";
|
|
8
|
+
import { SetJsonBodyBlock } from "../../../map/(external)/hoppscotch/_methods/set-json-body.method.block.js";
|
|
9
|
+
import { SendRequestBlock } from "../../../map/(external)/hoppscotch/_methods/send-request.method.block.js";
|
|
10
|
+
import { AssertStatusBlock } from "../../../map/(external)/hoppscotch/_methods/assert-status.method.block.js";
|
|
11
|
+
import { ReadResponseBodyBlock } from "../../../map/(external)/hoppscotch/_methods/read-response-body.method.block.js";
|
|
12
|
+
import { AssertResponseBodyBlock } from "../../../map/(external)/hoppscotch/_methods/assert-response-body.method.block.js";
|
|
13
|
+
import "../../../map/(external)/hoppscotch/_page.block.js";
|
|
14
|
+
|
|
15
|
+
/** POST JSON with a header, check the status and the response body. Needs `extensions: ["hoppscotch"]`. */
|
|
16
|
+
export const hoppscotchPostFlow = engine
|
|
17
|
+
.map()
|
|
18
|
+
.start()
|
|
19
|
+
.gotoExternal(
|
|
20
|
+
NavHoppscotchSettingsBlock.stubBefore((ctx) => {
|
|
21
|
+
ctx.title("Scaffold: Hoppscotch POST with header and JSON body");
|
|
22
|
+
}),
|
|
23
|
+
)
|
|
24
|
+
.method(PickExtensionInterceptorBlock)
|
|
25
|
+
.gotoExternal(NavHoppscotchRestBlock)
|
|
26
|
+
.method(SetMethodBlock)
|
|
27
|
+
.method(SetUrlBlock)
|
|
28
|
+
.method(SetHeadersBlock)
|
|
29
|
+
.method(SetJsonBodyBlock)
|
|
30
|
+
.method(SendRequestBlock)
|
|
31
|
+
.assert(AssertStatusBlock)
|
|
32
|
+
.method(ReadResponseBodyBlock)
|
|
33
|
+
.assert(AssertResponseBodyBlock)
|
|
34
|
+
.end();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { engine } from "../../../config/waygraph.config.js";
|
|
2
|
+
import { NavMailpitInboxBlock } from "../../map/(external)/mailpit/_nav.block.js";
|
|
3
|
+
import { AssertEmailReceivedBlock } from "../../map/(external)/mailpit/_methods/assert-email-received.method.block.js";
|
|
4
|
+
import { OpenMessageBlock } from "../../map/(external)/mailpit/_methods/open-message.method.block.js";
|
|
5
|
+
import { AssertEmailContentBlock } from "../../map/(external)/mailpit/_methods/assert-email-content.method.block.js";
|
|
6
|
+
import { ExtractEmailLinkBlock } from "../../map/(external)/mailpit/_methods/extract-email-link.method.block.js";
|
|
7
|
+
import { NavVerificationLinkBlock } from "../../map/(app_base)/home/nav-verification-link.block.js";
|
|
8
|
+
import "../../map/(external)/mailpit/_page.block.js";
|
|
9
|
+
import { MailpitSel } from "../../map/(external)/mailpit/_sel.js";
|
|
10
|
+
|
|
11
|
+
/** Needs a Mailpit at WAYGRAPH_MAIL_URL (default http://127.0.0.1:8025); tests/mail-verify.spec.ts starts one. */
|
|
12
|
+
export const emailVerificationFlow = engine
|
|
13
|
+
.map()
|
|
14
|
+
.start()
|
|
15
|
+
.gotoExternal(
|
|
16
|
+
NavMailpitInboxBlock.stubBefore((ctx) => {
|
|
17
|
+
ctx.title("Scaffold: Cross-origin mail verification (QA-rich)");
|
|
18
|
+
}),
|
|
19
|
+
)
|
|
20
|
+
.assert(
|
|
21
|
+
AssertEmailReceivedBlock.stubAfter((ctx) => {
|
|
22
|
+
ctx.ring("note", {
|
|
23
|
+
selector: MailpitSel.messageRow,
|
|
24
|
+
label: "AC · email arrived",
|
|
25
|
+
detail: "Confirmed before opening anything.",
|
|
26
|
+
tag: "AC",
|
|
27
|
+
});
|
|
28
|
+
}),
|
|
29
|
+
)
|
|
30
|
+
.method(OpenMessageBlock)
|
|
31
|
+
.assert(
|
|
32
|
+
AssertEmailContentBlock.stubAfter((ctx) => {
|
|
33
|
+
ctx.ring("note", { selector: MailpitSel.previewIframe, label: "AC · email body matches expected copy", tag: "AC" });
|
|
34
|
+
}),
|
|
35
|
+
)
|
|
36
|
+
.method(
|
|
37
|
+
ExtractEmailLinkBlock.stubAfter((ctx) => {
|
|
38
|
+
ctx.ring("note", {
|
|
39
|
+
selector: MailpitSel.previewIframe,
|
|
40
|
+
label: "AC · link found",
|
|
41
|
+
detail: "Read from the message preview iframe.",
|
|
42
|
+
tag: "AC",
|
|
43
|
+
});
|
|
44
|
+
}),
|
|
45
|
+
)
|
|
46
|
+
.gotoPage(NavVerificationLinkBlock)
|
|
47
|
+
.end();
|
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
# `src/flows/`
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
all point at an export here by name (`waygraph run --blocks
|
|
3
|
+
A flow is one **use case**: a path a user takes through the app, with its own pass or fail. `waygraph run`, `demo` and
|
|
4
|
+
`test` all point at an export here by name (`waygraph run --blocks homeCartFlow`).
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
`.branch()`) over hand-composing raw Blocks - it checks each step's Block kind at runtime and
|
|
8
|
-
the Checkpoint chain at compile time, so a wrong step is a build/test failure, not a
|
|
9
|
-
mid-run surprise.
|
|
10
|
-
- `withTitle`/`withHighlightFixtures`/`withSessionReset`/etc. are non-destructive wrappers -
|
|
11
|
-
`someFlow.withTitle("...")` doesn't mutate `someFlow`, it returns a new one. Compose them
|
|
12
|
-
around the finished `.end()` result, not inside the Map chain itself.
|
|
13
|
-
- One flow, one scenario. A flow that needs a genuinely different starting session
|
|
14
|
-
(re-entering the login page mid-chain) wraps that segment in `withSessionReset`, rather than
|
|
15
|
-
hand-clearing cookies inside a Block.
|
|
6
|
+
## The folder mocks the front end
|
|
16
7
|
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
`src/flows/` mirrors the app's endpoints the way `src/map/` does, but where the map holds the *pieces* of a page, a flow
|
|
9
|
+
folder holds the *stories* of it. One folder per page or area, one file per use case. The scaffold only has the demo pages, so `auth/login/` below is the shape to copy for a real login page:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
src/flows/
|
|
13
|
+
auth/login/
|
|
14
|
+
success.flow.ts # valid credentials -> dashboard
|
|
15
|
+
wrong-password.flow.ts # refused, error banner, stays on login
|
|
16
|
+
empty-fields.flow.ts # each required field flags itself
|
|
17
|
+
home/
|
|
18
|
+
hello.flow.ts
|
|
19
|
+
cart.flow.ts
|
|
20
|
+
(user-stories)/ # not a page: a story that crosses pages
|
|
21
|
+
email-verification.flow.ts
|
|
22
|
+
(external)/hoppscotch/ # a tool on another origin
|
|
23
|
+
post.flow.ts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **`<area>/<page>/`** matches the app URL, like the map. Open the folder and you see every way a user can go through that
|
|
27
|
+
page: the happy path, each failure, each bad input.
|
|
28
|
+
- **`(user-stories)/`** (any `(group)` name) is for flows that don't belong to one page: checkout end to end, onboarding,
|
|
29
|
+
"a viewer is locked out". Parentheses mean organizational only, as in the map.
|
|
30
|
+
- **A file is one scenario**, named for the outcome (`success`, `wrong-password`), not for the Blocks it happens to use.
|
|
31
|
+
An export is `<page><Outcome>Flow` (`loginWrongPasswordFlow`) so `--blocks` names stay unique across the project; a file path works too (`waygraph run src/flows/auth/login/success.flow.ts`).
|
|
32
|
+
|
|
33
|
+
## Writing one
|
|
34
|
+
|
|
35
|
+
- Build it with `engine.map({ memStub })…end()`; nest another with `.flow(sub, { reset: true })`. No `defineFlow([...])`,
|
|
36
|
+
no nested `withTitle(withMemStub(...))`, and `.end()` takes nothing.
|
|
37
|
+
- The flow's name and narration are ctx stubs chained on the Block where they apply: `ctx.title("…")`, `ctx.ring(…)`.
|
|
38
|
+
- A flow expected to fail (a locked-out user) says so in `map({ expectedFailure: "…" })`.
|
|
39
|
+
- A step's speed in the demo is `{ pace }`: `.method(FillBlock, { pace: "blitz" })`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { engine } from "../../../config/waygraph.config.js";
|
|
2
|
+
import { DemoSel } from "../../map/(app_base)/home/_sel.js";
|
|
3
|
+
import { NavHomeBlock } from "../../map/(app_base)/home/_nav.block.js";
|
|
4
|
+
import { AddItemBlock } from "../../map/(app_base)/home/_methods/add-item.effect.block.js";
|
|
5
|
+
import { AssertItemAddedBlock } from "../../map/(app_base)/home/_methods/assert-item-added.method.block.js";
|
|
6
|
+
import { RemoveItemBlock } from "../../map/(app_base)/home/_methods/remove-item.effect.block.js";
|
|
7
|
+
import { ClearCartBlock } from "../../map/(app_base)/home/_methods/clear-cart.method.block.js";
|
|
8
|
+
import "../../states/demo.mem-stubs.js";
|
|
9
|
+
import "../../map/(app_base)/home/_page.block.js";
|
|
10
|
+
|
|
11
|
+
// `map({ memStub: true })` fills SelectedItem (alpha) from src/states/demo.mem-stubs.ts; --data overrides it.
|
|
12
|
+
const AddShown = AddItemBlock.stubBefore((ctx) => {
|
|
13
|
+
ctx.ring("add", { selector: DemoSel.catalog, label: "Catalog" });
|
|
14
|
+
})
|
|
15
|
+
.stubAfter((ctx) => {
|
|
16
|
+
ctx.ring("cart", {
|
|
17
|
+
selector: DemoSel.cartCount,
|
|
18
|
+
label: "AC · item in cart",
|
|
19
|
+
detail: "Cart count bumped after Add.",
|
|
20
|
+
tag: "AC",
|
|
21
|
+
duration: true,
|
|
22
|
+
});
|
|
23
|
+
})
|
|
24
|
+
.stubOnError((ctx) => {
|
|
25
|
+
ctx.ring("cart", { selector: DemoSel.cartCount, label: "Cart at failure", tag: "FAIL" });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const RemoveShown = RemoveItemBlock.stubAfter((ctx) => {
|
|
29
|
+
ctx.ring("cart", { selector: DemoSel.cartCount, label: "Cart count", duration: true });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const ClearShown = ClearCartBlock.stubBefore((ctx) => {
|
|
33
|
+
ctx.ring("clear", { selector: DemoSel.clear, label: "Clear cart" });
|
|
34
|
+
}).stubAfter((ctx) => {
|
|
35
|
+
ctx.ring("cart", { selector: DemoSel.cartCount, label: "Empty cart", duration: true });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const homeCartFlow = engine
|
|
39
|
+
.map({ memStub: true })
|
|
40
|
+
.start()
|
|
41
|
+
.gotoPage(
|
|
42
|
+
NavHomeBlock.stubBefore((ctx) => {
|
|
43
|
+
ctx.title("Scaffold: Shop (Effect + clear)");
|
|
44
|
+
}),
|
|
45
|
+
)
|
|
46
|
+
.method(AddShown)
|
|
47
|
+
.assert(AssertItemAddedBlock)
|
|
48
|
+
.method(RemoveShown)
|
|
49
|
+
.method(AddShown)
|
|
50
|
+
.method(ClearShown)
|
|
51
|
+
.end();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { engine } from "../../../config/waygraph.config.js";
|
|
2
|
+
import { DemoSel } from "../../map/(app_base)/home/_sel.js";
|
|
3
|
+
import { NavHomeBlock } from "../../map/(app_base)/home/_nav.block.js";
|
|
4
|
+
import { AssertHelloBlock } from "../../map/(app_base)/home/_methods/assert-hello.method.block.js";
|
|
5
|
+
import { NavDocsBlock } from "../../map/(app_base)/docs/_nav.block.js";
|
|
6
|
+
// Page hubs registered for auto/docs grouping (arrival-only after nav).
|
|
7
|
+
import "../../map/(app_base)/home/_page.block.js";
|
|
8
|
+
import "../../map/(app_base)/docs/_page.block.js";
|
|
9
|
+
|
|
10
|
+
/** Smoke: nav + assert (no mem), then nav to the sibling docs/ page. Green offline. */
|
|
11
|
+
export const homeHelloFlow = engine
|
|
12
|
+
.map()
|
|
13
|
+
.start()
|
|
14
|
+
.gotoPage(
|
|
15
|
+
NavHomeBlock.stubBefore((ctx) => {
|
|
16
|
+
ctx.title("Scaffold: Hello Waygraph");
|
|
17
|
+
}).stubAfter((ctx) => {
|
|
18
|
+
ctx.ring("title", { selector: DemoSel.title, label: "Home heading", duration: true });
|
|
19
|
+
}).stubOnError((ctx) => {
|
|
20
|
+
ctx.ring("title", { selector: DemoSel.title, label: "Home heading at failure" });
|
|
21
|
+
}),
|
|
22
|
+
)
|
|
23
|
+
.assert(
|
|
24
|
+
AssertHelloBlock.stubBefore((ctx) => {
|
|
25
|
+
ctx.ring("title", { selector: DemoSel.title, label: "Heading about to check" });
|
|
26
|
+
})
|
|
27
|
+
.stubAfter((ctx) => {
|
|
28
|
+
ctx.ring("title", {
|
|
29
|
+
selector: DemoSel.title,
|
|
30
|
+
label: "AC · Hello visible",
|
|
31
|
+
detail: "Replace this copy with your ticket lines.",
|
|
32
|
+
tag: "AC",
|
|
33
|
+
duration: true,
|
|
34
|
+
});
|
|
35
|
+
ctx.ring("sub", { selector: DemoSel.sub, label: "Subcopy", duration: 1500 });
|
|
36
|
+
})
|
|
37
|
+
.stubOnError((ctx) => {
|
|
38
|
+
ctx.ring("title", { selector: DemoSel.title, label: "Heading at failure", tag: "FAIL" });
|
|
39
|
+
}),
|
|
40
|
+
)
|
|
41
|
+
.gotoPage(NavDocsBlock)
|
|
42
|
+
.end();
|
|
@@ -2,15 +2,6 @@ import { defineNavBlock, Trait } from "waygraph";
|
|
|
2
2
|
import type { Docs } from "../../../states/demo.states.js";
|
|
3
3
|
import { DocsSel, DOCS_URL } from "./_sel.js";
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Kind: Nav
|
|
7
|
-
* Route: (app_base)/docs/
|
|
8
|
-
*
|
|
9
|
-
* Same shape as home/ (its own file:// page, no server) - lives
|
|
10
|
-
* under (app_base), not (external). See docs/REFERENCE.md "Waygraph Map" and
|
|
11
|
-
* openspec/changes/waygraph-map. `(app_base)` is purely organizational
|
|
12
|
-
* (Next.js route-group style) - never part of the Checkpoint tag.
|
|
13
|
-
*/
|
|
14
5
|
export const NavDocsBlock = defineNavBlock<Docs>({
|
|
15
6
|
name: "nav-docs",
|
|
16
7
|
description: "Opens the docs page (Waygraph Map convention demo).",
|
|
@@ -2,11 +2,6 @@ import { definePageBlock, Trait } from "waygraph";
|
|
|
2
2
|
import type { Docs } from "../../../states/demo.states.js";
|
|
3
3
|
import { DocsSel } from "./_sel.js";
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Kind: Page
|
|
7
|
-
* Route: (app_base)/docs/
|
|
8
|
-
* Arrival-only (nav.block.ts already landed on Docs).
|
|
9
|
-
*/
|
|
10
5
|
export const DocsPageBlock = definePageBlock<Docs>({
|
|
11
6
|
name: "page-docs",
|
|
12
7
|
description: "Docs hub - Waygraph Map convention demo.",
|
|
@@ -7,14 +7,7 @@ import type { CanAddItem, ItemInCart } from "../../../../states/demo.states.js";
|
|
|
7
7
|
import { SelectedItem } from "../../../../states/demo.mem-keys.js";
|
|
8
8
|
import { DemoSel } from "../_sel.js";
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Kind: Effect
|
|
12
|
-
* Helper: defineEffectBlock
|
|
13
|
-
* Route: demo-web/methods/
|
|
14
|
-
*
|
|
15
|
-
* Mutates one catalog row from mem. `waygraph auto` lists one "Add …" row per
|
|
16
|
-
* live Add button via instanceOptions.
|
|
17
|
-
*/
|
|
10
|
+
/** `waygraph auto` lists one "Add …" row per live Add button via `instanceOptions`. */
|
|
18
11
|
export const AddItemBlock = defineEffectBlock<CanAddItem, ItemInCart>({
|
|
19
12
|
name: "add-item",
|
|
20
13
|
description: "Adds the mem-selected catalog item to the cart.",
|
|
@@ -36,7 +29,7 @@ export const AddItemBlock = defineEffectBlock<CanAddItem, ItemInCart>({
|
|
|
36
29
|
],
|
|
37
30
|
},
|
|
38
31
|
async instanceOptions(page): Promise<readonly WaygraphInstanceOption[]> {
|
|
39
|
-
const buttons = page.locator(
|
|
32
|
+
const buttons = page.locator(DemoSel.addButtons);
|
|
40
33
|
const n = await buttons.count();
|
|
41
34
|
const out: WaygraphInstanceOption[] = [];
|
|
42
35
|
for (let i = 0; i < n; i++) {
|
|
@@ -55,13 +48,4 @@ export const AddItemBlock = defineEffectBlock<CanAddItem, ItemInCart>({
|
|
|
55
48
|
}
|
|
56
49
|
return out;
|
|
57
50
|
},
|
|
58
|
-
})
|
|
59
|
-
.stubBefore((ctx) => {
|
|
60
|
-
ctx.ring("add", { selector: DemoSel.catalog, label: "Catalog" });
|
|
61
|
-
})
|
|
62
|
-
.stubAfter((ctx) => {
|
|
63
|
-
ctx.ring("cart", { selector: DemoSel.cartCount, label: "Cart count", duration: true });
|
|
64
|
-
})
|
|
65
|
-
.stubOnError((ctx) => {
|
|
66
|
-
ctx.ring("cart", { selector: DemoSel.cartCount, label: "Cart at failure", tag: "FAIL" });
|
|
67
|
-
});
|
|
51
|
+
});
|
|
@@ -2,15 +2,7 @@ import { defineAssertBlock, Trait } from "waygraph";
|
|
|
2
2
|
import type { Home } from "../../../../states/demo.states.js";
|
|
3
3
|
import { DemoSel } from "../_sel.js";
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Kind: Assert
|
|
7
|
-
* Helper: defineAssertBlock
|
|
8
|
-
* Route: demo-web/methods/
|
|
9
|
-
*
|
|
10
|
-
* Self-loop-only "verify what's already on the page" check - no hand-written
|
|
11
|
-
* act/resolve to accidentally do a second thing. Same-URL assert after nav,
|
|
12
|
-
* with stubs + fixtures + a YAP slide.
|
|
13
|
-
*/
|
|
5
|
+
/** Heading still shows after arrival; the slide is a YAP caption for the demo overlay. */
|
|
14
6
|
export const AssertHelloBlock = defineAssertBlock<Home>({
|
|
15
7
|
name: "assert-hello",
|
|
16
8
|
description: "Confirms the hello heading is still visible after arrival.",
|
|
@@ -25,19 +17,4 @@ export const AssertHelloBlock = defineAssertBlock<Home>({
|
|
|
25
17
|
duration: true,
|
|
26
18
|
},
|
|
27
19
|
],
|
|
28
|
-
})
|
|
29
|
-
.stubBefore((ctx) => {
|
|
30
|
-
ctx.ring("title", { selector: DemoSel.title, label: "Heading about to check" });
|
|
31
|
-
})
|
|
32
|
-
.stubAfter((ctx) => {
|
|
33
|
-
ctx.ring("title", {
|
|
34
|
-
selector: DemoSel.title,
|
|
35
|
-
label: "Hello confirmed",
|
|
36
|
-
detail: "Offline scaffold proof point.",
|
|
37
|
-
duration: true,
|
|
38
|
-
});
|
|
39
|
-
ctx.ring("sub", { selector: DemoSel.sub, label: "Subcopy", duration: 1500 });
|
|
40
|
-
})
|
|
41
|
-
.stubOnError((ctx) => {
|
|
42
|
-
ctx.ring("title", { selector: DemoSel.title, label: "Heading at failure", tag: "FAIL" });
|
|
43
|
-
});
|
|
20
|
+
});
|
package/templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts
CHANGED
|
@@ -3,11 +3,7 @@ import type { ItemInCart } from "../../../../states/demo.states.js";
|
|
|
3
3
|
import { SelectedItem } from "../../../../states/demo.mem-keys.js";
|
|
4
4
|
import { DemoSel } from "../_sel.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* Mem-aware Trait: which item's Remove button should be visible depends on
|
|
8
|
-
* which row add-item just acted on (mem, not a static selector) - a Trait's
|
|
9
|
-
* own `check(page, mem)` already receives mem, so this reads it directly.
|
|
10
|
-
*/
|
|
6
|
+
// Reads mem at check time, so it follows whichever item add-item acted on.
|
|
11
7
|
const removeButtonVisibleForSelectedItem: TraitType = {
|
|
12
8
|
name: "remove-button-visible-for-selected-item",
|
|
13
9
|
async check(page, mem) {
|
|
@@ -16,20 +12,7 @@ const removeButtonVisibleForSelectedItem: TraitType = {
|
|
|
16
12
|
},
|
|
17
13
|
};
|
|
18
14
|
|
|
19
|
-
/**
|
|
20
|
-
* Kind: Assert
|
|
21
|
-
* Helper: defineAssertBlock
|
|
22
|
-
* Route: demo-web/methods/
|
|
23
|
-
*
|
|
24
|
-
* "Did add-item actually work" - provable only after the fact, from the
|
|
25
|
-
* method's real effect on the page: the specific row's own Remove button
|
|
26
|
-
* appeared (mem-matched, not just "some" row), AND the aggregate cart count
|
|
27
|
-
* reflects it. Self-loop on ItemInCart - no state change, verify only.
|
|
28
|
-
*
|
|
29
|
-
* Explicit `<ItemInCart>` type argument - `defineAssertBlock` defaults to
|
|
30
|
-
* wildcard `Checkpoint<string>` when uninferred, which only "works" when the
|
|
31
|
-
* assert sits last in a chain; this one sits mid-chain (see shop.flow.ts).
|
|
32
|
-
*/
|
|
15
|
+
/** The mem-selected item's own Remove button appeared and the cart count reflects it. */
|
|
33
16
|
export const AssertItemAddedBlock = defineAssertBlock<ItemInCart>({
|
|
34
17
|
name: "assert-item-added",
|
|
35
18
|
description: "Confirms the mem-selected item's own Remove button appeared and the cart count reflects it.",
|