Skip to content

Commit fb9f3d8

Browse files
committed
move logic out from dispatcher
1 parent 382dd0c commit fb9f3d8

2 files changed

Lines changed: 38 additions & 33 deletions

File tree

packages/playwright-core/src/server/dispatchers/pageDispatcher.ts

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,9 @@ import { WebSocketRouteDispatcher } from './webSocketRouteDispatcher';
2828
import { SdkObject } from '../instrumentation';
2929
import { deserializeURLMatch, urlMatches } from '../../utils/isomorphic/urlMatch';
3030
import { PageAgentDispatcher } from './pageAgentDispatcher';
31-
import { Recorder, RecorderEvent } from '../recorder';
32-
import { ManualPromise } from '../../utils/isomorphic/manualPromise';
33-
import { eventsHelper } from '../utils/eventsHelper';
31+
import { Recorder } from '../recorder';
3432
import { isUnderTest } from '../utils/debug';
3533

36-
import type { ElementInfo } from '@recorder/recorderTypes';
37-
import type { RegisteredListener } from '../utils/eventsHelper';
38-
3934
import type { Artifact } from '../artifact';
4035
import type { BrowserContext } from '../browserContext';
4136
import type { CRCoverage } from '../chromium/crCoverage';
@@ -355,33 +350,8 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
355350
if (!this._page.browserContext._browser.options.headful && !isUnderTest())
356351
throw new Error('pickLocator() is only available in headed mode');
357352
const recorder = await Recorder.forContext(this._page.browserContext, { omitCallTracking: true, hideToolbar: true });
358-
const selectorPromise = new ManualPromise<string>();
359-
let recorderChangedState = false;
360-
const onElementPicked = (elementInfo: ElementInfo) => {
361-
selectorPromise.resolve(elementInfo.selector);
362-
};
363-
const onModeChanged = () => {
364-
recorderChangedState = true;
365-
selectorPromise.reject(new Error('Locator picking was cancelled'));
366-
};
367-
const onContextClosed = () => {
368-
recorderChangedState = true;
369-
selectorPromise.reject(new Error('Context was closed'));
370-
};
371-
recorder.setMode('inspecting');
372-
const listeners: RegisteredListener[] = [
373-
eventsHelper.addEventListener(recorder, RecorderEvent.ElementPicked, onElementPicked),
374-
eventsHelper.addEventListener(recorder, RecorderEvent.ModeChanged, onModeChanged),
375-
eventsHelper.addEventListener(recorder, RecorderEvent.ContextClosed, onContextClosed),
376-
];
377-
try {
378-
const selector = await progress.race(selectorPromise);
379-
return { selector };
380-
} finally {
381-
eventsHelper.removeEventListeners(listeners);
382-
if (!recorderChangedState)
383-
recorder.setMode('none');
384-
}
353+
const selector = await recorder.pickLocator(progress);
354+
return { selector };
385355
}
386356

387357
async videoStart(params: channels.PageVideoStartParams, progress: Progress): Promise<channels.PageVideoStartResult> {

packages/playwright-core/src/server/recorder.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import { buildFullSelector, generateFrameSelector, metadataToCallLog } from './r
2424
import { locatorOrSelectorAsSelector } from '../utils/isomorphic/locatorParser';
2525
import { stringifySelector } from '../utils/isomorphic/selectorParser';
2626
import { ProgressController } from './progress';
27+
import { ManualPromise } from '../utils/isomorphic/manualPromise';
28+
2729
import { RecorderSignalProcessor } from './recorder/recorderSignalProcessor';
2830
import * as rawRecorderSource from './../generated/pollingRecorderSource';
2931
import { eventsHelper, monotonicTime } from './../utils';
@@ -35,6 +37,7 @@ import type { Language } from './codegen/types';
3537
import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
3638
import type { Point } from '../utils/isomorphic/types';
3739
import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot';
40+
import type { Progress } from './progress';
3841
import type * as channels from '@protocol/channels';
3942
import type * as actions from '@recorder/actions';
4043
import type { CallLog, CallLogStatus, ElementInfo, Mode, OverlayState, Source, UIState } from '@recorder/recorderTypes';
@@ -262,6 +265,38 @@ export class Recorder extends EventEmitter<RecorderEventMap> implements Instrume
262265
this._refreshOverlay();
263266
}
264267

268+
async pickLocator(progress: Progress): Promise<string> {
269+
const selectorPromise = new ManualPromise<string>();
270+
let recorderChangedState = false;
271+
const onElementPicked = (elementInfo: ElementInfo) => {
272+
selectorPromise.resolve(elementInfo.selector);
273+
};
274+
const onModeChanged = () => {
275+
recorderChangedState = true;
276+
selectorPromise.reject(new Error('Locator picking was cancelled'));
277+
};
278+
const onContextClosed = () => {
279+
recorderChangedState = true;
280+
selectorPromise.reject(new Error('Context was closed'));
281+
};
282+
// Register listeners after setMode() to avoid consuming the ModeChanged
283+
// event that fires synchronously from our own setMode('inspecting') call.
284+
this.setMode('inspecting');
285+
const listeners: RegisteredListener[] = [
286+
eventsHelper.addEventListener(this, RecorderEvent.ElementPicked, onElementPicked),
287+
eventsHelper.addEventListener(this, RecorderEvent.ModeChanged, onModeChanged),
288+
eventsHelper.addEventListener(this, RecorderEvent.ContextClosed, onContextClosed),
289+
];
290+
try {
291+
return await progress.race(selectorPromise);
292+
} finally {
293+
// Remove listeners before setMode('none') to avoid triggering onModeChanged.
294+
eventsHelper.removeEventListeners(listeners);
295+
if (!recorderChangedState)
296+
this.setMode('none');
297+
}
298+
}
299+
265300
url(): string | undefined {
266301
const page = this._context.pages()[0];
267302
return page?.mainFrame().url();

0 commit comments

Comments
 (0)