Skip to content

Commit be27f47

Browse files
authored
feat(tracing): introduce context.tracing, allow exporting trace (#6313)
1 parent a9219aa commit be27f47

File tree

18 files changed

+305
-163
lines changed

18 files changed

+305
-163
lines changed

package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,15 @@
5151
"proxy-from-env": "^1.1.0",
5252
"rimraf": "^3.0.2",
5353
"stack-utils": "^2.0.3",
54-
"ws": "^7.3.1"
54+
"ws": "^7.3.1",
55+
"yazl": "^2.5.1"
5556
},
5657
"devDependencies": {
58+
"@storybook/addon-actions": "^6.1.20",
59+
"@storybook/addon-essentials": "^6.1.20",
60+
"@storybook/addon-links": "^6.1.20",
61+
"@storybook/node-logger": "^6.1.20",
62+
"@storybook/react": "^6.1.20",
5763
"@types/debug": "^4.1.5",
5864
"@types/extract-zip": "^1.6.2",
5965
"@types/mime": "^2.0.3",
@@ -68,6 +74,7 @@
6874
"@types/rimraf": "^3.0.0",
6975
"@types/webpack": "^4.41.25",
7076
"@types/ws": "7.2.6",
77+
"@types/yazl": "^2.4.2",
7178
"@typescript-eslint/eslint-plugin": "^3.10.1",
7279
"@typescript-eslint/parser": "^3.10.1",
7380
"chokidar": "^3.5.0",
@@ -88,11 +95,6 @@
8895
"react": "^17.0.1",
8996
"react-dom": "^17.0.1",
9097
"socksv5": "0.0.6",
91-
"@storybook/addon-actions": "^6.1.20",
92-
"@storybook/addon-essentials": "^6.1.20",
93-
"@storybook/addon-links": "^6.1.20",
94-
"@storybook/node-logger": "^6.1.20",
95-
"@storybook/react": "^6.1.20",
9698
"style-loader": "^1.2.1",
9799
"ts-loader": "^8.0.3",
98100
"typescript": "^4.0.2",

src/client/browserContext.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { isSafeCloseError } from '../utils/errors';
3333
import * as api from '../../types/types';
3434
import * as structs from '../../types/structs';
3535
import { CDPSession } from './cdpSession';
36+
import { Tracing } from './tracing';
3637

3738
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
3839
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
@@ -49,6 +50,8 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
4950
sdkLanguage: 'javascript'
5051
};
5152

53+
readonly _tracing: Tracing;
54+
5255
readonly _backgroundPages = new Set<Page>();
5356
readonly _serviceWorkers = new Set<Worker>();
5457
readonly _isChromium: boolean;
@@ -66,6 +69,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
6669
if (parent instanceof Browser)
6770
this._browser = parent;
6871
this._isChromium = this._browser?._name === 'chromium';
72+
this._tracing = new Tracing(this);
6973

7074
this._channel.on('bindingCall', ({binding}) => this._onBinding(BindingCall.from(binding)));
7175
this._channel.on('close', () => this._onClose());
@@ -279,18 +283,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
279283
this.emit(Events.BrowserContext.Close, this);
280284
}
281285

282-
async _startTracing() {
283-
return await this._wrapApiCall('browserContext.startTracing', async (channel: channels.BrowserContextChannel) => {
284-
await channel.startTracing();
285-
});
286-
}
287-
288-
async _stopTracing() {
289-
return await this._wrapApiCall('browserContext.stopTracing', async (channel: channels.BrowserContextChannel) => {
290-
await channel.stopTracing();
291-
});
292-
}
293-
294286
async close(): Promise<void> {
295287
try {
296288
await this._wrapApiCall('browserContext.close', async (channel: channels.BrowserContextChannel) => {

src/client/tracing.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as channels from '../protocol/channels';
18+
import { Artifact } from './artifact';
19+
import { BrowserContext } from './browserContext';
20+
21+
export class Tracing {
22+
private _context: BrowserContext;
23+
24+
constructor(channel: BrowserContext) {
25+
this._context = channel;
26+
}
27+
28+
async start(options: { snapshots?: boolean, screenshots?: boolean } = {}) {
29+
await this._context._wrapApiCall('tracing.start', async (channel: channels.BrowserContextChannel) => {
30+
return await channel.tracingStart(options);
31+
});
32+
}
33+
34+
async stop() {
35+
await this._context._wrapApiCall('tracing.stop', async (channel: channels.BrowserContextChannel) => {
36+
await channel.tracingStop();
37+
});
38+
}
39+
40+
async export(path: string): Promise<void> {
41+
const result = await this._context._wrapApiCall('tracing.export', async (channel: channels.BrowserContextChannel) => {
42+
return await channel.tracingExport();
43+
});
44+
const artifact = Artifact.from(result.artifact);
45+
if (this._context.browser()?._isRemote)
46+
artifact._isRemote = true;
47+
await artifact.saveAs(path);
48+
await artifact.delete();
49+
}
50+
}

src/dispatchers/artifactDispatcher.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,6 @@ export class ArtifactDispatcher extends Dispatcher<Artifact, channels.ArtifactIn
9494

9595
async delete(): Promise<void> {
9696
await this._object.delete();
97+
this._dispose();
9798
}
9899
}

src/dispatchers/browserContextDispatcher.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,16 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
158158
return { session: new CDPSessionDispatcher(this._scope, await crBrowserContext.newCDPSession((params.page as PageDispatcher)._object)) };
159159
}
160160

161-
async startTracing(params: channels.BrowserContextStartTracingParams): Promise<void> {
162-
await this._context.startTracing();
161+
async tracingStart(params: channels.BrowserContextTracingStartParams): Promise<channels.BrowserContextTracingStartResult> {
162+
await this._context.tracing.start(params);
163163
}
164164

165-
async stopTracing(): Promise<channels.BrowserContextStopTracingResult> {
166-
await this._context.stopTracing();
165+
async tracingStop(params: channels.BrowserContextTracingStopParams): Promise<channels.BrowserContextTracingStopResult> {
166+
await this._context.tracing.stop();
167+
}
168+
169+
async tracingExport(params: channels.BrowserContextTracingExportParams): Promise<channels.BrowserContextTracingExportResult> {
170+
const artifact = await this._context.tracing.export();
171+
return { artifact: new ArtifactDispatcher(this._scope, artifact) };
167172
}
168173
}

src/protocol/channels.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,9 @@ export interface BrowserContextChannel extends Channel {
610610
pause(params?: BrowserContextPauseParams, metadata?: Metadata): Promise<BrowserContextPauseResult>;
611611
recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: Metadata): Promise<BrowserContextRecorderSupplementEnableResult>;
612612
newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: Metadata): Promise<BrowserContextNewCDPSessionResult>;
613-
startTracing(params?: BrowserContextStartTracingParams, metadata?: Metadata): Promise<BrowserContextStartTracingResult>;
614-
stopTracing(params?: BrowserContextStopTracingParams, metadata?: Metadata): Promise<BrowserContextStopTracingResult>;
613+
tracingStart(params: BrowserContextTracingStartParams, metadata?: Metadata): Promise<BrowserContextTracingStartResult>;
614+
tracingStop(params?: BrowserContextTracingStopParams, metadata?: Metadata): Promise<BrowserContextTracingStopResult>;
615+
tracingExport(params?: BrowserContextTracingExportParams, metadata?: Metadata): Promise<BrowserContextTracingExportResult>;
615616
}
616617
export type BrowserContextBindingCallEvent = {
617618
binding: BindingCallChannel,
@@ -788,12 +789,25 @@ export type BrowserContextNewCDPSessionOptions = {
788789
export type BrowserContextNewCDPSessionResult = {
789790
session: CDPSessionChannel,
790791
};
791-
export type BrowserContextStartTracingParams = {};
792-
export type BrowserContextStartTracingOptions = {};
793-
export type BrowserContextStartTracingResult = void;
794-
export type BrowserContextStopTracingParams = {};
795-
export type BrowserContextStopTracingOptions = {};
796-
export type BrowserContextStopTracingResult = void;
792+
export type BrowserContextTracingStartParams = {
793+
name?: string,
794+
snapshots?: boolean,
795+
screenshots?: boolean,
796+
};
797+
export type BrowserContextTracingStartOptions = {
798+
name?: string,
799+
snapshots?: boolean,
800+
screenshots?: boolean,
801+
};
802+
export type BrowserContextTracingStartResult = void;
803+
export type BrowserContextTracingStopParams = {};
804+
export type BrowserContextTracingStopOptions = {};
805+
export type BrowserContextTracingStopResult = void;
806+
export type BrowserContextTracingExportParams = {};
807+
export type BrowserContextTracingExportOptions = {};
808+
export type BrowserContextTracingExportResult = {
809+
artifact: ArtifactChannel,
810+
};
797811

798812
// ----------- Page -----------
799813
export type PageInitializer = {

src/protocol/protocol.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,9 +601,17 @@ BrowserContext:
601601
returns:
602602
session: CDPSession
603603

604-
startTracing:
604+
tracingStart:
605+
parameters:
606+
name: string?
607+
snapshots: boolean?
608+
screenshots: boolean?
605609

606-
stopTracing:
610+
tracingStop:
611+
612+
tracingExport:
613+
returns:
614+
artifact: Artifact
607615

608616
events:
609617

src/protocol/validator.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,13 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
385385
scheme.BrowserContextNewCDPSessionParams = tObject({
386386
page: tChannel('Page'),
387387
});
388-
scheme.BrowserContextStartTracingParams = tOptional(tObject({}));
389-
scheme.BrowserContextStopTracingParams = tOptional(tObject({}));
388+
scheme.BrowserContextTracingStartParams = tObject({
389+
name: tOptional(tString),
390+
snapshots: tOptional(tBoolean),
391+
screenshots: tOptional(tBoolean),
392+
});
393+
scheme.BrowserContextTracingStopParams = tOptional(tObject({}));
394+
scheme.BrowserContextTracingExportParams = tOptional(tObject({}));
390395
scheme.PageSetDefaultNavigationTimeoutNoReplyParams = tObject({
391396
timeout: tNumber,
392397
});

src/server/browserContext.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export abstract class BrowserContext extends SdkObject {
5757
private _selectors?: Selectors;
5858
private _origins = new Set<string>();
5959
private _harTracer: HarTracer | undefined;
60-
private _tracer: Tracer | null = null;
60+
readonly tracing: Tracer;
6161

6262
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
6363
super(browser, 'browser-context');
@@ -70,6 +70,7 @@ export abstract class BrowserContext extends SdkObject {
7070

7171
if (this._options.recordHar)
7272
this._harTracer = new HarTracer(this, this._options.recordHar);
73+
this.tracing = new Tracer(this);
7374
}
7475

7576
_setSelectors(selectors: Selectors) {
@@ -263,7 +264,7 @@ export abstract class BrowserContext extends SdkObject {
263264
this._closedStatus = 'closing';
264265

265266
await this._harTracer?.flush();
266-
await this._tracer?.stop();
267+
await this.tracing.stop();
267268

268269
// Cleanup.
269270
const promises: Promise<void>[] = [];
@@ -370,21 +371,6 @@ export abstract class BrowserContext extends SdkObject {
370371
this.on(BrowserContext.Events.Page, installInPage);
371372
return Promise.all(this.pages().map(installInPage));
372373
}
373-
374-
async startTracing() {
375-
if (this._tracer)
376-
throw new Error('Tracing has already been started');
377-
const traceDir = this._browser.options.traceDir;
378-
if (!traceDir)
379-
throw new Error('Tracing directory is not specified when launching the browser');
380-
this._tracer = new Tracer(this, traceDir);
381-
await this._tracer.start();
382-
}
383-
384-
async stopTracing() {
385-
await this._tracer?.stop();
386-
this._tracer = null;
387-
}
388374
}
389375

390376
export function assertBrowserContextIsNotOwned(context: BrowserContext) {

0 commit comments

Comments
 (0)