mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: throw InvalidSelectorError from selector parser, add some tests (#19636)
This commit is contained in:
parent
3acf3f5cae
commit
eaae8ebbf3
@ -59,25 +59,25 @@ export function parseSelector(selector: string): ParsedSelector {
|
|||||||
try {
|
try {
|
||||||
const unescaped = JSON.parse('[' + part.body + ']');
|
const unescaped = JSON.parse('[' + part.body + ']');
|
||||||
if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== 'string')
|
if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== 'string')
|
||||||
throw new Error(`Malformed selector: ${part.name}=` + part.body);
|
throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);
|
||||||
innerSelector = unescaped[0];
|
innerSelector = unescaped[0];
|
||||||
if (unescaped.length === 2) {
|
if (unescaped.length === 2) {
|
||||||
if (typeof unescaped[1] !== 'number' || !kNestedSelectorNamesWithDistance.has(part.name))
|
if (typeof unescaped[1] !== 'number' || !kNestedSelectorNamesWithDistance.has(part.name))
|
||||||
throw new Error(`Malformed selector: ${part.name}=` + part.body);
|
throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);
|
||||||
distance = unescaped[1];
|
distance = unescaped[1];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Malformed selector: ${part.name}=` + part.body);
|
throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);
|
||||||
}
|
}
|
||||||
const result = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } };
|
const result = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } };
|
||||||
if (result.body.parsed.parts.some(part => part.name === 'internal:control' && part.body === 'enter-frame'))
|
if (result.body.parsed.parts.some(part => part.name === 'internal:control' && part.body === 'enter-frame'))
|
||||||
throw new Error(`Frames are not allowed inside "${part.name}" selectors`);
|
throw new InvalidSelectorError(`Frames are not allowed inside "${part.name}" selectors`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return { ...part, source: part.body };
|
return { ...part, source: part.body };
|
||||||
});
|
});
|
||||||
if (kNestedSelectorNames.has(parts[0].name))
|
if (kNestedSelectorNames.has(parts[0].name))
|
||||||
throw new Error(`"${parts[0].name}" selector cannot be first`);
|
throw new InvalidSelectorError(`"${parts[0].name}" selector cannot be first`);
|
||||||
return {
|
return {
|
||||||
capture: result.capture,
|
capture: result.capture,
|
||||||
parts
|
parts
|
||||||
@ -241,8 +241,8 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
|
|||||||
|
|
||||||
const syntaxError = (stage: string|undefined) => {
|
const syntaxError = (stage: string|undefined) => {
|
||||||
if (EOL)
|
if (EOL)
|
||||||
throw new Error(`Unexpected end of selector while parsing selector \`${selector}\``);
|
throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \`${selector}\``);
|
||||||
throw new Error(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}` + (stage ? ' during ' + stage : ''));
|
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}` + (stage ? ' during ' + stage : ''));
|
||||||
};
|
};
|
||||||
|
|
||||||
function skipSpaces() {
|
function skipSpaces() {
|
||||||
@ -313,7 +313,7 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
|
|||||||
try {
|
try {
|
||||||
return new RegExp(source, flags);
|
return new RegExp(source, flags);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Error while parsing selector \`${selector}\`: ${e.message}`);
|
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\`: ${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +369,7 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
|
|||||||
skipSpaces();
|
skipSpaces();
|
||||||
if (next() === '/') {
|
if (next() === '/') {
|
||||||
if (operator !== '=')
|
if (operator !== '=')
|
||||||
throw new Error(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with regular expression`);
|
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with regular expression`);
|
||||||
value = readRegularExpression();
|
value = readRegularExpression();
|
||||||
} else if (next() === `'` || next() === `"`) {
|
} else if (next() === `'` || next() === `"`) {
|
||||||
value = readQuotedString(next()).slice(1, -1);
|
value = readQuotedString(next()).slice(1, -1);
|
||||||
@ -403,7 +403,7 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
|
|||||||
|
|
||||||
eat1();
|
eat1();
|
||||||
if (operator !== '=' && typeof value !== 'string')
|
if (operator !== '=' && typeof value !== 'string')
|
||||||
throw new Error(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);
|
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);
|
||||||
return { name: jsonPath.join('.'), jsonPath, op: operator, value, caseSensitive };
|
return { name: jsonPath.join('.'), jsonPath, op: operator, value, caseSensitive };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,6 +420,6 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
|
|||||||
if (!EOL)
|
if (!EOL)
|
||||||
syntaxError(undefined);
|
syntaxError(undefined);
|
||||||
if (!result.name && !result.attributes.length)
|
if (!result.name && !result.attributes.length)
|
||||||
throw new Error(`Error while parsing selector \`${selector}\` - selector cannot be empty`);
|
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - selector cannot be empty`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -311,6 +311,41 @@ test.describe('toBeVisible', () => {
|
|||||||
await page.setContent('<div id=node>Text content</div>');
|
await page.setContent('<div id=node>Text content</div>');
|
||||||
await expect(page.locator('no-such-thing')).not.toBeVisible({ timeout: 1 });
|
await expect(page.locator('no-such-thing')).not.toBeVisible({ timeout: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('with frameLocator', async ({ page }) => {
|
||||||
|
await page.setContent('<div></div>');
|
||||||
|
const locator = page.frameLocator('iframe').locator('input');
|
||||||
|
let done = false;
|
||||||
|
const promise = expect(locator).toBeVisible().then(() => done = true);
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
expect(done).toBe(false);
|
||||||
|
await page.setContent('<iframe srcdoc="<input>"></iframe>');
|
||||||
|
await promise;
|
||||||
|
expect(done).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with frameLocator 2', async ({ page }) => {
|
||||||
|
await page.setContent('<iframe></iframe>');
|
||||||
|
const locator = page.frameLocator('iframe').locator('input');
|
||||||
|
let done = false;
|
||||||
|
const promise = expect(locator).toBeVisible().then(() => done = true);
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
expect(done).toBe(false);
|
||||||
|
await page.setContent('<iframe srcdoc="<input>"></iframe>');
|
||||||
|
await promise;
|
||||||
|
expect(done).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('over navigation', async ({ page, server }) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
let done = false;
|
||||||
|
const promise = expect(page.locator('input')).toBeVisible().then(() => done = true);
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
expect(done).toBe(false);
|
||||||
|
await page.goto(server.PREFIX + '/input/checkbox.html');
|
||||||
|
await promise;
|
||||||
|
expect(done).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('toBeHidden', () => {
|
test.describe('toBeHidden', () => {
|
||||||
|
@ -516,7 +516,7 @@ test('should print expected/received on Ctrl+C', async ({ runInlineTest }) => {
|
|||||||
test('times out waiting for text', async ({ page }) => {
|
test('times out waiting for text', async ({ page }) => {
|
||||||
await page.setContent('<div id=node>Text content</div>');
|
await page.setContent('<div id=node>Text content</div>');
|
||||||
const promise = expect(page.locator('#node')).toHaveText('Text 2');
|
const promise = expect(page.locator('#node')).toHaveText('Text 2');
|
||||||
await new Promise(f => setTimeout(f, 500));
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
console.log('\\n%%SEND-SIGINT%%');
|
console.log('\\n%%SEND-SIGINT%%');
|
||||||
await promise;
|
await promise;
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user