feat(selectors): auto-detect xpath starting with ".." (#3239)
This commit is contained in:
parent
235c5df8de
commit
2f95b6e34e
|
|
@ -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.
|
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:
|
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`;
|
- selector starting and ending with a quote (either `"` or `'`) is assumed to be `text=selector`;
|
||||||
- otherwise selector is assumed to be `css=selector`.
|
- otherwise selector is assumed to be `css=selector`.
|
||||||
|
|
||||||
|
|
@ -4478,6 +4478,9 @@ const handle = await page.$('//html/body/div');
|
||||||
// converted to 'text="foo"'
|
// converted to 'text="foo"'
|
||||||
const handle = await page.$('"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
|
// queries 'span' css selector inside the div handle
|
||||||
const handle = await divHandle.$('css=span');
|
const handle = await divHandle.$('css=span');
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
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:
|
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"')`.
|
- 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')`.
|
- Otherwise, selector is assumed to be `css=selector`. Example: `page.click('div')` is converted to `page.click('css=div')`.
|
||||||
|
|
||||||
|
|
@ -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`.
|
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.
|
Note that `xpath` does not pierce shadow roots.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,10 @@ export function parseSelector(selector: string): ParsedSelector {
|
||||||
} else if (part.length > 1 && part[0] === "'" && part[part.length - 1] === "'") {
|
} else if (part.length > 1 && part[0] === "'" && part[part.length - 1] === "'") {
|
||||||
name = 'text';
|
name = 'text';
|
||||||
body = part;
|
body = part;
|
||||||
} else if (/^\(*\/\//.test(part)) {
|
} else if (/^\(*\/\//.test(part) || part.startsWith('..')) {
|
||||||
// If selector starts with '//' or '//' prefixed with multiple opening
|
// If selector starts with '//' or '//' prefixed with multiple opening
|
||||||
// parenthesis, consider xpath. @see https://github.com/microsoft/playwright/issues/817
|
// parenthesis, consider xpath. @see https://github.com/microsoft/playwright/issues/817
|
||||||
|
// If selector starts with '..', consider xpath as well.
|
||||||
name = 'xpath';
|
name = 'xpath';
|
||||||
body = part;
|
body = part;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,13 @@ describe('Page.$', function() {
|
||||||
const element = await page.$('(//section)[1]');
|
const element = await page.$('(//section)[1]');
|
||||||
expect(element).toBeTruthy();
|
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}) => {
|
it('should auto-detect text selector', async({page, server}) => {
|
||||||
await page.setContent('<section>test</section>');
|
await page.setContent('<section>test</section>');
|
||||||
const element = await page.$('"test"');
|
const element = await page.$('"test"');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue