lighthouse 9.5.0-dev.20220628 → 9.5.0-dev.20220701

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.
Files changed (84) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/dist/report/bundle.esm.js +7 -6
  3. package/dist/report/flow.js +24 -24
  4. package/dist/report/standalone.js +19 -19
  5. package/{eslint-local-rules.js → eslint-local-rules.cjs} +0 -0
  6. package/flow-report/test/common-test.tsx +5 -4
  7. package/flow-report/test/flow-report-pptr-test.ts +4 -7
  8. package/flow-report/test/run-flow-report-tests.sh +20 -0
  9. package/flow-report/test/setup/env-setup.ts +44 -24
  10. package/flow-report/test/sidebar/sidebar-test.tsx +0 -1
  11. package/flow-report/test/topbar-test.tsx +5 -5
  12. package/flow-report/test/util-test.tsx +3 -3
  13. package/flow-report/tsconfig.json +4 -1
  14. package/lighthouse-cli/cli-flags.js +313 -305
  15. package/lighthouse-cli/run.js +1 -1
  16. package/lighthouse-cli/test/smokehouse/report-assert-test.js +1 -1
  17. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +30 -5
  18. package/lighthouse-core/audits/final-screenshot.js +2 -2
  19. package/lighthouse-core/audits/screenshot-thumbnails.js +6 -6
  20. package/lighthouse-core/audits/work-during-interaction.js +3 -3
  21. package/lighthouse-core/computed/metrics/cumulative-layout-shift.js +3 -3
  22. package/lighthouse-core/computed/metrics/first-meaningful-paint.js +2 -2
  23. package/lighthouse-core/computed/metrics/interactive.js +5 -5
  24. package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +3 -3
  25. package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +3 -3
  26. package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +2 -2
  27. package/lighthouse-core/computed/metrics/largest-contentful-paint.js +2 -2
  28. package/lighthouse-core/computed/speedline.js +4 -4
  29. package/lighthouse-core/config/config-plugin.js +26 -0
  30. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +1 -0
  31. package/lighthouse-core/fraggle-rock/gather/session.js +3 -2
  32. package/lighthouse-core/gather/connections/connection.js +2 -2
  33. package/lighthouse-core/gather/driver/wait-for-condition.js +3 -3
  34. package/lighthouse-core/gather/driver.js +3 -2
  35. package/lighthouse-core/gather/gather-runner.js +2 -1
  36. package/lighthouse-core/gather/gatherers/main-document-content.js +0 -2
  37. package/lighthouse-core/lib/asset-saver.js +6 -6
  38. package/lighthouse-core/lib/lh-env.js +1 -1
  39. package/lighthouse-core/lib/lh-error.js +5 -5
  40. package/lighthouse-core/lib/lh-trace-processor.js +6 -6
  41. package/lighthouse-core/lib/navigation-error.js +34 -14
  42. package/lighthouse-core/lib/sentry.js +3 -3
  43. package/lighthouse-core/lib/url-shim.js +2 -2
  44. package/lighthouse-core/runner.js +6 -4
  45. package/lighthouse-core/util-commonjs.js +7 -6
  46. package/package.json +24 -19
  47. package/report/renderer/util.js +7 -6
  48. package/report/test/clients/bundle-test.js +4 -4
  49. package/report/test/generator/report-generator-test.js +3 -1
  50. package/report/test/renderer/category-renderer-test.js +3 -3
  51. package/report/test/renderer/components-test.js +36 -34
  52. package/report/test/renderer/crc-details-renderer-test.js +2 -2
  53. package/report/test/renderer/details-renderer-test.js +2 -2
  54. package/report/test/renderer/dom-test.js +4 -4
  55. package/report/test/renderer/element-screenshot-renderer-test.js +3 -2
  56. package/report/test/renderer/performance-category-renderer-test.js +4 -4
  57. package/report/test/renderer/pwa-category-renderer-test.js +3 -3
  58. package/report/test/renderer/report-renderer-axe-test.js +6 -8
  59. package/report/test/renderer/report-renderer-test.js +5 -5
  60. package/report/test/renderer/report-ui-features-test.js +8 -8
  61. package/report/test/renderer/snippet-renderer-test.js +2 -2
  62. package/report/test/renderer/text-encoding-test.js +2 -2
  63. package/report/test/renderer/util-test.js +1 -1
  64. package/root.js +0 -18
  65. package/shared/localization/format.js +1 -1
  66. package/shared/localization/locales/en-US.json +3 -0
  67. package/shared/localization/locales/en-XL.json +3 -0
  68. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -1
  69. package/third-party/chromium-synchronization/installability-errors-test.js +1 -3
  70. package/third-party/devtools-tests/README.md +2 -2
  71. package/third-party/devtools-tests/e2e/lighthouse/BUILD.gn +17 -0
  72. package/third-party/devtools-tests/e2e/resources/lighthouse/BUILD.gn +9 -0
  73. package/tsconfig.json +1 -1
  74. package/types/global-lh.d.ts +2 -2
  75. package/types/test.d.ts +53 -0
  76. package/flow-report/.eslintrc.cjs +0 -51
  77. package/flow-report/jest.config.js +0 -24
  78. package/flow-report/test/setup/global-setup.ts +0 -11
  79. package/jest.config.js +0 -30
  80. package/lighthouse-cli/.eslintrc.cjs +0 -28
  81. package/report/.eslintrc.cjs +0 -37
  82. package/report/test/.eslintrc.cjs +0 -11
  83. package/shared/test/localization/.eslintrc.cjs +0 -11
  84. package/types/jest.d.ts +0 -25
@@ -5,17 +5,17 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
- const LHError = require('../lib/lh-error.js');
8
+ const LighthouseError = require('../lib/lh-error.js');
9
9
  const TraceProcessor = require('../lib/tracehouse/trace-processor.js');
10
10
 
11
- // TraceProcessor throws generic errors, but we'd like our special localized and code-specific LHError
11
+ // TraceProcessor throws generic errors, but we'd like our special localized and code-specific LighthouseError
12
12
  // objects to be thrown instead.
13
13
  class LHTraceProcessor extends TraceProcessor {
14
14
  /**
15
15
  * @return {Error}
16
16
  */
17
17
  static createNoNavstartError() {
18
- return new LHError(LHError.errors.NO_NAVSTART);
18
+ return new LighthouseError(LighthouseError.errors.NO_NAVSTART);
19
19
  }
20
20
 
21
21
  /**
@@ -25,21 +25,21 @@ class LHTraceProcessor extends TraceProcessor {
25
25
  * @return {Error}
26
26
  */
27
27
  static createNoResourceSendRequestError() {
28
- return new LHError(LHError.errors.NO_RESOURCE_REQUEST);
28
+ return new LighthouseError(LighthouseError.errors.NO_RESOURCE_REQUEST);
29
29
  }
30
30
 
31
31
  /**
32
32
  * @return {Error}
33
33
  */
34
34
  static createNoTracingStartedError() {
35
- return new LHError(LHError.errors.NO_TRACING_STARTED);
35
+ return new LighthouseError(LighthouseError.errors.NO_TRACING_STARTED);
36
36
  }
37
37
 
38
38
  /**
39
39
  * @return {Error}
40
40
  */
41
41
  static createNoFirstContentfulPaintError() {
42
- return new LHError(LHError.errors.NO_FCP);
42
+ return new LighthouseError(LighthouseError.errors.NO_FCP);
43
43
  }
44
44
  }
45
45
 
@@ -5,9 +5,25 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
- const LHError = require('./lh-error.js');
8
+ const LighthouseError = 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.
@@ -16,7 +32,7 @@ const NetworkRequest = require('./network-request.js');
16
32
  */
17
33
  function getNetworkError(mainRecord) {
18
34
  if (!mainRecord) {
19
- return new LHError(LHError.errors.NO_DOCUMENT_REQUEST);
35
+ return new LighthouseError(LighthouseError.errors.NO_DOCUMENT_REQUEST);
20
36
  } else if (mainRecord.failed) {
21
37
  const netErr = mainRecord.localizedFailDescription;
22
38
  // Match all resolution and DNS failures
@@ -26,12 +42,13 @@ function getNetworkError(mainRecord) {
26
42
  netErr === 'net::ERR_NAME_RESOLUTION_FAILED' ||
27
43
  netErr.startsWith('net::ERR_DNS_')
28
44
  ) {
29
- return new LHError(LHError.errors.DNS_FAILURE);
45
+ return new LighthouseError(LighthouseError.errors.DNS_FAILURE);
30
46
  } else {
31
- return new LHError(LHError.errors.FAILED_DOCUMENT_REQUEST, {errorDetails: netErr});
47
+ return new LighthouseError(
48
+ LighthouseError.errors.FAILED_DOCUMENT_REQUEST, {errorDetails: netErr});
32
49
  }
33
50
  } else if (mainRecord.hasErrorStatusCode()) {
34
- return new LHError(LHError.errors.ERRORED_DOCUMENT_REQUEST, {
51
+ return new LighthouseError(LighthouseError.errors.ERRORED_DOCUMENT_REQUEST, {
35
52
  statusCode: `${mainRecord.statusCode}`,
36
53
  });
37
54
  }
@@ -60,13 +77,13 @@ function getInterstitialError(mainRecord, networkRecords) {
60
77
 
61
78
  // If a request failed with the `net::ERR_CERT_*` collection of errors, then it's a security issue.
62
79
  if (mainRecord.localizedFailDescription.startsWith('net::ERR_CERT')) {
63
- return new LHError(LHError.errors.INSECURE_DOCUMENT_REQUEST, {
80
+ return new LighthouseError(LighthouseError.errors.INSECURE_DOCUMENT_REQUEST, {
64
81
  securityMessages: mainRecord.localizedFailDescription,
65
82
  });
66
83
  }
67
84
 
68
85
  // If we made it this far, it's a generic Chrome interstitial error.
69
- return new LHError(LHError.errors.CHROME_INTERSTITIAL_ERROR);
86
+ return new LighthouseError(LighthouseError.errors.CHROME_INTERSTITIAL_ERROR);
70
87
  }
71
88
 
72
89
  /**
@@ -76,16 +93,15 @@ function getInterstitialError(mainRecord, networkRecords) {
76
93
  * @return {LH.LighthouseError|undefined}
77
94
  */
78
95
  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
96
  // If we never requested a document, there's no doctype error, let other cases handle it.
83
97
  if (!finalRecord) return undefined;
84
98
 
85
99
  // mimeType is determined by the browser, we assume Chrome is determining mimeType correctly,
86
100
  // independently of 'Content-Type' response headers, and always sending mimeType if well-formed.
87
- if (HTML_MIME_TYPE !== finalRecord.mimeType) {
88
- return new LHError(LHError.errors.NOT_HTML, {mimeType: finalRecord.mimeType});
101
+ if (finalRecord.mimeType !== HTML_MIME_TYPE && finalRecord.mimeType !== XHTML_MIME_TYPE) {
102
+ return new LighthouseError(LighthouseError.errors.NOT_HTML, {
103
+ mimeType: finalRecord.mimeType,
104
+ });
89
105
  }
90
106
 
91
107
  return undefined;
@@ -95,7 +111,7 @@ function getNonHtmlError(finalRecord) {
95
111
  * Returns an error if the page load should be considered failed, e.g. from a
96
112
  * main document request failure, a security issue, etc.
97
113
  * @param {LH.LighthouseError|undefined} navigationError
98
- * @param {{url: string, loadFailureMode: LH.Gatherer.PassContext['passConfig']['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>}} context
114
+ * @param {{url: string, loadFailureMode: LH.Gatherer.PassContext['passConfig']['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
99
115
  * @return {LH.LighthouseError|undefined}
100
116
  */
101
117
  function getPageLoadError(navigationError, context) {
@@ -120,6 +136,10 @@ function getPageLoadError(navigationError, context) {
120
136
  finalRecord = NetworkAnalyzer.resolveRedirects(mainRecord);
121
137
  }
122
138
 
139
+ if (finalRecord?.mimeType === XHTML_MIME_TYPE) {
140
+ context.warnings.push(str_(UIStrings.warningXhtml));
141
+ }
142
+
123
143
  const networkError = getNetworkError(mainRecord);
124
144
  const interstitialError = getInterstitialError(mainRecord, networkRecords);
125
145
  const nonHtmlError = getNonHtmlError(finalRecord);
@@ -145,10 +165,10 @@ function getPageLoadError(navigationError, context) {
145
165
  return navigationError;
146
166
  }
147
167
 
148
-
149
168
  module.exports = {
150
169
  getNetworkError,
151
170
  getInterstitialError,
152
171
  getPageLoadError,
153
172
  getNonHtmlError,
173
+ UIStrings,
154
174
  };
@@ -108,14 +108,14 @@ function init(opts) {
108
108
  const sampledErrorMatch = SAMPLED_ERRORS.find(sample => sample.pattern.test(err.message));
109
109
  if (sampledErrorMatch && sampledErrorMatch.rate <= Math.random()) return;
110
110
 
111
- // @ts-expect-error - properties added to protocol method LHErrors.
111
+ // @ts-expect-error - properties added to protocol method LighthouseErrors.
112
112
  if (err.protocolMethod) {
113
113
  // Protocol errors all share same stack trace, so add more to fingerprint
114
- // @ts-expect-error - properties added to protocol method LHErrors.
114
+ // @ts-expect-error - properties added to protocol method LighthouseErrors.
115
115
  opts.fingerprint = ['{{ default }}', err.protocolMethod, err.protocolError];
116
116
 
117
117
  opts.tags = opts.tags || {};
118
- // @ts-expect-error - properties added to protocol method LHErrors.
118
+ // @ts-expect-error - properties added to protocol method LighthouseErrors.
119
119
  opts.tags.protocolMethod = err.protocolMethod;
120
120
  }
121
121
 
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  const {Util} = require('../util-commonjs.js');
13
- const LHError = require('../lib/lh-error.js');
13
+ const LighthouseError = require('../lib/lh-error.js');
14
14
 
15
15
  /** @typedef {import('./network-request.js')} NetworkRequest */
16
16
 
@@ -260,7 +260,7 @@ class URLShim extends URL {
260
260
  // Use canonicalized URL (with trailing slashes and such)
261
261
  return new URL(url).href;
262
262
  } else {
263
- throw new LHError(LHError.errors.INVALID_URL);
263
+ throw new LighthouseError(LighthouseError.errors.INVALID_URL);
264
264
  }
265
265
  }
266
266
  }
@@ -18,7 +18,7 @@ const fs = require('fs');
18
18
  const path = require('path');
19
19
  const Sentry = require('./lib/sentry.js');
20
20
  const generateReport = require('../report/generator/report-generator.js').generateReport;
21
- const LHError = require('./lib/lh-error.js');
21
+ const LighthouseError = require('./lib/lh-error.js');
22
22
  const {version: lighthouseVersion} = require('../package.json');
23
23
 
24
24
  /** @typedef {import('./gather/connections/connection.js')} Connection */
@@ -357,7 +357,8 @@ class Runner {
357
357
  if (noArtifact || noRequiredTrace || noRequiredDevtoolsLog) {
358
358
  log.warn('Runner',
359
359
  `${artifactName} gatherer, required by audit ${audit.meta.id}, did not run.`);
360
- throw new LHError(LHError.errors.MISSING_REQUIRED_ARTIFACT, {artifactName});
360
+ throw new LighthouseError(
361
+ LighthouseError.errors.MISSING_REQUIRED_ARTIFACT, {artifactName});
361
362
  }
362
363
 
363
364
  // If artifact was an error, output error result on behalf of audit.
@@ -375,7 +376,7 @@ class Runner {
375
376
  ` encountered an error: ${artifactError.message}`);
376
377
 
377
378
  // Create a friendlier display error and mark it as expected to avoid duplicates in Sentry
378
- const error = new LHError(LHError.errors.ERRORED_REQUIRED_ARTIFACT,
379
+ const error = new LighthouseError(LighthouseError.errors.ERRORED_REQUIRED_ARTIFACT,
379
380
  {artifactName, errorMessage: artifactError.message});
380
381
  // @ts-expect-error Non-standard property added to Error
381
382
  error.expected = true;
@@ -435,7 +436,8 @@ class Runner {
435
436
  ];
436
437
 
437
438
  for (const possibleErrorArtifact of possibleErrorArtifacts) {
438
- if (possibleErrorArtifact instanceof LHError && possibleErrorArtifact.lhrRuntimeError) {
439
+ // eslint-disable-next-line max-len
440
+ if (possibleErrorArtifact instanceof LighthouseError && possibleErrorArtifact.lhrRuntimeError) {
439
441
  const errorMessage = possibleErrorArtifact.friendlyMessage || possibleErrorArtifact.message;
440
442
 
441
443
  return {
@@ -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
- let svgSuffix = 0;
580
- return function() {
581
- return svgSuffix++;
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.20220628",
3
+ "version": "9.5.0-dev.20220701",
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
- "jest": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
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 jest \"$PWD/clients/\" && yarn jest --testMatch=\"**/clients/test/**/*-test-pptr.js\"",
42
- "test-viewer": "yarn unit-viewer && yarn jest --testMatch=\"**/viewer/**/*-test-pptr.js\"",
43
- "test-treemap": "yarn unit-treemap && yarn jest --testMatch=\"**/treemap/**/*-test-pptr.js\"",
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 jest \"lighthouse-core\"",
49
- "unit-cli": "yarn jest \"lighthouse-cli/\"",
50
- "unit-report": "yarn jest \"report/\"",
51
- "unit-treemap": "yarn jest \"treemap/.*-test.js\"",
52
- "unit-viewer": "yarn jest \"viewer/.*-test.js\"",
53
- "unit-flow": "yarn jest \"flow-report/.*-test.[tj]s[x]?\"",
54
- "unit": "yarn jest",
55
- "unit:ci": "NODE_OPTIONS=--max-old-space-size=8192 npm run jest --ci .",
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": "^2.0.1",
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.4.0",
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.1.1",
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.5.14",
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
- "ts-jest": "^27.0.4",
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",
@@ -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
- let svgSuffix = 0;
576
- return function() {
577
- return svgSuffix++;
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 {jest} from '@jest/globals';
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
- beforeAll(() => {
21
- global.console.warn = jest.fn();
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
- afterAll(() => {
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 sampleResults = require('../../../lighthouse-core/test/results/sample_v2.json');
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 '../../../root.js';
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
- beforeAll(() => {
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
- afterAll(() => {
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
- const originalCreateElement = DOM.prototype.createElement;
86
- const originalCreateElementNS = DOM.prototype.createElementNS;
87
- beforeAll(() => {
88
- /**
89
- * @param {string} classNames
90
- */
91
- function checkPrefix(classNames) {
92
- if (!classNames) return;
93
-
94
- for (const className of classNames.split(' ')) {
95
- if (!className.startsWith('lh-')) {
96
- throw new Error(`expected classname to start with lh-, got: ${className}`);
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
- afterAll(() => {
114
- DOM.prototype.createElement = originalCreateElement;
115
- DOM.prototype.createElementNS = originalCreateElementNS;
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
- for (const tmpEl of tmplEls) {
119
- it(`${tmpEl.id} component matches HTML source`, async () => {
120
- await assertDOMTreeMatches(tmpEl);
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
- beforeAll(() => {
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
- afterAll(() => {
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
- beforeAll(() => {
25
+ before(() => {
26
26
  Util.i18n = new I18n('en', {...Util.UIStrings});
27
27
  createRenderer();
28
28
  });
29
29
 
30
- afterAll(() => {
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 {jest} from '@jest/globals';
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
- beforeAll(() => {
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 = jest.fn(_ => `https://fake-origin/blahblah-blobid`);
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
- afterAll(() => {
35
+ after(() => {
36
36
  Util.i18n = undefined;
37
37
  URL.createObjectURL = nativeCreateObjectURL;
38
38
  });
@@ -26,14 +26,15 @@ function makeRect(opts) {
26
26
  describe('ElementScreenshotRenderer', () => {
27
27
  let dom;
28
28
 
29
- beforeAll(() => {
29
+ before(() => {
30
30
  Util.i18n = new I18n('en', {...Util.UIStrings});
31
31
 
32
32
  const {document} = new jsdom.JSDOM().window;
33
33
  dom = new DOM(document);
34
+ Util.resetUniqueSuffix();
34
35
  });
35
36
 
36
- afterAll(() => {
37
+ after(() => {
37
38
  Util.i18n = undefined;
38
39
  });
39
40