chore: improve aria template error reporting (#33438)
This commit is contained in:
parent
36a975c30b
commit
f138c30915
|
|
@ -19,7 +19,9 @@ import { yaml } from '../utilsBundle';
|
||||||
import { assert } from '../utils';
|
import { assert } from '../utils';
|
||||||
|
|
||||||
export function parseAriaSnapshot(text: string): AriaTemplateNode {
|
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' };
|
const result: AriaTemplateNode = { kind: 'role', role: 'fragment' };
|
||||||
populateNode(result, fragment);
|
populateNode(result, fragment);
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -73,7 +75,7 @@ function populateNode(node: AriaTemplateRoleNode, container: any[]) {
|
||||||
|
|
||||||
function applyAttribute(node: AriaTemplateRoleNode, key: string, value: string) {
|
function applyAttribute(node: AriaTemplateRoleNode, key: string, value: string) {
|
||||||
if (key === 'checked') {
|
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';
|
node.checked = value === 'true' ? true : value === 'false' ? false : 'mixed';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +104,7 @@ function applyAttribute(node: AriaTemplateRoleNode, key: string, value: string)
|
||||||
node.selected = value === 'true';
|
node.selected = value === 'true';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(`Unsupported attribute [${key}] `);
|
throw new Error(`Unsupported attribute [${key}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeWhitespace(text: string) {
|
function normalizeWhitespace(text: string) {
|
||||||
|
|
@ -149,7 +151,7 @@ export class KeyParser {
|
||||||
|
|
||||||
private _readIdentifier(): string {
|
private _readIdentifier(): string {
|
||||||
if (this._eof())
|
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;
|
const start = this._pos;
|
||||||
while (!this._eof() && /[a-zA-Z]/.test(this._peek()))
|
while (!this._eof() && /[a-zA-Z]/.test(this._peek()))
|
||||||
this._pos++;
|
this._pos++;
|
||||||
|
|
@ -173,7 +175,11 @@ export class KeyParser {
|
||||||
result += ch;
|
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 {
|
private _readRegex(): string {
|
||||||
|
|
@ -193,7 +199,7 @@ export class KeyParser {
|
||||||
result += ch;
|
result += ch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('Unterminated regex starting at position ' + this._pos);
|
this._throwError('Unterminated regex');
|
||||||
}
|
}
|
||||||
|
|
||||||
private _readStringOrRegex(): string | RegExp | null {
|
private _readStringOrRegex(): string | RegExp | null {
|
||||||
|
|
@ -229,7 +235,7 @@ export class KeyParser {
|
||||||
}
|
}
|
||||||
this._skipWhitespace();
|
this._skipWhitespace();
|
||||||
if (this._peek() !== ']')
|
if (this._peek() !== ']')
|
||||||
throw new Error('Expected ] at position ' + this._pos);
|
this._throwError('Expected ]');
|
||||||
|
|
||||||
this._next(); // Consume ']'
|
this._next(); // Consume ']'
|
||||||
flags.set(flagName, flagValue || 'true');
|
flags.set(flagName, flagValue || 'true');
|
||||||
|
|
@ -252,8 +258,7 @@ export class KeyParser {
|
||||||
applyAttribute(result, name, value);
|
applyAttribute(result, name, value);
|
||||||
this._skipWhitespace();
|
this._skipWhitespace();
|
||||||
if (!this._eof())
|
if (!this._eof())
|
||||||
throw new Error('Unexpected input at position ' + this._pos);
|
this._throwError('Unexpected input');
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
^
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue