diff --git a/src/cli/cli.ts b/src/cli/cli.ts index a5366446edfa4..705cee68c1604 100755 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -327,7 +327,15 @@ async function codegen(options: Options, url: string | undefined, language: stri const { context, launchOptions, contextOptions } = await launchContext(options, false); if (process.env.PWTRACE) contextOptions._traceDir = path.join(process.cwd(), '.trace'); - await context._enableRecorder(language, launchOptions, contextOptions, options.device, options.saveStorage, !!process.stdout.columns, outputFile ? path.resolve(outputFile) : undefined); + await context._enableRecorder({ + language, + launchOptions, + contextOptions, + device: options.device, + saveStorage: options.saveStorage, + terminal: !!process.stdout.columns, + outputFile: outputFile ? path.resolve(outputFile) : undefined + }); await openPage(context, url); if (process.env.PWCLI_EXIT_FOR_TEST) await Promise.all(context.pages().map(p => p.close())); diff --git a/src/client/browserContext.ts b/src/client/browserContext.ts index efd8175206a31..03f819e867bb9 100644 --- a/src/client/browserContext.ts +++ b/src/client/browserContext.ts @@ -270,23 +270,16 @@ export class BrowserContext extends ChannelOwner Promise; playwrightRecorderRecordAction: (action: actions.Action) => Promise; playwrightRecorderCommitAction: () => Promise; - playwrightRecorderState: () => Promise<{ state: any, paused: boolean, tool: 'codegen' | 'pause' }>; + playwrightRecorderState: () => Promise<{ state: any, paused: boolean, app: 'codegen' | 'debug' | 'pause' }>; playwrightRecorderSetState: (state: any) => Promise; playwrightRecorderResume: () => Promise; } @@ -51,7 +51,7 @@ export class Recorder { private _recordElement: HTMLElement; private _resumeElement: HTMLElement; private _mode: 'inspecting' | 'recording' | 'none' = 'none'; - private _tool: 'codegen' | 'pause' = 'pause'; + private _app: 'codegen' | 'debug' | 'pause' = 'debug'; private _paused = false; constructor(injectedScript: InjectedScript) { @@ -130,7 +130,7 @@ export class Recorder { `; this._resumeElement = html` - + `; @@ -180,6 +180,9 @@ export class Recorder { x-pw-button.hidden { display: none; } + x-pw-button svg { + pointer-events: none; + } `); const iconElement = html``; @@ -244,7 +247,7 @@ export class Recorder { clearTimeout(this._pollRecorderModeTimer); const result = await window.playwrightRecorderState().catch(e => null); if (result) { - const { state, paused, tool } = result; + const { state, paused, app } = result; if (state && state.mode !== this._mode) { this._mode = state.mode as any; this._inspectElement.classList.toggle('toggled', this._mode === 'inspecting'); @@ -255,11 +258,12 @@ export class Recorder { } if (paused !== this._paused) { this._paused = paused; + this._resumeElement.classList.toggle('hidden', false); this._resumeElement.classList.toggle('disabled', !this._paused); } - if (tool !== this._tool) { - this._tool = tool; - this._resumeElement.classList.toggle('hidden', this._tool !== 'pause'); + if (app !== this._app) { + this._app = app; + this._resumeElement.classList.toggle('hidden', this._app !== 'pause'); } } this._pollRecorderModeTimer = setTimeout(() => this._pollRecorderMode(), 250); @@ -295,6 +299,8 @@ export class Recorder { } private _onClick(event: MouseEvent) { + if (this._mode === 'inspecting' && !this._isInToolbar(event.target as HTMLElement)) + console.log(this._hoveredModel ? this._hoveredModel.selector : ''); // eslint-disable-line no-console if (this._shouldIgnoreMouseEvent(event)) return; if (this._actionInProgress(event)) diff --git a/src/server/supplements/inspectorController.ts b/src/server/supplements/inspectorController.ts index e86607063f4a2..bb6efa1c016fc 100644 --- a/src/server/supplements/inspectorController.ts +++ b/src/server/supplements/inspectorController.ts @@ -17,6 +17,9 @@ import { BrowserContext, ContextListener, contextListeners } from '../browserContext'; import { isDebugMode } from '../../utils/utils'; import { ConsoleApiSupplement } from './consoleApiSupplement'; +import { RecorderSupplement } from './recorderSupplement'; +import { Page } from '../page'; +import { ConsoleMessage } from '../console'; export function installInspectorController() { contextListeners.add(new InspectorController()); @@ -27,6 +30,13 @@ class InspectorController implements ContextListener { if (isDebugMode()) { const consoleApi = new ConsoleApiSupplement(context); await consoleApi.install(); + RecorderSupplement.getOrCreate(context, 'debug', { + language: 'javascript', + terminal: true, + }); + context.on(BrowserContext.Events.Page, (page: Page) => { + page.on(Page.Events.Console, (message: ConsoleMessage) => context.emit(BrowserContext.Events.StdOut, message.text() + '\n')); + }); } } async onContextWillDestroy(context: BrowserContext): Promise {} diff --git a/src/server/supplements/recorderSupplement.ts b/src/server/supplements/recorderSupplement.ts index e2deaad6e8789..cf9abb39b2115 100644 --- a/src/server/supplements/recorderSupplement.ts +++ b/src/server/supplements/recorderSupplement.ts @@ -31,7 +31,7 @@ import * as consoleApiSource from '../../generated/consoleApiSource'; import { FileOutput, FlushingTerminalOutput, OutputMultiplexer, RecorderOutput, TerminalOutput, Writable } from './recorder/outputs'; type BindingSource = { frame: Frame, page: Page }; -type Tool = 'codegen' | 'pause'; +type App = 'codegen' | 'debug' | 'pause'; type Mode = 'inspecting' | 'recording' | 'none'; const symbol = Symbol('RecorderSupplement'); @@ -47,23 +47,23 @@ export class RecorderSupplement { private _resumeCallback: (() => void) | null = null; private _recorderState: { mode: Mode }; private _paused = false; - private _tool: Tool; + private _app: App; private _output: OutputMultiplexer; - static getOrCreate(context: BrowserContext, tool: Tool, params: channels.BrowserContextRecorderSupplementEnableParams): Promise { + static getOrCreate(context: BrowserContext, app: App, params: channels.BrowserContextRecorderSupplementEnableParams): Promise { let recorderPromise = (context as any)[symbol] as Promise; if (!recorderPromise) { - const recorder = new RecorderSupplement(context, tool, params); + const recorder = new RecorderSupplement(context, app, params); recorderPromise = recorder.install().then(() => recorder); (context as any)[symbol] = recorderPromise; } return recorderPromise; } - constructor(context: BrowserContext, tool: Tool, params: channels.BrowserContextRecorderSupplementEnableParams) { + constructor(context: BrowserContext, app: App, params: channels.BrowserContextRecorderSupplementEnableParams) { this._context = context; - this._tool = tool; - this._recorderState = { mode: tool === 'codegen' ? 'recording' : 'none' }; + this._app = app; + this._recorderState = { mode: app === 'codegen' ? 'recording' : 'none' }; let languageGenerator: LanguageGenerator; switch (params.language) { case 'javascript': languageGenerator = new JavaScriptLanguageGenerator(); break; @@ -83,10 +83,10 @@ export class RecorderSupplement { if (params.outputFile) outputs.push(new FileOutput(params.outputFile)); this._output = new OutputMultiplexer(outputs); - this._output.setEnabled(tool === 'codegen'); + this._output.setEnabled(app === 'codegen'); context.on(BrowserContext.Events.BeforeClose, () => this._output.flush()); - const generator = new CodeGenerator(context._browser._options.name, tool === 'codegen', params.launchOptions || {}, params.contextOptions || {}, this._output, languageGenerator, params.device, params.saveStorage); + const generator = new CodeGenerator(context._browser._options.name, app === 'codegen', params.launchOptions || {}, params.contextOptions || {}, this._output, languageGenerator, params.device, params.saveStorage); this._generator = generator; } @@ -117,7 +117,7 @@ export class RecorderSupplement { await this._context.exposeBinding('playwrightRecorderState', false, () => { return { state: this._recorderState, - tool: this._tool, + app: this._app, paused: this._paused }; }); diff --git a/test/cli/cli.fixtures.ts b/test/cli/cli.fixtures.ts index 206fd193e8136..6165e28e031b4 100644 --- a/test/cli/cli.fixtures.ts +++ b/test/cli/cli.fixtures.ts @@ -39,7 +39,7 @@ fixtures.contextWrapper.init(async ({ browser }, runTest) => { const context = await browser.newContext() as BrowserContext; const outputBuffer = new WritableBuffer(); (context as any)._stdout = outputBuffer; - await (context as any)._enableRecorder('javascript'); + await (context as any)._enableRecorder({ language: 'javascript' }); await runTest({ context, output: outputBuffer }); await context.close(); });