From 1f2eb499d20e85f3d5fd626b0272a755b07eb44f Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Fri, 10 Jan 2025 19:31:47 +0000 Subject: [PATCH] fix(aria snapshots): normalize whitespace (#34285) --- .../src/server/injected/ariaSnapshot.ts | 8 +++--- .../src/utils/isomorphic/ariaSnapshot.ts | 2 +- tests/page/page-aria-snapshot.spec.ts | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/playwright-core/src/server/injected/ariaSnapshot.ts b/packages/playwright-core/src/server/injected/ariaSnapshot.ts index 84537cddd7d94..ee27cbceeb35e 100644 --- a/packages/playwright-core/src/server/injected/ariaSnapshot.ts +++ b/packages/playwright-core/src/server/injected/ariaSnapshot.ts @@ -16,7 +16,7 @@ import * as roleUtils from './roleUtils'; import { getElementComputedStyle } from './domUtils'; -import { escapeRegExp, longestCommonSubstring } from '@isomorphic/stringUtils'; +import { escapeRegExp, longestCommonSubstring, normalizeWhiteSpace } from '@isomorphic/stringUtils'; import { yamlEscapeKeyIfNeeded, yamlEscapeValueIfNeeded } from './yaml'; import type { AriaProps, AriaRole, AriaTemplateNode, AriaTemplateRoleNode, AriaTemplateTextNode } from '@isomorphic/ariaSnapshot'; @@ -137,7 +137,7 @@ function toAriaNode(element: Element): AriaNode | null { if (!role || role === 'presentation' || role === 'none') return null; - const name = roleUtils.getElementAccessibleName(element, false) || ''; + const name = normalizeWhiteSpace(roleUtils.getElementAccessibleName(element, false) || ''); const result: AriaNode = { role, name, children: [], element }; if (roleUtils.kAriaCheckedRoles.includes(role)) @@ -170,7 +170,7 @@ function normalizeStringChildren(rootA11yNode: AriaNode) { const flushChildren = (buffer: string[], normalizedChildren: (AriaNode | string)[]) => { if (!buffer.length) return; - const text = normalizeWhitespaceWithin(buffer.join('')).trim(); + const text = normalizeWhiteSpace(buffer.join('')); if (text) normalizedChildren.push(text); buffer.length = 0; @@ -196,8 +196,6 @@ function normalizeStringChildren(rootA11yNode: AriaNode) { visit(rootA11yNode); } -const normalizeWhitespaceWithin = (text: string) => text.replace(/[\u200b\s\t\r\n]+/g, ' '); - function matchesText(text: string, template: RegExp | string | undefined): boolean { if (!template) return true; diff --git a/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts b/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts index 67db4651494ba..59b26ec04929a 100644 --- a/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts +++ b/packages/playwright-core/src/utils/isomorphic/ariaSnapshot.ts @@ -212,7 +212,7 @@ class KeyParser { const ch = this._peek(); if (ch === '"') { this._next(); - return this._readString(); + return normalizeWhitespace(this._readString()); } if (ch === '/') { diff --git a/tests/page/page-aria-snapshot.spec.ts b/tests/page/page-aria-snapshot.spec.ts index b7bb77091aef1..878262400d950 100644 --- a/tests/page/page-aria-snapshot.spec.ts +++ b/tests/page/page-aria-snapshot.spec.ts @@ -509,6 +509,32 @@ it('should escape yaml text in text nodes', async ({ page }) => { `); }); +it('should normalize whitespace', async ({ page }) => { + await page.setContent(` +
+ one \n two link  \n 1 +
+ + `); + + await checkAndMatchSnapshot(page.locator('body'), ` + - group: + - text: one two + - link "link 1" + - textbox: hello world + `); + + // Weird whitespace in the template should be normalized. + await expect(page.locator('body')).toMatchAriaSnapshot(` + - group: + - text: | + one + two + - link " link 1 " + - textbox: hello world + `); +}); + it('should handle long strings', async ({ page }) => { const s = 'a'.repeat(10000); await page.setContent(`