lighthouse 9.5.0-dev.20220504 → 9.5.0-dev.20220507
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/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +97 -22
- package/lighthouse-cli/test/smokehouse/version-check-test.js +0 -2
- package/lighthouse-core/audits/bootup-time.js +2 -70
- package/lighthouse-core/audits/long-tasks.js +3 -3
- package/lighthouse-core/audits/metrics/experimental-interaction-to-next-paint.js +7 -5
- package/lighthouse-core/audits/third-party-summary.js +3 -3
- package/lighthouse-core/computed/metrics/responsiveness.js +14 -13
- package/lighthouse-core/fraggle-rock/gather/driver.js +1 -1
- package/lighthouse-core/gather/driver.js +1 -1
- package/lighthouse-core/gather/fetcher.js +34 -210
- package/lighthouse-core/gather/gather-runner.js +0 -11
- package/lighthouse-core/gather/gatherers/seo/robots-txt.js +0 -29
- package/lighthouse-core/gather/gatherers/source-maps.js +0 -1
- package/lighthouse-core/lib/cdt/generated/SourceMap.js +1 -1
- package/lighthouse-core/lib/stack-packs.js +4 -5
- package/lighthouse-core/lib/tracehouse/task-groups.js +1 -0
- package/lighthouse-core/lib/tracehouse/task-summary.js +87 -0
- package/package.json +5 -5
- package/report/test/.eslintrc.cjs +11 -0
- package/report/test/clients/bundle-test.js +0 -3
- package/report/test/generator/file-namer-test.js +0 -1
- package/report/test/generator/report-generator-test.js +0 -2
- package/report/test/renderer/category-renderer-test.js +0 -2
- package/report/test/renderer/components-test.js +0 -2
- package/report/test/renderer/crc-details-renderer-test.js +0 -2
- package/report/test/renderer/details-renderer-test.js +0 -2
- package/report/test/renderer/dom-test.js +0 -2
- package/report/test/renderer/element-screenshot-renderer-test.js +0 -2
- package/report/test/renderer/i18n-test.js +0 -2
- package/report/test/renderer/performance-category-renderer-test.js +0 -2
- package/report/test/renderer/pwa-category-renderer-test.js +0 -2
- package/report/test/renderer/report-renderer-axe-test.js +0 -2
- package/report/test/renderer/report-renderer-test.js +0 -2
- package/report/test/renderer/report-ui-features-test.js +0 -2
- package/report/test/renderer/snippet-renderer-test.js +0 -2
- package/report/test/renderer/text-encoding-test.js +0 -2
- package/report/test/renderer/util-test.js +0 -2
- package/shared/test/localization/.eslintrc.cjs +11 -0
- package/shared/test/localization/format-test.js +0 -2
- package/shared/test/localization/locales-test.js +0 -2
- package/shared/test/localization/swap-locale-test.js +0 -2
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +0 -2
- package/third-party/chromium-synchronization/installability-errors-test.js +0 -2
- package/tsconfig.json +2 -0
- package/types/artifacts.d.ts +17 -0
|
@@ -8,47 +8,76 @@
|
|
|
8
8
|
* @fileoverview A runner that launches Chrome and executes Lighthouse via a
|
|
9
9
|
* bundle to test that bundling has produced correct and runnable code.
|
|
10
10
|
* Currently uses `lighthouse-dt-bundle.js`.
|
|
11
|
+
* Runs in a worker to avoid messing up marky's global state.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import fs from 'fs';
|
|
15
|
+
import os from 'os';
|
|
16
|
+
import {Worker, isMainThread, parentPort, workerData} from 'worker_threads';
|
|
17
|
+
import {once} from 'events';
|
|
14
18
|
|
|
15
19
|
import puppeteer from 'puppeteer-core';
|
|
16
20
|
import ChromeLauncher from 'chrome-launcher';
|
|
17
21
|
|
|
18
22
|
import ChromeProtocol from '../../../../lighthouse-core/gather/connections/cri.js';
|
|
19
23
|
import {LH_ROOT} from '../../../../root.js';
|
|
24
|
+
import {loadArtifacts, saveArtifacts} from '../../../../lighthouse-core/lib/asset-saver.js';
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
// This runs only in the worker. The rest runs on the main thread.
|
|
27
|
+
if (!isMainThread && parentPort) {
|
|
28
|
+
(async () => {
|
|
29
|
+
const {url, configJson, testRunnerOptions} = workerData;
|
|
30
|
+
try {
|
|
31
|
+
const result = await runBundledLighthouse(url, configJson, testRunnerOptions);
|
|
32
|
+
// Save to assets directory because LighthouseError won't survive postMessage.
|
|
33
|
+
const assetsDir = fs.mkdtempSync(os.tmpdir() + '/smoke-bundle-assets-');
|
|
34
|
+
await saveArtifacts(result.artifacts, assetsDir);
|
|
35
|
+
const value = {
|
|
36
|
+
lhr: result.lhr,
|
|
37
|
+
assetsDir,
|
|
38
|
+
};
|
|
39
|
+
parentPort?.postMessage({type: 'result', value});
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error(err);
|
|
42
|
+
parentPort?.postMessage({type: 'error', value: err});
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
26
45
|
}
|
|
27
46
|
|
|
28
|
-
// Load bundle, which creates a `global.runBundledLighthouse`.
|
|
29
|
-
eval(fs.readFileSync(LH_ROOT + '/dist/lighthouse-dt-bundle.js', 'utf-8'));
|
|
30
|
-
|
|
31
|
-
global.require = originalRequire;
|
|
32
|
-
global.Buffer = originalBuffer;
|
|
33
|
-
|
|
34
|
-
/** @type {import('../../../../lighthouse-core/index.js')} */
|
|
35
|
-
// @ts-expect-error - not worth giving test global an actual type.
|
|
36
|
-
const lighthouse = global.runBundledLighthouse;
|
|
37
|
-
|
|
38
47
|
/**
|
|
39
|
-
* Launch Chrome and do a full Lighthouse run via the Lighthouse DevTools bundle.
|
|
40
48
|
* @param {string} url
|
|
41
|
-
* @param {LH.Config.Json
|
|
42
|
-
* @param {{isDebug?: boolean, useFraggleRock?: boolean}
|
|
43
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
49
|
+
* @param {LH.Config.Json|undefined} configJson
|
|
50
|
+
* @param {{isDebug?: boolean, useFraggleRock?: boolean}} testRunnerOptions
|
|
51
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
44
52
|
*/
|
|
45
|
-
async function
|
|
53
|
+
async function runBundledLighthouse(url, configJson, testRunnerOptions) {
|
|
54
|
+
if (isMainThread || !parentPort) {
|
|
55
|
+
throw new Error('must be called in worker');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const originalBuffer = global.Buffer;
|
|
59
|
+
const originalRequire = global.require;
|
|
60
|
+
if (typeof globalThis === 'undefined') {
|
|
61
|
+
// @ts-expect-error - exposing for loading of dt-bundle.
|
|
62
|
+
global.globalThis = global;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Load bundle, which creates a `global.runBundledLighthouse`.
|
|
66
|
+
eval(fs.readFileSync(LH_ROOT + '/dist/lighthouse-dt-bundle.js', 'utf-8'));
|
|
67
|
+
|
|
68
|
+
global.require = originalRequire;
|
|
69
|
+
global.Buffer = originalBuffer;
|
|
70
|
+
|
|
71
|
+
/** @type {import('../../../../lighthouse-core/index.js')} */
|
|
72
|
+
// @ts-expect-error - not worth giving test global an actual type.
|
|
73
|
+
const lighthouse = global.runBundledLighthouse;
|
|
74
|
+
|
|
46
75
|
// Launch and connect to Chrome.
|
|
47
76
|
const launchedChrome = await ChromeLauncher.launch();
|
|
48
77
|
const port = launchedChrome.port;
|
|
49
78
|
|
|
79
|
+
// Run Lighthouse.
|
|
50
80
|
try {
|
|
51
|
-
// Run Lighthouse.
|
|
52
81
|
const logLevel = testRunnerOptions.isDebug ? 'info' : undefined;
|
|
53
82
|
let runnerResult;
|
|
54
83
|
if (testRunnerOptions.useFraggleRock) {
|
|
@@ -66,7 +95,6 @@ async function runLighthouse(url, configJson, testRunnerOptions = {}) {
|
|
|
66
95
|
return {
|
|
67
96
|
lhr: runnerResult.lhr,
|
|
68
97
|
artifacts: runnerResult.artifacts,
|
|
69
|
-
log: '', // TODO: if want to run in parallel, need to capture lighthouse-logger output.
|
|
70
98
|
};
|
|
71
99
|
} finally {
|
|
72
100
|
// Clean up and return results.
|
|
@@ -74,6 +102,53 @@ async function runLighthouse(url, configJson, testRunnerOptions = {}) {
|
|
|
74
102
|
}
|
|
75
103
|
}
|
|
76
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Launch Chrome and do a full Lighthouse run via the Lighthouse DevTools bundle.
|
|
107
|
+
* @param {string} url
|
|
108
|
+
* @param {LH.Config.Json=} configJson
|
|
109
|
+
* @param {{isDebug?: boolean, useFraggleRock?: boolean}=} testRunnerOptions
|
|
110
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, log: string}>}
|
|
111
|
+
*/
|
|
112
|
+
async function runLighthouse(url, configJson, testRunnerOptions = {}) {
|
|
113
|
+
/** @type {string[]} */
|
|
114
|
+
const logs = [];
|
|
115
|
+
const worker = new Worker(new URL(import.meta.url), {
|
|
116
|
+
stdout: true,
|
|
117
|
+
stderr: true,
|
|
118
|
+
workerData: {url, configJson, testRunnerOptions},
|
|
119
|
+
});
|
|
120
|
+
worker.stdout.setEncoding('utf8');
|
|
121
|
+
worker.stderr.setEncoding('utf8');
|
|
122
|
+
worker.stdout.addListener('data', (data) => {
|
|
123
|
+
process.stdout.write(data);
|
|
124
|
+
logs.push(`STDOUT: ${data}`);
|
|
125
|
+
});
|
|
126
|
+
worker.stderr.addListener('data', (data) => {
|
|
127
|
+
process.stderr.write(data);
|
|
128
|
+
logs.push(`STDERR: ${data}`);
|
|
129
|
+
});
|
|
130
|
+
const [workerResponse] = await once(worker, 'message');
|
|
131
|
+
const log = logs.join('') + '\n';
|
|
132
|
+
|
|
133
|
+
if (workerResponse.type === 'error') {
|
|
134
|
+
new Error(`Worker returned an error: ${workerResponse.value}\nLog:\n${log}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const result = workerResponse.value;
|
|
138
|
+
if (!result.lhr || !result.assetsDir) {
|
|
139
|
+
throw new Error(`invalid response from worker:\n${JSON.stringify(result, null, 2)}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const artifacts = loadArtifacts(result.assetsDir);
|
|
143
|
+
fs.rmSync(result.assetsDir, {recursive: true});
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
lhr: result.lhr,
|
|
147
|
+
artifacts,
|
|
148
|
+
log,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
77
152
|
export {
|
|
78
153
|
runLighthouse,
|
|
79
154
|
};
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const Audit = require('./audit.js');
|
|
9
|
-
const NetworkRequest = require('../lib/network-request.js');
|
|
10
9
|
const {taskGroups} = require('../lib/tracehouse/task-groups.js');
|
|
11
10
|
const i18n = require('../lib/i18n/i18n.js');
|
|
12
11
|
const NetworkRecords = require('../computed/network-records.js');
|
|
13
12
|
const MainThreadTasks = require('../computed/main-thread-tasks.js');
|
|
13
|
+
const {getExecutionTimingsByURL} = require('../lib/tracehouse/task-summary.js');
|
|
14
14
|
|
|
15
15
|
const UIStrings = {
|
|
16
16
|
/** Title of a diagnostic audit that provides detail on the time spent executing javascript files during the load. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
@@ -34,18 +34,6 @@ const UIStrings = {
|
|
|
34
34
|
|
|
35
35
|
const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
|
|
36
36
|
|
|
37
|
-
// These trace events, when not triggered by a script inside a particular task, are just general Chrome overhead.
|
|
38
|
-
const BROWSER_TASK_NAMES_SET = new Set([
|
|
39
|
-
'CpuProfiler::StartProfiling',
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
// These trace events, when not triggered by a script inside a particular task, are GC Chrome overhead.
|
|
43
|
-
const BROWSER_GC_TASK_NAMES_SET = new Set([
|
|
44
|
-
'V8.GCCompactor',
|
|
45
|
-
'MajorGC',
|
|
46
|
-
'MinorGC',
|
|
47
|
-
]);
|
|
48
|
-
|
|
49
37
|
class BootupTime extends Audit {
|
|
50
38
|
/**
|
|
51
39
|
* @return {LH.Audit.Meta}
|
|
@@ -74,61 +62,6 @@ class BootupTime extends Audit {
|
|
|
74
62
|
};
|
|
75
63
|
}
|
|
76
64
|
|
|
77
|
-
/**
|
|
78
|
-
* @param {LH.Artifacts.NetworkRequest[]} records
|
|
79
|
-
*/
|
|
80
|
-
static getJavaScriptURLs(records) {
|
|
81
|
-
/** @type {Set<string>} */
|
|
82
|
-
const urls = new Set();
|
|
83
|
-
for (const record of records) {
|
|
84
|
-
if (record.resourceType === NetworkRequest.TYPES.Script) {
|
|
85
|
-
urls.add(record.url);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return urls;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* @param {LH.Artifacts.TaskNode} task
|
|
94
|
-
* @param {Set<string>} jsURLs
|
|
95
|
-
* @return {string}
|
|
96
|
-
*/
|
|
97
|
-
static getAttributableURLForTask(task, jsURLs) {
|
|
98
|
-
const jsURL = task.attributableURLs.find(url => jsURLs.has(url));
|
|
99
|
-
const fallbackURL = task.attributableURLs[0];
|
|
100
|
-
let attributableURL = jsURL || fallbackURL;
|
|
101
|
-
// If we can't find what URL was responsible for this execution, attribute it to the root page
|
|
102
|
-
// or Chrome depending on the type of work.
|
|
103
|
-
if (!attributableURL || attributableURL === 'about:blank') {
|
|
104
|
-
if (BROWSER_TASK_NAMES_SET.has(task.event.name)) attributableURL = 'Browser';
|
|
105
|
-
else if (BROWSER_GC_TASK_NAMES_SET.has(task.event.name)) attributableURL = 'Browser GC';
|
|
106
|
-
else attributableURL = 'Unattributable';
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return attributableURL;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* @param {LH.Artifacts.TaskNode[]} tasks
|
|
114
|
-
* @param {Set<string>} jsURLs
|
|
115
|
-
* @return {Map<string, Object<string, number>>}
|
|
116
|
-
*/
|
|
117
|
-
static getExecutionTimingsByURL(tasks, jsURLs) {
|
|
118
|
-
/** @type {Map<string, Object<string, number>>} */
|
|
119
|
-
const result = new Map();
|
|
120
|
-
|
|
121
|
-
for (const task of tasks) {
|
|
122
|
-
const attributableURL = BootupTime.getAttributableURLForTask(task, jsURLs);
|
|
123
|
-
const timingByGroupId = result.get(attributableURL) || {};
|
|
124
|
-
const originalTime = timingByGroupId[task.group.id] || 0;
|
|
125
|
-
timingByGroupId[task.group.id] = originalTime + task.selfTime;
|
|
126
|
-
result.set(attributableURL, timingByGroupId);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
65
|
/**
|
|
133
66
|
* @param {LH.Artifacts} artifacts
|
|
134
67
|
* @param {LH.Audit.Context} context
|
|
@@ -143,8 +76,7 @@ class BootupTime extends Audit {
|
|
|
143
76
|
const multiplier = settings.throttlingMethod === 'simulate' ?
|
|
144
77
|
settings.throttling.cpuSlowdownMultiplier : 1;
|
|
145
78
|
|
|
146
|
-
const
|
|
147
|
-
const executionTimings = BootupTime.getExecutionTimingsByURL(tasks, jsURLs);
|
|
79
|
+
const executionTimings = getExecutionTimingsByURL(tasks, networkRecords);
|
|
148
80
|
|
|
149
81
|
let hadExcessiveChromeExtension = false;
|
|
150
82
|
let totalBootupTime = 0;
|
|
@@ -9,9 +9,9 @@ const Audit = require('./audit.js');
|
|
|
9
9
|
const NetworkRecords = require('../computed/network-records.js');
|
|
10
10
|
const i18n = require('../lib/i18n/i18n.js');
|
|
11
11
|
const MainThreadTasks = require('../computed/main-thread-tasks.js');
|
|
12
|
-
const BootupTime = require('./bootup-time.js');
|
|
13
12
|
const PageDependencyGraph = require('../computed/page-dependency-graph.js');
|
|
14
13
|
const LoadSimulator = require('../computed/load-simulator.js');
|
|
14
|
+
const {getJavaScriptURLs, getAttributableURLForTask} = require('../lib/tracehouse/task-summary.js');
|
|
15
15
|
|
|
16
16
|
/** We don't always have timing data for short tasks, if we're missing timing data. Treat it as though it were 0ms. */
|
|
17
17
|
const DEFAULT_TIMING = {startTime: 0, endTime: 0, duration: 0};
|
|
@@ -78,7 +78,7 @@ class LongTasks extends Audit {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const jsURLs =
|
|
81
|
+
const jsURLs = getJavaScriptURLs(networkRecords);
|
|
82
82
|
// Only consider up to 20 long, top-level (no parent) tasks that have an explicit endTime
|
|
83
83
|
const longtasks = tasks
|
|
84
84
|
.map(t => {
|
|
@@ -91,7 +91,7 @@ class LongTasks extends Audit {
|
|
|
91
91
|
|
|
92
92
|
// TODO(beytoven): Add start time that matches with the simulated throttling
|
|
93
93
|
const results = longtasks.map(task => ({
|
|
94
|
-
url:
|
|
94
|
+
url: getAttributableURLForTask(task, jsURLs),
|
|
95
95
|
duration: task.duration,
|
|
96
96
|
startTime: task.startTime,
|
|
97
97
|
}));
|
|
@@ -63,19 +63,21 @@ class ExperimentalInteractionToNextPaint extends Audit {
|
|
|
63
63
|
|
|
64
64
|
const trace = artifacts.traces[Audit.DEFAULT_PASS];
|
|
65
65
|
const metricData = {trace, settings};
|
|
66
|
-
const
|
|
66
|
+
const responsivenessEvent = await ComputedResponsivenes.request(metricData, context);
|
|
67
67
|
|
|
68
68
|
// TODO: include the no-interaction state in the report instead of using n/a.
|
|
69
|
-
if (
|
|
69
|
+
if (responsivenessEvent === null) {
|
|
70
70
|
return {score: null, notApplicable: true};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
const timing = responsivenessEvent.args.data.maxDuration;
|
|
74
|
+
|
|
73
75
|
return {
|
|
74
76
|
score: Audit.computeLogNormalScore({p10: context.options.p10, median: context.options.median},
|
|
75
|
-
|
|
76
|
-
numericValue:
|
|
77
|
+
timing),
|
|
78
|
+
numericValue: timing,
|
|
77
79
|
numericUnit: 'millisecond',
|
|
78
|
-
displayValue: str_(i18n.UIStrings.ms, {timeInMs:
|
|
80
|
+
displayValue: str_(i18n.UIStrings.ms, {timeInMs: timing}),
|
|
79
81
|
};
|
|
80
82
|
}
|
|
81
83
|
}
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const Audit = require('./audit.js');
|
|
9
|
-
const BootupTime = require('./bootup-time.js');
|
|
10
9
|
const i18n = require('../lib/i18n/i18n.js');
|
|
11
10
|
const thirdPartyWeb = require('../lib/third-party-web.js');
|
|
12
11
|
const NetworkRecords = require('../computed/network-records.js');
|
|
13
12
|
const MainThreadTasks = require('../computed/main-thread-tasks.js');
|
|
13
|
+
const {getJavaScriptURLs, getAttributableURLForTask} = require('../lib/tracehouse/task-summary.js');
|
|
14
14
|
|
|
15
15
|
const UIStrings = {
|
|
16
16
|
/** Title of a diagnostic audit that provides details about the code on a web page that the user doesn't control (referred to as "third-party code"). This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
@@ -98,10 +98,10 @@ class ThirdPartySummary extends Audit {
|
|
|
98
98
|
byURL.set(request.url, urlSummary);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
const jsURLs =
|
|
101
|
+
const jsURLs = getJavaScriptURLs(networkRecords);
|
|
102
102
|
|
|
103
103
|
for (const task of mainThreadTasks) {
|
|
104
|
-
const attributableURL =
|
|
104
|
+
const attributableURL = getAttributableURLForTask(task, jsURLs);
|
|
105
105
|
|
|
106
106
|
const urlSummary = byURL.get(attributableURL) || {...defaultSummary};
|
|
107
107
|
const taskDuration = task.selfTime * cpuMultiplier;
|
|
@@ -11,24 +11,25 @@
|
|
|
11
11
|
* user input in the provided trace).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
/** @typedef {LH.Trace.CompleteEvent & {name: 'Responsiveness.Renderer.UserInteraction', args: {frame: string, data: {interactionType: 'drag'|'keyboard'|'tapOrClick', maxDuration: number}}}} ResponsivenessEvent */
|
|
15
|
+
|
|
14
16
|
const makeComputedArtifact = require('../computed-artifact.js');
|
|
15
17
|
const ProcessedTrace = require('../processed-trace.js');
|
|
16
18
|
|
|
17
19
|
class Responsiveness {
|
|
18
20
|
/**
|
|
19
21
|
* @param {LH.Artifacts.ProcessedTrace} processedTrace
|
|
20
|
-
* @return {
|
|
22
|
+
* @return {ResponsivenessEvent|null}
|
|
21
23
|
*/
|
|
22
24
|
static getHighPercentileResponsiveness(processedTrace) {
|
|
23
|
-
const
|
|
25
|
+
const responsivenessEvents = processedTrace.frameTreeEvents
|
|
24
26
|
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/timing/responsiveness_metrics.cc;l=146-150;drc=a1a2302f30b0a58f7669a41c80acdf1fa11958dd
|
|
25
|
-
.filter(e
|
|
26
|
-
|
|
27
|
-
.
|
|
28
|
-
.sort((a, b) => b - a);
|
|
27
|
+
.filter(/** @return {e is ResponsivenessEvent} */ e => {
|
|
28
|
+
return e.name === 'Responsiveness.Renderer.UserInteraction';
|
|
29
|
+
}).sort((a, b) => b.args.data.maxDuration - a.args.data.maxDuration);
|
|
29
30
|
|
|
30
31
|
// If there were no interactions with the page, the metric is N/A.
|
|
31
|
-
if (
|
|
32
|
+
if (responsivenessEvents.length === 0) {
|
|
32
33
|
return null;
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -36,17 +37,15 @@ class Responsiveness {
|
|
|
36
37
|
// keeps the 10 worst events around, so it can never be more than the 10th from
|
|
37
38
|
// last array element. To keep things simpler, sort desc and pick from front.
|
|
38
39
|
// See https://source.chromium.org/chromium/chromium/src/+/main:components/page_load_metrics/browser/responsiveness_metrics_normalization.cc;l=45-59;drc=cb0f9c8b559d9c7c3cb4ca94fc1118cc015d38ad
|
|
39
|
-
const index = Math.min(9, Math.floor(
|
|
40
|
+
const index = Math.min(9, Math.floor(responsivenessEvents.length / 50));
|
|
40
41
|
|
|
41
|
-
return
|
|
42
|
-
timing: durations[index],
|
|
43
|
-
};
|
|
42
|
+
return responsivenessEvents[index];
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
46
|
* @param {{trace: LH.Trace, settings: Immutable<LH.Config.Settings>}} data
|
|
48
47
|
* @param {LH.Artifacts.ComputedContext} context
|
|
49
|
-
* @return {Promise<
|
|
48
|
+
* @return {Promise<ResponsivenessEvent|null>}
|
|
50
49
|
*/
|
|
51
50
|
static async compute_(data, context) {
|
|
52
51
|
if (data.settings.throttlingMethod === 'simulate') {
|
|
@@ -54,7 +53,9 @@ class Responsiveness {
|
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
const processedTrace = await ProcessedTrace.request(data.trace, context);
|
|
57
|
-
|
|
56
|
+
const event = Responsiveness.getHighPercentileResponsiveness(processedTrace);
|
|
57
|
+
|
|
58
|
+
return JSON.parse(JSON.stringify(event));
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -74,7 +74,7 @@ class Driver {
|
|
|
74
74
|
const session = await this._page.target().createCDPSession();
|
|
75
75
|
this._session = this.defaultSession = new ProtocolSession(session);
|
|
76
76
|
this._executionContext = new ExecutionContext(this._session);
|
|
77
|
-
this._fetcher = new Fetcher(this._session
|
|
77
|
+
this._fetcher = new Fetcher(this._session);
|
|
78
78
|
log.timeEnd(status);
|
|
79
79
|
}
|
|
80
80
|
|