diff --git a/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts b/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts index 7964ef4d46..74819bcda4 100644 --- a/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts +++ b/packages/playwright-core/src/server/injected/xpathSelectorEngine.ts @@ -18,7 +18,7 @@ import type { SelectorEngine, SelectorRoot } from './selectorEngine'; export const XPathEngine: SelectorEngine = { queryAll(root: SelectorRoot, selector: string): Element[] { - if (selector.startsWith('/')) + if (selector.startsWith('/') && root.nodeType !== Node.DOCUMENT_NODE) selector = '.' + selector; const result: Element[] = []; const document = root.ownerDocument || root; diff --git a/tests/library/locator-generator.spec.ts b/tests/library/locator-generator.spec.ts index de551db28f..e5b6392483 100644 --- a/tests/library/locator-generator.spec.ts +++ b/tests/library/locator-generator.spec.ts @@ -494,6 +494,14 @@ it('asLocator internal:chain', async () => { expect.soft(asLocator('csharp', 'div >> internal:chain="span >> article"', false)).toBe(`Locator("div").Locator(Locator("span").Locator("article"))`); }); +it('asLocator xpath', async () => { + const selector = `//*[contains(normalizer-text(), 'foo']`; + expect.soft(asLocator('javascript', selector, false)).toBe(`locator('xpath=//*[contains(normalizer-text(), \\'foo\\']')`); + expect.soft(asLocator('python', selector, false)).toBe(`locator(\"xpath=//*[contains(normalizer-text(), 'foo']\")`); + expect.soft(asLocator('java', selector, false)).toBe(`locator(\"xpath=//*[contains(normalizer-text(), 'foo']\")`); + expect.soft(asLocator('csharp', selector, false)).toBe(`Locator(\"xpath=//*[contains(normalizer-text(), 'foo']\")`); +}); + it('parse locators strictly', () => { const selector = 'div >> internal:has-text=\"Goodbye world\"i >> span'; diff --git a/tests/page/selectors-misc.spec.ts b/tests/page/selectors-misc.spec.ts index 66252fa289..384bf4aef1 100644 --- a/tests/page/selectors-misc.spec.ts +++ b/tests/page/selectors-misc.spec.ts @@ -318,6 +318,12 @@ it('should work with pipe in xpath', async ({ page, server }) => { await page.click(`//code|//span[@id="t2"]`); }); +it('should print original xpath in error', async ({ page, browserName }) => { + const error = await page.locator(`//*[contains(@Class, 'foo']`).isVisible().catch(e => e); + expect(error.message).toContain('//*[contains(@Class, \\\'foo\\\']'); + expect(error.message).not.toContain('.//*[contains(@Class, \'foo\']'); +}); + it('data-testid on the handle should be relative', async ({ page }) => { await page.setContent(` 1