browsertime 17.15.0 → 17.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Browsertime changelog (we do [semantic versioning](https://semver.org))
|
|
2
2
|
|
|
3
|
+
## 17.17.0 - 2022-10-11
|
|
4
|
+
### Added
|
|
5
|
+
* Firefox 118, Edge 117 and Chrome/Chromedriver 118 in the Docker container [#1996](https://github.com/sitespeedio/browsertime/pull/1996).
|
|
6
|
+
* Expose trace start/stop for Chrome in scripting (the same way as for Firefox). Thank you [KS](https://github.com/92kns) for [#1988](https://github.com/sitespeedio/browsertime/pull/1988). Documentation is coming when the functionality is rolled out in sitespeed.io.
|
|
7
|
+
|
|
8
|
+
## 17.16.0 - 2022-09-04
|
|
9
|
+
### Added
|
|
10
|
+
* Firefox 117 and Edge 116 in the Docker container.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
* Updated the BiDi HAR exporter for Firefox to version 0.0.12. This version will not work with Firefox 116 so if you use the BiDi HAR exporter you need to upgrade to Firefox 117.
|
|
14
|
+
|
|
3
15
|
## 17.15.0 - 2022-08-16
|
|
4
16
|
### Added
|
|
5
17
|
* Updated Chrome and Chromedriver to 116.
|
|
@@ -15,11 +15,7 @@ import { parse } from '../traceCategoriesParser.js';
|
|
|
15
15
|
import { pathToFolder } from '../../support/pathToFolder.js';
|
|
16
16
|
import { ChromeDevtoolsProtocol } from '../chromeDevtoolsProtocol.js';
|
|
17
17
|
import { Android, isAndroidConfigured } from '../../android/index.js';
|
|
18
|
-
import {
|
|
19
|
-
getFirstContentFulPaintEvent,
|
|
20
|
-
getLargestContentfulPaintEvent,
|
|
21
|
-
getRecalculateStyleElementsAndTimeBefore
|
|
22
|
-
} from './traceUtilities.js';
|
|
18
|
+
import { getRenderBlocking } from './traceUtilities.js';
|
|
23
19
|
const unlink = promisify(_unlink);
|
|
24
20
|
const rm = promisify(_rm);
|
|
25
21
|
|
|
@@ -160,7 +156,11 @@ export class Chromium {
|
|
|
160
156
|
await this.android.resetPowerUsage();
|
|
161
157
|
}
|
|
162
158
|
|
|
163
|
-
if (
|
|
159
|
+
if (
|
|
160
|
+
this.collectTracingEvents &&
|
|
161
|
+
!this.isTracing &&
|
|
162
|
+
this.options.chrome.timelineRecordingType !== 'custom'
|
|
163
|
+
) {
|
|
164
164
|
this.isTracing = true;
|
|
165
165
|
return this.cdpClient.startTrace();
|
|
166
166
|
}
|
|
@@ -174,7 +174,11 @@ export class Chromium {
|
|
|
174
174
|
*/
|
|
175
175
|
async afterPageCompleteCheck(runner, index, url, alias) {
|
|
176
176
|
const result = { url, alias };
|
|
177
|
-
if (
|
|
177
|
+
if (
|
|
178
|
+
this.collectTracingEvents &&
|
|
179
|
+
this.isTracing &&
|
|
180
|
+
this.options.chrome.timelineRecordingType !== 'custom'
|
|
181
|
+
) {
|
|
178
182
|
// We are ready and can stop collecting events
|
|
179
183
|
this.isTracing = false;
|
|
180
184
|
this.events = await this.cdpClient.stopTrace();
|
|
@@ -334,7 +338,10 @@ export class Chromium {
|
|
|
334
338
|
await this.storageManager.gzip(filename, gzFilename, true);
|
|
335
339
|
}
|
|
336
340
|
|
|
337
|
-
if (
|
|
341
|
+
if (
|
|
342
|
+
this.collectTracingEvents &&
|
|
343
|
+
this.options.chrome.timelineRecordingType !== 'custom'
|
|
344
|
+
) {
|
|
338
345
|
const trace = parse(this.events, result.url);
|
|
339
346
|
const name = this.options.enableProfileRun
|
|
340
347
|
? `trace-${index}-extra-run.json`
|
|
@@ -345,50 +352,19 @@ export class Chromium {
|
|
|
345
352
|
result.cpu = cpu;
|
|
346
353
|
|
|
347
354
|
// Collect render blocking info
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
task =>
|
|
351
|
-
task.cat === 'devtools.timeline' &&
|
|
352
|
-
task.name === 'ResourceSendRequest' &&
|
|
353
|
-
task.args.data.url &&
|
|
354
|
-
task.args.data.renderBlocking
|
|
355
|
-
);
|
|
356
|
-
for (let asset of urlsWithBlockingInfo) {
|
|
357
|
-
renderBlockingInfo[asset.args.data.url] =
|
|
358
|
-
asset.args.data.renderBlocking;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const fcpEvent = getFirstContentFulPaintEvent(trace.traceEvents);
|
|
362
|
-
const lcpEvent = getLargestContentfulPaintEvent(trace.traceEvents);
|
|
363
|
-
|
|
364
|
-
result.renderBlocking = { recalculateStyle: {}, requests: {} };
|
|
365
|
-
|
|
366
|
-
if (fcpEvent) {
|
|
367
|
-
const beforeFCP = getRecalculateStyleElementsAndTimeBefore(
|
|
368
|
-
trace.traceEvents,
|
|
369
|
-
fcpEvent.ts
|
|
370
|
-
);
|
|
371
|
-
result.renderBlocking.recalculateStyle.beforeFCP = beforeFCP;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (lcpEvent) {
|
|
375
|
-
const beforeLCP = getRecalculateStyleElementsAndTimeBefore(
|
|
376
|
-
trace.traceEvents,
|
|
377
|
-
lcpEvent.ts
|
|
378
|
-
);
|
|
379
|
-
result.renderBlocking.recalculateStyle.beforeLCP = beforeLCP;
|
|
380
|
-
}
|
|
355
|
+
const render = await getRenderBlocking(trace);
|
|
356
|
+
result.renderBlocking = render.renderBlocking;
|
|
381
357
|
|
|
382
358
|
if (!this.options.skipHar) {
|
|
383
359
|
for (let harRequest of this.hars[index - 1].log.entries) {
|
|
384
|
-
if (renderBlockingInfo[harRequest.request.url]) {
|
|
360
|
+
if (render.renderBlockingInfo[harRequest.request.url]) {
|
|
385
361
|
harRequest._renderBlocking =
|
|
386
|
-
renderBlockingInfo[harRequest.request.url];
|
|
362
|
+
render.renderBlockingInfo[harRequest.request.url];
|
|
387
363
|
}
|
|
388
364
|
}
|
|
389
365
|
}
|
|
390
366
|
|
|
391
|
-
result.renderBlocking.requests = renderBlockingInfo;
|
|
367
|
+
result.renderBlocking.requests = render.renderBlockingInfo;
|
|
392
368
|
}
|
|
393
369
|
|
|
394
370
|
// Google Web Vitals hacksery
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import intel from 'intel';
|
|
2
2
|
const log = intel.getLogger('browsertime.chrome');
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
function getLargestContentfulPaintEvent(traceEvents) {
|
|
5
5
|
const lcpCandidates = traceEvents.filter(
|
|
6
6
|
task => task.name === 'largestContentfulPaint::Candidate'
|
|
7
7
|
);
|
|
@@ -20,7 +20,7 @@ export function getLargestContentfulPaintEvent(traceEvents) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
function getFirstContentFulPaintEvent(traceEvents) {
|
|
24
24
|
// Get first contentful paint
|
|
25
25
|
const fcpEvent = traceEvents.find(
|
|
26
26
|
task => task.name === 'firstContentfulPaint'
|
|
@@ -33,10 +33,7 @@ export function getFirstContentFulPaintEvent(traceEvents) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
traceEvents,
|
|
38
|
-
timestamp
|
|
39
|
-
) {
|
|
36
|
+
function getRecalculateStyleElementsAndTimeBefore(traceEvents, timestamp) {
|
|
40
37
|
const recalculatesBefore = traceEvents.filter(
|
|
41
38
|
task =>
|
|
42
39
|
task.cat === 'disabled-by-default-devtools.timeline' &&
|
|
@@ -62,3 +59,41 @@ export function getRecalculateStyleElementsAndTimeBefore(
|
|
|
62
59
|
|
|
63
60
|
return { elements, durationInMillis: duration / 1000 };
|
|
64
61
|
}
|
|
62
|
+
|
|
63
|
+
export async function getRenderBlocking(trace) {
|
|
64
|
+
const renderBlockingInfo = {};
|
|
65
|
+
|
|
66
|
+
const urlsWithBlockingInfo = trace.traceEvents.filter(
|
|
67
|
+
task =>
|
|
68
|
+
task.cat === 'devtools.timeline' &&
|
|
69
|
+
task.name === 'ResourceSendRequest' &&
|
|
70
|
+
task.args.data.url &&
|
|
71
|
+
task.args.data.renderBlocking
|
|
72
|
+
);
|
|
73
|
+
for (let asset of urlsWithBlockingInfo) {
|
|
74
|
+
renderBlockingInfo[asset.args.data.url] = asset.args.data.renderBlocking;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const fcpEvent = getFirstContentFulPaintEvent(trace.traceEvents);
|
|
78
|
+
const lcpEvent = getLargestContentfulPaintEvent(trace.traceEvents);
|
|
79
|
+
|
|
80
|
+
const renderBlocking = { recalculateStyle: {}, requests: {} };
|
|
81
|
+
|
|
82
|
+
if (fcpEvent) {
|
|
83
|
+
const beforeFCP = getRecalculateStyleElementsAndTimeBefore(
|
|
84
|
+
trace.traceEvents,
|
|
85
|
+
fcpEvent.ts
|
|
86
|
+
);
|
|
87
|
+
renderBlocking.recalculateStyle.beforeFCP = beforeFCP;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (lcpEvent) {
|
|
91
|
+
const beforeLCP = getRecalculateStyleElementsAndTimeBefore(
|
|
92
|
+
trace.traceEvents,
|
|
93
|
+
lcpEvent.ts
|
|
94
|
+
);
|
|
95
|
+
renderBlocking.recalculateStyle.beforeLCP = beforeLCP;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { renderBlockingInfo, renderBlocking };
|
|
99
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import intel from 'intel';
|
|
2
|
+
|
|
3
|
+
import { getRenderBlocking } from '../../../chrome/webdriver/traceUtilities.js';
|
|
4
|
+
import { parse } from '../../../chrome/traceCategoriesParser.js';
|
|
5
|
+
import { parseCPUTrace } from '../../../chrome/parseCpuTrace.js';
|
|
6
|
+
const log = intel.getLogger('browsertime.command.chrometrace');
|
|
7
|
+
export class ChromeTrace {
|
|
8
|
+
constructor(engineDelegate, index, options, result) {
|
|
9
|
+
this.engineDelegate = engineDelegate;
|
|
10
|
+
this.options = options;
|
|
11
|
+
this.result = result;
|
|
12
|
+
this.index = index;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async start() {
|
|
16
|
+
if (this.options.browser === 'chrome') {
|
|
17
|
+
if (this.options.chrome.timelineRecordingType === 'custom') {
|
|
18
|
+
return this.engineDelegate.getCDPClient().startTrace();
|
|
19
|
+
} else {
|
|
20
|
+
log.info(
|
|
21
|
+
'You need to set traceRecordingType to custom to turn on the profiler in scripting'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error('Trace only works in Chrome');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async stop() {
|
|
30
|
+
if (this.options.browser === 'chrome') {
|
|
31
|
+
if (this.options.chrome.timelineRecordingType === 'custom') {
|
|
32
|
+
let result = this.result[0];
|
|
33
|
+
|
|
34
|
+
this.events = [];
|
|
35
|
+
this.events = await this.engineDelegate.getCDPClient().stopTrace();
|
|
36
|
+
const trace = parse(this.events, result.url);
|
|
37
|
+
const name = this.options.enableProfileRun
|
|
38
|
+
? `trace-${this.index}-extra-run.json`
|
|
39
|
+
: `trace-${this.index}.json`;
|
|
40
|
+
result.extraJson[name] = trace;
|
|
41
|
+
|
|
42
|
+
const cpu = await parseCPUTrace(trace, result.url);
|
|
43
|
+
result.cpu = cpu;
|
|
44
|
+
|
|
45
|
+
// Collect render blocking info
|
|
46
|
+
const render = await getRenderBlocking(trace);
|
|
47
|
+
|
|
48
|
+
result.renderBlocking = render.renderBlocking;
|
|
49
|
+
|
|
50
|
+
if (!this.options.skipHar) {
|
|
51
|
+
for (let harRequest of this.hars[this.index - 1].log.entries) {
|
|
52
|
+
if (render.renderBlockingInfo[harRequest.request.url]) {
|
|
53
|
+
harRequest._renderBlocking =
|
|
54
|
+
render.renderBlockingInfo[harRequest.request.url];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
throw new Error('Trace only works in Chrome');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -24,6 +24,7 @@ import { Select } from './command/select.js';
|
|
|
24
24
|
import { Debug } from './command/debug.js';
|
|
25
25
|
import { AndroidCommand } from './command/android.js';
|
|
26
26
|
import { ChromeDevelopmentToolsProtocol } from './command/chromeDevToolsProtocol.js';
|
|
27
|
+
import { ChromeTrace } from './command/chromeTrace.js';
|
|
27
28
|
import {
|
|
28
29
|
addConnectivity,
|
|
29
30
|
removeConnectivity
|
|
@@ -164,10 +165,12 @@ export class Iteration {
|
|
|
164
165
|
engineDelegate,
|
|
165
166
|
options.browser
|
|
166
167
|
);
|
|
168
|
+
const trace = new ChromeTrace(engineDelegate, index, options, result);
|
|
167
169
|
const android = new Android(options);
|
|
168
170
|
const debug = new Debug(browser, options);
|
|
169
171
|
const commands = {
|
|
170
172
|
profiler: profiler,
|
|
173
|
+
trace: trace,
|
|
171
174
|
click: new Click(browser, this.pageCompleteCheck),
|
|
172
175
|
scroll: new Scroll(browser, options),
|
|
173
176
|
addText: new AddText(browser),
|
package/lib/support/cli.js
CHANGED
|
@@ -274,6 +274,14 @@ export function parseCommandLine() {
|
|
|
274
274
|
type: 'boolean',
|
|
275
275
|
group: 'chrome'
|
|
276
276
|
})
|
|
277
|
+
.option('chrome.timelineRecordingType', {
|
|
278
|
+
alias: 'chrome.traceRecordingType',
|
|
279
|
+
describe: 'Expose the start/stop commands for the chrome trace',
|
|
280
|
+
default: 'pageload',
|
|
281
|
+
choices: ['pageload', 'custom'],
|
|
282
|
+
type: 'string',
|
|
283
|
+
group: 'chrome'
|
|
284
|
+
})
|
|
277
285
|
.option('chrome.collectPerfLog', {
|
|
278
286
|
type: 'boolean',
|
|
279
287
|
describe:
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browsertime",
|
|
3
3
|
"description": "Get performance metrics from your web page using Browsertime.",
|
|
4
|
-
"version": "17.
|
|
4
|
+
"version": "17.17.0",
|
|
5
5
|
"bin": "./bin/browsertime.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@cypress/xvfb": "1.2.4",
|
|
9
9
|
"@devicefarmer/adbkit": "2.11.3",
|
|
10
|
-
"@sitespeed.io/chromedriver": "
|
|
10
|
+
"@sitespeed.io/chromedriver": "118.0.5993-70",
|
|
11
11
|
"@sitespeed.io/edgedriver": "115.0.1901-183",
|
|
12
12
|
"@sitespeed.io/geckodriver": "0.33.0",
|
|
13
13
|
"@sitespeed.io/throttle": "5.0.0",
|
|
14
14
|
"@sitespeed.io/tracium": "0.3.3",
|
|
15
|
-
"ff-test-bidi-har-export": "0.0.
|
|
15
|
+
"ff-test-bidi-har-export": "0.0.12",
|
|
16
16
|
"btoa": "1.2.1",
|
|
17
17
|
"chrome-har": "0.13.2",
|
|
18
18
|
"chrome-remote-interface": "0.33.0",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"lodash.merge": "4.6.2",
|
|
30
30
|
"lodash.pick": "4.4.0",
|
|
31
31
|
"lodash.set": "4.3.2",
|
|
32
|
-
"selenium-webdriver": "4.
|
|
32
|
+
"selenium-webdriver": "4.12.0",
|
|
33
33
|
"yargs": "17.7.2"
|
|
34
34
|
},
|
|
35
35
|
"optionalDependencies": {
|