diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index e1461dbab79d3..e8e5b246c7186 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -46,7 +46,7 @@ import type { RegisteredListener } from '@utils/eventsHelper'; const recorderSymbol = Symbol('recorderSymbol'); type BindingSource = { frame: Frame, page: Page }; -type RecorderParams = channels.BrowserContextEnableRecorderParams & { hideToolbar?: boolean }; +type RecorderParams = channels.BrowserContextEnableRecorderParams & { hideToolbar?: boolean, __testHookFrameSelectorTimeout?: number }; export const RecorderEvent = { PausedStateChanged: 'pausedStateChanged', @@ -194,7 +194,7 @@ export class Recorder extends EventEmitter implements Instrume }); await this._context.exposeBinding(progress, '__pw_recorderElementPicked', async ({ frame }, elementInfo: ElementInfo) => { - const selector = await buildFullSelectorForFrame(progress, frame, elementInfo.selector); + const selector = await buildFullSelectorForFrame(progress, frame, elementInfo.selector, this._params.__testHookFrameSelectorTimeout); this.emit(RecorderEvent.ElementPicked, { selector, ariaSnapshot: elementInfo.ariaSnapshot }, true); }); @@ -540,15 +540,15 @@ export class Recorder extends EventEmitter implements Instrume } private async _performAction(progress: Progress, frame: Frame, action: actions.PerformableAction) { - const selector = await buildFullSelectorForFrame(progress, frame, action.selector); + const selector = await buildFullSelectorForFrame(progress, frame, action.selector, this._params.__testHookFrameSelectorTimeout); await performAction(progress, frame._page.mainFrame(), { ...action, selector }); } private async _recordAction(progress: Progress, frame: Frame, action: actions.Action, preconditionSelector?: string) { if (preconditionSelector) - this._signalProcessor.signal(frame, { name: 'expect', selector: await buildFullSelectorForFrame(progress, frame, preconditionSelector) }); + this._signalProcessor.signal(frame, { name: 'expect', selector: await buildFullSelectorForFrame(progress, frame, preconditionSelector, this._params.__testHookFrameSelectorTimeout) }); if ('selector' in action) - action.selector = await buildFullSelectorForFrame(progress, frame, action.selector); + action.selector = await buildFullSelectorForFrame(progress, frame, action.selector, this._params.__testHookFrameSelectorTimeout); const actionInContext: actions.ActionInContext = { pageGuid: frame._page.guid, action, diff --git a/packages/playwright-core/src/server/recorder/recorderUtils.ts b/packages/playwright-core/src/server/recorder/recorderUtils.ts index eb18550169e0e..5d1523a5def09 100644 --- a/packages/playwright-core/src/server/recorder/recorderUtils.ts +++ b/packages/playwright-core/src/server/recorder/recorderUtils.ts @@ -28,8 +28,8 @@ function buildFullSelector(framePath: string[], selector: string) { return [...framePath, selector].join(' >> internal:control=enter-frame >> '); } -export async function buildFullSelectorForFrame(progress: Progress, frame: Frame, selector: string): Promise { - const framePath = await generateFrameSelector(progress, frame); +export async function buildFullSelectorForFrame(progress: Progress, frame: Frame, selector: string, timeout = 2000): Promise { + const framePath = await generateFrameSelector(progress, frame, timeout); if (!frame._page.browserContext._options.pierceFrames || !framePath.length) return buildFullSelector(framePath, selector); @@ -40,7 +40,7 @@ export async function buildFullSelectorForFrame(progress: Progress, frame: Frame if (await resolvesToFrame(progress, candidate, frame)) return candidate; } - }, monotonicTime() + 2000)); + }, monotonicTime() + timeout)); if (!result.timedOut && result.result) return result.result; @@ -83,14 +83,14 @@ export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus) } -async function generateFrameSelector(progress: Progress, frame: Frame): Promise { +async function generateFrameSelector(progress: Progress, frame: Frame, timeout: number): Promise { const selectorPromises: Promise[] = []; progress.setAllowConcurrentOrNestedRaces(true); while (frame) { const parent = frame.parentFrame(); if (!parent) break; - selectorPromises.push(generateFrameSelectorInParent(progress, parent, frame)); + selectorPromises.push(generateFrameSelectorInParent(progress, parent, frame, timeout)); frame = parent; } const result = await Promise.all(selectorPromises); @@ -98,7 +98,7 @@ async function generateFrameSelector(progress: Progress, frame: Frame): Promise< return result.reverse(); } -async function generateFrameSelectorInParent(prgoress: Progress, parent: Frame, frame: Frame): Promise { +async function generateFrameSelectorInParent(prgoress: Progress, parent: Frame, frame: Frame, timeout: number): Promise { const result = await raceAgainstDeadline(async () => { try { const frameElement = await frame.frameElement(prgoress); @@ -112,7 +112,7 @@ async function generateFrameSelectorInParent(prgoress: Progress, parent: Frame, return selector; } catch (e) { } - }, monotonicTime() + 2000); + }, monotonicTime() + timeout); if (!result.timedOut && result.result) return result.result; diff --git a/tests/library/inspector/cli-codegen-3.spec.ts b/tests/library/inspector/cli-codegen-3.spec.ts index 5d3852b89ce1b..2a170b055c828 100644 --- a/tests/library/inspector/cli-codegen-3.spec.ts +++ b/tests/library/inspector/cli-codegen-3.spec.ts @@ -1166,8 +1166,10 @@ test.describe('cli codegen with pierceFrames', () => { await page.getByText('Hello2').click();`); }); - test('should prepend the frame selector to disambiguate', async ({ openRecorder, server }) => { - const { page, recorder } = await openRecorder(); + test('should prepend the frame selector to disambiguate', async ({ openRecorder, server, browserName, isMac }) => { + const { page, recorder } = await openRecorder({ + __testHookFrameSelectorTimeout: browserName === 'firefox' && isMac && process.arch === 'x64' ? 10_000 : undefined, + }); await recorder.setContentAndWait(` diff --git a/tests/library/inspector/inspectorTest.ts b/tests/library/inspector/inspectorTest.ts index 9fa145a651b01..d6e8c9b87a832 100644 --- a/tests/library/inspector/inspectorTest.ts +++ b/tests/library/inspector/inspectorTest.ts @@ -27,7 +27,7 @@ export { expect } from '@playwright/test'; type CLITestArgs = { recorderPageGetter: () => Promise; closeRecorder: () => Promise; - openRecorder: (options?: { testIdAttributeName?: string, language?: string }) => Promise<{ recorder: Recorder, page: Page }>; + openRecorder: (options?: { testIdAttributeName?: string, language?: string, __testHookFrameSelectorTimeout?: number }) => Promise<{ recorder: Recorder, page: Page }>; runCLI: (args: string[]) => CLIMock; };