chore: cache normalized whitespaces in recorder (#31349)

Reference: https://github.com/microsoft/playwright/issues/31254

On the web page from the bug it reduces time to compute selectors by 8x:

**Before:**
<img width="549" alt="before"
src="https://github.com/microsoft/playwright/assets/9798949/f4482860-29d5-4643-92ab-b360a702f232">

**After:**
<img width="580" alt="after"
src="https://github.com/microsoft/playwright/assets/9798949/b6aca6a1-9306-4041-9042-d504dce1c33a">
This commit is contained in:
Yury Semikhatsky 2024-06-17 18:20:15 -07:00 committed by GitHub
parent 5443b66636
commit 9e6772818e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 15 additions and 4 deletions

View file

@ -33,7 +33,7 @@ import { getChecked, getAriaDisabled, getAriaRole, getElementAccessibleName, get
import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } from './layoutSelectorUtils'; import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } from './layoutSelectorUtils';
import { asLocator } from '../../utils/isomorphic/locatorGenerators'; import { asLocator } from '../../utils/isomorphic/locatorGenerators';
import type { Language } from '../../utils/isomorphic/locatorGenerators'; import type { Language } from '../../utils/isomorphic/locatorGenerators';
import { normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils'; import { cacheNormalizedWhitespaces, normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils';
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any }; export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
@ -66,7 +66,7 @@ export class InjectedScript {
// eslint-disable-next-line no-restricted-globals // eslint-disable-next-line no-restricted-globals
readonly window: Window & typeof globalThis; readonly window: Window & typeof globalThis;
readonly document: Document; readonly document: Document;
readonly utils = { isInsideScope, elementText, asLocator, normalizeWhiteSpace }; readonly utils = { isInsideScope, elementText, asLocator, normalizeWhiteSpace, cacheNormalizedWhitespaces };
// eslint-disable-next-line no-restricted-globals // eslint-disable-next-line no-restricted-globals
constructor(window: Window & typeof globalThis, isUnderTest: boolean, sdkLanguage: Language, testIdAttributeNameForStrictErrorAndConsoleCodegen: string, stableRafCount: number, browserName: string, customEngines: { name: string, engine: SelectorEngine }[]) { constructor(window: Window & typeof globalThis, isUnderTest: boolean, sdkLanguage: Language, testIdAttributeNameForStrictErrorAndConsoleCodegen: string, stableRafCount: number, browserName: string, customEngines: { name: string, engine: SelectorEngine }[]) {

View file

@ -973,7 +973,7 @@ export class Recorder {
body[data-pw-cursor=text] *, body[data-pw-cursor=text] *::after { cursor: text !important; } body[data-pw-cursor=text] *, body[data-pw-cursor=text] *::after { cursor: text !important; }
`); `);
this.installListeners(); this.installListeners();
injectedScript.utils.cacheNormalizedWhitespaces();
if (injectedScript.isUnderTest) if (injectedScript.isUnderTest)
console.error('Recorder script ready for test'); // eslint-disable-line no-console console.error('Recorder script ready for test'); // eslint-disable-line no-console
} }

View file

@ -67,8 +67,19 @@ function cssEscapeOne(s: string, i: number): string {
return '\\' + s.charAt(i); return '\\' + s.charAt(i);
} }
let normalizedWhitespaceCache: Map<string, string> | undefined;
export function cacheNormalizedWhitespaces() {
normalizedWhitespaceCache = new Map();
}
export function normalizeWhiteSpace(text: string): string { export function normalizeWhiteSpace(text: string): string {
return text.replace(/\u200b/g, '').trim().replace(/\s+/g, ' '); let result = normalizedWhitespaceCache?.get(text);
if (result === undefined) {
result = text.replace(/\u200b/g, '').trim().replace(/\s+/g, ' ');
normalizedWhitespaceCache?.set(text, result);
}
return result;
} }
export function normalizeEscapedRegexQuotes(source: string) { export function normalizeEscapedRegexQuotes(source: string) {