@sightmap/playwright 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -7
- package/dist/cli.js +92 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -36,9 +36,21 @@ Global flags:
|
|
|
36
36
|
|
|
37
37
|
### `snapshot` — enriched a11y snapshot
|
|
38
38
|
|
|
39
|
-
Captures an ARIA snapshot from the current page and annotates
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
Captures an ARIA snapshot from the current page and **annotates the a11y
|
|
40
|
+
tree itself**: every node that matches a sightmap component gets an inline
|
|
41
|
+
`[sightmap=Name]` token, placed right after the engine's `[ref=eN]` token.
|
|
42
|
+
A header still lists the matched view, view memory, and components (with
|
|
43
|
+
live `matchCount`) as an index.
|
|
44
|
+
|
|
45
|
+
The annotation is **purely additive**:
|
|
46
|
+
|
|
47
|
+
- The engine's native `[ref=eN]` tokens are preserved verbatim — the
|
|
48
|
+
agent's click/navigation and sightmap-bootstrap paths are untouched.
|
|
49
|
+
- Unmatched nodes are byte-identical to the un-enriched tool, so a page
|
|
50
|
+
with zero sightmap coverage looks exactly as before.
|
|
51
|
+
- Multiple component matches on one node are comma-joined
|
|
52
|
+
(`[sightmap=PrimaryCTA,Checkout.SubmitButton]`); view-scoped components
|
|
53
|
+
are slash-qualified (`[sightmap=Checkout/PaymentForm]`).
|
|
42
54
|
|
|
43
55
|
```text
|
|
44
56
|
$ sightmap-playwright snapshot
|
|
@@ -51,12 +63,17 @@ Components:
|
|
|
51
63
|
memory: clicking opens detail in a new tab
|
|
52
64
|
|
|
53
65
|
--- ARIA snapshot ---
|
|
54
|
-
- main:
|
|
55
|
-
- heading "Results for 'sightmap'" [level=1]
|
|
56
|
-
|
|
66
|
+
- main [ref=e1]:
|
|
67
|
+
- heading "Results for 'sightmap'" [level=1] [ref=e2]
|
|
68
|
+
- searchbox "Search" [ref=e3] [sightmap=SearchBar]
|
|
69
|
+
- listitem [ref=e7] [sightmap=ResultCard]:
|
|
70
|
+
- link "First result" [ref=e8]
|
|
57
71
|
```
|
|
58
72
|
|
|
59
|
-
|
|
73
|
+
Annotation requires a Chromium-backed session; non-Chromium engines (no
|
|
74
|
+
CDP) degrade gracefully to the un-annotated tree. Pass `--json` to get the
|
|
75
|
+
structured `EnrichedSnapshot` (now including `enrichedAriaText`) plus the
|
|
76
|
+
raw ARIA text under `ariaSnapshot` for back-compat.
|
|
60
77
|
|
|
61
78
|
### `match URL` — show the matching sightmap view
|
|
62
79
|
|
package/dist/cli.js
CHANGED
|
@@ -274,6 +274,71 @@ function parseInPageEvalStdout(stdout) {
|
|
|
274
274
|
return [];
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
// src/ref-join.ts
|
|
278
|
+
function buildRefJoinSnippet(components, refs) {
|
|
279
|
+
const componentsJson = JSON.stringify(components);
|
|
280
|
+
const refsJson = JSON.stringify(refs);
|
|
281
|
+
return `async (page) => {
|
|
282
|
+
const components = ${componentsJson};
|
|
283
|
+
const refs = ${refsJson};
|
|
284
|
+
await page.evaluate((components) => {
|
|
285
|
+
for (const c of components) {
|
|
286
|
+
for (const sel of (c.selector || [])) {
|
|
287
|
+
let els;
|
|
288
|
+
try { els = document.querySelectorAll(sel); } catch { continue; }
|
|
289
|
+
for (const el of Array.from(els)) {
|
|
290
|
+
let names = [];
|
|
291
|
+
try { names = JSON.parse(el.getAttribute("data-sm-mark") || "[]"); } catch { names = []; }
|
|
292
|
+
if (!names.includes(c.name)) names.push(c.name);
|
|
293
|
+
el.setAttribute("data-sm-mark", JSON.stringify(names));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}, components);
|
|
298
|
+
const refAnnotations = {};
|
|
299
|
+
await Promise.all(refs.map(async (ref) => {
|
|
300
|
+
try {
|
|
301
|
+
const mark = await page.locator("aria-ref=" + ref).evaluate((el) => el.getAttribute("data-sm-mark"));
|
|
302
|
+
if (mark) {
|
|
303
|
+
const names = JSON.parse(mark);
|
|
304
|
+
if (Array.isArray(names) && names.length > 0) refAnnotations[ref] = names;
|
|
305
|
+
}
|
|
306
|
+
} catch { /* ref no longer resolvable \u2014 skip */ }
|
|
307
|
+
}));
|
|
308
|
+
try {
|
|
309
|
+
await page.evaluate(() => {
|
|
310
|
+
for (const el of Array.from(document.querySelectorAll("[data-sm-mark]"))) {
|
|
311
|
+
el.removeAttribute("data-sm-mark");
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
} catch { /* best-effort cleanup */ }
|
|
315
|
+
return { refAnnotations };
|
|
316
|
+
}`;
|
|
317
|
+
}
|
|
318
|
+
function parseRefJoinOutput(stdout) {
|
|
319
|
+
const empty = /* @__PURE__ */ new Map();
|
|
320
|
+
const text = stdout.trim();
|
|
321
|
+
if (text.length === 0) return empty;
|
|
322
|
+
const afterResult = text.split(/^###\s*Result/im)[1] ?? text;
|
|
323
|
+
const start = afterResult.indexOf("{");
|
|
324
|
+
if (start < 0) return empty;
|
|
325
|
+
for (let end = afterResult.length; end > start; end--) {
|
|
326
|
+
try {
|
|
327
|
+
const parsed = JSON.parse(afterResult.slice(start, end));
|
|
328
|
+
const ann = parsed.refAnnotations;
|
|
329
|
+
if (ann && typeof ann === "object") {
|
|
330
|
+
const m = /* @__PURE__ */ new Map();
|
|
331
|
+
for (const [ref, names] of Object.entries(ann)) {
|
|
332
|
+
if (Array.isArray(names) && names.length > 0) m.set(ref, names);
|
|
333
|
+
}
|
|
334
|
+
return m;
|
|
335
|
+
}
|
|
336
|
+
} catch {
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return empty;
|
|
340
|
+
}
|
|
341
|
+
|
|
277
342
|
// src/commands/snapshot.ts
|
|
278
343
|
async function runSnapshotCommand(opts) {
|
|
279
344
|
const sightmap = await loadDirectory4(opts.sightmapDir);
|
|
@@ -293,18 +358,43 @@ async function runSnapshotCommand(opts) {
|
|
|
293
358
|
selector: c.selector
|
|
294
359
|
}));
|
|
295
360
|
const inPage = await runInPageMatcher(allComponents, opts.sessionName);
|
|
361
|
+
const refs = parsed.nodes.map((n) => n.ref).filter((r) => r !== void 0);
|
|
362
|
+
const refAnnotations = await resolveRefAnnotations(
|
|
363
|
+
allComponents,
|
|
364
|
+
refs,
|
|
365
|
+
opts.sessionName
|
|
366
|
+
);
|
|
296
367
|
const enriched = enrichSnapshot({
|
|
297
368
|
sightmap,
|
|
298
369
|
currentUrl: parsed.url,
|
|
299
|
-
inPageMatches: inPage
|
|
370
|
+
inPageMatches: inPage,
|
|
371
|
+
ariaText: parsed.ariaText,
|
|
372
|
+
refAnnotations
|
|
300
373
|
});
|
|
374
|
+
const ariaForHuman = enriched.enrichedAriaText ?? parsed.ariaText;
|
|
301
375
|
if (opts.json) {
|
|
302
376
|
process.stdout.write(
|
|
303
377
|
JSON.stringify({ ...enriched, ariaSnapshot: parsed.ariaText }, null, 2) + "\n"
|
|
304
378
|
);
|
|
305
379
|
} else {
|
|
306
|
-
process.stdout.write(formatHuman(enriched,
|
|
380
|
+
process.stdout.write(formatHuman(enriched, ariaForHuman));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
async function resolveRefAnnotations(components, refs, sessionName) {
|
|
384
|
+
if (components.length === 0 || refs.length === 0) {
|
|
385
|
+
return /* @__PURE__ */ new Map();
|
|
386
|
+
}
|
|
387
|
+
const snippet = buildRefJoinSnippet(components, refs);
|
|
388
|
+
const result = await execPlaywrightCli([
|
|
389
|
+
"-s",
|
|
390
|
+
sessionName,
|
|
391
|
+
"run-code",
|
|
392
|
+
snippet
|
|
393
|
+
]);
|
|
394
|
+
if (result.exitCode !== 0) {
|
|
395
|
+
return /* @__PURE__ */ new Map();
|
|
307
396
|
}
|
|
397
|
+
return parseRefJoinOutput(result.stdout);
|
|
308
398
|
}
|
|
309
399
|
function formatHuman(enriched, ariaText) {
|
|
310
400
|
const lines = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sightmap/playwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Sightmap-aware wrapper around @playwright/cli. Adds sightmap_* commands and enriches snapshot output with component names from .sightmap/.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"vitest": "^3.0.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@sightmap/sightmap": "^0.
|
|
39
|
+
"@sightmap/sightmap": "^0.10.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@playwright/cli": "^0.1.0"
|