Skip to content

Commit

Permalink
feat(selectors): auto-detect xpath starting with ".." (#3239)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgozman committed Jul 30, 2020
1 parent 235c5df commit 2f95b6e
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 4 deletions.
5 changes: 4 additions & 1 deletion docs/api.md
Expand Up @@ -4452,7 +4452,7 @@ Selector describes an element in the page. It can be used to obtain `ElementHand
Selector has the following format: `engine=body [>> engine=body]*`. Here `engine` is one of the supported [selector engines](selectors.md) (e.g. `css` or `xpath`), and `body` is a selector body in the format of the particular engine. When multiple `engine=body` clauses are present (separated by `>>`), next one is queried relative to the previous one's result.

For convenience, selectors in the wrong format are heuristically converted to the right format:
- selector starting with `//` is assumed to be `xpath=selector`;
- selector starting with `//` or `..` is assumed to be `xpath=selector`;
- selector starting and ending with a quote (either `"` or `'`) is assumed to be `text=selector`;
- otherwise selector is assumed to be `css=selector`.

Expand All @@ -4478,6 +4478,9 @@ const handle = await page.$('//html/body/div');
// converted to 'text="foo"'
const handle = await page.$('"foo"');

// queries '../span' xpath selector starting with the result of 'div' css selector
const handle = await page.$('div >> ../span');

// queries 'span' css selector inside the div handle
const handle = await divHandle.$('css=span');
```
Expand Down
4 changes: 2 additions & 2 deletions docs/selectors.md
Expand Up @@ -31,7 +31,7 @@ document
Selector engine name can be prefixed with `*` to capture element that matches the particular clause instead of the last one. For example, `css=article >> text=Hello` captures the element with the text `Hello`, and `*css=article >> text=Hello` (note the `*`) captures the `article` element that contains some element with the text `Hello`.

For convenience, selectors in the wrong format are heuristically converted to the right format:
- Selector starting with `//` is assumed to be `xpath=selector`. Example: `page.click('//html')` is converted to `page.click('xpath=//html')`.
- Selector starting with `//` or `..` is assumed to be `xpath=selector`. Example: `page.click('//html')` is converted to `page.click('xpath=//html')`.
- Selector starting and ending with a quote (either `"` or `'`) is assumed to be `text=selector`. Example: `page.click('"foo"')` is converted to `page.click('text="foo"')`.
- Otherwise, selector is assumed to be `css=selector`. Example: `page.click('div')` is converted to `page.click('css=div')`.

Expand Down Expand Up @@ -108,7 +108,7 @@ Note that `<open mode shadow root>` is not an html element, but rather a shadow

XPath engine is equivalent to [`Document.evaluate`](https://developer.mozilla.org/en/docs/Web/API/Document/evaluate). Example: `xpath=//html/body`.

Malformed selector starting with `//` is assumed to be an xpath selector. For example, Playwright converts `page.$('//html/body')` to `page.$('xpath=//html/body')`.
Malformed selector starting with `//` or `..` is assumed to be an xpath selector. For example, Playwright converts `page.$('//html/body')` to `page.$('xpath=//html/body')`.

Note that `xpath` does not pierce shadow roots.

Expand Down
3 changes: 2 additions & 1 deletion src/common/selectorParser.ts
Expand Up @@ -43,9 +43,10 @@ export function parseSelector(selector: string): ParsedSelector {
} else if (part.length > 1 && part[0] === "'" && part[part.length - 1] === "'") {
name = 'text';
body = part;
} else if (/^\(*\/\//.test(part)) {
} else if (/^\(*\/\//.test(part) || part.startsWith('..')) {
// If selector starts with '//' or '//' prefixed with multiple opening
// parenthesis, consider xpath. @see https://github.com/microsoft/playwright/issues/817
// If selector starts with '..', consider xpath as well.
name = 'xpath';
body = part;
} else {
Expand Down
7 changes: 7 additions & 0 deletions test/queryselector.jest.js
Expand Up @@ -263,6 +263,13 @@ describe('Page.$', function() {
const element = await page.$('(//section)[1]');
expect(element).toBeTruthy();
});
it('should auto-detect xpath selector starting with ..', async({page, server}) => {
await page.setContent('<div><section>test</section><span></span></div>');
const span = await page.$('"test" >> ../span');
expect(await span.evaluate(e => e.nodeName)).toBe('SPAN');
const div = await page.$('"test" >> ..');
expect(await div.evaluate(e => e.nodeName)).toBe('DIV');
});
it('should auto-detect text selector', async({page, server}) => {
await page.setContent('<section>test</section>');
const element = await page.$('"test"');
Expand Down

0 comments on commit 2f95b6e

Please sign in to comment.