lighthouse 9.5.0-dev.20220509 → 9.6.1

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 (259) hide show
  1. package/changelog.md +70 -0
  2. package/dist/report/bundle.esm.js +400 -808
  3. package/dist/report/flow.js +22 -27
  4. package/dist/report/standalone.js +18 -23
  5. package/flow-report/src/summary/category.tsx +1 -1
  6. package/jest.config.js +1 -1
  7. package/lighthouse-cli/.eslintrc.cjs +4 -3
  8. package/lighthouse-cli/bin.js +1 -0
  9. package/lighthouse-cli/cli-flags.js +4 -3
  10. package/lighthouse-cli/commands/commands.js +1 -0
  11. package/lighthouse-cli/commands/list-audits.js +1 -0
  12. package/lighthouse-cli/commands/list-locales.js +1 -0
  13. package/lighthouse-cli/commands/list-trace-categories.js +1 -0
  14. package/lighthouse-cli/index.js +1 -0
  15. package/lighthouse-cli/printer.js +1 -0
  16. package/lighthouse-cli/run.js +24 -9
  17. package/lighthouse-cli/sentry-prompt.js +1 -0
  18. package/lighthouse-cli/test/smokehouse/core-tests.js +1 -0
  19. package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -0
  20. package/lighthouse-cli/test/smokehouse/frontends/lib.js +1 -0
  21. package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -0
  22. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +1 -0
  23. package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -0
  24. package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -0
  25. package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -0
  26. package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +23 -109
  27. package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +3 -2
  28. package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +19 -28
  29. package/lighthouse-cli/test/smokehouse/report-assert.js +4 -3
  30. package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
  31. package/lighthouse-cli/test/smokehouse/version-check-test.js +3 -0
  32. package/lighthouse-cli/test/smokehouse/version-check.js +1 -0
  33. package/lighthouse-core/audits/accessibility/accesskeys.js +1 -1
  34. package/lighthouse-core/audits/accessibility/aria-allowed-attr.js +1 -1
  35. package/lighthouse-core/audits/accessibility/aria-command-name.js +1 -1
  36. package/lighthouse-core/audits/accessibility/aria-hidden-body.js +1 -1
  37. package/lighthouse-core/audits/accessibility/aria-hidden-focus.js +1 -1
  38. package/lighthouse-core/audits/accessibility/aria-input-field-name.js +1 -1
  39. package/lighthouse-core/audits/accessibility/aria-meter-name.js +1 -1
  40. package/lighthouse-core/audits/accessibility/aria-progressbar-name.js +1 -1
  41. package/lighthouse-core/audits/accessibility/aria-required-attr.js +1 -1
  42. package/lighthouse-core/audits/accessibility/aria-required-children.js +1 -1
  43. package/lighthouse-core/audits/accessibility/aria-required-parent.js +1 -1
  44. package/lighthouse-core/audits/accessibility/aria-roles.js +1 -1
  45. package/lighthouse-core/audits/accessibility/aria-toggle-field-name.js +1 -1
  46. package/lighthouse-core/audits/accessibility/aria-tooltip-name.js +1 -1
  47. package/lighthouse-core/audits/accessibility/aria-treeitem-name.js +1 -1
  48. package/lighthouse-core/audits/accessibility/aria-valid-attr-value.js +1 -1
  49. package/lighthouse-core/audits/accessibility/aria-valid-attr.js +1 -1
  50. package/lighthouse-core/audits/accessibility/button-name.js +1 -1
  51. package/lighthouse-core/audits/accessibility/bypass.js +1 -1
  52. package/lighthouse-core/audits/accessibility/color-contrast.js +1 -1
  53. package/lighthouse-core/audits/accessibility/definition-list.js +1 -1
  54. package/lighthouse-core/audits/accessibility/dlitem.js +1 -1
  55. package/lighthouse-core/audits/accessibility/document-title.js +1 -1
  56. package/lighthouse-core/audits/accessibility/duplicate-id-active.js +1 -1
  57. package/lighthouse-core/audits/accessibility/duplicate-id-aria.js +1 -1
  58. package/lighthouse-core/audits/accessibility/form-field-multiple-labels.js +1 -1
  59. package/lighthouse-core/audits/accessibility/frame-title.js +1 -1
  60. package/lighthouse-core/audits/accessibility/heading-order.js +1 -1
  61. package/lighthouse-core/audits/accessibility/html-has-lang.js +1 -1
  62. package/lighthouse-core/audits/accessibility/html-lang-valid.js +1 -1
  63. package/lighthouse-core/audits/accessibility/image-alt.js +1 -1
  64. package/lighthouse-core/audits/accessibility/input-image-alt.js +1 -1
  65. package/lighthouse-core/audits/accessibility/label.js +1 -1
  66. package/lighthouse-core/audits/accessibility/link-name.js +1 -1
  67. package/lighthouse-core/audits/accessibility/list.js +1 -1
  68. package/lighthouse-core/audits/accessibility/listitem.js +5 -5
  69. package/lighthouse-core/audits/accessibility/meta-refresh.js +1 -1
  70. package/lighthouse-core/audits/accessibility/meta-viewport.js +1 -1
  71. package/lighthouse-core/audits/accessibility/object-alt.js +1 -1
  72. package/lighthouse-core/audits/accessibility/tabindex.js +1 -1
  73. package/lighthouse-core/audits/accessibility/td-headers-attr.js +1 -1
  74. package/lighthouse-core/audits/accessibility/th-has-data-cells.js +1 -1
  75. package/lighthouse-core/audits/accessibility/valid-lang.js +1 -1
  76. package/lighthouse-core/audits/accessibility/video-caption.js +1 -1
  77. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +1 -2
  78. package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +12 -11
  79. package/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +1 -1
  80. package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +24 -22
  81. package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +2 -3
  82. package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +3 -2
  83. package/lighthouse-core/audits/byte-efficiency/unminified-css.js +5 -3
  84. package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +11 -10
  85. package/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +1 -0
  86. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +8 -11
  87. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +1 -1
  88. package/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +1 -1
  89. package/lighthouse-core/audits/deprecations.js +598 -8
  90. package/lighthouse-core/audits/diagnostics.js +3 -4
  91. package/lighthouse-core/audits/dobetterweb/doctype.js +9 -20
  92. package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +1 -1
  93. package/lighthouse-core/audits/dobetterweb/no-document-write.js +1 -1
  94. package/lighthouse-core/audits/dobetterweb/notification-on-start.js +1 -1
  95. package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -2
  96. package/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js +1 -1
  97. package/lighthouse-core/audits/errors-in-console.js +2 -2
  98. package/lighthouse-core/audits/long-tasks.js +2 -3
  99. package/lighthouse-core/audits/metrics/experimental-interaction-to-next-paint.js +3 -1
  100. package/lighthouse-core/audits/metrics/first-contentful-paint-3g.js +2 -2
  101. package/lighthouse-core/audits/metrics/first-contentful-paint.js +2 -3
  102. package/lighthouse-core/audits/metrics/first-meaningful-paint.js +2 -3
  103. package/lighthouse-core/audits/metrics/interactive.js +2 -3
  104. package/lighthouse-core/audits/metrics/largest-contentful-paint.js +2 -3
  105. package/lighthouse-core/audits/metrics/max-potential-fid.js +2 -3
  106. package/lighthouse-core/audits/metrics/speed-index.js +2 -3
  107. package/lighthouse-core/audits/metrics/total-blocking-time.js +2 -3
  108. package/lighthouse-core/audits/metrics.js +2 -3
  109. package/lighthouse-core/audits/no-unload-listeners.js +15 -7
  110. package/lighthouse-core/audits/predictive-perf.js +1 -2
  111. package/lighthouse-core/audits/preload-lcp-image.js +3 -3
  112. package/lighthouse-core/audits/redirects.js +1 -1
  113. package/lighthouse-core/audits/script-treemap-data.js +45 -45
  114. package/lighthouse-core/audits/seo/http-status-code.js +4 -2
  115. package/lighthouse-core/audits/seo/link-text.js +0 -2
  116. package/lighthouse-core/audits/server-response-time.js +17 -2
  117. package/lighthouse-core/audits/service-worker.js +2 -7
  118. package/lighthouse-core/audits/timing-budget.js +2 -3
  119. package/lighthouse-core/audits/uses-rel-preconnect.js +1 -1
  120. package/lighthouse-core/audits/uses-rel-preload.js +1 -1
  121. package/lighthouse-core/audits/valid-source-maps.js +14 -12
  122. package/lighthouse-core/audits/violation-audit.js +1 -1
  123. package/lighthouse-core/audits/work-during-interaction.js +280 -0
  124. package/lighthouse-core/computed/critical-request-chains.js +9 -5
  125. package/lighthouse-core/computed/js-bundles.js +11 -11
  126. package/lighthouse-core/computed/main-resource.js +2 -3
  127. package/lighthouse-core/computed/metrics/first-contentful-paint-all-frames.js +1 -1
  128. package/lighthouse-core/computed/metrics/first-contentful-paint.js +1 -1
  129. package/lighthouse-core/computed/metrics/first-meaningful-paint.js +1 -1
  130. package/lighthouse-core/computed/metrics/interactive.js +1 -1
  131. package/lighthouse-core/computed/metrics/lantern-first-contentful-paint.js +1 -1
  132. package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +1 -1
  133. package/lighthouse-core/computed/metrics/lantern-interactive.js +1 -1
  134. package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
  135. package/lighthouse-core/computed/metrics/lantern-max-potential-fid.js +1 -1
  136. package/lighthouse-core/computed/metrics/lantern-metric.js +5 -3
  137. package/lighthouse-core/computed/metrics/lantern-speed-index.js +1 -1
  138. package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +1 -1
  139. package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +1 -1
  140. package/lighthouse-core/computed/metrics/largest-contentful-paint.js +1 -1
  141. package/lighthouse-core/computed/metrics/max-potential-fid.js +1 -1
  142. package/lighthouse-core/computed/metrics/metric.js +0 -1
  143. package/lighthouse-core/computed/metrics/responsiveness.js +12 -9
  144. package/lighthouse-core/computed/metrics/speed-index.js +1 -1
  145. package/lighthouse-core/computed/metrics/timing-summary.js +6 -13
  146. package/lighthouse-core/computed/metrics/total-blocking-time.js +1 -1
  147. package/lighthouse-core/computed/module-duplication.js +5 -6
  148. package/lighthouse-core/computed/page-dependency-graph.js +28 -55
  149. package/lighthouse-core/computed/resource-summary.js +5 -5
  150. package/lighthouse-core/computed/unused-css.js +7 -6
  151. package/lighthouse-core/computed/unused-javascript-summary.js +36 -16
  152. package/lighthouse-core/config/budget.js +2 -2
  153. package/lighthouse-core/config/default-config.js +1 -2
  154. package/lighthouse-core/config/metrics-to-audits.js +5 -0
  155. package/lighthouse-core/fraggle-rock/api.js +1 -1
  156. package/lighthouse-core/fraggle-rock/config/default-config.js +8 -5
  157. package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +3 -6
  158. package/lighthouse-core/fraggle-rock/gather/driver.js +2 -2
  159. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +26 -51
  160. package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
  161. package/lighthouse-core/fraggle-rock/gather/session.js +2 -2
  162. package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -5
  163. package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +6 -6
  164. package/lighthouse-core/gather/connections/cri.js +0 -1
  165. package/lighthouse-core/gather/driver/execution-context.js +2 -4
  166. package/lighthouse-core/gather/driver/navigation.js +9 -9
  167. package/lighthouse-core/gather/driver/network-monitor.js +3 -16
  168. package/lighthouse-core/gather/driver.js +1 -6
  169. package/lighthouse-core/gather/fetcher.js +210 -34
  170. package/lighthouse-core/gather/gather-runner.js +17 -13
  171. package/lighthouse-core/gather/gatherers/accessibility.js +4 -16
  172. package/lighthouse-core/gather/gatherers/console-messages.js +2 -4
  173. package/lighthouse-core/gather/gatherers/dobetterweb/doctype.js +2 -4
  174. package/lighthouse-core/gather/gatherers/full-page-screenshot.js +2 -3
  175. package/lighthouse-core/gather/gatherers/js-usage.js +87 -12
  176. package/lighthouse-core/gather/gatherers/link-elements.js +14 -12
  177. package/lighthouse-core/gather/gatherers/main-document-content.js +8 -8
  178. package/lighthouse-core/gather/gatherers/script-elements.js +56 -7
  179. package/lighthouse-core/gather/gatherers/seo/robots-txt.js +30 -2
  180. package/lighthouse-core/gather/gatherers/source-maps.js +1 -3
  181. package/lighthouse-core/gather/gatherers/trace-elements.js +21 -2
  182. package/lighthouse-core/gather/gatherers/web-app-manifest.js +2 -2
  183. package/lighthouse-core/index.js +2 -26
  184. package/lighthouse-core/lib/cdt/generated/SourceMap.js +1 -1
  185. package/lighthouse-core/lib/dependency-graph/simulator/network-analyzer.js +29 -8
  186. package/lighthouse-core/lib/navigation-error.js +4 -13
  187. package/lighthouse-core/lib/script-helpers.js +6 -11
  188. package/lighthouse-core/lib/url-shim.js +2 -2
  189. package/lighthouse-core/runner.js +5 -0
  190. package/lighthouse-core/scripts/manual-chrome-launcher.js +1 -0
  191. package/lighthouse-core/util-commonjs.js +24 -28
  192. package/package.json +12 -12
  193. package/readme.md +1 -1
  194. package/report/.eslintrc.cjs +1 -3
  195. package/report/assets/styles.css +10 -12
  196. package/report/assets/templates.html +9 -10
  197. package/report/clients/bundle.js +1 -0
  198. package/report/clients/standalone.js +1 -0
  199. package/report/generator/report-generator.js +21 -46
  200. package/report/renderer/api.js +1 -0
  201. package/report/renderer/category-renderer.js +1 -0
  202. package/report/renderer/components.js +304 -303
  203. package/report/renderer/crc-details-renderer.js +1 -0
  204. package/report/renderer/details-renderer.js +10 -10
  205. package/report/renderer/dom.js +1 -0
  206. package/report/renderer/drop-down-menu.js +1 -0
  207. package/report/renderer/element-screenshot-renderer.js +4 -0
  208. package/report/renderer/features-util.js +1 -0
  209. package/report/renderer/i18n.js +62 -118
  210. package/report/renderer/logger.js +1 -0
  211. package/report/renderer/open-tab.js +3 -1
  212. package/report/renderer/performance-category-renderer.js +1 -0
  213. package/report/renderer/pwa-category-renderer.js +1 -0
  214. package/report/renderer/report-renderer.js +1 -0
  215. package/report/renderer/report-ui-features.js +1 -0
  216. package/report/renderer/snippet-renderer.js +1 -0
  217. package/report/renderer/swap-locale-feature.js +1 -0
  218. package/report/renderer/text-encoding.js +1 -0
  219. package/report/renderer/topbar-features.js +1 -0
  220. package/report/renderer/util.js +24 -27
  221. package/report/test/clients/bundle-test.js +4 -0
  222. package/report/test/generator/file-namer-test.js +1 -0
  223. package/report/test/generator/report-generator-test.js +20 -25
  224. package/report/test/renderer/category-renderer-test.js +3 -0
  225. package/report/test/renderer/components-test.js +3 -0
  226. package/report/test/renderer/crc-details-renderer-test.js +3 -0
  227. package/report/test/renderer/details-renderer-test.js +12 -58
  228. package/report/test/renderer/dom-test.js +3 -0
  229. package/report/test/renderer/element-screenshot-renderer-test.js +6 -3
  230. package/report/test/renderer/i18n-test.js +25 -70
  231. package/report/test/renderer/performance-category-renderer-test.js +4 -11
  232. package/report/test/renderer/pwa-category-renderer-test.js +3 -0
  233. package/report/test/renderer/report-renderer-axe-test.js +3 -0
  234. package/report/test/renderer/report-renderer-test.js +3 -0
  235. package/report/test/renderer/report-ui-features-test.js +3 -0
  236. package/report/test/renderer/snippet-renderer-test.js +3 -0
  237. package/report/test/renderer/text-encoding-test.js +3 -0
  238. package/report/test/renderer/util-test.js +5 -2
  239. package/report/test-assets/faux-psi.js +1 -2
  240. package/shared/localization/locales/en-US.json +260 -53
  241. package/shared/localization/locales/en-XL.json +260 -53
  242. package/shared/test/localization/format-test.js +2 -0
  243. package/shared/test/localization/locales-test.js +2 -0
  244. package/shared/test/localization/swap-locale-test.js +2 -0
  245. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +2 -0
  246. package/third-party/chromium-synchronization/installability-errors-test.js +2 -0
  247. package/tsconfig.json +0 -1
  248. package/types/artifacts.d.ts +9 -48
  249. package/types/config.d.ts +0 -2
  250. package/types/externs.d.ts +2 -2
  251. package/types/gatherer.d.ts +2 -1
  252. package/types/global-lh.d.ts +0 -3
  253. package/types/lhr/audit-details.d.ts +3 -1
  254. package/types/lhr/lhr.d.ts +2 -2
  255. package/types/lhr/treemap.d.ts +1 -0
  256. package/lighthouse-core/audits/script-elements-test-audit.js +0 -30
  257. package/lighthouse-core/gather/gatherers/scripts.js +0 -154
  258. package/report/test/.eslintrc.cjs +0 -11
  259. package/shared/test/localization/.eslintrc.cjs +0 -11
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /**
19
20
  * @fileoverview This file contains helpers for constructing and rendering the
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /** @typedef {import('./dom.js').DOM} DOM */
19
20
 
@@ -26,7 +27,6 @@
26
27
 
27
28
  import {Util} from './util.js';
28
29
  import {CriticalRequestChainRenderer} from './crc-details-renderer.js';
29
- import {SnippetRenderer} from './snippet-renderer.js';
30
30
  import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
31
31
 
32
32
  const URL_PREFIXES = ['http://', 'https://', 'data:'];
@@ -78,8 +78,9 @@ export class DetailsRenderer {
78
78
  * @return {Element}
79
79
  */
80
80
  _renderBytes(details) {
81
- // TODO: handle displayUnit once we have something other than 'KiB'
82
- const value = Util.i18n.formatBytesToKiB(details.value, details.granularity || 0.1);
81
+ // TODO: handle displayUnit once we have something other than 'kb'
82
+ // Note that 'kb' is historical and actually represents KiB.
83
+ const value = Util.i18n.formatBytesToKiB(details.value, details.granularity);
83
84
  const textEl = this._renderText(value);
84
85
  textEl.title = Util.i18n.formatBytes(details.value);
85
86
  return textEl;
@@ -90,11 +91,9 @@ export class DetailsRenderer {
90
91
  * @return {Element}
91
92
  */
92
93
  _renderMilliseconds(details) {
93
- let value;
94
+ let value = Util.i18n.formatMilliseconds(details.value, details.granularity);
94
95
  if (details.displayUnit === 'duration') {
95
96
  value = Util.i18n.formatDuration(details.value);
96
- } else {
97
- value = Util.i18n.formatMilliseconds(details.value, details.granularity || 10);
98
97
  }
99
98
 
100
99
  return this._renderText(value);
@@ -173,7 +172,7 @@ export class DetailsRenderer {
173
172
  * @return {Element}
174
173
  */
175
174
  _renderNumeric(details) {
176
- const value = Util.i18n.formatNumber(details.value, details.granularity || 0.1);
175
+ const value = Util.i18n.formatNumber(details.value, details.granularity);
177
176
  const element = this._dom.createElement('div', 'lh-numeric');
178
177
  element.textContent = value;
179
178
  return element;
@@ -472,15 +471,16 @@ export class DetailsRenderer {
472
471
  }
473
472
 
474
473
  /**
475
- * @param {LH.Audit.Details.List} details
474
+ * @param {LH.FormattedIcu<LH.Audit.Details.List>} details
476
475
  * @return {Element}
477
476
  */
478
477
  _renderList(details) {
479
478
  const listContainer = this._dom.createElement('div', 'lh-list');
480
479
 
481
480
  details.items.forEach(item => {
482
- const snippetEl = SnippetRenderer.render(this._dom, item, this);
483
- listContainer.appendChild(snippetEl);
481
+ const listItem = this.render(item);
482
+ if (!listItem) return;
483
+ listContainer.append(listItem);
484
484
  });
485
485
 
486
486
  return listContainer;
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /* eslint-env browser */
19
20
 
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  /* eslint-env browser */
8
9
 
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  /**
8
9
  * @fileoverview These functions define {Rect}s and {Size}s using two different coordinate spaces:
@@ -252,6 +253,9 @@ export class ElementScreenshotRenderer {
252
253
  {width: screenshot.width, height: screenshot.height}
253
254
  );
254
255
 
256
+ const contentEl = dom.find('div.lh-element-screenshot__content', containerEl);
257
+ contentEl.style.top = `-${elementPreviewSizeDC.height}px`;
258
+
255
259
  const imageEl = dom.find('div.lh-element-screenshot__image', containerEl);
256
260
  imageEl.style.width = elementPreviewSizeDC.width + 'px';
257
261
  imageEl.style.height = elementPreviewSizeDC.height + 'px';
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  /* eslint-env browser */
8
9
 
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  // Not named `NBSP` because that creates a duplicate identifier (util.js).
8
9
  const NBSP2 = '\xa0';
@@ -21,7 +22,9 @@ export class I18n {
21
22
  // When testing, use a locale with more exciting numeric formatting.
22
23
  if (locale === 'en-XA') locale = 'de';
23
24
 
24
- this._locale = locale;
25
+ this._numberDateLocale = locale;
26
+ this._numberFormatter = new Intl.NumberFormat(locale);
27
+ this._percentFormatter = new Intl.NumberFormat(locale, {style: 'percent'});
25
28
  this._strings = strings;
26
29
  }
27
30
 
@@ -29,58 +32,15 @@ export class I18n {
29
32
  return this._strings;
30
33
  }
31
34
 
32
- /**
33
- * @param {number} number
34
- * @param {number|undefined} granularity
35
- * @param {Intl.NumberFormatOptions=} opts
36
- * @return {string}
37
- */
38
- _formatNumberWithGranularity(number, granularity, opts = {}) {
39
- if (granularity !== undefined) {
40
- const log10 = -Math.log10(granularity);
41
- if (!Number.isInteger(log10)) {
42
- console.warn(`granularity of ${granularity} is invalid. Using 1 instead`);
43
- granularity = 1;
44
- }
45
-
46
- if (granularity < 1) {
47
- opts = {...opts};
48
- opts.minimumFractionDigits = opts.maximumFractionDigits = Math.ceil(log10);
49
- }
50
-
51
- number = Math.round(number / granularity) * granularity;
52
-
53
- // Avoid displaying a negative value that rounds to zero as "0".
54
- if (Object.is(number, -0)) number = 0;
55
- } else if (Math.abs(number) < 0.0005) {
56
- // Also avoids "-0".
57
- number = 0;
58
- }
59
-
60
- return new Intl.NumberFormat(this._locale, opts).format(number).replace(' ', NBSP2);
61
- }
62
-
63
35
  /**
64
36
  * Format number.
65
37
  * @param {number} number
66
- * @param {number=} granularity Controls how coarse the displayed value is.
67
- * If undefined, the number will be displayed as described
68
- * by the Intl defaults: tinyurl.com/7s67w5x7
69
- * @return {string}
70
- */
71
- formatNumber(number, granularity) {
72
- return this._formatNumberWithGranularity(number, granularity);
73
- }
74
-
75
- /**
76
- * Format integer.
77
- * Just like {@link formatNumber} but uses a granularity of 1, rounding to the nearest
78
- * whole number.
79
- * @param {number} number
38
+ * @param {number=} granularity Number of decimal places to include. Defaults to 0.1.
80
39
  * @return {string}
81
40
  */
82
- formatInteger(number) {
83
- return this._formatNumberWithGranularity(number, 1);
41
+ formatNumber(number, granularity = 0.1) {
42
+ const coarseValue = Math.round(number / granularity) * granularity;
43
+ return this._numberFormatter.format(coarseValue);
84
44
  }
85
45
 
86
46
  /**
@@ -89,99 +49,92 @@ export class I18n {
89
49
  * @return {string}
90
50
  */
91
51
  formatPercent(number) {
92
- return new Intl.NumberFormat(this._locale, {style: 'percent'}).format(number);
52
+ return this._percentFormatter.format(number);
93
53
  }
94
54
 
95
55
  /**
96
56
  * @param {number} size
97
- * @param {number=} granularity Controls how coarse the displayed value is.
98
- * If undefined, the number will be displayed in full.
57
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
99
58
  * @return {string}
100
59
  */
101
- formatBytesToKiB(size, granularity = undefined) {
102
- return this._formatNumberWithGranularity(size / KiB, granularity) + `${NBSP2}KiB`;
60
+ formatBytesToKiB(size, granularity = 0.1) {
61
+ const formatter = this._byteFormatterForGranularity(granularity);
62
+ const kbs = formatter.format(Math.round(size / 1024 / granularity) * granularity);
63
+ return `${kbs}${NBSP2}KiB`;
103
64
  }
104
65
 
105
66
  /**
106
67
  * @param {number} size
107
- * @param {number=} granularity Controls how coarse the displayed value is.
108
- * If undefined, the number will be displayed in full.
68
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
109
69
  * @return {string}
110
70
  */
111
- formatBytesToMiB(size, granularity = undefined) {
112
- return this._formatNumberWithGranularity(size / MiB, granularity) + `${NBSP2}MiB`;
71
+ formatBytesToMiB(size, granularity = 0.1) {
72
+ const formatter = this._byteFormatterForGranularity(granularity);
73
+ const kbs = formatter.format(Math.round(size / (1024 ** 2) / granularity) * granularity);
74
+ return `${kbs}${NBSP2}MiB`;
113
75
  }
114
76
 
115
77
  /**
116
78
  * @param {number} size
117
- * @param {number=} granularity Controls how coarse the displayed value is.
118
- * If undefined, the number will be displayed in full.
79
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 1
119
80
  * @return {string}
120
81
  */
121
82
  formatBytes(size, granularity = 1) {
122
- return this._formatNumberWithGranularity(size, granularity, {
123
- style: 'unit',
124
- unit: 'byte',
125
- unitDisplay: 'long',
126
- });
83
+ const formatter = this._byteFormatterForGranularity(granularity);
84
+ const kbs = formatter.format(Math.round(size / granularity) * granularity);
85
+ return `${kbs}${NBSP2}bytes`;
127
86
  }
128
87
 
129
88
  /**
130
89
  * @param {number} size
131
- * @param {number=} granularity Controls how coarse the displayed value is.
132
- * If undefined, the number will be displayed in full.
90
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
133
91
  * @return {string}
134
92
  */
135
- formatBytesWithBestUnit(size, granularity = undefined) {
93
+ formatBytesWithBestUnit(size, granularity = 0.1) {
136
94
  if (size >= MiB) return this.formatBytesToMiB(size, granularity);
137
95
  if (size >= KiB) return this.formatBytesToKiB(size, granularity);
138
- return this._formatNumberWithGranularity(size, granularity, {
139
- style: 'unit',
140
- unit: 'byte',
141
- unitDisplay: 'narrow',
142
- });
96
+ return this.formatNumber(size, granularity) + '\xa0B';
143
97
  }
144
98
 
145
99
  /**
146
- * @param {number} size
147
- * @param {number=} granularity Controls how coarse the displayed value is.
148
- * If undefined, the number will be displayed in full.
149
- * @return {string}
100
+ * Format bytes with a constant number of fractional digits, i.e. for a granularity of 0.1, 10 becomes '10.0'
101
+ * @param {number} granularity Controls how coarse the displayed value is
102
+ * @return {Intl.NumberFormat}
150
103
  */
151
- formatKbps(size, granularity = undefined) {
152
- return this._formatNumberWithGranularity(size, granularity, {
153
- style: 'unit',
154
- unit: 'kilobit-per-second',
155
- unitDisplay: 'short',
104
+ _byteFormatterForGranularity(granularity) {
105
+ // assume any granularity above 1 will not contain fractional parts, i.e. will never be 1.5
106
+ let numberOfFractionDigits = 0;
107
+ if (granularity < 1) {
108
+ numberOfFractionDigits = -Math.floor(Math.log10(granularity));
109
+ }
110
+
111
+ return new Intl.NumberFormat(this._numberDateLocale, {
112
+ ...this._numberFormatter.resolvedOptions(),
113
+ maximumFractionDigits: numberOfFractionDigits,
114
+ minimumFractionDigits: numberOfFractionDigits,
156
115
  });
157
116
  }
158
117
 
159
118
  /**
160
119
  * @param {number} ms
161
- * @param {number=} granularity Controls how coarse the displayed value is.
162
- * If undefined, the number will be displayed in full.
120
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 10
163
121
  * @return {string}
164
122
  */
165
- formatMilliseconds(ms, granularity = undefined) {
166
- return this._formatNumberWithGranularity(ms, granularity, {
167
- style: 'unit',
168
- unit: 'millisecond',
169
- unitDisplay: 'short',
170
- });
123
+ formatMilliseconds(ms, granularity = 10) {
124
+ const coarseTime = Math.round(ms / granularity) * granularity;
125
+ return coarseTime === 0
126
+ ? `${this._numberFormatter.format(0)}${NBSP2}ms`
127
+ : `${this._numberFormatter.format(coarseTime)}${NBSP2}ms`;
171
128
  }
172
129
 
173
130
  /**
174
131
  * @param {number} ms
175
- * @param {number=} granularity Controls how coarse the displayed value is.
176
- * If undefined, the number will be displayed in full.
132
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
177
133
  * @return {string}
178
134
  */
179
- formatSeconds(ms, granularity = undefined) {
180
- return this._formatNumberWithGranularity(ms / 1000, granularity, {
181
- style: 'unit',
182
- unit: 'second',
183
- unitDisplay: 'short',
184
- });
135
+ formatSeconds(ms, granularity = 0.1) {
136
+ const coarseTime = Math.round(ms / 1000 / granularity) * granularity;
137
+ return `${this._numberFormatter.format(coarseTime)}${NBSP2}s`;
185
138
  }
186
139
 
187
140
  /**
@@ -201,10 +154,10 @@ export class I18n {
201
154
  // and https://github.com/GoogleChrome/lighthouse/pull/9822
202
155
  let formatter;
203
156
  try {
204
- formatter = new Intl.DateTimeFormat(this._locale, options);
157
+ formatter = new Intl.DateTimeFormat(this._numberDateLocale, options);
205
158
  } catch (err) {
206
159
  options.timeZone = 'UTC';
207
- formatter = new Intl.DateTimeFormat(this._locale, options);
160
+ formatter = new Intl.DateTimeFormat(this._numberDateLocale, options);
208
161
  }
209
162
 
210
163
  return formatter.format(new Date(date));
@@ -216,10 +169,6 @@ export class I18n {
216
169
  * @return {string}
217
170
  */
218
171
  formatDuration(timeInMilliseconds) {
219
- // There is a proposal for a Intl.DurationFormat.
220
- // https://github.com/tc39/proposal-intl-duration-format
221
- // Until then, we do things a bit more manually.
222
-
223
172
  let timeInSeconds = timeInMilliseconds / 1000;
224
173
  if (Math.round(timeInSeconds) === 0) {
225
174
  return 'None';
@@ -228,24 +177,19 @@ export class I18n {
228
177
  /** @type {Array<string>} */
229
178
  const parts = [];
230
179
  /** @type {Record<string, number>} */
231
- const unitToSecondsPer = {
232
- day: 60 * 60 * 24,
233
- hour: 60 * 60,
234
- minute: 60,
235
- second: 1,
180
+ const unitLabels = {
181
+ d: 60 * 60 * 24,
182
+ h: 60 * 60,
183
+ m: 60,
184
+ s: 1,
236
185
  };
237
186
 
238
- Object.keys(unitToSecondsPer).forEach(unit => {
239
- const secondsPerUnit = unitToSecondsPer[unit];
240
- const numberOfUnits = Math.floor(timeInSeconds / secondsPerUnit);
187
+ Object.keys(unitLabels).forEach(label => {
188
+ const unit = unitLabels[label];
189
+ const numberOfUnits = Math.floor(timeInSeconds / unit);
241
190
  if (numberOfUnits > 0) {
242
- timeInSeconds -= numberOfUnits * secondsPerUnit;
243
- const part = this._formatNumberWithGranularity(numberOfUnits, 1, {
244
- style: 'unit',
245
- unit,
246
- unitDisplay: 'narrow',
247
- });
248
- parts.push(part);
191
+ timeInSeconds -= numberOfUnits * unit;
192
+ parts.push(`${numberOfUnits}\xa0${label}`);
249
193
  }
250
194
  });
251
195
 
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /**
19
20
  * Logs messages via a UI butter.
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  import {TextEncoding} from './text-encoding.js';
19
20
 
@@ -37,7 +38,7 @@ function computeWindowNameSuffix(json) {
37
38
  // @ts-expect-error - If this is a v2 LHR, use old `generatedTime`.
38
39
  const fallbackFetchTime = /** @type {string} */ (json.generatedTime);
39
40
  const fetchTime = json.fetchTime || fallbackFetchTime;
40
- return `${json.lighthouseVersion}-${json.finalUrl}-${fetchTime}`;
41
+ return `${json.lighthouseVersion}-${json.requestedUrl}-${fetchTime}`;
41
42
  }
42
43
 
43
44
  /**
@@ -119,6 +120,7 @@ function openTreemap(json) {
119
120
  /** @type {LH.Treemap.Options} */
120
121
  const treemapOptions = {
121
122
  lhr: {
123
+ requestedUrl: json.requestedUrl,
122
124
  finalUrl: json.finalUrl,
123
125
  audits: {
124
126
  'script-treemap-data': json.audits['script-treemap-data'],
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /** @typedef {import('./dom.js').DOM} DOM */
19
20
 
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  import {Util} from './util.js';
19
20
  import {CategoryRenderer} from './category-renderer.js';
@@ -17,6 +17,7 @@
17
17
  * Dummy text for ensuring report robustness: </script> pre$`post %%LIGHTHOUSE_JSON%%
18
18
  * (this is handled by terser)
19
19
  */
20
+ 'use strict';
20
21
 
21
22
  /** @typedef {import('./dom.js').DOM} DOM */
22
23
 
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /**
19
20
  * @fileoverview Adds tools button, print, and other dynamic functionality to
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  /** @typedef {import('./details-renderer').DetailsRenderer} DetailsRenderer */
8
9
  /** @typedef {import('./dom').DOM} DOM */
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  /**
8
9
  * @fileoverview Creates a <select> element, filled with all supported locales
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  /* global CompressionStream */
8
9
 
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
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
+ 'use strict';
6
7
 
7
8
  /* eslint-env browser */
8
9
 
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /** @template T @typedef {import('./i18n').I18n<T>} I18n */
19
20
 
@@ -314,26 +315,24 @@ class Util {
314
315
  }
315
316
 
316
317
  const MAX_LENGTH = 64;
317
- if (parsedUrl.protocol !== 'data:') {
318
- // Always elide hexadecimal hash
319
- name = name.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g, `$1${ELLIPSIS}`);
320
- // Also elide other hash-like mixed-case strings
321
- name = name.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,
322
- `$1${ELLIPSIS}`);
323
- // Also elide long number sequences
324
- name = name.replace(/(\d{3})\d{6,}/g, `$1${ELLIPSIS}`);
325
- // Merge any adjacent ellipses
326
- name = name.replace(/\u2026+/g, ELLIPSIS);
327
-
328
- // Elide query params first
329
- if (name.length > MAX_LENGTH && name.includes('?')) {
330
- // Try to leave the first query parameter intact
331
- name = name.replace(/\?([^=]*)(=)?.*/, `?$1$2${ELLIPSIS}`);
332
-
333
- // Remove it all if it's still too long
334
- if (name.length > MAX_LENGTH) {
335
- name = name.replace(/\?.*/, `?${ELLIPSIS}`);
336
- }
318
+ // Always elide hexadecimal hash
319
+ name = name.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g, `$1${ELLIPSIS}`);
320
+ // Also elide other hash-like mixed-case strings
321
+ name = name.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,
322
+ `$1${ELLIPSIS}`);
323
+ // Also elide long number sequences
324
+ name = name.replace(/(\d{3})\d{6,}/g, `$1${ELLIPSIS}`);
325
+ // Merge any adjacent ellipses
326
+ name = name.replace(/\u2026+/g, ELLIPSIS);
327
+
328
+ // Elide query params first
329
+ if (name.length > MAX_LENGTH && name.includes('?')) {
330
+ // Try to leave the first query parameter intact
331
+ name = name.replace(/\?([^=]*)(=)?.*/, `?$1$2${ELLIPSIS}`);
332
+
333
+ // Remove it all if it's still too long
334
+ if (name.length > MAX_LENGTH) {
335
+ name = name.replace(/\?.*/, `?${ELLIPSIS}`);
337
336
  }
338
337
  }
339
338
 
@@ -429,11 +428,10 @@ class Util {
429
428
  break;
430
429
  case 'devtools': {
431
430
  const {cpuSlowdownMultiplier, requestLatencyMs} = throttling;
432
- // eslint-disable-next-line max-len
433
431
  cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`;
434
- networkThrottling = `${Util.i18n.formatMilliseconds(requestLatencyMs)} HTTP RTT, ` +
435
- `${Util.i18n.formatKbps(throttling.downloadThroughputKbps)} down, ` +
436
- `${Util.i18n.formatKbps(throttling.uploadThroughputKbps)} up (DevTools)`;
432
+ networkThrottling = `${Util.i18n.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, ` +
433
+ `${Util.i18n.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, ` +
434
+ `${Util.i18n.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`;
437
435
 
438
436
  const isSlow4G = () => {
439
437
  return requestLatencyMs === 150 * 3.75 &&
@@ -445,10 +443,9 @@ class Util {
445
443
  }
446
444
  case 'simulate': {
447
445
  const {cpuSlowdownMultiplier, rttMs, throughputKbps} = throttling;
448
- // eslint-disable-next-line max-len
449
446
  cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`;
450
- networkThrottling = `${Util.i18n.formatMilliseconds(rttMs)} TCP RTT, ` +
451
- `${Util.i18n.formatKbps(throughputKbps)} throughput (Simulated)`;
447
+ networkThrottling = `${Util.i18n.formatNumber(rttMs)}${NBSP}ms TCP RTT, ` +
448
+ `${Util.i18n.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`;
452
449
 
453
450
  const isSlow4G = () => {
454
451
  return rttMs === 150 && throughputKbps === 1.6 * 1024;
@@ -4,6 +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
+ 'use strict';
7
8
 
8
9
  import fs from 'fs';
9
10
 
@@ -16,6 +17,9 @@ import {LH_ROOT} from '../../../root.js';
16
17
  const sampleResultsStr =
17
18
  fs.readFileSync(LH_ROOT + '/lighthouse-core/test/results/sample_v2.json', 'utf-8');
18
19
 
20
+
21
+ /* eslint-env jest */
22
+
19
23
  describe('lighthouseRenderer bundle', () => {
20
24
  let document;
21
25
  beforeAll(() => {
@@ -9,6 +9,7 @@ const assert = require('assert').strict;
9
9
 
10
10
  const getLhrFilenamePrefix = require('../../generator/file-namer.js').getLhrFilenamePrefix;
11
11
 
12
+ /* eslint-env jest */
12
13
  describe('file-namer helper', () => {
13
14
  it('generates filename prefixes', () => {
14
15
  const results = {