chore: introduce "instrumentation" that is used for debug and trace (#3775)

This commit is contained in:
Dmitry Gozman 2020-09-04 16:31:52 -07:00 committed by GitHub
parent 25fe115719
commit 675ce00432
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 216 additions and 148 deletions

View File

@ -0,0 +1,61 @@
/**
* 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 '../server/browserContext';
import * as frames from '../server/frames';
import * as js from '../server/javascript';
import { Page } from '../server/page';
import { InstrumentingAgent } from '../server/instrumentation';
import type DebugScript from './injected/debugScript';
import { Progress } from '../server/progress';
import { isDebugMode } from '../utils/utils';
import * as debugScriptSource from '../generated/debugScriptSource';
const debugScriptSymbol = Symbol('debugScript');
export class DebugController implements InstrumentingAgent {
private async ensureInstalledInFrame(frame: frames.Frame) {
try {
const mainContext = await frame._mainContext();
if ((mainContext as any)[debugScriptSymbol])
return;
(mainContext as any)[debugScriptSymbol] = true;
const objectId = await mainContext._delegate.rawEvaluate(`new (${debugScriptSource.source})()`);
const debugScript = new js.JSHandle(mainContext, 'object', objectId) as js.JSHandle<DebugScript>;
const injectedScript = await mainContext.injectedScript();
await debugScript.evaluate((debugScript, injectedScript) => {
debugScript.initialize(injectedScript, { console: true });
}, injectedScript);
} catch (e) {
}
}
async onContextCreated(context: BrowserContext): Promise<void> {
if (!isDebugMode())
return;
context.on(BrowserContext.Events.Page, (page: Page) => {
for (const frame of page.frames())
this.ensureInstalledInFrame(frame);
page.on(Page.Events.FrameNavigated, frame => this.ensureInstalledInFrame(frame));
});
}
async onContextDestroyed(context: BrowserContext): Promise<void> {
}
async onBeforePageAction(page: Page, progress: Progress): Promise<void> {
}
}

View File

@ -14,8 +14,8 @@
* limitations under the License.
*/
import { ParsedSelector, parseSelector } from '../../common/selectorParser';
import type InjectedScript from '../../injected/injectedScript';
import { ParsedSelector, parseSelector } from '../../server/common/selectorParser';
import type InjectedScript from '../../server/injected/injectedScript';
import { html } from './html';
export class ConsoleAPI {

View File

@ -15,7 +15,7 @@
*/
import { ConsoleAPI } from './consoleApi';
import type InjectedScript from '../../injected/injectedScript';
import type InjectedScript from '../../server/injected/injectedScript';
export default class DebugScript {
consoleAPI: ConsoleAPI | undefined;

View File

@ -15,7 +15,7 @@
*/
const path = require('path');
const InlineSource = require('../../injected/webpack-inline-source-plugin');
const InlineSource = require('../../server/injected/webpack-inline-source-plugin');
module.exports = {
entry: path.join(__dirname, 'debugScript.ts'),
@ -38,9 +38,9 @@ module.exports = {
output: {
libraryTarget: 'var',
filename: 'debugScriptSource.js',
path: path.resolve(__dirname, '../../../../lib/server/injected/packed')
path: path.resolve(__dirname, '../../../lib/server/injected/packed')
},
plugins: [
new InlineSource(path.join(__dirname, '..', '..', '..', 'generated', 'debugScriptSource.ts')),
new InlineSource(path.join(__dirname, '..', '..', 'generated', 'debugScriptSource.ts')),
]
};

View File

@ -21,8 +21,12 @@ import { PlaywrightDispatcher } from './dispatchers/playwrightDispatcher';
import { Connection } from './client/connection';
import { BrowserServerLauncherImpl } from './browserServerImpl';
import { isDevMode } from './utils/utils';
import { instrumentingAgents } from './server/instrumentation';
import { DebugController } from './debug/debugController';
export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI {
instrumentingAgents.add(new DebugController());
const clientConnection = new Connection();
const dispatcherConnection = new DispatcherConnection();

View File

@ -20,6 +20,10 @@ import { Playwright } from './server/playwright';
import { PlaywrightDispatcher } from './dispatchers/playwrightDispatcher';
import { Electron } from './server/electron/electron';
import { gracefullyCloseAll } from './server/processLauncher';
import { instrumentingAgents } from './server/instrumentation';
import { DebugController } from './debug/debugController';
instrumentingAgents.add(new DebugController());
const dispatcherConnection = new DispatcherConnection();
const transport = new Transport(process.stdout, process.stdin);

View File

@ -27,10 +27,8 @@ import { Download } from './download';
import { Browser } from './browser';
import { EventEmitter } from 'events';
import { Progress } from './progress';
import { DebugController } from './debug/debugController';
import { isDebugMode } from '../utils/utils';
import { Snapshotter, SnapshotterDelegate } from './snapshotter';
import { Selectors, serverSelectors } from './selectors';
import { instrumentingAgents } from './instrumentation';
export class Screencast {
readonly page: Page;
@ -70,7 +68,6 @@ export abstract class BrowserContext extends EventEmitter {
readonly _browser: Browser;
readonly _browserContextId: string | undefined;
private _selectors?: Selectors;
_snapshotter?: Snapshotter;
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
super();
@ -90,14 +87,8 @@ export abstract class BrowserContext extends EventEmitter {
}
async _initialize() {
if (isDebugMode())
new DebugController(this);
}
// Used by test runner.
async _initSnapshotter(delegate: SnapshotterDelegate): Promise<Snapshotter> {
this._snapshotter = new Snapshotter(this, delegate);
return this._snapshotter;
for (const agent of instrumentingAgents)
await agent.onContextCreated(this);
}
_browserClosed() {
@ -250,8 +241,8 @@ export abstract class BrowserContext extends EventEmitter {
this._closedStatus = 'closing';
await this._doClose();
await Promise.all([...this._downloads].map(d => d.delete()));
if (this._snapshotter)
this._snapshotter._dispose();
for (const agent of instrumentingAgents)
await agent.onContextDestroyed(this);
this._didCloseInternal();
}
await this._closePromise;

View File

@ -1,39 +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 { BrowserContext } from '../browserContext';
import * as frames from '../frames';
import * as js from '../javascript';
import { Page } from '../page';
import type DebugScript from './injected/debugScript';
export class DebugController {
constructor(context: BrowserContext) {
context.on(BrowserContext.Events.Page, (page: Page) => {
for (const frame of page.frames())
this.ensureInstalledInFrame(frame);
page.on(Page.Events.FrameNavigated, frame => this.ensureInstalledInFrame(frame));
});
}
private async ensureInstalledInFrame(frame: frames.Frame): Promise<js.JSHandle<DebugScript> | undefined> {
try {
const mainContext = await frame._mainContext();
return await mainContext.createDebugScript({ console: true });
} catch (e) {
}
}
}

View File

@ -18,13 +18,11 @@ import * as frames from './frames';
import { assert } from '../utils/utils';
import type { InjectedScript, InjectedScriptPoll } from './injected/injectedScript';
import * as injectedScriptSource from '../generated/injectedScriptSource';
import * as debugScriptSource from '../generated/debugScriptSource';
import * as js from './javascript';
import { Page } from './page';
import { SelectorInfo } from './selectors';
import * as types from './types';
import { Progress } from './progress';
import type DebugScript from './debug/injected/debugScript';
import { FatalDOMError, RetargetableDOMError } from './common/domErrors';
export class FrameExecutionContext extends js.ExecutionContext {
@ -92,18 +90,6 @@ export class FrameExecutionContext extends js.ExecutionContext {
return this._injectedScriptPromise;
}
createDebugScript(options: { console?: boolean }): Promise<js.JSHandle<DebugScript> | undefined> {
if (!this._debugScriptPromise) {
const source = `new (${debugScriptSource.source})()`;
this._debugScriptPromise = this._delegate.rawEvaluate(source).then(objectId => new js.JSHandle(this, 'object', objectId)).then(async debugScript => {
const injectedScript = await this.injectedScript();
await debugScript.evaluate((debugScript: DebugScript, { injectedScript, options }) => debugScript.initialize(injectedScript, options), { injectedScript, options });
return debugScript;
}).catch(e => undefined);
}
return this._debugScriptPromise;
}
async doSlowMo() {
return this.frame._page._doSlowMo();
}

View File

@ -0,0 +1,27 @@
/**
* 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';
import { Page } from './page';
import { Progress } from './progress';
export interface InstrumentingAgent {
onContextCreated(context: BrowserContext): Promise<void>;
onContextDestroyed(context: BrowserContext): Promise<void>;
onBeforePageAction(page: Page, progress: Progress): Promise<void>;
}
export const instrumentingAgents = new Set<InstrumentingAgent>();

View File

@ -32,6 +32,7 @@ import { Progress, runAbortableTask } from './progress';
import { assert, isError } from '../utils/utils';
import { debugLogger } from '../utils/debugLogger';
import { Selectors } from './selectors';
import { instrumentingAgents } from './instrumentation';
export interface PageDelegate {
readonly rawMouse: input.RawMouse;
@ -199,8 +200,8 @@ export class Page extends EventEmitter {
async _runAbortableTask<T>(task: (progress: Progress) => Promise<T>, timeout: number): Promise<T> {
return runAbortableTask(async progress => {
if (this._browserContext._snapshotter)
await this._browserContext._snapshotter._doSnapshot(progress, this, 'progress');
for (const agent of instrumentingAgents)
await agent.onBeforePageAction(this, progress);
return task(progress);
}, timeout);
}

View File

@ -14,15 +14,14 @@
* limitations under the License.
*/
import { BrowserContext } from './browserContext';
import { Page } from './page';
import * as network from './network';
import { helper, RegisteredListener } from './helper';
import { Progress, runAbortableTask } from './progress';
import { BrowserContext } from '../server/browserContext';
import { Page } from '../server/page';
import * as network from '../server/network';
import { helper, RegisteredListener } from '../server/helper';
import { Progress } from '../server/progress';
import { debugLogger } from '../utils/debugLogger';
import { Frame } from './frames';
import * as js from './javascript';
import * as types from './types';
import { Frame } from '../server/frames';
import * as js from '../server/javascript';
import { SnapshotData, takeSnapshotInFrame } from './snapshotterInjected';
import { assert, calculateSha1, createGuid } from '../utils/utils';
@ -53,11 +52,9 @@ export type PageSnapshot = {
};
export interface SnapshotterDelegate {
onContextCreated(context: BrowserContext): void;
onContextDestroyed(context: BrowserContext): void;
onBlob(context: BrowserContext, blob: SnapshotterBlob): void;
onResource(context: BrowserContext, resource: SanpshotterResource): void;
onSnapshot(context: BrowserContext, snapshot: PageSnapshot): void;
onBlob(blob: SnapshotterBlob): void;
onResource(resource: SanpshotterResource): void;
onSnapshot(snapshot: PageSnapshot): void;
}
export class Snapshotter {
@ -71,25 +68,17 @@ export class Snapshotter {
this._eventListeners = [
helper.addEventListener(this._context, BrowserContext.Events.Page, this._onPage.bind(this)),
];
this._delegate.onContextCreated(this._context);
}
async captureSnapshot(page: Page, options: types.TimeoutOptions & { label?: string } = {}): Promise<void> {
return runAbortableTask(async progress => {
await this._doSnapshot(progress, page, options.label || 'snapshot');
}, page._timeoutSettings.timeout(options));
}
_dispose() {
dispose() {
helper.removeEventListeners(this._eventListeners);
this._delegate.onContextDestroyed(this._context);
}
async _doSnapshot(progress: Progress, page: Page, label: string): Promise<void> {
async takeSnapshot(progress: Progress, page: Page, label: string): Promise<void> {
assert(page.context() === this._context);
const snapshot = await this._snapshotPage(progress, page, label);
if (snapshot)
this._delegate.onSnapshot(this._context, snapshot);
this._delegate.onSnapshot(snapshot);
}
private _onPage(page: Page) {
@ -124,9 +113,9 @@ export class Snapshotter {
responseHeaders: response.headers(),
sha1,
};
this._delegate.onResource(this._context, resource);
this._delegate.onResource(resource);
if (body)
this._delegate.onBlob(this._context, { sha1, buffer: body });
this._delegate.onBlob({ sha1, buffer: body });
}
private async _snapshotPage(progress: Progress, page: Page, label: string): Promise<PageSnapshot | null> {
@ -214,7 +203,7 @@ export class Snapshotter {
for (const { url, content } of data.resourceOverrides) {
const buffer = Buffer.from(content);
const sha1 = calculateSha1(buffer);
this._delegate.onBlob(this._context, { sha1, buffer });
this._delegate.onBlob({ sha1, buffer });
snapshot.resourceOverrides.push({ url, sha1 });
}

View File

@ -14,8 +14,6 @@
* limitations under the License.
*/
import type * as snapshotter from '../server/snapshotter';
export type ContextCreatedTraceEvent = {
type: 'context-created',
browserName: string,
@ -46,6 +44,3 @@ export type SnapshotTraceEvent = {
label: string,
sha1: string,
};
export type FrameSnapshot = snapshotter.FrameSnapshot;
export type PageSnapshot = snapshotter.PageSnapshot;

View File

@ -17,7 +17,8 @@
import * as path from 'path';
import * as util from 'util';
import * as fs from 'fs';
import { NetworkResourceTraceEvent, SnapshotTraceEvent, ContextCreatedTraceEvent, ContextDestroyedTraceEvent, FrameSnapshot, PageSnapshot } from './traceTypes';
import type { NetworkResourceTraceEvent, SnapshotTraceEvent, ContextCreatedTraceEvent, ContextDestroyedTraceEvent } from './traceTypes';
import type { FrameSnapshot, PageSnapshot } from './snapshotter';
import type { Browser, BrowserContext, Frame, Page, Route } from '../client/api';
import type { Playwright } from '../client/playwright';

View File

@ -15,59 +15,96 @@
*/
import type { BrowserContext } from '../server/browserContext';
import type { PageSnapshot, SanpshotterResource, SnapshotterBlob, SnapshotterDelegate } from '../server/snapshotter';
import type { PageSnapshot, SanpshotterResource, SnapshotterBlob, SnapshotterDelegate } from './snapshotter';
import { ContextCreatedTraceEvent, ContextDestroyedTraceEvent, NetworkResourceTraceEvent, SnapshotTraceEvent } from './traceTypes';
import * as path from 'path';
import * as util from 'util';
import * as fs from 'fs';
import { calculateSha1, createGuid, mkdirIfNeeded, monotonicTime } from '../utils/utils';
import { InstrumentingAgent, instrumentingAgents } from '../server/instrumentation';
import { Page } from '../server/page';
import { Progress, runAbortableTask } from '../server/progress';
import { Snapshotter } from './snapshotter';
import * as types from '../server/types';
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
const fsAppendFileAsync = util.promisify(fs.appendFile.bind(fs));
const fsAccessAsync = util.promisify(fs.access.bind(fs));
export class Tracer implements SnapshotterDelegate {
private _contextIds = new Map<BrowserContext, string>();
export class Tracer implements InstrumentingAgent {
private _contextTracers = new Map<BrowserContext, ContextTracer>();
constructor() {
instrumentingAgents.add(this);
}
dispose() {
instrumentingAgents.delete(this);
}
traceContext(context: BrowserContext, traceStorageDir: string, traceFile: string) {
const contextTracer = new ContextTracer(context, traceStorageDir, traceFile);
this._contextTracers.set(context, contextTracer);
}
async captureSnapshot(page: Page, options: types.TimeoutOptions & { label?: string } = {}): Promise<void> {
return runAbortableTask(async progress => {
const contextTracer = this._contextTracers.get(page.context());
if (contextTracer)
await contextTracer._snapshotter.takeSnapshot(progress, page, options.label || 'snapshot');
}, page._timeoutSettings.timeout(options));
}
async onContextCreated(context: BrowserContext): Promise<void> {
}
async onContextDestroyed(context: BrowserContext): Promise<void> {
const contextTracer = this._contextTracers.get(context);
if (contextTracer) {
await contextTracer.dispose();
this._contextTracers.delete(context);
}
}
async onBeforePageAction(page: Page, progress: Progress): Promise<void> {
const contextTracer = this._contextTracers.get(page.context());
if (contextTracer)
await contextTracer._snapshotter.takeSnapshot(progress, page, 'progress');
}
}
class ContextTracer implements SnapshotterDelegate {
private _contextId: string;
private _traceStoragePromise: Promise<string>;
private _appendEventChain: Promise<string>;
private _writeArtifactChain: Promise<void>;
readonly _snapshotter: Snapshotter;
constructor(traceStorageDir: string, traceFile: string) {
constructor(context: BrowserContext, traceStorageDir: string, traceFile: string) {
this._contextId = 'context@' + createGuid();
this._traceStoragePromise = mkdirIfNeeded(path.join(traceStorageDir, 'sha1')).then(() => traceStorageDir);
this._appendEventChain = mkdirIfNeeded(traceFile).then(() => traceFile);
this._writeArtifactChain = Promise.resolve();
}
onContextCreated(context: BrowserContext): void {
const contextId = 'context@' + createGuid();
this._contextIds.set(context, contextId);
const event: ContextCreatedTraceEvent = {
type: 'context-created',
browserName: context._browser._options.name,
contextId,
contextId: this._contextId,
isMobile: !!context._options.isMobile,
deviceScaleFactor: context._options.deviceScaleFactor || 1,
viewportSize: context._options.viewport || undefined,
};
this._appendTraceEvent(event);
this._snapshotter = new Snapshotter(context, this);
}
onContextDestroyed(context: BrowserContext): void {
const event: ContextDestroyedTraceEvent = {
type: 'context-destroyed',
contextId: this._contextIds.get(context)!,
};
this._appendTraceEvent(event);
}
onBlob(context: BrowserContext, blob: SnapshotterBlob): void {
onBlob(blob: SnapshotterBlob): void {
this._writeArtifact(blob.sha1, blob.buffer);
}
onResource(context: BrowserContext, resource: SanpshotterResource): void {
onResource(resource: SanpshotterResource): void {
const event: NetworkResourceTraceEvent = {
type: 'resource',
contextId: this._contextIds.get(context)!,
contextId: this._contextId,
frameId: resource.frameId,
url: resource.url,
contentType: resource.contentType,
@ -77,12 +114,12 @@ export class Tracer implements SnapshotterDelegate {
this._appendTraceEvent(event);
}
onSnapshot(context: BrowserContext, snapshot: PageSnapshot): void {
onSnapshot(snapshot: PageSnapshot): void {
const buffer = Buffer.from(JSON.stringify(snapshot));
const sha1 = calculateSha1(buffer);
const event: SnapshotTraceEvent = {
type: 'snapshot',
contextId: this._contextIds.get(context)!,
contextId: this._contextId,
label: snapshot.label,
sha1,
};
@ -91,6 +128,13 @@ export class Tracer implements SnapshotterDelegate {
}
async dispose() {
this._snapshotter.dispose();
const event: ContextDestroyedTraceEvent = {
type: 'context-destroyed',
contextId: this._contextId,
};
this._appendTraceEvent(event);
// Ensure all writes are finished.
await this._appendEventChain;
await this._writeArtifactChain;

View File

@ -137,7 +137,14 @@ registerWorkerFixture('playwright', async ({browserName}, test) => {
spawnedProcess.stderr.destroy();
await teardownCoverage();
} else {
await test(require('../index'));
const playwright = require('../index');
if (options.TRACING) {
const tracerFactory = require('../lib/trace/tracer').Tracer;
playwright.__tracer = new tracerFactory();
}
await test(playwright);
if (playwright.__tracer)
playwright.__tracer.dispose();
await teardownCoverage();
}
@ -148,7 +155,6 @@ registerWorkerFixture('playwright', async ({browserName}, test) => {
await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true });
await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8');
}
});
registerWorkerFixture('toImpl', async ({playwright}, test) => {
@ -182,25 +188,21 @@ registerWorkerFixture('golden', async ({browserName}, test) => {
await test(p => path.join(browserName, p));
});
registerFixture('context', async ({browser, toImpl}, runTest, info) => {
registerFixture('context', async ({browser, playwright, toImpl}, runTest, info) => {
const context = await browser.newContext();
const { test, config } = info;
if (options.TRACING) {
if ((playwright as any).__tracer) {
const traceStorageDir = path.join(config.outputDir, 'trace-storage');
const relativePath = path.relative(config.testDir, test.file).replace(/\.spec\.[jt]s/, '');
const sanitizedTitle = test.title.replace(/[^\w\d]+/g, '_');
const traceFile = path.join(config.outputDir, relativePath, sanitizedTitle + '.trace');
const tracerFactory = require('../lib/trace/tracer').Tracer;
(context as any).__tracer = new tracerFactory(traceStorageDir, traceFile);
(context as any).__snapshotter = await toImpl(context)._initSnapshotter((context as any).__tracer);
(playwright as any).__tracer.traceContext(toImpl(context), traceStorageDir, traceFile);
}
await runTest(context);
await context.close();
if ((context as any).__tracer)
await (context as any).__tracer.dispose();
});
registerFixture('page', async ({context, toImpl}, runTest, info) => {
registerFixture('page', async ({context, playwright, toImpl}, runTest, info) => {
const page = await context.newPage();
await runTest(page);
const { test, config, result } = info;
@ -209,8 +211,8 @@ registerFixture('page', async ({context, toImpl}, runTest, info) => {
const sanitizedTitle = test.title.replace(/[^\w\d]+/g, '_');
const assetPath = path.join(config.outputDir, relativePath, sanitizedTitle) + '-failed.png';
await page.screenshot({ timeout: 5000, path: assetPath });
if ((context as any).__snapshotter)
await (context as any).__snapshotter.captureSnapshot(toImpl(page), { timeout: 5000, label: 'Test Failed' });
if ((playwright as any).__tracer)
await (playwright as any).__tracer.captureSnapshot(toImpl(page), { timeout: 5000, label: 'Test Failed' });
}
});

View File

@ -18,7 +18,7 @@ import { it, options } from './playwright.fixtures';
it('should not throw', test => {
test.skip(!options.TRACING);
}, async ({page, server, context, toImpl}) => {
}, async ({page, server, playwright, toImpl}) => {
await page.goto(server.PREFIX + '/snapshot/snapshot-with-css.html');
await (context as any).__snapshotter.captureSnapshot(toImpl(page), { timeout: 5000, label: 'snapshot' });
await (playwright as any).__tracer.captureSnapshot(toImpl(page), { timeout: 5000, label: 'snapshot' });
});

View File

@ -103,7 +103,6 @@ DEPS['src/server/'] = [
// Can depend on any files in these subdirectories.
'src/server/common/**',
'src/server/injected/**',
'src/server/debug/**',
];
// No dependencies for code shared between node and page.
@ -117,7 +116,10 @@ DEPS['src/server/electron/'] = [...DEPS['src/server/'], 'src/server/chromium/'];
DEPS['src/server/playwright.ts'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/server/webkit/', 'src/server/firefox/'];
DEPS['src/server.ts'] = DEPS['src/inprocess.ts'] = DEPS['src/browserServerImpl.ts'] = ['src/**'];
// Tracing depends on client and server, but nothing should depend on tracing.
// 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/**'];
checkDeps();

View File

@ -20,7 +20,7 @@ const path = require('path');
const files = [
path.join('src', 'server', 'injected', 'injectedScript.webpack.config.js'),
path.join('src', 'server', 'injected', 'utilityScript.webpack.config.js'),
path.join('src', 'server', 'debug', 'injected', 'debugScript.webpack.config.js'),
path.join('src', 'debug', 'injected', 'debugScript.webpack.config.js'),
];
function runOne(runner, file) {