diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index 5cd941d5d42ed..a69414f2b2d80 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -595,7 +595,6 @@ async function codegen(options: Options & { target: string, output?: string, tes device: options.device, saveStorage: options.saveStorage, mode: 'recording', - codegenMode: process.env.PW_RECORDER_IS_TRACE_VIEWER ? 'trace-events' : 'actions', testIdAttributeName, outputFile: outputFile ? path.resolve(outputFile) : undefined, handleSIGINT: false, diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 9b14551fb8316..50e8b4f02ae63 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -970,7 +970,6 @@ scheme.BrowserContextPauseResult = tOptional(tObject({})); scheme.BrowserContextEnableRecorderParams = tObject({ language: tOptional(tString), mode: tOptional(tEnum(['inspecting', 'recording'])), - codegenMode: tOptional(tEnum(['actions', 'trace-events'])), pauseOnNextStatement: tOptional(tBoolean), testIdAttributeName: tOptional(tString), launchOptions: tOptional(tAny), diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 8a835d372669e..ce10daf0139bb 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -130,7 +130,7 @@ export abstract class BrowserContext extends SdkObject { // When PWDEBUG=1, show inspector for each context. if (debugMode() === 'inspector') - await Recorder.show('actions', this, RecorderApp.factory(this), { pauseOnNextStatement: true }); + await Recorder.show(this, RecorderApp.factory(this), { pauseOnNextStatement: true }); // When paused, show inspector. if (this._debugger.isPaused()) diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index 3579f4b3bb150..0dcfb23e0af9b 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -39,7 +39,6 @@ import type { Dialog } from '../dialog'; import type { ConsoleMessage } from '../console'; import { serializeError } from '../errors'; import { ElementHandleDispatcher } from './elementHandlerDispatcher'; -import { RecorderInTraceViewer } from '../recorder/recorderInTraceViewer'; import { RecorderApp } from '../recorder/recorderApp'; import { WebSocketRouteDispatcher } from './webSocketRouteDispatcher'; @@ -301,17 +300,7 @@ export class BrowserContextDispatcher extends Dispatcher { - if (params.codegenMode === 'trace-events') { - await this._context.tracing.start({ - name: 'trace', - snapshots: true, - screenshots: true, - live: true, - }); - await Recorder.show('trace-events', this._context, RecorderInTraceViewer.factory(this._context), params); - } else { - await Recorder.show('actions', this._context, RecorderApp.factory(this._context), params); - } + await Recorder.show(this._context, RecorderApp.factory(this._context), params); } async pause(params: channels.BrowserContextPauseParams, metadata: CallMetadata) { diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index 115639223c0bd..f763d5491b1b2 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -54,33 +54,33 @@ export class Recorder implements InstrumentationListener, IRecorder { static async showInspector(context: BrowserContext, params: channels.BrowserContextEnableRecorderParams, recorderAppFactory: IRecorderAppFactory) { if (isUnderTest()) params.language = process.env.TEST_INSPECTOR_LANGUAGE; - return await Recorder.show('actions', context, recorderAppFactory, params); + return await Recorder.show(context, recorderAppFactory, params); } static showInspectorNoReply(context: BrowserContext, recorderAppFactory: IRecorderAppFactory) { Recorder.showInspector(context, {}, recorderAppFactory).catch(() => {}); } - static show(codegenMode: 'actions' | 'trace-events', context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams): Promise { + static show(context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams): Promise { let recorderPromise = (context as any)[recorderSymbol] as Promise; if (!recorderPromise) { - recorderPromise = Recorder._create(codegenMode, context, recorderAppFactory, params); + recorderPromise = Recorder._create(context, recorderAppFactory, params); (context as any)[recorderSymbol] = recorderPromise; } return recorderPromise; } - private static async _create(codegenMode: 'actions' | 'trace-events', context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams = {}): Promise { - const recorder = new Recorder(codegenMode, context, params); + private static async _create(context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams = {}): Promise { + const recorder = new Recorder(context, params); const recorderApp = await recorderAppFactory(recorder); await recorder._install(recorderApp); return recorder; } - constructor(codegenMode: 'actions' | 'trace-events', context: BrowserContext, params: channels.BrowserContextEnableRecorderParams) { + constructor(context: BrowserContext, params: channels.BrowserContextEnableRecorderParams) { this._mode = params.mode || 'none'; this.handleSIGINT = params.handleSIGINT; - this._contextRecorder = new ContextRecorder(codegenMode, context, params, {}); + this._contextRecorder = new ContextRecorder(context, params, {}); this._context = context; this._omitCallTracking = !!params.omitCallTracking; this._debugger = context.debugger(); diff --git a/packages/playwright-core/src/server/recorder/DEPS.list b/packages/playwright-core/src/server/recorder/DEPS.list index 85ae7c9152053..b130c181dc1ec 100644 --- a/packages/playwright-core/src/server/recorder/DEPS.list +++ b/packages/playwright-core/src/server/recorder/DEPS.list @@ -10,6 +10,3 @@ ../../utils/** ../../utilsBundle.ts ../../zipBundle.ts - -[recorderInTraceViewer.ts] -../trace/viewer/traceViewer.ts diff --git a/packages/playwright-core/src/server/recorder/contextRecorder.ts b/packages/playwright-core/src/server/recorder/contextRecorder.ts index 9b641e96f83df..292271acd5832 100644 --- a/packages/playwright-core/src/server/recorder/contextRecorder.ts +++ b/packages/playwright-core/src/server/recorder/contextRecorder.ts @@ -54,11 +54,9 @@ export class ContextRecorder extends EventEmitter { private _throttledOutputFile: ThrottledFile | null = null; private _orderedLanguages: LanguageGenerator[] = []; private _listeners: RegisteredListener[] = []; - private _codegenMode: 'actions' | 'trace-events'; - constructor(codegenMode: 'actions' | 'trace-events', context: BrowserContext, params: channels.BrowserContextEnableRecorderParams, delegate: ContextRecorderDelegate) { + constructor(context: BrowserContext, params: channels.BrowserContextEnableRecorderParams, delegate: ContextRecorderDelegate) { super(); - this._codegenMode = codegenMode; this._context = context; this._params = params; this._delegate = delegate; @@ -150,12 +148,6 @@ export class ContextRecorder extends EventEmitter { setEnabled(enabled: boolean) { this._collection.setEnabled(enabled); - if (this._codegenMode === 'trace-events') { - if (enabled) - this._context.tracing.startChunk({ name: 'trace', title: 'trace' }).catch(() => {}); - else - this._context.tracing.stopChunk({ mode: 'discard' }).catch(() => {}); - } } dispose() { diff --git a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts b/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts deleted file mode 100644 index fcfd0a36c5331..0000000000000 --- a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * 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 path from 'path'; -import type { CallLog, ElementInfo, Mode, Source } from '@recorder/recorderTypes'; -import { EventEmitter } from 'events'; -import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorderFrontend'; -import { installRootRedirect, openTraceViewerApp, startTraceViewerServer } from '../trace/viewer/traceViewer'; -import type { TraceViewerServerOptions } from '../trace/viewer/traceViewer'; -import type { BrowserContext } from '../browserContext'; -import type { HttpServer, Transport } from '../../utils/httpServer'; -import type { Page } from '../page'; -import { ManualPromise } from '../../utils/manualPromise'; -import type * as actions from '@recorder/actions'; - -export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp { - readonly wsEndpointForTest: string | undefined; - private _transport: RecorderTransport; - private _tracePage: Page; - private _traceServer: HttpServer; - - static factory(context: BrowserContext): IRecorderAppFactory { - return async (recorder: IRecorder) => { - const transport = new RecorderTransport(); - const trace = path.join(context._browser.options.tracesDir, 'trace'); - const { wsEndpointForTest, tracePage, traceServer } = await openApp(trace, { transport, headless: !context._browser.options.headful }); - return new RecorderInTraceViewer(transport, tracePage, traceServer, wsEndpointForTest); - }; - } - - constructor(transport: RecorderTransport, tracePage: Page, traceServer: HttpServer, wsEndpointForTest: string | undefined) { - super(); - this._transport = transport; - this._transport.eventSink.resolve(this); - this._tracePage = tracePage; - this._traceServer = traceServer; - this.wsEndpointForTest = wsEndpointForTest; - this._tracePage.once('close', () => { - this.close(); - }); - } - - async close(): Promise { - await this._tracePage.context().close({ reason: 'Recorder window closed' }); - await this._traceServer.stop(); - } - - async setPaused(paused: boolean): Promise { - this._transport.deliverEvent('setPaused', { paused }); - } - - async setMode(mode: Mode): Promise { - this._transport.deliverEvent('setMode', { mode }); - } - - async setRunningFile(file: string | undefined): Promise { - this._transport.deliverEvent('setRunningFile', { file }); - } - - async elementPicked(elementInfo: ElementInfo, userGesture?: boolean): Promise { - this._transport.deliverEvent('elementPicked', { elementInfo, userGesture }); - } - - async updateCallLogs(callLogs: CallLog[]): Promise { - this._transport.deliverEvent('updateCallLogs', { callLogs }); - } - - async setSources(sources: Source[]): Promise { - this._transport.deliverEvent('setSources', { sources }); - if (process.env.PWTEST_CLI_IS_UNDER_TEST && sources.length) { - if ((process as any)._didSetSourcesForTest(sources[0].text)) - this.close(); - } - } - - async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise { - this._transport.deliverEvent('setActions', { actions, sources }); - } -} - -async function openApp(trace: string, options?: TraceViewerServerOptions & { headless?: boolean }): Promise<{ wsEndpointForTest: string | undefined, tracePage: Page, traceServer: HttpServer }> { - const traceServer = await startTraceViewerServer(options); - await installRootRedirect(traceServer, [trace], { ...options, webApp: 'recorder.html' }); - const page = await openTraceViewerApp(traceServer.urlPrefix('precise'), 'chromium', options); - return { wsEndpointForTest: page.context()._browser.options.wsEndpoint, tracePage: page, traceServer }; -} - -class RecorderTransport implements Transport { - private _connected = new ManualPromise(); - readonly eventSink = new ManualPromise(); - - constructor() { - } - - onconnect() { - this._connected.resolve(); - } - - async dispatch(method: string, params: any): Promise { - const eventSink = await this.eventSink; - eventSink.emit('event', { event: method, params }); - } - - onclose() { - } - - deliverEvent(method: string, params: any) { - this._connected.then(() => this.sendEvent?.(method, params)); - } - - sendEvent?: (method: string, params: any) => void; - close?: () => void; -} diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 6f9e36f0c328d..526cc599ab89d 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -1772,7 +1772,6 @@ export type BrowserContextPauseResult = void; export type BrowserContextEnableRecorderParams = { language?: string, mode?: 'inspecting' | 'recording', - codegenMode?: 'actions' | 'trace-events', pauseOnNextStatement?: boolean, testIdAttributeName?: string, launchOptions?: any, @@ -1786,7 +1785,6 @@ export type BrowserContextEnableRecorderParams = { export type BrowserContextEnableRecorderOptions = { language?: string, mode?: 'inspecting' | 'recording', - codegenMode?: 'actions' | 'trace-events', pauseOnNextStatement?: boolean, testIdAttributeName?: string, launchOptions?: any, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index e8b9746b41cfe..df54dcbe1ca0f 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1198,11 +1198,6 @@ BrowserContext: literals: - inspecting - recording - codegenMode: - type: enum? - literals: - - actions - - trace-events pauseOnNextStatement: boolean? testIdAttributeName: string? launchOptions: json? diff --git a/packages/trace-viewer/recorder.html b/packages/trace-viewer/recorder.html deleted file mode 100644 index c33d6586e58f0..0000000000000 --- a/packages/trace-viewer/recorder.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - Playwright Recorder - - -
- - - diff --git a/packages/trace-viewer/src/DEPS.list b/packages/trace-viewer/src/DEPS.list index 3d486b5452496..f52c0a024e556 100644 --- a/packages/trace-viewer/src/DEPS.list +++ b/packages/trace-viewer/src/DEPS.list @@ -6,7 +6,3 @@ ui/ [sw-main.ts] sw/** - - -[recorder.tsx] -ui/recorder/** diff --git a/packages/trace-viewer/src/recorder.tsx b/packages/trace-viewer/src/recorder.tsx deleted file mode 100644 index 5e6b9764e3431..0000000000000 --- a/packages/trace-viewer/src/recorder.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 '@web/common.css'; -import { applyTheme } from '@web/theme'; -import '@web/third_party/vscode/codicon.css'; -import * as ReactDOM from 'react-dom/client'; -import { RecorderView } from './ui/recorder/recorderView'; - -(async () => { - applyTheme(); - - if (window.location.protocol !== 'file:') { - if (!navigator.serviceWorker) - throw new Error(`Service workers are not supported.\nMake sure to serve the Recorder (${window.location}) via HTTPS or localhost.`); - navigator.serviceWorker.register('sw.bundle.js'); - if (!navigator.serviceWorker.controller) { - await new Promise(f => { - navigator.serviceWorker.oncontrollerchange = () => f(); - }); - } - - // Keep SW running. - setInterval(function() { fetch('ping'); }, 10000); - } - - ReactDOM.createRoot(document.querySelector('#root')!).render(); -})(); diff --git a/packages/trace-viewer/src/ui/recorder/DEPS.list b/packages/trace-viewer/src/ui/recorder/DEPS.list deleted file mode 100644 index a504a7dba1efb..0000000000000 --- a/packages/trace-viewer/src/ui/recorder/DEPS.list +++ /dev/null @@ -1,5 +0,0 @@ -[*] -@isomorphic/** -@trace/** -@web/** -../** diff --git a/packages/trace-viewer/src/ui/recorder/actionListView.tsx b/packages/trace-viewer/src/ui/recorder/actionListView.tsx deleted file mode 100644 index 8e9fa0df45eae..0000000000000 --- a/packages/trace-viewer/src/ui/recorder/actionListView.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 * as actionTypes from '@recorder/actions'; -import { ListView } from '@web/components/listView'; -import * as React from 'react'; -import '../actionList.css'; -import { traceParamsForAction } from '@isomorphic/recorderUtils'; -import { asLocator } from '@isomorphic/locatorGenerators'; -import type { Language } from '@isomorphic/locatorGenerators'; - -const ActionList = ListView; - -export const ActionListView: React.FC<{ - sdkLanguage: Language, - actions: actionTypes.ActionInContext[], - selectedAction: actionTypes.ActionInContext | undefined, - onSelectedAction: (action: actionTypes.ActionInContext | undefined) => void, -}> = ({ - sdkLanguage, - actions, - selectedAction, - onSelectedAction, -}) => { - const render = React.useCallback((action: actionTypes.ActionInContext) => { - return renderAction(sdkLanguage, action); - }, [sdkLanguage]); - return
- -
; -}; - -export const renderAction = (sdkLanguage: Language, action: actionTypes.ActionInContext) => { - const { method, apiName, params } = traceParamsForAction(action); - const locator = params.selector ? asLocator(sdkLanguage || 'javascript', params.selector) : undefined; - - return <> -
- {apiName} - {locator &&
{locator}
} - {method === 'goto' && params.url &&
{params.url}
} -
- ; -}; diff --git a/packages/trace-viewer/src/ui/recorder/backendContext.tsx b/packages/trace-viewer/src/ui/recorder/backendContext.tsx deleted file mode 100644 index 312281001e7a3..0000000000000 --- a/packages/trace-viewer/src/ui/recorder/backendContext.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/* - 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 * as actionTypes from '@recorder/actions'; -import type { Mode, Source } from '@recorder/recorderTypes'; -import * as React from 'react'; - -export const BackendContext = React.createContext(undefined); - -export const BackendProvider: React.FunctionComponent> = ({ guid, children }) => { - const [connection, setConnection] = React.useState(undefined); - const [mode, setMode] = React.useState('none'); - const [actions, setActions] = React.useState<{ actions: actionTypes.ActionInContext[], sources: Source[] }>({ actions: [], sources: [] }); - const callbacks = React.useRef({ setMode, setActions }); - - React.useEffect(() => { - const wsURL = new URL(`../${guid}`, window.location.toString()); - wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:'); - const webSocket = new WebSocket(wsURL.toString()); - setConnection(new Connection(webSocket, callbacks.current)); - return () => { - webSocket.close(); - }; - }, [guid]); - - const backend = React.useMemo(() => { - return connection ? { mode, actions: actions.actions, sources: actions.sources, connection } : undefined; - }, [actions, mode, connection]); - - return - {children} - ; -}; - -export type Backend = { - actions: actionTypes.ActionInContext[], - sources: Source[], - connection: Connection, -}; - -type ConnectionCallbacks = { - setMode: (mode: Mode) => void; - setActions: (data: { actions: actionTypes.ActionInContext[], sources: Source[] }) => void; -}; - -class Connection { - private _lastId = 0; - private _webSocket: WebSocket; - private _callbacks = new Map void, reject: (arg: Error) => void }>(); - private _options: ConnectionCallbacks; - - constructor(webSocket: WebSocket, options: ConnectionCallbacks) { - this._webSocket = webSocket; - this._callbacks = new Map(); - this._options = options; - - this._webSocket.addEventListener('message', event => { - const message = JSON.parse(event.data); - const { id, result, error, method, params } = message; - if (id) { - const callback = this._callbacks.get(id); - if (!callback) - return; - this._callbacks.delete(id); - if (error) - callback.reject(new Error(error)); - else - callback.resolve(result); - } else { - this._dispatchEvent(method, params); - } - }); - } - - setMode(mode: Mode) { - this._sendMessageNoReply('setMode', { mode }); - } - - private async _sendMessage(method: string, params?: any): Promise { - const id = ++this._lastId; - const message = { id, method, params }; - this._webSocket.send(JSON.stringify(message)); - return new Promise((resolve, reject) => { - this._callbacks.set(id, { resolve, reject }); - }); - } - - private _sendMessageNoReply(method: string, params?: any) { - this._sendMessage(method, params).catch(() => { }); - } - - private _dispatchEvent(method: string, params?: any) { - if (method === 'setMode') { - const { mode } = params as { mode: Mode }; - this._options.setMode(mode); - } - if (method === 'setActions') { - const { actions, sources } = params as { actions: actionTypes.ActionInContext[], sources: Source[] }; - this._options.setActions({ actions: actions.filter(a => a.action.name !== 'openPage' && a.action.name !== 'closePage'), sources }); - (window as any).playwrightSourcesEchoForTest = sources; - } - } -} diff --git a/packages/trace-viewer/src/ui/recorder/modelContext.tsx b/packages/trace-viewer/src/ui/recorder/modelContext.tsx deleted file mode 100644 index 98f450361b3db..0000000000000 --- a/packages/trace-viewer/src/ui/recorder/modelContext.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - 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 { sha1 } from '@web/uiUtils'; -import * as React from 'react'; -import type { ContextEntry } from '../../types/entries'; -import { MultiTraceModel } from '../modelUtil'; - -export const ModelContext = React.createContext(undefined); - -export const ModelProvider: React.FunctionComponent> = ({ trace, children }) => { - const [model, setModel] = React.useState<{ model: MultiTraceModel, sha1: string } | undefined>(); - const [counter, setCounter] = React.useState(0); - const pollTimer = React.useRef(null); - - React.useEffect(() => { - if (pollTimer.current) - clearTimeout(pollTimer.current); - - // Start polling running test. - pollTimer.current = setTimeout(async () => { - try { - const result = await loadSingleTraceFile(trace); - if (result.sha1 !== model?.sha1) - setModel(result); - } catch { - setModel(undefined); - } finally { - setCounter(counter + 1); - } - }, 500); - return () => { - if (pollTimer.current) - clearTimeout(pollTimer.current); - }; - }, [counter, model, trace]); - - return - {children} - ; -}; - -async function loadSingleTraceFile(url: string): Promise<{ model: MultiTraceModel, sha1: string }> { - const params = new URLSearchParams(); - params.set('trace', url); - params.set('limit', '1'); - const response = await fetch(`contexts?${params.toString()}`); - const contextEntries = await response.json() as ContextEntry[]; - - const tokens: string[] = []; - for (const entry of contextEntries) { - entry.actions.forEach(a => tokens.push(a.type + '@' + a.startTime + '-' + a.endTime)); - entry.events.forEach(e => tokens.push(e.type + '@' + e.time)); - } - return { model: new MultiTraceModel(contextEntries), sha1: await sha1(tokens.join('|')) }; -} diff --git a/packages/trace-viewer/src/ui/recorder/recorderView.css b/packages/trace-viewer/src/ui/recorder/recorderView.css deleted file mode 100644 index ad03e78e7d233..0000000000000 --- a/packages/trace-viewer/src/ui/recorder/recorderView.css +++ /dev/null @@ -1,15 +0,0 @@ -/* - 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. -*/ diff --git a/packages/trace-viewer/src/ui/recorder/recorderView.tsx b/packages/trace-viewer/src/ui/recorder/recorderView.tsx deleted file mode 100644 index 93db2b917dfb0..0000000000000 --- a/packages/trace-viewer/src/ui/recorder/recorderView.tsx +++ /dev/null @@ -1,299 +0,0 @@ -/* - 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 * as actionTypes from '@recorder/actions'; -import { SourceChooser } from '@web/components/sourceChooser'; -import { SplitView } from '@web/components/splitView'; -import type { TabbedPaneTabModel } from '@web/components/tabbedPane'; -import { TabbedPane } from '@web/components/tabbedPane'; -import { Toolbar } from '@web/components/toolbar'; -import { ToolbarButton, ToolbarSeparator } from '@web/components/toolbarButton'; -import { copy, useSetting } from '@web/uiUtils'; -import * as React from 'react'; -import { ConsoleTab, useConsoleTabModel } from '../consoleTab'; -import type { Boundaries } from '../geometry'; -import { InspectorTab } from '../inspectorTab'; -import type * as modelUtil from '../modelUtil'; -import type { SourceLocation } from '../modelUtil'; -import { NetworkTab, useNetworkTabModel } from '../networkTab'; -import { collectSnapshots, extendSnapshot, SnapshotView } from '../snapshotTab'; -import { SourceTab } from '../sourceTab'; -import { ModelContext, ModelProvider } from './modelContext'; -import './recorderView.css'; -import { ActionListView } from './actionListView'; -import { BackendContext, BackendProvider } from './backendContext'; -import type { Language } from '@isomorphic/locatorGenerators'; -import { SettingsToolbarButton } from '../settingsToolbarButton'; -import type { HighlightedElement } from '../snapshotTab'; - -export const RecorderView: React.FunctionComponent = () => { - const searchParams = new URLSearchParams(window.location.search); - const guid = searchParams.get('ws')!; - const trace = searchParams.get('trace') + '.json'; - - return - - - - ; -}; - -export const Workbench: React.FunctionComponent = () => { - const backend = React.useContext(BackendContext); - const model = React.useContext(ModelContext); - const [fileId, setFileId] = React.useState(); - const [selectedStartTime, setSelectedStartTime] = React.useState(undefined); - const [isInspecting, setIsInspecting] = React.useState(false); - const [highlightedElementInProperties, setHighlightedElementInProperties] = React.useState({ lastEdited: 'none' }); - const [highlightedElementInTrace, setHighlightedElementInTrace] = React.useState({ lastEdited: 'none' }); - const [traceCallId, setTraceCallId] = React.useState(); - - const setSelectedAction = React.useCallback((action: actionTypes.ActionInContext | undefined) => { - setSelectedStartTime(action?.startTime); - }, []); - - const selectedAction = React.useMemo(() => { - return backend?.actions.find(a => a.startTime === selectedStartTime); - }, [backend?.actions, selectedStartTime]); - - React.useEffect(() => { - const callId = model?.actions.find(a => a.endTime && a.endTime === selectedAction?.endTime)?.callId; - if (callId) - setTraceCallId(callId); - }, [model, selectedAction]); - - const source = React.useMemo(() => backend?.sources.find(s => s.id === fileId) || backend?.sources[0], [backend?.sources, fileId]); - const sourceLocation = React.useMemo(() => { - if (!source) - return undefined; - const sourceLocation: SourceLocation = { - file: '', - line: 0, - column: 0, - source: { - errors: [], - content: source.text - } - }; - return sourceLocation; - }, [source]); - - const sdkLanguage: Language = source?.language || 'javascript'; - - const { boundaries } = React.useMemo(() => { - const boundaries = { minimum: model?.startTime || 0, maximum: model?.endTime || 30000 }; - if (boundaries.minimum > boundaries.maximum) { - boundaries.minimum = 0; - boundaries.maximum = 30000; - } - // Leave some nice free space on the right hand side. - boundaries.maximum += (boundaries.maximum - boundaries.minimum) / 20; - return { boundaries }; - }, [model]); - - const elementPickedInTrace = React.useCallback((element: HighlightedElement) => { - setHighlightedElementInProperties(element); - setHighlightedElementInTrace({ lastEdited: 'none' }); - setIsInspecting(false); - }, []); - - const elementTypedInProperties = React.useCallback((element: HighlightedElement) => { - setHighlightedElementInTrace(element); - setHighlightedElementInProperties(element); - }, []); - - const actionList = ; - - const actionsTab: TabbedPaneTabModel = { - id: 'actions', - title: 'Actions', - component: actionList, - }; - - const toolbar = -
- { - setIsInspecting(!isInspecting); - }} /> - { - }} /> - { - }} /> - { - }} /> - - { - if (source?.text) - copy(source.text); - }}> -
-
Target:
- { - setFileId(fileId); - }} /> - -
; - - const sidebarTabbedPane = ; - const traceView = ; - const propertiesView = ; - - return
- - {toolbar} - {traceView} -
} - sidebar={propertiesView} - />} - sidebar={sidebarTabbedPane} - /> - ; -}; - -const PropertiesView: React.FunctionComponent<{ - sdkLanguage: Language, - boundaries: Boundaries, - setIsInspecting: (value: boolean) => void, - highlightedElement: HighlightedElement, - setHighlightedElement: (element: HighlightedElement) => void, - sourceLocation: modelUtil.SourceLocation | undefined, -}> = ({ - sdkLanguage, - boundaries, - setIsInspecting, - highlightedElement, - setHighlightedElement, - sourceLocation, -}) => { - const model = React.useContext(ModelContext); - const consoleModel = useConsoleTabModel(model, boundaries); - const networkModel = useNetworkTabModel(model, boundaries); - const sourceModel = React.useRef(new Map()); - const [selectedPropertiesTab, setSelectedPropertiesTab] = useSetting('recorderPropertiesTab', 'source'); - - const inspectorTab: TabbedPaneTabModel = { - id: 'inspector', - title: 'Locator', - render: () => , - }; - - const sourceTab: TabbedPaneTabModel = { - id: 'source', - title: 'Source', - render: () => - }; - const consoleTab: TabbedPaneTabModel = { - id: 'console', - title: 'Console', - count: consoleModel.entries.length, - render: () => - }; - const networkTab: TabbedPaneTabModel = { - id: 'network', - title: 'Network', - count: networkModel.resources.length, - render: () => - }; - - const tabs: TabbedPaneTabModel[] = [ - sourceTab, - inspectorTab, - consoleTab, - networkTab, - ]; - - return ; -}; - -const TraceView: React.FunctionComponent<{ - sdkLanguage: Language, - callId: string | undefined, - isInspecting: boolean; - setIsInspecting: (value: boolean) => void; - highlightedElement: HighlightedElement; - setHighlightedElement: (element: HighlightedElement) => void; -}> = ({ - sdkLanguage, - callId, - isInspecting, - setIsInspecting, - highlightedElement, - setHighlightedElement, -}) => { - const model = React.useContext(ModelContext); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [shouldPopulateCanvasFromScreenshot, _] = useSetting('shouldPopulateCanvasFromScreenshot', false); - - const action = React.useMemo(() => { - return model?.actions.find(a => a.callId === callId); - }, [model, callId]); - - const snapshot = React.useMemo(() => { - const snapshot = collectSnapshots(action); - return snapshot.action || snapshot.after || snapshot.before; - }, [action]); - const snapshotUrls = React.useMemo(() => { - return snapshot ? extendSnapshot(snapshot, shouldPopulateCanvasFromScreenshot) : undefined; - }, [snapshot, shouldPopulateCanvasFromScreenshot]); - - return ; -}; diff --git a/packages/trace-viewer/vite.config.ts b/packages/trace-viewer/vite.config.ts index c26e020fded13..00b367bbc8e7f 100644 --- a/packages/trace-viewer/vite.config.ts +++ b/packages/trace-viewer/vite.config.ts @@ -46,7 +46,6 @@ export default defineConfig({ input: { index: path.resolve(__dirname, 'index.html'), uiMode: path.resolve(__dirname, 'uiMode.html'), - recorder: path.resolve(__dirname, 'recorder.html'), snapshot: path.resolve(__dirname, 'snapshot.html'), }, output: { diff --git a/tests/config/testModeFixtures.ts b/tests/config/testModeFixtures.ts index 1231a78260994..6b6feff7c2913 100644 --- a/tests/config/testModeFixtures.ts +++ b/tests/config/testModeFixtures.ts @@ -21,7 +21,6 @@ import * as playwrightLibrary from 'playwright-core'; export type TestModeWorkerOptions = { mode: TestModeName; - codegenMode: 'trace-events' | 'actions'; }; export type TestModeTestFixtures = { @@ -49,7 +48,6 @@ export const testModeTest = test.extend { await use((playwright as any)._toImpl); diff --git a/tests/library/inspector/cli-codegen-1.spec.ts b/tests/library/inspector/cli-codegen-1.spec.ts index 0fd5f69d0bbb9..6936aeee41459 100644 --- a/tests/library/inspector/cli-codegen-1.spec.ts +++ b/tests/library/inspector/cli-codegen-1.spec.ts @@ -19,7 +19,6 @@ import type { ConsoleMessage } from 'playwright'; test.describe('cli codegen', () => { test.skip(({ mode }) => mode !== 'default'); - test.skip(({ trace, codegenMode }) => trace === 'on' && codegenMode === 'trace-events'); test('should click', async ({ openRecorder }) => { const { page, recorder } = await openRecorder(); @@ -413,7 +412,7 @@ await page.GetByRole(AriaRole.Textbox).PressAsync("Shift+Enter");`); expect(messages[0].text()).toBe('press'); }); - test('should update selected element after pressing Tab', async ({ openRecorder, browserName, codegenMode }) => { + test('should update selected element after pressing Tab', async ({ openRecorder }) => { const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(` diff --git a/tests/library/inspector/cli-codegen-2.spec.ts b/tests/library/inspector/cli-codegen-2.spec.ts index 47afcc2fab985..920ecbdf76042 100644 --- a/tests/library/inspector/cli-codegen-2.spec.ts +++ b/tests/library/inspector/cli-codegen-2.spec.ts @@ -20,7 +20,6 @@ import fs from 'fs'; test.describe('cli codegen', () => { test.skip(({ mode }) => mode !== 'default'); - test.skip(({ trace, codegenMode }) => trace === 'on' && codegenMode === 'trace-events'); test('should contain open page', async ({ openRecorder }) => { const { recorder } = await openRecorder(); @@ -310,8 +309,7 @@ await page.GetByRole(AriaRole.Button, new() { Name = "click me" }).ClickAsync(); } }); - test('should record open in a new tab with url', async ({ openRecorder, browserName, codegenMode }) => { - test.skip(codegenMode === 'trace-events'); + test('should record open in a new tab with url', async ({ openRecorder, browserName }) => { const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(`link`); @@ -453,8 +451,7 @@ await page1.GotoAsync("about:blank?foo");`); await recorder.waitForOutput('JavaScript', `await page.goto('${server.PREFIX}/page2.html');`); }); - test('should --save-trace', async ({ runCLI, codegenMode }, testInfo) => { - test.skip(codegenMode === 'trace-events'); + test('should --save-trace', async ({ runCLI }, testInfo) => { const traceFileName = testInfo.outputPath('trace.zip'); const cli = runCLI([`--save-trace=${traceFileName}`], { autoExitWhen: ' ', @@ -463,8 +460,7 @@ await page1.GotoAsync("about:blank?foo");`); expect(fs.existsSync(traceFileName)).toBeTruthy(); }); - test('should save assets via SIGINT', async ({ runCLI, platform, codegenMode }, testInfo) => { - test.skip(codegenMode === 'trace-events'); + test('should save assets via SIGINT', async ({ runCLI, platform }, testInfo) => { test.skip(platform === 'win32', 'SIGINT not supported on Windows'); const traceFileName = testInfo.outputPath('trace.zip'); diff --git a/tests/library/inspector/cli-codegen-3.spec.ts b/tests/library/inspector/cli-codegen-3.spec.ts index 87c7e7bfec6c6..8af5a764723aa 100644 --- a/tests/library/inspector/cli-codegen-3.spec.ts +++ b/tests/library/inspector/cli-codegen-3.spec.ts @@ -21,7 +21,6 @@ import type { Page } from '@playwright/test'; test.describe('cli codegen', () => { test.skip(({ mode }) => mode !== 'default'); - test.skip(({ trace, codegenMode }) => trace === 'on' && codegenMode === 'trace-events'); test('should click locator.first', async ({ openRecorder }) => { const { page, recorder } = await openRecorder(); diff --git a/tests/library/inspector/cli-codegen-aria.spec.ts b/tests/library/inspector/cli-codegen-aria.spec.ts index 354ca6495ae47..a11cf333422e8 100644 --- a/tests/library/inspector/cli-codegen-aria.spec.ts +++ b/tests/library/inspector/cli-codegen-aria.spec.ts @@ -19,7 +19,6 @@ import { roundBox } from '../../page/pageTest'; test.describe(() => { test.skip(({ mode }) => mode !== 'default'); - test.skip(({ trace, codegenMode }) => trace === 'on' && codegenMode === 'trace-events'); test('should generate aria snapshot', async ({ openRecorder }) => { const { recorder } = await openRecorder(); diff --git a/tests/library/inspector/cli-codegen-pick-locator.spec.ts b/tests/library/inspector/cli-codegen-pick-locator.spec.ts index 53d106b4c9a1e..bd1a612842b22 100644 --- a/tests/library/inspector/cli-codegen-pick-locator.spec.ts +++ b/tests/library/inspector/cli-codegen-pick-locator.spec.ts @@ -19,7 +19,6 @@ import { roundBox } from '../../page/pageTest'; test.describe(() => { test.skip(({ mode }) => mode !== 'default'); - test.skip(({ trace, codegenMode }) => trace === 'on' && codegenMode === 'trace-events'); test('should inspect locator', async ({ openRecorder }) => { const { recorder } = await openRecorder(); diff --git a/tests/library/inspector/inspectorTest.ts b/tests/library/inspector/inspectorTest.ts index a90f73fcdfb46..b94bfc09a0a5c 100644 --- a/tests/library/inspector/inspectorTest.ts +++ b/tests/library/inspector/inspectorTest.ts @@ -67,7 +67,7 @@ export const test = contextTest.extend({ }); }, - runCLI: async ({ childProcess, browserName, channel, headless, mode, launchOptions, codegenMode }, run, testInfo) => { + runCLI: async ({ childProcess, browserName, channel, headless, mode, launchOptions }, run, testInfo) => { testInfo.skip(mode.startsWith('service')); await run((cliArgs, { autoExitWhen } = {}) => { @@ -78,17 +78,15 @@ export const test = contextTest.extend({ args: cliArgs, executablePath: launchOptions.executablePath, autoExitWhen, - codegenMode }); }); }, - openRecorder: async ({ context, recorderPageGetter, codegenMode }, run) => { + openRecorder: async ({ context, recorderPageGetter }, run) => { await run(async (options?: { testIdAttributeName?: string }) => { await (context as any)._enableRecorder({ language: 'javascript', mode: 'recording', - codegenMode, ...options }); const page = await context.newPage(); @@ -235,7 +233,7 @@ export class Recorder { class CLIMock { process: TestChildProcess; - constructor(childProcess: CommonFixtures['childProcess'], options: { browserName: string, channel: string | undefined, headless: boolean | undefined, args: string[], executablePath: string | undefined, autoExitWhen: string | undefined, codegenMode?: 'trace-events' | 'actions'}) { + constructor(childProcess: CommonFixtures['childProcess'], options: { browserName: string, channel: string | undefined, headless: boolean | undefined, args: string[], executablePath: string | undefined, autoExitWhen: string | undefined}) { const nodeArgs = [ 'node', path.join(__dirname, '..', '..', '..', 'packages', 'playwright-core', 'cli.js'), @@ -248,7 +246,6 @@ class CLIMock { this.process = childProcess({ command: nodeArgs, env: { - PW_RECORDER_IS_TRACE_VIEWER: options.codegenMode === 'trace-events' ? '1' : undefined, PWTEST_CLI_AUTO_EXIT_WHEN: options.autoExitWhen, PWTEST_CLI_IS_UNDER_TEST: '1', PWTEST_CLI_HEADLESS: options.headless ? '1' : undefined, diff --git a/tests/library/playwright.config.ts b/tests/library/playwright.config.ts index 399eec1b6bb87..f588ee2f45c59 100644 --- a/tests/library/playwright.config.ts +++ b/tests/library/playwright.config.ts @@ -147,18 +147,6 @@ for (const browserName of browserNames) { testDir: path.join(testDir, 'page'), ...projectTemplate, }); - - // TODO: figure out reporting to flakiness dashboard (Problem: they get merged, we want to keep them separate) - // config.projects.push({ - // name: `${browserName}-codegen-mode-trace`, - // testDir: path.join(testDir, 'library'), - // testMatch: '**/cli-codegen-*.spec.ts', - // ...projectTemplate, - // use: { - // ...projectTemplate.use, - // codegenMode: 'trace-events', - // } - // }); } export default config;