lighthouse 10.3.0-dev.20230709 → 10.4.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/cli/test/smokehouse/frontends/smokehouse-bin.js +19 -13
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +3 -5
- package/core/audits/long-tasks.d.ts +48 -0
- package/core/audits/long-tasks.js +146 -19
- package/dist/report/flow.js +1 -1
- package/package.json +1 -1
- package/shared/localization/locales/ar-XB.json +233 -2
- package/shared/localization/locales/ar.json +233 -2
- package/shared/localization/locales/bg.json +233 -2
- package/shared/localization/locales/ca.json +233 -2
- package/shared/localization/locales/cs.json +246 -15
- package/shared/localization/locales/da.json +239 -8
- package/shared/localization/locales/de.json +238 -7
- package/shared/localization/locales/el.json +234 -3
- package/shared/localization/locales/en-GB.json +233 -2
- package/shared/localization/locales/en-XA.json +233 -2
- package/shared/localization/locales/es-419.json +233 -2
- package/shared/localization/locales/es.json +233 -2
- package/shared/localization/locales/fi.json +233 -2
- package/shared/localization/locales/fil.json +236 -5
- package/shared/localization/locales/fr.json +235 -4
- package/shared/localization/locales/he.json +233 -2
- package/shared/localization/locales/hi.json +236 -5
- package/shared/localization/locales/hr.json +234 -3
- package/shared/localization/locales/hu.json +233 -2
- package/shared/localization/locales/id.json +233 -2
- package/shared/localization/locales/it.json +234 -3
- package/shared/localization/locales/ja.json +233 -2
- package/shared/localization/locales/ko.json +233 -2
- package/shared/localization/locales/lt.json +233 -2
- package/shared/localization/locales/lv.json +250 -19
- package/shared/localization/locales/nl.json +233 -2
- package/shared/localization/locales/no.json +235 -4
- package/shared/localization/locales/pl.json +233 -2
- package/shared/localization/locales/pt-PT.json +233 -2
- package/shared/localization/locales/pt.json +234 -3
- package/shared/localization/locales/ro.json +233 -2
- package/shared/localization/locales/ru.json +233 -2
- package/shared/localization/locales/sk.json +233 -2
- package/shared/localization/locales/sl.json +233 -2
- package/shared/localization/locales/sr-Latn.json +233 -2
- package/shared/localization/locales/sr.json +233 -2
- package/shared/localization/locales/sv.json +233 -2
- package/shared/localization/locales/ta.json +233 -2
- package/shared/localization/locales/te.json +233 -2
- package/shared/localization/locales/th.json +233 -2
- package/shared/localization/locales/tr.json +233 -2
- package/shared/localization/locales/uk.json +233 -2
- package/shared/localization/locales/vi.json +233 -2
- package/shared/localization/locales/zh-HK.json +233 -2
- package/shared/localization/locales/zh-TW.json +235 -4
- package/shared/localization/locales/zh.json +239 -8
|
@@ -25,6 +25,8 @@ import {runSmokehouse, getShardedDefinitions} from '../smokehouse.js';
|
|
|
25
25
|
import {updateTestDefnFormat} from './back-compat-util.js';
|
|
26
26
|
import {LH_ROOT} from '../../../../root.js';
|
|
27
27
|
import exclusions from '../config/exclusions.js';
|
|
28
|
+
import {saveArtifacts} from '../../../../core/lib/asset-saver.js';
|
|
29
|
+
import {saveLhr} from '../../../../core/lib/asset-saver.js';
|
|
28
30
|
|
|
29
31
|
const coreTestDefnsPath =
|
|
30
32
|
path.join(LH_ROOT, 'cli/test/smokehouse/core-tests.js');
|
|
@@ -230,19 +232,23 @@ async function begin() {
|
|
|
230
232
|
if (!smokehouseResult.success) {
|
|
231
233
|
const failedTestResults = smokehouseResult.testResults.filter(r => r.failed);
|
|
232
234
|
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
235
|
+
// Save failed runs to directory. In CI, this is uploaded as an artifact.
|
|
236
|
+
const failuresDir = `${LH_ROOT}/.tmp/smokehouse-failures`;
|
|
237
|
+
fs.rmSync(failuresDir, {recursive: true, force: true});
|
|
238
|
+
fs.mkdirSync(failuresDir);
|
|
239
|
+
|
|
240
|
+
for (const testResult of failedTestResults) {
|
|
241
|
+
for (let i = 0; i < testResult.runs.length; i++) {
|
|
242
|
+
const runDir = `${failuresDir}/${i}/${testResult.id}`;
|
|
243
|
+
fs.mkdirSync(runDir, {recursive: true});
|
|
244
|
+
|
|
245
|
+
const run = testResult.runs[i];
|
|
246
|
+
await saveArtifacts(run.artifacts, runDir);
|
|
247
|
+
await saveLhr(run.lhr, runDir);
|
|
248
|
+
fs.writeFileSync(`${runDir}/assertionLog.txt`, run.assertionLog);
|
|
249
|
+
fs.writeFileSync(`${runDir}/lighthouseLog.txt`, run.lighthouseLog);
|
|
250
|
+
if (run.networkRequests) {
|
|
251
|
+
fs.writeFileSync(`${runDir}/networkRequests.txt`, run.networkRequests.join('\n'));
|
|
246
252
|
}
|
|
247
253
|
}
|
|
248
254
|
}
|
|
@@ -82,7 +82,7 @@ async function runBundledLighthouse(url, config, testRunnerOptions) {
|
|
|
82
82
|
|
|
83
83
|
// Run Lighthouse.
|
|
84
84
|
try {
|
|
85
|
-
const logLevel = testRunnerOptions.isDebug ? '
|
|
85
|
+
const logLevel = testRunnerOptions.isDebug ? 'verbose' : 'info';
|
|
86
86
|
let runnerResult;
|
|
87
87
|
if (testRunnerOptions.useLegacyNavigation) {
|
|
88
88
|
const connection = new CriConnection(port);
|
|
@@ -124,12 +124,10 @@ async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
|
124
124
|
worker.stdout.setEncoding('utf8');
|
|
125
125
|
worker.stderr.setEncoding('utf8');
|
|
126
126
|
worker.stdout.addListener('data', (data) => {
|
|
127
|
-
|
|
128
|
-
logs.push(`STDOUT: ${data}`);
|
|
127
|
+
logs.push(`[STDOUT] ${data}`);
|
|
129
128
|
});
|
|
130
129
|
worker.stderr.addListener('data', (data) => {
|
|
131
|
-
|
|
132
|
-
logs.push(`STDERR: ${data}`);
|
|
130
|
+
logs.push(`[STDERR] ${data}`);
|
|
133
131
|
});
|
|
134
132
|
const [workerResponse] = await once(worker, 'message');
|
|
135
133
|
const log = logs.join('') + '\n';
|
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
export default LongTasks;
|
|
2
|
+
export type TaskGroupIds = import('../lib/tracehouse/task-groups.js').TaskGroupIds;
|
|
3
|
+
export type Timing = {
|
|
4
|
+
startTime: number;
|
|
5
|
+
duration: number;
|
|
6
|
+
};
|
|
7
|
+
export type DebugTask = Timing & {
|
|
8
|
+
[p: string]: number;
|
|
9
|
+
urlIndex: number;
|
|
10
|
+
};
|
|
11
|
+
/** @typedef {import('../lib/tracehouse/task-groups.js').TaskGroupIds} TaskGroupIds */
|
|
12
|
+
/** @typedef {{startTime: number, duration: number}} Timing */
|
|
13
|
+
/** @typedef {Timing & {urlIndex: number, [p: string]: number}} DebugTask */
|
|
2
14
|
declare class LongTasks extends Audit {
|
|
15
|
+
/**
|
|
16
|
+
* Returns the timing information for the given task, recursively walking the
|
|
17
|
+
* task's children and adding up time spent in each type of task activity.
|
|
18
|
+
* If `taskTimingsByEvent` is present, it will be used for task timing instead
|
|
19
|
+
* of the timings on the tasks themselves.
|
|
20
|
+
* If `timeByTaskGroup` is not provided, a new Map will be populated with
|
|
21
|
+
* timing breakdown; if one is provided, timing breakdown will be added to the
|
|
22
|
+
* existing breakdown.
|
|
23
|
+
*
|
|
24
|
+
* TODO: when simulated, a significant number of child tasks are dropped, so
|
|
25
|
+
* most time will be attributed to 'other' (the category of the top-level
|
|
26
|
+
* RunTask). See pruning in `PageDependencyGraph.linkCPUNodes`.
|
|
27
|
+
* @param {LH.Artifacts.TaskNode} task
|
|
28
|
+
* @param {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
|
|
29
|
+
* @param {Map<TaskGroupIds, number>} [timeByTaskGroup]
|
|
30
|
+
* @return {{startTime: number, duration: number, timeByTaskGroup: Map<TaskGroupIds, number>}}
|
|
31
|
+
*/
|
|
32
|
+
static getTimingBreakdown(task: LH.Artifacts.TaskNode, taskTimingsByEvent: Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming> | undefined, timeByTaskGroup?: Map<import("../lib/tracehouse/task-groups.js").TaskGroupIds, number> | undefined): {
|
|
33
|
+
startTime: number;
|
|
34
|
+
duration: number;
|
|
35
|
+
timeByTaskGroup: Map<TaskGroupIds, number>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* @param {Array<LH.Artifacts.TaskNode>} longTasks
|
|
39
|
+
* @param {Set<string>} jsUrls
|
|
40
|
+
* @param {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
|
|
41
|
+
* @return {LH.Audit.Details.DebugData}
|
|
42
|
+
*/
|
|
43
|
+
static makeDebugData(longTasks: Array<LH.Artifacts.TaskNode>, jsUrls: Set<string>, taskTimingsByEvent: Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming> | undefined): LH.Audit.Details.DebugData;
|
|
44
|
+
/**
|
|
45
|
+
* Get timing from task, overridden by taskTimingsByEvent if provided.
|
|
46
|
+
* @param {LH.Artifacts.TaskNode} task
|
|
47
|
+
* @param {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
|
|
48
|
+
* @return {Timing}
|
|
49
|
+
*/
|
|
50
|
+
static getTiming(task: LH.Artifacts.TaskNode, taskTimingsByEvent: Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming> | undefined): Timing;
|
|
3
51
|
/**
|
|
4
52
|
* @param {LH.Artifacts} artifacts
|
|
5
53
|
* @param {LH.Audit.Context} context
|
|
@@ -14,6 +14,7 @@ import {getJavaScriptURLs, getAttributableURLForTask} from '../lib/tracehouse/ta
|
|
|
14
14
|
|
|
15
15
|
/** We don't always have timing data for short tasks, if we're missing timing data. Treat it as though it were 0ms. */
|
|
16
16
|
const DEFAULT_TIMING = {startTime: 0, endTime: 0, duration: 0};
|
|
17
|
+
const DISPLAYED_TASK_COUNT = 20;
|
|
17
18
|
|
|
18
19
|
const UIStrings = {
|
|
19
20
|
/** Title of a diagnostic LH audit that provides details on the longest running tasks that occur when the page loads. */
|
|
@@ -31,6 +32,30 @@ const UIStrings = {
|
|
|
31
32
|
|
|
32
33
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Insert `url` into `urls` array if not already present. Returns
|
|
37
|
+
* the index of `url` in `urls` for later lookup.
|
|
38
|
+
* @param {Array<string>} urls
|
|
39
|
+
* @param {string} url
|
|
40
|
+
*/
|
|
41
|
+
function insertUrl(urls, url) {
|
|
42
|
+
const index = urls.indexOf(url);
|
|
43
|
+
if (index > -1) return index;
|
|
44
|
+
return urls.push(url) - 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {number} value
|
|
49
|
+
* @return {number}
|
|
50
|
+
*/
|
|
51
|
+
function roundTenths(value) {
|
|
52
|
+
return Math.round(value * 10) / 10;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @typedef {import('../lib/tracehouse/task-groups.js').TaskGroupIds} TaskGroupIds */
|
|
56
|
+
/** @typedef {{startTime: number, duration: number}} Timing */
|
|
57
|
+
/** @typedef {Timing & {urlIndex: number, [p: string]: number}} DebugTask */
|
|
58
|
+
|
|
34
59
|
class LongTasks extends Audit {
|
|
35
60
|
/**
|
|
36
61
|
* @return {LH.Audit.Meta}
|
|
@@ -45,6 +70,102 @@ class LongTasks extends Audit {
|
|
|
45
70
|
};
|
|
46
71
|
}
|
|
47
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Returns the timing information for the given task, recursively walking the
|
|
75
|
+
* task's children and adding up time spent in each type of task activity.
|
|
76
|
+
* If `taskTimingsByEvent` is present, it will be used for task timing instead
|
|
77
|
+
* of the timings on the tasks themselves.
|
|
78
|
+
* If `timeByTaskGroup` is not provided, a new Map will be populated with
|
|
79
|
+
* timing breakdown; if one is provided, timing breakdown will be added to the
|
|
80
|
+
* existing breakdown.
|
|
81
|
+
*
|
|
82
|
+
* TODO: when simulated, a significant number of child tasks are dropped, so
|
|
83
|
+
* most time will be attributed to 'other' (the category of the top-level
|
|
84
|
+
* RunTask). See pruning in `PageDependencyGraph.linkCPUNodes`.
|
|
85
|
+
* @param {LH.Artifacts.TaskNode} task
|
|
86
|
+
* @param {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
|
|
87
|
+
* @param {Map<TaskGroupIds, number>} [timeByTaskGroup]
|
|
88
|
+
* @return {{startTime: number, duration: number, timeByTaskGroup: Map<TaskGroupIds, number>}}
|
|
89
|
+
*/
|
|
90
|
+
static getTimingBreakdown(task, taskTimingsByEvent, timeByTaskGroup = new Map()) {
|
|
91
|
+
const taskTiming = LongTasks.getTiming(task, taskTimingsByEvent);
|
|
92
|
+
|
|
93
|
+
// Add up child time, while recursively stepping in to accumulate group times.
|
|
94
|
+
let childrenTime = 0;
|
|
95
|
+
if (taskTiming.duration > 0) {
|
|
96
|
+
for (const child of task.children) {
|
|
97
|
+
const {duration} = LongTasks.getTimingBreakdown(child, taskTimingsByEvent, timeByTaskGroup);
|
|
98
|
+
childrenTime += duration;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Add this task's selfTime to its group's total time.
|
|
103
|
+
const selfTime = taskTiming.duration - childrenTime;
|
|
104
|
+
const taskGroupTime = timeByTaskGroup.get(task.group.id) || 0;
|
|
105
|
+
timeByTaskGroup.set(task.group.id, taskGroupTime + selfTime);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
startTime: taskTiming.startTime,
|
|
109
|
+
duration: taskTiming.duration,
|
|
110
|
+
timeByTaskGroup,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {Array<LH.Artifacts.TaskNode>} longTasks
|
|
116
|
+
* @param {Set<string>} jsUrls
|
|
117
|
+
* @param {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
|
|
118
|
+
* @return {LH.Audit.Details.DebugData}
|
|
119
|
+
*/
|
|
120
|
+
static makeDebugData(longTasks, jsUrls, taskTimingsByEvent) {
|
|
121
|
+
/** @type {Array<string>} */
|
|
122
|
+
const urls = [];
|
|
123
|
+
/** @type {Array<DebugTask>} */
|
|
124
|
+
const tasks = [];
|
|
125
|
+
|
|
126
|
+
for (const longTask of longTasks) {
|
|
127
|
+
const attributableUrl = getAttributableURLForTask(longTask, jsUrls);
|
|
128
|
+
|
|
129
|
+
const {startTime, duration, timeByTaskGroup} =
|
|
130
|
+
LongTasks.getTimingBreakdown(longTask, taskTimingsByEvent);
|
|
131
|
+
|
|
132
|
+
// Round time per group and sort entries so order is consistent.
|
|
133
|
+
const timeByTaskGroupEntries = [...timeByTaskGroup]
|
|
134
|
+
.map(/** @return {[TaskGroupIds, number]} */ ([group, time]) => [group, roundTenths(time)])
|
|
135
|
+
.sort((a, b) => a[0].localeCompare(b[0]));
|
|
136
|
+
|
|
137
|
+
tasks.push({
|
|
138
|
+
urlIndex: insertUrl(urls, attributableUrl),
|
|
139
|
+
startTime: roundTenths(startTime),
|
|
140
|
+
duration: roundTenths(duration),
|
|
141
|
+
...Object.fromEntries(timeByTaskGroupEntries),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
type: 'debugdata',
|
|
147
|
+
urls,
|
|
148
|
+
tasks,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get timing from task, overridden by taskTimingsByEvent if provided.
|
|
154
|
+
* @param {LH.Artifacts.TaskNode} task
|
|
155
|
+
* @param {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
|
|
156
|
+
* @return {Timing}
|
|
157
|
+
*/
|
|
158
|
+
static getTiming(task, taskTimingsByEvent) {
|
|
159
|
+
/** @type {Timing} */
|
|
160
|
+
let timing = task;
|
|
161
|
+
if (taskTimingsByEvent) {
|
|
162
|
+
timing = taskTimingsByEvent.get(task.event) || DEFAULT_TIMING;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const {duration, startTime} = timing;
|
|
166
|
+
return {duration, startTime};
|
|
167
|
+
}
|
|
168
|
+
|
|
48
169
|
/**
|
|
49
170
|
* @param {LH.Artifacts} artifacts
|
|
50
171
|
* @param {LH.Audit.Context} context
|
|
@@ -58,10 +179,12 @@ class LongTasks extends Audit {
|
|
|
58
179
|
const devtoolsLog = artifacts.devtoolsLogs[LongTasks.DEFAULT_PASS];
|
|
59
180
|
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
60
181
|
|
|
61
|
-
/** @type {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming
|
|
62
|
-
|
|
182
|
+
/** @type {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} */
|
|
183
|
+
let taskTimingsByEvent;
|
|
63
184
|
|
|
64
185
|
if (settings.throttlingMethod === 'simulate') {
|
|
186
|
+
taskTimingsByEvent = new Map();
|
|
187
|
+
|
|
65
188
|
const simulatorOptions = {devtoolsLog, settings: context.settings};
|
|
66
189
|
const pageGraph = await PageDependencyGraph.request({trace, devtoolsLog, URL}, context);
|
|
67
190
|
const simulator = await LoadSimulator.request(simulatorOptions, context);
|
|
@@ -70,30 +193,32 @@ class LongTasks extends Audit {
|
|
|
70
193
|
if (node.type !== 'cpu') continue;
|
|
71
194
|
taskTimingsByEvent.set(node.event, timing);
|
|
72
195
|
}
|
|
73
|
-
} else {
|
|
74
|
-
for (const task of tasks) {
|
|
75
|
-
if (task.unbounded || task.parent) continue;
|
|
76
|
-
taskTimingsByEvent.set(task.event, task);
|
|
77
|
-
}
|
|
78
196
|
}
|
|
79
197
|
|
|
80
198
|
const jsURLs = getJavaScriptURLs(networkRecords);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
199
|
+
|
|
200
|
+
// Only consider top-level (no parent) long tasks that have an explicit endTime.
|
|
201
|
+
const longTasks = tasks
|
|
202
|
+
.map(task => {
|
|
203
|
+
// Use duration from simulation, if available.
|
|
204
|
+
const {duration} = LongTasks.getTiming(task, taskTimingsByEvent);
|
|
205
|
+
return {task, duration};
|
|
206
|
+
})
|
|
207
|
+
.filter(({task, duration}) => {
|
|
208
|
+
return duration >= 50 && !task.unbounded && !task.parent;
|
|
86
209
|
})
|
|
87
|
-
.filter(t => t.duration >= 50 && !t.unbounded && !t.parent)
|
|
88
210
|
.sort((a, b) => b.duration - a.duration)
|
|
89
|
-
.
|
|
211
|
+
.map(({task}) => task);
|
|
90
212
|
|
|
91
213
|
// TODO(beytoven): Add start time that matches with the simulated throttling
|
|
92
|
-
const results =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
214
|
+
const results = longTasks.map(task => {
|
|
215
|
+
const timing = LongTasks.getTiming(task, taskTimingsByEvent);
|
|
216
|
+
return {
|
|
217
|
+
url: getAttributableURLForTask(task, jsURLs),
|
|
218
|
+
duration: timing.duration,
|
|
219
|
+
startTime: timing.startTime,
|
|
220
|
+
};
|
|
221
|
+
}).slice(0, DISPLAYED_TASK_COUNT);
|
|
97
222
|
|
|
98
223
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
99
224
|
const headings = [
|
|
@@ -107,6 +232,8 @@ class LongTasks extends Audit {
|
|
|
107
232
|
const tableDetails = Audit.makeTableDetails(headings, results,
|
|
108
233
|
{sortedBy: ['duration'], skipSumming: ['startTime']});
|
|
109
234
|
|
|
235
|
+
tableDetails.debugData = LongTasks.makeDebugData(longTasks, jsURLs, taskTimingsByEvent);
|
|
236
|
+
|
|
110
237
|
let displayValue;
|
|
111
238
|
if (results.length > 0) {
|
|
112
239
|
displayValue = str_(UIStrings.displayValue, {itemCount: results.length});
|