UNPKG

webdriverio

Version:

Next-gen browser and mobile automation test framework for Node.js

83 lines (82 loc) • 2.98 kB
// src/scripts/customElement.ts function customElementWrapper() { const reportedHosts = /* @__PURE__ */ new WeakSet(); const sendShadowRoot = (host, hasShadowRoot) => { let parentNode = host; while (parentNode.parentNode) { parentNode = parentNode.parentNode; } if (hasShadowRoot && host.isConnected) { reportedHosts.add(host); } console.debug("[WDIO]", "newShadowRoot", host, parentNode, parentNode === document, document.documentElement); }; const reportShadowRoot = (host, hasShadowRoot) => { if (host.isConnected) { const containingHosts = []; for (let root = host.getRootNode(); root instanceof ShadowRoot; root = root.host.getRootNode()) { containingHosts.unshift(root.host); } for (const containingHost of containingHosts) { if (containingHost.shadowRoot && !reportedHosts.has(containingHost)) { sendShadowRoot(containingHost, true); } } } sendShadowRoot(host, hasShadowRoot); }; const reportShadowRootsIn = (root) => { const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT); for (let node = walker.nextNode(); node; node = walker.nextNode()) { const shadowRoot = node.shadowRoot; if (!shadowRoot) { continue; } if (!reportedHosts.has(node)) { reportShadowRoot(node, true); } reportShadowRootsIn(shadowRoot); } }; const origFn = customElements.define.bind(customElements); customElements.define = function(name, Constructor, options) { const origConnectedCallback = Constructor.prototype.connectedCallback; Constructor.prototype.connectedCallback = function() { reportShadowRoot(this, Boolean(this.shadowRoot)); return origConnectedCallback?.call(this); }; const origDisconnectedCallback = Constructor.prototype.disconnectedCallback; Constructor.prototype.disconnectedCallback = function() { console.debug("[WDIO]", "removeShadowRoot", this); return origDisconnectedCallback?.call(this); }; return origFn(name, Constructor, options); }; const origAttachShadow = Element.prototype.attachShadow; Element.prototype.attachShadow = function(init) { const shadowRoot = origAttachShadow.call(this, init); reportShadowRoot(this, true); return shadowRoot; }; for (const proto of [Element.prototype, ShadowRoot.prototype]) { const origSetHTMLUnsafe = proto.setHTMLUnsafe; if (typeof origSetHTMLUnsafe !== "function") { continue; } proto.setHTMLUnsafe = function(...args) { const result = origSetHTMLUnsafe.apply(this, args); if (this.isConnected) { reportShadowRootsIn(this); } return result; }; } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => reportShadowRootsIn(document), { once: true }); } else { reportShadowRootsIn(document); } } export { customElementWrapper as default };