waygraph 0.15.49 → 0.15.50
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/auto-session/session.js +1 -1
- package/dist/cli/flags.d.ts +7 -5
- package/dist/cli/flags.js +9 -32
- package/dist/cli/run-chain.js +1 -1
- package/dist/commands/check.js +1 -1
- package/dist/commands/run.js +4 -4
- package/dist/commands/session.js +7 -7
- package/dist/config.d.ts +34 -0
- package/dist/config.js +94 -0
- package/dist/env-file.d.ts +10 -0
- package/dist/env-file.js +58 -0
- package/dist/highlights/types.d.ts +6 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/runner/inpage/caption.js +2 -2
- package/dist/session/auto-explore-run.d.ts +4 -1
- package/dist/session/auto-explore-run.js +5 -26
- package/dist/traverse/run.js +2 -21
- package/examples/saucedemo/.waygraph-auto/161cc8dd.log +22 -0
- package/examples/saucedemo/.waygraph-auto/22048fff.log +4 -0
- package/examples/saucedemo/.waygraph-auto/5d285cd3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7fcb512f.log +22 -0
- package/examples/saucedemo/.waygraph-auto/84dc6480.log +6 -0
- package/examples/saucedemo/.waygraph-auto/88eda676.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8a08b68d.log +6 -0
- package/examples/saucedemo/.waygraph-auto/fdf66b74.log +0 -0
- package/package.json +1 -1
- package/templates/scaffold/src/flows/shop.flow.ts +42 -27
|
@@ -54,7 +54,7 @@ export class AutoSession {
|
|
|
54
54
|
this.headless = headless;
|
|
55
55
|
}
|
|
56
56
|
static async start(init) {
|
|
57
|
-
const baseURL = init.baseURL ?? resolveBaseUrl(init.projectDir);
|
|
57
|
+
const baseURL = init.baseURL ?? (await resolveBaseUrl(init.projectDir));
|
|
58
58
|
const skipNav = init.skipInitialNavigation === true;
|
|
59
59
|
const startUrl = skipNav ? undefined : (init.startUrl ?? baseURL);
|
|
60
60
|
const exploreOpts = {
|
package/dist/cli/flags.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveBaseUrl } from "../config.js";
|
|
1
2
|
interface RunFlags {
|
|
2
3
|
step?: boolean;
|
|
3
4
|
autoplay?: boolean;
|
|
@@ -73,16 +74,18 @@ export declare function applyRunFlags(flags: RunFlags, opts?: {
|
|
|
73
74
|
allowDemoUi?: boolean;
|
|
74
75
|
}): void;
|
|
75
76
|
/**
|
|
76
|
-
* BASE_URL when neither --base-url nor WAYGRAPH_BASE_URL is set
|
|
77
|
-
*
|
|
77
|
+
* BASE_URL when neither --base-url nor WAYGRAPH_BASE_URL is set - see `../config.js`'s own
|
|
78
|
+
* `resolveBaseUrl` for the full resolution order (env / `.env` / `waygraph.config.*` /
|
|
79
|
+
* package.json / playwright.config). Re-exported here so existing importers of this module
|
|
80
|
+
* don't need to change their import path.
|
|
78
81
|
*/
|
|
79
|
-
export
|
|
82
|
+
export { resolveBaseUrl };
|
|
80
83
|
/**
|
|
81
84
|
* Friendly demo defaults: STEP on; headed only for live watch (no --video).
|
|
82
85
|
* --video / --auto-play-video => headless unless --non-headless.
|
|
83
86
|
* Call after applyRunFlags so explicit flags already won.
|
|
84
87
|
*/
|
|
85
|
-
export declare function applyDemoDefaults(projectDir: string): void
|
|
88
|
+
export declare function applyDemoDefaults(projectDir: string): Promise<void>;
|
|
86
89
|
export declare function resolveProjectDir(flags: RunFlags, fallbackIndex?: number): string;
|
|
87
90
|
export declare function resolveSpec(flags: RunFlags): string | undefined;
|
|
88
91
|
/** `shop.flow.ts` / `src/flows/cart-bulk.flow.ts` / absolute path — not a Checkpoint or export id. */
|
|
@@ -92,4 +95,3 @@ export declare function looksLikeFlowFileRef(ref: string): boolean;
|
|
|
92
95
|
* `src/flows/shop.flow.ts` → `shopFlow`; leaves `loginFlow then nav-cart` alone.
|
|
93
96
|
*/
|
|
94
97
|
export declare function expandSpecFlowFiles(projectDir: string, spec: string): Promise<string>;
|
|
95
|
-
export {};
|
package/dist/cli/flags.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
-
import { basename,
|
|
2
|
+
import { basename, relative, resolve } from "node:path";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { walkDir } from "./discover.js";
|
|
5
|
+
import { resolveBaseUrl } from "../config.js";
|
|
5
6
|
function takeFlagValue(argv, i, a, flag) {
|
|
6
7
|
if (a.startsWith(flag + "=")) {
|
|
7
8
|
return { value: a.slice(flag.length + 1), nextI: i };
|
|
@@ -310,42 +311,18 @@ export function applyRunFlags(flags, opts) {
|
|
|
310
311
|
}
|
|
311
312
|
}
|
|
312
313
|
/**
|
|
313
|
-
* BASE_URL when neither --base-url nor WAYGRAPH_BASE_URL is set
|
|
314
|
-
*
|
|
314
|
+
* BASE_URL when neither --base-url nor WAYGRAPH_BASE_URL is set - see `../config.js`'s own
|
|
315
|
+
* `resolveBaseUrl` for the full resolution order (env / `.env` / `waygraph.config.*` /
|
|
316
|
+
* package.json / playwright.config). Re-exported here so existing importers of this module
|
|
317
|
+
* don't need to change their import path.
|
|
315
318
|
*/
|
|
316
|
-
export
|
|
317
|
-
const pkgPath = join(projectDir, "package.json");
|
|
318
|
-
if (existsSync(pkgPath)) {
|
|
319
|
-
try {
|
|
320
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
321
|
-
const fromPkg = pkg.waygraph?.baseUrl ??
|
|
322
|
-
pkg.waygraph?.baseURL ??
|
|
323
|
-
pkg.config?.waygraph?.baseUrl ??
|
|
324
|
-
pkg.config?.waygraph?.baseURL;
|
|
325
|
-
if (typeof fromPkg === "string" && fromPkg.trim())
|
|
326
|
-
return fromPkg.trim();
|
|
327
|
-
}
|
|
328
|
-
catch {
|
|
329
|
-
// ignore malformed package.json - fall through to playwright
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
for (const name of ["playwright.config.ts", "playwright.config.mts", "playwright.config.js", "playwright.config.mjs"]) {
|
|
333
|
-
const p = join(projectDir, name);
|
|
334
|
-
if (!existsSync(p))
|
|
335
|
-
continue;
|
|
336
|
-
const src = readFileSync(p, "utf-8");
|
|
337
|
-
const m = src.match(/baseURL\s*:\s*["']([^"']+)["']/);
|
|
338
|
-
if (m?.[1])
|
|
339
|
-
return m[1];
|
|
340
|
-
}
|
|
341
|
-
return undefined;
|
|
342
|
-
}
|
|
319
|
+
export { resolveBaseUrl };
|
|
343
320
|
/**
|
|
344
321
|
* Friendly demo defaults: STEP on; headed only for live watch (no --video).
|
|
345
322
|
* --video / --auto-play-video => headless unless --non-headless.
|
|
346
323
|
* Call after applyRunFlags so explicit flags already won.
|
|
347
324
|
*/
|
|
348
|
-
export function applyDemoDefaults(projectDir) {
|
|
325
|
+
export async function applyDemoDefaults(projectDir) {
|
|
349
326
|
if (process.env.WAYGRAPH_STEP === undefined)
|
|
350
327
|
process.env.WAYGRAPH_STEP = "1";
|
|
351
328
|
if (process.env.WAYGRAPH_STEP === "1" && process.env.WAYGRAPH_HEADED === undefined) {
|
|
@@ -363,7 +340,7 @@ export function applyDemoDefaults(projectDir) {
|
|
|
363
340
|
}
|
|
364
341
|
}
|
|
365
342
|
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
366
|
-
const resolved = resolveBaseUrl(projectDir);
|
|
343
|
+
const resolved = await resolveBaseUrl(projectDir);
|
|
367
344
|
if (resolved)
|
|
368
345
|
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
369
346
|
}
|
package/dist/cli/run-chain.js
CHANGED
|
@@ -41,7 +41,7 @@ mod.startRunner();
|
|
|
41
41
|
export async function runChain(projectDir, spec) {
|
|
42
42
|
const expanded = await expandSpecFlowFiles(projectDir, spec);
|
|
43
43
|
if (process.env.WAYGRAPH_VIDEO && !process.env.WAYGRAPH_BASE_URL) {
|
|
44
|
-
const resolved = resolveBaseUrl(projectDir);
|
|
44
|
+
const resolved = await resolveBaseUrl(projectDir);
|
|
45
45
|
if (resolved)
|
|
46
46
|
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
47
47
|
}
|
package/dist/commands/check.js
CHANGED
|
@@ -67,7 +67,7 @@ export async function runChainAuto(projectDir, fromTag, toTag) {
|
|
|
67
67
|
const spec = path.join(" then ");
|
|
68
68
|
console.error(`waygraph chain auto: ${fromTag} -> ${toTag} via ${spec}`);
|
|
69
69
|
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
70
|
-
const resolved = resolveBaseUrl(projectDir);
|
|
70
|
+
const resolved = await resolveBaseUrl(projectDir);
|
|
71
71
|
if (resolved)
|
|
72
72
|
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
73
73
|
}
|
package/dist/commands/run.js
CHANGED
|
@@ -24,7 +24,7 @@ export async function runCase(args) {
|
|
|
24
24
|
process.exit(1);
|
|
25
25
|
}
|
|
26
26
|
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
27
|
-
const resolved = resolveBaseUrl(proj);
|
|
27
|
+
const resolved = await resolveBaseUrl(proj);
|
|
28
28
|
if (resolved)
|
|
29
29
|
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
30
30
|
}
|
|
@@ -60,13 +60,13 @@ export async function chainCase(args) {
|
|
|
60
60
|
}
|
|
61
61
|
const proj = resolve(flags.blocks ? (flags.positionals[0] ?? process.cwd()) : (flags.positionals[1] ?? process.cwd()));
|
|
62
62
|
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
63
|
-
const resolved = resolveBaseUrl(proj);
|
|
63
|
+
const resolved = await resolveBaseUrl(proj);
|
|
64
64
|
if (resolved)
|
|
65
65
|
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
66
66
|
}
|
|
67
67
|
// If caller asked for step overlay, behave like demo; else like run.
|
|
68
68
|
if (flags.step === true || process.env.WAYGRAPH_STEP === "1") {
|
|
69
|
-
applyDemoDefaults(proj);
|
|
69
|
+
await applyDemoDefaults(proj);
|
|
70
70
|
}
|
|
71
71
|
await runChain(proj, spec);
|
|
72
72
|
return;
|
|
@@ -92,7 +92,7 @@ export async function demoCase(args) {
|
|
|
92
92
|
if (flags.step === undefined && process.env.WAYGRAPH_STEP === undefined) {
|
|
93
93
|
process.env.WAYGRAPH_STEP = "1";
|
|
94
94
|
}
|
|
95
|
-
applyDemoDefaults(proj);
|
|
95
|
+
await applyDemoDefaults(proj);
|
|
96
96
|
console.error(`waygraph demo: STEP=${process.env.WAYGRAPH_STEP === "1" ? "on" : "off"}` +
|
|
97
97
|
` AUTO_NEXT=${process.env.WAYGRAPH_AUTOPLAY === "1" ? "on" : "off"}` +
|
|
98
98
|
` HEADED=${process.env.WAYGRAPH_HEADED === "0" ? "off" : "on"}` +
|
package/dist/commands/session.js
CHANGED
|
@@ -62,7 +62,7 @@ export async function sessionCase(args, command) {
|
|
|
62
62
|
console.error(`waygraph browser: no such directory: ${proj}`);
|
|
63
63
|
process.exit(1);
|
|
64
64
|
}
|
|
65
|
-
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
65
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? (await resolveBaseUrl(proj));
|
|
66
66
|
try {
|
|
67
67
|
const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
|
|
68
68
|
const result = await browserStart({
|
|
@@ -148,7 +148,7 @@ export async function sessionCase(args, command) {
|
|
|
148
148
|
const flags = parseRunFlags(flagArgs);
|
|
149
149
|
applyRunFlags(flags);
|
|
150
150
|
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
151
|
-
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
151
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? (await resolveBaseUrl(proj));
|
|
152
152
|
try {
|
|
153
153
|
const result = await pilotStart({
|
|
154
154
|
projectDir: proj,
|
|
@@ -483,7 +483,7 @@ export async function sessionCase(args, command) {
|
|
|
483
483
|
process.exit(1);
|
|
484
484
|
}
|
|
485
485
|
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
486
|
-
const resolved = resolveBaseUrl(proj);
|
|
486
|
+
const resolved = await resolveBaseUrl(proj);
|
|
487
487
|
if (resolved)
|
|
488
488
|
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
489
489
|
}
|
|
@@ -510,7 +510,7 @@ export async function sessionCase(args, command) {
|
|
|
510
510
|
console.error("waygraph auto --detach requires --cli (headful has no session control)");
|
|
511
511
|
process.exit(1);
|
|
512
512
|
}
|
|
513
|
-
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
513
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? (await resolveBaseUrl(proj));
|
|
514
514
|
try {
|
|
515
515
|
const meta = await spawnDetachedSession({
|
|
516
516
|
projectDir: proj,
|
|
@@ -535,7 +535,7 @@ export async function sessionCase(args, command) {
|
|
|
535
535
|
}
|
|
536
536
|
if (flags.blocks && isFileSelectToken(flags.blocks)) {
|
|
537
537
|
const blocksSelect = parseBlocksSelect(flags.blocks);
|
|
538
|
-
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
538
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? (await resolveBaseUrl(proj));
|
|
539
539
|
await runAutoExplore(proj, baseURL
|
|
540
540
|
? { cli: cliPicker, baseURL, blocksSelect }
|
|
541
541
|
: { cli: cliPicker, blocksSelect });
|
|
@@ -565,7 +565,7 @@ export async function sessionCase(args, command) {
|
|
|
565
565
|
}
|
|
566
566
|
return;
|
|
567
567
|
}
|
|
568
|
-
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
568
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? (await resolveBaseUrl(proj));
|
|
569
569
|
await runAutoExplore(proj, baseURL ? { cli: cliPicker, baseURL } : { cli: cliPicker });
|
|
570
570
|
return;
|
|
571
571
|
}
|
|
@@ -578,7 +578,7 @@ export async function autoServeCase(args) {
|
|
|
578
578
|
console.error("waygraph __auto-serve: missing --session-id");
|
|
579
579
|
process.exit(1);
|
|
580
580
|
}
|
|
581
|
-
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
581
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? (await resolveBaseUrl(proj));
|
|
582
582
|
const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
|
|
583
583
|
let headless;
|
|
584
584
|
if (flags.headlessBrowser)
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface WaygraphConfig {
|
|
2
|
+
/** Same value `resolveBaseUrl` would otherwise read from package.json's `waygraph.baseUrl`. */
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Identity helper, purely for editor type-checking/autocomplete - same role `defineConfig` plays
|
|
6
|
+
* in a `playwright.config.ts` or `vite.config.ts`. `export default defineConfig({ ... })`. */
|
|
7
|
+
export declare function defineConfig(config: WaygraphConfig): WaygraphConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Looks for the first `waygraph.config.*` in `projectDir` and imports it - `.ts` works
|
|
10
|
+
* unmodified because `bin/waygraph` already registers `tsx/esm` before `dist/cli.js` ever runs
|
|
11
|
+
* (the same mechanism that already lets a consumer's `.block.ts`/`.flow.ts` import directly), so
|
|
12
|
+
* no separate loader/transpile step is needed here. A broken config file throws loud, naming the
|
|
13
|
+
* file - silently swallowing it would make a missing `baseUrl` look like "nobody configured
|
|
14
|
+
* anything" instead of "your config file has a bug."
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadWaygraphConfig(projectDir: string): Promise<WaygraphConfig | undefined>;
|
|
17
|
+
/**
|
|
18
|
+
* Single source of truth for a project's base URL. Was three independently drifted copies
|
|
19
|
+
* (`src/cli/flags.ts`, `src/traverse/run.ts`, `src/session/auto-explore-run.ts`), each checking
|
|
20
|
+
* a slightly different subset of sources in a different order - real duplication, not just
|
|
21
|
+
* stylistic, since one copy skipped `config.waygraph.*` nesting and another checked
|
|
22
|
+
* `WAYGRAPH_BASE_URL` first instead of last. Checked in this order:
|
|
23
|
+
*
|
|
24
|
+
* 1. `WAYGRAPH_BASE_URL` - a real shell export, or one loaded from a `.env` file (see
|
|
25
|
+
* `loadEnvFiles`, called here so every caller gets native `.env` support for free, not just
|
|
26
|
+
* whichever command remembered to call it).
|
|
27
|
+
* 2. A `waygraph.config.*` file's `baseUrl`.
|
|
28
|
+
* 3. `package.json`'s `waygraph.baseUrl`/`baseURL` (`config.waygraph.*` also checked - a common
|
|
29
|
+
* nested-config convention some tooling uses).
|
|
30
|
+
* 4. `playwright.config.*`'s own `baseURL` (regex-read as text, never imported - importing a
|
|
31
|
+
* consumer's real playwright.config risks pulling in `@playwright/test`'s own global setup
|
|
32
|
+
* as a side effect, which resolving a URL string has no business doing).
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveBaseUrl(projectDir: string): Promise<string | undefined>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// waygraph.config.* - new, additive, optional. package.json's own "waygraph" field (baseUrl)
|
|
2
|
+
// keeps working untouched; this is a second, richer place to put the same kind of project-level
|
|
3
|
+
// setting, for consumers who'd rather have a real typed file than a JSON field. Real ask: "I
|
|
4
|
+
// think its time to add waygraph config now."
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
import { loadEnvFiles } from "./env-file.js";
|
|
9
|
+
/** Identity helper, purely for editor type-checking/autocomplete - same role `defineConfig` plays
|
|
10
|
+
* in a `playwright.config.ts` or `vite.config.ts`. `export default defineConfig({ ... })`. */
|
|
11
|
+
export function defineConfig(config) {
|
|
12
|
+
return config;
|
|
13
|
+
}
|
|
14
|
+
const CONFIG_FILENAMES = [
|
|
15
|
+
"waygraph.config.ts",
|
|
16
|
+
"waygraph.config.mts",
|
|
17
|
+
"waygraph.config.mjs",
|
|
18
|
+
"waygraph.config.js",
|
|
19
|
+
"waygraph.config.cjs",
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Looks for the first `waygraph.config.*` in `projectDir` and imports it - `.ts` works
|
|
23
|
+
* unmodified because `bin/waygraph` already registers `tsx/esm` before `dist/cli.js` ever runs
|
|
24
|
+
* (the same mechanism that already lets a consumer's `.block.ts`/`.flow.ts` import directly), so
|
|
25
|
+
* no separate loader/transpile step is needed here. A broken config file throws loud, naming the
|
|
26
|
+
* file - silently swallowing it would make a missing `baseUrl` look like "nobody configured
|
|
27
|
+
* anything" instead of "your config file has a bug."
|
|
28
|
+
*/
|
|
29
|
+
export async function loadWaygraphConfig(projectDir) {
|
|
30
|
+
for (const name of CONFIG_FILENAMES) {
|
|
31
|
+
const p = join(projectDir, name);
|
|
32
|
+
if (!existsSync(p))
|
|
33
|
+
continue;
|
|
34
|
+
let mod;
|
|
35
|
+
try {
|
|
36
|
+
mod = (await import(pathToFileURL(p).href));
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
throw new Error(`waygraph: failed to load ${name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
40
|
+
}
|
|
41
|
+
return mod.default ?? mod;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Single source of truth for a project's base URL. Was three independently drifted copies
|
|
47
|
+
* (`src/cli/flags.ts`, `src/traverse/run.ts`, `src/session/auto-explore-run.ts`), each checking
|
|
48
|
+
* a slightly different subset of sources in a different order - real duplication, not just
|
|
49
|
+
* stylistic, since one copy skipped `config.waygraph.*` nesting and another checked
|
|
50
|
+
* `WAYGRAPH_BASE_URL` first instead of last. Checked in this order:
|
|
51
|
+
*
|
|
52
|
+
* 1. `WAYGRAPH_BASE_URL` - a real shell export, or one loaded from a `.env` file (see
|
|
53
|
+
* `loadEnvFiles`, called here so every caller gets native `.env` support for free, not just
|
|
54
|
+
* whichever command remembered to call it).
|
|
55
|
+
* 2. A `waygraph.config.*` file's `baseUrl`.
|
|
56
|
+
* 3. `package.json`'s `waygraph.baseUrl`/`baseURL` (`config.waygraph.*` also checked - a common
|
|
57
|
+
* nested-config convention some tooling uses).
|
|
58
|
+
* 4. `playwright.config.*`'s own `baseURL` (regex-read as text, never imported - importing a
|
|
59
|
+
* consumer's real playwright.config risks pulling in `@playwright/test`'s own global setup
|
|
60
|
+
* as a side effect, which resolving a URL string has no business doing).
|
|
61
|
+
*/
|
|
62
|
+
export async function resolveBaseUrl(projectDir) {
|
|
63
|
+
loadEnvFiles(projectDir);
|
|
64
|
+
if (process.env.WAYGRAPH_BASE_URL)
|
|
65
|
+
return process.env.WAYGRAPH_BASE_URL;
|
|
66
|
+
const config = await loadWaygraphConfig(projectDir);
|
|
67
|
+
if (config?.baseUrl && config.baseUrl.trim())
|
|
68
|
+
return config.baseUrl.trim();
|
|
69
|
+
const pkgPath = join(projectDir, "package.json");
|
|
70
|
+
if (existsSync(pkgPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
73
|
+
const fromPkg = pkg.waygraph?.baseUrl ??
|
|
74
|
+
pkg.waygraph?.baseURL ??
|
|
75
|
+
pkg.config?.waygraph?.baseUrl ??
|
|
76
|
+
pkg.config?.waygraph?.baseURL;
|
|
77
|
+
if (typeof fromPkg === "string" && fromPkg.trim())
|
|
78
|
+
return fromPkg.trim();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// ignore malformed package.json - fall through to playwright.config
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const name of ["playwright.config.ts", "playwright.config.mts", "playwright.config.js", "playwright.config.mjs"]) {
|
|
85
|
+
const p = join(projectDir, name);
|
|
86
|
+
if (!existsSync(p))
|
|
87
|
+
continue;
|
|
88
|
+
const src = readFileSync(p, "utf-8");
|
|
89
|
+
const m = src.match(/baseURL\s*:\s*["']([^"']+)["']/);
|
|
90
|
+
if (m?.[1])
|
|
91
|
+
return m[1];
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-loads `.env` / `.env.local` / `.env.<mode>` / `.env.<mode>.local` from a project's root,
|
|
3
|
+
* same layering Vite/Next use (`mode` defaults to `NODE_ENV`, falling back to "development"),
|
|
4
|
+
* merged into `process.env` in increasing priority. A real, already-set `process.env` value -
|
|
5
|
+
* whatever the shell actually exported - always wins over every file; a `.env` file only fills
|
|
6
|
+
* a gap, it never silently overrides something the caller explicitly set. Safe to call more
|
|
7
|
+
* than once (e.g. once eagerly at `cwd`, again once the real project dir is known) - loading
|
|
8
|
+
* twice is a no-op the second time for any key already filled.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadEnvFiles(projectDir: string, mode?: string): void;
|
package/dist/env-file.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Native .env support - no `dotenv` dependency (same "bring nothing, read what's already there"
|
|
2
|
+
// philosophy as mem-stub.ts's own Faker note: this is a ~20-line parser, not a new runtime dep).
|
|
3
|
+
// Real, direct user ask: FE consumers keep their own .env and had to hack around waygraph not
|
|
4
|
+
// reading it (manually re-exporting values, duplicating them into package.json's "waygraph"
|
|
5
|
+
// field) to get waygraph to see the same config their actual app already uses.
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
/** `KEY=value` lines only - no `${VAR}` expansion, no multiline values. Matches the common
|
|
9
|
+
* Vite/Next `.env` shape, not full dotenv-expand parity. */
|
|
10
|
+
function parseEnvFile(content) {
|
|
11
|
+
const out = {};
|
|
12
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
13
|
+
const line = rawLine.trim();
|
|
14
|
+
if (!line || line.startsWith("#"))
|
|
15
|
+
continue;
|
|
16
|
+
const withoutExport = line.startsWith("export ") ? line.slice("export ".length) : line;
|
|
17
|
+
const eq = withoutExport.indexOf("=");
|
|
18
|
+
if (eq === -1)
|
|
19
|
+
continue;
|
|
20
|
+
const key = withoutExport.slice(0, eq).trim();
|
|
21
|
+
if (!key)
|
|
22
|
+
continue;
|
|
23
|
+
let value = withoutExport.slice(eq + 1).trim();
|
|
24
|
+
const quoted = (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"));
|
|
25
|
+
if (quoted)
|
|
26
|
+
value = value.slice(1, -1);
|
|
27
|
+
out[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Auto-loads `.env` / `.env.local` / `.env.<mode>` / `.env.<mode>.local` from a project's root,
|
|
33
|
+
* same layering Vite/Next use (`mode` defaults to `NODE_ENV`, falling back to "development"),
|
|
34
|
+
* merged into `process.env` in increasing priority. A real, already-set `process.env` value -
|
|
35
|
+
* whatever the shell actually exported - always wins over every file; a `.env` file only fills
|
|
36
|
+
* a gap, it never silently overrides something the caller explicitly set. Safe to call more
|
|
37
|
+
* than once (e.g. once eagerly at `cwd`, again once the real project dir is known) - loading
|
|
38
|
+
* twice is a no-op the second time for any key already filled.
|
|
39
|
+
*/
|
|
40
|
+
export function loadEnvFiles(projectDir, mode = process.env.NODE_ENV || "development") {
|
|
41
|
+
const files = [".env", ".env.local", `.env.${mode}`, `.env.${mode}.local`];
|
|
42
|
+
for (const name of files) {
|
|
43
|
+
const p = join(projectDir, name);
|
|
44
|
+
if (!existsSync(p))
|
|
45
|
+
continue;
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = parseEnvFile(readFileSync(p, "utf-8"));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue; // unreadable file - never fail a run over an optional .env
|
|
52
|
+
}
|
|
53
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
54
|
+
if (process.env[key] === undefined)
|
|
55
|
+
process.env[key] = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -165,9 +165,9 @@ export type StubPhaseFixtures = {
|
|
|
165
165
|
/** Banner UX: collision (move away from a ring it overlaps), hidden. See {@link BannerUiOpts}. */
|
|
166
166
|
bannerUi?: BannerUiOpts;
|
|
167
167
|
/**
|
|
168
|
-
* Bottom
|
|
169
|
-
*
|
|
170
|
-
*
|
|
168
|
+
* Bottom narration caption - a white rounded card (black text, shadow), bottom-centered above
|
|
169
|
+
* the step pill, distinct from the banner (top, card-shaped). Omit/clear to hide it; set again
|
|
170
|
+
* to change the text, fading out/in.
|
|
171
171
|
*/
|
|
172
172
|
caption?: string;
|
|
173
173
|
/**
|
|
@@ -421,9 +421,9 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
421
421
|
*/
|
|
422
422
|
bannerUi(opts: BannerUiOpts): void;
|
|
423
423
|
/**
|
|
424
|
-
* Bottom
|
|
425
|
-
*
|
|
426
|
-
*
|
|
424
|
+
* Bottom narration caption - a white rounded card (black text, shadow), bottom-centered above
|
|
425
|
+
* the step pill; distinct from {@link StubCtx.title}, which is the top banner card. Fades in/out
|
|
426
|
+
* on change. Pass `""` (or call {@link StubCtx.hideCaption}) to clear.
|
|
427
427
|
* @example ctx.caption("Click Add to cart to continue");
|
|
428
428
|
*/
|
|
429
429
|
caption(text: string): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -29,3 +29,5 @@ export { AutoSession, stubFilePath } from "./auto-session.js";
|
|
|
29
29
|
export type { AutoSessionInit, SessionSnapshot, SessionSnapshotEdge, SessionSnapshotSection, ApplyPickResult, ApplyPathResult, TraceStep, ConsoleLogEntry, StorageSnapshot, ServiceWorkerRegistrationInfo, CookieInfo, StubFileKind, } from "./auto-session.js";
|
|
30
30
|
export { pilotStart } from "./session/pilot.js";
|
|
31
31
|
export type { PilotStartResult } from "./session/pilot.js";
|
|
32
|
+
export { defineConfig, resolveBaseUrl } from "./config.js";
|
|
33
|
+
export type { WaygraphConfig } from "./config.js";
|
package/dist/index.js
CHANGED
|
@@ -14,3 +14,4 @@ export { buildCoverageReport, collectGraphEdgeKeys, defaultCoverageOutPath, form
|
|
|
14
14
|
export { WG_UI_SEL, WG_READY_PANEL_SEL, WG_READY_AUTO_SEL, WG_READY_BANNER_SEL, readWgOverlayBeacon, readWgOverlayBeaconInPage, assertWgOverlayReady, } from "./session/overlay-beacon.js";
|
|
15
15
|
export { AutoSession, stubFilePath } from "./auto-session.js";
|
|
16
16
|
export { pilotStart } from "./session/pilot.js";
|
|
17
|
+
export { defineConfig, resolveBaseUrl } from "./config.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// In-page installer (page.evaluate target) - must stay self-contained, no module-scope closure.
|
|
2
|
-
// Bottom
|
|
3
|
-
// own header)
|
|
2
|
+
// Bottom narration caption - a white rounded card, not the dark zsign-ported scrim this started as
|
|
3
|
+
// (see ui/css/caption.css's own header); narration-timeline/TTS sync not ported yet.
|
|
4
4
|
export function showCaption(text) {
|
|
5
5
|
let bar = __wgById("wg-caption-bar");
|
|
6
6
|
if (!bar) {
|
|
@@ -3,6 +3,7 @@ import { Engine } from "../core.js";
|
|
|
3
3
|
import { MemPage } from "../mem-page.js";
|
|
4
4
|
import { type BlockEntry } from "./auto-explore.js";
|
|
5
5
|
import type { Checkpoint } from "../types.js";
|
|
6
|
+
import { resolveBaseUrl } from "../config.js";
|
|
6
7
|
export interface AutoExploreOptions {
|
|
7
8
|
/** Terminal menu (browser still runs for act()). Default false = headful picker panel. */
|
|
8
9
|
cli?: boolean;
|
|
@@ -12,7 +13,9 @@ export interface AutoExploreOptions {
|
|
|
12
13
|
/** Phase C: glob / regex / bare `--blocks` file select for discovery. */
|
|
13
14
|
blocksSelect?: import("../blocks-select.js").BlocksSelect;
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
/** See `../config.js`'s own `resolveBaseUrl` - re-exported so existing importers of this module
|
|
17
|
+
* don't need to change their import path. */
|
|
18
|
+
export { resolveBaseUrl };
|
|
16
19
|
export declare function detectHere(page: Page, navBlocks: BlockEntry[]): Promise<string | null>;
|
|
17
20
|
export declare function ensureLivePage(context: BrowserContext, page: Page, startUrl: string | undefined): Promise<Page>;
|
|
18
21
|
/**
|
|
@@ -1,35 +1,14 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { ensureShadowRoot } from "../ui/shadow.js";
|
|
3
3
|
import { stdin as input, stdout as output } from "node:process";
|
|
4
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
6
4
|
import { Engine, start, end, locate } from "../core.js";
|
|
7
5
|
import { MemPage } from "../mem-page.js";
|
|
8
6
|
import { buildExploreContext, buildExploreMenu, } from "./auto-explore.js";
|
|
9
7
|
import { installDemoChrome, instrumentInteractionHighlighting } from "./step-overlay.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
15
|
-
const fromPkg = pkg.waygraph?.baseUrl ?? pkg.waygraph?.baseURL;
|
|
16
|
-
if (typeof fromPkg === "string" && fromPkg.trim())
|
|
17
|
-
return fromPkg.trim();
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
/* ignore */
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
for (const name of ["playwright.config.ts", "playwright.config.mts", "playwright.config.js"]) {
|
|
24
|
-
const p = join(projectDir, name);
|
|
25
|
-
if (!existsSync(p))
|
|
26
|
-
continue;
|
|
27
|
-
const m = readFileSync(p, "utf-8").match(/baseURL\s*:\s*["']([^"']+)["']/);
|
|
28
|
-
if (m?.[1])
|
|
29
|
-
return m[1];
|
|
30
|
-
}
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
8
|
+
import { resolveBaseUrl } from "../config.js";
|
|
9
|
+
/** See `../config.js`'s own `resolveBaseUrl` - re-exported so existing importers of this module
|
|
10
|
+
* don't need to change their import path. */
|
|
11
|
+
export { resolveBaseUrl };
|
|
33
12
|
/** URL-first - several Checkpoints share nav verify traits (e.g. LoggedIn = /inventory.html). */
|
|
34
13
|
async function inferCheckpointFromUrl(page) {
|
|
35
14
|
try {
|
|
@@ -577,7 +556,7 @@ async function cliPick(menu, rl) {
|
|
|
577
556
|
export async function runAutoExplore(projectDir, options = {}) {
|
|
578
557
|
lastRunNote = null;
|
|
579
558
|
const cli = options.cli === true;
|
|
580
|
-
const baseURL = options.baseURL ?? resolveBaseUrl(projectDir) ?? process.env.WAYGRAPH_BASE_URL;
|
|
559
|
+
const baseURL = options.baseURL ?? (await resolveBaseUrl(projectDir)) ?? process.env.WAYGRAPH_BASE_URL;
|
|
581
560
|
const startUrl = options.startUrl ?? baseURL;
|
|
582
561
|
const { graph, library } = await buildExploreContext(projectDir, options.blocksSelect ? { blocksSelect: options.blocksSelect } : undefined);
|
|
583
562
|
if (options.blocksSelect) {
|
package/dist/traverse/run.js
CHANGED
|
@@ -1,29 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase B+D: graph traverse (serial or --parallel with --session clone).
|
|
3
|
-
* Walk unused legal edges until leaf / budget / break.
|
|
4
|
-
*/
|
|
5
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
1
|
import { Engine, start, end, locate } from "../core.js";
|
|
8
2
|
import { MemPage } from "../mem-page.js";
|
|
9
3
|
import { buildExploreContext, buildExploreMenu, } from "../session/auto-explore.js";
|
|
10
4
|
import { defaultMemValueForKey } from "../session/auto-explore-run.js";
|
|
5
|
+
import { resolveBaseUrl } from "../config.js";
|
|
11
6
|
import { EdgeLeaseCoordinator } from "./lease.js";
|
|
12
7
|
import { buildCoverageReport, collectGraphEdgeKeys, defaultCoverageOutPath, formatCoverageLine, writeCoverageReport, } from "./coverage.js";
|
|
13
|
-
function resolveBaseUrl(projectDir) {
|
|
14
|
-
if (process.env.WAYGRAPH_BASE_URL)
|
|
15
|
-
return process.env.WAYGRAPH_BASE_URL;
|
|
16
|
-
const pkgPath = join(projectDir, "package.json");
|
|
17
|
-
if (!existsSync(pkgPath))
|
|
18
|
-
return undefined;
|
|
19
|
-
try {
|
|
20
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
21
|
-
return pkg.waygraph?.baseUrl;
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
8
|
function seedMem(library, mem, dataJson) {
|
|
28
9
|
const raw = dataJson ?? process.env.WAYGRAPH_DATA ?? process.env.WAYGRAPH_AUTO_MEM;
|
|
29
10
|
let envParsed = {};
|
|
@@ -257,7 +238,7 @@ export async function runTraverse(projectDir, options = {}) {
|
|
|
257
238
|
const maxVisitsPerEdge = options.maxVisitsPerEdge ?? 1;
|
|
258
239
|
const timeoutMs = options.timeoutMs ?? 15 * 60_000;
|
|
259
240
|
const headed = options.headed === true || process.env.WAYGRAPH_HEADED === "1";
|
|
260
|
-
const baseURL = options.baseURL ?? resolveBaseUrl(projectDir) ?? process.env.WAYGRAPH_BASE_URL;
|
|
241
|
+
const baseURL = options.baseURL ?? (await resolveBaseUrl(projectDir)) ?? process.env.WAYGRAPH_BASE_URL;
|
|
261
242
|
const startUrl = options.startUrl ?? baseURL;
|
|
262
243
|
const minEdgeCoverage = options.minEdgeCoverage != null && Number.isFinite(options.minEdgeCoverage)
|
|
263
244
|
? options.minEdgeCoverage
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[16:29:47.576] waygraph pilot: stub.before ctx.title("Signing in · mobile")
|
|
2
|
+
[16:29:47.576] waygraph pilot: stub.before ctx.todos n=6 index=0
|
|
3
|
+
[16:29:47.576] waygraph pilot: stub.before ctx.device(mobile)
|
|
4
|
+
[16:29:47.576] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
5
|
+
[16:29:47.576] waygraph pilot: stub.before ring #user-name tone=info label="Username"
|
|
6
|
+
[16:29:48.942] waygraph pilot: stub.after ctx.todos n=6 index=1
|
|
7
|
+
[16:29:50.794] waygraph pilot: stub.before ring #password tone=planned label="Password"
|
|
8
|
+
[16:29:50.965] waygraph pilot: stub.after ctx.todos n=6 index=2
|
|
9
|
+
[16:29:52.406] waygraph pilot: stub.before ring #login-button tone=warning label="Login"
|
|
10
|
+
[16:29:52.726] waygraph pilot: stub.after ctx.title("Inventory · landscape")
|
|
11
|
+
[16:29:52.727] waygraph pilot: stub.after ctx.todos n=6 index=3
|
|
12
|
+
[16:29:52.727] waygraph pilot: stub.after ctx.device(mobile)
|
|
13
|
+
[16:29:52.727] waygraph pilot: stub.after ring .inventory_list tone=success label="Product shelf"
|
|
14
|
+
[16:29:54.148] waygraph pilot: ring not painted - selector matched nothing (or a zero-size element): .inventory_list
|
|
15
|
+
[16:29:58.462] waygraph pilot: stub.before ctx.title("Open cart · landscape")
|
|
16
|
+
[16:29:58.462] waygraph pilot: stub.before ctx.todos n=6 index=5
|
|
17
|
+
[16:29:58.462] waygraph pilot: stub.before ctx.device(mobile)
|
|
18
|
+
[16:29:58.462] waygraph pilot: stub.before ctx.zoom(1.4)
|
|
19
|
+
[16:29:58.462] waygraph pilot: stub.before ring .shopping_cart_link tone=planned label="Cart"
|
|
20
|
+
[16:29:58.870] waygraph pilot: stub.after ctx.title("Your cart · desktop")
|
|
21
|
+
[16:29:58.871] waygraph pilot: stub.after ring .cart_item tone=success label="Cart items"
|
|
22
|
+
[16:29:58.934] waygraph pilot: ring not painted - selector matched nothing (or a zero-size element): .cart_item
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
[16:29:18.968] waygraph pilot: highlight ring #user-name tone=planned label="Username"
|
|
2
|
+
[16:29:18.969] waygraph pilot: highlight ring #password tone=info label="Password"
|
|
3
|
+
[16:29:18.969] waygraph pilot: highlight ring #login-button tone=success label="Login"
|
|
4
|
+
[16:29:23.306] waygraph pilot: highlight ring #user-name tone=planned label="Username"
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[16:29:27.265] waygraph pilot: stub.before ctx.title("Signing in · mobile")
|
|
2
|
+
[16:29:27.265] waygraph pilot: stub.before ctx.todos n=6 index=0
|
|
3
|
+
[16:29:27.265] waygraph pilot: stub.before ctx.device(mobile)
|
|
4
|
+
[16:29:27.266] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
5
|
+
[16:29:27.266] waygraph pilot: stub.before ring #user-name tone=info label="Username"
|
|
6
|
+
[16:29:28.809] waygraph pilot: stub.after ctx.todos n=6 index=1
|
|
7
|
+
[16:29:34.946] waygraph pilot: stub.before ring #password tone=planned label="Password"
|
|
8
|
+
[16:29:35.328] waygraph pilot: stub.after ctx.todos n=6 index=2
|
|
9
|
+
[16:29:41.327] waygraph pilot: stub.before ring #login-button tone=warning label="Login"
|
|
10
|
+
[16:29:42.199] waygraph pilot: stub.after ctx.title("Inventory · landscape")
|
|
11
|
+
[16:29:42.200] waygraph pilot: stub.after ctx.todos n=6 index=3
|
|
12
|
+
[16:29:42.200] waygraph pilot: stub.after ctx.device(mobile)
|
|
13
|
+
[16:29:42.200] waygraph pilot: stub.after ring .inventory_list tone=success label="Product shelf"
|
|
14
|
+
[16:29:50.154] waygraph pilot: stub.before ctx.title("Pick a product · tablet")
|
|
15
|
+
[16:29:50.154] waygraph pilot: stub.before ctx.todos n=6 index=3
|
|
16
|
+
[16:29:50.154] waygraph pilot: stub.before ctx.device(tablet)
|
|
17
|
+
[16:29:50.154] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
18
|
+
[16:29:50.154] waygraph pilot: stub.before ring div.inventory_item:has(button[data-test="add-to-cart-sauce-labs-backpack"], button[data-test="remove-sauce-labs-backpack"]) .inventory_item_name tone=info label="Sauce Labs Backpack"
|
|
19
|
+
[16:29:50.154] waygraph pilot: stub.before ring [data-test="add-to-cart-sauce-labs-backpack"] tone=success label="Add to cart"
|
|
20
|
+
[16:29:51.029] waygraph pilot: stub.after ctx.title("In the cart")
|
|
21
|
+
[16:29:51.029] waygraph pilot: stub.after ctx.todos n=6 index=4
|
|
22
|
+
[16:29:51.029] waygraph pilot: stub.after ring .shopping_cart_badge tone=success label="Cart badge"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
[16:30:31.665] waygraph pilot: stub.before ctx.title("Signing in · mobile")
|
|
2
|
+
[16:30:31.665] waygraph pilot: stub.before ctx.todos n=6 index=0
|
|
3
|
+
[16:30:31.665] waygraph pilot: stub.before ctx.device(mobile)
|
|
4
|
+
[16:30:31.665] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
5
|
+
[16:30:31.665] waygraph pilot: stub.before ring #user-name tone=info label="Username"
|
|
6
|
+
[16:30:32.427] waygraph pilot: stub.after ctx.todos n=6 index=1
|
|
File without changes
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
[16:30:12.884] waygraph pilot: highlight ctx.todos n=3 index=0
|
|
2
|
+
[16:30:12.884] waygraph pilot: highlight ring #user-name tone=planned label="Username"
|
|
3
|
+
[16:30:12.892] waygraph pilot: highlight ring #password tone=info label="Password"
|
|
4
|
+
[16:30:17.796] waygraph pilot: highlight ring #no-such-element-waygraph tone=danger label="Gone"
|
|
5
|
+
[16:30:17.817] waygraph pilot: ring not painted - selector matched nothing (or a zero-size element): #no-such-element-waygraph
|
|
6
|
+
[16:30:20.108] waygraph pilot: highlight ring #login-button tone=success label="Login"
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -5,21 +5,34 @@ import {
|
|
|
5
5
|
withTitle,
|
|
6
6
|
withHighlightFixtures,
|
|
7
7
|
withSessionReset,
|
|
8
|
+
withMemStub,
|
|
9
|
+
registerMemStub,
|
|
8
10
|
} from "waygraph";
|
|
9
11
|
import { NavHomeBlock } from "../map/(app_base)/home/_nav.block.js";
|
|
10
12
|
import { AddItemBlock } from "../map/(app_base)/home/_methods/add-item.effect.block.js";
|
|
11
13
|
import { AssertItemAddedBlock } from "../map/(app_base)/home/_methods/assert-item-added.method.block.js";
|
|
12
14
|
import { RemoveItemBlock } from "../map/(app_base)/home/_methods/remove-item.effect.block.js";
|
|
13
15
|
import { ClearCartBlock } from "../map/(app_base)/home/_methods/clear-cart.method.block.js";
|
|
16
|
+
import { SelectedItem } from "../states/demo.mem-keys.js";
|
|
14
17
|
import "../map/(app_base)/home/_page.block.js";
|
|
15
18
|
|
|
16
19
|
const engine = new Engine();
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
22
|
+
* memStub: a real fixture row (`alpha`/`Alpha`), registered once so this Flow never needs
|
|
23
|
+
* --data at all. Still override-able - `--data '{"demo.selectedItem":{"id":"beta",...}}'`
|
|
24
|
+
* wins over this registration, same as any other memStub (see docs/REFERENCE.md's own
|
|
25
|
+
* "memStub - fake values for requires" section).
|
|
26
|
+
*/
|
|
27
|
+
registerMemStub(SelectedItem.key, () => ({ id: "alpha", name: "Alpha" }));
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Effect + Method demo. `requires: [SelectedItem.key]` (on `add-item`) is filled automatically
|
|
31
|
+
* by the memStub registration above - no --data needed. Override it with real data, or use
|
|
32
|
+
* `waygraph auto` which fills SelectedItem via instanceOptions instead:
|
|
21
33
|
*
|
|
22
|
-
* waygraph demo --blocks shopFlow
|
|
34
|
+
* waygraph demo --blocks shopFlow # memStub fills it
|
|
35
|
+
* waygraph demo --blocks shopFlow --data '{"demo.selectedItem":{"id":"beta","name":"Beta"}}'
|
|
23
36
|
* waygraph auto # after landing on home, pick Add Alpha / Add Beta / Remove ...
|
|
24
37
|
*
|
|
25
38
|
* Add -> assert it actually landed (mem-matched Remove button + cart count,
|
|
@@ -28,32 +41,34 @@ const engine = new Engine();
|
|
|
28
41
|
* Clear cart (bulk Method, `clear-cart`) - keeps both the per-item and bulk
|
|
29
42
|
* clear paths atomic and each wired into a real flow, not orphaned.
|
|
30
43
|
*/
|
|
31
|
-
export const shopFlow =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
export const shopFlow = withMemStub(
|
|
45
|
+
withHighlightFixtures(
|
|
46
|
+
withTitle(
|
|
47
|
+
withSessionReset(
|
|
48
|
+
engine.defineFlow([
|
|
49
|
+
start,
|
|
50
|
+
NavHomeBlock,
|
|
51
|
+
AddItemBlock,
|
|
52
|
+
AssertItemAddedBlock,
|
|
53
|
+
RemoveItemBlock,
|
|
54
|
+
AddItemBlock,
|
|
55
|
+
ClearCartBlock,
|
|
56
|
+
end,
|
|
57
|
+
]),
|
|
58
|
+
),
|
|
59
|
+
"Scaffold: Shop (Effect + clear)",
|
|
44
60
|
),
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
duration: true,
|
|
61
|
+
{
|
|
62
|
+
"add-item": {
|
|
63
|
+
stubAfter: {
|
|
64
|
+
cart: {
|
|
65
|
+
label: "AC · item in cart",
|
|
66
|
+
detail: "Cart count bumped after Add.",
|
|
67
|
+
tag: "AC",
|
|
68
|
+
duration: true,
|
|
69
|
+
},
|
|
55
70
|
},
|
|
56
71
|
},
|
|
57
72
|
},
|
|
58
|
-
|
|
73
|
+
),
|
|
59
74
|
);
|