chore: use objects for string aria template notes (#33371)
This commit is contained in:
parent
a43b99368e
commit
676f014b5f
|
|
@ -14,19 +14,19 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { AriaTemplateNode } 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 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 {
|
||||||
const fragment = yaml.parse(text) as any[];
|
const fragment = yaml.parse(text) as any[];
|
||||||
const result: AriaTemplateNode = { role: 'fragment' };
|
const result: AriaTemplateNode = { kind: 'role', role: 'fragment' };
|
||||||
populateNode(result, fragment);
|
populateNode(result, fragment);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateNode(node: AriaTemplateNode, 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 = parseKey(object);
|
||||||
|
|
@ -36,17 +36,33 @@ function populateNode(node: AriaTemplateNode, container: any[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of Object.keys(object)) {
|
for (const key of Object.keys(object)) {
|
||||||
const childNode = parseKey(key);
|
|
||||||
const value = object[key];
|
|
||||||
node.children = node.children || [];
|
node.children = node.children || [];
|
||||||
|
const value = object[key];
|
||||||
|
|
||||||
if (childNode.role === 'text') {
|
if (key === 'text') {
|
||||||
node.children.push(valueOrRegex(value));
|
node.children.push({
|
||||||
|
kind: 'text',
|
||||||
|
text: valueOrRegex(value)
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const childNode = parseKey(key);
|
||||||
|
if (childNode.kind === 'text') {
|
||||||
|
node.children.push({
|
||||||
|
kind: 'text',
|
||||||
|
text: valueOrRegex(value)
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
node.children.push({ ...childNode, children: [valueOrRegex(value)] });
|
node.children.push({
|
||||||
|
...childNode, children: [{
|
||||||
|
kind: 'text',
|
||||||
|
text: valueOrRegex(value)
|
||||||
|
}]
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +72,7 @@ function populateNode(node: AriaTemplateNode, container: any[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyAttribute(node: AriaTemplateNode, 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 "disabled" 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';
|
||||||
|
|
@ -100,7 +116,7 @@ function parseKey(key: string): AriaTemplateNode {
|
||||||
if (tokens.length === 0)
|
if (tokens.length === 0)
|
||||||
throw new Error(`Invalid key ${key}`);
|
throw new Error(`Invalid key ${key}`);
|
||||||
|
|
||||||
const role = tokens[0] as AriaRole | 'text';
|
const role = tokens[0] as AriaRole;
|
||||||
|
|
||||||
let name: string | RegExp = '';
|
let name: string | RegExp = '';
|
||||||
let index = 1;
|
let index = 1;
|
||||||
|
|
@ -115,7 +131,7 @@ function parseKey(key: string): AriaTemplateNode {
|
||||||
index = 2;
|
index = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: AriaTemplateNode = { role, name };
|
const result: AriaTemplateRoleNode = { kind: 'role', role, name };
|
||||||
for (; index < tokens.length; index++) {
|
for (; index < tokens.length; index++) {
|
||||||
const attrToken = tokens[index];
|
const attrToken = tokens[index];
|
||||||
if (attrToken.startsWith('[') && attrToken.endsWith(']')) {
|
if (attrToken.startsWith('[') && attrToken.endsWith(']')) {
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,20 @@ type AriaNode = AriaProps & {
|
||||||
children: (AriaNode | string)[];
|
children: (AriaNode | string)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AriaTemplateNode = AriaProps & {
|
export type AriaTemplateTextNode = {
|
||||||
role: AriaRole | 'fragment' | 'text';
|
kind: 'text';
|
||||||
name?: RegExp | string;
|
text: RegExp | string;
|
||||||
children?: (AriaTemplateNode | string | RegExp)[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AriaTemplateRoleNode = AriaProps & {
|
||||||
|
kind: 'role';
|
||||||
|
role: AriaRole | 'fragment';
|
||||||
|
name?: RegExp | string;
|
||||||
|
children?: AriaTemplateNode[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AriaTemplateNode = AriaTemplateRoleNode | AriaTemplateTextNode;
|
||||||
|
|
||||||
export function generateAriaTree(rootElement: Element): AriaNode {
|
export function generateAriaTree(rootElement: Element): AriaNode {
|
||||||
const visit = (ariaNode: AriaNode, node: Node) => {
|
const visit = (ariaNode: AriaNode, node: Node) => {
|
||||||
if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {
|
if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {
|
||||||
|
|
@ -172,7 +180,7 @@ function normalizeStringChildren(rootA11yNode: AriaNode) {
|
||||||
|
|
||||||
const normalizeWhitespaceWithin = (text: string) => text.replace(/[\u200b\s\t\r\n]+/g, ' ');
|
const normalizeWhitespaceWithin = (text: string) => text.replace(/[\u200b\s\t\r\n]+/g, ' ');
|
||||||
|
|
||||||
function matchesText(text: string | undefined, template: RegExp | string | undefined) {
|
function matchesText(text: string, template: RegExp | string | undefined): boolean {
|
||||||
if (!template)
|
if (!template)
|
||||||
return true;
|
return true;
|
||||||
if (!text)
|
if (!text)
|
||||||
|
|
@ -182,7 +190,20 @@ function matchesText(text: string | undefined, template: RegExp | string | undef
|
||||||
return !!text.match(template);
|
return !!text.match(template);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode): { matches: boolean, received: { raw: string, regex: string } } {
|
function matchesTextNode(text: string, template: AriaTemplateTextNode) {
|
||||||
|
return matchesText(text, template.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchesName(text: string, template: AriaTemplateRoleNode) {
|
||||||
|
return matchesText(text, template.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MatcherReceived = {
|
||||||
|
raw: string;
|
||||||
|
regex: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode): { matches: boolean, received: MatcherReceived } {
|
||||||
const root = generateAriaTree(rootElement);
|
const root = generateAriaTree(rootElement);
|
||||||
const matches = matchesNodeDeep(root, template);
|
const matches = matchesNodeDeep(root, template);
|
||||||
return {
|
return {
|
||||||
|
|
@ -197,11 +218,11 @@ export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchesNode(node: AriaNode | string, template: AriaTemplateNode | RegExp | string, depth: number): boolean {
|
function matchesNode(node: AriaNode | string, template: AriaTemplateNode, depth: number): boolean {
|
||||||
if (typeof node === 'string' && (typeof template === 'string' || template instanceof RegExp))
|
if (typeof node === 'string' && template.kind === 'text')
|
||||||
return matchesText(node, template);
|
return matchesTextNode(node, template);
|
||||||
|
|
||||||
if (typeof node === 'object' && typeof template === 'object' && !(template instanceof RegExp)) {
|
if (typeof node === 'object' && template.kind === 'role') {
|
||||||
if (template.role !== 'fragment' && template.role !== node.role)
|
if (template.role !== 'fragment' && template.role !== node.role)
|
||||||
return false;
|
return false;
|
||||||
if (template.checked !== undefined && template.checked !== node.checked)
|
if (template.checked !== undefined && template.checked !== node.checked)
|
||||||
|
|
@ -216,7 +237,7 @@ function matchesNode(node: AriaNode | string, template: AriaTemplateNode | RegEx
|
||||||
return false;
|
return false;
|
||||||
if (template.selected !== undefined && template.selected !== node.selected)
|
if (template.selected !== undefined && template.selected !== node.selected)
|
||||||
return false;
|
return false;
|
||||||
if (!matchesText(node.name, template.name))
|
if (!matchesName(node.name, template))
|
||||||
return false;
|
return false;
|
||||||
if (!containsList(node.children || [], template.children || [], depth))
|
if (!containsList(node.children || [], template.children || [], depth))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -225,7 +246,7 @@ function matchesNode(node: AriaNode | string, template: AriaTemplateNode | RegEx
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function containsList(children: (AriaNode | string)[], template: (AriaTemplateNode | RegExp | string)[], depth: number): boolean {
|
function containsList(children: (AriaNode | string)[], template: AriaTemplateNode[], depth: number): boolean {
|
||||||
if (template.length > children.length)
|
if (template.length > children.length)
|
||||||
return false;
|
return false;
|
||||||
const cc = children.slice();
|
const cc = children.slice();
|
||||||
|
|
@ -363,6 +384,9 @@ function includeText(node: AriaNode, text: string): boolean {
|
||||||
if (!node.name)
|
if (!node.name)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (node.name.length > text.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Figure out if text adds any value.
|
// Figure out if text adds any value.
|
||||||
const substr = longestCommonSubstring(text, node.name);
|
const substr = longestCommonSubstring(text, node.name);
|
||||||
let filtered = text;
|
let filtered = text;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import { EXPECTED_COLOR } from '../common/expectBundle';
|
||||||
import { callLogText } from '../util';
|
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';
|
||||||
|
|
||||||
export async function toMatchAriaSnapshot(
|
export async function toMatchAriaSnapshot(
|
||||||
this: ExpectMatcherState,
|
this: ExpectMatcherState,
|
||||||
|
|
@ -70,11 +71,7 @@ export async function toMatchAriaSnapshot(
|
||||||
const timeout = options.timeout ?? this.timeout;
|
const timeout = options.timeout ?? this.timeout;
|
||||||
expected = unshift(expected);
|
expected = unshift(expected);
|
||||||
const { matches: pass, received, log, timedOut } = await receiver._expect('to.match.aria', { expectedValue: expected, isNot: this.isNot, timeout });
|
const { matches: pass, received, log, timedOut } = await receiver._expect('to.match.aria', { expectedValue: expected, isNot: this.isNot, timeout });
|
||||||
const typedReceived = received as {
|
const typedReceived = received as MatcherReceived | typeof kNoElementsFoundError;
|
||||||
raw: string;
|
|
||||||
noText: string;
|
|
||||||
regex: string;
|
|
||||||
} | typeof kNoElementsFoundError;
|
|
||||||
|
|
||||||
const messagePrefix = matcherHint(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined);
|
const messagePrefix = matcherHint(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined);
|
||||||
const notFound = typedReceived === kNoElementsFoundError;
|
const notFound = typedReceived === kNoElementsFoundError;
|
||||||
|
|
@ -94,12 +91,12 @@ export async function toMatchAriaSnapshot(
|
||||||
if (notFound)
|
if (notFound)
|
||||||
return messagePrefix + `Expected: not ${this.utils.printExpected(escapedExpected)}\nReceived: ${escapedReceived}` + callLogText(log);
|
return messagePrefix + `Expected: not ${this.utils.printExpected(escapedExpected)}\nReceived: ${escapedReceived}` + callLogText(log);
|
||||||
const printedReceived = printReceivedStringContainExpectedSubstring(escapedReceived, escapedReceived.indexOf(escapedExpected), escapedExpected.length);
|
const printedReceived = printReceivedStringContainExpectedSubstring(escapedReceived, escapedReceived.indexOf(escapedExpected), escapedExpected.length);
|
||||||
return messagePrefix + `Expected: not ${this.utils.printExpected(escapedExpected)}\nReceived string: ${printedReceived}` + callLogText(log);
|
return messagePrefix + `Expected: not ${this.utils.printExpected(escapedExpected)}\nReceived: ${printedReceived}` + callLogText(log);
|
||||||
} else {
|
} else {
|
||||||
const labelExpected = `Expected`;
|
const labelExpected = `Expected`;
|
||||||
if (notFound)
|
if (notFound)
|
||||||
return messagePrefix + `${labelExpected}: ${this.utils.printExpected(escapedExpected)}\nReceived: ${escapedReceived}` + callLogText(log);
|
return messagePrefix + `${labelExpected}: ${this.utils.printExpected(escapedExpected)}\nReceived: ${escapedReceived}` + callLogText(log);
|
||||||
return messagePrefix + this.utils.printDiffOrStringify(escapedExpected, escapedReceived, labelExpected, 'Received string', false) + callLogText(log);
|
return messagePrefix + this.utils.printDiffOrStringify(escapedExpected, escapedReceived, labelExpected, 'Received', false) + callLogText(log);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -396,8 +396,8 @@ test('expected formatter', async ({ page }) => {
|
||||||
|
|
||||||
expect(stripAnsi(error.message)).toContain(`
|
expect(stripAnsi(error.message)).toContain(`
|
||||||
Locator: locator('body')
|
Locator: locator('body')
|
||||||
- Expected - 2
|
- Expected - 2
|
||||||
+ Received string + 3
|
+ Received + 3
|
||||||
|
|
||||||
- - heading "todos"
|
- - heading "todos"
|
||||||
- - textbox "Wrong text"
|
- - textbox "Wrong text"
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/test": "1.49.0-alpha-2024-10-26"
|
"@playwright/test": "1.49.0-alpha-2024-10-29"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.49.0-alpha-2024-10-26",
|
"version": "1.49.0-alpha-2024-10-29",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-26.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-29.tgz",
|
||||||
"integrity": "sha512-EUl8wIsAWVJJlX2ynKdY1KxRs44Yz9MPDmN8AH6HIdwazSRe1ML46kaM3V49gQvMVMo5JZfuXnRzbtYDMFpKYA==",
|
"integrity": "sha512-JyT6BHjuJl5Iv91PvaYa1RXRQfSwHk1Abq/hzYFpebQQuKKNr3pck55qmih39+S/bGsuYW6XdzqAX+CfknR3sA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.49.0-alpha-2024-10-26"
|
"playwright": "1.49.0-alpha-2024-10-29"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
|
@ -36,11 +36,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.49.0-alpha-2024-10-26",
|
"version": "1.49.0-alpha-2024-10-29",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-26.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-29.tgz",
|
||||||
"integrity": "sha512-1qh/6z4UdWv7qMocNQmUMbvZAXzzS93jckUzjGr0mWMn9rs4QavHhuK0s2HIS0hLB+t5T1+NBUpHudWzeasudA==",
|
"integrity": "sha512-ypwaWQwpxAiB5JEz4ACrztZsII4BdD5zOuAnjPtiXZtemSZNwxxY7phKlX8nLUlGwWDpb8aGe9tBcxoyrcFIww==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.49.0-alpha-2024-10-26"
|
"playwright-core": "1.49.0-alpha-2024-10-29"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
|
@ -53,9 +53,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.49.0-alpha-2024-10-26",
|
"version": "1.49.0-alpha-2024-10-29",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-26.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-29.tgz",
|
||||||
"integrity": "sha512-ELIdRRHkdzkHP7siPcFSE9jBLRnDHE1l3UigIgEzVN9o34yGBgH8TAkC2uK1M8Jrkomc3jKQm5faiBsimu0XEQ==",
|
"integrity": "sha512-pJmBdOnVFzBzA6Jo1q7FtJferyLK0a2cNZGbuOMO0LOPWY7FOT91225TYZ9a1qgaYMav+uudmYw6im/qjEwmIQ==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
},
|
},
|
||||||
|
|
@ -66,11 +66,11 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/test": {
|
"@playwright/test": {
|
||||||
"version": "1.49.0-alpha-2024-10-26",
|
"version": "1.49.0-alpha-2024-10-29",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-26.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-29.tgz",
|
||||||
"integrity": "sha512-EUl8wIsAWVJJlX2ynKdY1KxRs44Yz9MPDmN8AH6HIdwazSRe1ML46kaM3V49gQvMVMo5JZfuXnRzbtYDMFpKYA==",
|
"integrity": "sha512-JyT6BHjuJl5Iv91PvaYa1RXRQfSwHk1Abq/hzYFpebQQuKKNr3pck55qmih39+S/bGsuYW6XdzqAX+CfknR3sA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"playwright": "1.49.0-alpha-2024-10-26"
|
"playwright": "1.49.0-alpha-2024-10-29"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fsevents": {
|
"fsevents": {
|
||||||
|
|
@ -80,18 +80,18 @@
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"playwright": {
|
"playwright": {
|
||||||
"version": "1.49.0-alpha-2024-10-26",
|
"version": "1.49.0-alpha-2024-10-29",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-26.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-29.tgz",
|
||||||
"integrity": "sha512-1qh/6z4UdWv7qMocNQmUMbvZAXzzS93jckUzjGr0mWMn9rs4QavHhuK0s2HIS0hLB+t5T1+NBUpHudWzeasudA==",
|
"integrity": "sha512-ypwaWQwpxAiB5JEz4ACrztZsII4BdD5zOuAnjPtiXZtemSZNwxxY7phKlX8nLUlGwWDpb8aGe9tBcxoyrcFIww==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "2.3.2",
|
"fsevents": "2.3.2",
|
||||||
"playwright-core": "1.49.0-alpha-2024-10-26"
|
"playwright-core": "1.49.0-alpha-2024-10-29"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"playwright-core": {
|
"playwright-core": {
|
||||||
"version": "1.49.0-alpha-2024-10-26",
|
"version": "1.49.0-alpha-2024-10-29",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-26.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-29.tgz",
|
||||||
"integrity": "sha512-ELIdRRHkdzkHP7siPcFSE9jBLRnDHE1l3UigIgEzVN9o34yGBgH8TAkC2uK1M8Jrkomc3jKQm5faiBsimu0XEQ=="
|
"integrity": "sha512-pJmBdOnVFzBzA6Jo1q7FtJferyLK0a2cNZGbuOMO0LOPWY7FOT91225TYZ9a1qgaYMav+uudmYw6im/qjEwmIQ=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/test": "1.49.0-alpha-2024-10-26"
|
"@playwright/test": "1.49.0-alpha-2024-10-29"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue