chore: move recorder to server side (#5128)
This commit is contained in:
Родитель
3e4e511d84
Коммит
be9bef513e
|
@ -22,7 +22,6 @@ import * as path from 'path';
|
|||
import * as program from 'commander';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as consoleApiSource from '../generated/consoleApiSource';
|
||||
import { OutputMultiplexer, TerminalOutput, FileOutput } from './codegen/outputs';
|
||||
import { CodeGenerator, CodeGeneratorOutput } from './codegen/codeGenerator';
|
||||
import { JavaScriptLanguageGenerator, LanguageGenerator } from './codegen/languages';
|
||||
|
@ -318,7 +317,7 @@ 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)._extendInjectedScript(consoleApiSource.source);
|
||||
(context as any)._exposeConsoleApi();
|
||||
await openPage(context, url);
|
||||
if (process.env.PWCLI_EXIT_FOR_TEST)
|
||||
await Promise.all(context.pages().map(p => p.close()));
|
||||
|
@ -347,7 +346,7 @@ async function codegen(options: Options, url: string | undefined, target: string
|
|||
|
||||
const generator = new CodeGenerator(browserName, launchOptions, contextOptions, output, languageGenerator, options.device, options.saveStorage);
|
||||
new RecorderController(context, generator);
|
||||
(context as any)._extendInjectedScript(consoleApiSource.source);
|
||||
(context as any)._exposeConsoleApi();
|
||||
await openPage(context, url);
|
||||
if (process.env.PWCLI_EXIT_FOR_TEST)
|
||||
await Promise.all(context.pages().map(p => p.close()));
|
||||
|
|
|
@ -18,7 +18,6 @@ import type { Page, BrowserContext, Frame, Download, Dialog } from '../../..';
|
|||
import * as actions from './recorderActions';
|
||||
import { CodeGenerator, ActionInContext } from './codeGenerator';
|
||||
import { toClickOptions, toModifiers } from './utils';
|
||||
import * as recorderSource from '../../generated/recorderSource';
|
||||
|
||||
type BindingSource = { frame: Frame, page: Page };
|
||||
|
||||
|
@ -30,7 +29,7 @@ export class RecorderController {
|
|||
private _timers = new Set<NodeJS.Timeout>();
|
||||
|
||||
constructor(context: BrowserContext, generator: CodeGenerator) {
|
||||
(context as any)._extendInjectedScript(recorderSource.source);
|
||||
(context as any)._enableRecorder();
|
||||
|
||||
this._generator = generator;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { installDebugController } from '../debug/debugController';
|
||||
import { installInspectorController } from '../server/inspector/inspectorController';
|
||||
import { DispatcherConnection } from '../dispatchers/dispatcher';
|
||||
import { PlaywrightDispatcher } from '../dispatchers/playwrightDispatcher';
|
||||
import { installBrowsersWithProgressBar } from '../install/installer';
|
||||
|
@ -38,7 +38,7 @@ export function printProtocol() {
|
|||
}
|
||||
|
||||
export function runServer() {
|
||||
installDebugController();
|
||||
installInspectorController();
|
||||
installTracer();
|
||||
installHarTracer();
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import { Waiter } from './waiter';
|
|||
import { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState } from './types';
|
||||
import { isUnderTest, headersObjectToArray, mkdirIfNeeded } from '../utils/utils';
|
||||
import { isSafeCloseError } from '../utils/errors';
|
||||
import { serializeArgument } from './jsHandle';
|
||||
import * as api from '../../types/types';
|
||||
import * as structs from '../../types/structs';
|
||||
|
||||
|
@ -255,8 +254,12 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
|||
}
|
||||
}
|
||||
|
||||
async _extendInjectedScript<Arg>(source: string, arg?: Arg) {
|
||||
await this._channel.extendInjectedScript({ source, arg: serializeArgument(arg) });
|
||||
async _exposeConsoleApi() {
|
||||
await this._channel.exposeConsoleApi();
|
||||
}
|
||||
|
||||
async _enableRecorder<Arg>() {
|
||||
await this._channel.enableRecorder();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import * as channels from '../protocol/channels';
|
|||
import { RouteDispatcher, RequestDispatcher } from './networkDispatchers';
|
||||
import { CRBrowserContext } from '../server/chromium/crBrowser';
|
||||
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
|
||||
import { parseArgument } from './jsHandleDispatcher';
|
||||
|
||||
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextInitializer> implements channels.BrowserContextChannel {
|
||||
private _context: BrowserContext;
|
||||
|
@ -126,8 +125,12 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
await this._context.close();
|
||||
}
|
||||
|
||||
async extendInjectedScript(params: channels.BrowserContextExtendInjectedScriptParams): Promise<void> {
|
||||
await this._context.extendInjectedScript(params.source, parseArgument(params.arg));
|
||||
async exposeConsoleApi(): Promise<void> {
|
||||
await this._context.exposeConsoleApi();
|
||||
}
|
||||
|
||||
async enableRecorder(): Promise<void> {
|
||||
await this._context.enableRecorder();
|
||||
}
|
||||
|
||||
async crNewCDPSession(params: channels.BrowserContextCrNewCDPSessionParams): Promise<channels.BrowserContextCrNewCDPSessionResult> {
|
||||
|
|
|
@ -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 { installDebugController } from './debug/debugController';
|
||||
import { installInspectorController } from './server/inspector/inspectorController';
|
||||
import { installTracer } from './trace/tracer';
|
||||
import { installHarTracer } from './trace/harTracer';
|
||||
import * as path from 'path';
|
||||
|
@ -28,7 +28,7 @@ import * as path from 'path';
|
|||
function setupInProcess(): PlaywrightAPI {
|
||||
const playwright = new PlaywrightImpl(path.join(__dirname, '..'), require('../browsers.json')['browsers']);
|
||||
|
||||
installDebugController();
|
||||
installInspectorController();
|
||||
installTracer();
|
||||
installHarTracer();
|
||||
|
||||
|
|
|
@ -552,7 +552,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>;
|
||||
extendInjectedScript(params: BrowserContextExtendInjectedScriptParams, metadata?: Metadata): Promise<BrowserContextExtendInjectedScriptResult>;
|
||||
exposeConsoleApi(params?: BrowserContextExposeConsoleApiParams, metadata?: Metadata): Promise<BrowserContextExposeConsoleApiResult>;
|
||||
enableRecorder(params?: BrowserContextEnableRecorderParams, metadata?: Metadata): Promise<BrowserContextEnableRecorderResult>;
|
||||
crNewCDPSession(params: BrowserContextCrNewCDPSessionParams, metadata?: Metadata): Promise<BrowserContextCrNewCDPSessionResult>;
|
||||
}
|
||||
export type BrowserContextBindingCallEvent = {
|
||||
|
@ -694,14 +695,12 @@ export type BrowserContextStorageStateResult = {
|
|||
cookies: NetworkCookie[],
|
||||
origins: OriginStorage[],
|
||||
};
|
||||
export type BrowserContextExtendInjectedScriptParams = {
|
||||
source: string,
|
||||
arg: SerializedArgument,
|
||||
};
|
||||
export type BrowserContextExtendInjectedScriptOptions = {
|
||||
|
||||
};
|
||||
export type BrowserContextExtendInjectedScriptResult = void;
|
||||
export type BrowserContextExposeConsoleApiParams = {};
|
||||
export type BrowserContextExposeConsoleApiOptions = {};
|
||||
export type BrowserContextExposeConsoleApiResult = void;
|
||||
export type BrowserContextEnableRecorderParams = {};
|
||||
export type BrowserContextEnableRecorderOptions = {};
|
||||
export type BrowserContextEnableRecorderResult = void;
|
||||
export type BrowserContextCrNewCDPSessionParams = {
|
||||
page: PageChannel,
|
||||
};
|
||||
|
|
|
@ -599,11 +599,11 @@ BrowserContext:
|
|||
type: array
|
||||
items: OriginStorage
|
||||
|
||||
extendInjectedScript:
|
||||
exposeConsoleApi:
|
||||
experimental: True
|
||||
|
||||
enableRecorder:
|
||||
experimental: True
|
||||
parameters:
|
||||
source: string
|
||||
arg: SerializedArgument
|
||||
|
||||
crNewCDPSession:
|
||||
parameters:
|
||||
|
|
|
@ -335,10 +335,8 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
offline: tBoolean,
|
||||
});
|
||||
scheme.BrowserContextStorageStateParams = tOptional(tObject({}));
|
||||
scheme.BrowserContextExtendInjectedScriptParams = tObject({
|
||||
source: tString,
|
||||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.BrowserContextExposeConsoleApiParams = tOptional(tObject({}));
|
||||
scheme.BrowserContextEnableRecorderParams = tOptional(tObject({}));
|
||||
scheme.BrowserContextCrNewCDPSessionParams = tObject({
|
||||
page: tChannel('Page'),
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import * as debug from 'debug';
|
||||
import * as http from 'http';
|
||||
import * as WebSocket from 'ws';
|
||||
import { installDebugController } from '../debug/debugController';
|
||||
import { installInspectorController } from '../server/inspector/inspectorController';
|
||||
import { DispatcherConnection } from '../dispatchers/dispatcher';
|
||||
import { PlaywrightDispatcher } from '../dispatchers/playwrightDispatcher';
|
||||
import { Playwright } from '../server/playwright';
|
||||
|
@ -27,7 +27,7 @@ import { installHarTracer } from '../trace/harTracer';
|
|||
|
||||
const debugLog = debug('pw:server');
|
||||
|
||||
installDebugController();
|
||||
installInspectorController();
|
||||
installTracer();
|
||||
installHarTracer();
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ import { EventEmitter } from 'events';
|
|||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import { mkdirIfNeeded } from '../utils/utils';
|
||||
import { Browser, BrowserOptions } from './browser';
|
||||
import * as consoleApiSource from '../generated/consoleApiSource';
|
||||
import * as recorderSource from '../generated/recorderSource';
|
||||
import * as dom from './dom';
|
||||
import { Download } from './download';
|
||||
import * as frames from './frames';
|
||||
|
@ -379,8 +381,16 @@ export abstract class BrowserContext extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
async extendInjectedScript(source: string, arg?: any) {
|
||||
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source, arg).catch(e => {});
|
||||
async exposeConsoleApi() {
|
||||
await this._extendInjectedScript(consoleApiSource.source);
|
||||
}
|
||||
|
||||
async enableRecorder() {
|
||||
await this._extendInjectedScript(recorderSource.source);
|
||||
}
|
||||
|
||||
private async _extendInjectedScript(source: string) {
|
||||
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source).catch(e => {});
|
||||
const installInPage = (page: Page) => {
|
||||
page.on(Page.Events.InternalFrameNavigatedToNewDocument, installInFrame);
|
||||
return Promise.all(page.frames().map(installInFrame));
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type InjectedScript from '../../server/injected/injectedScript';
|
||||
import type InjectedScript from '../../injected/injectedScript';
|
||||
import { generateSelector } from './selectorGenerator';
|
||||
|
||||
export class ConsoleAPI {
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
const path = require('path');
|
||||
const InlineSource = require('../../server/injected/webpack-inline-source-plugin');
|
||||
const InlineSource = require('../../injected/webpack-inline-source-plugin');
|
||||
|
||||
/** @type {import('webpack').Configuration} */
|
||||
module.exports = {
|
||||
|
@ -45,6 +45,6 @@ module.exports = {
|
|||
path: path.resolve(__dirname, '../../../lib/server/injected/packed')
|
||||
},
|
||||
plugins: [
|
||||
new InlineSource(path.join(__dirname, '..', '..', 'generated', 'consoleApiSource.ts')),
|
||||
new InlineSource(path.join(__dirname, '..', '..', '..', 'generated', 'consoleApiSource.ts')),
|
||||
]
|
||||
};
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type * as actions from '../codegen/recorderActions';
|
||||
import type InjectedScript from '../../server/injected/injectedScript';
|
||||
import { generateSelector } from '../../debug/injected/selectorGenerator';
|
||||
import type * as actions from '../../../cli/codegen/recorderActions';
|
||||
import type InjectedScript from '../../injected/injectedScript';
|
||||
import { generateSelector } from './selectorGenerator';
|
||||
import { html } from './html';
|
||||
|
||||
declare global {
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
const path = require('path');
|
||||
const InlineSource = require('../../server/injected/webpack-inline-source-plugin');
|
||||
const InlineSource = require('../../injected/webpack-inline-source-plugin');
|
||||
|
||||
/** @type {import('webpack').Configuration} */
|
||||
module.exports = {
|
||||
|
@ -39,12 +39,12 @@ module.exports = {
|
|||
},
|
||||
output: {
|
||||
libraryTarget: 'var',
|
||||
libraryExport: 'default',
|
||||
library: 'pwExport',
|
||||
libraryExport: 'default',
|
||||
filename: 'recorderSource.js',
|
||||
path: path.resolve(__dirname, '../../../lib/server/injected/packed')
|
||||
},
|
||||
plugins: [
|
||||
new InlineSource(path.join(__dirname, '..', '..', 'generated', 'recorderSource.ts')),
|
||||
new InlineSource(path.join(__dirname, '..', '..', '..', 'generated', 'recorderSource.ts')),
|
||||
]
|
||||
};
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type InjectedScript from '../../server/injected/injectedScript';
|
||||
import type InjectedScript from '../../injected/injectedScript';
|
||||
|
||||
export function generateSelector(injectedScript: InjectedScript, targetElement: Element): { selector: string, elements: Element[] } {
|
||||
const path: SelectorToken[] = [];
|
|
@ -14,18 +14,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserContext, ContextListener, contextListeners } from '../server/browserContext';
|
||||
import { isDebugMode } from '../utils/utils';
|
||||
import * as consoleApiSource from '../generated/consoleApiSource';
|
||||
import { BrowserContext, ContextListener, contextListeners } from '../browserContext';
|
||||
import { isDebugMode } from '../../utils/utils';
|
||||
|
||||
export function installDebugController() {
|
||||
contextListeners.add(new DebugController());
|
||||
export function installInspectorController() {
|
||||
contextListeners.add(new InspectorController());
|
||||
}
|
||||
|
||||
class DebugController implements ContextListener {
|
||||
class InspectorController implements ContextListener {
|
||||
async onContextCreated(context: BrowserContext): Promise<void> {
|
||||
if (isDebugMode())
|
||||
context.extendInjectedScript(consoleApiSource.source);
|
||||
context.exposeConsoleApi();
|
||||
}
|
||||
async onContextWillDestroy(context: BrowserContext): Promise<void> {}
|
||||
async onContextDidDestroy(context: BrowserContext): Promise<void> {}
|
|
@ -82,26 +82,3 @@ it('exposeBindingHandle should work', async ({context}) => {
|
|||
expect(await target.evaluate(x => x.foo)).toBe(42);
|
||||
expect(result).toEqual(17);
|
||||
});
|
||||
|
||||
it('extendInjectedScript should work', async ({ context, server }) => {
|
||||
await (context as any)._extendInjectedScript(`var pwExport = (() => {
|
||||
class Foo {
|
||||
constructor() {
|
||||
window._counter = (window._counter || 0) + 1;
|
||||
}
|
||||
}
|
||||
return Foo;
|
||||
})()`);
|
||||
|
||||
const page = await context.newPage();
|
||||
await page.waitForFunction(() => (window as any)._counter === 1);
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.waitForFunction(() => (window as any)._counter === 1);
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.evaluate(() => history.pushState({}, '', '/url.html'))
|
||||
]);
|
||||
expect(await page.evaluate(() => (window as any)._counter)).toBe(1);
|
||||
});
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
|
||||
import { folio } from './fixtures';
|
||||
import type { Page, Frame } from '..';
|
||||
import { source } from '../src/generated/consoleApiSource';
|
||||
|
||||
const fixtures = folio.extend();
|
||||
fixtures.context.override(async ({ context }, run) => {
|
||||
await (context as any)._extendInjectedScript(source);
|
||||
await (context as any)._exposeConsoleApi();
|
||||
await run(context);
|
||||
});
|
||||
const { describe, it, expect } = fixtures.build();
|
||||
|
|
|
@ -68,8 +68,8 @@ function runBuild() {
|
|||
const webPackFiles = [
|
||||
'src/server/injected/injectedScript.webpack.config.js',
|
||||
'src/server/injected/utilityScript.webpack.config.js',
|
||||
'src/debug/injected/consoleApi.webpack.config.js',
|
||||
'src/cli/injected/recorder.webpack.config.js',
|
||||
'src/server/inspector/injected/consoleApi.webpack.config.js',
|
||||
'src/server/inspector/injected/recorder.webpack.config.js',
|
||||
'src/cli/traceViewer/web/web.webpack.config.js',
|
||||
];
|
||||
for (const file of webPackFiles) {
|
||||
|
|
|
@ -131,6 +131,7 @@ DEPS['src/server/'] = [
|
|||
DEPS['src/server/common/'] = [];
|
||||
// Strict dependencies for injected code.
|
||||
DEPS['src/server/injected/'] = ['src/server/common/'];
|
||||
DEPS['src/server/inspector/injected/'] = ['src/server/common/', 'src/cli/codegen/', 'src/server/injected/'];
|
||||
|
||||
// Electron and Clank use chromium internally.
|
||||
DEPS['src/server/android/'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/protocol/'];
|
||||
|
@ -142,11 +143,8 @@ DEPS['src/cli/driver.ts'] = DEPS['src/inprocess.ts'] = DEPS['src/browserServerIm
|
|||
// Tracing is a client/server plugin, nothing should depend on it.
|
||||
DEPS['src/trace/'] = ['src/utils/', 'src/client/**', 'src/server/**'];
|
||||
|
||||
// Debug is a server plugin, nothing should depend on it.
|
||||
DEPS['src/debug/'] = ['src/utils/', 'src/generated/', 'src/server/**', 'src/debug/**'];
|
||||
|
||||
// The service is a cross-cutting feature, and so it depends on a bunch of things.
|
||||
DEPS['src/remote/'] = ['src/client/', 'src/debug/', 'src/dispatchers/', 'src/server/', 'src/server/electron/', 'src/trace/'];
|
||||
DEPS['src/remote/'] = ['src/client/', 'src/debug/', 'src/dispatchers/', 'src/server/', 'src/server/inspector/', 'src/server/electron/', 'src/trace/'];
|
||||
DEPS['src/service.ts'] = ['src/remote/'];
|
||||
|
||||
// CLI should only use client-side features.
|
||||
|
|
Загрузка…
Ссылка в новой задаче