waygraph 0.15.43 → 0.15.45
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/analysis/coverage-gap.d.ts +11 -0
- package/dist/analysis/coverage-gap.js +119 -0
- package/dist/analysis/graph.d.ts +92 -0
- package/dist/analysis/graph.js +467 -0
- package/dist/analysis/map-check.d.ts +18 -0
- package/dist/analysis/map-check.js +140 -0
- package/dist/analysis/practices-check.d.ts +14 -0
- package/dist/analysis/practices-check.js +195 -0
- package/dist/auto-explore.d.ts +1 -1
- package/dist/auto-explore.js +1 -1
- package/dist/auto-session/helpers.d.ts +2 -2
- package/dist/auto-session/session.js +4 -4
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/util.d.ts +8 -1
- package/dist/cli/util.js +9 -2
- package/dist/commands/check.js +2 -2
- package/dist/commands/map.js +1 -1
- package/dist/commands/session.js +7 -7
- package/dist/commands/traverse.js +2 -2
- package/dist/engine/branch-regression.js +2 -1
- package/dist/engine/compose.js +3 -2
- package/dist/engine/core.js +5 -4
- package/dist/engine/engine-class.js +13 -12
- package/dist/engine/flow.js +4 -3
- package/dist/engine/run-graph.js +4 -3
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +125 -0
- package/dist/highlights/shorthand.d.ts +24 -0
- package/dist/highlights/shorthand.js +30 -0
- package/dist/index.d.ts +13 -11
- package/dist/index.js +7 -6
- package/dist/mem-page.js +2 -1
- package/dist/pilot.d.ts +1 -1
- package/dist/pilot.js +1 -1
- package/dist/session/auto-explore-run.d.ts +37 -0
- package/dist/session/auto-explore-run.js +670 -0
- package/dist/session/auto-explore.d.ts +57 -0
- package/dist/session/auto-explore.js +376 -0
- package/dist/session/auto-session-ipc.d.ts +150 -0
- package/dist/session/auto-session-ipc.js +499 -0
- package/dist/session/block-inject.d.ts +21 -0
- package/dist/session/block-inject.js +97 -0
- package/dist/session/browser.d.ts +15 -0
- package/dist/session/browser.js +29 -0
- package/dist/session/overlay-beacon.d.ts +44 -0
- package/dist/session/overlay-beacon.js +85 -0
- package/dist/session/pilot.d.ts +54 -0
- package/dist/session/pilot.js +80 -0
- package/dist/traverse/coverage.d.ts +59 -0
- package/dist/traverse/coverage.js +127 -0
- package/dist/traverse/lease.d.ts +18 -0
- package/dist/traverse/lease.js +62 -0
- package/dist/traverse/run.d.ts +47 -0
- package/dist/traverse/run.js +439 -0
- package/examples/saucedemo/.waygraph-auto/00b0c55c.log +22 -0
- package/examples/saucedemo/.waygraph-auto/00e5d037.log +5 -0
- package/examples/saucedemo/.waygraph-auto/015be8f6.log +6 -0
- package/examples/saucedemo/.waygraph-auto/026e188e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/050e5b55.log +0 -0
- package/examples/saucedemo/.waygraph-auto/07845ed1.log +0 -0
- package/examples/saucedemo/.waygraph-auto/096fea05.log +20 -0
- package/examples/saucedemo/.waygraph-auto/0c19e232.log +5 -0
- package/examples/saucedemo/.waygraph-auto/1122c13c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/123d1cad.log +5 -0
- package/examples/saucedemo/.waygraph-auto/12ed8e28.log +0 -0
- package/examples/saucedemo/.waygraph-auto/1cb6f44c.log +5 -0
- package/examples/saucedemo/.waygraph-auto/1fdb2100.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2b9b56fc.log +20 -0
- package/examples/saucedemo/.waygraph-auto/30a5c848.log +20 -0
- package/examples/saucedemo/.waygraph-auto/33a50dc0.log +0 -0
- package/examples/saucedemo/.waygraph-auto/3a208bb8.log +20 -0
- package/examples/saucedemo/.waygraph-auto/3e3c0f3b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/573abedd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/756fd326.log +20 -0
- package/examples/saucedemo/.waygraph-auto/78043767.log +20 -0
- package/examples/saucedemo/.waygraph-auto/976394cd.log +20 -0
- package/examples/saucedemo/.waygraph-auto/9a26134c.log +20 -0
- package/examples/saucedemo/.waygraph-auto/9d827954.log +20 -0
- package/examples/saucedemo/.waygraph-auto/a4f8d355.log +20 -0
- package/examples/saucedemo/.waygraph-auto/aa226dd0.log +22 -0
- package/examples/saucedemo/.waygraph-auto/ac5dde3d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/af2fae12.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b0752c49.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b14811d7.log +6 -0
- package/examples/saucedemo/.waygraph-auto/c9d76e32.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cae91469.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cc989184.log +20 -0
- package/examples/saucedemo/.waygraph-auto/d846cd78.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e5127c03.log +22 -0
- package/examples/saucedemo/.waygraph-auto/eb38d92e.log +22 -0
- package/examples/saucedemo/.waygraph-auto/ee86203e.log +0 -0
- package/package.json +1 -1
- package/templates/agents/waygraph-author.agent.md +24 -51
- package/templates/agents/waygraph-planner.agent.md +12 -5
- package/templates/scaffold/scripts/README.md +13 -0
- package/templates/scaffold/src/flows/README.md +18 -0
- package/templates/scaffold/src/map/README.md +31 -0
- package/templates/scaffold/src/states/README.md +15 -0
- package/templates/scaffold/tests/README.md +10 -0
|
@@ -0,0 +1,439 @@
|
|
|
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
|
+
import { Engine, start, end, locate } from "../engine.js";
|
|
8
|
+
import { MemPage } from "../mem-page.js";
|
|
9
|
+
import { buildExploreContext, buildExploreMenu, } from "../session/auto-explore.js";
|
|
10
|
+
import { defaultMemValueForKey } from "../session/auto-explore-run.js";
|
|
11
|
+
import { EdgeLeaseCoordinator } from "./lease.js";
|
|
12
|
+
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
|
+
function seedMem(library, mem, dataJson) {
|
|
28
|
+
const raw = dataJson ?? process.env.WAYGRAPH_DATA ?? process.env.WAYGRAPH_AUTO_MEM;
|
|
29
|
+
let envParsed = {};
|
|
30
|
+
if (raw) {
|
|
31
|
+
try {
|
|
32
|
+
envParsed = JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new Error(`waygraph traverse: --data / WAYGRAPH_DATA is not valid JSON`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const entry of library.values()) {
|
|
39
|
+
for (const k of entry.block.requires ?? []) {
|
|
40
|
+
if (mem.has(k))
|
|
41
|
+
continue;
|
|
42
|
+
if (k.name in envParsed) {
|
|
43
|
+
mem.set(k, envParsed[k.name]);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const def = defaultMemValueForKey(k.name);
|
|
47
|
+
if (def !== undefined)
|
|
48
|
+
mem.set(k, def);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function snapshotMem(library, mem) {
|
|
53
|
+
const out = {};
|
|
54
|
+
for (const entry of library.values()) {
|
|
55
|
+
for (const k of entry.block.requires ?? []) {
|
|
56
|
+
if (mem.has(k) && !(k.name in out)) {
|
|
57
|
+
try {
|
|
58
|
+
out[k.name] = mem.get(k);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
/* skip */
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
function restoreMem(library, mem, snap, dataJson) {
|
|
69
|
+
seedMem(library, mem, dataJson);
|
|
70
|
+
for (const entry of library.values()) {
|
|
71
|
+
for (const k of entry.block.requires ?? []) {
|
|
72
|
+
if (k.name in snap) {
|
|
73
|
+
mem.set(k, snap[k.name]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function missingMemKeys(entry, mem) {
|
|
79
|
+
return (entry.block.requires ?? []).filter((k) => !mem.has(k));
|
|
80
|
+
}
|
|
81
|
+
function ensureMem(entry, mem) {
|
|
82
|
+
const missing = missingMemKeys(entry, mem);
|
|
83
|
+
if (missing.length === 0)
|
|
84
|
+
return;
|
|
85
|
+
throw new Error(`Block "${entry.block.name}" needs Mem keys: ${missing.map((k) => k.name).join(", ")} ` +
|
|
86
|
+
`(pass --data '{...}' or WAYGRAPH_DATA)`);
|
|
87
|
+
}
|
|
88
|
+
function edgeKey(edge) {
|
|
89
|
+
const id = edge.instanceOption?.id;
|
|
90
|
+
return id ? `${edge.block}::${id}` : edge.block;
|
|
91
|
+
}
|
|
92
|
+
async function detectHere(page, navBlocks) {
|
|
93
|
+
try {
|
|
94
|
+
const url = page.url();
|
|
95
|
+
if (url.includes("/login"))
|
|
96
|
+
return "PiaLogin";
|
|
97
|
+
if (url.includes("mailpit") || url.includes(":54324") || url.includes("/mailhog")) {
|
|
98
|
+
return "PiaMailpitGui";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* ignore */
|
|
103
|
+
}
|
|
104
|
+
const tags = navBlocks.map((n) => n.block);
|
|
105
|
+
return locate(page, tags, { timeoutMs: 1500 });
|
|
106
|
+
}
|
|
107
|
+
async function runOneBlock(engine, entry, context, page, mem) {
|
|
108
|
+
const flow = engine.defineFlow([start, entry.block, end]);
|
|
109
|
+
const BLOCK_TIMEOUT_MS = 45_000;
|
|
110
|
+
let timer;
|
|
111
|
+
try {
|
|
112
|
+
const ran = await Promise.race([
|
|
113
|
+
flow.run(context, mem, { page, closeOnFinish: false }),
|
|
114
|
+
new Promise((_, reject) => {
|
|
115
|
+
timer = setTimeout(() => reject(new Error(`Block "${entry.block.name}" timed out after ${BLOCK_TIMEOUT_MS / 1000}s`)), BLOCK_TIMEOUT_MS);
|
|
116
|
+
}),
|
|
117
|
+
]);
|
|
118
|
+
return ran.result;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
if (timer)
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function runTraverseWorker(opts) {
|
|
126
|
+
const { traverseId, workerIndex, parallel, maxSteps, maxVisitsPerNode, maxVisitsPerEdge, timeoutMs, graph, library, engine, context, mem, leases, } = opts;
|
|
127
|
+
let page = opts.page;
|
|
128
|
+
const startUrl = opts.startUrl;
|
|
129
|
+
const nodeVisits = new Map();
|
|
130
|
+
const edgeVisits = new Map();
|
|
131
|
+
const edgesHit = new Set();
|
|
132
|
+
let steps = 0;
|
|
133
|
+
const startedAt = Date.now();
|
|
134
|
+
let lastHere = opts.from ?? null;
|
|
135
|
+
let exitCode = 0;
|
|
136
|
+
let leaf;
|
|
137
|
+
try {
|
|
138
|
+
for (;;) {
|
|
139
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
140
|
+
console.log(`[Killed timeout[${traverseId}] node=${lastHere ?? "?"} wallMs=${timeoutMs}]`);
|
|
141
|
+
exitCode = 1;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
if (steps >= maxSteps) {
|
|
145
|
+
console.log(`[Killed max-steps[${traverseId}] node=${lastHere ?? "?"} steps=${steps} max=${maxSteps}]`);
|
|
146
|
+
exitCode = 1;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
if (page.isClosed()) {
|
|
150
|
+
page = await context.newPage();
|
|
151
|
+
if (startUrl) {
|
|
152
|
+
await page.goto(startUrl, { waitUntil: "domcontentloaded" }).catch(() => { });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const detected = await detectHere(page, library.navBlocks);
|
|
156
|
+
const here = lastHere ?? detected ?? opts.from ?? null;
|
|
157
|
+
if (here && (nodeVisits.get(here) ?? 0) >= maxVisitsPerNode) {
|
|
158
|
+
console.log(`[Killed loop[${traverseId}] node=${here} visits=${nodeVisits.get(here)} max=${maxVisitsPerNode}]`);
|
|
159
|
+
exitCode = 1;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
const menu = await buildExploreMenu(page, graph, library, here);
|
|
163
|
+
const candidates = menu.flat.filter((edge) => {
|
|
164
|
+
const key = edgeKey(edge);
|
|
165
|
+
if ((edgeVisits.get(key) ?? 0) >= maxVisitsPerEdge)
|
|
166
|
+
return false;
|
|
167
|
+
if (parallel > 1) {
|
|
168
|
+
if (EdgeLeaseCoordinator.partitionIndex(key, parallel) !== workerIndex) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
});
|
|
174
|
+
if (candidates.length === 0) {
|
|
175
|
+
const label = here ?? detected ?? "unknown";
|
|
176
|
+
leaf = label;
|
|
177
|
+
console.log(`[Reached Leaf Node[${traverseId}] at=${label} steps=${steps}]`);
|
|
178
|
+
console.error(`waygraph traverse[${traverseId}]: PASS leaf edgesHit=${edgesHit.size} steps=${steps}`);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
const pick = [...candidates].sort((a, b) => {
|
|
182
|
+
const aWild = a.from === "*" ? 1 : 0;
|
|
183
|
+
const bWild = b.from === "*" ? 1 : 0;
|
|
184
|
+
if (aWild !== bWild)
|
|
185
|
+
return aWild - bWild;
|
|
186
|
+
return edgeKey(a).localeCompare(edgeKey(b));
|
|
187
|
+
})[0];
|
|
188
|
+
const key = edgeKey(pick);
|
|
189
|
+
if (!leases.tryClaim(key, traverseId)) {
|
|
190
|
+
// Another worker won a race (instance fan-out); treat as unavailable.
|
|
191
|
+
edgeVisits.set(key, maxVisitsPerEdge);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const entry = library.byName.get(pick.block);
|
|
195
|
+
if (!entry) {
|
|
196
|
+
console.log(`[Broke at edge[${traverseId}] block=${pick.block} from=${here ?? "?"} to=${pick.to} reason=not-loaded]`);
|
|
197
|
+
exitCode = 1;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
const fromLabel = here ?? pick.from;
|
|
201
|
+
try {
|
|
202
|
+
if (pick.instanceOption) {
|
|
203
|
+
mem.set(pick.instanceOption.key, pick.instanceOption.value);
|
|
204
|
+
}
|
|
205
|
+
ensureMem(entry, mem);
|
|
206
|
+
console.error(`waygraph traverse[${traverseId}]: step ${steps + 1} ${fromLabel} -[${pick.block}]-> ${pick.to}` +
|
|
207
|
+
(pick.label ? ` (${pick.label})` : ""));
|
|
208
|
+
const out = await runOneBlock(engine, entry, context, page, mem);
|
|
209
|
+
const observed = typeof out === "object" && out && "__state" in out
|
|
210
|
+
? String(out.__state)
|
|
211
|
+
: pick.to;
|
|
212
|
+
edgeVisits.set(key, (edgeVisits.get(key) ?? 0) + 1);
|
|
213
|
+
edgesHit.add(key);
|
|
214
|
+
if (here)
|
|
215
|
+
nodeVisits.set(here, (nodeVisits.get(here) ?? 0) + 1);
|
|
216
|
+
lastHere = observed;
|
|
217
|
+
steps += 1;
|
|
218
|
+
if (observed !== pick.to && pick.to !== "*") {
|
|
219
|
+
console.error(`waygraph traverse[${traverseId}]: WARN static to=${pick.to} observed=${observed}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
224
|
+
console.log(`[Broke at edge[${traverseId}] block=${pick.block} from=${fromLabel} to=${pick.to}] ${msg}`);
|
|
225
|
+
exitCode = 1;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
console.error(`waygraph traverse[${traverseId}]: done exit=${exitCode} steps=${steps} edgesHit=${edgesHit.size}`);
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
exitCode,
|
|
235
|
+
steps,
|
|
236
|
+
edgesHit: [...edgesHit],
|
|
237
|
+
...(leaf ? { leaf } : {}),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Serial or parallel graph crawl. Prints greppable PASS/FAIL + Coverage lines.
|
|
242
|
+
* Exit: 0 leaf ok (+ coverage ok), 1 break/timeout, 2 coverage gate fail (Phase E).
|
|
243
|
+
*/
|
|
244
|
+
export async function runTraverse(projectDir, options = {}) {
|
|
245
|
+
let parallel = Math.max(1, Math.floor(options.parallel ?? 1));
|
|
246
|
+
if (parallel > 4) {
|
|
247
|
+
console.error(`waygraph traverse: --parallel ${parallel} capped to 4`);
|
|
248
|
+
parallel = 4;
|
|
249
|
+
}
|
|
250
|
+
const session = options.session ?? (parallel > 1 ? "clone" : "clone");
|
|
251
|
+
if (parallel > 1 && session === "inherit") {
|
|
252
|
+
console.error("waygraph traverse: --session inherit is refused with --parallel > 1 (races); use --session clone");
|
|
253
|
+
return 1;
|
|
254
|
+
}
|
|
255
|
+
const maxSteps = options.maxSteps ?? 50;
|
|
256
|
+
const maxVisitsPerNode = options.maxVisitsPerNode ?? 2;
|
|
257
|
+
const maxVisitsPerEdge = options.maxVisitsPerEdge ?? 1;
|
|
258
|
+
const timeoutMs = options.timeoutMs ?? 15 * 60_000;
|
|
259
|
+
const headed = options.headed === true || process.env.WAYGRAPH_HEADED === "1";
|
|
260
|
+
const baseURL = options.baseURL ?? resolveBaseUrl(projectDir) ?? process.env.WAYGRAPH_BASE_URL;
|
|
261
|
+
const startUrl = options.startUrl ?? baseURL;
|
|
262
|
+
const minEdgeCoverage = options.minEdgeCoverage != null && Number.isFinite(options.minEdgeCoverage)
|
|
263
|
+
? options.minEdgeCoverage
|
|
264
|
+
: undefined;
|
|
265
|
+
const coverageOut = options.coverageOut ?? defaultCoverageOutPath(projectDir);
|
|
266
|
+
const { graph, library } = await buildExploreContext(projectDir, options.blocksSelect ? { blocksSelect: options.blocksSelect } : undefined);
|
|
267
|
+
if (options.blocksSelect) {
|
|
268
|
+
console.error(`waygraph traverse: --blocks ${options.blocksSelect.raw} -> ` +
|
|
269
|
+
`${library.byName.size} block(s), ${graph.edges.length} edge(s)`);
|
|
270
|
+
}
|
|
271
|
+
const allEdgeKeys = collectGraphEdgeKeys(graph.edges);
|
|
272
|
+
const leases = new EdgeLeaseCoordinator({ projectDir, persist: parallel > 1 });
|
|
273
|
+
const engine = new Engine({ headless: !headed, slowMo: headed ? 100 : 0 });
|
|
274
|
+
const { chromium } = await import("@playwright/test");
|
|
275
|
+
const executablePath = process.env.CHROME_PATH || process.env.CHROMIUM_PATH;
|
|
276
|
+
const launchOpts = {
|
|
277
|
+
headless: !headed,
|
|
278
|
+
};
|
|
279
|
+
if (executablePath)
|
|
280
|
+
launchOpts.executablePath = executablePath;
|
|
281
|
+
const browser = await chromium.launch(launchOpts);
|
|
282
|
+
console.error(`waygraph traverse: ${graph.nodes.length} node(s), ${graph.edges.length} edge(s)` +
|
|
283
|
+
(baseURL ? ` base=${baseURL}` : "") +
|
|
284
|
+
` parallel=${parallel} session=${session}` +
|
|
285
|
+
` maxSteps=${maxSteps} maxVisits/node=${maxVisitsPerNode} maxVisits/edge=${maxVisitsPerEdge}` +
|
|
286
|
+
(minEdgeCoverage !== undefined
|
|
287
|
+
? ` minEdgeCoverage=${(minEdgeCoverage * 100).toFixed(1)}%`
|
|
288
|
+
: ""));
|
|
289
|
+
const emitCoverage = (workers) => {
|
|
290
|
+
const hitUnion = new Set();
|
|
291
|
+
for (const w of workers) {
|
|
292
|
+
for (const k of w.result.edgesHit)
|
|
293
|
+
hitUnion.add(k);
|
|
294
|
+
}
|
|
295
|
+
const report = buildCoverageReport({
|
|
296
|
+
projectDir,
|
|
297
|
+
parallel,
|
|
298
|
+
allEdgeKeys,
|
|
299
|
+
hitKeys: hitUnion,
|
|
300
|
+
...(minEdgeCoverage !== undefined ? { minEdgeCoverage } : {}),
|
|
301
|
+
workers: workers.map(({ traverseId, result }) => ({
|
|
302
|
+
traverseId,
|
|
303
|
+
exitCode: result.exitCode,
|
|
304
|
+
steps: result.steps,
|
|
305
|
+
edgesHit: result.edgesHit.length,
|
|
306
|
+
...(result.leaf ? { leaf: result.leaf } : {}),
|
|
307
|
+
})),
|
|
308
|
+
});
|
|
309
|
+
console.log(formatCoverageLine(report));
|
|
310
|
+
console.error(`waygraph traverse: coverage hit=${report.edgesHit}/${report.edgesTotal}` +
|
|
311
|
+
(report.missed.length
|
|
312
|
+
? ` missed=${report.missed.slice(0, 8).join(",")}${report.missed.length > 8 ? "..." : ""}`
|
|
313
|
+
: "") +
|
|
314
|
+
(options.noCoverageReport ? "" : ` report=${coverageOut}`));
|
|
315
|
+
if (!options.noCoverageReport) {
|
|
316
|
+
writeCoverageReport(coverageOut, report);
|
|
317
|
+
}
|
|
318
|
+
if (report.coveragePass === false) {
|
|
319
|
+
console.error(`waygraph traverse: FAIL coverage gate ratio=${(report.ratio * 100).toFixed(1)}%` +
|
|
320
|
+
` < min=${((report.minEdgeCoverage ?? 0) * 100).toFixed(1)}%`);
|
|
321
|
+
return 2;
|
|
322
|
+
}
|
|
323
|
+
return 0;
|
|
324
|
+
};
|
|
325
|
+
try {
|
|
326
|
+
if (parallel <= 1) {
|
|
327
|
+
const contextOpts = {
|
|
328
|
+
viewport: { width: 1280, height: 720 },
|
|
329
|
+
};
|
|
330
|
+
if (baseURL)
|
|
331
|
+
contextOpts.baseURL = baseURL;
|
|
332
|
+
const context = await browser.newContext(contextOpts);
|
|
333
|
+
const page = await context.newPage();
|
|
334
|
+
const mem = new MemPage();
|
|
335
|
+
seedMem(library.byName, mem, options.data);
|
|
336
|
+
if (startUrl) {
|
|
337
|
+
await page.goto(startUrl, { waitUntil: "domcontentloaded" }).catch(() => { });
|
|
338
|
+
await page.waitForLoadState("load").catch(() => { });
|
|
339
|
+
}
|
|
340
|
+
const traverseId = options.traverseId ?? "traverse-1";
|
|
341
|
+
const result = await runTraverseWorker({
|
|
342
|
+
traverseId,
|
|
343
|
+
workerIndex: 0,
|
|
344
|
+
parallel: 1,
|
|
345
|
+
maxSteps,
|
|
346
|
+
maxVisitsPerNode,
|
|
347
|
+
maxVisitsPerEdge,
|
|
348
|
+
timeoutMs,
|
|
349
|
+
...(options.from ? { from: options.from } : {}),
|
|
350
|
+
...(startUrl ? { startUrl } : {}),
|
|
351
|
+
...(baseURL ? { baseURL } : {}),
|
|
352
|
+
...(options.data ? { data: options.data } : {}),
|
|
353
|
+
graph,
|
|
354
|
+
library,
|
|
355
|
+
engine,
|
|
356
|
+
context,
|
|
357
|
+
page,
|
|
358
|
+
mem,
|
|
359
|
+
leases,
|
|
360
|
+
});
|
|
361
|
+
await context.close().catch(() => { });
|
|
362
|
+
const coverageCode = emitCoverage([{ traverseId, result }]);
|
|
363
|
+
if (result.exitCode !== 0)
|
|
364
|
+
return result.exitCode;
|
|
365
|
+
return coverageCode;
|
|
366
|
+
}
|
|
367
|
+
// Phase D: bootstrap one context for storageState, then N clones.
|
|
368
|
+
const bootOpts = {
|
|
369
|
+
viewport: { width: 1280, height: 720 },
|
|
370
|
+
};
|
|
371
|
+
if (baseURL)
|
|
372
|
+
bootOpts.baseURL = baseURL;
|
|
373
|
+
const bootContext = await browser.newContext(bootOpts);
|
|
374
|
+
const bootPage = await bootContext.newPage();
|
|
375
|
+
const bootMem = new MemPage();
|
|
376
|
+
seedMem(library.byName, bootMem, options.data);
|
|
377
|
+
if (startUrl) {
|
|
378
|
+
await bootPage.goto(startUrl, { waitUntil: "domcontentloaded" }).catch(() => { });
|
|
379
|
+
await bootPage.waitForLoadState("load").catch(() => { });
|
|
380
|
+
}
|
|
381
|
+
const storageState = await bootContext.storageState();
|
|
382
|
+
const memSnap = snapshotMem(library.byName, bootMem);
|
|
383
|
+
await bootContext.close().catch(() => { });
|
|
384
|
+
console.error(`waygraph traverse: forked ${parallel} clone workers (storageState + mem snapshot)`);
|
|
385
|
+
const workerMeta = await Promise.all(Array.from({ length: parallel }, async (_, i) => {
|
|
386
|
+
const traverseId = `traverse-${i + 1}`;
|
|
387
|
+
const ctxOpts = {
|
|
388
|
+
viewport: { width: 1280, height: 720 },
|
|
389
|
+
storageState,
|
|
390
|
+
};
|
|
391
|
+
if (baseURL)
|
|
392
|
+
ctxOpts.baseURL = baseURL;
|
|
393
|
+
const context = await browser.newContext(ctxOpts);
|
|
394
|
+
const page = await context.newPage();
|
|
395
|
+
const mem = new MemPage();
|
|
396
|
+
restoreMem(library.byName, mem, memSnap, options.data);
|
|
397
|
+
if (startUrl) {
|
|
398
|
+
await page.goto(startUrl, { waitUntil: "domcontentloaded" }).catch(() => { });
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const result = await runTraverseWorker({
|
|
402
|
+
traverseId,
|
|
403
|
+
workerIndex: i,
|
|
404
|
+
parallel,
|
|
405
|
+
maxSteps,
|
|
406
|
+
maxVisitsPerNode,
|
|
407
|
+
maxVisitsPerEdge,
|
|
408
|
+
timeoutMs,
|
|
409
|
+
...(options.from ? { from: options.from } : {}),
|
|
410
|
+
...(startUrl ? { startUrl } : {}),
|
|
411
|
+
...(baseURL ? { baseURL } : {}),
|
|
412
|
+
...(options.data ? { data: options.data } : {}),
|
|
413
|
+
graph,
|
|
414
|
+
library,
|
|
415
|
+
engine,
|
|
416
|
+
context,
|
|
417
|
+
page,
|
|
418
|
+
mem,
|
|
419
|
+
leases,
|
|
420
|
+
});
|
|
421
|
+
return { traverseId, result };
|
|
422
|
+
}
|
|
423
|
+
finally {
|
|
424
|
+
await context.close().catch(() => { });
|
|
425
|
+
}
|
|
426
|
+
}));
|
|
427
|
+
const results = workerMeta.map((w) => w.result);
|
|
428
|
+
const worst = results.reduce((a, b) => Math.max(a, b.exitCode), 0);
|
|
429
|
+
const pass = results.filter((c) => c.exitCode === 0).length;
|
|
430
|
+
console.error(`waygraph traverse: suite parallel=${parallel} pass=${pass}/${parallel} exit=${worst}`);
|
|
431
|
+
const coverageCode = emitCoverage(workerMeta);
|
|
432
|
+
if (worst !== 0)
|
|
433
|
+
return worst;
|
|
434
|
+
return coverageCode;
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
await browser.close().catch(() => { });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[13:03:06.191] waygraph pilot: stub.before ctx.title("Signing in · mobile")
|
|
2
|
+
[13:03:06.193] waygraph pilot: stub.before ctx.todos n=6 index=0
|
|
3
|
+
[13:03:06.193] waygraph pilot: stub.before ctx.device(mobile)
|
|
4
|
+
[13:03:06.193] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
5
|
+
[13:03:06.193] waygraph pilot: stub.before ring #user-name tone=info label="Username"
|
|
6
|
+
[13:03:07.307] waygraph pilot: stub.after ctx.todos n=6 index=1
|
|
7
|
+
[13:03:10.364] waygraph pilot: stub.before ring #password tone=planned label="Password"
|
|
8
|
+
[13:03:10.548] waygraph pilot: stub.after ctx.todos n=6 index=2
|
|
9
|
+
[13:03:13.519] waygraph pilot: stub.before ring #login-button tone=warning label="Login"
|
|
10
|
+
[13:03:13.719] waygraph pilot: stub.after ctx.title("Inventory · landscape")
|
|
11
|
+
[13:03:13.719] waygraph pilot: stub.after ctx.todos n=6 index=3
|
|
12
|
+
[13:03:13.719] waygraph pilot: stub.after ctx.device(mobile)
|
|
13
|
+
[13:03:13.719] waygraph pilot: stub.after ring .inventory_list tone=success label="Product shelf"
|
|
14
|
+
[13:03:19.068] waygraph pilot: stub.before ctx.title("Pick a product · tablet")
|
|
15
|
+
[13:03:19.068] waygraph pilot: stub.before ctx.todos n=6 index=3
|
|
16
|
+
[13:03:19.068] waygraph pilot: stub.before ctx.device(tablet)
|
|
17
|
+
[13:03:19.068] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
18
|
+
[13:03:19.068] 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
|
+
[13:03:19.068] waygraph pilot: stub.before ring [data-test="add-to-cart-sauce-labs-backpack"] tone=success label="Add to cart"
|
|
20
|
+
[13:03:20.353] waygraph pilot: stub.after ctx.title("In the cart")
|
|
21
|
+
[13:03:20.353] waygraph pilot: stub.after ctx.todos n=6 index=4
|
|
22
|
+
[13:03:20.353] waygraph pilot: stub.after ring .shopping_cart_badge tone=success label="Cart badge"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[13:02:45.353] waygraph pilot: highlight ctx.todos n=3 index=0
|
|
2
|
+
[13:02:45.353] waygraph pilot: highlight ring #user-name tone=planned label="Username"
|
|
3
|
+
[13:02:45.354] waygraph pilot: highlight ring #password tone=info label="Password"
|
|
4
|
+
[13:02:47.600] waygraph pilot: highlight ring #no-such-element-waygraph tone=danger label="Gone"
|
|
5
|
+
[13:02:50.359] waygraph pilot: highlight ring #login-button tone=success label="Login"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
[13:03:12.156] waygraph pilot: stub.before ctx.title("Signing in · mobile")
|
|
2
|
+
[13:03:12.157] waygraph pilot: stub.before ctx.todos n=6 index=0
|
|
3
|
+
[13:03:12.157] waygraph pilot: stub.before ctx.device(mobile)
|
|
4
|
+
[13:03:12.159] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
5
|
+
[13:03:12.159] waygraph pilot: stub.before ring #user-name tone=info label="Username"
|
|
6
|
+
[13:03:13.508] waygraph pilot: stub.after ctx.todos n=6 index=1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[12:51:40.250] waygraph pilot: stub.before ctx.title("Signing in · mobile")
|
|
2
|
+
[12:51:40.251] waygraph pilot: stub.before ctx.todos n=6 index=0
|
|
3
|
+
[12:51:40.251] waygraph pilot: stub.before ctx.device(mobile)
|
|
4
|
+
[12:51:40.251] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
5
|
+
[12:51:40.251] waygraph pilot: stub.before ring #user-name tone=info label="Username"
|
|
6
|
+
[12:51:41.434] waygraph pilot: stub.after ctx.todos n=6 index=1
|
|
7
|
+
[12:51:43.164] waygraph pilot: stub.before ring #password tone=planned label="Password"
|
|
8
|
+
[12:51:43.434] waygraph pilot: stub.after ctx.todos n=6 index=2
|
|
9
|
+
[12:51:45.089] waygraph pilot: stub.before ring #login-button tone=warning label="Login"
|
|
10
|
+
[12:51:45.274] waygraph pilot: stub.after ctx.title("Inventory · landscape")
|
|
11
|
+
[12:51:45.274] waygraph pilot: stub.after ctx.todos n=6 index=3
|
|
12
|
+
[12:51:45.274] waygraph pilot: stub.after ctx.device(mobile)
|
|
13
|
+
[12:51:45.274] waygraph pilot: stub.after ring .inventory_list tone=success label="Product shelf"
|
|
14
|
+
[12:51:48.597] waygraph pilot: stub.before ctx.title("Open cart · landscape")
|
|
15
|
+
[12:51:48.597] waygraph pilot: stub.before ctx.todos n=6 index=5
|
|
16
|
+
[12:51:48.597] waygraph pilot: stub.before ctx.device(mobile)
|
|
17
|
+
[12:51:48.597] waygraph pilot: stub.before ctx.zoom(1.4)
|
|
18
|
+
[12:51:48.597] waygraph pilot: stub.before ring .shopping_cart_link tone=planned label="Cart"
|
|
19
|
+
[12:51:48.903] waygraph pilot: stub.after ctx.title("Your cart · desktop")
|
|
20
|
+
[12:51:48.903] waygraph pilot: stub.after ring .cart_item tone=success label="Cart items"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[13:04:34.053] waygraph pilot: highlight ctx.todos n=3 index=0
|
|
2
|
+
[13:04:34.053] waygraph pilot: highlight ring #user-name tone=planned label="Username"
|
|
3
|
+
[13:04:34.054] waygraph pilot: highlight ring #password tone=info label="Password"
|
|
4
|
+
[13:04:37.020] waygraph pilot: highlight ring #no-such-element-waygraph tone=danger label="Gone"
|
|
5
|
+
[13:04:39.671] waygraph pilot: highlight ring #login-button tone=success label="Login"
|
|
File without changes
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[12:44:51.974] waygraph pilot: highlight ctx.todos n=3 index=0
|
|
2
|
+
[12:44:51.974] waygraph pilot: highlight ring #user-name tone=planned label="Username"
|
|
3
|
+
[12:44:51.975] waygraph pilot: highlight ring #password tone=info label="Password"
|
|
4
|
+
[12:44:54.579] waygraph pilot: highlight ring #no-such-element-waygraph tone=danger label="Gone"
|
|
5
|
+
[12:44:56.716] waygraph pilot: highlight ring #login-button tone=success label="Login"
|
|
File without changes
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[12:51:26.993] waygraph pilot: highlight ctx.todos n=3 index=0
|
|
2
|
+
[12:51:26.994] waygraph pilot: highlight ring #user-name tone=planned label="Username"
|
|
3
|
+
[12:51:26.994] waygraph pilot: highlight ring #password tone=info label="Password"
|
|
4
|
+
[12:51:29.348] waygraph pilot: highlight ring #no-such-element-waygraph tone=danger label="Gone"
|
|
5
|
+
[12:51:31.573] waygraph pilot: highlight ring #login-button tone=success label="Login"
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
node:internal/modules/esm/resolve:272
|
|
2
|
+
throw new ERR_MODULE_NOT_FOUND(
|
|
3
|
+
^
|
|
4
|
+
|
|
5
|
+
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/dan/Desktop/Projects/waygraph/dist/dist/cli.js' imported from /home/dan/Desktop/Projects/waygraph/examples/saucedemo/
|
|
6
|
+
[90m at finalizeResolution (node:internal/modules/esm/resolve:272:11)[39m
|
|
7
|
+
[90m at moduleResolve (node:internal/modules/esm/resolve:879:10)[39m
|
|
8
|
+
[90m at defaultResolve (node:internal/modules/esm/resolve:1006:11)[39m
|
|
9
|
+
[90m at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)[39m
|
|
10
|
+
[90m at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)[39m
|
|
11
|
+
[90m at nextStep (node:internal/modules/customization_hooks:189:26)[39m
|
|
12
|
+
at resolveBaseSync (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:10932)
|
|
13
|
+
at resolveDirectorySync (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:12238)
|
|
14
|
+
at resolveTsPathsSync (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:13445)
|
|
15
|
+
at resolve (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:15991) {
|
|
16
|
+
code: [32m'ERR_MODULE_NOT_FOUND'[39m,
|
|
17
|
+
url: [32m'file:///home/dan/Desktop/Projects/waygraph/dist/dist/cli.js'[39m
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Node.js v26.7.0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
node:internal/modules/esm/resolve:272
|
|
2
|
+
throw new ERR_MODULE_NOT_FOUND(
|
|
3
|
+
^
|
|
4
|
+
|
|
5
|
+
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/dan/Desktop/Projects/waygraph/dist/dist/cli.js' imported from /home/dan/Desktop/Projects/waygraph/
|
|
6
|
+
[90m at finalizeResolution (node:internal/modules/esm/resolve:272:11)[39m
|
|
7
|
+
[90m at moduleResolve (node:internal/modules/esm/resolve:879:10)[39m
|
|
8
|
+
[90m at defaultResolve (node:internal/modules/esm/resolve:1006:11)[39m
|
|
9
|
+
[90m at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)[39m
|
|
10
|
+
[90m at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)[39m
|
|
11
|
+
[90m at nextStep (node:internal/modules/customization_hooks:189:26)[39m
|
|
12
|
+
at resolveBaseSync [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:10932[90m)[39m
|
|
13
|
+
at resolveDirectorySync [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:12238[90m)[39m
|
|
14
|
+
at resolveTsPathsSync [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:13445[90m)[39m
|
|
15
|
+
at resolve [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:15991[90m)[39m {
|
|
16
|
+
code: [32m'ERR_MODULE_NOT_FOUND'[39m,
|
|
17
|
+
url: [32m'file:///home/dan/Desktop/Projects/waygraph/dist/dist/cli.js'[39m
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Node.js v26.7.0
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
node:internal/modules/esm/resolve:272
|
|
2
|
+
throw new ERR_MODULE_NOT_FOUND(
|
|
3
|
+
^
|
|
4
|
+
|
|
5
|
+
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/dan/Desktop/Projects/waygraph/dist/dist/cli.js' imported from /home/dan/Desktop/Projects/waygraph/
|
|
6
|
+
[90m at finalizeResolution (node:internal/modules/esm/resolve:272:11)[39m
|
|
7
|
+
[90m at moduleResolve (node:internal/modules/esm/resolve:879:10)[39m
|
|
8
|
+
[90m at defaultResolve (node:internal/modules/esm/resolve:1006:11)[39m
|
|
9
|
+
[90m at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)[39m
|
|
10
|
+
[90m at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)[39m
|
|
11
|
+
[90m at nextStep (node:internal/modules/customization_hooks:189:26)[39m
|
|
12
|
+
at resolveBaseSync [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:10932[90m)[39m
|
|
13
|
+
at resolveDirectorySync [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:12238[90m)[39m
|
|
14
|
+
at resolveTsPathsSync [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:13445[90m)[39m
|
|
15
|
+
at resolve [90m(file:///home/dan/Desktop/Projects/waygraph/[39mnode_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:15991[90m)[39m {
|
|
16
|
+
code: [32m'ERR_MODULE_NOT_FOUND'[39m,
|
|
17
|
+
url: [32m'file:///home/dan/Desktop/Projects/waygraph/dist/dist/cli.js'[39m
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Node.js v26.7.0
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[13:04:45.981] waygraph pilot: stub.before ctx.title("Signing in · mobile")
|
|
2
|
+
[13:04:45.982] waygraph pilot: stub.before ctx.todos n=6 index=0
|
|
3
|
+
[13:04:45.982] waygraph pilot: stub.before ctx.device(mobile)
|
|
4
|
+
[13:04:45.982] waygraph pilot: stub.before ctx.zoom(1.35)
|
|
5
|
+
[13:04:45.982] waygraph pilot: stub.before ring #user-name tone=info label="Username"
|
|
6
|
+
[13:04:47.594] waygraph pilot: stub.after ctx.todos n=6 index=1
|
|
7
|
+
[13:04:49.088] waygraph pilot: stub.before ring #password tone=planned label="Password"
|
|
8
|
+
[13:04:49.276] waygraph pilot: stub.after ctx.todos n=6 index=2
|
|
9
|
+
[13:04:51.102] waygraph pilot: stub.before ring #login-button tone=warning label="Login"
|
|
10
|
+
[13:04:51.352] waygraph pilot: stub.after ctx.title("Inventory · landscape")
|
|
11
|
+
[13:04:51.353] waygraph pilot: stub.after ctx.todos n=6 index=3
|
|
12
|
+
[13:04:51.353] waygraph pilot: stub.after ctx.device(mobile)
|
|
13
|
+
[13:04:51.354] waygraph pilot: stub.after ring .inventory_list tone=success label="Product shelf"
|
|
14
|
+
[13:04:55.294] waygraph pilot: stub.before ctx.title("Open cart · landscape")
|
|
15
|
+
[13:04:55.294] waygraph pilot: stub.before ctx.todos n=6 index=5
|
|
16
|
+
[13:04:55.294] waygraph pilot: stub.before ctx.device(mobile)
|
|
17
|
+
[13:04:55.294] waygraph pilot: stub.before ctx.zoom(1.4)
|
|
18
|
+
[13:04:55.294] waygraph pilot: stub.before ring .shopping_cart_link tone=planned label="Cart"
|
|
19
|
+
[13:04:55.768] waygraph pilot: stub.after ctx.title("Your cart · desktop")
|
|
20
|
+
[13:04:55.768] waygraph pilot: stub.after ring .cart_item tone=success label="Cart items"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
node:internal/modules/esm/resolve:272
|
|
2
|
+
throw new ERR_MODULE_NOT_FOUND(
|
|
3
|
+
^
|
|
4
|
+
|
|
5
|
+
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/dan/Desktop/Projects/waygraph/dist/dist/cli.js' imported from /home/dan/Desktop/Projects/waygraph/examples/saucedemo/
|
|
6
|
+
[90m at finalizeResolution (node:internal/modules/esm/resolve:272:11)[39m
|
|
7
|
+
[90m at moduleResolve (node:internal/modules/esm/resolve:879:10)[39m
|
|
8
|
+
[90m at defaultResolve (node:internal/modules/esm/resolve:1006:11)[39m
|
|
9
|
+
[90m at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)[39m
|
|
10
|
+
[90m at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)[39m
|
|
11
|
+
[90m at nextStep (node:internal/modules/customization_hooks:189:26)[39m
|
|
12
|
+
at resolveBaseSync (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:10932)
|
|
13
|
+
at resolveDirectorySync (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:12238)
|
|
14
|
+
at resolveTsPathsSync (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:13445)
|
|
15
|
+
at resolve (file:///home/dan/Desktop/Projects/waygraph/node_modules/[4mtsx[24m/dist/register-SoqaU4rg.mjs:2:15991) {
|
|
16
|
+
code: [32m'ERR_MODULE_NOT_FOUND'[39m,
|
|
17
|
+
url: [32m'file:///home/dan/Desktop/Projects/waygraph/dist/dist/cli.js'[39m
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Node.js v26.7.0
|