@ulpi/browse 0.7.3 → 0.7.4
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/package.json +1 -1
- package/src/snapshot.ts +65 -12
package/package.json
CHANGED
package/src/snapshot.ts
CHANGED
|
@@ -446,27 +446,80 @@ export async function handleSnapshot(
|
|
|
446
446
|
output.push(outputLine);
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
-
// Viewport filter: remove elements
|
|
449
|
+
// Viewport filter: remove elements outside the visible viewport
|
|
450
|
+
// Uses a single page.evaluate() for speed — checking 189 locators individually is slow
|
|
450
451
|
if (opts.viewport) {
|
|
451
452
|
const vp = page.viewportSize();
|
|
452
453
|
if (vp) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
454
|
+
// Build a list of {ref, role, name} to check in the DOM
|
|
455
|
+
const checks = Array.from(refMap.keys()).map(ref => {
|
|
456
|
+
const line = output.find(l => l.includes(`@${ref} `));
|
|
457
|
+
const roleMatch = line?.match(/\[(\w+)\]/);
|
|
458
|
+
const nameMatch = line?.match(/"([^"]*)"/);
|
|
459
|
+
return { ref, role: roleMatch?.[1] || '', name: nameMatch?.[1] || '' };
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
const visibleRefs = await evalCtx.evaluate(
|
|
463
|
+
({ checks, vpHeight }) => {
|
|
464
|
+
const ROLE_TO_SELECTOR: Record<string, string> = {
|
|
465
|
+
link: 'a,[role="link"]',
|
|
466
|
+
button: 'button,[role="button"],input[type="button"],input[type="submit"]',
|
|
467
|
+
textbox: 'input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="hidden"]),textarea,[role="textbox"]',
|
|
468
|
+
checkbox: 'input[type="checkbox"],[role="checkbox"]',
|
|
469
|
+
radio: 'input[type="radio"],[role="radio"]',
|
|
470
|
+
combobox: 'select,[role="combobox"]',
|
|
471
|
+
searchbox: 'input[type="search"],[role="searchbox"]',
|
|
472
|
+
tab: '[role="tab"]',
|
|
473
|
+
switch: '[role="switch"]',
|
|
474
|
+
slider: 'input[type="range"],[role="slider"]',
|
|
475
|
+
menuitem: '[role="menuitem"]',
|
|
476
|
+
option: 'option,[role="option"]',
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const visible = new Set<string>();
|
|
480
|
+
// Track which elements we've already matched per role+name
|
|
481
|
+
const roleCounts = new Map<string, number>();
|
|
482
|
+
|
|
483
|
+
for (const { ref, role, name } of checks) {
|
|
484
|
+
const selector = ROLE_TO_SELECTOR[role] || `[role="${role}"]`;
|
|
485
|
+
const all = document.querySelectorAll(selector);
|
|
486
|
+
const key = `${role}:${name}`;
|
|
487
|
+
const skip = roleCounts.get(key) || 0;
|
|
488
|
+
|
|
489
|
+
let matched = 0;
|
|
490
|
+
for (let i = 0; i < all.length; i++) {
|
|
491
|
+
const el = all[i] as HTMLElement;
|
|
492
|
+
// Match by accessible name (textContent or aria-label)
|
|
493
|
+
const accName = (el.getAttribute('aria-label') || el.textContent || '').trim();
|
|
494
|
+
// For terse mode, name may be truncated — check startsWith
|
|
495
|
+
const nameMatches = !name || accName === name ||
|
|
496
|
+
(name.endsWith('...') && accName.startsWith(name.slice(0, -3)));
|
|
497
|
+
if (!nameMatches) continue;
|
|
498
|
+
|
|
499
|
+
if (matched < skip) { matched++; continue; }
|
|
500
|
+
|
|
501
|
+
const rect = el.getBoundingClientRect();
|
|
502
|
+
if (rect.y + rect.height > 0 && rect.y < vpHeight) {
|
|
503
|
+
visible.add(ref);
|
|
504
|
+
}
|
|
505
|
+
matched++;
|
|
506
|
+
break;
|
|
460
507
|
}
|
|
461
|
-
|
|
462
|
-
toRemove.add(ref);
|
|
508
|
+
roleCounts.set(key, skip + 1);
|
|
463
509
|
}
|
|
464
|
-
|
|
510
|
+
return [...visible];
|
|
511
|
+
},
|
|
512
|
+
{ checks, vpHeight: vp.height }
|
|
465
513
|
);
|
|
514
|
+
|
|
515
|
+
const visibleSet = new Set(visibleRefs);
|
|
516
|
+
const toRemove = new Set<string>();
|
|
517
|
+
for (const ref of refMap.keys()) {
|
|
518
|
+
if (!visibleSet.has(ref)) toRemove.add(ref);
|
|
519
|
+
}
|
|
466
520
|
for (const ref of toRemove) {
|
|
467
521
|
refMap.delete(ref);
|
|
468
522
|
}
|
|
469
|
-
// Remove output lines for filtered refs
|
|
470
523
|
for (let i = output.length - 1; i >= 0; i--) {
|
|
471
524
|
const match = output[i].match(/@(e\d+)/);
|
|
472
525
|
if (match && toRemove.has(match[1])) {
|