lighthouse 8.0.0-dev.20210709 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/docs/architecture.md +101 -0
  2. package/docs/authenticated-pages.md +44 -0
  3. package/docs/bug-labels.md +35 -0
  4. package/docs/configuration.md +213 -0
  5. package/docs/emulation.md +32 -0
  6. package/docs/error-reporting.md +42 -0
  7. package/docs/hacking-tips.md +55 -0
  8. package/docs/headless-chrome.md +119 -0
  9. package/docs/lantern.md +55 -0
  10. package/docs/new-audits.md +99 -0
  11. package/docs/performance-budgets.md +187 -0
  12. package/docs/plugins.md +374 -0
  13. package/docs/puppeteer.md +95 -0
  14. package/docs/readme.md +164 -0
  15. package/docs/recipes/auth/README.md +129 -0
  16. package/docs/recipes/auth/example-lh-auth.js +81 -0
  17. package/docs/recipes/auth/package.json +11 -0
  18. package/docs/recipes/auth/server/public/dashboard-unauthenticated.html +21 -0
  19. package/docs/recipes/auth/server/public/dashboard.html +25 -0
  20. package/docs/recipes/auth/server/public/home-unauthenticated.html +27 -0
  21. package/docs/recipes/auth/server/public/home.html +17 -0
  22. package/docs/recipes/auth/server/public/unauthenticated.html +22 -0
  23. package/docs/recipes/auth/server/server.js +85 -0
  24. package/docs/recipes/custom-audit/custom-config.js +36 -0
  25. package/docs/recipes/custom-audit/package.json +8 -0
  26. package/docs/recipes/custom-audit/readme.md +32 -0
  27. package/docs/recipes/custom-audit/searchable-audit.js +46 -0
  28. package/docs/recipes/custom-audit/searchable-gatherer.js +32 -0
  29. package/docs/recipes/custom-gatherer-puppeteer/custom-audit.js +34 -0
  30. package/{root.js → docs/recipes/custom-gatherer-puppeteer/custom-config.js} +23 -2
  31. package/docs/recipes/custom-gatherer-puppeteer/custom-gatherer.js +51 -0
  32. package/docs/recipes/custom-gatherer-puppeteer/package.json +13 -0
  33. package/docs/recipes/custom-gatherer-puppeteer/readme.md +7 -0
  34. package/docs/recipes/custom-gatherer-puppeteer/test.sh +9 -0
  35. package/docs/recipes/gulp/gulpfile.js +79 -0
  36. package/docs/recipes/gulp/package.json +12 -0
  37. package/docs/recipes/gulp/public/index.html +22 -0
  38. package/docs/recipes/gulp/readme.md +6 -0
  39. package/docs/recipes/integration-test/README.md +14 -0
  40. package/docs/recipes/integration-test/example-lh-auth.test.js +160 -0
  41. package/docs/recipes/integration-test/package.json +9 -0
  42. package/docs/recipes/lighthouse-plugin-example/audits/preload-as.js +46 -0
  43. package/docs/recipes/lighthouse-plugin-example/package.json +11 -0
  44. package/docs/recipes/lighthouse-plugin-example/plugin-recipe-screenshot.png +0 -0
  45. package/docs/recipes/lighthouse-plugin-example/plugin.js +24 -0
  46. package/docs/recipes/lighthouse-plugin-example/readme.md +53 -0
  47. package/docs/releasing.md +164 -0
  48. package/docs/scoring.md +79 -0
  49. package/docs/throttling.md +150 -0
  50. package/docs/understanding-results.md +209 -0
  51. package/docs/v8-perf-faq.md +244 -0
  52. package/docs/variability.md +150 -0
  53. package/jest.config.js +0 -3
  54. package/lighthouse-cli/bin.js +8 -6
  55. package/lighthouse-core/audits/accessibility/aria-command-name.js +1 -1
  56. package/lighthouse-core/audits/accessibility/aria-meter-name.js +1 -1
  57. package/lighthouse-core/audits/accessibility/aria-progressbar-name.js +1 -1
  58. package/lighthouse-core/audits/accessibility/aria-tooltip-name.js +1 -1
  59. package/lighthouse-core/audits/accessibility/aria-treeitem-name.js +1 -1
  60. package/lighthouse-core/audits/apple-touch-icon.js +0 -1
  61. package/lighthouse-core/audits/audit.js +3 -3
  62. package/lighthouse-core/audits/autocomplete.js +2 -2
  63. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +14 -29
  64. package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +2 -3
  65. package/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +1 -1
  66. package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +2 -3
  67. package/lighthouse-core/audits/byte-efficiency/modern-image-formats.js +15 -55
  68. package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +4 -8
  69. package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +8 -15
  70. package/lighthouse-core/audits/byte-efficiency/total-byte-weight.js +9 -10
  71. package/lighthouse-core/audits/byte-efficiency/unminified-css.js +1 -1
  72. package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +1 -1
  73. package/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +1 -1
  74. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -2
  75. package/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js +2 -2
  76. package/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +1 -2
  77. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +24 -46
  78. package/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +1 -1
  79. package/lighthouse-core/audits/critical-request-chains.js +0 -1
  80. package/lighthouse-core/audits/csp-xss.js +1 -1
  81. package/lighthouse-core/audits/diagnostics.js +0 -1
  82. package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +0 -1
  83. package/lighthouse-core/audits/dobetterweb/inspector-issues.js +1 -1
  84. package/lighthouse-core/audits/dobetterweb/notification-on-start.js +0 -1
  85. package/lighthouse-core/audits/dobetterweb/uses-http2.js +0 -1
  86. package/lighthouse-core/audits/final-screenshot.js +4 -8
  87. package/lighthouse-core/audits/font-display.js +0 -1
  88. package/lighthouse-core/audits/installable-manifest.js +3 -4
  89. package/lighthouse-core/audits/largest-contentful-paint-element.js +0 -1
  90. package/lighthouse-core/audits/long-tasks.js +1 -1
  91. package/lighthouse-core/audits/maskable-icon.js +0 -1
  92. package/lighthouse-core/audits/metrics/first-contentful-paint-3g.js +2 -4
  93. package/lighthouse-core/audits/metrics/first-contentful-paint.js +2 -4
  94. package/lighthouse-core/audits/metrics/first-meaningful-paint.js +2 -4
  95. package/lighthouse-core/audits/metrics/interactive.js +2 -4
  96. package/lighthouse-core/audits/metrics/largest-contentful-paint.js +2 -4
  97. package/lighthouse-core/audits/metrics/max-potential-fid.js +2 -4
  98. package/lighthouse-core/audits/metrics/speed-index.js +2 -4
  99. package/lighthouse-core/audits/metrics/total-blocking-time.js +2 -10
  100. package/lighthouse-core/audits/metrics.js +2 -4
  101. package/lighthouse-core/audits/performance-budget.js +0 -1
  102. package/lighthouse-core/audits/predictive-perf.js +6 -9
  103. package/lighthouse-core/audits/preload-fonts.js +1 -11
  104. package/lighthouse-core/audits/preload-lcp-image.js +5 -18
  105. package/lighthouse-core/audits/redirects.js +8 -10
  106. package/lighthouse-core/audits/screenshot-thumbnails.js +13 -28
  107. package/lighthouse-core/audits/seo/canonical.js +1 -2
  108. package/lighthouse-core/audits/seo/font-size.js +4 -4
  109. package/lighthouse-core/audits/seo/hreflang.js +2 -3
  110. package/lighthouse-core/audits/seo/http-status-code.js +16 -21
  111. package/lighthouse-core/audits/seo/is-crawlable.js +3 -4
  112. package/lighthouse-core/audits/seo/robots-txt.js +2 -2
  113. package/lighthouse-core/audits/seo/tap-targets.js +5 -5
  114. package/lighthouse-core/audits/server-response-time.js +2 -20
  115. package/lighthouse-core/audits/splash-screen.js +0 -1
  116. package/lighthouse-core/audits/themed-omnibox.js +0 -1
  117. package/lighthouse-core/audits/third-party-facades.js +0 -1
  118. package/lighthouse-core/audits/third-party-summary.js +3 -1
  119. package/lighthouse-core/audits/timing-budget.js +2 -4
  120. package/lighthouse-core/audits/uses-rel-preconnect.js +9 -14
  121. package/lighthouse-core/audits/uses-rel-preload.js +2 -13
  122. package/lighthouse-core/computed/main-thread-tasks.js +2 -2
  123. package/lighthouse-core/computed/metrics/cumulative-layout-shift.js +3 -3
  124. package/lighthouse-core/computed/metrics/first-contentful-paint-all-frames.js +6 -6
  125. package/lighthouse-core/computed/metrics/first-contentful-paint.js +8 -9
  126. package/lighthouse-core/computed/metrics/first-meaningful-paint.js +9 -10
  127. package/lighthouse-core/computed/metrics/interactive.js +24 -25
  128. package/lighthouse-core/computed/metrics/lantern-first-contentful-paint.js +6 -6
  129. package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +6 -6
  130. package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +6 -6
  131. package/lighthouse-core/computed/metrics/lantern-metric.js +8 -16
  132. package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +7 -7
  133. package/lighthouse-core/computed/metrics/largest-contentful-paint.js +9 -10
  134. package/lighthouse-core/computed/metrics/max-potential-fid.js +11 -8
  135. package/lighthouse-core/computed/metrics/metric.js +5 -32
  136. package/lighthouse-core/computed/metrics/speed-index.js +5 -6
  137. package/lighthouse-core/computed/metrics/timing-summary.js +45 -55
  138. package/lighthouse-core/computed/metrics/total-blocking-time.js +29 -36
  139. package/lighthouse-core/computed/page-dependency-graph.js +14 -14
  140. package/lighthouse-core/computed/resource-summary.js +7 -3
  141. package/lighthouse-core/computed/speedline.js +3 -3
  142. package/lighthouse-core/computed/trace-of-tab.js +86 -15
  143. package/lighthouse-core/computed/user-timings.js +5 -5
  144. package/lighthouse-core/config/default-config.js +0 -1
  145. package/lighthouse-core/fraggle-rock/config/config.js +7 -9
  146. package/lighthouse-core/fraggle-rock/config/default-config.js +2 -40
  147. package/lighthouse-core/fraggle-rock/config/filters.js +6 -39
  148. package/lighthouse-core/fraggle-rock/config/validation.js +4 -4
  149. package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +19 -54
  150. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +10 -21
  151. package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +2 -2
  152. package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +2 -2
  153. package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +2 -2
  154. package/lighthouse-core/gather/driver/prepare.js +0 -3
  155. package/lighthouse-core/gather/fetcher.js +2 -0
  156. package/lighthouse-core/gather/gather-runner.js +14 -2
  157. package/lighthouse-core/gather/gatherers/image-elements.js +61 -24
  158. package/lighthouse-core/gather/gatherers/inspector-issues.js +2 -2
  159. package/lighthouse-core/gather/gatherers/script-elements.js +12 -45
  160. package/lighthouse-core/gather/gatherers/seo/font-size.js +5 -5
  161. package/lighthouse-core/gather/gatherers/seo/tap-targets.js +12 -64
  162. package/lighthouse-core/gather/gatherers/service-worker.js +3 -20
  163. package/lighthouse-core/gather/gatherers/trace-compat.js +1 -1
  164. package/lighthouse-core/gather/gatherers/trace-elements.js +3 -15
  165. package/lighthouse-core/gather/gatherers/trace.js +4 -13
  166. package/lighthouse-core/gather/gatherers/viewport-dimensions.js +1 -1
  167. package/lighthouse-core/lib/dependency-graph/simulator/network-analyzer.js +2 -13
  168. package/lighthouse-core/lib/emulation.js +2 -0
  169. package/lighthouse-core/lib/i18n/i18n.js +2 -2
  170. package/lighthouse-core/lib/i18n/locales/ar-XB.json +141 -141
  171. package/lighthouse-core/lib/i18n/locales/ar.json +143 -143
  172. package/lighthouse-core/lib/i18n/locales/bg.json +143 -143
  173. package/lighthouse-core/lib/i18n/locales/ca.json +141 -141
  174. package/lighthouse-core/lib/i18n/locales/cs.json +141 -141
  175. package/lighthouse-core/lib/i18n/locales/da.json +141 -141
  176. package/lighthouse-core/lib/i18n/locales/de.json +142 -142
  177. package/lighthouse-core/lib/i18n/locales/el.json +141 -141
  178. package/lighthouse-core/lib/i18n/locales/en-GB.json +141 -141
  179. package/lighthouse-core/lib/i18n/locales/en-US.ctc.json +6659 -0
  180. package/lighthouse-core/lib/i18n/locales/en-US.json +144 -150
  181. package/lighthouse-core/lib/i18n/locales/en-XA.json +141 -141
  182. package/lighthouse-core/lib/i18n/locales/en-XL.ctc.json +6628 -0
  183. package/lighthouse-core/lib/i18n/locales/en-XL.json +144 -150
  184. package/lighthouse-core/lib/i18n/locales/es-419.json +141 -141
  185. package/lighthouse-core/lib/i18n/locales/es.json +141 -141
  186. package/lighthouse-core/lib/i18n/locales/fi.json +143 -143
  187. package/lighthouse-core/lib/i18n/locales/fil.json +142 -142
  188. package/lighthouse-core/lib/i18n/locales/fr.json +141 -141
  189. package/lighthouse-core/lib/i18n/locales/he.json +142 -142
  190. package/lighthouse-core/lib/i18n/locales/hi.json +143 -143
  191. package/lighthouse-core/lib/i18n/locales/hr.json +141 -141
  192. package/lighthouse-core/lib/i18n/locales/hu.json +143 -143
  193. package/lighthouse-core/lib/i18n/locales/id.json +143 -143
  194. package/lighthouse-core/lib/i18n/locales/it.json +143 -143
  195. package/lighthouse-core/lib/i18n/locales/ja.json +143 -143
  196. package/lighthouse-core/lib/i18n/locales/ko.json +143 -143
  197. package/lighthouse-core/lib/i18n/locales/lt.json +143 -143
  198. package/lighthouse-core/lib/i18n/locales/lv.json +143 -143
  199. package/lighthouse-core/lib/i18n/locales/nl.json +141 -141
  200. package/lighthouse-core/lib/i18n/locales/no.json +141 -141
  201. package/lighthouse-core/lib/i18n/locales/pl.json +141 -141
  202. package/lighthouse-core/lib/i18n/locales/pt-PT.json +141 -141
  203. package/lighthouse-core/lib/i18n/locales/pt.json +141 -141
  204. package/lighthouse-core/lib/i18n/locales/ro.json +144 -144
  205. package/lighthouse-core/lib/i18n/locales/ru.json +143 -143
  206. package/lighthouse-core/lib/i18n/locales/sk.json +142 -142
  207. package/lighthouse-core/lib/i18n/locales/sl.json +141 -141
  208. package/lighthouse-core/lib/i18n/locales/sr-Latn.json +143 -143
  209. package/lighthouse-core/lib/i18n/locales/sr.json +143 -143
  210. package/lighthouse-core/lib/i18n/locales/sv.json +142 -142
  211. package/lighthouse-core/lib/i18n/locales/ta.json +141 -141
  212. package/lighthouse-core/lib/i18n/locales/te.json +143 -143
  213. package/lighthouse-core/lib/i18n/locales/th.json +143 -143
  214. package/lighthouse-core/lib/i18n/locales/tr.json +143 -143
  215. package/lighthouse-core/lib/i18n/locales/uk.json +141 -141
  216. package/lighthouse-core/lib/i18n/locales/vi.json +143 -143
  217. package/lighthouse-core/lib/i18n/locales/zh-HK.json +141 -141
  218. package/lighthouse-core/lib/i18n/locales/zh-TW.json +143 -143
  219. package/lighthouse-core/lib/i18n/locales/zh.json +143 -143
  220. package/lighthouse-core/lib/lh-error.js +2 -2
  221. package/lighthouse-core/lib/minify-trace.js +1 -1
  222. package/lighthouse-core/lib/network-request.js +1 -17
  223. package/lighthouse-core/lib/page-functions.js +5 -6
  224. package/lighthouse-core/lib/rect-helpers.js +3 -3
  225. package/lighthouse-core/lib/sd-validation/assets/jsonldcontext.json +7137 -0
  226. package/lighthouse-core/lib/sd-validation/assets/schema-tree.json +9900 -0
  227. package/{report/report-assets.js → lighthouse-core/lib/sd-validation/helpers/walk-object.js} +20 -12
  228. package/lighthouse-core/lib/sd-validation/json-expander.js +56 -0
  229. package/lighthouse-core/lib/sd-validation/json-linter.js +49 -0
  230. package/lighthouse-core/lib/sd-validation/jsonld-keyword-validator.js +50 -0
  231. package/lighthouse-core/lib/sd-validation/line-number-from-jsonld-path.js +60 -0
  232. package/lighthouse-core/lib/sd-validation/schema-validator.js +146 -0
  233. package/lighthouse-core/lib/sd-validation/scripts/download-jsonldcontext.js +30 -0
  234. package/lighthouse-core/lib/sd-validation/scripts/generate-schema-tree.js +97 -0
  235. package/lighthouse-core/lib/sd-validation/sd-validation.js +77 -0
  236. package/lighthouse-core/lib/tappable-rects.js +1 -1
  237. package/lighthouse-core/lib/tracehouse/trace-processor.js +42 -116
  238. package/lighthouse-core/lib/url-shim.js +2 -29
  239. package/lighthouse-core/report/html/html-report-assets.js +38 -0
  240. package/{report/README.md → lighthouse-core/report/html/readme.md} +11 -7
  241. package/{report → lighthouse-core/report/html}/renderer/category-renderer.js +0 -0
  242. package/{report → lighthouse-core/report/html}/renderer/crc-details-renderer.js +0 -0
  243. package/{report → lighthouse-core/report/html}/renderer/details-renderer.js +1 -1
  244. package/{report → lighthouse-core/report/html}/renderer/dom.js +0 -0
  245. package/{report → lighthouse-core/report/html}/renderer/element-screenshot-renderer.js +0 -0
  246. package/{report → lighthouse-core/report/html}/renderer/i18n.js +1 -1
  247. package/{report → lighthouse-core/report/html}/renderer/logger.js +0 -0
  248. package/{report → lighthouse-core/report/html}/renderer/performance-category-renderer.js +0 -0
  249. package/{report → lighthouse-core/report/html}/renderer/psi.js +0 -0
  250. package/{report → lighthouse-core/report/html}/renderer/pwa-category-renderer.js +0 -0
  251. package/{report → lighthouse-core/report/html}/renderer/report-renderer.js +0 -0
  252. package/{report → lighthouse-core/report/html}/renderer/report-ui-features.js +3 -3
  253. package/{report → lighthouse-core/report/html}/renderer/snippet-renderer.js +0 -0
  254. package/{report → lighthouse-core/report/html}/renderer/text-encoding.js +0 -0
  255. package/{report → lighthouse-core/report/html}/renderer/util.js +4 -7
  256. package/{report/assets/styles.css → lighthouse-core/report/html/report-styles.css} +0 -0
  257. package/{report/assets/standalone-template.html → lighthouse-core/report/html/report-template.html} +41 -1
  258. package/{report/assets → lighthouse-core/report/html}/templates.html +0 -0
  259. package/{report → lighthouse-core/report}/report-generator.js +1 -1
  260. package/lighthouse-core/runner.js +1 -1
  261. package/package.json +12 -13
  262. package/readme.md +2 -2
  263. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +2 -3
  264. package/third-party/chromium-synchronization/installability-errors-test.js +2 -2
  265. package/third-party/download-content-shell/download-content-shell.js +1 -2
  266. package/third-party/snyk/snapshot.json +1 -1
  267. package/tsconfig.json +1 -0
  268. package/types/artifacts.d.ts +21 -41
  269. package/types/audit.d.ts +0 -2
  270. package/types/config.d.ts +2 -16
  271. package/types/externs.d.ts +0 -1
  272. package/types/gatherer.d.ts +13 -13
  273. package/types/html-renderer.d.ts +14 -14
  274. package/types/i18n.d.ts +1 -1
  275. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +0 -94
  276. package/lighthouse-core/computed/image-records.js +0 -62
  277. package/lighthouse-core/computed/metrics/navigation-metric.js +0 -46
  278. package/lighthouse-core/computed/processed-navigation.js +0 -21
  279. package/lighthouse-core/computed/processed-trace.js +0 -21
  280. package/lighthouse-core/gather/gatherers/gather-context.js +0 -25
  281. package/lighthouse-core/gather/gatherers/host-form-factor.js +0 -31
  282. package/lighthouse-core/gather/gatherers/host-user-agent.js +0 -29
  283. package/lighthouse-core/lib/lh-trace-processor.js +0 -46
  284. package/report/clients/standalone.js +0 -52
  285. package/report/test/renderer/category-renderer-test.js +0 -488
  286. package/report/test/renderer/crc-details-renderer-test.js +0 -115
  287. package/report/test/renderer/details-renderer-test.js +0 -843
  288. package/report/test/renderer/dom-test.js +0 -157
  289. package/report/test/renderer/element-screenshot-renderer-test.js +0 -189
  290. package/report/test/renderer/i18n-test.js +0 -146
  291. package/report/test/renderer/performance-category-renderer-test.js +0 -332
  292. package/report/test/renderer/psi-test.js +0 -152
  293. package/report/test/renderer/pwa-category-renderer-test.js +0 -303
  294. package/report/test/renderer/report-renderer-test.js +0 -325
  295. package/report/test/renderer/report-ui-features-test.js +0 -572
  296. package/report/test/renderer/snippet-renderer-test.js +0 -291
  297. package/report/test/renderer/text-encoding-test.js +0 -41
  298. package/report/test/renderer/util-test.js +0 -388
  299. package/report/test/report-generator-test.js +0 -145
@@ -1,843 +0,0 @@
1
- /**
2
- * @license Copyright 2017 The Lighthouse Authors. All Rights Reserved.
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
- * 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
- */
6
- 'use strict';
7
-
8
- const assert = require('assert').strict;
9
- const jsdom = require('jsdom');
10
- const reportAssets = require('../../report-assets.js');
11
- const DOM = require('../../renderer/dom.js');
12
- const Util = require('../../renderer/util.js');
13
- const I18n = require('../../renderer/i18n.js');
14
- const DetailsRenderer = require('../../renderer/details-renderer.js');
15
- const SnippetRenderer = require('../../renderer/snippet-renderer.js');
16
- const CrcDetailsRenderer = require('../../renderer/crc-details-renderer.js');
17
- const ElementScreenshotRenderer = require('../../renderer/element-screenshot-renderer.js');
18
-
19
- /* eslint-env jest */
20
-
21
- describe('DetailsRenderer', () => {
22
- let renderer;
23
-
24
- function createRenderer(options) {
25
- const {document} = new jsdom.JSDOM(reportAssets.REPORT_TEMPLATES).window;
26
- const dom = new DOM(document);
27
- renderer = new DetailsRenderer(dom, options);
28
- renderer.setTemplateContext(dom.document());
29
- }
30
-
31
- beforeAll(() => {
32
- global.Util = Util;
33
- global.Util.i18n = new I18n('en', {...Util.UIStrings});
34
- global.CriticalRequestChainRenderer = CrcDetailsRenderer;
35
- global.SnippetRenderer = SnippetRenderer;
36
- global.ElementScreenshotRenderer = ElementScreenshotRenderer;
37
- createRenderer();
38
- });
39
-
40
- afterAll(() => {
41
- global.Util.i18n = undefined;
42
- global.Util = undefined;
43
- global.CriticalRequestChainRenderer = undefined;
44
- global.SnippetRenderer = undefined;
45
- global.ElementScreenshotRenderer = undefined;
46
- });
47
-
48
- describe('render', () => {
49
- it('renders filmstrips', () => {
50
- const el = renderer.render({
51
- type: 'filmstrip',
52
- items: [
53
- {timing: 1020, data: 'data:image/jpeg;base64,foobar'},
54
- {timing: 3030, data: 'data:image/jpeg;base64,foobaz'},
55
- ],
56
- });
57
-
58
- assert.ok(el.localName === 'div');
59
- assert.ok(el.classList.contains('lh-filmstrip'));
60
-
61
- const frames = [...el.querySelectorAll('.lh-filmstrip__frame')];
62
- assert.equal(frames.length, 2);
63
-
64
- const thumbnails = [...el.querySelectorAll('.lh-filmstrip__thumbnail')];
65
- assert.equal(thumbnails.length, 2);
66
- assert.equal(thumbnails[0].src, 'data:image/jpeg;base64,foobar');
67
- assert.ok(thumbnails[0].alt, 'did not set alt text');
68
- });
69
-
70
- it('renders tables', () => {
71
- const el = renderer.render({
72
- type: 'table',
73
- headings: [
74
- {text: 'First', key: 'a', itemType: 'text'},
75
- {text: 'Second', key: 'b', itemType: 'text'},
76
- {text: 'Preview', key: 'c', itemType: 'thumbnail'},
77
- ],
78
- items: [
79
- {
80
- a: 'value A.1',
81
- b: 'value A.2',
82
- c: 'http://example.com/image.jpg',
83
- },
84
- {
85
- a: 'value B.1',
86
- b: 'value B.2',
87
- c: 'unknown',
88
- },
89
- ],
90
- });
91
-
92
- assert.equal(el.localName, 'table', 'did not render table');
93
- assert.ok(el.querySelector('img'), 'did not render recursive items');
94
- assert.equal(el.querySelectorAll('th').length, 3, 'did not render header items');
95
- assert.equal(el.querySelectorAll('td').length, 6, 'did not render table cells');
96
- assert.equal(el.querySelectorAll('.lh-table-column--text').length, 6, '--text not set');
97
- assert.equal(el.querySelectorAll('.lh-table-column--thumbnail').length, 3,
98
- '--thumbnail not set');
99
- });
100
-
101
- it('renders critical request chains', () => {
102
- const details = {
103
- type: 'criticalrequestchain',
104
- longestChain: {
105
- duration: 2,
106
- length: 1,
107
- transferSize: 221,
108
- },
109
- chains: {
110
- F3B687683512E0F003DD41EB23E2091A: {
111
- request: {
112
- url: 'https://example.com',
113
- startTime: 0,
114
- endTime: 2,
115
- responseReceivedTime: 1,
116
- transferSize: 221,
117
- },
118
- children: {},
119
- },
120
- },
121
- };
122
-
123
- const crcEl = renderer.render(details);
124
- assert.ok(crcEl.classList.contains('lh-crc-container'));
125
- assert.strictEqual(crcEl.querySelectorAll('.crc-node').length, 1);
126
- });
127
-
128
- it('renders opportunity details as a table', () => {
129
- const details = {
130
- type: 'opportunity',
131
- headings: [
132
- {key: 'url', valueType: 'url', label: 'URL'},
133
- {key: 'totalBytes', valueType: 'bytes', label: 'Size (KiB)'},
134
- {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings (KiB)'},
135
- ],
136
- items: [{
137
- url: 'https://example.com',
138
- totalBytes: 71654,
139
- wastedBytes: 30470,
140
- wastedPercent: 42,
141
- }],
142
- overallSavingsMs: 150,
143
- overallSavingsBytes: 30470,
144
- };
145
-
146
- const oppEl = renderer.render(details);
147
- assert.equal(oppEl.localName, 'table');
148
- assert.ok(oppEl.querySelector('.lh-text__url').title === 'https://example.com', 'did not render recursive items');
149
- assert.equal(oppEl.querySelectorAll('th').length, 3, 'did not render header items');
150
- assert.equal(oppEl.querySelectorAll('td').length, 3, 'did not render table cells');
151
- assert.equal(oppEl.querySelectorAll('.lh-table-column--url').length, 2, 'url column not set');
152
- assert.equal(oppEl.querySelectorAll('.lh-table-column--bytes').length, 4, 'bytes not set');
153
- });
154
-
155
- it('renders lists', () => {
156
- const snippet = {
157
- type: 'snippet',
158
- lines: [{lineNumber: 1, content: ''}],
159
- title: 'Some snippet',
160
- lineMessages: [],
161
- generalMessages: [],
162
- lineCount: 100,
163
- };
164
-
165
- const el = renderer.render({
166
- type: 'list',
167
- items: [snippet, snippet],
168
- });
169
-
170
- assert.equal(el.localName, 'div');
171
- assert.ok(el.classList.contains('lh-list'), 'has list class');
172
- assert.ok(el.children.length, 2, 'renders all items');
173
- assert.ok(el.children[0].textContent.includes('Some snippet'), 'renders item content');
174
- });
175
-
176
- it('does not render internal-only screenshot details', () => {
177
- const details = {
178
- type: 'screenshot',
179
- timestamp: 185600000,
180
- data: 'data:image/jpeg;base64,/9j/4AAQSkZJRYP/2Q==',
181
- };
182
-
183
- const screenshotEl = renderer.render(details);
184
- assert.strictEqual(screenshotEl, null);
185
- });
186
-
187
- it('does not render internal-only diagnostic details', () => {
188
- const details = {
189
- type: 'debugdata',
190
- items: [{
191
- failures: ['No manifest was fetched'],
192
- isParseFailure: true,
193
- parseFailureReason: 'No manifest was fetched',
194
- }],
195
- };
196
-
197
- const diagnosticEl = renderer.render(details);
198
- assert.strictEqual(diagnosticEl, null);
199
- });
200
-
201
- describe('unknown types', () => {
202
- // console.error is set to a no-op, to avoid distracting log spam in the test output.
203
- let consoleError;
204
- beforeEach(() => {
205
- consoleError = console.error;
206
- console.error = () => {};
207
- });
208
- afterEach(() => {
209
- console.error = consoleError;
210
- });
211
-
212
- it('renders an unknown details type', () => {
213
- // Disallowed by type system, but test that we get an error message out just in case.
214
- const details = {
215
- type: 'imaginary',
216
- items: 5,
217
- };
218
-
219
- const el = renderer.render(details);
220
- const summaryEl = el.querySelector('summary');
221
- expect(summaryEl.textContent)
222
- .toContain('We don\'t know how to render audit details of type `imaginary`');
223
- assert.strictEqual(el.lastChild.textContent, JSON.stringify(details, null, 2));
224
- });
225
- });
226
- });
227
-
228
- describe('Table rendering', () => {
229
- it('renders text values', () => {
230
- const details = {
231
- type: 'table',
232
- headings: [{key: 'content', itemType: 'text', text: 'Heading'}],
233
- items: [{content: 'My text content'}],
234
- };
235
-
236
- const el = renderer.render(details);
237
- const textEls = el.querySelectorAll('.lh-text');
238
- assert.strictEqual(textEls[0].textContent, 'Heading');
239
- assert.strictEqual(textEls[1].textContent, 'My text content');
240
- });
241
-
242
- it('renders not much if items are empty', () => {
243
- const details = {
244
- type: 'table',
245
- headings: [{key: 'content', itemType: 'text', text: 'Heading'}],
246
- items: [],
247
- };
248
-
249
- const el = renderer.render(details);
250
- assert.strictEqual(el.outerHTML, '<span></span>');
251
- });
252
-
253
- it('renders an empty cell if item is missing a property', () => {
254
- const details = {
255
- type: 'table',
256
- headings: [{key: 'content', itemType: 'text', text: 'Heading'}],
257
- items: [
258
- {},
259
- {content: undefined},
260
- {content: 'a thing'},
261
- ],
262
- };
263
-
264
- const el = renderer.render(details);
265
- const itemEls = el.querySelectorAll('td');
266
- assert.strictEqual(itemEls.length, 3);
267
-
268
- // missing prop
269
- assert.ok(itemEls[0].classList.contains('lh-table-column--empty'));
270
- assert.strictEqual(itemEls[0].innerHTML, '');
271
-
272
- // undefined prop
273
- assert.ok(itemEls[1].classList.contains('lh-table-column--empty'));
274
- assert.strictEqual(itemEls[1].innerHTML, '');
275
-
276
- // defined prop
277
- assert.ok(itemEls[2].classList.contains('lh-table-column--text'));
278
- assert.strictEqual(itemEls[2].textContent, 'a thing');
279
- });
280
-
281
- it('renders code values from a string', () => {
282
- const details = {
283
- type: 'table',
284
- headings: [{key: 'content', itemType: 'code', text: 'Heading'}],
285
- items: [{content: 'code snippet'}],
286
- };
287
-
288
- const el = renderer.render(details);
289
- const codeEl = el.querySelector('.lh-code');
290
- assert.ok(codeEl.localName === 'pre');
291
- assert.equal(codeEl.textContent, 'code snippet');
292
- });
293
-
294
- it('renders code values from a code details object', () => {
295
- const code = {
296
- type: 'code',
297
- value: 'code object',
298
- };
299
-
300
- const details = {
301
- type: 'table',
302
- headings: [{key: 'content', itemType: 'code', text: 'Heading'}],
303
- items: [{content: code}],
304
- };
305
-
306
- const el = renderer.render(details);
307
- const codeEl = el.querySelector('.lh-code');
308
- assert.ok(codeEl.localName === 'pre');
309
- assert.equal(codeEl.textContent, 'code object');
310
- });
311
-
312
- it('renders thumbnail values', () => {
313
- const details = {
314
- type: 'table',
315
- headings: [{key: 'content', itemType: 'thumbnail', text: 'Heading'}],
316
- items: [{content: 'http://example.com/my-image.jpg'}],
317
- };
318
-
319
- const el = renderer.render(details);
320
- const thumbnailEl = el.querySelector('img');
321
- assert.ok(thumbnailEl.classList.contains('lh-thumbnail'));
322
- assert.strictEqual(thumbnailEl.src, 'http://example.com/my-image.jpg');
323
- assert.strictEqual(thumbnailEl.title, 'http://example.com/my-image.jpg');
324
- assert.strictEqual(thumbnailEl.alt, '');
325
- });
326
-
327
- it('renders link values', () => {
328
- const linkText = 'Example Site';
329
- const linkUrl = 'https://example.com/';
330
- const link = {
331
- type: 'link',
332
- text: linkText,
333
- url: linkUrl,
334
- };
335
- const details = {
336
- type: 'table',
337
- headings: [{key: 'content', itemType: 'link', text: 'Heading'}],
338
- items: [{content: link}],
339
- };
340
-
341
- const el = renderer.render(details);
342
- const anchorEl = el.querySelector('a');
343
- assert.equal(anchorEl.textContent, linkText);
344
- assert.equal(anchorEl.href, linkUrl);
345
- assert.equal(anchorEl.rel, 'noopener');
346
- assert.equal(anchorEl.target, '_blank');
347
- });
348
-
349
- it('renders link value as text if URL is not allowed', () => {
350
- const linkText = 'Evil Link';
351
- const linkUrl = 'javascript:alert(5)';
352
- const link = {
353
- type: 'link',
354
- text: linkText,
355
- url: linkUrl,
356
- };
357
- const details = {
358
- type: 'table',
359
- headings: [{key: 'content', itemType: 'link', text: 'Heading'}],
360
- items: [{content: link}],
361
- };
362
-
363
- const el = renderer.render(details);
364
- const linkEl = el.querySelector('td.lh-table-column--link > .lh-text');
365
- assert.equal(linkEl.localName, 'div');
366
- assert.equal(linkEl.textContent, linkText);
367
- });
368
-
369
- it('renders link value as text if URL is invalid', () => {
370
- const linkText = 'Invalid Link';
371
- const linkUrl = 'link nonsense';
372
- const link = {
373
- type: 'link',
374
- text: linkText,
375
- url: linkUrl,
376
- };
377
- const details = {
378
- type: 'table',
379
- headings: [{key: 'content', itemType: 'link', text: 'Heading'}],
380
- items: [{content: link}],
381
- };
382
-
383
- const el = renderer.render(details);
384
- const linkEl = el.querySelector('td.lh-table-column--link > .lh-text');
385
- assert.equal(linkEl.localName, 'div');
386
- assert.equal(linkEl.textContent, linkText);
387
- });
388
-
389
- describe('renders node values', () => {
390
- it('renders', () => {
391
- const node = {
392
- type: 'node',
393
- path: '3,HTML,1,BODY,5,DIV,0,H2',
394
- selector: 'h2',
395
- snippet: '<h2>Do better web tester page</h2>',
396
- };
397
- const details = {
398
- type: 'table',
399
- headings: [{key: 'content', itemType: 'node', text: 'Heading'}],
400
- items: [{content: node}],
401
- };
402
-
403
- const el = renderer.render(details);
404
- const nodeEl = el.querySelector('.lh-node');
405
- assert.strictEqual(nodeEl.localName, 'span');
406
- assert.equal(nodeEl.textContent, node.snippet);
407
- assert.equal(nodeEl.title, node.selector);
408
- assert.equal(nodeEl.getAttribute('data-path'), node.path);
409
- assert.equal(nodeEl.getAttribute('data-selector'), node.selector);
410
- assert.equal(nodeEl.getAttribute('data-snippet'), node.snippet);
411
- });
412
-
413
- it('renders screenshot using FullPageScreenshot rect data', () => {
414
- createRenderer({
415
- fullPageScreenshot: {
416
- screenshot: {
417
- data: '',
418
- width: 1000,
419
- height: 1000,
420
- },
421
- nodes: {
422
- 'page-0-0': {left: 1, right: 101, top: 1, bottom: 101, width: 100, height: 100},
423
- },
424
- },
425
- });
426
-
427
- const node = {
428
- type: 'node',
429
- lhId: 'page-0-0',
430
- boundingRect: {left: 0, right: 100, top: 0, bottom: 100, width: 100, height: 100},
431
- };
432
- const details = {
433
- type: 'table',
434
- headings: [{key: 'content', itemType: 'node', text: 'Heading'}],
435
- items: [{content: node}],
436
- };
437
-
438
- const el = renderer.render(details);
439
- const nodeEl = el.querySelector('.lh-node');
440
- const screenshotEl = nodeEl.querySelector('.lh-element-screenshot');
441
- assert.equal(screenshotEl.dataset['rectLeft'], '1');
442
- assert.equal(screenshotEl.dataset['rectTop'], '1');
443
- });
444
-
445
- it('does not render screenshot if rect missing in FullPageScreenshot', () => {
446
- createRenderer({
447
- fullPageScreenshot: {
448
- screenshot: {
449
- data: '',
450
- width: 1000,
451
- height: 1000,
452
- },
453
- nodes: {
454
- 'ignore-me': {left: 1, right: 101, top: 1, bottom: 101, width: 100, height: 100},
455
- },
456
- },
457
- });
458
-
459
- const node = {
460
- type: 'node',
461
- lhId: 'page-0-0',
462
- boundingRect: {left: 0, right: 100, top: 0, bottom: 100, width: 100, height: 100},
463
- };
464
- const details = {
465
- type: 'table',
466
- headings: [{key: 'content', itemType: 'node', text: 'Heading'}],
467
- items: [{content: node}],
468
- };
469
-
470
- const el = renderer.render(details);
471
- const nodeEl = el.querySelector('.lh-node');
472
- const screenshotEl = nodeEl.querySelector('.lh-element-screenshot');
473
- expect(screenshotEl).toBeNull();
474
- });
475
- });
476
-
477
- it('renders source-location values', () => {
478
- const sourceLocation = {
479
- type: 'source-location',
480
- url: 'https://www.example.com/script.js',
481
- urlProvider: 'network',
482
- line: 10,
483
- column: 5,
484
- };
485
- const details = {
486
- type: 'table',
487
- headings: [{key: 'content', itemType: 'source-location', text: 'Heading'}],
488
- items: [{content: sourceLocation}],
489
- };
490
-
491
- const el = renderer.render(details);
492
- const sourceLocationEl = el.querySelector('.lh-source-location');
493
- const anchorEl = sourceLocationEl.querySelector('a');
494
- assert.strictEqual(sourceLocationEl.localName, 'div');
495
- assert.equal(anchorEl.href, 'https://www.example.com/script.js');
496
- assert.equal(sourceLocationEl.textContent, '/script.js:11:5(www.example.com)');
497
- assert.equal(sourceLocationEl.getAttribute('data-source-url'), sourceLocation.url);
498
- assert.equal(sourceLocationEl.getAttribute('data-source-line'), `${sourceLocation.line}`);
499
- assert.equal(sourceLocationEl.getAttribute('data-source-column'), `${sourceLocation.column}`);
500
- });
501
-
502
- it('renders source-location values using source map data', () => {
503
- const sourceLocation = {
504
- type: 'source-location',
505
- url: 'https://www.example.com/script.js',
506
- urlProvider: 'network',
507
- line: 10,
508
- column: 5,
509
- original: {
510
- file: 'main.js',
511
- line: 100,
512
- column: 10,
513
- },
514
- };
515
- const details = {
516
- type: 'table',
517
- headings: [{key: 'content', itemType: 'source-location', text: 'Heading'}],
518
- items: [{content: sourceLocation}],
519
- };
520
-
521
- const el = renderer.render(details);
522
- const sourceLocationEl = el.querySelector('.lh-source-location.lh-link');
523
- assert.strictEqual(sourceLocationEl.localName, 'a');
524
- assert.equal(sourceLocationEl.href, 'https://www.example.com/script.js');
525
- assert.equal(sourceLocationEl.textContent, 'main.js:101:10');
526
- assert.equal(sourceLocationEl.title, 'maps to generated location https://www.example.com/script.js:11:5');
527
- // DevTools should still use the generated location.
528
- assert.equal(sourceLocationEl.getAttribute('data-source-url'), sourceLocation.url);
529
- assert.equal(sourceLocationEl.getAttribute('data-source-line'), `${sourceLocation.line}`);
530
- assert.equal(sourceLocationEl.getAttribute('data-source-column'), `${sourceLocation.column}`);
531
- });
532
-
533
- it('renders source-location with lh-link class for relative url', () => {
534
- const sourceLocation = {
535
- type: 'source-location',
536
- url: '/root-relative-url/script.js',
537
- urlProvider: 'network',
538
- line: 0,
539
- column: 0,
540
- };
541
- const details = {
542
- type: 'table',
543
- headings: [{key: 'content', itemType: 'source-location', text: 'Heading'}],
544
- items: [{content: sourceLocation}],
545
- };
546
-
547
- const el = renderer.render(details);
548
- const sourceLocationEl = el.querySelector('.lh-source-location');
549
- const anchorEl = sourceLocationEl.querySelector('.lh-link');
550
- assert.ok(anchorEl);
551
- assert.strictEqual(sourceLocationEl.localName, 'div');
552
- assert.equal(sourceLocationEl.textContent, '/root-relative-url/script.js:1:0');
553
- assert.equal(sourceLocationEl.getAttribute('data-source-url'), sourceLocation.url);
554
- assert.equal(sourceLocationEl.getAttribute('data-source-line'), `${sourceLocation.line}`);
555
- assert.equal(sourceLocationEl.getAttribute('data-source-column'), `${sourceLocation.column}`);
556
- });
557
-
558
- it('renders source-location with lh-link class for invalid urls', () => {
559
- const sourceLocation = {
560
- type: 'source-location',
561
- url: 'thisisclearlynotavalidurl',
562
- urlProvider: 'network',
563
- line: 0,
564
- column: 0,
565
- };
566
- const details = {
567
- type: 'table',
568
- headings: [{key: 'content', itemType: 'source-location', text: 'Heading'}],
569
- items: [{content: sourceLocation}],
570
- };
571
-
572
- const el = renderer.render(details);
573
- const sourceLocationEl = el.querySelector('.lh-source-location');
574
- const anchorEl = sourceLocationEl.querySelector('.lh-link');
575
- assert.ok(anchorEl);
576
- assert.strictEqual(sourceLocationEl.localName, 'div');
577
- assert.equal(sourceLocationEl.textContent, 'thisisclearlynotavalidurl:1:0');
578
- assert.equal(sourceLocationEl.getAttribute('data-source-url'), sourceLocation.url);
579
- assert.equal(sourceLocationEl.getAttribute('data-source-line'), `${sourceLocation.line}`);
580
- assert.equal(sourceLocationEl.getAttribute('data-source-column'), `${sourceLocation.column}`);
581
- });
582
-
583
- it('renders source-location values that aren\'t network resources', () => {
584
- const sourceLocation = {
585
- type: 'source-location',
586
- url: 'https://www.example.com/script.js',
587
- urlProvider: 'comment',
588
- line: 0,
589
- column: 0,
590
- };
591
- const details = {
592
- type: 'table',
593
- headings: [{key: 'content', itemType: 'source-location', text: 'Heading'}],
594
- items: [{content: sourceLocation}],
595
- };
596
-
597
- const el = renderer.render(details);
598
- const sourceLocationEl = el.querySelector('.lh-source-location');
599
- const anchorEl = sourceLocationEl.querySelector('a');
600
- assert.ok(!anchorEl);
601
- assert.strictEqual(sourceLocationEl.localName, 'div');
602
- assert.equal(sourceLocationEl.textContent, 'https://www.example.com/script.js:1:0 (from sourceURL)');
603
- assert.equal(sourceLocationEl.getAttribute('data-source-url'), sourceLocation.url);
604
- assert.equal(sourceLocationEl.getAttribute('data-source-line'), `${sourceLocation.line}`);
605
- assert.equal(sourceLocationEl.getAttribute('data-source-column'), `${sourceLocation.column}`);
606
- });
607
-
608
- it('renders text URL values from a string', () => {
609
- const urlText = 'https://example.com/';
610
- const displayUrlText = 'https://example.com';
611
-
612
- const details = {
613
- type: 'table',
614
- headings: [{key: 'content', itemType: 'url', text: 'Heading'}],
615
- items: [{content: urlText}],
616
- };
617
-
618
- const el = renderer.render(details);
619
- const urlEl = el.querySelector('td.lh-table-column--url > .lh-text__url');
620
-
621
- assert.equal(urlEl.localName, 'div');
622
- assert.equal(urlEl.title, urlText);
623
- assert.equal(urlEl.dataset.url, urlText);
624
- assert.equal(urlEl.firstChild.nodeName, 'A');
625
- assert.equal(urlEl.firstChild.href, urlText);
626
- assert.equal(urlEl.firstChild.rel, 'noopener');
627
- assert.equal(urlEl.firstChild.target, '_blank');
628
- assert.equal(urlEl.textContent, displayUrlText);
629
- });
630
-
631
- it('renders text URL values from a url details object', () => {
632
- const urlText = 'https://example.com/';
633
- const displayUrlText = 'https://example.com';
634
- const url = {
635
- type: 'url',
636
- value: urlText,
637
- };
638
-
639
- const details = {
640
- type: 'table',
641
- headings: [{key: 'content', itemType: 'url', text: 'Heading'}],
642
- items: [{content: url}],
643
- overallSavingsMs: 100,
644
- };
645
-
646
- const el = renderer.render(details);
647
- const urlEl = el.querySelector('td.lh-table-column--url > .lh-text__url');
648
-
649
- assert.equal(urlEl.localName, 'div');
650
- assert.equal(urlEl.title, urlText);
651
- assert.equal(urlEl.dataset.url, urlText);
652
- assert.equal(urlEl.firstChild.nodeName, 'A');
653
- assert.equal(urlEl.textContent, displayUrlText);
654
- });
655
-
656
- it('renders text URL values as code if not an allowed URL', () => {
657
- const urlText = 'invalid-url://example.com/';
658
-
659
- const details = {
660
- type: 'table',
661
- headings: [{key: 'content', itemType: 'url', text: 'Heading'}],
662
- items: [{content: urlText}],
663
- };
664
-
665
- const el = renderer.render(details);
666
- const codeItemEl = el.querySelector('td.lh-table-column--url');
667
- assert.strictEqual(codeItemEl.innerHTML, '<pre class="lh-code">invalid-url://example.com/</pre>');
668
- });
669
-
670
- describe('unknown types', () => {
671
- let consoleError;
672
- beforeEach(() => {
673
- consoleError = console.error;
674
- console.error = () => {};
675
- });
676
- afterEach(() => {
677
- console.error = consoleError;
678
- });
679
-
680
- it('renders an unknown heading itemType', () => {
681
- // Disallowed by type system, but test that we get an error message out just in case.
682
- const details = {
683
- type: 'table',
684
- headings: [{key: 'content', itemType: 'notRealValueType', text: 'Heading'}],
685
- items: [{content: 'some string'}],
686
- };
687
-
688
- const el = renderer.render(details);
689
- const unknownEl = el.querySelector('td.lh-table-column--notRealValueType .lh-unknown');
690
- const summaryEl = unknownEl.querySelector('summary');
691
- expect(summaryEl.textContent)
692
- .toContain('We don\'t know how to render audit details of type `notRealValueType`');
693
- assert.strictEqual(unknownEl.lastChild.textContent, '"some string"');
694
- });
695
-
696
- it('renders an unknown item object type', () => {
697
- // Disallowed by type system, but test that we get an error message out just in case.
698
- const item = {
699
- type: 'imaginaryItem',
700
- items: 'alllll the items',
701
- };
702
-
703
- const details = {
704
- type: 'table',
705
- headings: [{key: 'content', itemType: 'url', text: 'Heading'}],
706
- items: [{content: item}],
707
- };
708
-
709
- const el = renderer.render(details);
710
- const unknownEl = el.querySelector('td.lh-table-column--url .lh-unknown');
711
- const summaryEl = unknownEl.querySelector('summary');
712
- expect(summaryEl.textContent)
713
- .toContain('We don\'t know how to render audit details of type `imaginaryItem`');
714
- assert.strictEqual(unknownEl.lastChild.textContent, JSON.stringify(item, null, 2));
715
- });
716
- });
717
-
718
- it('uses the item\'s type over the heading type', () => {
719
- const details = {
720
- type: 'table',
721
- // itemType is overriden by code object
722
- headings: [{key: 'content', itemType: 'url', text: 'Heading'}],
723
- items: [
724
- {content: {type: 'code', value: 'https://codeobject.com'}},
725
- {content: 'https://example.com'},
726
- ],
727
- };
728
-
729
- const el = renderer.render(details);
730
- const itemElements = el.querySelectorAll('td.lh-table-column--url');
731
-
732
- // First item's value uses its own type.
733
- const codeEl = itemElements[0].firstChild;
734
- assert.equal(codeEl.localName, 'pre');
735
- assert.ok(codeEl.classList.contains('lh-code'));
736
- assert.equal(codeEl.textContent, 'https://codeobject.com');
737
-
738
- // Second item uses the heading's specified type for the column.
739
- const urlEl = itemElements[1].firstChild;
740
- assert.equal(urlEl.localName, 'div');
741
- assert.ok(urlEl.classList.contains('lh-text__url'));
742
- assert.equal(urlEl.title, 'https://example.com');
743
- assert.equal(urlEl.textContent, 'https://example.com');
744
- });
745
-
746
- describe('subitems', () => {
747
- function makeSubitems(items) {
748
- return {
749
- type: 'subitems',
750
- items,
751
- };
752
- }
753
-
754
- it('renders', () => {
755
- const details = {
756
- type: 'table',
757
- headings: [
758
- {key: 'url', itemType: 'url', subItemsHeading: {key: 'source', itemType: 'code'}},
759
- ],
760
- items: [
761
- {
762
- url: 'https://www.example.com',
763
- subItems: makeSubitems([
764
- {source: 'a'},
765
- {source: 'b'},
766
- {source: 'c'},
767
- ]),
768
- },
769
- ],
770
- };
771
-
772
- const el = renderer.render(details);
773
-
774
- const rowEls = el.querySelectorAll('tbody tr');
775
- let rowEl;
776
- let columnEl;
777
-
778
- // First row contains a 'url' item type.
779
- rowEl = rowEls[0];
780
- columnEl = rowEl.querySelector('td.lh-table-column--url');
781
- const codeEl = columnEl.firstChild;
782
- assert.equal(codeEl.localName, 'div');
783
- assert.ok(codeEl.classList.contains('lh-text__url'));
784
- assert.equal(codeEl.textContent, 'https://www.example.com');
785
-
786
- // The subItems contain a 'code' item type.
787
- for (let i = 0; i < details.items[0].subItems.items; i++) {
788
- const source = details.items[0].subItems.items[i].source;
789
- rowEl = rowEls[i + 1];
790
- columnEl = rowEl.querySelector('td.lh-table-column--code');
791
- assert.ok(rowEl.classList.contains('lh-sub-item-row'));
792
- assert.ok(columnEl.firstChild.classList.contains('lh-code'));
793
- assert.equal(rowEl.textContent, source);
794
- }
795
- });
796
-
797
- it('renders, uses heading properties as fallback', () => {
798
- const details = {
799
- type: 'table',
800
- headings: [{key: 'url', itemType: 'url', subItemsHeading: {key: 'source'}}],
801
- items: [
802
- {
803
- url: 'https://www.example.com',
804
- subItems: makeSubitems([
805
- {source: 'https://www.a.com'},
806
- {source: {type: 'code', value: 'https://www.b.com'}},
807
- {source: 'https://www.c.com'},
808
- ]),
809
- },
810
- ],
811
- };
812
-
813
- const el = renderer.render(details);
814
- const rowEls = el.querySelectorAll('tbody tr');
815
- let rowEl;
816
- let columnEl;
817
-
818
- // First row contains a 'url' item type.
819
- rowEl = rowEls[0];
820
- columnEl = rowEl.querySelector('td.lh-table-column--url');
821
- const codeEl = columnEl.firstChild;
822
- assert.equal(codeEl.localName, 'div');
823
- assert.ok(codeEl.classList.contains('lh-text__url'));
824
- assert.equal(codeEl.textContent, 'https://www.example.com');
825
-
826
- // The sub-rows contain a 'url' item type, except for the second one, which is 'code'.
827
- for (let i = 0; i < details.items[0].subItems.items.length; i++) {
828
- const source = details.items[0].subItems.items[i].source;
829
- rowEl = rowEls[i + 1];
830
- assert.ok(rowEl.classList.contains('lh-sub-item-row'));
831
- columnEl = rowEl.querySelector('td');
832
- if (typeof source === 'string') {
833
- assert.ok(columnEl.firstChild.classList.contains('lh-text__url'));
834
- assert.equal(rowEl.textContent, source);
835
- } else {
836
- assert.ok(columnEl.firstChild.classList.contains('lh-code'));
837
- assert.equal(columnEl.textContent, source.value);
838
- }
839
- }
840
- });
841
- });
842
- });
843
- });