chore: more yaml escaping tests (#33387)
This commit is contained in:
parent
dcf85edcb7
commit
135ed28740
|
|
@ -187,6 +187,10 @@ test('should correctly render links in attachments', async ({ mount }) => {
|
||||||
await expect(body).toBeVisible();
|
await expect(body).toBeVisible();
|
||||||
await expect(body.locator('a').filter({ hasText: 'playwright.dev' })).toHaveAttribute('href', 'https://playwright.dev/docs/intro');
|
await expect(body.locator('a').filter({ hasText: 'playwright.dev' })).toHaveAttribute('href', 'https://playwright.dev/docs/intro');
|
||||||
await expect(body.locator('a').filter({ hasText: 'github.com' })).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
await expect(body.locator('a').filter({ hasText: 'github.com' })).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
||||||
|
await expect(component).toMatchAriaSnapshot(`
|
||||||
|
- link "https://playwright.dev/docs/intro"
|
||||||
|
- link "https://github.com/microsoft/playwright/issues/31284"
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should correctly render links in attachment name', async ({ mount }) => {
|
test('should correctly render links in attachment name', async ({ mount }) => {
|
||||||
|
|
@ -194,6 +198,9 @@ test('should correctly render links in attachment name', async ({ mount }) => {
|
||||||
const link = component.getByText('attachment with inline link').locator('a');
|
const link = component.getByText('attachment with inline link').locator('a');
|
||||||
await expect(link).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
await expect(link).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
||||||
await expect(link).toHaveText('https://github.com/microsoft/playwright/issues/31284');
|
await expect(link).toHaveText('https://github.com/microsoft/playwright/issues/31284');
|
||||||
|
await expect(component).toMatchAriaSnapshot(`
|
||||||
|
- link /https:\\/\\/github\\.com\\/microsoft\\/playwright\\/issues\\/\\d+/
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should correctly render prev and next', async ({ mount }) => {
|
test('should correctly render prev and next', async ({ mount }) => {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
import type { AriaTemplateNode, AriaTemplateRoleNode } from './injected/ariaSnapshot';
|
import type { AriaTemplateNode, AriaTemplateRoleNode } from './injected/ariaSnapshot';
|
||||||
import { yaml } from '../utilsBundle';
|
import { yaml } from '../utilsBundle';
|
||||||
import type { AriaRole } from '@injected/roleUtils';
|
|
||||||
import { assert } from '../utils';
|
import { assert } from '../utils';
|
||||||
|
|
||||||
export function parseAriaSnapshot(text: string): AriaTemplateNode {
|
export function parseAriaSnapshot(text: string): AriaTemplateNode {
|
||||||
|
|
@ -29,7 +28,7 @@ export function parseAriaSnapshot(text: string): AriaTemplateNode {
|
||||||
function populateNode(node: AriaTemplateRoleNode, container: any[]) {
|
function populateNode(node: AriaTemplateRoleNode, container: any[]) {
|
||||||
for (const object of container) {
|
for (const object of container) {
|
||||||
if (typeof object === 'string') {
|
if (typeof object === 'string') {
|
||||||
const childNode = parseKey(object);
|
const childNode = KeyParser.parse(object);
|
||||||
node.children = node.children || [];
|
node.children = node.children || [];
|
||||||
node.children.push(childNode);
|
node.children.push(childNode);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -47,7 +46,7 @@ function populateNode(node: AriaTemplateRoleNode, container: any[]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const childNode = parseKey(key);
|
const childNode = KeyParser.parse(key);
|
||||||
if (childNode.kind === 'text') {
|
if (childNode.kind === 'text') {
|
||||||
node.children.push({
|
node.children.push({
|
||||||
kind: 'text',
|
kind: 'text',
|
||||||
|
|
@ -106,47 +105,6 @@ function applyAttribute(node: AriaTemplateRoleNode, key: string, value: string)
|
||||||
throw new Error(`Unsupported attribute [${key}] `);
|
throw new Error(`Unsupported attribute [${key}] `);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseKey(key: string): AriaTemplateNode {
|
|
||||||
const tokenRegex = /\s*([a-z]+|"(?:[^"]*)"|\/(?:[^\/]*)\/|\[.*?\])/g;
|
|
||||||
let match;
|
|
||||||
const tokens = [];
|
|
||||||
while ((match = tokenRegex.exec(key)) !== null)
|
|
||||||
tokens.push(match[1]);
|
|
||||||
|
|
||||||
if (tokens.length === 0)
|
|
||||||
throw new Error(`Invalid key ${key}`);
|
|
||||||
|
|
||||||
const role = tokens[0] as AriaRole;
|
|
||||||
|
|
||||||
let name: string | RegExp = '';
|
|
||||||
let index = 1;
|
|
||||||
if (tokens.length > 1 && (tokens[1].startsWith('"') || tokens[1].startsWith('/'))) {
|
|
||||||
const nameToken = tokens[1];
|
|
||||||
if (nameToken.startsWith('"')) {
|
|
||||||
name = nameToken.slice(1, -1);
|
|
||||||
} else {
|
|
||||||
const pattern = nameToken.slice(1, -1);
|
|
||||||
name = new RegExp(pattern);
|
|
||||||
}
|
|
||||||
index = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: AriaTemplateRoleNode = { kind: 'role', role, name };
|
|
||||||
for (; index < tokens.length; index++) {
|
|
||||||
const attrToken = tokens[index];
|
|
||||||
if (attrToken.startsWith('[') && attrToken.endsWith(']')) {
|
|
||||||
const attrContent = attrToken.slice(1, -1).trim();
|
|
||||||
const [attrName, attrValue] = attrContent.split('=', 2);
|
|
||||||
const value = attrValue !== undefined ? attrValue.trim() : 'true';
|
|
||||||
applyAttribute(result, attrName, value);
|
|
||||||
} else {
|
|
||||||
throw new Error(`Invalid attribute token ${attrToken} in key ${key}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeWhitespace(text: string) {
|
function normalizeWhitespace(text: string) {
|
||||||
return text.replace(/[\r\n\s\t]+/g, ' ').trim();
|
return text.replace(/[\r\n\s\t]+/g, ' ').trim();
|
||||||
}
|
}
|
||||||
|
|
@ -154,3 +112,148 @@ function normalizeWhitespace(text: string) {
|
||||||
function valueOrRegex(value: string): string | RegExp {
|
function valueOrRegex(value: string): string | RegExp {
|
||||||
return value.startsWith('/') && value.endsWith('/') ? new RegExp(value.slice(1, -1)) : normalizeWhitespace(value);
|
return value.startsWith('/') && value.endsWith('/') ? new RegExp(value.slice(1, -1)) : normalizeWhitespace(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class KeyParser {
|
||||||
|
private _input: string;
|
||||||
|
private _pos: number;
|
||||||
|
private _length: number;
|
||||||
|
|
||||||
|
static parse(input: string): AriaTemplateNode {
|
||||||
|
return new KeyParser(input)._parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(input: string) {
|
||||||
|
this._input = input;
|
||||||
|
this._pos = 0;
|
||||||
|
this._length = input.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _peek() {
|
||||||
|
return this._input[this._pos] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private _next() {
|
||||||
|
if (this._pos < this._length)
|
||||||
|
return this._input[this._pos++];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _eof() {
|
||||||
|
return this._pos >= this._length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _skipWhitespace() {
|
||||||
|
while (!this._eof() && /\s/.test(this._peek()))
|
||||||
|
this._pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _readIdentifier(): string {
|
||||||
|
if (this._eof())
|
||||||
|
throw new Error('Unexpected end of input when expecting identifier');
|
||||||
|
const start = this._pos;
|
||||||
|
while (!this._eof() && /[a-zA-Z]/.test(this._peek()))
|
||||||
|
this._pos++;
|
||||||
|
return this._input.slice(start, this._pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _readString(): string {
|
||||||
|
let result = '';
|
||||||
|
let escaped = false;
|
||||||
|
while (!this._eof()) {
|
||||||
|
const ch = this._next();
|
||||||
|
if (escaped) {
|
||||||
|
result += ch;
|
||||||
|
escaped = false;
|
||||||
|
} else if (ch === '\\') {
|
||||||
|
escaped = true;
|
||||||
|
result += ch;
|
||||||
|
} else if (ch === '"') {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
result += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('Unterminated string starting at position ' + this._pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _readRegex(): string {
|
||||||
|
let result = '';
|
||||||
|
let escaped = false;
|
||||||
|
while (!this._eof()) {
|
||||||
|
const ch = this._next();
|
||||||
|
if (escaped) {
|
||||||
|
result += ch;
|
||||||
|
escaped = false;
|
||||||
|
} else if (ch === '\\') {
|
||||||
|
escaped = true;
|
||||||
|
result += ch;
|
||||||
|
} else if (ch === '/') {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
result += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('Unterminated regex starting at position ' + this._pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _readStringOrRegex(): string | RegExp | null {
|
||||||
|
const ch = this._peek();
|
||||||
|
if (ch === '"') {
|
||||||
|
this._next();
|
||||||
|
return this._readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch === '/') {
|
||||||
|
this._next();
|
||||||
|
return new RegExp(this._readRegex());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _readFlags(): Map<string, string> {
|
||||||
|
const flags = new Map<string, string>();
|
||||||
|
while (true) {
|
||||||
|
this._skipWhitespace();
|
||||||
|
if (this._peek() === '[') {
|
||||||
|
this._next();
|
||||||
|
this._skipWhitespace();
|
||||||
|
const flagName = this._readIdentifier();
|
||||||
|
this._skipWhitespace();
|
||||||
|
let flagValue = '';
|
||||||
|
if (this._peek() === '=') {
|
||||||
|
this._next();
|
||||||
|
this._skipWhitespace();
|
||||||
|
while (this._peek() !== ']' && !this._eof())
|
||||||
|
flagValue += this._next();
|
||||||
|
}
|
||||||
|
this._skipWhitespace();
|
||||||
|
if (this._peek() !== ']')
|
||||||
|
throw new Error('Expected ] at position ' + this._pos);
|
||||||
|
|
||||||
|
this._next(); // Consume ']'
|
||||||
|
flags.set(flagName, flagValue || 'true');
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
_parse(): AriaTemplateNode {
|
||||||
|
this._skipWhitespace();
|
||||||
|
|
||||||
|
const role = this._readIdentifier() as AriaTemplateRoleNode['role'];
|
||||||
|
this._skipWhitespace();
|
||||||
|
const name = this._readStringOrRegex() || '';
|
||||||
|
const result: AriaTemplateRoleNode = { kind: 'role', role, name };
|
||||||
|
const flags = this._readFlags();
|
||||||
|
for (const [name, value] of flags)
|
||||||
|
applyAttribute(result, name, value);
|
||||||
|
this._skipWhitespace();
|
||||||
|
if (!this._eof())
|
||||||
|
throw new Error('Unexpected input at position ' + this._pos);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,9 @@ ${body}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function quoteMultiline(text: string, indent = ' ') {
|
export function quoteMultiline(text: string, indent = ' ') {
|
||||||
const escape = (text: string) => text.replace(/`/g, '\\`').replace(/\\/g, '\\\\');
|
const escape = (text: string) => text.replace(/\\/g, '\\\\')
|
||||||
|
.replace(/`/g, '\\`')
|
||||||
|
.replace(/\$\{/g, '\\${');
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
if (lines.length === 1)
|
if (lines.length === 1)
|
||||||
return '`' + escape(text) + '`';
|
return '`' + escape(text) + '`';
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import * as roleUtils from './roleUtils';
|
||||||
import { getElementComputedStyle } from './domUtils';
|
import { getElementComputedStyle } from './domUtils';
|
||||||
import type { AriaRole } from './roleUtils';
|
import type { AriaRole } from './roleUtils';
|
||||||
import { escapeRegExp, longestCommonSubstring } from '@isomorphic/stringUtils';
|
import { escapeRegExp, longestCommonSubstring } from '@isomorphic/stringUtils';
|
||||||
import { yamlEscapeStringIfNeeded, yamlQuoteFragment } from './yaml';
|
import { yamlEscapeKeyIfNeeded, yamlEscapeValueIfNeeded, yamlQuoteFragment } from './yaml';
|
||||||
|
|
||||||
type AriaProps = {
|
type AriaProps = {
|
||||||
checked?: boolean | 'mixed';
|
checked?: boolean | 'mixed';
|
||||||
|
|
@ -317,13 +317,13 @@ export function renderAriaTree(ariaNode: AriaNode, options?: { mode?: 'raw' | 'r
|
||||||
if (ariaNode.selected === true)
|
if (ariaNode.selected === true)
|
||||||
key += ` [selected]`;
|
key += ` [selected]`;
|
||||||
|
|
||||||
const escapedKey = indent + '- ' + yamlEscapeStringIfNeeded(key, '\'');
|
const escapedKey = indent + '- ' + yamlEscapeKeyIfNeeded(key);
|
||||||
if (!ariaNode.children.length) {
|
if (!ariaNode.children.length) {
|
||||||
lines.push(escapedKey);
|
lines.push(escapedKey);
|
||||||
} else if (ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string') {
|
} else if (ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string') {
|
||||||
const text = includeText(ariaNode, ariaNode.children[0]) ? renderString(ariaNode.children[0] as string) : null;
|
const text = includeText(ariaNode, ariaNode.children[0]) ? renderString(ariaNode.children[0] as string) : null;
|
||||||
if (text)
|
if (text)
|
||||||
lines.push(escapedKey + ': ' + yamlEscapeStringIfNeeded(text, '"'));
|
lines.push(escapedKey + ': ' + yamlEscapeValueIfNeeded(text));
|
||||||
else
|
else
|
||||||
lines.push(escapedKey);
|
lines.push(escapedKey);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,21 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function yamlEscapeStringIfNeeded(str: string, quote = '"'): string {
|
export function yamlEscapeKeyIfNeeded(str: string): string {
|
||||||
if (!yamlStringNeedsQuotes(str))
|
if (!yamlStringNeedsQuotes(str))
|
||||||
return str;
|
return str;
|
||||||
return yamlEscapeString(str, quote);
|
return `'` + str.replace(/'/g, `''`) + `'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function yamlEscapeString(str: string, quote = '"'): string {
|
export function yamlEscapeValueIfNeeded(str: string): string {
|
||||||
return quote + str.replace(/[\\"\x00-\x1f\x7f-\x9f]/g, c => {
|
if (!yamlStringNeedsQuotes(str))
|
||||||
|
return str;
|
||||||
|
return '"' + str.replace(/[\\"\x00-\x1f\x7f-\x9f]/g, c => {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\\':
|
case '\\':
|
||||||
return '\\\\';
|
return '\\\\';
|
||||||
case '"':
|
case '"':
|
||||||
return quote === '"' ? '\\"' : '"';
|
return '\\"';
|
||||||
case '\'':
|
|
||||||
return quote === '\'' ? '\\\'' : '\'';
|
|
||||||
case '\b':
|
case '\b':
|
||||||
return '\\b';
|
return '\\b';
|
||||||
case '\f':
|
case '\f':
|
||||||
|
|
@ -43,7 +43,7 @@ export function yamlEscapeString(str: string, quote = '"'): string {
|
||||||
const code = c.charCodeAt(0);
|
const code = c.charCodeAt(0);
|
||||||
return '\\x' + code.toString(16).padStart(2, '0');
|
return '\\x' + code.toString(16).padStart(2, '0');
|
||||||
}
|
}
|
||||||
}) + quote;
|
}) + '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function yamlQuoteFragment(str: string, quote = '"'): string {
|
export function yamlQuoteFragment(str: string, quote = '"'): string {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,13 @@ export function escapeWithQuotes(text: string, char: string = '\'') {
|
||||||
throw new Error('Invalid escape char');
|
throw new Error('Invalid escape char');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function escapeTemplateString(text: string): string {
|
||||||
|
return text
|
||||||
|
.replace(/\\/g, '\\\\')
|
||||||
|
.replace(/`/g, '\\`')
|
||||||
|
.replace(/\$\{/g, '\\${');
|
||||||
|
}
|
||||||
|
|
||||||
export function isString(obj: any): obj is string {
|
export function isString(obj: any): obj is string {
|
||||||
return typeof obj === 'string' || obj instanceof String;
|
return typeof obj === 'string' || obj instanceof String;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { callLogText } from '../util';
|
||||||
import { printReceivedStringContainExpectedSubstring } from './expect';
|
import { printReceivedStringContainExpectedSubstring } from './expect';
|
||||||
import { currentTestInfo } from '../common/globals';
|
import { currentTestInfo } from '../common/globals';
|
||||||
import type { MatcherReceived } from '@injected/ariaSnapshot';
|
import type { MatcherReceived } from '@injected/ariaSnapshot';
|
||||||
|
import { escapeTemplateString } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
export async function toMatchAriaSnapshot(
|
export async function toMatchAriaSnapshot(
|
||||||
this: ExpectMatcherState,
|
this: ExpectMatcherState,
|
||||||
|
|
@ -102,7 +103,7 @@ export async function toMatchAriaSnapshot(
|
||||||
|
|
||||||
if (!this.isNot && pass === this.isNot && generateNewBaseline) {
|
if (!this.isNot && pass === this.isNot && generateNewBaseline) {
|
||||||
// Only rebaseline failed snapshots.
|
// Only rebaseline failed snapshots.
|
||||||
const suggestedRebaseline = `toMatchAriaSnapshot(\`\n${indent(typedReceived.regex, '${indent} ')}\n\${indent}\`)`;
|
const suggestedRebaseline = `toMatchAriaSnapshot(\`\n${escapeTemplateString(indent(typedReceived.regex, '{indent} '))}\n{indent}\`)`;
|
||||||
return { pass: this.isNot, message: () => '', name: 'toMatchAriaSnapshot', suggestedRebaseline };
|
return { pass: this.isNot, message: () => '', name: 'toMatchAriaSnapshot', suggestedRebaseline };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +119,7 @@ export async function toMatchAriaSnapshot(
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapePrivateUsePoints(str: string) {
|
function escapePrivateUsePoints(str: string) {
|
||||||
return str.replace(/[\uE000-\uF8FF]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
return escapeTemplateString(str).replace(/[\uE000-\uF8FF]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unshift(snapshot: string): string {
|
function unshift(snapshot: string): string {
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export async function applySuggestedRebaselines(config: FullConfigInternal) {
|
||||||
if (matcher.loc!.start.column + 1 !== replacement.location.column)
|
if (matcher.loc!.start.column + 1 !== replacement.location.column)
|
||||||
continue;
|
continue;
|
||||||
const indent = lines[matcher.loc!.start.line - 1].match(/^\s*/)![0];
|
const indent = lines[matcher.loc!.start.line - 1].match(/^\s*/)![0];
|
||||||
const newText = replacement.code.replace(/\$\{indent\}/g, indent);
|
const newText = replacement.code.replace(/\{indent\}/g, indent);
|
||||||
ranges.push({ start: matcher.start!, end: node.end!, oldText: source.substring(matcher.start!, node.end!), newText });
|
ranges.push({ start: matcher.start!, end: node.end!, oldText: source.substring(matcher.start!, node.end!), newText });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -386,8 +386,7 @@ it('should include pseudo codepoints', async ({ page, server }) => {
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('check aria-hidden text', async ({ page, server }) => {
|
it('check aria-hidden text', async ({ page }) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
await page.setContent(`
|
await page.setContent(`
|
||||||
<p>
|
<p>
|
||||||
<span>hello</span>
|
<span>hello</span>
|
||||||
|
|
@ -400,8 +399,7 @@ it('check aria-hidden text', async ({ page, server }) => {
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore presentation and none roles', async ({ page, server }) => {
|
it('should ignore presentation and none roles', async ({ page }) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
await page.setContent(`
|
await page.setContent(`
|
||||||
<ul>
|
<ul>
|
||||||
<li role='presentation'>hello</li>
|
<li role='presentation'>hello</li>
|
||||||
|
|
|
||||||
|
|
@ -405,3 +405,56 @@ Locator: locator('body')
|
||||||
+ - heading "todos" [level=1]
|
+ - heading "todos" [level=1]
|
||||||
+ - textbox "What needs to be done?"`);
|
+ - textbox "What needs to be done?"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should unpack escaped names', async ({ page }) => {
|
||||||
|
{
|
||||||
|
await page.setContent(`
|
||||||
|
<button>Click: me</button>
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- 'button "Click: me"'
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- 'button /Click: me/'
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await page.setContent(`
|
||||||
|
<button>Click / me</button>
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- button "Click / me"
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- button /Click \\/ me/
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- 'button /Click \\/ me/'
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await page.setContent(`
|
||||||
|
<button>Click \\ me</button>
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- button "Click \\ me"
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- button /Click \\\\ me/
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- 'button /Click \\\\ me/'
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await page.setContent(`
|
||||||
|
<button>Click ' me</button>
|
||||||
|
`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- 'button "Click '' me"'
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -114,14 +114,14 @@ test('should generate baseline with regex', async ({ runInlineTest }, testInfo)
|
||||||
+ - list:
|
+ - list:
|
||||||
+ - listitem: Item 1
|
+ - listitem: Item 1
|
||||||
+ - listitem: Item 2
|
+ - listitem: Item 2
|
||||||
+ - listitem: /Time \\d+:\\d+/
|
+ - listitem: /Time \\\\d+:\\\\d+/
|
||||||
+ - listitem: /Year \\d+/
|
+ - listitem: /Year \\\\d+/
|
||||||
+ - listitem: /Duration \\d+[hmsp]+/
|
+ - listitem: /Duration \\\\d+[hmsp]+/
|
||||||
+ - listitem: /\\d+,\\d+/
|
+ - listitem: /\\\\d+,\\\\d+/
|
||||||
+ - listitem: /\\d+,\\d+\\.\\d+/
|
+ - listitem: /\\\\d+,\\\\d+\\\\.\\\\d+/
|
||||||
+ - listitem: /Total \\d+/
|
+ - listitem: /Total \\\\d+/
|
||||||
+ - listitem: /Regex 1/
|
+ - listitem: /Regex 1/
|
||||||
+ - listitem: /\\/Regex \\d+[hmsp]+\\//
|
+ - listitem: /\\\\/Regex \\\\d+[hmsp]+\\\\//
|
||||||
+ \`);
|
+ \`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -136,6 +136,8 @@ test('should generate baseline with special characters', async ({ runInlineTest
|
||||||
await page.setContent(\`<ul>
|
await page.setContent(\`<ul>
|
||||||
<button>Click: me</button>
|
<button>Click: me</button>
|
||||||
<button>Click: 123</button>
|
<button>Click: 123</button>
|
||||||
|
<button>Click ' me</button>
|
||||||
|
<button>Click: ' me</button>
|
||||||
<li>Item: 1</li>
|
<li>Item: 1</li>
|
||||||
<li>Item {a: b}</li>
|
<li>Item {a: b}</li>
|
||||||
</ul>\`);
|
</ul>\`);
|
||||||
|
|
@ -149,7 +151,7 @@ test('should generate baseline with special characters', async ({ runInlineTest
|
||||||
const data = fs.readFileSync(patchPath, 'utf-8');
|
const data = fs.readFileSync(patchPath, 'utf-8');
|
||||||
expect(data).toBe(`--- a/a.spec.ts
|
expect(data).toBe(`--- a/a.spec.ts
|
||||||
+++ b/a.spec.ts
|
+++ b/a.spec.ts
|
||||||
@@ -7,6 +7,12 @@
|
@@ -9,6 +9,14 @@
|
||||||
<li>Item: 1</li>
|
<li>Item: 1</li>
|
||||||
<li>Item {a: b}</li>
|
<li>Item {a: b}</li>
|
||||||
</ul>\`);
|
</ul>\`);
|
||||||
|
|
@ -158,6 +160,8 @@ test('should generate baseline with special characters', async ({ runInlineTest
|
||||||
+ - list:
|
+ - list:
|
||||||
+ - 'button "Click: me"'
|
+ - 'button "Click: me"'
|
||||||
+ - 'button /Click: \\\\d+/'
|
+ - 'button /Click: \\\\d+/'
|
||||||
|
+ - button "Click ' me"
|
||||||
|
+ - 'button "Click: '' me"'
|
||||||
+ - listitem: \"Item: 1\"
|
+ - listitem: \"Item: 1\"
|
||||||
+ - listitem: \"Item {a: b}\"
|
+ - listitem: \"Item {a: b}\"
|
||||||
+ \`);
|
+ \`);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue