UNPKG

playwright-dompath

Version:

Retrieve xpath and css selector paths from ElementHandle and Locator objects in Playwright

269 lines 9.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.xPath = exports.cssPath = void 0; require("css.escape"); const cssPath = async function cssPath(elHandle, optimized) { return (await elHandle.evaluate(async (node, optimized) => { function nodeNameInCorrectCase(node) { const shadowRoot = node.shadowRoot; const shadowRootType = shadowRoot && shadowRoot.mode; if (shadowRootType) { return `#shadow-root (${shadowRootType})`; } if (!node.localName) { return node.nodeName; } if (node.localName.length !== node.nodeName.length) { return node.nodeName; } return node.localName; } class Step { value; optimized; constructor(value, optimized) { this.value = value; this.optimized = optimized || false; } toString() { return this.value; } } const cssPathStep = function cssPathStep(node, optimized, isTargetNode) { function idSelector(id) { return `#${CSS.escape(id)}`; } if (node.nodeType !== Node.ELEMENT_NODE) { return null; } const id = node.id; if (optimized) { if (id) { return new Step(idSelector(id), true); } const nodeNameLower = node.nodeName.toLowerCase(); if (nodeNameLower === "body" || nodeNameLower === "head" || nodeNameLower === "html") { return new Step(nodeNameInCorrectCase(node), true); } } const nodeName = nodeNameInCorrectCase(node); if (id) { return new Step(nodeName + idSelector(id), true); } const parent = node.parentNode; if (!parent || parent.nodeType === Node.DOCUMENT_NODE) { return new Step(nodeName, true); } function prefixedElementClassNames(node) { const classAttribute = node.className; if (!classAttribute) { return []; } return classAttribute .split(/\s+/g) .filter(Boolean) .map((name) => `$${name}`); } const prefixedOwnClassNamesArray = prefixedElementClassNames(node); let needsClassNames = false; let needsNthChild = false; let ownIndex = -1; let elementIndex = -1; const siblings = parent.childNodes; for (let i = 0; siblings && (ownIndex === -1 || !needsNthChild) && i < siblings.length; ++i) { const sibling = siblings[i]; if (sibling.nodeType !== Node.ELEMENT_NODE) { continue; } elementIndex += 1; if (sibling === node) { ownIndex = elementIndex; continue; } if (needsNthChild) { continue; } if (nodeNameInCorrectCase(sibling) !== nodeName) { continue; } needsClassNames = true; const ownClassNames = new Set(prefixedOwnClassNamesArray); if (!ownClassNames.size) { needsNthChild = true; continue; } const siblingClassNamesArray = prefixedElementClassNames(sibling); for (let j = 0; j < siblingClassNamesArray.length; ++j) { const siblingClass = siblingClassNamesArray[j]; if (!ownClassNames.has(siblingClass)) { continue; } ownClassNames.delete(siblingClass); if (!ownClassNames.size) { needsNthChild = true; break; } } } let result = nodeName; if (isTargetNode && nodeName.toLowerCase() === "input" && node.getAttribute("type") && !node.id && !node.className) { result += `[type=${CSS.escape(node.getAttribute("type") || "")}]`; } if (needsNthChild) { result += `:nth-child(${ownIndex + 1})`; } else if (needsClassNames) { for (const prefixedName of prefixedOwnClassNamesArray) { result += `.${CSS.escape(prefixedName.slice(1))}`; } } return new Step(result, false); }; if (node.nodeType !== Node.ELEMENT_NODE) { return ""; } const steps = []; let contextNode = node; while (contextNode) { const step = cssPathStep(contextNode, Boolean(optimized), contextNode === node); if (!step) { break; } steps.push(step); if (step.optimized) { break; } contextNode = contextNode.parentNode; } steps.reverse(); return steps.join(" > "); }, optimized)); }; exports.cssPath = cssPath; const xPath = async function xPath(elHandle, optimized) { return (await elHandle.evaluate(async (node, optimized) => { class Step { value; optimized; constructor(value, optimized) { this.value = value; this.optimized = optimized || false; } toString() { return this.value; } } const xPathValue = function xPathValue(node, optimized) { let ownValue; const ownIndex = xPathIndex(node); if (ownIndex === -1) { return null; } switch (node.nodeType) { case Node.ELEMENT_NODE: if (optimized && node.id) { return new Step(`//*[@id="${node.id}"]`, true); } ownValue = node.localName; break; case Node.ATTRIBUTE_NODE: ownValue = `@${node.nodeName}`; break; case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: ownValue = "text()"; break; case Node.PROCESSING_INSTRUCTION_NODE: ownValue = "processing-instruction()"; break; case Node.COMMENT_NODE: ownValue = "comment()"; break; case Node.DOCUMENT_NODE: ownValue = ""; break; default: ownValue = ""; break; } if (ownIndex > 0) { ownValue += `[${ownIndex}]`; } return new Step(ownValue, node.nodeType === Node.DOCUMENT_NODE); }; const xPathIndex = function xPathIndex(node) { function areNodesSimilar(left, right) { if (left === right) { return true; } if (left.nodeType === Node.ELEMENT_NODE && right.nodeType === Node.ELEMENT_NODE) { return left.localName === right.localName; } if (left.nodeType === right.nodeType) { return true; } const leftType = left.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : left.nodeType; const rightType = right.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : right.nodeType; return leftType === rightType; } const parentNode = node.parentNode; const siblings = parentNode ? parentNode.children : null; if (!siblings) { return 0; } let hasSameNamedElements; for (let i = 0; i < siblings.length; ++i) { if (areNodesSimilar(node, siblings[i]) && !(siblings[i] === node)) { hasSameNamedElements = true; break; } } if (!hasSameNamedElements) { return 0; } let ownIndex = 1; for (let i = 0; i < siblings.length; ++i) { if (areNodesSimilar(node, siblings[i])) { if (siblings[i] === node) { return ownIndex; } ++ownIndex; } } return -1; }; if (node.nodeType === Node.DOCUMENT_NODE) { return "/"; } const steps = []; let contextNode = node; while (contextNode) { const step = xPathValue(contextNode, optimized); if (!step) { break; } steps.push(step); if (step.optimized) { break; } contextNode = contextNode.parentNode; } steps.reverse(); return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/"); }, optimized)); }; exports.xPath = xPath; //# sourceMappingURL=DOMPath.js.map