Skip to content

Commit a91ec9a

Browse files
authored
feat(rpc): pass more tests (#2896)
Includes coverage, tracing and misc close() tests.
1 parent 7039409 commit a91ec9a

15 files changed

+148
-53
lines changed

src/chromium/crCoverage.ts

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,6 @@ import { Protocol } from './protocol';
2121
import * as types from '../types';
2222
import * as sourceMap from '../utils/sourceMap';
2323

24-
type JSRange = {
25-
startOffset: number,
26-
endOffset: number,
27-
count: number
28-
}
29-
30-
type CSSCoverageEntry = {
31-
url: string,
32-
text?: string,
33-
ranges: {
34-
start: number,
35-
end: number
36-
}[]
37-
};
38-
39-
type JSCoverageEntry = {
40-
url: string,
41-
source?: string,
42-
functions: {
43-
functionName: string,
44-
ranges: JSRange[]
45-
}[]
46-
};
47-
4824
export class CRCoverage {
4925
private _jsCoverage: JSCoverage;
5026
private _cssCoverage: CSSCoverage;
@@ -58,15 +34,15 @@ export class CRCoverage {
5834
return await this._jsCoverage.start(options);
5935
}
6036

61-
async stopJSCoverage(): Promise<JSCoverageEntry[]> {
37+
async stopJSCoverage(): Promise<types.JSCoverageEntry[]> {
6238
return await this._jsCoverage.stop();
6339
}
6440

6541
async startCSSCoverage(options?: types.CSSCoverageOptions) {
6642
return await this._cssCoverage.start(options);
6743
}
6844

69-
async stopCSSCoverage(): Promise<CSSCoverageEntry[]> {
45+
async stopCSSCoverage(): Promise<types.CSSCoverageEntry[]> {
7046
return await this._cssCoverage.stop();
7147
}
7248
}
@@ -134,7 +110,7 @@ class JSCoverage {
134110
this._scriptSources.set(event.scriptId, response.scriptSource);
135111
}
136112

137-
async stop(): Promise<JSCoverageEntry[]> {
113+
async stop(): Promise<types.JSCoverageEntry[]> {
138114
assert(this._enabled, 'JSCoverage is not enabled');
139115
this._enabled = false;
140116
const [profileResponse] = await Promise.all([
@@ -145,7 +121,7 @@ class JSCoverage {
145121
] as const);
146122
helper.removeEventListeners(this._eventListeners);
147123

148-
const coverage: JSCoverageEntry[] = [];
124+
const coverage: types.JSCoverageEntry[] = [];
149125
for (const entry of profileResponse.result) {
150126
if (!this._scriptIds.has(entry.scriptId))
151127
continue;
@@ -216,7 +192,7 @@ class CSSCoverage {
216192
}
217193
}
218194

219-
async stop(): Promise<CSSCoverageEntry[]> {
195+
async stop(): Promise<types.CSSCoverageEntry[]> {
220196
assert(this._enabled, 'CSSCoverage is not enabled');
221197
this._enabled = false;
222198
const ruleTrackingResponse = await this._client.send('CSS.stopRuleUsageTracking');
@@ -241,7 +217,7 @@ class CSSCoverage {
241217
});
242218
}
243219

244-
const coverage: CSSCoverageEntry[] = [];
220+
const coverage: types.CSSCoverageEntry[] = [];
245221
for (const styleSheetId of this._stylesheetURLs.keys()) {
246222
const url = this._stylesheetURLs.get(styleSheetId)!;
247223
const text = this._stylesheetSources.get(styleSheetId)!;

src/rpc/channels.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ export interface BrowserChannel extends Channel {
6464
newContext(params: types.BrowserContextOptions): Promise<BrowserContextChannel>;
6565

6666
crNewBrowserCDPSession(): Promise<CDPSessionChannel>;
67+
crStartTracing(params: { page?: PageChannel, path?: string, screenshots?: boolean, categories?: string[] }): Promise<void>;
68+
crStopTracing(): Promise<Binary>;
6769
}
6870
export type BrowserInitializer = {};
6971

@@ -154,9 +156,13 @@ export interface PageChannel extends Channel {
154156
mouseUp(params: { button?: types.MouseButton, clickCount?: number }): Promise<void>;
155157
mouseClick(params: { x: number, y: number, delay?: number, button?: types.MouseButton, clickCount?: number }): Promise<void>;
156158

157-
// A11Y
158159
accessibilitySnapshot(params: { interestingOnly?: boolean, root?: ElementHandleChannel }): Promise<types.SerializedAXNode | null>;
159160
pdf: (params: types.PDFOptions) => Promise<Binary>;
161+
162+
crStartJSCoverage(params: types.JSCoverageOptions): Promise<void>;
163+
crStopJSCoverage(): Promise<types.JSCoverageEntry[]>;
164+
crStartCSSCoverage(params: types.CSSCoverageOptions): Promise<void>;
165+
crStopCSSCoverage(): Promise<types.CSSCoverageEntry[]>;
160166
}
161167

162168
export type PageInitializer = {

src/rpc/client/browser.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
2727
readonly _contexts = new Set<BrowserContext>();
2828
private _isConnected = true;
2929
private _isClosedOrClosing = false;
30-
30+
private _closedPromise: Promise<void>;
3131

3232
static from(browser: BrowserChannel): Browser {
3333
return (browser as any)._object;
@@ -45,6 +45,7 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
4545
this._isClosedOrClosing = true;
4646
this._scope.dispose();
4747
});
48+
this._closedPromise = new Promise(f => this.once(Events.Browser.Disconnected, f));
4849
}
4950

5051
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
@@ -73,13 +74,22 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
7374
}
7475

7576
async close(): Promise<void> {
76-
if (this._isClosedOrClosing)
77-
return;
78-
this._isClosedOrClosing = true;
79-
await this._channel.close();
77+
if (!this._isClosedOrClosing) {
78+
this._isClosedOrClosing = true;
79+
await this._channel.close();
80+
}
81+
await this._closedPromise;
8082
}
8183

8284
async newBrowserCDPSession(): Promise<CDPSession> {
8385
return CDPSession.from(await this._channel.crNewBrowserCDPSession());
8486
}
87+
88+
async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) {
89+
await this._channel.crStartTracing({ ...options, page: page ? page._channel : undefined });
90+
}
91+
92+
async stopTracing(): Promise<Buffer> {
93+
return Buffer.from(await this._channel.crStopTracing(), 'base64');
94+
}
8595
}

src/rpc/client/browserContext.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
4040
private _pendingWaitForEvents = new Map<(error: Error) => void, string>();
4141
_timeoutSettings = new TimeoutSettings();
4242
_ownerPage: Page | undefined;
43+
private _isClosedOrClosing = false;
44+
private _closedPromise: Promise<void>;
4345

4446
static from(context: BrowserContextChannel): BrowserContext {
4547
return (context as any)._object;
@@ -60,6 +62,7 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
6062
this._channel.on('close', () => this._onClose());
6163
this._channel.on('page', page => this._onPage(Page.from(page)));
6264
this._channel.on('route', ({ route, request }) => this._onRoute(network.Route.from(route), network.Request.from(request)));
65+
this._closedPromise = new Promise(f => this.once(Events.BrowserContext.Close, f));
6366

6467
initializer.crBackgroundPages.forEach(p => {
6568
const page = Page.from(p);
@@ -211,6 +214,7 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
211214
}
212215

213216
private async _onClose() {
217+
this._isClosedOrClosing = true;
214218
if (this._browser)
215219
this._browser._contexts.delete(this);
216220

@@ -225,7 +229,11 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
225229
}
226230

227231
async close(): Promise<void> {
228-
await this._channel.close();
232+
if (!this._isClosedOrClosing) {
233+
this._isClosedOrClosing = true;
234+
await this._channel.close();
235+
}
236+
await this._closedPromise;
229237
}
230238

231239
async newCDPSession(page: Page): Promise<CDPSession> {

src/rpc/client/coverage.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 types from '../../types';
18+
import { PageChannel } from '../channels';
19+
20+
export class Coverage {
21+
private _channel: PageChannel;
22+
23+
constructor(channel: PageChannel) {
24+
this._channel = channel;
25+
}
26+
27+
async startJSCoverage(options: types.JSCoverageOptions = {}) {
28+
await this._channel.crStartJSCoverage(options);
29+
}
30+
31+
async stopJSCoverage(): Promise<types.JSCoverageEntry[]> {
32+
return await this._channel.crStopJSCoverage();
33+
}
34+
35+
async startCSSCoverage(options: types.CSSCoverageOptions = {}) {
36+
await this._channel.crStartCSSCoverage(options);
37+
}
38+
39+
async stopCSSCoverage(): Promise<types.CSSCoverageEntry[]> {
40+
return await this._channel.crStopCSSCoverage();
41+
}
42+
}

src/rpc/client/page.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { Func1, FuncOn, SmartHandle } from './jsHandle';
3838
import { Request, Response, Route, RouteHandler } from './network';
3939
import { FileChooser } from './fileChooser';
4040
import { Buffer } from 'buffer';
41+
import { Coverage } from './coverage';
4142

4243
export class Page extends ChannelOwner<PageChannel, PageInitializer> {
4344

@@ -54,6 +55,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
5455
readonly accessibility: Accessibility;
5556
readonly keyboard: Keyboard;
5657
readonly mouse: Mouse;
58+
readonly coverage: Coverage;
5759
readonly _bindings = new Map<string, FunctionWithSource>();
5860
private _pendingWaitForEvents = new Map<(error: Error) => void, string>();
5961
private _timeoutSettings = new TimeoutSettings();
@@ -72,6 +74,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
7274
this.accessibility = new Accessibility(this._channel);
7375
this.keyboard = new Keyboard(this._channel);
7476
this.mouse = new Mouse(this._channel);
77+
this.coverage = new Coverage(this._channel);
7578

7679
this._mainFrame = Frame.from(initializer.mainFrame);
7780
this._mainFrame._page = this;

src/rpc/server/browserDispatcher.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ import { Browser, BrowserBase } from '../../browser';
1818
import { BrowserContextBase } from '../../browserContext';
1919
import { Events } from '../../events';
2020
import * as types from '../../types';
21-
import { BrowserChannel, BrowserContextChannel, BrowserInitializer, CDPSessionChannel } from '../channels';
21+
import { BrowserChannel, BrowserContextChannel, BrowserInitializer, CDPSessionChannel, Binary } from '../channels';
2222
import { BrowserContextDispatcher } from './browserContextDispatcher';
2323
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
2424
import { Dispatcher, DispatcherScope } from './dispatcher';
2525
import { CRBrowser } from '../../chromium/crBrowser';
26+
import { PageDispatcher } from './pageDispatcher';
2627

2728
export class BrowserDispatcher extends Dispatcher<Browser, BrowserInitializer> implements BrowserChannel {
2829
constructor(scope: DispatcherScope, browser: BrowserBase) {
@@ -45,4 +46,15 @@ export class BrowserDispatcher extends Dispatcher<Browser, BrowserInitializer> i
4546
const crBrowser = this._object as CRBrowser;
4647
return new CDPSessionDispatcher(this._scope, await crBrowser.newBrowserCDPSession());
4748
}
49+
50+
async crStartTracing(params: { page?: PageDispatcher, path?: string, screenshots?: boolean, categories?: string[] }): Promise<void> {
51+
const crBrowser = this._object as CRBrowser;
52+
await crBrowser.startTracing(params.page ? params.page._object : undefined, params);
53+
}
54+
55+
async crStopTracing(): Promise<Binary> {
56+
const crBrowser = this._object as CRBrowser;
57+
const buffer = await crBrowser.stopTracing();
58+
return buffer.toString('base64');
59+
}
4860
}

src/rpc/server/pageDispatcher.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { RequestDispatcher, ResponseDispatcher, RouteDispatcher } from './networ
3131
import { serializeResult, parseArgument } from './jsHandleDispatcher';
3232
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
3333
import { FileChooser } from '../../fileChooser';
34+
import { CRCoverage } from '../../chromium/crCoverage';
3435

3536
export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements PageChannel {
3637
private _page: Page;
@@ -125,7 +126,7 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
125126
});
126127
}
127128

128-
async screenshot(params: types.ScreenshotOptions): Promise<string> {
129+
async screenshot(params: types.ScreenshotOptions): Promise<Binary> {
129130
return (await this._page.screenshot(params)).toString('base64');
130131
}
131132

@@ -190,6 +191,26 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
190191
return binary.toString('base64');
191192
}
192193

194+
async crStartJSCoverage(params: types.JSCoverageOptions): Promise<void> {
195+
const coverage = this._page.coverage as CRCoverage;
196+
await coverage.startJSCoverage(params);
197+
}
198+
199+
async crStopJSCoverage(): Promise<types.JSCoverageEntry[]> {
200+
const coverage = this._page.coverage as CRCoverage;
201+
return await coverage.stopJSCoverage();
202+
}
203+
204+
async crStartCSSCoverage(params: types.CSSCoverageOptions): Promise<void> {
205+
const coverage = this._page.coverage as CRCoverage;
206+
await coverage.startCSSCoverage(params);
207+
}
208+
209+
async crStopCSSCoverage(): Promise<types.CSSCoverageEntry[]> {
210+
const coverage = this._page.coverage as CRCoverage;
211+
return await coverage.stopCSSCoverage();
212+
}
213+
193214
_onFrameAttached(frame: Frame) {
194215
this._dispatchEvent('frameAttached', FrameDispatcher.from(this._scope, frame));
195216
}

src/types.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,6 @@ export type PDFOptions = {
123123
path?: string,
124124
}
125125

126-
export type CoverageEntry = {
127-
url: string,
128-
text: string,
129-
ranges: {start: number, end: number}[]
130-
};
131-
132126
export type CSSCoverageOptions = {
133127
resetOnNavigation?: boolean,
134128
};
@@ -138,6 +132,30 @@ export type JSCoverageOptions = {
138132
reportAnonymousScripts?: boolean,
139133
};
140134

135+
export type JSRange = {
136+
startOffset: number,
137+
endOffset: number,
138+
count: number
139+
};
140+
141+
export type CSSCoverageEntry = {
142+
url: string,
143+
text?: string,
144+
ranges: {
145+
start: number,
146+
end: number
147+
}[]
148+
};
149+
150+
export type JSCoverageEntry = {
151+
url: string,
152+
source?: string,
153+
functions: {
154+
functionName: string,
155+
ranges: JSRange[]
156+
}[]
157+
};
158+
141159
export type InjectedScriptProgress = {
142160
aborted: boolean,
143161
log: (message: string) => void,

test/browsercontext.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ describe('BrowserContext', function() {
121121
let error = await promise;
122122
expect(error.message).toContain('Context closed');
123123
});
124-
it.fail(CHANNEL)('close() should be callable twice', async({browser}) => {
124+
it('close() should be callable twice', async({browser}) => {
125125
const context = await browser.newContext();
126126
await Promise.all([
127127
context.close(),

0 commit comments

Comments
 (0)