diff --git a/src/firefox/snapshot/injected/elementCollector.ts b/src/firefox/snapshot/injected/elementCollector.ts
index cfcc1460..fb766035 100644
--- a/src/firefox/snapshot/injected/elementCollector.ts
+++ b/src/firefox/snapshot/injected/elementCollector.ts
@@ -59,7 +59,7 @@ export function isVisible(el: Element): boolean {
} catch {
return false;
}
- current = current.parentElement;
+ current = current.parentElement ?? (current.getRootNode() as ShadowRoot).host ?? null;
}
return true;
diff --git a/src/firefox/snapshot/injected/treeWalker.ts b/src/firefox/snapshot/injected/treeWalker.ts
index 12774e8b..0e31897f 100644
--- a/src/firefox/snapshot/injected/treeWalker.ts
+++ b/src/firefox/snapshot/injected/treeWalker.ts
@@ -115,13 +115,14 @@ export function walkTree(
}
} else {
// Walk regular children
- for (let i = 0; i < el.children.length; i++) {
+ const children = el.shadowRoot ? [...el.shadowRoot.children, ...el.children] : el.children;
+ for (let i = 0; i < children.length; i++) {
if (nodeCount >= MAX_NODES) {
truncated = true;
break;
}
- const child = el.children[i];
+ const child = children[i];
if (!child) {
continue;
}
diff --git a/tests/integration/firefox/snapshot.test.ts b/tests/integration/firefox/snapshot.test.ts
index f2441e7d..45e1df78 100644
--- a/tests/integration/firefox/snapshot.test.ts
+++ b/tests/integration/firefox/snapshot.test.ts
@@ -233,4 +233,46 @@ describe('Snapshot Integration Tests', () => {
expect(hasInvisibleButton).toBe(false);
}, 10000);
+
+ describe('shadow DOM', () => {
+ const SHADOW_PAGE =
+ 'data:text/html,' +
+ '';
+
+ function shadowNode(snapshot: { json: { root: SnapshotNode } }, id: string) {
+ return findNodeInSnapshot(snapshot.json.root, (node) => node.id === id);
+ }
+
+ it('should include the elements of an open shadow root', async () => {
+ await firefox.navigate(SHADOW_PAGE);
+
+ const snapshot = await firefox.takeSnapshot();
+
+ expect(shadowNode(snapshot, 'deepBtn')?.name).toBe('Deep');
+ }, 10000);
+
+ it('should let shadow DOM elements be filled and clicked by UID', async () => {
+ await firefox.navigate(SHADOW_PAGE);
+ const snapshot = await firefox.takeSnapshot();
+
+ await firefox.fillByUid(shadowNode(snapshot, 'deep')!.uid, 'abc');
+ await firefox.clickByUid(shadowNode(snapshot, 'deepBtn')!.uid);
+
+ expect(await firefox.evaluate('document.getElementById("out").textContent')).toBe('open:abc');
+ }, 10000);
+
+ it('should leave out the shadow root of a hidden host', async () => {
+ await firefox.navigate(SHADOW_PAGE);
+
+ const snapshot = await firefox.takeSnapshot({ includeAll: true });
+
+ expect(findNodesInSnapshot(snapshot.json.root, (node) => node.id === 'deepBtn')).toHaveLength(
+ 1
+ );
+ }, 10000);
+ });
});