Skip to content

Commit

Permalink
chore: make recorder a supplement (#5131)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Jan 25, 2021
1 parent be9bef5 commit 464fdc1
Show file tree
Hide file tree
Showing 34 changed files with 341 additions and 226 deletions.
69 changes: 34 additions & 35 deletions src/cli/cli.ts
Expand Up @@ -22,16 +22,17 @@ import * as path from 'path';
import * as program from 'commander';
import * as os from 'os';
import * as fs from 'fs';
import { OutputMultiplexer, TerminalOutput, FileOutput } from './codegen/outputs';
import { CodeGenerator, CodeGeneratorOutput } from './codegen/codeGenerator';
import { JavaScriptLanguageGenerator, LanguageGenerator } from './codegen/languages';
import { PythonLanguageGenerator } from './codegen/languages/python';
import { CSharpLanguageGenerator } from './codegen/languages/csharp';
import { RecorderController } from './codegen/recorderController';
import { runServer, printApiJson, installBrowsers } from './driver';
import { showTraceViewer } from './traceViewer/traceViewer';
import type { Browser, BrowserContext, Page, BrowserType, BrowserContextOptions, LaunchOptions } from '../..';
import * as playwright from '../..';
import { BrowserContext } from '../client/browserContext';
import { Browser } from '../client/browser';
import { Page } from '../client/page';
import { BrowserType } from '../client/browserType';
import { BrowserContextOptions, LaunchOptions } from '../client/types';
import { RecorderOutput, RecorderSupplement } from '../client/supplements/recorderSupplement';
import { ConsoleApiSupplement } from '../client/supplements/consoleApiSupplement';
import { FileOutput, OutputMultiplexer, TerminalOutput } from '../client/supplements/recorderOutputs';

program
.version('Version ' + require('../../package.json').version)
Expand Down Expand Up @@ -317,36 +318,31 @@ async function openPage(context: BrowserContext, url: string | undefined): Promi

async function open(options: Options, url: string | undefined) {
const { context } = await launchContext(options, false);
(context as any)._exposeConsoleApi();
new ConsoleApiSupplement(context);
await openPage(context, url);
if (process.env.PWCLI_EXIT_FOR_TEST)
await Promise.all(context.pages().map(p => p.close()));
}

async function codegen(options: Options, url: string | undefined, target: string, outputFile?: string) {
let languageGenerator: LanguageGenerator;

switch (target) {
case 'javascript': languageGenerator = new JavaScriptLanguageGenerator(); break;
case 'csharp': languageGenerator = new CSharpLanguageGenerator(); break;
case 'python':
case 'python-async': languageGenerator = new PythonLanguageGenerator(target === 'python-async'); break;
default: throw new Error(`Invalid target: '${target}'`);
}

const { context, browserName, launchOptions, contextOptions } = await launchContext(options, false);

if (process.env.PWTRACE)
(contextOptions as any)._traceDir = path.join(process.cwd(), '.trace');

const outputs: CodeGeneratorOutput[] = [TerminalOutput.create(process.stdout, languageGenerator.highlighterType())];
async function codegen(options: Options, url: string | undefined, language: string, outputFile?: string) {
const { context, launchOptions, contextOptions } = await launchContext(options, false);
let highlighterType = language;
if (highlighterType === 'python-async')
highlighterType = 'python';
const outputs: RecorderOutput[] = [TerminalOutput.create(process.stdout, highlighterType)];
if (outputFile)
outputs.push(new FileOutput(outputFile));
const output = new OutputMultiplexer(outputs);

const generator = new CodeGenerator(browserName, launchOptions, contextOptions, output, languageGenerator, options.device, options.saveStorage);
new RecorderController(context, generator);
(context as any)._exposeConsoleApi();
new ConsoleApiSupplement(context);
new RecorderSupplement(context,
language,
launchOptions,
contextOptions,
options.device,
options.saveStorage,
output);

await openPage(context, url);
if (process.env.PWCLI_EXIT_FOR_TEST)
await Promise.all(context.pages().map(p => p.close()));
Expand Down Expand Up @@ -387,20 +383,23 @@ async function pdf(options: Options, captureOptions: CaptureOptions, url: string
await browser.close();
}

function lookupBrowserType(options: Options): BrowserType<Browser> {
function lookupBrowserType(options: Options): BrowserType {
let name = options.browser;
if (options.device) {
const device = playwright.devices[options.device];
name = device.defaultBrowserType;
}
let browserType: any;
switch (name) {
case 'chromium': return playwright.chromium!;
case 'webkit': return playwright.webkit!;
case 'firefox': return playwright.firefox!;
case 'cr': return playwright.chromium!;
case 'wk': return playwright.webkit!;
case 'ff': return playwright.firefox!;
case 'chromium': browserType = playwright.chromium; break;
case 'webkit': browserType = playwright.webkit; break;
case 'firefox': browserType = playwright.firefox; break;
case 'cr': browserType = playwright.chromium; break;
case 'wk': browserType = playwright.webkit; break;
case 'ff': browserType = playwright.firefox; break;
}
if (browserType)
return browserType;
program.help();
}

Expand Down
2 changes: 1 addition & 1 deletion src/cli/driver.ts
Expand Up @@ -18,7 +18,7 @@

import * as fs from 'fs';
import * as path from 'path';
import { installInspectorController } from '../server/inspector/inspectorController';
import { installInspectorController } from '../server/supplements/inspectorController';
import { DispatcherConnection } from '../dispatchers/dispatcher';
import { PlaywrightDispatcher } from '../dispatchers/playwrightDispatcher';
import { installBrowsersWithProgressBar } from '../install/installer';
Expand Down
8 changes: 0 additions & 8 deletions src/client/browserContext.ts
Expand Up @@ -253,14 +253,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
throw e;
}
}

async _exposeConsoleApi() {
await this._channel.exposeConsoleApi();
}

async _enableRecorder<Arg>() {
await this._channel.enableRecorder();
}
}

export async function prepareBrowserContextOptions(options: BrowserContextOptions): Promise<channels.BrowserNewContextOptions> {
Expand Down
23 changes: 23 additions & 0 deletions src/client/supplements/consoleApiSupplement.ts
@@ -0,0 +1,23 @@
/**
* 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 { BrowserContext } from '../browserContext';

export class ConsoleApiSupplement {
constructor(context: BrowserContext) {
context._channel.consoleSupplementExpose().catch(e => {});
}
}
Expand Up @@ -18,11 +18,11 @@ import * as fs from 'fs';
import * as querystring from 'querystring';
import { Writable } from 'stream';
import * as hljs from '../../third_party/highlightjs/highlightjs';
import { CodeGeneratorOutput } from './codeGenerator';
import { RecorderOutput } from './recorderSupplement';

export class OutputMultiplexer implements CodeGeneratorOutput {
private _outputs: CodeGeneratorOutput[]
constructor(outputs: CodeGeneratorOutput[]) {
export class OutputMultiplexer implements RecorderOutput {
private _outputs: RecorderOutput[]
constructor(outputs: RecorderOutput[]) {
this._outputs = outputs;
}

Expand Down Expand Up @@ -58,7 +58,7 @@ export class BufferOutput {
}
}

export class FileOutput extends BufferOutput implements CodeGeneratorOutput {
export class FileOutput extends BufferOutput implements RecorderOutput {
private _fileName: string;

constructor(fileName: string) {
Expand All @@ -71,7 +71,7 @@ export class FileOutput extends BufferOutput implements CodeGeneratorOutput {
}
}

export class TerminalOutput implements CodeGeneratorOutput {
export class TerminalOutput implements RecorderOutput {
private _output: Writable
private _language: string;

Expand Down Expand Up @@ -127,7 +127,7 @@ export class TerminalOutput implements CodeGeneratorOutput {
flush() {}
}

export class FlushingTerminalOutput extends BufferOutput implements CodeGeneratorOutput {
export class FlushingTerminalOutput extends BufferOutput implements RecorderOutput {
private _output: Writable

constructor(output: Writable) {
Expand Down
51 changes: 51 additions & 0 deletions src/client/supplements/recorderSupplement.ts
@@ -0,0 +1,51 @@
/**
* 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 * as path from 'path';

import { BrowserContext } from '../browserContext';
import { BrowserContextOptions, LaunchOptions } from '../types';

export class RecorderSupplement {
constructor(context: BrowserContext,
language: string,
launchOptions: LaunchOptions,
contextOptions: BrowserContextOptions,
device: string | undefined,
saveStorage: string | undefined,
output: RecorderOutput) {

if (process.env.PWTRACE)
contextOptions._traceDir = path.join(process.cwd(), '.trace');

context._channel.on('recorderSupplementPrintLn', event => output.printLn(event.text));
context._channel.on('recorderSupplementPopLn', event => output.popLn(event.text));
context.on('close', () => output.flush());
context._channel.recorderSupplementEnable({
language,
launchOptions,
contextOptions,
device,
saveStorage,
}).catch(e => {});
}
}

export interface RecorderOutput {
printLn(text: string): void;
popLn(text: string): void;
flush(): void;
}
15 changes: 11 additions & 4 deletions src/dispatchers/browserContextDispatcher.ts
Expand Up @@ -21,6 +21,8 @@ import * as channels from '../protocol/channels';
import { RouteDispatcher, RequestDispatcher } from './networkDispatchers';
import { CRBrowserContext } from '../server/chromium/crBrowser';
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
import { RecorderSupplement } from '../server/supplements/recorderSupplement';
import { ConsoleApiSupplement } from '../server/supplements/consoleApiSupplement';

export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextInitializer> implements channels.BrowserContextChannel {
private _context: BrowserContext;
Expand Down Expand Up @@ -125,12 +127,17 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
await this._context.close();
}

async exposeConsoleApi(): Promise<void> {
await this._context.exposeConsoleApi();
async consoleSupplementExpose(): Promise<void> {
const consoleApi = new ConsoleApiSupplement(this._context);
await consoleApi.install();
}

async enableRecorder(): Promise<void> {
await this._context.enableRecorder();
async recorderSupplementEnable(params: channels.BrowserContextRecorderSupplementEnableParams): Promise<void> {
const recorder = new RecorderSupplement(this._context, params, {
printLn: text => this._dispatchEvent('recorderSupplementPrintLn', { text }),
popLn: text => this._dispatchEvent('recorderSupplementPopLn', { text }),
});
await recorder.install();
}

async crNewCDPSession(params: channels.BrowserContextCrNewCDPSessionParams): Promise<channels.BrowserContextCrNewCDPSessionResult> {
Expand Down
2 changes: 1 addition & 1 deletion src/inprocess.ts
Expand Up @@ -20,7 +20,7 @@ import type { Playwright as PlaywrightAPI } from './client/playwright';
import { PlaywrightDispatcher } from './dispatchers/playwrightDispatcher';
import { Connection } from './client/connection';
import { BrowserServerLauncherImpl } from './browserServerImpl';
import { installInspectorController } from './server/inspector/inspectorController';
import { installInspectorController } from './server/supplements/inspectorController';
import { installTracer } from './trace/tracer';
import { installHarTracer } from './trace/harTracer';
import * as path from 'path';
Expand Down
33 changes: 25 additions & 8 deletions src/protocol/channels.ts
Expand Up @@ -535,6 +535,8 @@ export interface BrowserContextChannel extends Channel {
on(event: 'route', callback: (params: BrowserContextRouteEvent) => void): this;
on(event: 'crBackgroundPage', callback: (params: BrowserContextCrBackgroundPageEvent) => void): this;
on(event: 'crServiceWorker', callback: (params: BrowserContextCrServiceWorkerEvent) => void): this;
on(event: 'recorderSupplementPrintLn', callback: (params: BrowserContextRecorderSupplementPrintLnEvent) => void): this;
on(event: 'recorderSupplementPopLn', callback: (params: BrowserContextRecorderSupplementPopLnEvent) => void): this;
addCookies(params: BrowserContextAddCookiesParams, metadata?: Metadata): Promise<BrowserContextAddCookiesResult>;
addInitScript(params: BrowserContextAddInitScriptParams, metadata?: Metadata): Promise<BrowserContextAddInitScriptResult>;
clearCookies(params?: BrowserContextClearCookiesParams, metadata?: Metadata): Promise<BrowserContextClearCookiesResult>;
Expand All @@ -552,8 +554,8 @@ export interface BrowserContextChannel extends Channel {
setNetworkInterceptionEnabled(params: BrowserContextSetNetworkInterceptionEnabledParams, metadata?: Metadata): Promise<BrowserContextSetNetworkInterceptionEnabledResult>;
setOffline(params: BrowserContextSetOfflineParams, metadata?: Metadata): Promise<BrowserContextSetOfflineResult>;
storageState(params?: BrowserContextStorageStateParams, metadata?: Metadata): Promise<BrowserContextStorageStateResult>;
exposeConsoleApi(params?: BrowserContextExposeConsoleApiParams, metadata?: Metadata): Promise<BrowserContextExposeConsoleApiResult>;
enableRecorder(params?: BrowserContextEnableRecorderParams, metadata?: Metadata): Promise<BrowserContextEnableRecorderResult>;
consoleSupplementExpose(params?: BrowserContextConsoleSupplementExposeParams, metadata?: Metadata): Promise<BrowserContextConsoleSupplementExposeResult>;
recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: Metadata): Promise<BrowserContextRecorderSupplementEnableResult>;
crNewCDPSession(params: BrowserContextCrNewCDPSessionParams, metadata?: Metadata): Promise<BrowserContextCrNewCDPSessionResult>;
}
export type BrowserContextBindingCallEvent = {
Expand All @@ -573,6 +575,12 @@ export type BrowserContextCrBackgroundPageEvent = {
export type BrowserContextCrServiceWorkerEvent = {
worker: WorkerChannel,
};
export type BrowserContextRecorderSupplementPrintLnEvent = {
text: string,
};
export type BrowserContextRecorderSupplementPopLnEvent = {
text: string,
};
export type BrowserContextAddCookiesParams = {
cookies: SetNetworkCookie[],
};
Expand Down Expand Up @@ -695,12 +703,21 @@ export type BrowserContextStorageStateResult = {
cookies: NetworkCookie[],
origins: OriginStorage[],
};
export type BrowserContextExposeConsoleApiParams = {};
export type BrowserContextExposeConsoleApiOptions = {};
export type BrowserContextExposeConsoleApiResult = void;
export type BrowserContextEnableRecorderParams = {};
export type BrowserContextEnableRecorderOptions = {};
export type BrowserContextEnableRecorderResult = void;
export type BrowserContextConsoleSupplementExposeParams = {};
export type BrowserContextConsoleSupplementExposeOptions = {};
export type BrowserContextConsoleSupplementExposeResult = void;
export type BrowserContextRecorderSupplementEnableParams = {
language: string,
launchOptions: any,
contextOptions: any,
device?: string,
saveStorage?: string,
};
export type BrowserContextRecorderSupplementEnableOptions = {
device?: string,
saveStorage?: string,
};
export type BrowserContextRecorderSupplementEnableResult = void;
export type BrowserContextCrNewCDPSessionParams = {
page: PageChannel,
};
Expand Down
16 changes: 14 additions & 2 deletions src/protocol/protocol.yml
Expand Up @@ -599,11 +599,17 @@ BrowserContext:
type: array
items: OriginStorage

exposeConsoleApi:
consoleSupplementExpose:
experimental: True

enableRecorder:
recorderSupplementEnable:
experimental: True
parameters:
language: string
launchOptions: json
contextOptions: json
device: string?
saveStorage: string?

crNewCDPSession:
parameters:
Expand Down Expand Up @@ -636,7 +642,13 @@ BrowserContext:
parameters:
worker: Worker

recorderSupplementPrintLn:
parameters:
text: string

recorderSupplementPopLn:
parameters:
text: string

Page:
type: interface
Expand Down

0 comments on commit 464fdc1

Please sign in to comment.