lighthouse 11.0.0 → 11.1.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/bin.js +1 -1
- package/cli/cli-flags.js +1 -1
- package/cli/test/smokehouse/core-tests.js +3 -1
- package/cli/test/smokehouse/frontends/smokehouse-bin.js +1 -1
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +1 -1
- package/cli/test/smokehouse/lighthouse-runners/cli.js +1 -1
- package/cli/test/smokehouse/lighthouse-runners/devtools.js +1 -1
- package/core/audits/accessibility/accesskeys.js +1 -1
- package/core/audits/accessibility/aria-allowed-attr.js +1 -1
- package/core/audits/accessibility/aria-allowed-role.js +1 -1
- package/core/audits/accessibility/aria-command-name.js +1 -1
- package/core/audits/accessibility/aria-dialog-name.js +1 -1
- package/core/audits/accessibility/aria-hidden-body.js +1 -1
- package/core/audits/accessibility/aria-hidden-focus.js +1 -1
- package/core/audits/accessibility/aria-input-field-name.js +1 -1
- package/core/audits/accessibility/aria-meter-name.js +1 -1
- package/core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/core/audits/accessibility/aria-required-attr.js +1 -1
- package/core/audits/accessibility/aria-required-children.js +1 -1
- package/core/audits/accessibility/aria-required-parent.js +1 -1
- package/core/audits/accessibility/aria-roles.js +1 -1
- package/core/audits/accessibility/aria-text.js +1 -1
- package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
- package/core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
- package/core/audits/accessibility/aria-valid-attr.js +1 -1
- package/core/audits/accessibility/button-name.js +1 -1
- package/core/audits/accessibility/bypass.js +1 -1
- package/core/audits/accessibility/color-contrast.js +1 -1
- package/core/audits/accessibility/definition-list.js +1 -1
- package/core/audits/accessibility/dlitem.js +1 -1
- package/core/audits/accessibility/document-title.js +1 -1
- package/core/audits/accessibility/duplicate-id-active.js +1 -1
- package/core/audits/accessibility/duplicate-id-aria.js +2 -1
- package/core/audits/accessibility/empty-heading.js +1 -1
- package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
- package/core/audits/accessibility/frame-title.js +1 -1
- package/core/audits/accessibility/heading-order.js +1 -1
- package/core/audits/accessibility/html-has-lang.js +1 -1
- package/core/audits/accessibility/html-lang-valid.js +1 -1
- package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
- package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
- package/core/audits/accessibility/image-alt.js +1 -1
- package/core/audits/accessibility/image-redundant-alt.js +1 -1
- package/core/audits/accessibility/input-button-name.js +1 -1
- package/core/audits/accessibility/input-image-alt.js +1 -1
- package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
- package/core/audits/accessibility/label.js +1 -1
- package/core/audits/accessibility/landmark-one-main.js +1 -1
- package/core/audits/accessibility/link-in-text-block.js +1 -1
- package/core/audits/accessibility/link-name.js +1 -1
- package/core/audits/accessibility/list.js +1 -1
- package/core/audits/accessibility/listitem.js +1 -1
- package/core/audits/accessibility/meta-refresh.js +1 -1
- package/core/audits/accessibility/meta-viewport.js +1 -1
- package/core/audits/accessibility/object-alt.js +1 -1
- package/core/audits/accessibility/select-name.js +1 -1
- package/core/audits/accessibility/skip-link.js +1 -1
- package/core/audits/accessibility/tabindex.js +1 -1
- package/core/audits/accessibility/table-duplicate-name.js +1 -1
- package/core/audits/accessibility/table-fake-caption.js +1 -1
- package/core/audits/accessibility/target-size.js +1 -1
- package/core/audits/accessibility/td-has-header.js +1 -1
- package/core/audits/accessibility/td-headers-attr.js +1 -1
- package/core/audits/accessibility/th-has-data-cells.js +1 -1
- package/core/audits/accessibility/valid-lang.js +1 -1
- package/core/audits/accessibility/video-caption.js +1 -1
- package/core/audits/bf-cache.js +1 -0
- package/core/audits/bootup-time.d.ts +6 -0
- package/core/audits/bootup-time.js +37 -1
- package/core/audits/byte-efficiency/duplicated-javascript.js +1 -0
- package/core/audits/byte-efficiency/efficient-animated-content.js +1 -0
- package/core/audits/byte-efficiency/legacy-javascript.js +2 -1
- package/core/audits/byte-efficiency/modern-image-formats.js +1 -0
- package/core/audits/byte-efficiency/offscreen-images.js +1 -0
- package/core/audits/byte-efficiency/render-blocking-resources.js +1 -0
- package/core/audits/byte-efficiency/total-byte-weight.js +1 -0
- package/core/audits/byte-efficiency/unminified-css.js +1 -0
- package/core/audits/byte-efficiency/unminified-javascript.js +1 -0
- package/core/audits/byte-efficiency/unused-css-rules.js +1 -0
- package/core/audits/byte-efficiency/unused-javascript.js +1 -0
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +1 -0
- package/core/audits/byte-efficiency/uses-optimized-images.js +1 -0
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +1 -0
- package/core/audits/byte-efficiency/uses-responsive-images.js +1 -0
- package/core/audits/byte-efficiency/uses-text-compression.js +1 -0
- package/core/audits/critical-request-chains.js +1 -0
- package/core/audits/dobetterweb/dom-size.d.ts +8 -2
- package/core/audits/dobetterweb/dom-size.js +51 -3
- package/core/audits/dobetterweb/no-document-write.js +1 -0
- package/core/audits/dobetterweb/uses-http2.js +1 -0
- package/core/audits/dobetterweb/uses-passive-event-listeners.js +1 -0
- package/core/audits/font-display.js +1 -0
- package/core/audits/image-aspect-ratio.js +10 -1
- package/core/audits/installable-manifest.d.ts +0 -6
- package/core/audits/installable-manifest.js +1 -17
- package/core/audits/largest-contentful-paint-element.js +22 -4
- package/core/audits/layout-shift-elements.js +1 -0
- package/core/audits/lcp-lazy-loaded.d.ts +3 -2
- package/core/audits/lcp-lazy-loaded.js +35 -5
- package/core/audits/long-tasks.js +1 -0
- package/core/audits/mainthread-work-breakdown.js +1 -0
- package/core/audits/non-composited-animations.js +1 -0
- package/core/audits/prioritize-lcp-image.js +1 -0
- package/core/audits/redirects.js +2 -4
- package/core/audits/seo/crawlable-anchors.js +2 -1
- package/core/audits/server-response-time.js +1 -0
- package/core/audits/third-party-facades.js +14 -5
- package/core/audits/third-party-summary.d.ts +4 -2
- package/core/audits/third-party-summary.js +25 -11
- package/core/audits/unsized-images.js +1 -0
- package/core/audits/user-timings.js +1 -0
- package/core/audits/uses-rel-preconnect.js +1 -0
- package/core/audits/uses-rel-preload.js +1 -0
- package/core/audits/viewport.js +1 -0
- package/core/audits/work-during-interaction.js +1 -0
- package/core/computed/main-resource.d.ts +1 -1
- package/core/computed/main-resource.js +10 -4
- package/core/computed/page-dependency-graph.js +2 -1
- package/core/computed/tbt-impact-tasks.d.ts +8 -13
- package/core/computed/tbt-impact-tasks.js +4 -6
- package/core/computed/viewport-meta.js +11 -1
- package/core/config/config-helpers.js +1 -1
- package/core/config/config.js +1 -1
- package/core/gather/gatherers/accessibility.js +5 -0
- package/core/gather/timespan-runner.d.ts +3 -0
- package/core/gather/timespan-runner.js +23 -0
- package/core/index.cjs +13 -13
- package/core/index.d.cts +15 -2
- package/core/lib/asset-saver.d.ts +0 -3
- package/core/lib/asset-saver.js +68 -42
- package/core/lib/dependency-graph/simulator/network-analyzer.d.ts +7 -1
- package/core/lib/dependency-graph/simulator/network-analyzer.js +17 -1
- package/core/lib/emulation.js +1 -1
- package/core/lib/i18n/i18n.js +2 -2
- package/core/runner.js +5 -4
- package/dist/report/bundle.esm.js +3 -1
- package/dist/report/flow.js +3 -1
- package/dist/report/standalone.js +3 -1
- package/flow-report/tsconfig.json +0 -2
- package/package.json +8 -7
- package/readme.md +4 -1
- package/report/assets/templates.html +2 -0
- package/report/generator/flow-report-assets.js +1 -1
- package/report/generator/report-assets.js +1 -1
- package/report/generator/tsconfig.json +1 -1
- package/report/renderer/components.js +1 -1
- package/report/renderer/i18n-formatter.js +1 -1
- package/shared/localization/format.js +1 -1
- package/shared/localization/locales/ar-XB.json +36 -39
- package/shared/localization/locales/ar.json +36 -39
- package/shared/localization/locales/bg.json +36 -39
- package/shared/localization/locales/ca.json +36 -39
- package/shared/localization/locales/cs.json +36 -39
- package/shared/localization/locales/da.json +36 -39
- package/shared/localization/locales/de.json +36 -39
- package/shared/localization/locales/el.json +36 -39
- package/shared/localization/locales/en-GB.json +7 -10
- package/shared/localization/locales/en-US.json +64 -64
- package/shared/localization/locales/en-XA.json +36 -39
- package/shared/localization/locales/en-XL.json +64 -64
- package/shared/localization/locales/es-419.json +36 -39
- package/shared/localization/locales/es.json +36 -39
- package/shared/localization/locales/fi.json +36 -39
- package/shared/localization/locales/fil.json +36 -39
- package/shared/localization/locales/fr.json +36 -39
- package/shared/localization/locales/he.json +36 -39
- package/shared/localization/locales/hi.json +36 -39
- package/shared/localization/locales/hr.json +36 -39
- package/shared/localization/locales/hu.json +36 -39
- package/shared/localization/locales/id.json +36 -39
- package/shared/localization/locales/it.json +36 -39
- package/shared/localization/locales/ja.json +36 -39
- package/shared/localization/locales/ko.json +36 -39
- package/shared/localization/locales/lt.json +36 -39
- package/shared/localization/locales/lv.json +36 -39
- package/shared/localization/locales/nl.json +36 -39
- package/shared/localization/locales/no.json +36 -39
- package/shared/localization/locales/pl.json +36 -39
- package/shared/localization/locales/pt-PT.json +36 -39
- package/shared/localization/locales/pt.json +36 -39
- package/shared/localization/locales/ro.json +36 -39
- package/shared/localization/locales/ru.json +36 -39
- package/shared/localization/locales/sk.json +36 -39
- package/shared/localization/locales/sl.json +36 -39
- package/shared/localization/locales/sr-Latn.json +36 -39
- package/shared/localization/locales/sr.json +36 -39
- package/shared/localization/locales/sv.json +36 -39
- package/shared/localization/locales/ta.json +36 -39
- package/shared/localization/locales/te.json +36 -39
- package/shared/localization/locales/th.json +36 -39
- package/shared/localization/locales/tr.json +36 -39
- package/shared/localization/locales/uk.json +36 -39
- package/shared/localization/locales/vi.json +36 -39
- package/shared/localization/locales/zh-HK.json +36 -39
- package/shared/localization/locales/zh-TW.json +36 -39
- package/shared/localization/locales/zh.json +36 -39
- package/shared/localization/locales.js +1 -1
- package/{root.js → shared/root.js} +2 -1
- package/shared/tsconfig.json +0 -1
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -1
- package/third-party/chromium-synchronization/installability-errors-test.js +0 -1
- package/third-party/download-content-shell/download-content-shell.js +1 -1
- package/tsconfig.json +0 -2
- package/types/artifacts.d.ts +2 -1
- package/types/audit.d.ts +2 -0
- /package/{esm-utils.d.ts → shared/esm-utils.d.ts} +0 -0
- /package/{esm-utils.js → shared/esm-utils.js} +0 -0
- /package/{root.d.ts → shared/root.d.ts} +0 -0
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
export { TBTImpactTasksComputed as TBTImpactTasks };
|
|
2
|
-
export type TBTImpactTask = LH.Artifacts.TaskNode & {
|
|
3
|
-
tbtImpact: number;
|
|
4
|
-
selfTbtImpact: number;
|
|
5
|
-
};
|
|
6
2
|
declare const TBTImpactTasksComputed: typeof TBTImpactTasks & {
|
|
7
3
|
request: (dependencies: import("../index.js").Artifacts.MetricComputationDataInput, context: import("../../types/utility-types.js").default.ImmutableObject<{
|
|
8
4
|
computedCache: Map<string, import("../lib/arbitrary-equality-map.js").ArbitraryEqualityMap>;
|
|
9
|
-
}>) => Promise<TBTImpactTask[]>;
|
|
5
|
+
}>) => Promise<import("../index.js").Artifacts.TBTImpactTask[]>;
|
|
10
6
|
};
|
|
11
|
-
/** @typedef {LH.Artifacts.TaskNode & {tbtImpact: number, selfTbtImpact: number}} TBTImpactTask */
|
|
12
7
|
declare class TBTImpactTasks {
|
|
13
8
|
/**
|
|
14
9
|
* @param {LH.Artifacts.TaskNode} task
|
|
@@ -28,27 +23,27 @@ declare class TBTImpactTasks {
|
|
|
28
23
|
* @param {LH.Artifacts.TaskNode[]} tasks
|
|
29
24
|
* @param {Map<LH.Artifacts.TaskNode, number>} taskToImpact
|
|
30
25
|
*/
|
|
31
|
-
static createImpactTasks(tasks: LH.Artifacts.TaskNode[], taskToImpact: Map<LH.Artifacts.TaskNode, number>): TBTImpactTask[];
|
|
26
|
+
static createImpactTasks(tasks: LH.Artifacts.TaskNode[], taskToImpact: Map<LH.Artifacts.TaskNode, number>): import("../index.js").Artifacts.TBTImpactTask[];
|
|
32
27
|
/**
|
|
33
28
|
* @param {LH.Artifacts.TaskNode[]} tasks
|
|
34
29
|
* @param {number} startTimeMs
|
|
35
30
|
* @param {number} endTimeMs
|
|
36
|
-
* @return {TBTImpactTask[]}
|
|
31
|
+
* @return {LH.Artifacts.TBTImpactTask[]}
|
|
37
32
|
*/
|
|
38
|
-
static computeImpactsFromObservedTasks(tasks: LH.Artifacts.TaskNode[], startTimeMs: number, endTimeMs: number): TBTImpactTask[];
|
|
33
|
+
static computeImpactsFromObservedTasks(tasks: LH.Artifacts.TaskNode[], startTimeMs: number, endTimeMs: number): LH.Artifacts.TBTImpactTask[];
|
|
39
34
|
/**
|
|
40
35
|
* @param {LH.Artifacts.TaskNode[]} tasks
|
|
41
36
|
* @param {LH.Gatherer.Simulation.Result['nodeTimings']} tbtNodeTimings
|
|
42
37
|
* @param {number} startTimeMs
|
|
43
38
|
* @param {number} endTimeMs
|
|
44
|
-
* @return {TBTImpactTask[]}
|
|
39
|
+
* @return {LH.Artifacts.TBTImpactTask[]}
|
|
45
40
|
*/
|
|
46
|
-
static computeImpactsFromLantern(tasks: LH.Artifacts.TaskNode[], tbtNodeTimings: LH.Gatherer.Simulation.Result['nodeTimings'], startTimeMs: number, endTimeMs: number): TBTImpactTask[];
|
|
41
|
+
static computeImpactsFromLantern(tasks: LH.Artifacts.TaskNode[], tbtNodeTimings: LH.Gatherer.Simulation.Result['nodeTimings'], startTimeMs: number, endTimeMs: number): LH.Artifacts.TBTImpactTask[];
|
|
47
42
|
/**
|
|
48
43
|
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
|
|
49
44
|
* @param {LH.Artifacts.ComputedContext} context
|
|
50
|
-
* @return {Promise<TBTImpactTask[]>}
|
|
45
|
+
* @return {Promise<LH.Artifacts.TBTImpactTask[]>}
|
|
51
46
|
*/
|
|
52
|
-
static compute_(metricComputationData: LH.Artifacts.MetricComputationDataInput, context: LH.Artifacts.ComputedContext): Promise<TBTImpactTask[]>;
|
|
47
|
+
static compute_(metricComputationData: LH.Artifacts.MetricComputationDataInput, context: LH.Artifacts.ComputedContext): Promise<LH.Artifacts.TBTImpactTask[]>;
|
|
53
48
|
}
|
|
54
49
|
//# sourceMappingURL=tbt-impact-tasks.d.ts.map
|
|
@@ -12,8 +12,6 @@ import {TotalBlockingTime} from './metrics/total-blocking-time.js';
|
|
|
12
12
|
import {ProcessedTrace} from './processed-trace.js';
|
|
13
13
|
import {calculateTbtImpactForEvent} from './metrics/tbt-utils.js';
|
|
14
14
|
|
|
15
|
-
/** @typedef {LH.Artifacts.TaskNode & {tbtImpact: number, selfTbtImpact: number}} TBTImpactTask */
|
|
16
|
-
|
|
17
15
|
class TBTImpactTasks {
|
|
18
16
|
/**
|
|
19
17
|
* @param {LH.Artifacts.TaskNode} task
|
|
@@ -66,7 +64,7 @@ class TBTImpactTasks {
|
|
|
66
64
|
* @param {Map<LH.Artifacts.TaskNode, number>} taskToImpact
|
|
67
65
|
*/
|
|
68
66
|
static createImpactTasks(tasks, taskToImpact) {
|
|
69
|
-
/** @type {TBTImpactTask[]} */
|
|
67
|
+
/** @type {LH.Artifacts.TBTImpactTask[]} */
|
|
70
68
|
const tbtImpactTasks = [];
|
|
71
69
|
|
|
72
70
|
for (const task of tasks) {
|
|
@@ -92,7 +90,7 @@ class TBTImpactTasks {
|
|
|
92
90
|
* @param {LH.Artifacts.TaskNode[]} tasks
|
|
93
91
|
* @param {number} startTimeMs
|
|
94
92
|
* @param {number} endTimeMs
|
|
95
|
-
* @return {TBTImpactTask[]}
|
|
93
|
+
* @return {LH.Artifacts.TBTImpactTask[]}
|
|
96
94
|
*/
|
|
97
95
|
static computeImpactsFromObservedTasks(tasks, startTimeMs, endTimeMs) {
|
|
98
96
|
/** @type {Map<LH.Artifacts.TaskNode, number>} */
|
|
@@ -125,7 +123,7 @@ class TBTImpactTasks {
|
|
|
125
123
|
* @param {LH.Gatherer.Simulation.Result['nodeTimings']} tbtNodeTimings
|
|
126
124
|
* @param {number} startTimeMs
|
|
127
125
|
* @param {number} endTimeMs
|
|
128
|
-
* @return {TBTImpactTask[]}
|
|
126
|
+
* @return {LH.Artifacts.TBTImpactTask[]}
|
|
129
127
|
*/
|
|
130
128
|
static computeImpactsFromLantern(tasks, tbtNodeTimings, startTimeMs, endTimeMs) {
|
|
131
129
|
/** @type {Map<LH.Artifacts.TaskNode, number>} */
|
|
@@ -193,7 +191,7 @@ class TBTImpactTasks {
|
|
|
193
191
|
/**
|
|
194
192
|
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
|
|
195
193
|
* @param {LH.Artifacts.ComputedContext} context
|
|
196
|
-
* @return {Promise<TBTImpactTask[]>}
|
|
194
|
+
* @return {Promise<LH.Artifacts.TBTImpactTask[]>}
|
|
197
195
|
*/
|
|
198
196
|
static async compute_(metricComputationData, context) {
|
|
199
197
|
const tbtResult = await TotalBlockingTime.request(metricComputationData, context);
|
|
@@ -35,7 +35,17 @@ class ViewportMeta {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const viewportProps = parsedProps.validProperties;
|
|
38
|
-
const
|
|
38
|
+
const initialScale = Number(viewportProps['initial-scale']);
|
|
39
|
+
|
|
40
|
+
if (!isNaN(initialScale) && initialScale < 1) {
|
|
41
|
+
return {
|
|
42
|
+
hasViewportTag: true,
|
|
43
|
+
isMobileOptimized: false,
|
|
44
|
+
parserWarnings: warnings,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isMobileOptimized = Boolean(viewportProps.width || initialScale);
|
|
39
49
|
|
|
40
50
|
return {
|
|
41
51
|
hasViewportTag: true,
|
|
@@ -16,7 +16,7 @@ import ConfigPlugin from './config-plugin.js';
|
|
|
16
16
|
import {Runner} from '../runner.js';
|
|
17
17
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
18
18
|
import * as validation from './validation.js';
|
|
19
|
-
import {getModuleDirectory} from '../../esm-utils.js';
|
|
19
|
+
import {getModuleDirectory} from '../../shared/esm-utils.js';
|
|
20
20
|
|
|
21
21
|
const require = createRequire(import.meta.url);
|
|
22
22
|
|
package/core/config/config.js
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
mergeConfigFragment,
|
|
29
29
|
mergeConfigFragmentArrayByKey,
|
|
30
30
|
} from './config-helpers.js';
|
|
31
|
-
import {getModuleDirectory} from '../../esm-utils.js';
|
|
31
|
+
import {getModuleDirectory} from '../../shared/esm-utils.js';
|
|
32
32
|
import * as format from '../../shared/localization/format.js';
|
|
33
33
|
|
|
34
34
|
const defaultConfigPath = path.join(
|
|
@@ -42,12 +42,17 @@ async function runA11yChecks() {
|
|
|
42
42
|
'accesskeys': {enabled: true},
|
|
43
43
|
'area-alt': {enabled: false},
|
|
44
44
|
'aria-allowed-role': {enabled: true},
|
|
45
|
+
'aria-braille-equivalent': {enabled: false},
|
|
46
|
+
'aria-conditional-attr': {enabled: false},
|
|
47
|
+
'aria-deprecated-role': {enabled: false},
|
|
45
48
|
'aria-dialog-name': {enabled: true},
|
|
49
|
+
'aria-prohibited-attr': {enabled: false},
|
|
46
50
|
'aria-roledescription': {enabled: false},
|
|
47
51
|
'aria-treeitem-name': {enabled: true},
|
|
48
52
|
'aria-text': {enabled: true},
|
|
49
53
|
'audio-caption': {enabled: false},
|
|
50
54
|
'blink': {enabled: false},
|
|
55
|
+
'duplicate-id-active': {enabled: true},
|
|
51
56
|
'duplicate-id': {enabled: false},
|
|
52
57
|
'empty-heading': {enabled: true},
|
|
53
58
|
'frame-focusable-content': {enabled: false},
|
|
@@ -9,4 +9,7 @@ export function startTimespanGather(page: LH.Puppeteer.Page, options?: {
|
|
|
9
9
|
} | undefined): Promise<{
|
|
10
10
|
endTimespanGather(): Promise<LH.Gatherer.GatherResult>;
|
|
11
11
|
}>;
|
|
12
|
+
export namespace UIStrings {
|
|
13
|
+
const warningNavigationDetected: string;
|
|
14
|
+
}
|
|
12
15
|
//# sourceMappingURL=timespan-runner.d.ts.map
|
|
@@ -12,6 +12,16 @@ import {getEmptyArtifactState, collectPhaseArtifacts, awaitArtifacts} from './ru
|
|
|
12
12
|
import {enableAsyncStacks, prepareTargetForTimespanMode} from './driver/prepare.js';
|
|
13
13
|
import {initializeConfig} from '../config/config.js';
|
|
14
14
|
import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
|
|
15
|
+
import * as i18n from '../lib/i18n/i18n.js';
|
|
16
|
+
|
|
17
|
+
/* eslint-disable max-len */
|
|
18
|
+
const UIStrings = {
|
|
19
|
+
/** A warning that indicates page navigations should be audited using navigation mode, as opposed to timespan mode. "navigation mode" refers to a Lighthouse mode that analyzes a page navigation. "timespan mode" refers to a Lighthouse mode that analyzes user interactions over an arbitrary period of time. */
|
|
20
|
+
warningNavigationDetected: 'A page navigation was detected during the run. Using timespan mode to audit page navigations is not recommended. Use navigation mode to audit page navigations for better third-party attribution and main thread detection.',
|
|
21
|
+
};
|
|
22
|
+
/* eslint-enable max-len */
|
|
23
|
+
|
|
24
|
+
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
15
25
|
|
|
16
26
|
/**
|
|
17
27
|
* @param {LH.Puppeteer.Page} page
|
|
@@ -45,6 +55,13 @@ async function startTimespanGather(page, options = {}) {
|
|
|
45
55
|
|
|
46
56
|
await prepareTargetForTimespanMode(driver, resolvedConfig.settings);
|
|
47
57
|
|
|
58
|
+
let pageNavigationDetected = false;
|
|
59
|
+
function onFrameNavigated() {
|
|
60
|
+
pageNavigationDetected = true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
driver.defaultSession.on('Page.frameNavigated', onFrameNavigated);
|
|
64
|
+
|
|
48
65
|
const disableAsyncStacks = await enableAsyncStacks(driver.defaultSession);
|
|
49
66
|
|
|
50
67
|
await collectPhaseArtifacts({phase: 'startInstrumentation', ...phaseOptions});
|
|
@@ -67,6 +84,11 @@ async function startTimespanGather(page, options = {}) {
|
|
|
67
84
|
// We should disable our `Page.frameNavigated` handlers before that.
|
|
68
85
|
await disableAsyncStacks();
|
|
69
86
|
|
|
87
|
+
driver.defaultSession.off('Page.frameNavigated', onFrameNavigated);
|
|
88
|
+
if (pageNavigationDetected) {
|
|
89
|
+
baseArtifacts.LighthouseRunWarnings.push(str_(UIStrings.warningNavigationDetected));
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
await collectPhaseArtifacts({phase: 'getArtifact', ...phaseOptions});
|
|
71
93
|
await driver.disconnect();
|
|
72
94
|
|
|
@@ -82,4 +104,5 @@ async function startTimespanGather(page, options = {}) {
|
|
|
82
104
|
|
|
83
105
|
export {
|
|
84
106
|
startTimespanGather,
|
|
107
|
+
UIStrings,
|
|
85
108
|
};
|
package/core/index.cjs
CHANGED
|
@@ -5,38 +5,38 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* @typedef ExportType
|
|
10
|
+
* @property {import('./index.js')['startFlow']} startFlow
|
|
11
|
+
* @property {import('./index.js')['navigation']} navigation
|
|
12
|
+
* @property {import('./index.js')['startTimespan']} startTimespan
|
|
13
|
+
* @property {import('./index.js')['snapshot']} snapshot
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** @type {import('./index.js')['default'] & ExportType} */
|
|
9
17
|
const lighthouse = async function lighthouse(...args) {
|
|
10
18
|
const {default: lighthouse} = await import('./index.js');
|
|
11
19
|
return lighthouse(...args);
|
|
12
20
|
};
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
const startFlow = async function startFlow(...args) {
|
|
22
|
+
lighthouse.startFlow = async function startFlow(...args) {
|
|
16
23
|
const {startFlow} = await import('./index.js');
|
|
17
24
|
return startFlow(...args);
|
|
18
25
|
};
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
const navigation = async function navigation(...args) {
|
|
27
|
+
lighthouse.navigation = async function navigation(...args) {
|
|
22
28
|
const {navigation} = await import('./index.js');
|
|
23
29
|
return navigation(...args);
|
|
24
30
|
};
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
const startTimespan = async function startTimespan(...args) {
|
|
32
|
+
lighthouse.startTimespan = async function startTimespan(...args) {
|
|
28
33
|
const {startTimespan} = await import('./index.js');
|
|
29
34
|
return startTimespan(...args);
|
|
30
35
|
};
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
const snapshot = async function snapshot(...args) {
|
|
37
|
+
lighthouse.snapshot = async function snapshot(...args) {
|
|
34
38
|
const {snapshot} = await import('./index.js');
|
|
35
39
|
return snapshot(...args);
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
module.exports = lighthouse;
|
|
39
|
-
module.exports.startFlow = startFlow;
|
|
40
|
-
module.exports.navigation = navigation;
|
|
41
|
-
module.exports.startTimespan = startTimespan;
|
|
42
|
-
module.exports.snapshot = snapshot;
|
package/core/index.d.cts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
export = lighthouse;
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @typedef ExportType
|
|
4
|
+
* @property {import('./index.js')['startFlow']} startFlow
|
|
5
|
+
* @property {import('./index.js')['navigation']} navigation
|
|
6
|
+
* @property {import('./index.js')['startTimespan']} startTimespan
|
|
7
|
+
* @property {import('./index.js')['snapshot']} snapshot
|
|
8
|
+
*/
|
|
9
|
+
/** @type {import('./index.js')['default'] & ExportType} */
|
|
10
|
+
declare const lighthouse: typeof import("./index.js")['default'] & ExportType;
|
|
11
|
+
type ExportType = {
|
|
12
|
+
startFlow: typeof import("./index.js")['startFlow'];
|
|
13
|
+
navigation: typeof import("./index.js")['navigation'];
|
|
14
|
+
startTimespan: typeof import("./index.js")['startTimespan'];
|
|
15
|
+
snapshot: typeof import("./index.js")['snapshot'];
|
|
16
|
+
};
|
|
4
17
|
//# sourceMappingURL=index.d.cts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export type PreparedAssets = {
|
|
2
|
-
passName: string;
|
|
3
2
|
traceData: LH.Trace;
|
|
4
3
|
devtoolsLog: import("../index.js").DevtoolsLog;
|
|
5
4
|
};
|
|
@@ -35,7 +34,6 @@ export function saveFlowArtifacts(flowArtifacts: LH.UserFlow.FlowArtifacts, base
|
|
|
35
34
|
export function saveLhr(lhr: LH.Result, basePath: string): void;
|
|
36
35
|
/**
|
|
37
36
|
* @typedef {object} PreparedAssets
|
|
38
|
-
* @property {string} passName
|
|
39
37
|
* @property {LH.Trace} traceData
|
|
40
38
|
* @property {LH.DevtoolsLog} devtoolsLog
|
|
41
39
|
*/
|
|
@@ -60,7 +58,6 @@ export function loadFlowArtifacts(basePath: string): LH.UserFlow.FlowArtifacts;
|
|
|
60
58
|
*/
|
|
61
59
|
export function saveAssets(artifacts: LH.Artifacts, audits: LH.Result['audits'], pathWithBasename: string): Promise<void>;
|
|
62
60
|
/**
|
|
63
|
-
* Filter traces and extract screenshots to prepare for saving.
|
|
64
61
|
* @param {LH.Artifacts} artifacts
|
|
65
62
|
* @param {LH.Result['audits']} [audits]
|
|
66
63
|
* @return {Promise<Array<PreparedAssets>>}
|
package/core/lib/asset-saver.js
CHANGED
|
@@ -17,17 +17,18 @@ import {MetricTraceEvents} from './traces/metric-trace-events.js';
|
|
|
17
17
|
import {NetworkAnalysis} from '../computed/network-analysis.js';
|
|
18
18
|
import {LoadSimulator} from '../computed/load-simulator.js';
|
|
19
19
|
import {LighthouseError} from '../lib/lh-error.js';
|
|
20
|
-
import {LH_ROOT} from '../../root.js';
|
|
20
|
+
import {LH_ROOT} from '../../shared/root.js';
|
|
21
21
|
|
|
22
22
|
const optionsFilename = 'options.json';
|
|
23
23
|
const artifactsFilename = 'artifacts.json';
|
|
24
24
|
const traceSuffix = '.trace.json';
|
|
25
25
|
const devtoolsLogSuffix = '.devtoolslog.json';
|
|
26
|
+
const defaultPrefix = 'defaultPass';
|
|
27
|
+
const errorPrefix = 'pageLoadError-defaultPass';
|
|
26
28
|
const stepDirectoryRegex = /^step(\d+)$/;
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* @typedef {object} PreparedAssets
|
|
30
|
-
* @property {string} passName
|
|
31
32
|
* @property {LH.Trace} traceData
|
|
32
33
|
* @property {LH.DevtoolsLog} devtoolsLog
|
|
33
34
|
*/
|
|
@@ -53,32 +54,30 @@ function loadArtifacts(basePath) {
|
|
|
53
54
|
|
|
54
55
|
const filenames = fs.readdirSync(basePath);
|
|
55
56
|
|
|
56
|
-
// load devtoolsLogs
|
|
57
|
-
artifacts.devtoolsLogs = {};
|
|
58
57
|
filenames.filter(f => f.endsWith(devtoolsLogSuffix)).forEach(filename => {
|
|
59
|
-
|
|
58
|
+
if (!artifacts.devtoolsLogs) artifacts.devtoolsLogs = {};
|
|
59
|
+
const prefix = filename.replace(devtoolsLogSuffix, '');
|
|
60
60
|
const devtoolsLog = JSON.parse(fs.readFileSync(path.join(basePath, filename), 'utf8'));
|
|
61
|
-
artifacts.devtoolsLogs[
|
|
62
|
-
if (
|
|
61
|
+
artifacts.devtoolsLogs[prefix] = devtoolsLog;
|
|
62
|
+
if (prefix === defaultPrefix) {
|
|
63
63
|
artifacts.DevtoolsLog = devtoolsLog;
|
|
64
64
|
}
|
|
65
|
-
if (
|
|
65
|
+
if (prefix === errorPrefix) {
|
|
66
66
|
artifacts.DevtoolsLogError = devtoolsLog;
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
// load traces
|
|
71
|
-
artifacts.traces = {};
|
|
72
70
|
filenames.filter(f => f.endsWith(traceSuffix)).forEach(filename => {
|
|
71
|
+
if (!artifacts.traces) artifacts.traces = {};
|
|
73
72
|
const file = fs.readFileSync(path.join(basePath, filename), {encoding: 'utf-8'});
|
|
74
73
|
const trace = JSON.parse(file);
|
|
75
|
-
const
|
|
76
|
-
artifacts.traces[
|
|
77
|
-
if (
|
|
78
|
-
artifacts.Trace = artifacts.traces[
|
|
74
|
+
const prefix = filename.replace(traceSuffix, '');
|
|
75
|
+
artifacts.traces[prefix] = Array.isArray(trace) ? {traceEvents: trace} : trace;
|
|
76
|
+
if (prefix === defaultPrefix) {
|
|
77
|
+
artifacts.Trace = artifacts.traces[prefix];
|
|
79
78
|
}
|
|
80
|
-
if (
|
|
81
|
-
artifacts.TraceError = artifacts.traces[
|
|
79
|
+
if (prefix === errorPrefix) {
|
|
80
|
+
artifacts.TraceError = artifacts.traces[prefix];
|
|
82
81
|
}
|
|
83
82
|
});
|
|
84
83
|
|
|
@@ -220,23 +219,34 @@ async function saveArtifacts(artifacts, basePath) {
|
|
|
220
219
|
}
|
|
221
220
|
}
|
|
222
221
|
|
|
223
|
-
// `
|
|
222
|
+
// `devtoolsLogs` and `traces` are duplicate compat artifacts.
|
|
224
223
|
// We don't need to save them twice, so extract them here.
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
const {
|
|
225
|
+
// eslint-disable-next-line no-unused-vars
|
|
226
|
+
traces,
|
|
227
|
+
// eslint-disable-next-line no-unused-vars
|
|
228
|
+
devtoolsLogs,
|
|
229
|
+
DevtoolsLog,
|
|
230
|
+
Trace,
|
|
231
|
+
DevtoolsLogError,
|
|
232
|
+
TraceError,
|
|
233
|
+
...restArtifacts
|
|
234
|
+
} = artifacts;
|
|
235
|
+
|
|
236
|
+
if (Trace) {
|
|
237
|
+
await saveTrace(Trace, `${basePath}/${defaultPrefix}${traceSuffix}`);
|
|
238
|
+
}
|
|
227
239
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
for (const [passName, trace] of Object.entries(traces)) {
|
|
231
|
-
await saveTrace(trace, `${basePath}/${passName}${traceSuffix}`);
|
|
232
|
-
}
|
|
240
|
+
if (TraceError) {
|
|
241
|
+
await saveTrace(TraceError, `${basePath}/${errorPrefix}${traceSuffix}`);
|
|
233
242
|
}
|
|
234
243
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
244
|
+
if (DevtoolsLog) {
|
|
245
|
+
await saveDevtoolsLog(DevtoolsLog, `${basePath}/${defaultPrefix}${devtoolsLogSuffix}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (DevtoolsLogError) {
|
|
249
|
+
await saveDevtoolsLog(DevtoolsLogError, `${basePath}/${errorPrefix}${devtoolsLogSuffix}`);
|
|
240
250
|
}
|
|
241
251
|
|
|
242
252
|
// save everything else, using a replacer to serialize LighthouseErrors in the artifacts.
|
|
@@ -256,33 +266,49 @@ function saveLhr(lhr, basePath) {
|
|
|
256
266
|
}
|
|
257
267
|
|
|
258
268
|
/**
|
|
259
|
-
* Filter
|
|
269
|
+
* Filter trace and extract screenshots to prepare for saving.
|
|
270
|
+
* @param {LH.Trace} trace
|
|
271
|
+
* @param {LH.Result['audits']} [audits]
|
|
272
|
+
* @return {LH.Trace}
|
|
273
|
+
*/
|
|
274
|
+
function prepareTraceAsset(trace, audits) {
|
|
275
|
+
if (!trace) return trace;
|
|
276
|
+
|
|
277
|
+
const traceData = Object.assign({}, trace);
|
|
278
|
+
if (audits) {
|
|
279
|
+
const evts = new MetricTraceEvents(traceData.traceEvents, audits).generateFakeEvents();
|
|
280
|
+
traceData.traceEvents = traceData.traceEvents.concat(evts);
|
|
281
|
+
}
|
|
282
|
+
return traceData;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
260
286
|
* @param {LH.Artifacts} artifacts
|
|
261
287
|
* @param {LH.Result['audits']} [audits]
|
|
262
288
|
* @return {Promise<Array<PreparedAssets>>}
|
|
263
289
|
*/
|
|
264
290
|
async function prepareAssets(artifacts, audits) {
|
|
265
|
-
const passNames = Object.keys(artifacts.traces);
|
|
266
291
|
/** @type {Array<PreparedAssets>} */
|
|
267
292
|
const assets = [];
|
|
268
293
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const traceData = Object.assign({}, trace);
|
|
274
|
-
if (audits) {
|
|
275
|
-
const evts = new MetricTraceEvents(traceData.traceEvents, audits).generateFakeEvents();
|
|
276
|
-
traceData.traceEvents = traceData.traceEvents.concat(evts);
|
|
277
|
-
}
|
|
278
|
-
|
|
294
|
+
const devtoolsLog = artifacts.DevtoolsLog;
|
|
295
|
+
const traceData = prepareTraceAsset(artifacts.Trace, audits);
|
|
296
|
+
if (traceData || devtoolsLog) {
|
|
279
297
|
assets.push({
|
|
280
|
-
passName,
|
|
281
298
|
traceData,
|
|
282
299
|
devtoolsLog,
|
|
283
300
|
});
|
|
284
301
|
}
|
|
285
302
|
|
|
303
|
+
const devtoolsLogError = artifacts.DevtoolsLogError;
|
|
304
|
+
const traceErrorData = prepareTraceAsset(artifacts.TraceError, audits);
|
|
305
|
+
if (devtoolsLogError || traceErrorData) {
|
|
306
|
+
assets.push({
|
|
307
|
+
traceData: traceErrorData,
|
|
308
|
+
devtoolsLog: devtoolsLogError,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
286
312
|
return assets;
|
|
287
313
|
}
|
|
288
314
|
|
|
@@ -174,9 +174,15 @@ export class NetworkAnalyzer {
|
|
|
174
174
|
* @return {LH.Artifacts.NetworkRequest|undefined}
|
|
175
175
|
*/
|
|
176
176
|
static findResourceForUrl(records: Array<LH.Artifacts.NetworkRequest>, resourceUrl: string): LH.Artifacts.NetworkRequest | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* @param {Array<LH.Artifacts.NetworkRequest>} records
|
|
179
|
+
* @param {string} resourceUrl
|
|
180
|
+
* @return {LH.Artifacts.NetworkRequest|undefined}
|
|
181
|
+
*/
|
|
182
|
+
static findLastDocumentForUrl(records: Array<LH.Artifacts.NetworkRequest>, resourceUrl: string): LH.Artifacts.NetworkRequest | undefined;
|
|
177
183
|
/**
|
|
178
184
|
* Resolves redirect chain given a main document.
|
|
179
|
-
* See: {@link NetworkAnalyzer.
|
|
185
|
+
* See: {@link NetworkAnalyzer.findLastDocumentForUrl}) for how to retrieve main document.
|
|
180
186
|
*
|
|
181
187
|
* @param {LH.Artifacts.NetworkRequest} request
|
|
182
188
|
* @return {LH.Artifacts.NetworkRequest}
|
|
@@ -506,9 +506,25 @@ class NetworkAnalyzer {
|
|
|
506
506
|
);
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
+
/**
|
|
510
|
+
* @param {Array<LH.Artifacts.NetworkRequest>} records
|
|
511
|
+
* @param {string} resourceUrl
|
|
512
|
+
* @return {LH.Artifacts.NetworkRequest|undefined}
|
|
513
|
+
*/
|
|
514
|
+
static findLastDocumentForUrl(records, resourceUrl) {
|
|
515
|
+
// equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first
|
|
516
|
+
const matchingRequests = records.filter(request =>
|
|
517
|
+
request.resourceType === 'Document' &&
|
|
518
|
+
// Note: `request.url` should never have a fragment, else this optimization gives wrong results.
|
|
519
|
+
resourceUrl.startsWith(request.url) &&
|
|
520
|
+
UrlUtils.equalWithExcludedFragments(request.url, resourceUrl)
|
|
521
|
+
);
|
|
522
|
+
return matchingRequests[matchingRequests.length - 1];
|
|
523
|
+
}
|
|
524
|
+
|
|
509
525
|
/**
|
|
510
526
|
* Resolves redirect chain given a main document.
|
|
511
|
-
* See: {@link NetworkAnalyzer.
|
|
527
|
+
* See: {@link NetworkAnalyzer.findLastDocumentForUrl}) for how to retrieve main document.
|
|
512
528
|
*
|
|
513
529
|
* @param {LH.Artifacts.NetworkRequest} request
|
|
514
530
|
* @return {LH.Artifacts.NetworkRequest}
|
package/core/lib/emulation.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {lighthouseVersion} from '../../root.js';
|
|
7
|
+
import {lighthouseVersion} from '../../shared/root.js';
|
|
8
8
|
|
|
9
9
|
const NO_THROTTLING_METRICS = {
|
|
10
10
|
latency: 0,
|
package/core/lib/i18n/i18n.js
CHANGED
|
@@ -13,9 +13,9 @@ import lookupClosestLocale from 'lookup-closest-locale';
|
|
|
13
13
|
import log from 'lighthouse-logger';
|
|
14
14
|
|
|
15
15
|
import {getAvailableLocales} from '../../../shared/localization/format.js';
|
|
16
|
-
import {LH_ROOT} from '../../../root.js';
|
|
16
|
+
import {LH_ROOT} from '../../../shared/root.js';
|
|
17
17
|
import {isIcuMessage, formatMessage, DEFAULT_LOCALE} from '../../../shared/localization/format.js';
|
|
18
|
-
import {getModulePath} from '../../../esm-utils.js';
|
|
18
|
+
import {getModulePath} from '../../../shared/esm-utils.js';
|
|
19
19
|
|
|
20
20
|
const UIStrings = {
|
|
21
21
|
/** Used to show the duration in milliseconds that something lasted. The `{timeInMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 63 ms) */
|
package/core/runner.js
CHANGED
|
@@ -19,8 +19,8 @@ import * as assetSaver from './lib/asset-saver.js';
|
|
|
19
19
|
import {Sentry} from './lib/sentry.js';
|
|
20
20
|
import {ReportGenerator} from '../report/generator/report-generator.js';
|
|
21
21
|
import {LighthouseError} from './lib/lh-error.js';
|
|
22
|
-
import {lighthouseVersion} from '../root.js';
|
|
23
|
-
import {getModuleDirectory} from '../esm-utils.js';
|
|
22
|
+
import {lighthouseVersion} from '../shared/root.js';
|
|
23
|
+
import {getModuleDirectory} from '../shared/esm-utils.js';
|
|
24
24
|
import {EntityClassification} from './computed/entity-classification.js';
|
|
25
25
|
import UrlUtils from './lib/url-utils.js';
|
|
26
26
|
|
|
@@ -375,9 +375,10 @@ class Runner {
|
|
|
375
375
|
|
|
376
376
|
// If trace/devtoolsLog required, check that DEFAULT_PASS trace/devtoolsLog exists.
|
|
377
377
|
// NOTE: for now, not a pass-specific check of traces or devtoolsLogs.
|
|
378
|
-
const noRequiredTrace = artifactName === 'traces' &&
|
|
378
|
+
const noRequiredTrace = artifactName === 'traces' &&
|
|
379
|
+
!artifacts.traces?.[Audit.DEFAULT_PASS];
|
|
379
380
|
const noRequiredDevtoolsLog = artifactName === 'devtoolsLogs' &&
|
|
380
|
-
|
|
381
|
+
!artifacts.devtoolsLogs?.[Audit.DEFAULT_PASS];
|
|
381
382
|
|
|
382
383
|
if (noArtifact || noRequiredTrace || noRequiredDevtoolsLog) {
|
|
383
384
|
log.warn('Runner',
|