chore: improve aria template error reporting (#33438)

This commit is contained in:
Pavel Feldman 2024-11-04 15:33:09 -08:00 committed by GitHub
parent 36a975c30b
commit f138c30915
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 111 additions and 9 deletions

View file

@ -19,7 +19,9 @@ import { yaml } from '../utilsBundle';
import { assert } from '../utils';
export function parseAriaSnapshot(text: string): AriaTemplateNode {
const fragment = yaml.parse(text) as any[];
const fragment = yaml.parse(text);
if (!Array.isArray(fragment))
throw new Error('Expected object key starting with "- ":\n\n' + text + '\n');
const result: AriaTemplateNode = { kind: 'role', role: 'fragment' };
populateNode(result, fragment);
return result;
@ -73,7 +75,7 @@ function populateNode(node: AriaTemplateRoleNode, container: any[]) {
function applyAttribute(node: AriaTemplateRoleNode, key: string, value: string) {
if (key === 'checked') {
assert(value === 'true' || value === 'false' || value === 'mixed', 'Value of "disabled" attribute must be a boolean or "mixed"');
assert(value === 'true' || value === 'false' || value === 'mixed', 'Value of "checked\" attribute must be a boolean or "mixed"');
node.checked = value === 'true' ? true : value === 'false' ? false : 'mixed';
return;
}
@ -102,7 +104,7 @@ function applyAttribute(node: AriaTemplateRoleNode, key: string, value: string)
node.selected = value === 'true';
return;
}
throw new Error(`Unsupported attribute [${key}] `);
throw new Error(`Unsupported attribute [${key}]`);
}
function normalizeWhitespace(text: string) {
@ -149,7 +151,7 @@ export class KeyParser {
private _readIdentifier(): string {
if (this._eof())
throw new Error('Unexpected end of input when expecting identifier');
this._throwError('Unexpected end of input when expecting identifier');
const start = this._pos;
while (!this._eof() && /[a-zA-Z]/.test(this._peek()))
this._pos++;
@ -173,7 +175,11 @@ export class KeyParser {
result += ch;
}
}
throw new Error('Unterminated string starting at position ' + this._pos);
this._throwError('Unterminated string');
}
private _throwError(message: string): never {
throw new Error(message + ':\n\n' + this._input + '\n' + ' '.repeat(this._pos) + '^\n');
}
private _readRegex(): string {
@ -193,7 +199,7 @@ export class KeyParser {
result += ch;
}
}
throw new Error('Unterminated regex starting at position ' + this._pos);
this._throwError('Unterminated regex');
}
private _readStringOrRegex(): string | RegExp | null {
@ -229,7 +235,7 @@ export class KeyParser {
}
this._skipWhitespace();
if (this._peek() !== ']')
throw new Error('Expected ] at position ' + this._pos);
this._throwError('Expected ]');
this._next(); // Consume ']'
flags.set(flagName, flagValue || 'true');
@ -252,8 +258,7 @@ export class KeyParser {
applyAttribute(result, name, value);
this._skipWhitespace();
if (!this._eof())
throw new Error('Unexpected input at position ' + this._pos);
this._throwError('Unexpected input');
return result;
}
}

View file

@ -458,3 +458,100 @@ test('should unpack escaped names', async ({ page }) => {
`);
}
});
test('should report error in YAML', async ({ page }) => {
await page.setContent(`<h1>title</h1>`);
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
heading "title"
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Expected object key starting with "- ":
heading "title"
`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading: a:
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Nested mappings are not allowed in compact mappings at line 1, column 12:
- heading: a:
^
`);
}
});
test('should report error in YAML keys', async ({ page }) => {
await page.setContent(`<h1>title</h1>`);
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading "title
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Unterminated string:
heading "title
^
`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading /title
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Unterminated regex:
heading /title
^
`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading [level=a]
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Value of "level" attribute must be a number`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading [expanded=FALSE]
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Value of "expanded" attribute must be a boolean`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading [checked=foo]
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Value of "checked" attribute must be a boolean or "mixed"`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading [level=]
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Value of "level" attribute must be a number`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading [bogus]
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Unsupported attribute [bogus]`);
}
{
const error = await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading invalid
`).catch(e => e);
expect.soft(error.message).toBe(`expect.toMatchAriaSnapshot: Unexpected input:
heading invalid
^
`);
}
});