fix(selectors): fix selector parsing for css attributes and quotes (#2389)
- css attribute selector may contain spaces; - >> escaping inside strings was sometimes incorrect. See added test cases for more details.
This commit is contained in:
parent
7981e4e3da
commit
fdbd4fe197
|
|
@ -219,6 +219,7 @@ function parentElementOrShadowHost(element: Element): Element | undefined {
|
|||
function split(selector: string): string[][] {
|
||||
let index = 0;
|
||||
let quote: string | undefined;
|
||||
let insideAttr = false;
|
||||
let start = 0;
|
||||
let space: 'none' | 'before' | 'after' = 'none';
|
||||
const result: string[][] = [];
|
||||
|
|
@ -235,7 +236,7 @@ function split(selector: string): string[][] {
|
|||
};
|
||||
while (index < selector.length) {
|
||||
const c = selector[index];
|
||||
if (!quote && c === ' ') {
|
||||
if (!quote && !insideAttr && c === ' ') {
|
||||
if (space === 'none' || space === 'before')
|
||||
space = 'before';
|
||||
index++;
|
||||
|
|
@ -256,10 +257,16 @@ function split(selector: string): string[][] {
|
|||
} else if (c === quote) {
|
||||
quote = undefined;
|
||||
index++;
|
||||
} else if (c === '\'' || c === '"') {
|
||||
} else if (!quote && (c === '\'' || c === '"')) {
|
||||
quote = c;
|
||||
index++;
|
||||
} else if (!quote && c === ',') {
|
||||
} else if (!quote && c === '[') {
|
||||
insideAttr = true;
|
||||
index++;
|
||||
} else if (!quote && insideAttr && c === ']') {
|
||||
insideAttr = false;
|
||||
index++;
|
||||
} else if (!quote && !insideAttr && c === ',') {
|
||||
appendToResult();
|
||||
index++;
|
||||
start = index;
|
||||
|
|
|
|||
|
|
@ -219,6 +219,9 @@ export function parseSelector(selector: string): types.ParsedSelector {
|
|||
} else if (c === quote) {
|
||||
quote = undefined;
|
||||
index++;
|
||||
} else if (!quote && (c === '"' || c === '\'' || c === '`')) {
|
||||
quote = c;
|
||||
index++;
|
||||
} else if (!quote && c === '>' && selector[index + 1] === '>') {
|
||||
append();
|
||||
index += 2;
|
||||
|
|
|
|||
|
|
@ -494,6 +494,14 @@ describe('text selector', () => {
|
|||
expect(await page.$eval(`text="`, e => e.outerHTML)).toBe('<div> " </div>');
|
||||
expect(await page.$eval(`text='`, e => e.outerHTML)).toBe('<div> \' </div>');
|
||||
|
||||
await page.setContent(`<div>Hi''>>foo=bar</div>`);
|
||||
expect(await page.$eval(`text="Hi''>>foo=bar"`, e => e.outerHTML)).toBe(`<div>Hi''>>foo=bar</div>`);
|
||||
await page.setContent(`<div>Hi'">>foo=bar</div>`);
|
||||
expect(await page.$eval(`text="Hi'\\">>foo=bar"`, e => e.outerHTML)).toBe(`<div>Hi'">>foo=bar</div>`);
|
||||
|
||||
await page.setContent(`<div>Hi>><span></span></div>`);
|
||||
expect(await page.$eval(`text="Hi>>">>span`, e => e.outerHTML)).toBe(`<span></span>`);
|
||||
|
||||
await page.setContent(`<div>a<br>b</div><div>a</div>`);
|
||||
expect(await page.$eval(`text=a`, e => e.outerHTML)).toBe('<div>a<br>b</div>');
|
||||
expect(await page.$eval(`text=b`, e => e.outerHTML)).toBe('<div>a<br>b</div>');
|
||||
|
|
@ -622,6 +630,30 @@ describe('css selector', () => {
|
|||
expect(await page.$eval(`css=[attr='hello,world!']`, e => e.outerHTML)).toBe('<div attr="hello,world!"></div>');
|
||||
expect(await page.$eval(`css=div[attr="hello,world!"],span`, e => e.outerHTML)).toBe('<span></span>');
|
||||
});
|
||||
|
||||
it('should work with attribute selectors', async({page}) => {
|
||||
await page.setContent(`<div attr="hello world" attr2="hello-''>>foo=bar[]" attr3="] span"><span></span></div>`);
|
||||
await page.evaluate(() => window.div = document.querySelector('div'));
|
||||
const selectors = [
|
||||
`[attr="hello world"]`,
|
||||
`[attr = "hello world"]`,
|
||||
`[attr ~= world]`,
|
||||
`[attr ^=hello ]`,
|
||||
`[attr $= world ]`,
|
||||
`[attr *= "llo wor" ]`,
|
||||
`[attr2 |= hello]`,
|
||||
`[attr = "Hello World" i ]`,
|
||||
`[attr *= "llo WOR"i]`,
|
||||
`[attr $= woRLD i]`,
|
||||
`[attr2 = "hello-''>>foo=bar[]"]`,
|
||||
`[attr2 $="foo=bar[]"]`,
|
||||
];
|
||||
for (const selector of selectors)
|
||||
expect(await page.$eval(selector, e => e === div)).toBe(true);
|
||||
expect(await page.$eval(`[attr*=hello] span`, e => e.parentNode === div)).toBe(true);
|
||||
expect(await page.$eval(`[attr*=hello] >> span`, e => e.parentNode === div)).toBe(true);
|
||||
expect(await page.$eval(`[attr3="] span"] >> span`, e => e.parentNode === div)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('attribute selector', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue