From 0cdc7ee1a3b392d9ab37618e2ee32bc1b929caa3 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 23 Sep 2024 08:42:18 -0700 Subject: [PATCH] chore: extract polling recorder (#32749) We are reusing recorder in a snapshot tab, no need for the polling harness to be there. --- .../injected/recorder/pollingRecorder.ts | 91 +++++++++++++++++++ .../src/server/injected/recorder/recorder.ts | 73 +-------------- .../src/server/recorder/DEPS.list | 2 +- .../src/server/recorder/contextRecorder.ts | 2 +- utils/generate_injected.js | 2 +- 5 files changed, 95 insertions(+), 75 deletions(-) create mode 100644 packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts diff --git a/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts b/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts new file mode 100644 index 0000000000..57627f3723 --- /dev/null +++ b/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes'; +import type * as actions from '../../recorder/recorderActions'; +import type { InjectedScript } from '../injectedScript'; +import { Recorder } from './recorder'; +import type { RecorderDelegate } from './recorder'; + +interface Embedder { + __pw_recorderPerformAction(action: actions.PerformOnRecordAction): Promise; + __pw_recorderRecordAction(action: actions.Action): Promise; + __pw_recorderState(): Promise; + __pw_recorderSetSelector(selector: string): Promise; + __pw_recorderSetMode(mode: Mode): Promise; + __pw_recorderSetOverlayState(state: OverlayState): Promise; + __pw_refreshOverlay(): void; +} + +export class PollingRecorder implements RecorderDelegate { + private _recorder: Recorder; + private _embedder: Embedder; + private _pollRecorderModeTimer: number | undefined; + + constructor(injectedScript: InjectedScript) { + this._recorder = new Recorder(injectedScript); + this._embedder = injectedScript.window as any; + + injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners()); + + const refreshOverlay = () => { + this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console + }; + this._embedder.__pw_refreshOverlay = refreshOverlay; + refreshOverlay(); + } + + private async _pollRecorderMode() { + const pollPeriod = 1000; + if (this._pollRecorderModeTimer) + clearTimeout(this._pollRecorderModeTimer); + const state = await this._embedder.__pw_recorderState().catch(() => {}); + if (!state) { + this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); + return; + } + const win = this._recorder.document.defaultView!; + if (win.top !== win) { + // Only show action point in the main frame, since it is relative to the page's viewport. + // Otherwise we'll see multiple action points at different locations. + state.actionPoint = undefined; + } + this._recorder.setUIState(state, this); + this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); + } + + async performAction(action: actions.PerformOnRecordAction) { + await this._embedder.__pw_recorderPerformAction(action); + } + + async recordAction(action: actions.Action): Promise { + await this._embedder.__pw_recorderRecordAction(action); + } + + async setSelector(selector: string): Promise { + await this._embedder.__pw_recorderSetSelector(selector); + } + + async setMode(mode: Mode): Promise { + await this._embedder.__pw_recorderSetMode(mode); + } + + async setOverlayState(state: OverlayState): Promise { + await this._embedder.__pw_recorderSetOverlayState(state); + } +} + +export default PollingRecorder; diff --git a/packages/playwright-core/src/server/injected/recorder/recorder.ts b/packages/playwright-core/src/server/injected/recorder/recorder.ts index 76d5791b64..c255a14b08 100644 --- a/packages/playwright-core/src/server/injected/recorder/recorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -21,9 +21,8 @@ import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes'; import type { ElementText } from '../selectorUtils'; import type { Highlight, HighlightOptions } from '../highlight'; import clipPaths from './clipPaths'; -import type { SimpleDomNode } from '../simpleDom'; -interface RecorderDelegate { +export interface RecorderDelegate { performAction?(action: actions.PerformOnRecordAction): Promise; recordAction?(action: actions.Action): Promise; setSelector?(selector: string): Promise; @@ -1457,73 +1456,3 @@ function createSvgElement(doc: Document, { tagName, attrs, children }: SvgJson): return elem; } - -interface Embedder { - __pw_recorderPerformAction(action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode): Promise; - __pw_recorderRecordAction(action: actions.Action, simpleDomNode?: SimpleDomNode): Promise; - __pw_recorderState(): Promise; - __pw_recorderSetSelector(selector: string): Promise; - __pw_recorderSetMode(mode: Mode): Promise; - __pw_recorderSetOverlayState(state: OverlayState): Promise; - __pw_refreshOverlay(): void; -} - -export class PollingRecorder implements RecorderDelegate { - private _recorder: Recorder; - private _embedder: Embedder; - private _pollRecorderModeTimer: number | undefined; - - constructor(injectedScript: InjectedScript) { - this._recorder = new Recorder(injectedScript); - this._embedder = injectedScript.window as any; - - injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners()); - - const refreshOverlay = () => { - this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console - }; - this._embedder.__pw_refreshOverlay = refreshOverlay; - refreshOverlay(); - } - - private async _pollRecorderMode() { - const pollPeriod = 1000; - if (this._pollRecorderModeTimer) - clearTimeout(this._pollRecorderModeTimer); - const state = await this._embedder.__pw_recorderState().catch(() => {}); - if (!state) { - this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); - return; - } - const win = this._recorder.document.defaultView!; - if (win.top !== win) { - // Only show action point in the main frame, since it is relative to the page's viewport. - // Otherwise we'll see multiple action points at different locations. - state.actionPoint = undefined; - } - this._recorder.setUIState(state, this); - this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); - } - - async performAction(action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode) { - await this._embedder.__pw_recorderPerformAction(action, simpleDomNode); - } - - async recordAction(action: actions.Action, simpleDomNode?: SimpleDomNode): Promise { - await this._embedder.__pw_recorderRecordAction(action, simpleDomNode); - } - - async setSelector(selector: string): Promise { - await this._embedder.__pw_recorderSetSelector(selector); - } - - async setMode(mode: Mode): Promise { - await this._embedder.__pw_recorderSetMode(mode); - } - - async setOverlayState(state: OverlayState): Promise { - await this._embedder.__pw_recorderSetOverlayState(state); - } -} - -export default PollingRecorder; diff --git a/packages/playwright-core/src/server/recorder/DEPS.list b/packages/playwright-core/src/server/recorder/DEPS.list index f3bbfc23bf..85ae7c9152 100644 --- a/packages/playwright-core/src/server/recorder/DEPS.list +++ b/packages/playwright-core/src/server/recorder/DEPS.list @@ -5,7 +5,7 @@ ../isomorphic/** ../registry/** ../../common/ -../../generated/recorderSource.ts +../../generated/pollingRecorderSource.ts ../../protocol/ ../../utils/** ../../utilsBundle.ts diff --git a/packages/playwright-core/src/server/recorder/contextRecorder.ts b/packages/playwright-core/src/server/recorder/contextRecorder.ts index dc38866167..694393b860 100644 --- a/packages/playwright-core/src/server/recorder/contextRecorder.ts +++ b/packages/playwright-core/src/server/recorder/contextRecorder.ts @@ -17,7 +17,7 @@ import type * as channels from '@protocol/channels'; import type { Source } from '@recorder/recorderTypes'; import { EventEmitter } from 'events'; -import * as recorderSource from '../../generated/recorderSource'; +import * as recorderSource from '../../generated/pollingRecorderSource'; import { eventsHelper, monotonicTime, quoteCSSAttributeValue, type RegisteredListener } from '../../utils'; import { raceAgainstDeadline } from '../../utils/timeoutRunner'; import { BrowserContext } from '../browserContext'; diff --git a/utils/generate_injected.js b/utils/generate_injected.js index cb56ce2db5..bffef1cfaa 100644 --- a/utils/generate_injected.js +++ b/utils/generate_injected.js @@ -45,7 +45,7 @@ const injectedScripts = [ true, ], [ - path.join(ROOT, 'packages', 'playwright-core', 'src', 'server', 'injected', 'recorder', 'recorder.ts'), + path.join(ROOT, 'packages', 'playwright-core', 'src', 'server', 'injected', 'recorder', 'pollingRecorder.ts'), path.join(ROOT, 'packages', 'playwright-core', 'lib', 'server', 'injected', 'packed'), path.join(ROOT, 'packages', 'playwright-core', 'src', 'generated'), true,