chore: throw InvalidSelectorError from selector parser, add some tests (#19636)

This commit is contained in:
Dmitry Gozman 2022-12-21 14:27:35 -08:00 коммит произвёл GitHub
Родитель 3acf3f5cae
Коммит eaae8ebbf3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 47 добавлений и 12 удалений

Просмотреть файл

@ -59,25 +59,25 @@ export function parseSelector(selector: string): ParsedSelector {
try {
const unescaped = JSON.parse('[' + part.body + ']');
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];
if (unescaped.length === 2) {
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];
}
} 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 } };
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 { ...part, source: part.body };
});
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 {
capture: result.capture,
parts
@ -241,8 +241,8 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
const syntaxError = (stage: string|undefined) => {
if (EOL)
throw new Error(`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(`Unexpected end of selector while parsing selector \`${selector}\``);
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}` + (stage ? ' during ' + stage : ''));
};
function skipSpaces() {
@ -313,7 +313,7 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
try {
return new RegExp(source, flags);
} 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();
if (next() === '/') {
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();
} else if (next() === `'` || next() === `"`) {
value = readQuotedString(next()).slice(1, -1);
@ -403,7 +403,7 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
eat1();
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 };
}
@ -420,6 +420,6 @@ export function parseAttributeSelector(selector: string, allowUnquotedStrings: b
if (!EOL)
syntaxError(undefined);
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;
}

Просмотреть файл

@ -311,6 +311,41 @@ test.describe('toBeVisible', () => {
await page.setContent('<div id=node>Text content</div>');
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', () => {

Просмотреть файл

@ -516,7 +516,7 @@ test('should print expected/received on Ctrl+C', async ({ runInlineTest }) => {
test('times out waiting for text', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
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%%');
await promise;
});