Skip to content

getElementXPath does not pass parameter optimised recursively #6323

@millcek

Description

@millcek

What happened?

getElementXPath returns incorrect values.

Steps to Reproduce

Run this code in about:blank. Functions are copied from @opentelemetry/sdk-trace-web@1.30.1.

document.documentElement.innerHTML = `<body id="body-id"><div></div></body>`
console.log('Body XPath:', getElementXPath(document.body, true))
console.log('Div XPath:', getElementXPath(document.querySelector('div'), true))

function getElementXPath(target, optimised) {
    if (target.nodeType === Node.DOCUMENT_NODE) {
        return '/';
    }
    const targetValue = getNodeValue(target, optimised);
    if (optimised && targetValue.indexOf('@id') > 0) {
        return targetValue;
    }
    let xpath = '';
    if (target.parentNode) {
        xpath += getElementXPath(target.parentNode, false);
    }
    xpath += targetValue;
    return xpath;
}

function getNodeValue(target, optimised) {
    const nodeType = target.nodeType;
    const index = getNodeIndex(target);
    let nodeValue = '';
    if (nodeType === Node.ELEMENT_NODE) {
        const id = target.getAttribute('id');
        if (optimised && id) {
            return `//*[@id="${id}"]`;
        }
        nodeValue = target.localName;
    }
    else if (nodeType === Node.TEXT_NODE ||
        nodeType === Node.CDATA_SECTION_NODE) {
        nodeValue = 'text()';
    }
    else if (nodeType === Node.COMMENT_NODE) {
        nodeValue = 'comment()';
    }
    else {
        return '';
    }
    // if index is 1 it can be omitted in xpath
    if (nodeValue && index > 1) {
        return `/${nodeValue}[${index}]`;
    }
    return `/${nodeValue}`;
}

function getNodeIndex(target) {
    if (!target.parentNode) {
        return 0;
    }
    const allowedTypes = [target.nodeType];
    if (target.nodeType === Node.CDATA_SECTION_NODE) {
        allowedTypes.push(Node.TEXT_NODE);
    }
    let elements = Array.from(target.parentNode.childNodes);
    elements = elements.filter((element) => {
        const localName = element.localName;
        return (allowedTypes.indexOf(element.nodeType) >= 0 &&
            localName === target.localName);
    });
    if (elements.length >= 1) {
        return elements.indexOf(target) + 1; // xpath starts from 1
    }
    // if there are no other similar child xpath doesn't need index
    return 0;
}

Expected Result

Body XPath: //*[@id="body-id"]
Div XPath: //*[@id="body-id"]/div

Actual Result

Body XPath: //*[@id="body-id"]
Div XPath: //html/body/div

Additional Details

getElementXPath does not pass parameter optimised recursively.
The line
xpath += getElementXPath(target.parentNode, false);
should be
xpath += getElementXPath(target.parentNode, optimised);

OpenTelemetry Setup Code

package.json

Relevant log output

Operating System and Version

No response

Runtime and Version

No response

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomerspriority:p2Bugs and spec inconsistencies which cause telemetry to be incomplete or incorrecttriageup-for-grabsGood for taking. Extra help will be provided by maintainers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions