From 0b38c3faff887507a71ba6249ae16c2393a136a9 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Fri, 8 Nov 2024 10:02:12 +0100 Subject: [PATCH] test: Add support for querying shadow DOM Add a `querySelectorAll()` variant which can transparently pierce and traverse shadow DOMs without having to know their structure (i.e. doing selectors piece-wise and iterating on their `.shadowRoot` attribute). As this is more expensive than standard querySelector()`, only use it if the page actually contains any shadow root. This makes it a lot more convenient to write tests for pages which use web components, like https://patternflyelements.org. --- test/common/test-functions.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/common/test-functions.js b/test/common/test-functions.js index 3f944a7796c9..22430be62be8 100644 --- a/test/common/test-functions.js +++ b/test/common/test-functions.js @@ -4,12 +4,38 @@ * These are routines used by our testing code. */ +/* Detect if we have any shadow DOM */ +window.__haveShadowDom = function() { + if (window.__haveShadowDomResult === undefined) + window.__haveShadowDomResult = Array.from(document.querySelectorAll('*')).filter(el => el.shadowRoot).length > 0; + + return window.__haveShadowDomResult; +}; + +// Like querySelectorAll(), but traverses shadow DOM +window.querySelectorAllDeep = function(query, element) { + const result = Array.from( + element.shadowRoot + ? element.shadowRoot.childNodes + : element.nodeName === 'SLOT' ? element.assignedElements() : element.childNodes, + ) + .filter(element => element instanceof Element) + .map(element => window.querySelectorAllDeep(query, element)) + .flat(); + + if (element.matches?.(query)) + result.push(element); + return result; +}; + window.ph_select = function(sel) { if (sel.includes(":contains(")) { if (!window.Sizzle) { throw new Error("Using ':contains' when window.Sizzle is not available."); } return window.Sizzle(sel); + } else if (window.__haveShadowDom()) { + return window.querySelectorAllDeep(sel, document); } else { return Array.from(document.querySelectorAll(sel)); }