lighthouse 8.5.1-dev.20211012 → 8.5.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 (172) hide show
  1. package/dist/report/flow.js +149 -0
  2. package/dist/report/standalone.js +228 -0
  3. package/flow-report/assets/styles.css +9 -272
  4. package/flow-report/jest.config.js +1 -1
  5. package/flow-report/src/app.tsx +9 -31
  6. package/flow-report/src/common.tsx +7 -86
  7. package/flow-report/src/icons.tsx +3 -20
  8. package/flow-report/src/sidebar/sidebar.tsx +16 -11
  9. package/flow-report/src/summary/category.tsx +24 -42
  10. package/flow-report/src/summary/summary.tsx +29 -32
  11. package/flow-report/src/topbar.tsx +4 -54
  12. package/flow-report/src/util.ts +1 -26
  13. package/flow-report/src/wrappers/report-renderer.tsx +1 -1
  14. package/flow-report/src/wrappers/report.tsx +21 -6
  15. package/flow-report/test/app-test.tsx +13 -17
  16. package/flow-report/test/setup/env-setup.ts +0 -1
  17. package/flow-report/test/sidebar/flow-test.tsx +12 -6
  18. package/flow-report/test/sidebar/sidebar-test.tsx +13 -7
  19. package/flow-report/test/summary/category-test.tsx +34 -79
  20. package/flow-report/test/summary/summary-test.tsx +17 -11
  21. package/flow-report/test/util-test.tsx +12 -1
  22. package/flow-report/tsconfig.json +0 -1
  23. package/flow-report/types/flow-report.d.ts +1 -2
  24. package/jest.config.js +15 -1
  25. package/lighthouse-cli/bin.js +0 -5
  26. package/lighthouse-cli/cli-flags.js +3 -10
  27. package/lighthouse-cli/commands/commands.js +0 -1
  28. package/lighthouse-cli/run.js +2 -2
  29. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +0 -0
  30. package/lighthouse-cli/test/smokehouse/report-assert.js +4 -20
  31. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +1 -2
  32. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
  33. package/lighthouse-core/audits/seo/crawlable-anchors.js +12 -10
  34. package/lighthouse-core/computed/js-bundles.js +2 -1
  35. package/lighthouse-core/computed/unused-javascript-summary.js +3 -3
  36. package/lighthouse-core/config/config.js +2 -2
  37. package/lighthouse-core/config/default-config.js +0 -5
  38. package/lighthouse-core/fraggle-rock/api.js +0 -10
  39. package/lighthouse-core/fraggle-rock/config/filters.js +3 -22
  40. package/lighthouse-core/fraggle-rock/gather/driver.js +0 -4
  41. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +2 -2
  42. package/lighthouse-core/fraggle-rock/user-flow.js +6 -35
  43. package/lighthouse-core/gather/driver/navigation.js +0 -5
  44. package/lighthouse-core/gather/driver/prepare.js +0 -14
  45. package/lighthouse-core/gather/driver/storage.js +0 -5
  46. package/lighthouse-core/gather/gather-runner.js +2 -2
  47. package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +1 -5
  48. package/lighthouse-core/gather/gatherers/full-page-screenshot.js +24 -30
  49. package/{shared/localization/i18n-module.js → lighthouse-core/lib/cdt/Common.js} +8 -3
  50. package/lighthouse-core/lib/cdt/SDK.js +123 -3
  51. package/lighthouse-core/lib/cdt/generated/SourceMap.js +312 -148
  52. package/lighthouse-core/lib/csp-evaluator.js +1 -2
  53. package/lighthouse-core/lib/i18n/README.md +6 -6
  54. package/lighthouse-core/lib/i18n/i18n.js +342 -9
  55. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ar-XB.json +0 -0
  56. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ar.json +0 -0
  57. package/{shared/localization → lighthouse-core/lib/i18n}/locales/bg.json +0 -0
  58. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ca.json +0 -0
  59. package/{shared/localization → lighthouse-core/lib/i18n}/locales/cs.json +0 -0
  60. package/{shared/localization → lighthouse-core/lib/i18n}/locales/da.json +0 -0
  61. package/{shared/localization → lighthouse-core/lib/i18n}/locales/de.json +0 -0
  62. package/{shared/localization → lighthouse-core/lib/i18n}/locales/el.json +0 -0
  63. package/{shared/localization → lighthouse-core/lib/i18n}/locales/en-GB.json +0 -0
  64. package/{shared/localization → lighthouse-core/lib/i18n}/locales/en-US.json +0 -111
  65. package/{shared/localization → lighthouse-core/lib/i18n}/locales/en-XA.json +0 -0
  66. package/{shared/localization → lighthouse-core/lib/i18n}/locales/en-XL.json +0 -111
  67. package/{shared/localization → lighthouse-core/lib/i18n}/locales/es-419.json +0 -0
  68. package/{shared/localization → lighthouse-core/lib/i18n}/locales/es.json +0 -0
  69. package/{shared/localization → lighthouse-core/lib/i18n}/locales/fi.json +0 -0
  70. package/{shared/localization → lighthouse-core/lib/i18n}/locales/fil.json +0 -0
  71. package/{shared/localization → lighthouse-core/lib/i18n}/locales/fr.json +0 -0
  72. package/{shared/localization → lighthouse-core/lib/i18n}/locales/he.json +0 -0
  73. package/{shared/localization → lighthouse-core/lib/i18n}/locales/hi.json +0 -0
  74. package/{shared/localization → lighthouse-core/lib/i18n}/locales/hr.json +0 -0
  75. package/{shared/localization → lighthouse-core/lib/i18n}/locales/hu.json +0 -0
  76. package/{shared/localization → lighthouse-core/lib/i18n}/locales/id.json +0 -0
  77. package/{shared/localization → lighthouse-core/lib/i18n}/locales/it.json +0 -0
  78. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ja.json +0 -0
  79. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ko.json +0 -0
  80. package/{shared/localization → lighthouse-core/lib/i18n}/locales/lt.json +0 -0
  81. package/{shared/localization → lighthouse-core/lib/i18n}/locales/lv.json +0 -0
  82. package/{shared/localization → lighthouse-core/lib/i18n}/locales/nl.json +0 -0
  83. package/{shared/localization → lighthouse-core/lib/i18n}/locales/no.json +0 -0
  84. package/{shared/localization → lighthouse-core/lib/i18n}/locales/pl.json +0 -0
  85. package/{shared/localization → lighthouse-core/lib/i18n}/locales/pt-PT.json +0 -0
  86. package/{shared/localization → lighthouse-core/lib/i18n}/locales/pt.json +0 -0
  87. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ro.json +0 -0
  88. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ru.json +0 -0
  89. package/{shared/localization → lighthouse-core/lib/i18n}/locales/sk.json +0 -0
  90. package/{shared/localization → lighthouse-core/lib/i18n}/locales/sl.json +0 -0
  91. package/{shared/localization → lighthouse-core/lib/i18n}/locales/sr-Latn.json +0 -0
  92. package/{shared/localization → lighthouse-core/lib/i18n}/locales/sr.json +0 -0
  93. package/{shared/localization → lighthouse-core/lib/i18n}/locales/sv.json +0 -0
  94. package/{shared/localization → lighthouse-core/lib/i18n}/locales/ta.json +0 -0
  95. package/{shared/localization → lighthouse-core/lib/i18n}/locales/te.json +0 -0
  96. package/{shared/localization → lighthouse-core/lib/i18n}/locales/th.json +0 -0
  97. package/{shared/localization → lighthouse-core/lib/i18n}/locales/tr.json +0 -0
  98. package/{shared/localization → lighthouse-core/lib/i18n}/locales/uk.json +0 -0
  99. package/{shared/localization → lighthouse-core/lib/i18n}/locales/vi.json +0 -0
  100. package/{shared/localization → lighthouse-core/lib/i18n}/locales/zh-HK.json +0 -0
  101. package/{shared/localization → lighthouse-core/lib/i18n}/locales/zh-TW.json +0 -0
  102. package/{shared/localization → lighthouse-core/lib/i18n}/locales/zh.json +0 -0
  103. package/{shared/localization → lighthouse-core/lib/i18n}/locales.js +1 -2
  104. package/{shared/localization → lighthouse-core/lib/i18n}/swap-locale.js +7 -9
  105. package/lighthouse-core/lib/page-functions.js +4 -4
  106. package/lighthouse-core/lib/stack-packs.js +13 -1
  107. package/{shared → lighthouse-core/lib}/type-verifiers.js +0 -0
  108. package/lighthouse-core/runner.js +6 -6
  109. package/lighthouse-core/scripts/manual-chrome-launcher.js +1 -4
  110. package/lighthouse-core/util-commonjs.js +4 -20
  111. package/package.json +16 -19
  112. package/readme.md +1 -1
  113. package/report/assets/standalone-template.html +1 -0
  114. package/report/assets/styles.css +41 -16
  115. package/report/assets/templates.html +4 -42
  116. package/report/clients/psi.js +0 -1
  117. package/report/clients/standalone.js +2 -1
  118. package/report/generator/file-namer.js +8 -17
  119. package/report/generator/report-generator.js +1 -0
  120. package/report/renderer/category-renderer.js +16 -4
  121. package/report/renderer/components.js +71 -107
  122. package/report/renderer/dom.js +0 -19
  123. package/report/renderer/logger.js +2 -35
  124. package/report/renderer/performance-category-renderer.js +21 -23
  125. package/report/renderer/pwa-category-renderer.js +2 -1
  126. package/report/renderer/report-renderer.js +0 -21
  127. package/report/renderer/report-ui-features.js +19 -7
  128. package/report/renderer/util.js +4 -20
  129. package/report/test/clients/psi-test.js +4 -0
  130. package/report/test/generator/file-namer-test.js +2 -2
  131. package/report/test/renderer/category-renderer-test.js +1 -1
  132. package/report/test/renderer/performance-category-renderer-test.js +0 -12
  133. package/report/test/renderer/report-renderer-test.js +55 -0
  134. package/report/test/renderer/util-test.js +8 -68
  135. package/report/test-assets/faux-psi-template.html +1 -0
  136. package/report/tsconfig.json +1 -2
  137. package/report/types/html-renderer.d.ts +1 -2
  138. package/root.js +0 -18
  139. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +3 -5
  140. package/third-party/chromium-synchronization/installability-errors-test.js +1 -2
  141. package/tsconfig-all.json +0 -1
  142. package/tsconfig-base.json +1 -1
  143. package/tsconfig.json +3 -2
  144. package/types/config.d.ts +0 -1
  145. package/types/externs.d.ts +0 -2
  146. package/types/lhr/flow.d.ts +0 -7
  147. package/types/lhr/i18n.d.ts +3 -0
  148. package/changelog.md +0 -5395
  149. package/flow-report/.eslintrc.cjs +0 -49
  150. package/flow-report/package.json +0 -4
  151. package/flow-report/src/header.tsx +0 -88
  152. package/flow-report/src/help-dialog.tsx +0 -120
  153. package/flow-report/src/i18n/i18n.tsx +0 -44
  154. package/flow-report/src/i18n/localized-strings.js +0 -11
  155. package/flow-report/src/i18n/ui-strings.js +0 -86
  156. package/flow-report/test/common-test.tsx +0 -89
  157. package/flow-report/test/header-test.tsx +0 -55
  158. package/flow-report/test/sample-flow.ts +0 -17
  159. package/flow-report/test/topbar-test.tsx +0 -68
  160. package/lighthouse-cli/commands/list-locales.js +0 -16
  161. package/lighthouse-core/lib/cdt/Platform.js +0 -55
  162. package/lighthouse-core/scripts/package.json +0 -4
  163. package/report/renderer/swap-locale-feature.js +0 -82
  164. package/report/test/renderer/report-renderer-axe-test.js +0 -86
  165. package/shared/localization/format.js +0 -422
  166. package/shared/localization/swap-flow-locale.js +0 -20
  167. package/shared/test/localization/format-test.js +0 -377
  168. package/shared/test/localization/locales-test.js +0 -47
  169. package/shared/test/localization/swap-locale-test.js +0 -140
  170. package/shared/tsconfig.json +0 -22
  171. package/shared/types/shared.d.ts +0 -24
  172. package/types/es-main.d.ts +0 -18
@@ -9,47 +9,34 @@ import {FunctionComponent} from 'preact';
9
9
  import {Util} from '../../../report/renderer/util';
10
10
  import {Separator} from '../common';
11
11
  import {CategoryScore} from '../wrappers/category-score';
12
- import {useUIStrings} from '../i18n/i18n';
13
12
 
14
- import type {UIStringsType} from '../i18n/ui-strings';
15
-
16
- function getGatherModeLabel(gatherMode: LH.Result.GatherMode, strings: UIStringsType) {
17
- switch (gatherMode) {
18
- case 'navigation': return strings.navigationReport;
19
- case 'timespan': return strings.timespanReport;
20
- case 'snapshot': return strings.snapshotReport;
21
- }
22
- }
13
+ const GATHER_MODE_LABELS: Record<LH.Result.GatherMode, string> = {
14
+ 'navigation': 'Navigation report',
15
+ 'timespan': 'Timespan report',
16
+ 'snapshot': 'Snapshot report',
17
+ };
23
18
 
24
- function getCategoryRating(rating: string, strings: UIStringsType) {
25
- switch (rating) {
26
- case 'pass': return strings.ratingPass;
27
- case 'average': return strings.ratingAverage;
28
- case 'fail': return strings.ratingFail;
29
- case 'error': return strings.ratingError;
30
- }
31
- }
19
+ const RATING_LABELS: Record<string, string> = {
20
+ 'pass': 'Good',
21
+ 'fail': 'Poor',
22
+ 'average': 'Average',
23
+ 'error': 'Error',
24
+ };
32
25
 
33
26
  export const SummaryTooltip: FunctionComponent<{
34
27
  category: LH.ReportResult.Category,
35
28
  gatherMode: LH.Result.GatherMode
36
29
  }> = ({category, gatherMode}) => {
37
- const strings = useUIStrings();
38
- const {
39
- numPassed,
40
- numPassableAudits,
41
- numInformative,
42
- totalWeight,
43
- } = Util.calculateCategoryFraction(category);
30
+ const {numPassed, numAudits, totalWeight} = Util.calculateCategoryFraction(category);
44
31
 
45
32
  const displayAsFraction = Util.shouldDisplayAsFraction(gatherMode);
46
33
  const rating = displayAsFraction ?
47
- Util.calculateRating(numPassed / numPassableAudits) :
34
+ Util.calculateRating(numPassed / numAudits) :
48
35
  Util.calculateRating(category.score);
49
36
 
50
37
  return (
51
38
  <div className="SummaryTooltip">
52
- <div className="SummaryTooltip__title">{getGatherModeLabel(gatherMode, strings)}</div>
39
+ <div className="SummaryTooltip__title">{GATHER_MODE_LABELS[gatherMode]}</div>
53
40
  <Separator/>
54
41
  <div className="SummaryTooltip__category">
55
42
  <div className="SummaryTooltip__category-title">
@@ -57,8 +44,11 @@ export const SummaryTooltip: FunctionComponent<{
57
44
  </div>
58
45
  {
59
46
  totalWeight !== 0 &&
60
- <div className={`SummaryTooltip__rating SummaryTooltip__rating--${rating}`}>
61
- <span>{getCategoryRating(rating, strings)}</span>
47
+ <div
48
+ className={`SummaryTooltip__rating SummaryTooltip__rating--${rating}`}
49
+ data-testid="SummaryTooltip__rating"
50
+ >
51
+ <span>{RATING_LABELS[rating]}</span>
62
52
  {
63
53
  !displayAsFraction && category.score && <>
64
54
  <span> · </span>
@@ -69,19 +59,8 @@ export const SummaryTooltip: FunctionComponent<{
69
59
  }
70
60
  </div>
71
61
  <div className="SummaryTooltip__fraction">
72
- {
73
- // TODO(FLOW-I18N): Placeholder format.
74
- `${numPassed} audits passed / ${numPassableAudits} passable audits`
75
- }
62
+ {`${numPassed} audits passed / ${numAudits} audits run`}
76
63
  </div>
77
- {
78
- // TODO(FLOW-I18N): Placeholder format.
79
- numInformative ?
80
- <div className="SummaryTooltip__informative">
81
- {`${numInformative} informative audits`}
82
- </div> :
83
- null
84
- }
85
64
  </div>
86
65
  );
87
66
  };
@@ -103,7 +82,10 @@ export const SummaryCategory: FunctionComponent<{
103
82
  />
104
83
  <SummaryTooltip category={category} gatherMode={gatherMode}/>
105
84
  </div> :
106
- <div className="SummaryCategory__null" data-testid="SummaryCategory__null"/>
85
+ <div
86
+ className="SummaryCategory__null"
87
+ data-testid="SummaryCategory__null"
88
+ />
107
89
  }
108
90
  </div>
109
91
  );
@@ -7,36 +7,28 @@
7
7
  import {FunctionComponent} from 'preact';
8
8
  import {useMemo} from 'preact/hooks';
9
9
 
10
- import {FlowSegment, FlowStepThumbnail, Separator} from '../common';
11
- import {getModeDescription, useFlowResult} from '../util';
10
+ import {FlowSegment, Separator} from '../common';
11
+ import {getScreenDimensions, getScreenshot, useFlowResult} from '../util';
12
12
  import {Util} from '../../../report/renderer/util';
13
13
  import {SummaryCategory} from './category';
14
- import {useUIStrings} from '../i18n/i18n';
15
14
 
16
15
  const DISPLAYED_CATEGORIES = ['performance', 'accessibility', 'best-practices', 'seo'];
17
16
  const THUMBNAIL_WIDTH = 50;
17
+ const MODE_DESCRIPTIONS: Record<LH.Result.GatherMode, string> = {
18
+ 'navigation': 'Page load',
19
+ 'timespan': 'User interactions',
20
+ 'snapshot': 'Captured state of page',
21
+ };
18
22
 
19
- const SummaryNavigationHeader: FunctionComponent<{lhr: LH.Result}> = ({lhr}) => {
20
- const strings = useUIStrings();
21
-
23
+ const SummaryNavigationHeader: FunctionComponent<{url: string}> = ({url}) => {
22
24
  return (
23
25
  <div className="SummaryNavigationHeader" data-testid="SummaryNavigationHeader">
24
26
  <FlowSegment/>
25
- <div className="SummaryNavigationHeader__url">
26
- <a rel="noopener" target="_blank" href={lhr.finalUrl}>{lhr.finalUrl}</a>
27
- </div>
28
- <div className="SummaryNavigationHeader__category">
29
- {strings.categoryPerformance}
30
- </div>
31
- <div className="SummaryNavigationHeader__category">
32
- {strings.categoryAccessibility}
33
- </div>
34
- <div className="SummaryNavigationHeader__category">
35
- {strings.categoryBestPractices}
36
- </div>
37
- <div className="SummaryNavigationHeader__category">
38
- {strings.categorySeo}
39
- </div>
27
+ <div className="SummaryNavigationHeader__url">{url}</div>
28
+ <div className="SummaryNavigationHeader__category">Performance</div>
29
+ <div className="SummaryNavigationHeader__category">Accessibility</div>
30
+ <div className="SummaryNavigationHeader__category">Best Practices</div>
31
+ <div className="SummaryNavigationHeader__category">SEO</div>
40
32
  </div>
41
33
  );
42
34
  };
@@ -50,23 +42,32 @@ export const SummaryFlowStep: FunctionComponent<{
50
42
  hashIndex: number,
51
43
  }> = ({lhr, label, hashIndex}) => {
52
44
  const reportResult = useMemo(() => Util.prepareReportResult(lhr), [lhr]);
53
- const strings = useUIStrings();
54
- const modeDescription = getModeDescription(lhr.gatherMode, strings);
45
+
46
+ const screenshot = reportResult.gatherMode !== 'timespan' ? getScreenshot(reportResult) : null;
47
+
48
+ // Crop the displayed image to the viewport dimensions.
49
+ const {width, height} = getScreenDimensions(reportResult);
50
+ const thumbnailHeight = height * THUMBNAIL_WIDTH / width;
55
51
 
56
52
  return (
57
53
  <div className="SummaryFlowStep">
58
54
  {
59
55
  lhr.gatherMode === 'navigation' || hashIndex === 0 ?
60
- <SummaryNavigationHeader lhr={lhr}/> :
56
+ <SummaryNavigationHeader url={lhr.finalUrl}/> :
61
57
  <div className="SummaryFlowStep__separator">
62
58
  <FlowSegment/>
63
59
  <Separator/>
64
60
  </div>
65
61
  }
66
- <FlowStepThumbnail reportResult={reportResult} width={THUMBNAIL_WIDTH}/>
62
+ <img
63
+ className="SummaryFlowStep__screenshot"
64
+ data-testid="SummaryFlowStep__screenshot"
65
+ src={screenshot || undefined}
66
+ style={{width: THUMBNAIL_WIDTH, maxHeight: thumbnailHeight}}
67
+ />
67
68
  <FlowSegment mode={lhr.gatherMode}/>
68
69
  <div className="SummaryFlowStep__label">
69
- <div className="SummaryFlowStep__mode">{modeDescription}</div>
70
+ <div className="SummaryFlowStep__mode">{MODE_DESCRIPTIONS[lhr.gatherMode]}</div>
70
71
  <a className="SummaryFlowStep__link" href={`#index=${hashIndex}`}>{label}</a>
71
72
  </div>
72
73
  {
@@ -107,7 +108,6 @@ const SummaryFlow: FunctionComponent = () => {
107
108
 
108
109
  export const SummaryHeader: FunctionComponent = () => {
109
110
  const flowResult = useFlowResult();
110
- const strings = useUIStrings();
111
111
 
112
112
  let numNavigation = 0;
113
113
  let numTimespan = 0;
@@ -126,7 +126,6 @@ export const SummaryHeader: FunctionComponent = () => {
126
126
  }
127
127
  }
128
128
 
129
- // TODO(FLOW-I18N): Placeholder format.
130
129
  const subtitleCounts = [];
131
130
  if (numNavigation) subtitleCounts.push(`${numNavigation} navigation reports`);
132
131
  if (numTimespan) subtitleCounts.push(`${numTimespan} timespan reports`);
@@ -135,7 +134,7 @@ export const SummaryHeader: FunctionComponent = () => {
135
134
 
136
135
  return (
137
136
  <div className="SummaryHeader">
138
- <div className="SummaryHeader__title">{strings.summary}</div>
137
+ <div className="SummaryHeader__title">Summary</div>
139
138
  <div className="SummaryHeader__subtitle">{subtitle}</div>
140
139
  </div>
141
140
  );
@@ -151,13 +150,11 @@ const SummarySectionHeader: FunctionComponent = ({children}) => {
151
150
  };
152
151
 
153
152
  export const Summary: FunctionComponent = () => {
154
- const strings = useUIStrings();
155
-
156
153
  return (
157
154
  <div className="Summary" data-testid="Summary">
158
155
  <SummaryHeader/>
159
156
  <Separator/>
160
- <SummarySectionHeader>{strings.allReports}</SummarySectionHeader>
157
+ <SummarySectionHeader>ALL REPORTS</SummarySectionHeader>
161
158
  <SummaryFlow/>
162
159
  </div>
163
160
  );
@@ -5,27 +5,8 @@
5
5
  */
6
6
 
7
7
  import {FunctionComponent, JSX} from 'preact';
8
- import {useState} from 'preact/hooks';
9
8
 
10
- import {HelpDialog} from './help-dialog';
11
- import {getFilenamePrefix} from '../../report/generator/file-namer';
12
- import {useUIStrings} from './i18n/i18n';
13
9
  import {HamburgerIcon} from './icons';
14
- import {useFlowResult} from './util';
15
- import {useReportRenderer} from './wrappers/report-renderer';
16
-
17
- import type {DOM} from '../../report/renderer/dom';
18
-
19
- function saveHtml(flowResult: LH.FlowResult, dom: DOM) {
20
- const htmlStr = document.documentElement.outerHTML;
21
- const blob = new Blob([htmlStr], {type: 'text/html'});
22
-
23
- const lhr = flowResult.steps[0].lhr;
24
- const name = flowResult.name.replace(/\s/g, '-');
25
- const filename = getFilenamePrefix(name, lhr.fetchTime);
26
-
27
- dom.saveFile(blob, filename);
28
- }
29
10
 
30
11
  /* eslint-disable max-len */
31
12
  const Logo: FunctionComponent = () => {
@@ -63,48 +44,17 @@ const Logo: FunctionComponent = () => {
63
44
  };
64
45
  /* eslint-enable max-len */
65
46
 
66
- const TopbarButton: FunctionComponent<{
67
- onClick: JSX.MouseEventHandler<HTMLButtonElement>,
68
- label: string,
69
- }> =
70
- ({onClick, label, children}) => {
71
- return (
72
- <button className="TopbarButton" onClick={onClick} aria-label={label}>
73
- {children}
74
- </button>
75
- );
76
- };
77
-
78
- export const Topbar: FunctionComponent<{onMenuClick: JSX.MouseEventHandler<HTMLButtonElement>}> =
47
+ export const Topbar: FunctionComponent<{onMenuClick: JSX.MouseEventHandler<HTMLDivElement>}> =
79
48
  ({onMenuClick}) => {
80
- const flowResult = useFlowResult();
81
- const {dom} = useReportRenderer();
82
- const strings = useUIStrings();
83
- const [showHelpDialog, setShowHelpDialog] = useState(false);
84
-
85
49
  return (
86
50
  <div className="Topbar">
87
- <TopbarButton onClick={onMenuClick} label="Button that opens and closes the sidebar">
51
+ <div className="Topbar__menu" onClick={onMenuClick} role="button">
88
52
  <HamburgerIcon/>
89
- </TopbarButton>
53
+ </div>
90
54
  <div className="Topbar__logo">
91
55
  <Logo/>
92
56
  </div>
93
- <div className="Topbar__title">{strings.title}</div>
94
- <TopbarButton
95
- onClick={() => saveHtml(flowResult, dom)}
96
- label="Button that saves the report as HTML"
97
- >{strings.save}</TopbarButton>
98
- <div style={{flexGrow: 1}} />
99
- <TopbarButton
100
- onClick={() => setShowHelpDialog(previous => !previous)}
101
- label="Button that toggles the help dialog">
102
- {strings.helpLabel}
103
- </TopbarButton>
104
- {showHelpDialog ?
105
- <HelpDialog onClose={() => setShowHelpDialog(false)} /> :
106
- null
107
- }
57
+ <div className="Topbar__title">Lighthouse User Flow Report</div>
108
58
  </div>
109
59
  );
110
60
  };
@@ -7,8 +7,6 @@
7
7
  import {createContext} from 'preact';
8
8
  import {useContext, useEffect, useMemo, useState} from 'preact/hooks';
9
9
 
10
- import type {UIStringsType} from './i18n/ui-strings';
11
-
12
10
  export const FlowResultContext = createContext<LH.FlowResult|undefined>(undefined);
13
11
 
14
12
  function getHashParam(param: string): string|null {
@@ -43,7 +41,6 @@ export function getScreenDimensions(reportResult: LH.ReportResult) {
43
41
  export function getScreenshot(reportResult: LH.ReportResult) {
44
42
  const fullPageScreenshotAudit = reportResult.audits['full-page-screenshot'];
45
43
  const fullPageScreenshot =
46
- fullPageScreenshotAudit &&
47
44
  fullPageScreenshotAudit.details &&
48
45
  fullPageScreenshotAudit.details.type === 'full-page-screenshot' &&
49
46
  fullPageScreenshotAudit.details.screenshot.data;
@@ -51,28 +48,6 @@ export function getScreenshot(reportResult: LH.ReportResult) {
51
48
  return fullPageScreenshot || null;
52
49
  }
53
50
 
54
- export function getFilmstripFrames(
55
- reportResult: LH.ReportResult
56
- ): Array<{data: string}> | undefined {
57
- const filmstripAudit = reportResult.audits['screenshot-thumbnails'];
58
- if (!filmstripAudit) return undefined;
59
-
60
- const frameItems =
61
- filmstripAudit.details &&
62
- filmstripAudit.details.type === 'filmstrip' &&
63
- filmstripAudit.details.items;
64
-
65
- return frameItems || undefined;
66
- }
67
-
68
- export function getModeDescription(mode: LH.Result.GatherMode, strings: UIStringsType) {
69
- switch (mode) {
70
- case 'navigation': return strings.navigationDescription;
71
- case 'timespan': return strings.timespanDescription;
72
- case 'snapshot': return strings.snapshotDescription;
73
- }
74
- }
75
-
76
51
  export function useFlowResult(): LH.FlowResult {
77
52
  const flowResult = useContext(FlowResultContext);
78
53
  if (!flowResult) throw Error('useFlowResult must be called in the FlowResultContext');
@@ -102,7 +77,7 @@ export function useHashParam(param: string) {
102
77
  return paramValue;
103
78
  }
104
79
 
105
- export function useCurrentLhr(): LH.FlowResult.LhrRef|null {
80
+ export function useCurrentLhr(): {value: LH.Result, index: number}|null {
106
81
  const flowResult = useFlowResult();
107
82
  const indexString = useHashParam('index');
108
83
 
@@ -19,7 +19,7 @@ interface ReportRendererGlobals {
19
19
  reportRenderer: ReportRenderer,
20
20
  }
21
21
 
22
- export const ReportRendererContext = createContext<ReportRendererGlobals|undefined>(undefined);
22
+ const ReportRendererContext = createContext<ReportRendererGlobals|undefined>(undefined);
23
23
 
24
24
  export function useReportRenderer() {
25
25
  const globals = useContext(ReportRendererContext);
@@ -5,8 +5,9 @@
5
5
  */
6
6
 
7
7
  import {FunctionComponent} from 'preact';
8
- import {useLayoutEffect, useRef} from 'preact/hooks';
8
+ import {useEffect, useLayoutEffect, useRef} from 'preact/hooks';
9
9
 
10
+ import {useCurrentLhr, useHashParam} from '../util';
10
11
  import {useReportRenderer} from './report-renderer';
11
12
 
12
13
  /**
@@ -15,7 +16,7 @@ import {useReportRenderer} from './report-renderer';
15
16
  * e.g. <a href="#link"> -> <a href="#index=0&anchor=link">
16
17
  */
17
18
  export function convertChildAnchors(element: HTMLElement, index: number) {
18
- const links = element.querySelectorAll('a[href]') as NodeListOf<HTMLAnchorElement>;
19
+ const links = element.querySelectorAll('a') as NodeListOf<HTMLAnchorElement>;
19
20
  for (const link of links) {
20
21
  // Check if the link destination is in the report.
21
22
  const currentUrl = new URL(location.href);
@@ -31,18 +32,19 @@ export function convertChildAnchors(element: HTMLElement, index: number) {
31
32
  }
32
33
  }
33
34
 
34
- export const Report: FunctionComponent<{currentLhr: LH.FlowResult.LhrRef}> =
35
- ({currentLhr}) => {
35
+ export const Report: FunctionComponent = () => {
36
36
  const {dom, reportRenderer} = useReportRenderer();
37
37
  const ref = useRef<HTMLDivElement>(null);
38
+ const anchor = useHashParam('anchor');
39
+ const currentLhr = useCurrentLhr();
38
40
 
39
41
  useLayoutEffect(() => {
42
+ if (!currentLhr) return;
43
+
40
44
  if (ref.current) {
41
45
  dom.clearComponentCache();
42
46
  reportRenderer.renderReport(currentLhr.value, ref.current);
43
47
  convertChildAnchors(ref.current, currentLhr.index);
44
- const topbar = ref.current.querySelector('.lh-topbar');
45
- if (topbar) topbar.remove();
46
48
  }
47
49
 
48
50
  return () => {
@@ -50,6 +52,19 @@ export const Report: FunctionComponent<{currentLhr: LH.FlowResult.LhrRef}> =
50
52
  };
51
53
  }, [reportRenderer, currentLhr]);
52
54
 
55
+ useEffect(() => {
56
+ if (anchor) {
57
+ const el = document.getElementById(anchor);
58
+ if (el) {
59
+ el.scrollIntoView({behavior: 'smooth'});
60
+ return;
61
+ }
62
+ }
63
+
64
+ // Scroll to top no anchor is found.
65
+ if (ref.current) ref.current.scrollIntoView();
66
+ }, [anchor, currentLhr]);
67
+
53
68
  return (
54
69
  <div ref={ref} className="lh-root" data-testid="Report"/>
55
70
  );
@@ -4,10 +4,21 @@
4
4
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
5
  */
6
6
 
7
- import {act, render} from '@testing-library/preact';
7
+ import fs from 'fs';
8
+ import {dirname} from 'path';
9
+ import {fileURLToPath} from 'url';
10
+
11
+ import {render} from '@testing-library/preact';
8
12
 
9
13
  import {App} from '../src/app';
10
- import {flowResult} from './sample-flow';
14
+
15
+ const flowResult = JSON.parse(
16
+ fs.readFileSync(
17
+ // eslint-disable-next-line max-len
18
+ `${dirname(fileURLToPath(import.meta.url))}/../../lighthouse-core/test/fixtures/fraggle-rock/reports/sample-lhrs.json`,
19
+ 'utf-8'
20
+ )
21
+ );
11
22
 
12
23
  it('renders a standalone report with summary', async () => {
13
24
  const root = render(<App flowResult={flowResult}/>);
@@ -35,18 +46,3 @@ it('renders the snapshot step', async () => {
35
46
 
36
47
  expect(root.getByTestId('Report')).toBeTruthy();
37
48
  });
38
-
39
- it('toggles collapsed mode when hamburger button clicked', async () => {
40
- const root = render(<App flowResult={flowResult}/>);
41
-
42
- const app = root.getByTestId('App');
43
- const hamburgerButton = root.getByLabelText('Button that opens and closes the sidebar');
44
-
45
- expect(app.classList).not.toContain('App--collapsed');
46
-
47
- await act(() => {
48
- hamburgerButton.click();
49
- });
50
-
51
- expect(app.classList).toContain('App--collapsed');
52
- });
@@ -17,7 +17,6 @@ export function setupJsDom() {
17
17
  global.window = window as any;
18
18
  global.document = window.document;
19
19
  global.location = window.location;
20
- global.Blob = window.Blob;
21
20
 
22
21
  // Function not implemented in JSDOM.
23
22
  window.Element.prototype.scrollIntoView = jest.fn();
@@ -1,15 +1,21 @@
1
- /**
2
- * @license Copyright 2021 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
- */
1
+ import fs from 'fs';
2
+ import {dirname} from 'path';
3
+ import {fileURLToPath} from 'url';
6
4
 
7
5
  import {render} from '@testing-library/preact';
8
6
  import {FunctionComponent} from 'preact';
9
7
 
10
8
  import {SidebarFlow} from '../../src/sidebar/flow';
11
9
  import {FlowResultContext} from '../../src/util';
12
- import {flowResult} from '../sample-flow';
10
+
11
+
12
+ const flowResult = JSON.parse(
13
+ fs.readFileSync(
14
+ // eslint-disable-next-line max-len
15
+ `${dirname(fileURLToPath(import.meta.url))}/../../../lighthouse-core/test/fixtures/fraggle-rock/reports/sample-lhrs.json`,
16
+ 'utf-8'
17
+ )
18
+ );
13
19
 
14
20
  let wrapper: FunctionComponent;
15
21
 
@@ -4,24 +4,30 @@
4
4
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
5
  */
6
6
 
7
+ import fs from 'fs';
8
+ import {dirname} from 'path';
9
+ import {fileURLToPath} from 'url';
10
+
7
11
  import {render} from '@testing-library/preact';
8
12
  import {FunctionComponent} from 'preact';
9
13
 
10
- import {I18nProvider} from '../../src/i18n/i18n';
11
14
  import {SidebarHeader, SidebarSummary} from '../../src/sidebar/sidebar';
12
15
  import {FlowResultContext} from '../../src/util';
13
- import {flowResult} from '../sample-flow';
14
16
 
15
17
 
18
+ const flowResult = JSON.parse(
19
+ fs.readFileSync(
20
+ // eslint-disable-next-line max-len
21
+ `${dirname(fileURLToPath(import.meta.url))}/../../../lighthouse-core/test/fixtures/fraggle-rock/reports/sample-lhrs.json`,
22
+ 'utf-8'
23
+ )
24
+ );
25
+
16
26
  let wrapper: FunctionComponent;
17
27
 
18
28
  beforeEach(() => {
19
29
  wrapper = ({children}) => (
20
- <FlowResultContext.Provider value={flowResult}>
21
- <I18nProvider>
22
- {children}
23
- </I18nProvider>
24
- </FlowResultContext.Provider>
30
+ <FlowResultContext.Provider value={flowResult}>{children}</FlowResultContext.Provider>
25
31
  );
26
32
  });
27
33