waygraph 0.15.23 → 0.15.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/discover.d.ts +40 -0
- package/dist/cli/discover.js +148 -0
- package/dist/cli/flags.d.ts +91 -0
- package/dist/cli/flags.js +442 -0
- package/dist/cli/run-chain.d.ts +8 -0
- package/dist/cli/run-chain.js +67 -0
- package/dist/cli/usage.d.ts +4 -0
- package/dist/cli/usage.js +236 -0
- package/dist/cli/util.d.ts +4 -0
- package/dist/cli/util.js +24 -0
- package/dist/cli.js +45 -2448
- package/dist/commands/agent-dive.d.ts +1 -0
- package/dist/commands/agent-dive.js +52 -0
- package/dist/commands/check.d.ts +4 -0
- package/dist/commands/check.js +192 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +54 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +67 -0
- package/dist/commands/map.d.ts +1 -0
- package/dist/commands/map.js +29 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +104 -0
- package/dist/commands/session.d.ts +2 -0
- package/dist/commands/session.js +600 -0
- package/dist/commands/test.d.ts +1 -0
- package/dist/commands/test.js +33 -0
- package/dist/commands/traverse.d.ts +1 -0
- package/dist/commands/traverse.js +177 -0
- package/dist/commands/try.d.ts +1 -0
- package/dist/commands/try.js +298 -0
- package/examples/saucedemo/.waygraph-auto/0ac58118.log +0 -0
- package/examples/saucedemo/.waygraph-auto/309c107f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/3ab7c7bd.log +44 -0
- package/examples/saucedemo/.waygraph-auto/45864828.log +0 -0
- package/examples/saucedemo/.waygraph-auto/95bd8371.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bcb9e31a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cd3e528c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/dce1535d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e280bf20.log +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { existsSync, statSync } from "node:fs";
|
|
4
|
+
import { parseBlocksSelect } from "../blocks-select.js";
|
|
5
|
+
import { parseMinEdgeCoverage } from "../traverse-coverage.js";
|
|
6
|
+
import { runTraverse } from "../traverse-run.js";
|
|
7
|
+
export async function traverseCase(args) {
|
|
8
|
+
if (args.includes("--step") || args.includes("--auto-next") || args.includes("--autoplay")) {
|
|
9
|
+
console.error("waygraph traverse: --step / --auto-next not supported (not a demo)");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const rest = args.slice(1);
|
|
13
|
+
let from;
|
|
14
|
+
let data;
|
|
15
|
+
let maxSteps;
|
|
16
|
+
let maxVisits;
|
|
17
|
+
let maxEdge;
|
|
18
|
+
let headed = false;
|
|
19
|
+
let baseUrl;
|
|
20
|
+
let blocksFilter;
|
|
21
|
+
let parallel;
|
|
22
|
+
let session;
|
|
23
|
+
let minEdgeCoverageRaw;
|
|
24
|
+
let coverageOut;
|
|
25
|
+
let noCoverageReport = false;
|
|
26
|
+
let projectDir = process.cwd();
|
|
27
|
+
for (let i = 0; i < rest.length; i++) {
|
|
28
|
+
const a = rest[i];
|
|
29
|
+
if (a === "--from" || a.startsWith("--from=")) {
|
|
30
|
+
from = a.startsWith("--from=") ? a.slice(7) : rest[++i];
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (a === "--data" || a.startsWith("--data=")) {
|
|
34
|
+
data = a.startsWith("--data=") ? a.slice(7) : rest[++i];
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (a === "--blocks" || a.startsWith("--blocks=")) {
|
|
38
|
+
if (a.startsWith("--blocks=")) {
|
|
39
|
+
blocksFilter = a.slice("--blocks=".length);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const v = rest[++i];
|
|
43
|
+
if (!v || v.startsWith("-")) {
|
|
44
|
+
console.error("waygraph traverse: --blocks needs a glob, /regex/, or path substring");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
blocksFilter = v;
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (a === "--parallel" || a.startsWith("--parallel=")) {
|
|
52
|
+
const v = a.startsWith("--parallel=") ? a.slice("--parallel=".length) : rest[++i];
|
|
53
|
+
parallel = Number(v);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (a === "--session" || a.startsWith("--session=")) {
|
|
57
|
+
const v = a.startsWith("--session=") ? a.slice("--session=".length) : rest[++i];
|
|
58
|
+
if (v !== "clone" && v !== "inherit") {
|
|
59
|
+
console.error('waygraph traverse: --session must be "clone" or "inherit"');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
session = v;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (a === "--min-edge-coverage" || a.startsWith("--min-edge-coverage=")) {
|
|
66
|
+
minEdgeCoverageRaw = a.startsWith("--min-edge-coverage=")
|
|
67
|
+
? a.slice("--min-edge-coverage=".length)
|
|
68
|
+
: rest[++i];
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (a === "--coverage-out" || a.startsWith("--coverage-out=")) {
|
|
72
|
+
coverageOut = a.startsWith("--coverage-out=")
|
|
73
|
+
? a.slice("--coverage-out=".length)
|
|
74
|
+
: rest[++i];
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (a === "--no-coverage-report") {
|
|
78
|
+
noCoverageReport = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (a === "--max-steps" || a.startsWith("--max-steps=")) {
|
|
82
|
+
const v = a.startsWith("--max-steps=") ? a.slice(12) : rest[++i];
|
|
83
|
+
maxSteps = Number(v);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (a === "--max-visits" || a.startsWith("--max-visits=")) {
|
|
87
|
+
const v = a.startsWith("--max-visits=") ? a.slice(13) : rest[++i];
|
|
88
|
+
maxVisits = Number(v);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (a === "--max-visits-per-edge" || a.startsWith("--max-visits-per-edge=")) {
|
|
92
|
+
const v = a.startsWith("--max-visits-per-edge=")
|
|
93
|
+
? a.slice("--max-visits-per-edge=".length)
|
|
94
|
+
: rest[++i];
|
|
95
|
+
maxEdge = Number(v);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (a === "--non-headless" || a === "--headed") {
|
|
99
|
+
headed = true;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (a === "--base-url" || a.startsWith("--base-url=")) {
|
|
103
|
+
baseUrl = a.startsWith("--base-url=") ? a.slice(11) : rest[++i];
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (a === "--help" || a === "-h") {
|
|
107
|
+
console.log(`waygraph traverse [project] [flags]
|
|
108
|
+
|
|
109
|
+
Graph crawl (RFC Phase B-E). Walks unused legal edges until a leaf,
|
|
110
|
+
budget kill, or first broken edge. --parallel N uses session clone + edge leases.
|
|
111
|
+
|
|
112
|
+
--blocks <glob|/regex/|substr> Phase C: filter *.block.ts discovery
|
|
113
|
+
--parallel N Phase D: N clone workers (default 1, max 4)
|
|
114
|
+
--session clone|inherit Phase D: default clone; inherit refused if parallel>1
|
|
115
|
+
--min-edge-coverage X Phase E: suite gate 0.8 | 80% | 80 (exit 2 if below)
|
|
116
|
+
--coverage-out PATH Phase E: write JSON (default .waygraph-traverse/coverage.json)
|
|
117
|
+
--no-coverage-report Phase E: print Coverage line only (no JSON file)
|
|
118
|
+
--from <Checkpoint> start checkpoint (optional)
|
|
119
|
+
--data '{...}' Mem seed JSON
|
|
120
|
+
--max-steps N default 50 (per worker)
|
|
121
|
+
--max-visits N per-node visit cap (default 2)
|
|
122
|
+
--max-visits-per-edge N default 1
|
|
123
|
+
--non-headless show browser
|
|
124
|
+
--base-url URL
|
|
125
|
+
|
|
126
|
+
Examples:
|
|
127
|
+
waygraph traverse --blocks '**/mailpit/**/*.block.ts'
|
|
128
|
+
waygraph traverse --parallel 2 --session clone --max-steps 20
|
|
129
|
+
waygraph traverse --min-edge-coverage 80% --coverage-out ./cov.json
|
|
130
|
+
|
|
131
|
+
PASS: [Reached Leaf Node[traverse-1] at=... steps=N]
|
|
132
|
+
FAIL: [Broke at edge[traverse-1] block=... from=... to=...]
|
|
133
|
+
COVER: [Coverage edges=H/T ratio=R% min=M% PASS|FAIL]
|
|
134
|
+
`);
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
if (!a.startsWith("-")) {
|
|
138
|
+
projectDir = resolve(a);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!existsSync(projectDir) || !statSync(projectDir).isDirectory()) {
|
|
142
|
+
console.error(`waygraph traverse: no such directory: ${projectDir}`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const blocksSelect = blocksFilter
|
|
146
|
+
? parseBlocksSelect(blocksFilter)
|
|
147
|
+
: undefined;
|
|
148
|
+
let minEdgeCoverage;
|
|
149
|
+
if (minEdgeCoverageRaw !== undefined) {
|
|
150
|
+
const parsed = parseMinEdgeCoverage(minEdgeCoverageRaw);
|
|
151
|
+
if (parsed === null) {
|
|
152
|
+
console.error(`waygraph traverse: --min-edge-coverage expects 0.8, 80%, or 80 (got ${JSON.stringify(minEdgeCoverageRaw)})`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
minEdgeCoverage = parsed;
|
|
156
|
+
}
|
|
157
|
+
const code = await runTraverse(projectDir, {
|
|
158
|
+
...(from ? { from } : {}),
|
|
159
|
+
...(data ? { data } : {}),
|
|
160
|
+
...(maxSteps !== undefined && Number.isFinite(maxSteps) ? { maxSteps } : {}),
|
|
161
|
+
...(maxVisits !== undefined && Number.isFinite(maxVisits)
|
|
162
|
+
? { maxVisitsPerNode: maxVisits }
|
|
163
|
+
: {}),
|
|
164
|
+
...(maxEdge !== undefined && Number.isFinite(maxEdge)
|
|
165
|
+
? { maxVisitsPerEdge: maxEdge }
|
|
166
|
+
: {}),
|
|
167
|
+
headed,
|
|
168
|
+
...(baseUrl ? { baseURL: baseUrl } : {}),
|
|
169
|
+
...(blocksSelect ? { blocksSelect } : {}),
|
|
170
|
+
...(parallel !== undefined && Number.isFinite(parallel) ? { parallel } : {}),
|
|
171
|
+
...(session ? { session } : {}),
|
|
172
|
+
...(minEdgeCoverage !== undefined ? { minEdgeCoverage } : {}),
|
|
173
|
+
...(coverageOut ? { coverageOut: resolve(coverageOut) } : {}),
|
|
174
|
+
...(noCoverageReport ? { noCoverageReport: true } : {}),
|
|
175
|
+
});
|
|
176
|
+
process.exit(code);
|
|
177
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function tryCase(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { cpSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { packageRoot, runInherited } from "../cli/util.js";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { runChain } from "../cli/run-chain.js";
|
|
8
|
+
import { applyRunFlags, parseRunFlags } from "../cli/flags.js";
|
|
9
|
+
/**
|
|
10
|
+
* Runs a throwaway probe script rooted at `projectDir` - the exact same
|
|
11
|
+
* directory the real demo files get copied into, so it resolves "waygraph"
|
|
12
|
+
* and "@playwright/test" exactly as they will - and actually launches (then
|
|
13
|
+
* immediately closes) chromium, a real check rather than a guess at whether
|
|
14
|
+
* the browser's installed. Never leaves the probe script behind.
|
|
15
|
+
*/
|
|
16
|
+
async function probeTryPrereqs(projectDir) {
|
|
17
|
+
const tsxEsm = import.meta.resolve("tsx/esm");
|
|
18
|
+
const probeScript = [
|
|
19
|
+
"try {",
|
|
20
|
+
' await import("waygraph");',
|
|
21
|
+
"} catch {",
|
|
22
|
+
' console.error("WAYGRAPH_TRY_MISSING_WAYGRAPH");',
|
|
23
|
+
" process.exit(1);",
|
|
24
|
+
"}",
|
|
25
|
+
"let mod;",
|
|
26
|
+
"try {",
|
|
27
|
+
' mod = await import("@playwright/test");',
|
|
28
|
+
"} catch {",
|
|
29
|
+
' console.error("WAYGRAPH_TRY_MISSING_PLAYWRIGHT_TEST");',
|
|
30
|
+
" process.exit(1);",
|
|
31
|
+
"}",
|
|
32
|
+
"try {",
|
|
33
|
+
" const browser = await mod.chromium.launch({ headless: true });",
|
|
34
|
+
" await browser.close();",
|
|
35
|
+
"} catch (err) {",
|
|
36
|
+
" if (String(err && err.message).includes(\"Executable doesn't exist\")) {",
|
|
37
|
+
' console.error("WAYGRAPH_TRY_MISSING_BROWSER");',
|
|
38
|
+
" process.exit(1);",
|
|
39
|
+
" }",
|
|
40
|
+
" console.error(String((err && err.stack) || err));",
|
|
41
|
+
" process.exit(1);",
|
|
42
|
+
"}",
|
|
43
|
+
].join("\n");
|
|
44
|
+
const probePath = join(projectDir, `.waygraph-try-probe-${process.pid}.mjs`);
|
|
45
|
+
writeFileSync(probePath, probeScript);
|
|
46
|
+
try {
|
|
47
|
+
const result = await new Promise((res, rej) => {
|
|
48
|
+
const child = spawn(process.execPath, ["--import", tsxEsm, probePath], {
|
|
49
|
+
cwd: projectDir,
|
|
50
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
51
|
+
env: process.env,
|
|
52
|
+
});
|
|
53
|
+
let stderrBuf = "";
|
|
54
|
+
child.stderr?.on("data", (d) => { stderrBuf += d.toString(); });
|
|
55
|
+
child.on("error", rej);
|
|
56
|
+
child.on("exit", (code) => res({ code: code ?? 1, stderr: stderrBuf }));
|
|
57
|
+
});
|
|
58
|
+
if (result.code === 0)
|
|
59
|
+
return { ok: true };
|
|
60
|
+
const stderr = result.stderr;
|
|
61
|
+
if (stderr.includes("WAYGRAPH_TRY_MISSING_WAYGRAPH"))
|
|
62
|
+
return { ok: false, missing: "waygraph" };
|
|
63
|
+
if (stderr.includes("WAYGRAPH_TRY_MISSING_PLAYWRIGHT_TEST"))
|
|
64
|
+
return { ok: false, missing: "playwright-test" };
|
|
65
|
+
if (stderr.includes("WAYGRAPH_TRY_MISSING_BROWSER"))
|
|
66
|
+
return { ok: false, missing: "browser" };
|
|
67
|
+
return { ok: false, missing: "unknown", detail: stderr.trim() || `probe exited with code ${result.code}` };
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
rmSync(probePath, { force: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Installs deps + chromium inside an already-copied quickstart folder. */
|
|
74
|
+
async function ensureDirPrereqs(dir, label) {
|
|
75
|
+
let status = await probeTryPrereqs(dir);
|
|
76
|
+
if (status.ok)
|
|
77
|
+
return null;
|
|
78
|
+
if (!existsSync(join(dir, "package.json"))) {
|
|
79
|
+
return `${label}: no package.json in ${dir}`;
|
|
80
|
+
}
|
|
81
|
+
if (status.missing === "waygraph" || status.missing === "playwright-test" || status.missing === "unknown") {
|
|
82
|
+
// Copied quickstart must not keep a workspace-tainted lockfile (breaks temp-dir npm install).
|
|
83
|
+
rmSync(join(dir, "package-lock.json"), { force: true });
|
|
84
|
+
console.log(`${label}: installing dependencies in ${dir} ...`);
|
|
85
|
+
const installCode = await runInherited("npm", ["install"], dir);
|
|
86
|
+
if (installCode !== 0) {
|
|
87
|
+
return `npm install exited with code ${installCode} - see the output above.`;
|
|
88
|
+
}
|
|
89
|
+
status = await probeTryPrereqs(dir);
|
|
90
|
+
if (status.ok)
|
|
91
|
+
return null;
|
|
92
|
+
if (status.missing === "unknown") {
|
|
93
|
+
return formatProbeFailure(status.detail ?? "probe failed");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
console.log(`${label}: downloading Playwright's chromium browser ...`);
|
|
97
|
+
const localPlaywrightBin = join(dir, "node_modules", ".bin", "playwright");
|
|
98
|
+
const browserCode = existsSync(localPlaywrightBin)
|
|
99
|
+
? await runInherited(localPlaywrightBin, ["install", "chromium"], dir)
|
|
100
|
+
: await runInherited("npx", ["--yes", "playwright", "install", "chromium"], dir);
|
|
101
|
+
if (browserCode !== 0) {
|
|
102
|
+
return `"playwright install chromium" exited with code ${browserCode} - see the output above.`;
|
|
103
|
+
}
|
|
104
|
+
status = await probeTryPrereqs(dir);
|
|
105
|
+
if (status.ok)
|
|
106
|
+
return null;
|
|
107
|
+
return formatProbeFailure(status.detail ?? "prerequisites still aren't ready after attempting to install them.");
|
|
108
|
+
}
|
|
109
|
+
/** Playwright browser binary present but OS libs missing (common in Docker/Codespaces). */
|
|
110
|
+
function formatProbeFailure(detail) {
|
|
111
|
+
const needsDeps = /shared libraries|libnspr4|libnss3|libatk|cannot open shared object file/i.test(detail);
|
|
112
|
+
const hint = needsDeps
|
|
113
|
+
? "\n\nChromium needs OS packages too - try:\n sudo npx playwright install-deps chromium\n" +
|
|
114
|
+
"In cloud/CI without a display, record headless:\n npx waygraph try demo --video ./out --no-step"
|
|
115
|
+
: "";
|
|
116
|
+
return `couldn't verify prerequisites:\n${detail}${hint}`;
|
|
117
|
+
}
|
|
118
|
+
const TRY_DEMO_CHAIN = 'loginFlow({"saucedemo.credentials":{"username":"standard_user","password":"secret_sauce"}}) then ' +
|
|
119
|
+
'shopFlow({"saucedemo.selectedItem":{"id":"sauce-labs-backpack","name":"Sauce Labs Backpack"}}) then ' +
|
|
120
|
+
'viewerBlockedFlow({"saucedemo.credentials":{"username":"locked_out_user","password":"secret_sauce"}})';
|
|
121
|
+
/**
|
|
122
|
+
* Point a copied quickstart at this checkout's waygraph build (not npm registry).
|
|
123
|
+
* Uses `npm pack` into destDir - a file: symlink to the dev tree can double-load
|
|
124
|
+
* @playwright/test when sibling projects also install Playwright.
|
|
125
|
+
*/
|
|
126
|
+
async function wireQuickstartToPackageRoot(destDir) {
|
|
127
|
+
const root = packageRoot();
|
|
128
|
+
const version = JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version;
|
|
129
|
+
const packCode = await runInherited("npm", ["pack", "--pack-destination", destDir, "--silent"], root);
|
|
130
|
+
if (packCode !== 0) {
|
|
131
|
+
throw new Error(`waygraph try demo: npm pack failed with exit code ${packCode}`);
|
|
132
|
+
}
|
|
133
|
+
const tgz = join(destDir, `waygraph-${version}.tgz`);
|
|
134
|
+
const pkgPath = join(destDir, "package.json");
|
|
135
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
136
|
+
if (!pkg.dependencies)
|
|
137
|
+
pkg.dependencies = {};
|
|
138
|
+
pkg.dependencies.waygraph = `file:${tgz}`;
|
|
139
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Copy templates/quickstart into a fresh OS temp dir and wire it to this
|
|
143
|
+
* package build. Returns destDir, or null after setting exitCode on failure.
|
|
144
|
+
*/
|
|
145
|
+
async function prepareTryQuickstart(label) {
|
|
146
|
+
const destDir = mkdtempSync(join(tmpdir(), `${label}-`));
|
|
147
|
+
cpSync(join(packageRoot(), "templates", "quickstart"), destDir, { recursive: true });
|
|
148
|
+
rmSync(join(destDir, "node_modules"), { recursive: true, force: true });
|
|
149
|
+
rmSync(join(destDir, "test-results"), { recursive: true, force: true });
|
|
150
|
+
rmSync(join(destDir, "package-lock.json"), { force: true });
|
|
151
|
+
try {
|
|
152
|
+
await wireQuickstartToPackageRoot(destDir);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const problem = await ensureDirPrereqs(destDir, label);
|
|
160
|
+
if (problem) {
|
|
161
|
+
console.error(`${label}: couldn't get ready.\n${problem}`);
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return destDir;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* `waygraph try demo` - copy a self-contained saucedemo project into a temp
|
|
169
|
+
* dir (never the caller's cwd), run a chainFlow step demo (Sign In + Shop +
|
|
170
|
+
* blocked Viewer), then run the headless Playwright test, and print where
|
|
171
|
+
* everything lives.
|
|
172
|
+
*/
|
|
173
|
+
async function runTryDemo() {
|
|
174
|
+
const destDir = await prepareTryQuickstart("waygraph-try-demo");
|
|
175
|
+
if (!destDir)
|
|
176
|
+
return;
|
|
177
|
+
process.env.WAYGRAPH_BASE_URL ??= "https://www.saucedemo.com";
|
|
178
|
+
process.env.WAYGRAPH_STEP ??= "1";
|
|
179
|
+
process.env.WAYGRAPH_AUTOPLAY ??= "0";
|
|
180
|
+
// Explicit --no-step --video = unattended headless .webm only (the
|
|
181
|
+
// documented cloud/CI recipe above) - nobody needs to watch a live
|
|
182
|
+
// window for Playwright's own recordVideo to capture the right thing.
|
|
183
|
+
// This must be decided BEFORE the headed default below, or the
|
|
184
|
+
// unconditional "??=" there always wins and the documented recipe never
|
|
185
|
+
// actually runs headless.
|
|
186
|
+
const unattendedVideo = Boolean(process.env.WAYGRAPH_VIDEO) && process.env.WAYGRAPH_STEP === "0";
|
|
187
|
+
if (unattendedVideo) {
|
|
188
|
+
process.env.WAYGRAPH_AUTOPLAY ??= "1";
|
|
189
|
+
process.env.WAYGRAPH_HEADED ??= "0";
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// Default: headed stepper, manual Next. --video records that session
|
|
193
|
+
// too (not a headless rush) as long as --step is still on.
|
|
194
|
+
process.env.WAYGRAPH_HEADED ??= "1";
|
|
195
|
+
}
|
|
196
|
+
const videoTo = process.env.WAYGRAPH_VIDEO;
|
|
197
|
+
console.log(videoTo
|
|
198
|
+
? "waygraph try demo: step-through + record (Sign In -> Shop & Checkout -> blocked Viewer login) - click Next; video -> " +
|
|
199
|
+
(videoTo === "1" ? ".waygraph-videos/" : videoTo)
|
|
200
|
+
: "waygraph try demo: step-through (Sign In -> Shop & Checkout -> blocked Viewer login) - click Next for each step ...");
|
|
201
|
+
await runChain(destDir, TRY_DEMO_CHAIN);
|
|
202
|
+
if (process.exitCode)
|
|
203
|
+
return;
|
|
204
|
+
console.log("\nwaygraph try demo: running headless chainFlow test ...");
|
|
205
|
+
const testCode = await runInherited("npm", ["test"], destDir);
|
|
206
|
+
if (testCode !== 0) {
|
|
207
|
+
process.exitCode = testCode;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const testFile = join(destDir, "tests", "chain-flow.spec.ts");
|
|
211
|
+
const loginFlowFile = join(destDir, "src", "flows", "login.flow.ts");
|
|
212
|
+
const shopFlowFile = join(destDir, "src", "flows", "shop.flow.ts");
|
|
213
|
+
const viewerBlockedFlowFile = join(destDir, "src", "flows", "viewer-blocked.flow.ts");
|
|
214
|
+
console.log("\nwaygraph try demo: done.\n\n" +
|
|
215
|
+
"Temp project (does not touch your cwd - lives under the OS temp dir):\n" +
|
|
216
|
+
` ${destDir}\n\n` +
|
|
217
|
+
"Episodes you just watched (same Flow objects the test imports):\n" +
|
|
218
|
+
` ${loginFlowFile}\n` +
|
|
219
|
+
` ${shopFlowFile}\n` +
|
|
220
|
+
` ${viewerBlockedFlowFile}\n\n` +
|
|
221
|
+
"Headless chainFlow test (automated - no clicking):\n" +
|
|
222
|
+
` ${testFile}\n\n` +
|
|
223
|
+
"Run again in that temp folder:\n" +
|
|
224
|
+
` cd ${destDir}\n` +
|
|
225
|
+
" npm run demo # step-through, manual Next (--step --no-autoplay)\n" +
|
|
226
|
+
" npm run auto # interactive explore (Effect Add/Remove, MemNav Open details)\n" +
|
|
227
|
+
" npm test # playwright chainFlow test\n\n" +
|
|
228
|
+
"Permanent full example in this package: examples/saucedemo\n" +
|
|
229
|
+
"Keep a permanent scaffold in your tree: waygraph init my-app\n");
|
|
230
|
+
}
|
|
231
|
+
const TRY_AUTO_DATA = '{"saucedemo.credentials":{"username":"standard_user","password":"secret_sauce"}}';
|
|
232
|
+
/**
|
|
233
|
+
* `waygraph try auto` / `try auto:cli` - temp Sauce Demo explore.
|
|
234
|
+
* Default is the **headed** browser panel. Pass `auto:cli` or `--cli` for the
|
|
235
|
+
* terminal menu (same rows).
|
|
236
|
+
*
|
|
237
|
+
* Runs as a child in the temp project so Block imports share that install's
|
|
238
|
+
* `@playwright/test`. In-process explore from the outer CLI against a packed
|
|
239
|
+
* `file:*.tgz` copy double-loads Playwright and silently yields 0 graph edges
|
|
240
|
+
* ("LoginPage / No moves").
|
|
241
|
+
*/
|
|
242
|
+
async function runTryAuto(opts = { cli: false }) {
|
|
243
|
+
const destDir = await prepareTryQuickstart("waygraph-try-auto");
|
|
244
|
+
if (!destDir)
|
|
245
|
+
return;
|
|
246
|
+
process.env.WAYGRAPH_BASE_URL ??= "https://www.saucedemo.com";
|
|
247
|
+
const cli = opts.cli === true;
|
|
248
|
+
console.log(cli
|
|
249
|
+
? "waygraph try auto: CLI explore on Sauce Demo (temp dir).\n" +
|
|
250
|
+
" Creds pre-seeded (saucedemo.credentials).\n" +
|
|
251
|
+
" Pick [1] submit-login, then inventory Add/Remove / Open details.\n" +
|
|
252
|
+
" Type q to quit. Headed panel: waygraph try auto\n"
|
|
253
|
+
: "waygraph try auto: headed explore on Sauce Demo (temp dir).\n" +
|
|
254
|
+
" Creds pre-seeded. CLI menu: waygraph try auto:cli\n" +
|
|
255
|
+
" Pick submit-login, then inventory menus. Quit from the panel.\n");
|
|
256
|
+
const bin = join(destDir, "node_modules", ".bin", "waygraph");
|
|
257
|
+
const args = ["auto"];
|
|
258
|
+
if (cli)
|
|
259
|
+
args.push("--cli");
|
|
260
|
+
args.push("--data", TRY_AUTO_DATA);
|
|
261
|
+
const code = await runInherited(bin, args, destDir);
|
|
262
|
+
if (code !== 0)
|
|
263
|
+
process.exitCode = code;
|
|
264
|
+
console.log("\nwaygraph try auto: explorer closed.\n\n" +
|
|
265
|
+
`Temp project:\n ${destDir}\n\n` +
|
|
266
|
+
"Run again:\n" +
|
|
267
|
+
` cd ${destDir} && npm run auto\n` +
|
|
268
|
+
` cd ${destDir} && npm run auto:cli\n\n` +
|
|
269
|
+
"In-package: examples/saucedemo\n" +
|
|
270
|
+
"Docs: https://deviate-dv8.github.io/waygraph/auto.html\n");
|
|
271
|
+
}
|
|
272
|
+
export async function tryCase(args) {
|
|
273
|
+
const flags = parseRunFlags(args.slice(1));
|
|
274
|
+
applyRunFlags(flags);
|
|
275
|
+
const modeRaw = (flags.positionals[0] ?? "demo").toLowerCase();
|
|
276
|
+
// auto:cli / --cli = CLI; bare try auto = headed panel
|
|
277
|
+
const mode = modeRaw === "auto:cli" || modeRaw === "auto-cli"
|
|
278
|
+
? "auto:cli"
|
|
279
|
+
: modeRaw === "auto"
|
|
280
|
+
? "auto"
|
|
281
|
+
: modeRaw;
|
|
282
|
+
if (mode === "auto" || mode === "auto:cli") {
|
|
283
|
+
const cli = mode === "auto:cli" || flags.cli === true
|
|
284
|
+
? true
|
|
285
|
+
: flags.headed === true || flags.nonHeadless === true
|
|
286
|
+
? false
|
|
287
|
+
: false; // try auto default = headed
|
|
288
|
+
await runTryAuto({ cli });
|
|
289
|
+
}
|
|
290
|
+
else if (mode === "demo" || mode === "") {
|
|
291
|
+
await runTryDemo();
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
console.error(`waygraph try: unknown mode "${modeRaw}" - use "demo", "auto", or "auto:cli"`);
|
|
295
|
+
process.exitCode = 1;
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
waygraph auto: skip src/blocks/saucedemo-web/cart/nav-cart.block.ts: Requiring @playwright/test second time,
|
|
2
|
+
waygraph auto: skip src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts: Requiring @playwright/test second time,
|
|
3
|
+
waygraph auto: skip src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts: Requiring @playwright/test second time,
|
|
4
|
+
waygraph auto: skip src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts: Requiring @playwright/test second time,
|
|
5
|
+
waygraph auto: skip src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts: Requiring @playwright/test second time,
|
|
6
|
+
waygraph auto: skip src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts: Requiring @playwright/test second time,
|
|
7
|
+
waygraph auto: skip src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts: Requiring @playwright/test second time,
|
|
8
|
+
waygraph auto: skip src/blocks/saucedemo-web/ff-owner-auth.block.ts: Requiring @playwright/test second time,
|
|
9
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory/inventory.page.block.ts: Requiring @playwright/test second time,
|
|
10
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory/methods/add-all-to-cart.method.block.ts: Requiring @playwright/test second time,
|
|
11
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts: Requiring @playwright/test second time,
|
|
12
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory/methods/remove-all-from-cart.method.block.ts: Requiring @playwright/test second time,
|
|
13
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory/methods/remove-from-cart.effect.block.ts: Requiring @playwright/test second time,
|
|
14
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory/nav-item-detail.block.ts: Requiring @playwright/test second time,
|
|
15
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts: Requiring @playwright/test second time,
|
|
16
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts: Requiring @playwright/test second time,
|
|
17
|
+
waygraph auto: skip src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts: Requiring @playwright/test second time,
|
|
18
|
+
waygraph auto: skip src/blocks/saucedemo-web/methods/fill-password.method.block.ts: Requiring @playwright/test second time,
|
|
19
|
+
waygraph auto: skip src/blocks/saucedemo-web/methods/fill-username.method.block.ts: Requiring @playwright/test second time,
|
|
20
|
+
waygraph auto: skip src/blocks/saucedemo-web/methods/submit-login.method.block.ts: Requiring @playwright/test second time,
|
|
21
|
+
waygraph auto: skip src/blocks/saucedemo-web/methods/submit-logout.method.block.ts: Requiring @playwright/test second time,
|
|
22
|
+
waygraph auto: skip src/blocks/saucedemo-web/nav-login.block.ts: Requiring @playwright/test second time,
|
|
23
|
+
waygraph graph: skip src/blocks/saucedemo-web/cart/nav-cart.block.ts: Requiring @playwright/test second time,
|
|
24
|
+
waygraph graph: skip src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts: Requiring @playwright/test second time,
|
|
25
|
+
waygraph graph: skip src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts: Requiring @playwright/test second time,
|
|
26
|
+
waygraph graph: skip src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts: Requiring @playwright/test second time,
|
|
27
|
+
waygraph graph: skip src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts: Requiring @playwright/test second time,
|
|
28
|
+
waygraph graph: skip src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts: Requiring @playwright/test second time,
|
|
29
|
+
waygraph graph: skip src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts: Requiring @playwright/test second time,
|
|
30
|
+
waygraph graph: skip src/blocks/saucedemo-web/ff-owner-auth.block.ts: Requiring @playwright/test second time,
|
|
31
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory/inventory.page.block.ts: Requiring @playwright/test second time,
|
|
32
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory/methods/add-all-to-cart.method.block.ts: Requiring @playwright/test second time,
|
|
33
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts: Requiring @playwright/test second time,
|
|
34
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory/methods/remove-all-from-cart.method.block.ts: Requiring @playwright/test second time,
|
|
35
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory/methods/remove-from-cart.effect.block.ts: Requiring @playwright/test second time,
|
|
36
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory/nav-item-detail.block.ts: Requiring @playwright/test second time,
|
|
37
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts: Requiring @playwright/test second time,
|
|
38
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts: Requiring @playwright/test second time,
|
|
39
|
+
waygraph graph: skip src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts: Requiring @playwright/test second time,
|
|
40
|
+
waygraph graph: skip src/blocks/saucedemo-web/methods/fill-password.method.block.ts: Requiring @playwright/test second time,
|
|
41
|
+
waygraph graph: skip src/blocks/saucedemo-web/methods/fill-username.method.block.ts: Requiring @playwright/test second time,
|
|
42
|
+
waygraph graph: skip src/blocks/saucedemo-web/methods/submit-login.method.block.ts: Requiring @playwright/test second time,
|
|
43
|
+
waygraph graph: skip src/blocks/saucedemo-web/methods/submit-logout.method.block.ts: Requiring @playwright/test second time,
|
|
44
|
+
waygraph graph: skip src/blocks/saucedemo-web/nav-login.block.ts: Requiring @playwright/test second time,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|