lighthouse 9.5.0-dev.20220628 → 9.5.0-dev.20220629
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/dist/report/bundle.esm.js +7 -6
- package/dist/report/flow.js +24 -24
- package/dist/report/standalone.js +19 -19
- package/flow-report/test/common-test.tsx +5 -4
- package/flow-report/test/flow-report-pptr-test.ts +4 -7
- package/flow-report/test/run-flow-report-tests.sh +20 -0
- package/flow-report/test/setup/env-setup.ts +44 -24
- package/flow-report/test/sidebar/sidebar-test.tsx +0 -1
- package/flow-report/test/topbar-test.tsx +5 -5
- package/flow-report/test/util-test.tsx +3 -3
- package/flow-report/tsconfig.json +4 -1
- package/lighthouse-cli/cli-flags.js +313 -305
- package/lighthouse-cli/test/smokehouse/report-assert-test.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +30 -5
- package/lighthouse-core/config/config-plugin.js +26 -0
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +1 -0
- package/lighthouse-core/gather/gather-runner.js +1 -0
- package/lighthouse-core/gather/gatherers/main-document-content.js +0 -2
- package/lighthouse-core/lib/lh-env.js +1 -1
- package/lighthouse-core/lib/navigation-error.js +26 -7
- package/lighthouse-core/util-commonjs.js +7 -6
- package/package.json +24 -19
- package/report/renderer/util.js +7 -6
- package/report/test/.eslintrc.cjs +4 -1
- package/report/test/clients/bundle-test.js +4 -4
- package/report/test/generator/report-generator-test.js +3 -1
- package/report/test/renderer/category-renderer-test.js +3 -3
- package/report/test/renderer/components-test.js +36 -34
- package/report/test/renderer/crc-details-renderer-test.js +2 -2
- package/report/test/renderer/details-renderer-test.js +2 -2
- package/report/test/renderer/dom-test.js +4 -4
- package/report/test/renderer/element-screenshot-renderer-test.js +3 -2
- package/report/test/renderer/performance-category-renderer-test.js +4 -4
- package/report/test/renderer/pwa-category-renderer-test.js +3 -3
- package/report/test/renderer/report-renderer-axe-test.js +6 -8
- package/report/test/renderer/report-renderer-test.js +5 -5
- package/report/test/renderer/report-ui-features-test.js +8 -8
- package/report/test/renderer/snippet-renderer-test.js +2 -2
- package/report/test/renderer/text-encoding-test.js +2 -2
- package/report/test/renderer/util-test.js +1 -1
- package/root.js +0 -18
- package/shared/localization/locales/en-US.json +3 -0
- package/shared/localization/locales/en-XL.json +3 -0
- package/shared/test/localization/.eslintrc.cjs +4 -1
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -1
- package/third-party/chromium-synchronization/installability-errors-test.js +1 -3
- package/types/test.d.ts +53 -0
- package/flow-report/jest.config.js +0 -24
- package/flow-report/test/setup/global-setup.ts +0 -11
- package/jest.config.js +0 -30
- package/types/jest.d.ts +0 -25
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
/* eslint-disable no-control-regex */
|
|
8
8
|
|
|
9
|
+
import {readJson} from '../../../lighthouse-core/test/test-utils.js';
|
|
9
10
|
import {findDifferences, getAssertionReport} from './report-assert.js';
|
|
10
|
-
import {readJson} from '../../../root.js';
|
|
11
11
|
|
|
12
12
|
describe('findDiffersences', () => {
|
|
13
13
|
const testCases = {
|
|
@@ -33,6 +33,9 @@ const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
|
|
|
33
33
|
|
|
34
34
|
const IGNORE_THRESHOLD_IN_BYTES = 4096;
|
|
35
35
|
|
|
36
|
+
// Ignore up to 12KB of waste if an effort was made with breakpoints.
|
|
37
|
+
const IGNORE_THRESHOLD_IN_BYTES_BREAKPOINTS_PRESENT = 12288;
|
|
38
|
+
|
|
36
39
|
class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
37
40
|
/**
|
|
38
41
|
* @return {LH.Audit.Meta}
|
|
@@ -112,6 +115,18 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {LH.Artifacts.ImageElement} image
|
|
121
|
+
* @return {number};
|
|
122
|
+
*/
|
|
123
|
+
static determineAllowableWaste(image) {
|
|
124
|
+
if (image.srcset || image.isPicture) {
|
|
125
|
+
return IGNORE_THRESHOLD_IN_BYTES_BREAKPOINTS_PRESENT;
|
|
126
|
+
}
|
|
127
|
+
return IGNORE_THRESHOLD_IN_BYTES;
|
|
128
|
+
}
|
|
129
|
+
|
|
115
130
|
/**
|
|
116
131
|
* @param {LH.Artifacts} artifacts
|
|
117
132
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
@@ -126,6 +141,8 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
|
126
141
|
const ViewportDimensions = artifacts.ViewportDimensions;
|
|
127
142
|
/** @type {Map<string, LH.Audit.ByteEfficiencyItem>} */
|
|
128
143
|
const resultsMap = new Map();
|
|
144
|
+
/** @type {Array<string>} */
|
|
145
|
+
const passedImageList = [];
|
|
129
146
|
for (const image of images) {
|
|
130
147
|
// Give SVG a free pass because creating a "responsive" SVG is of questionable value.
|
|
131
148
|
// Ignore CSS images because it's difficult to determine what is a spritesheet,
|
|
@@ -147,15 +164,23 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
|
147
164
|
);
|
|
148
165
|
if (!processed) continue;
|
|
149
166
|
|
|
150
|
-
//
|
|
167
|
+
// Verify the image wastes more than the minimum.
|
|
168
|
+
const exceedsAllowableWaste = processed.wastedBytes > this.determineAllowableWaste(image);
|
|
169
|
+
|
|
151
170
|
const existing = resultsMap.get(processed.url);
|
|
152
|
-
|
|
153
|
-
|
|
171
|
+
// Don't warn about an image that was later used appropriately, or wastes a trivial amount of data.
|
|
172
|
+
if (exceedsAllowableWaste && !passedImageList.includes(processed.url)) {
|
|
173
|
+
if ((!existing || existing.wastedBytes > processed.wastedBytes)) {
|
|
174
|
+
resultsMap.set(processed.url, processed);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// Ensure this url passes for future tests.
|
|
178
|
+
resultsMap.delete(processed.url);
|
|
179
|
+
passedImageList.push(processed.url);
|
|
154
180
|
}
|
|
155
181
|
}
|
|
156
182
|
|
|
157
|
-
const items = Array.from(resultsMap.values())
|
|
158
|
-
.filter(item => item.wastedBytes > IGNORE_THRESHOLD_IN_BYTES);
|
|
183
|
+
const items = Array.from(resultsMap.values());
|
|
159
184
|
|
|
160
185
|
/** @type {LH.Audit.Details.Opportunity['headings']} */
|
|
161
186
|
const headings = [
|
|
@@ -23,6 +23,24 @@ function isObjectOfUnknownProperties(val) {
|
|
|
23
23
|
return typeof val === 'object' && val !== null && !Array.isArray(val);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @param {unknown} str
|
|
28
|
+
* @return {str is LH.Gatherer.GatherMode}
|
|
29
|
+
*/
|
|
30
|
+
function objectIsGatherMode(str) {
|
|
31
|
+
if (typeof str !== 'string') return false;
|
|
32
|
+
return str === 'navigation' || str === 'timespan' || str === 'snapshot';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {unknown} arr
|
|
37
|
+
* @return {arr is Array<LH.Gatherer.GatherMode>}
|
|
38
|
+
*/
|
|
39
|
+
function isArrayOfGatherModes(arr) {
|
|
40
|
+
if (!Array.isArray(arr)) return false;
|
|
41
|
+
return arr.every(objectIsGatherMode);
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
/**
|
|
27
45
|
* Asserts that obj has no own properties, throwing a nice error message if it does.
|
|
28
46
|
* Plugin and object name are included for nicer logging.
|
|
@@ -124,6 +142,7 @@ class ConfigPlugin {
|
|
|
124
142
|
description,
|
|
125
143
|
manualDescription,
|
|
126
144
|
auditRefs: auditRefsJson,
|
|
145
|
+
supportedModes,
|
|
127
146
|
...invalidRest
|
|
128
147
|
} = categoryJson;
|
|
129
148
|
|
|
@@ -138,6 +157,12 @@ class ConfigPlugin {
|
|
|
138
157
|
if (!i18n.isStringOrIcuMessage(manualDescription) && manualDescription !== undefined) {
|
|
139
158
|
throw new Error(`${pluginName} has an invalid category manualDescription.`);
|
|
140
159
|
}
|
|
160
|
+
if (!isArrayOfGatherModes(supportedModes) && supportedModes !== undefined) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`${pluginName} supportedModes must be an array, ` +
|
|
163
|
+
`valid array values are "navigation", "timespan", and "snapshot".`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
141
166
|
const auditRefs = ConfigPlugin._parseAuditRefsList(auditRefsJson, pluginName);
|
|
142
167
|
|
|
143
168
|
return {
|
|
@@ -145,6 +170,7 @@ class ConfigPlugin {
|
|
|
145
170
|
auditRefs,
|
|
146
171
|
description: description,
|
|
147
172
|
manualDescription: manualDescription,
|
|
173
|
+
supportedModes,
|
|
148
174
|
};
|
|
149
175
|
}
|
|
150
176
|
|
|
@@ -604,6 +604,7 @@ class GatherRunner {
|
|
|
604
604
|
url: passContext.url,
|
|
605
605
|
loadFailureMode: passConfig.loadFailureMode,
|
|
606
606
|
networkRecords: loadData.networkRecords,
|
|
607
|
+
warnings: passContext.LighthouseRunWarnings,
|
|
607
608
|
});
|
|
608
609
|
if (pageLoadError) {
|
|
609
610
|
const localizedMessage = format.getFormatted(pageLoadError.friendlyMessage,
|
|
@@ -21,7 +21,6 @@ class MainDocumentContent extends FRGatherer {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
24
|
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
26
25
|
* @param {LH.Artifacts['DevtoolsLog']} devtoolsLog
|
|
27
26
|
* @return {Promise<LH.Artifacts['MainDocumentContent']>}
|
|
@@ -34,7 +33,6 @@ class MainDocumentContent extends FRGatherer {
|
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
|
-
*
|
|
38
36
|
* @param {LH.Gatherer.FRTransitionalContext<'DevtoolsLog'>} context
|
|
39
37
|
* @return {Promise<LH.Artifacts['MainDocumentContent']>}
|
|
40
38
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const process = require('process');
|
|
9
9
|
|
|
10
10
|
module.exports = {
|
|
11
|
-
// NODE_ENV is set to test by
|
|
11
|
+
// NODE_ENV is set to test by mocha-setup.js and the smokehouse CLI runner
|
|
12
12
|
// CI as a catchall for everything we do in GitHub Actions
|
|
13
13
|
isUnderTest: !!process.env.CI || process.env.NODE_ENV === 'test',
|
|
14
14
|
};
|
|
@@ -8,6 +8,22 @@
|
|
|
8
8
|
const LHError = require('./lh-error.js');
|
|
9
9
|
const NetworkAnalyzer = require('./dependency-graph/simulator/network-analyzer.js');
|
|
10
10
|
const NetworkRequest = require('./network-request.js');
|
|
11
|
+
const i18n = require('./i18n/i18n.js');
|
|
12
|
+
|
|
13
|
+
const UIStrings = {
|
|
14
|
+
/**
|
|
15
|
+
* Warning shown in report when the page under test is an XHTML document, which Lighthouse does not directly support
|
|
16
|
+
* so we display a warning.
|
|
17
|
+
*/
|
|
18
|
+
warningXhtml:
|
|
19
|
+
'The page MIME type is XHTML: Lighthouse does not explicitly support this document type',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
|
|
23
|
+
|
|
24
|
+
// MIME types are case-insensitive but Chrome normalizes MIME types to be lowercase.
|
|
25
|
+
const HTML_MIME_TYPE = 'text/html';
|
|
26
|
+
const XHTML_MIME_TYPE = 'application/xhtml+xml';
|
|
11
27
|
|
|
12
28
|
/**
|
|
13
29
|
* Returns an error if the original network request failed or wasn't found.
|
|
@@ -76,16 +92,15 @@ function getInterstitialError(mainRecord, networkRecords) {
|
|
|
76
92
|
* @return {LH.LighthouseError|undefined}
|
|
77
93
|
*/
|
|
78
94
|
function getNonHtmlError(finalRecord) {
|
|
79
|
-
// MIME types are case-insenstive but Chrome normalizes MIME types to be lowercase.
|
|
80
|
-
const HTML_MIME_TYPE = 'text/html';
|
|
81
|
-
|
|
82
95
|
// If we never requested a document, there's no doctype error, let other cases handle it.
|
|
83
96
|
if (!finalRecord) return undefined;
|
|
84
97
|
|
|
85
98
|
// mimeType is determined by the browser, we assume Chrome is determining mimeType correctly,
|
|
86
99
|
// independently of 'Content-Type' response headers, and always sending mimeType if well-formed.
|
|
87
|
-
if (
|
|
88
|
-
return new LHError(LHError.errors.NOT_HTML, {
|
|
100
|
+
if (finalRecord.mimeType !== HTML_MIME_TYPE && finalRecord.mimeType !== XHTML_MIME_TYPE) {
|
|
101
|
+
return new LHError(LHError.errors.NOT_HTML, {
|
|
102
|
+
mimeType: finalRecord.mimeType,
|
|
103
|
+
});
|
|
89
104
|
}
|
|
90
105
|
|
|
91
106
|
return undefined;
|
|
@@ -95,7 +110,7 @@ function getNonHtmlError(finalRecord) {
|
|
|
95
110
|
* Returns an error if the page load should be considered failed, e.g. from a
|
|
96
111
|
* main document request failure, a security issue, etc.
|
|
97
112
|
* @param {LH.LighthouseError|undefined} navigationError
|
|
98
|
-
* @param {{url: string, loadFailureMode: LH.Gatherer.PassContext['passConfig']['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>}} context
|
|
113
|
+
* @param {{url: string, loadFailureMode: LH.Gatherer.PassContext['passConfig']['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
|
|
99
114
|
* @return {LH.LighthouseError|undefined}
|
|
100
115
|
*/
|
|
101
116
|
function getPageLoadError(navigationError, context) {
|
|
@@ -120,6 +135,10 @@ function getPageLoadError(navigationError, context) {
|
|
|
120
135
|
finalRecord = NetworkAnalyzer.resolveRedirects(mainRecord);
|
|
121
136
|
}
|
|
122
137
|
|
|
138
|
+
if (finalRecord?.mimeType === XHTML_MIME_TYPE) {
|
|
139
|
+
context.warnings.push(str_(UIStrings.warningXhtml));
|
|
140
|
+
}
|
|
141
|
+
|
|
123
142
|
const networkError = getNetworkError(mainRecord);
|
|
124
143
|
const interstitialError = getInterstitialError(mainRecord, networkRecords);
|
|
125
144
|
const nonHtmlError = getNonHtmlError(finalRecord);
|
|
@@ -145,10 +164,10 @@ function getPageLoadError(navigationError, context) {
|
|
|
145
164
|
return navigationError;
|
|
146
165
|
}
|
|
147
166
|
|
|
148
|
-
|
|
149
167
|
module.exports = {
|
|
150
168
|
getNetworkError,
|
|
151
169
|
getInterstitialError,
|
|
152
170
|
getPageLoadError,
|
|
153
171
|
getNonHtmlError,
|
|
172
|
+
UIStrings,
|
|
154
173
|
};
|
|
@@ -572,15 +572,16 @@ class Util {
|
|
|
572
572
|
*/
|
|
573
573
|
Util.reportJson = null;
|
|
574
574
|
|
|
575
|
+
let svgSuffix = 0;
|
|
575
576
|
/**
|
|
576
577
|
* An always-increasing counter for making unique SVG ID suffixes.
|
|
577
578
|
*/
|
|
578
|
-
Util.getUniqueSuffix = (
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
579
|
+
Util.getUniqueSuffix = () => {
|
|
580
|
+
return svgSuffix++;
|
|
581
|
+
};
|
|
582
|
+
Util.resetUniqueSuffix = () => {
|
|
583
|
+
svgSuffix = 0;
|
|
584
|
+
};
|
|
584
585
|
|
|
585
586
|
/**
|
|
586
587
|
* Report-renderer-specific strings.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
|
-
"version": "9.5.0-dev.
|
|
3
|
+
"version": "9.5.0-dev.20220629",
|
|
4
4
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
5
5
|
"main": "./lighthouse-core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,24 +35,24 @@
|
|
|
35
35
|
"smoke": "node lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js",
|
|
36
36
|
"debug": "node --inspect-brk ./lighthouse-cli/index.js",
|
|
37
37
|
"start": "yarn build-report --standalone && node ./lighthouse-cli/index.js",
|
|
38
|
-
"
|
|
38
|
+
"mocha": "node lighthouse-core/test/scripts/run-mocha-tests.js",
|
|
39
39
|
"test": "yarn diff:sample-json && yarn lint --quiet && yarn unit && yarn type-check",
|
|
40
40
|
"test-bundle": "yarn smoke --runner bundle --retries=2",
|
|
41
|
-
"test-clients": "yarn
|
|
42
|
-
"test-viewer": "yarn unit-viewer && yarn
|
|
43
|
-
"test-treemap": "yarn unit-treemap && yarn
|
|
41
|
+
"test-clients": "yarn mocha --testMatch clients/**/*-test.js && yarn mocha --testMatch clients/**/*-test-pptr.js",
|
|
42
|
+
"test-viewer": "yarn unit-viewer && yarn mocha --testMatch viewer/**/*-test-pptr.js --timeout 35000",
|
|
43
|
+
"test-treemap": "yarn unit-treemap && yarn mocha --testMatch treemap/**/*-test-pptr.js --timeout 35000",
|
|
44
44
|
"test-lantern": "bash lighthouse-core/scripts/test-lantern.sh",
|
|
45
45
|
"test-legacy-javascript": "bash lighthouse-core/scripts/test-legacy-javascript.sh",
|
|
46
46
|
"test-docs": "yarn --cwd docs/recipes/ test",
|
|
47
47
|
"test-proto": "yarn compile-proto && yarn build-proto-roundtrip",
|
|
48
|
-
"unit-core": "yarn
|
|
49
|
-
"unit-cli": "yarn
|
|
50
|
-
"unit-report": "yarn
|
|
51
|
-
"unit-treemap": "yarn
|
|
52
|
-
"unit-viewer": "yarn
|
|
53
|
-
"unit-flow": "
|
|
54
|
-
"unit": "yarn
|
|
55
|
-
"unit:ci": "NODE_OPTIONS=--max-old-space-size=8192 npm run
|
|
48
|
+
"unit-core": "yarn mocha lighthouse-core",
|
|
49
|
+
"unit-cli": "yarn mocha --testMatch lighthouse-cli/**/*-test.js",
|
|
50
|
+
"unit-report": "yarn mocha --testMatch report/**/*-test.js",
|
|
51
|
+
"unit-treemap": "yarn mocha --testMatch treemap/**/*-test.js",
|
|
52
|
+
"unit-viewer": "yarn mocha --testMatch viewer/**/*-test.js",
|
|
53
|
+
"unit-flow": "bash flow-report/test/run-flow-report-tests.sh",
|
|
54
|
+
"unit": "yarn unit-flow && yarn mocha",
|
|
55
|
+
"unit:ci": "NODE_OPTIONS=--max-old-space-size=8192 npm run unit --forbid-only",
|
|
56
56
|
"core-unit": "yarn unit-core",
|
|
57
57
|
"cli-unit": "yarn unit-cli",
|
|
58
58
|
"viewer-unit": "yarn unit-viewer",
|
|
@@ -96,6 +96,8 @@
|
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@build-tracker/cli": "^1.0.0-beta.15",
|
|
99
|
+
"@esbuild-kit/esm-loader": "^2.1.1",
|
|
100
|
+
"@jest/fake-timers": "^28.1.0",
|
|
99
101
|
"@rollup/plugin-alias": "^3.1.2",
|
|
100
102
|
"@rollup/plugin-commonjs": "^20.0.0",
|
|
101
103
|
"@rollup/plugin-dynamic-import-vars": "^1.1.1",
|
|
@@ -103,7 +105,7 @@
|
|
|
103
105
|
"@rollup/plugin-node-resolve": "^13.0.4",
|
|
104
106
|
"@rollup/plugin-typescript": "^8.2.5",
|
|
105
107
|
"@stadtlandnetz/rollup-plugin-postprocess": "^1.1.0",
|
|
106
|
-
"@testing-library/preact": "^
|
|
108
|
+
"@testing-library/preact": "^3.1.1",
|
|
107
109
|
"@testing-library/preact-hooks": "^1.1.0",
|
|
108
110
|
"@types/archiver": "^2.1.2",
|
|
109
111
|
"@types/chrome": "^0.0.154",
|
|
@@ -113,10 +115,10 @@
|
|
|
113
115
|
"@types/estree": "^0.0.50",
|
|
114
116
|
"@types/gh-pages": "^2.0.0",
|
|
115
117
|
"@types/google.analytics": "0.0.39",
|
|
116
|
-
"@types/jest": "^27.0.3",
|
|
117
118
|
"@types/jpeg-js": "^0.3.7",
|
|
118
119
|
"@types/jsdom": "^16.2.13",
|
|
119
120
|
"@types/lodash": "^4.14.178",
|
|
121
|
+
"@types/mocha": "^9.0.0",
|
|
120
122
|
"@types/node": "*",
|
|
121
123
|
"@types/pako": "^1.0.1",
|
|
122
124
|
"@types/resize-observer-browser": "^0.1.1",
|
|
@@ -130,7 +132,7 @@
|
|
|
130
132
|
"acorn": "^8.5.0",
|
|
131
133
|
"angular": "^1.7.4",
|
|
132
134
|
"archiver": "^3.0.0",
|
|
133
|
-
"c8": "^7.
|
|
135
|
+
"c8": "^7.11.3",
|
|
134
136
|
"chalk": "^2.4.1",
|
|
135
137
|
"chrome-devtools-frontend": "1.0.1012379",
|
|
136
138
|
"concurrently": "^6.4.0",
|
|
@@ -146,22 +148,25 @@
|
|
|
146
148
|
"eslint-plugin-import": "^2.25.3",
|
|
147
149
|
"eslint-plugin-local-rules": "1.1.0",
|
|
148
150
|
"event-target-shim": "^6.0.2",
|
|
151
|
+
"expect": "^28.1.0",
|
|
149
152
|
"firebase": "^9.0.2",
|
|
150
153
|
"gh-pages": "^2.0.1",
|
|
151
154
|
"glob": "^7.1.3",
|
|
152
155
|
"idb-keyval": "2.2.0",
|
|
153
156
|
"intl-messageformat-parser": "^1.8.1",
|
|
154
|
-
"jest": "27.
|
|
157
|
+
"jest-mock": "^27.3.0",
|
|
158
|
+
"jest-snapshot": "^28.1.0",
|
|
155
159
|
"jsdom": "^12.2.0",
|
|
156
160
|
"jsonld": "^5.2.0",
|
|
157
161
|
"jsonlint-mod": "^1.7.6",
|
|
158
162
|
"lighthouse-plugin-publisher-ads": "^1.5.4",
|
|
159
163
|
"magic-string": "^0.25.7",
|
|
160
164
|
"mime-types": "^2.1.30",
|
|
165
|
+
"mocha": "^10.0.0",
|
|
161
166
|
"node-fetch": "^2.6.1",
|
|
162
167
|
"npm-run-posix-or-windows": "^2.0.2",
|
|
163
168
|
"pako": "^2.0.3",
|
|
164
|
-
"preact": "^10.
|
|
169
|
+
"preact": "^10.7.2",
|
|
165
170
|
"pretty-json-stringify": "^0.0.2",
|
|
166
171
|
"puppeteer": "13.7.0",
|
|
167
172
|
"resolve": "^1.20.0",
|
|
@@ -173,7 +178,7 @@
|
|
|
173
178
|
"rollup-plugin-terser": "^7.0.2",
|
|
174
179
|
"tabulator-tables": "^4.9.3",
|
|
175
180
|
"terser": "^5.3.8",
|
|
176
|
-
"
|
|
181
|
+
"testdouble": "^3.16.5",
|
|
177
182
|
"typed-query-selector": "^2.6.1",
|
|
178
183
|
"typescript": "^4.7.3",
|
|
179
184
|
"wait-for-expect": "^3.0.2",
|
package/report/renderer/util.js
CHANGED
|
@@ -568,15 +568,16 @@ class Util {
|
|
|
568
568
|
*/
|
|
569
569
|
Util.reportJson = null;
|
|
570
570
|
|
|
571
|
+
let svgSuffix = 0;
|
|
571
572
|
/**
|
|
572
573
|
* An always-increasing counter for making unique SVG ID suffixes.
|
|
573
574
|
*/
|
|
574
|
-
Util.getUniqueSuffix = (
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
575
|
+
Util.getUniqueSuffix = () => {
|
|
576
|
+
return svgSuffix++;
|
|
577
|
+
};
|
|
578
|
+
Util.resetUniqueSuffix = () => {
|
|
579
|
+
svgSuffix = 0;
|
|
580
|
+
};
|
|
580
581
|
|
|
581
582
|
/**
|
|
582
583
|
* Report-renderer-specific strings.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
|
|
9
9
|
import jsdom from 'jsdom';
|
|
10
|
-
import
|
|
10
|
+
import jestMock from 'jest-mock';
|
|
11
11
|
|
|
12
12
|
import * as lighthouseRenderer from '../../clients/bundle.js';
|
|
13
13
|
import {LH_ROOT} from '../../../root.js';
|
|
@@ -17,8 +17,8 @@ const sampleResultsStr =
|
|
|
17
17
|
|
|
18
18
|
describe('lighthouseRenderer bundle', () => {
|
|
19
19
|
let document;
|
|
20
|
-
|
|
21
|
-
global.console.warn =
|
|
20
|
+
before(() => {
|
|
21
|
+
global.console.warn = jestMock.fn();
|
|
22
22
|
|
|
23
23
|
const {window} = new jsdom.JSDOM();
|
|
24
24
|
document = window.document;
|
|
@@ -38,7 +38,7 @@ describe('lighthouseRenderer bundle', () => {
|
|
|
38
38
|
};
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
after(() => {
|
|
42
42
|
global.window = global.self = undefined;
|
|
43
43
|
global.HTMLInputElement = undefined;
|
|
44
44
|
});
|
|
@@ -13,7 +13,9 @@ const fs = require('fs');
|
|
|
13
13
|
const csvValidator = require('csv-validator');
|
|
14
14
|
|
|
15
15
|
const ReportGenerator = require('../../generator/report-generator.js');
|
|
16
|
-
const
|
|
16
|
+
const {readJson} = require('../../../root.js');
|
|
17
|
+
|
|
18
|
+
const sampleResults = readJson('lighthouse-core/test/results/sample_v2.json');
|
|
17
19
|
|
|
18
20
|
describe('ReportGenerator', () => {
|
|
19
21
|
describe('#replaceStrings', () => {
|
|
@@ -13,7 +13,7 @@ import {I18n} from '../../renderer/i18n.js';
|
|
|
13
13
|
import {DOM} from '../../renderer/dom.js';
|
|
14
14
|
import {DetailsRenderer} from '../../renderer/details-renderer.js';
|
|
15
15
|
import {CategoryRenderer} from '../../renderer/category-renderer.js';
|
|
16
|
-
import {readJson} from '../../../
|
|
16
|
+
import {readJson} from '../../../lighthouse-core/test/test-utils.js';
|
|
17
17
|
|
|
18
18
|
const sampleResultsOrig = readJson('../../../lighthouse-core/test/results/sample_v2.json', import.meta);
|
|
19
19
|
|
|
@@ -21,7 +21,7 @@ describe('CategoryRenderer', () => {
|
|
|
21
21
|
let renderer;
|
|
22
22
|
let sampleResults;
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
before(() => {
|
|
25
25
|
Util.i18n = new I18n('en', {...Util.UIStrings});
|
|
26
26
|
|
|
27
27
|
const {document} = new jsdom.JSDOM().window;
|
|
@@ -32,7 +32,7 @@ describe('CategoryRenderer', () => {
|
|
|
32
32
|
sampleResults = Util.prepareReportResult(sampleResultsOrig);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
after(() => {
|
|
36
36
|
Util.i18n = undefined;
|
|
37
37
|
});
|
|
38
38
|
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
|
|
9
9
|
import jsdom from 'jsdom';
|
|
10
|
-
import expect from 'expect';
|
|
11
10
|
|
|
12
11
|
import {DOM} from '../../renderer/dom.js';
|
|
13
12
|
import {LH_ROOT} from '../../../root.js';
|
|
@@ -82,41 +81,44 @@ async function assertDOMTreeMatches(tmplEl) {
|
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
describe('Components', () => {
|
|
85
|
+
const originalCreateElement = DOM.prototype.createElement;
|
|
86
|
+
const originalCreateElementNS = DOM.prototype.createElementNS;
|
|
87
|
+
|
|
88
|
+
before(() => {
|
|
89
|
+
/**
|
|
90
|
+
* @param {string} classNames
|
|
91
|
+
*/
|
|
92
|
+
function checkPrefix(classNames) {
|
|
93
|
+
if (!classNames) return;
|
|
94
|
+
|
|
95
|
+
for (const className of classNames.split(' ')) {
|
|
96
|
+
if (!className.startsWith('lh-')) {
|
|
97
|
+
throw new Error(`expected classname to start with lh-, got: ${className}`);
|
|
98
|
+
}
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
DOM.prototype.createElement = function(...args) {
|
|
102
|
-
const classNames = args[1];
|
|
103
|
-
checkPrefix(classNames);
|
|
104
|
-
return originalCreateElement.call(this, ...args);
|
|
105
|
-
};
|
|
106
|
-
DOM.prototype.createElementNS = function(...args) {
|
|
107
|
-
const classNames = args[2];
|
|
108
|
-
checkPrefix(classNames);
|
|
109
|
-
return originalCreateElementNS.call(this, ...args);
|
|
110
|
-
};
|
|
111
|
-
});
|
|
112
101
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
102
|
+
DOM.prototype.createElement = function(...args) {
|
|
103
|
+
const classNames = args[1];
|
|
104
|
+
checkPrefix(classNames);
|
|
105
|
+
return originalCreateElement.call(this, ...args);
|
|
106
|
+
};
|
|
107
|
+
DOM.prototype.createElementNS = function(...args) {
|
|
108
|
+
const classNames = args[2];
|
|
109
|
+
checkPrefix(classNames);
|
|
110
|
+
return originalCreateElementNS.call(this, ...args);
|
|
111
|
+
};
|
|
112
|
+
});
|
|
117
113
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
after(() => {
|
|
115
|
+
DOM.prototype.createElement = originalCreateElement;
|
|
116
|
+
DOM.prototype.createElementNS = originalCreateElementNS;
|
|
121
117
|
});
|
|
122
|
-
|
|
118
|
+
|
|
119
|
+
for (const tmpEl of tmplEls) {
|
|
120
|
+
it(`${tmpEl.id} component matches HTML source`, async () => {
|
|
121
|
+
await assertDOMTreeMatches(tmpEl);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
@@ -72,7 +72,7 @@ describe('DetailsRenderer', () => {
|
|
|
72
72
|
let dom;
|
|
73
73
|
let detailsRenderer;
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
before(() => {
|
|
76
76
|
Util.i18n = new I18n('en', {...Util.UIStrings});
|
|
77
77
|
|
|
78
78
|
const {document} = new jsdom.JSDOM().window;
|
|
@@ -80,7 +80,7 @@ describe('DetailsRenderer', () => {
|
|
|
80
80
|
detailsRenderer = new DetailsRenderer(dom);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
after(() => {
|
|
84
84
|
Util.i18n = undefined;
|
|
85
85
|
});
|
|
86
86
|
|
|
@@ -22,12 +22,12 @@ describe('DetailsRenderer', () => {
|
|
|
22
22
|
renderer = new DetailsRenderer(dom, options);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
before(() => {
|
|
26
26
|
Util.i18n = new I18n('en', {...Util.UIStrings});
|
|
27
27
|
createRenderer();
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
after(() => {
|
|
31
31
|
Util.i18n = undefined;
|
|
32
32
|
});
|
|
33
33
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {strict as assert} from 'assert';
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import jestMock from 'jest-mock';
|
|
10
10
|
import jsdom from 'jsdom';
|
|
11
11
|
|
|
12
12
|
import {DOM} from '../../renderer/dom.js';
|
|
@@ -19,20 +19,20 @@ describe('DOM', () => {
|
|
|
19
19
|
let window;
|
|
20
20
|
let nativeCreateObjectURL;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
before(() => {
|
|
23
23
|
Util.i18n = new I18n('en', {...Util.UIStrings});
|
|
24
24
|
window = new jsdom.JSDOM().window;
|
|
25
25
|
|
|
26
26
|
// The Node version of URL.createObjectURL isn't compatible with the jsdom blob type,
|
|
27
27
|
// so we stub it.
|
|
28
28
|
nativeCreateObjectURL = URL.createObjectURL;
|
|
29
|
-
URL.createObjectURL =
|
|
29
|
+
URL.createObjectURL = jestMock.fn(_ => `https://fake-origin/blahblah-blobid`);
|
|
30
30
|
|
|
31
31
|
dom = new DOM(window.document);
|
|
32
32
|
dom.setLighthouseChannel('someChannel');
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
after(() => {
|
|
36
36
|
Util.i18n = undefined;
|
|
37
37
|
URL.createObjectURL = nativeCreateObjectURL;
|
|
38
38
|
});
|