lighthouse 8.5.1 → 8.6.0-dev.20211013

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/changelog.md +5490 -0
  2. package/flow-report/.eslintrc.cjs +49 -0
  3. package/flow-report/assets/styles.css +272 -9
  4. package/flow-report/jest.config.js +1 -1
  5. package/flow-report/package.json +4 -0
  6. package/flow-report/src/app.tsx +31 -9
  7. package/flow-report/src/common.tsx +86 -7
  8. package/flow-report/src/header.tsx +88 -0
  9. package/flow-report/src/help-dialog.tsx +120 -0
  10. package/flow-report/src/i18n/i18n.tsx +44 -0
  11. package/flow-report/src/i18n/localized-strings.js +11 -0
  12. package/flow-report/src/i18n/ui-strings.js +86 -0
  13. package/flow-report/src/icons.tsx +20 -3
  14. package/flow-report/src/sidebar/sidebar.tsx +11 -16
  15. package/flow-report/src/summary/category.tsx +42 -24
  16. package/flow-report/src/summary/summary.tsx +32 -29
  17. package/flow-report/src/topbar.tsx +54 -4
  18. package/flow-report/src/util.ts +26 -1
  19. package/flow-report/src/wrappers/report-renderer.tsx +1 -1
  20. package/flow-report/src/wrappers/report.tsx +6 -21
  21. package/flow-report/test/app-test.tsx +17 -13
  22. package/flow-report/test/common-test.tsx +89 -0
  23. package/flow-report/test/header-test.tsx +55 -0
  24. package/flow-report/test/sample-flow.ts +17 -0
  25. package/flow-report/test/setup/env-setup.ts +1 -0
  26. package/flow-report/test/sidebar/flow-test.tsx +6 -12
  27. package/flow-report/test/sidebar/sidebar-test.tsx +7 -13
  28. package/flow-report/test/summary/category-test.tsx +79 -34
  29. package/flow-report/test/summary/summary-test.tsx +11 -17
  30. package/flow-report/test/topbar-test.tsx +68 -0
  31. package/flow-report/test/util-test.tsx +1 -12
  32. package/flow-report/tsconfig.json +1 -0
  33. package/flow-report/types/flow-report.d.ts +2 -1
  34. package/jest.config.js +1 -15
  35. package/lighthouse-cli/bin.js +5 -0
  36. package/lighthouse-cli/cli-flags.js +10 -3
  37. package/lighthouse-cli/commands/commands.js +1 -0
  38. package/lighthouse-cli/commands/list-locales.js +16 -0
  39. package/lighthouse-cli/run.js +2 -2
  40. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +0 -0
  41. package/lighthouse-cli/test/smokehouse/report-assert.js +20 -4
  42. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -1
  43. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
  44. package/lighthouse-core/audits/seo/crawlable-anchors.js +10 -12
  45. package/lighthouse-core/computed/js-bundles.js +1 -2
  46. package/lighthouse-core/computed/unused-javascript-summary.js +3 -3
  47. package/lighthouse-core/config/config.js +2 -2
  48. package/lighthouse-core/config/default-config.js +5 -0
  49. package/lighthouse-core/fraggle-rock/api.js +10 -0
  50. package/lighthouse-core/fraggle-rock/config/filters.js +22 -3
  51. package/lighthouse-core/fraggle-rock/gather/driver.js +4 -0
  52. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +2 -2
  53. package/lighthouse-core/fraggle-rock/user-flow.js +35 -6
  54. package/lighthouse-core/gather/driver/navigation.js +5 -0
  55. package/lighthouse-core/gather/driver/prepare.js +14 -0
  56. package/lighthouse-core/gather/driver/storage.js +5 -0
  57. package/lighthouse-core/gather/gather-runner.js +2 -2
  58. package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +5 -1
  59. package/lighthouse-core/gather/gatherers/full-page-screenshot.js +30 -24
  60. package/lighthouse-core/lib/cdt/Platform.js +55 -0
  61. package/lighthouse-core/lib/cdt/SDK.js +3 -123
  62. package/lighthouse-core/lib/cdt/generated/SourceMap.js +148 -312
  63. package/lighthouse-core/lib/csp-evaluator.js +2 -1
  64. package/lighthouse-core/lib/i18n/README.md +6 -6
  65. package/lighthouse-core/lib/i18n/i18n.js +9 -342
  66. package/lighthouse-core/lib/page-functions.js +4 -4
  67. package/lighthouse-core/lib/stack-packs.js +1 -13
  68. package/lighthouse-core/runner.js +6 -6
  69. package/lighthouse-core/scripts/manual-chrome-launcher.js +4 -1
  70. package/lighthouse-core/scripts/package.json +4 -0
  71. package/lighthouse-core/util-commonjs.js +20 -4
  72. package/package.json +19 -16
  73. package/readme.md +1 -1
  74. package/report/assets/standalone-template.html +0 -1
  75. package/report/assets/styles.css +16 -41
  76. package/report/assets/templates.html +42 -4
  77. package/report/clients/psi.js +1 -0
  78. package/report/clients/standalone.js +1 -2
  79. package/report/generator/file-namer.js +17 -8
  80. package/report/generator/report-generator.js +0 -1
  81. package/report/renderer/category-renderer.js +4 -16
  82. package/report/renderer/components.js +107 -71
  83. package/report/renderer/dom.js +19 -0
  84. package/report/renderer/logger.js +35 -2
  85. package/report/renderer/performance-category-renderer.js +23 -21
  86. package/report/renderer/pwa-category-renderer.js +1 -2
  87. package/report/renderer/report-renderer.js +21 -0
  88. package/report/renderer/report-ui-features.js +7 -19
  89. package/report/renderer/swap-locale-feature.js +82 -0
  90. package/report/renderer/util.js +20 -4
  91. package/report/test/clients/psi-test.js +0 -4
  92. package/report/test/generator/file-namer-test.js +2 -2
  93. package/report/test/renderer/category-renderer-test.js +1 -1
  94. package/report/test/renderer/performance-category-renderer-test.js +12 -0
  95. package/report/test/renderer/report-renderer-axe-test.js +86 -0
  96. package/report/test/renderer/report-renderer-test.js +0 -55
  97. package/report/test/renderer/util-test.js +68 -8
  98. package/report/test-assets/faux-psi-template.html +0 -1
  99. package/report/tsconfig.json +2 -1
  100. package/report/types/html-renderer.d.ts +2 -1
  101. package/root.js +18 -0
  102. package/shared/localization/format.js +422 -0
  103. package/{lighthouse-core/lib/cdt/Common.js → shared/localization/i18n-module.js} +3 -8
  104. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ar-XB.json +139 -4
  105. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ar.json +141 -6
  106. package/{lighthouse-core/lib/i18n → shared/localization}/locales/bg.json +139 -4
  107. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ca.json +140 -5
  108. package/{lighthouse-core/lib/i18n → shared/localization}/locales/cs.json +139 -4
  109. package/{lighthouse-core/lib/i18n → shared/localization}/locales/da.json +139 -4
  110. package/{lighthouse-core/lib/i18n → shared/localization}/locales/de.json +139 -4
  111. package/{lighthouse-core/lib/i18n → shared/localization}/locales/el.json +141 -6
  112. package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-GB.json +139 -4
  113. package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-US.json +111 -0
  114. package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-XA.json +139 -4
  115. package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-XL.json +111 -0
  116. package/{lighthouse-core/lib/i18n → shared/localization}/locales/es-419.json +139 -4
  117. package/{lighthouse-core/lib/i18n → shared/localization}/locales/es.json +139 -4
  118. package/{lighthouse-core/lib/i18n → shared/localization}/locales/fi.json +139 -4
  119. package/{lighthouse-core/lib/i18n → shared/localization}/locales/fil.json +139 -4
  120. package/{lighthouse-core/lib/i18n → shared/localization}/locales/fr.json +139 -4
  121. package/{lighthouse-core/lib/i18n → shared/localization}/locales/he.json +139 -4
  122. package/{lighthouse-core/lib/i18n → shared/localization}/locales/hi.json +142 -7
  123. package/{lighthouse-core/lib/i18n → shared/localization}/locales/hr.json +139 -4
  124. package/{lighthouse-core/lib/i18n → shared/localization}/locales/hu.json +139 -4
  125. package/{lighthouse-core/lib/i18n → shared/localization}/locales/id.json +139 -4
  126. package/{lighthouse-core/lib/i18n → shared/localization}/locales/it.json +139 -4
  127. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ja.json +139 -4
  128. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ko.json +139 -4
  129. package/{lighthouse-core/lib/i18n → shared/localization}/locales/lt.json +139 -4
  130. package/{lighthouse-core/lib/i18n → shared/localization}/locales/lv.json +139 -4
  131. package/{lighthouse-core/lib/i18n → shared/localization}/locales/nl.json +139 -4
  132. package/{lighthouse-core/lib/i18n → shared/localization}/locales/no.json +139 -4
  133. package/{lighthouse-core/lib/i18n → shared/localization}/locales/pl.json +139 -4
  134. package/{lighthouse-core/lib/i18n → shared/localization}/locales/pt-PT.json +139 -4
  135. package/{lighthouse-core/lib/i18n → shared/localization}/locales/pt.json +139 -4
  136. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ro.json +139 -4
  137. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ru.json +139 -4
  138. package/{lighthouse-core/lib/i18n → shared/localization}/locales/sk.json +139 -4
  139. package/{lighthouse-core/lib/i18n → shared/localization}/locales/sl.json +139 -4
  140. package/{lighthouse-core/lib/i18n → shared/localization}/locales/sr-Latn.json +139 -4
  141. package/{lighthouse-core/lib/i18n → shared/localization}/locales/sr.json +139 -4
  142. package/{lighthouse-core/lib/i18n → shared/localization}/locales/sv.json +139 -4
  143. package/{lighthouse-core/lib/i18n → shared/localization}/locales/ta.json +146 -11
  144. package/{lighthouse-core/lib/i18n → shared/localization}/locales/te.json +179 -44
  145. package/{lighthouse-core/lib/i18n → shared/localization}/locales/th.json +139 -4
  146. package/{lighthouse-core/lib/i18n → shared/localization}/locales/tr.json +139 -4
  147. package/{lighthouse-core/lib/i18n → shared/localization}/locales/uk.json +139 -4
  148. package/{lighthouse-core/lib/i18n → shared/localization}/locales/vi.json +139 -4
  149. package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh-HK.json +139 -4
  150. package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh-TW.json +139 -4
  151. package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh.json +139 -4
  152. package/{lighthouse-core/lib/i18n → shared/localization}/locales.js +2 -1
  153. package/shared/localization/swap-flow-locale.js +20 -0
  154. package/{lighthouse-core/lib/i18n → shared/localization}/swap-locale.js +9 -7
  155. package/shared/test/localization/format-test.js +377 -0
  156. package/shared/test/localization/locales-test.js +47 -0
  157. package/shared/test/localization/swap-locale-test.js +140 -0
  158. package/shared/tsconfig.json +22 -0
  159. package/{lighthouse-core/lib → shared}/type-verifiers.js +0 -0
  160. package/shared/types/shared.d.ts +24 -0
  161. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +5 -3
  162. package/third-party/chromium-synchronization/installability-errors-test.js +2 -1
  163. package/tsconfig-all.json +1 -0
  164. package/tsconfig-base.json +1 -1
  165. package/tsconfig.json +2 -3
  166. package/types/config.d.ts +1 -0
  167. package/types/es-main.d.ts +18 -0
  168. package/types/externs.d.ts +2 -0
  169. package/types/lhr/flow.d.ts +7 -0
  170. package/types/lhr/i18n.d.ts +0 -3
  171. package/dist/report/flow.js +0 -149
  172. package/dist/report/standalone.js +0 -228
@@ -4,65 +4,110 @@
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
+ import {FunctionComponent} from 'preact';
11
8
  import {render} from '@testing-library/preact';
12
9
 
13
10
  import {SummaryTooltip} from '../../src/summary/category';
14
- import {Util} from '../../../report/renderer/util';
11
+ import {flowResult} from '../sample-flow';
12
+ import {I18nProvider} from '../../src/i18n/i18n';
13
+ import {FlowResultContext} from '../../src/util';
14
+
15
+ let wrapper: FunctionComponent;
15
16
 
16
- const flowResult: LH.FlowResult = JSON.parse(
17
- fs.readFileSync(
18
- // eslint-disable-next-line max-len
19
- `${dirname(fileURLToPath(import.meta.url))}/../../../lighthouse-core/test/fixtures/fraggle-rock/reports/sample-lhrs.json`,
20
- 'utf-8'
21
- )
22
- );
17
+ beforeEach(() => {
18
+ // Include sample flowResult for locale in I18nProvider.
19
+ wrapper = ({children}) => (
20
+ <FlowResultContext.Provider value={flowResult}>
21
+ <I18nProvider>
22
+ {children}
23
+ </I18nProvider>
24
+ </FlowResultContext.Provider>
25
+ );
26
+ });
23
27
 
24
28
  describe('SummaryTooltip', () => {
25
29
  it('renders tooltip with rating', async () => {
26
- // Snapshot SEO
27
- const reportResult = Util.prepareReportResult(flowResult.steps[2].lhr);
28
- const category = reportResult.categories['seo'];
30
+ const category: any = {
31
+ id: 'performance',
32
+ score: 1,
33
+ auditRefs: [
34
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
35
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
36
+ {result: {score: 0, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
37
+ ],
38
+ };
29
39
 
30
40
  const root = render(
31
- <SummaryTooltip category={category} gatherMode={reportResult.gatherMode}/>
41
+ <SummaryTooltip category={category} gatherMode="snapshot"/>,
42
+ {wrapper}
32
43
  );
33
44
 
34
- const rating = root.getByTestId('SummaryTooltip__rating');
35
- expect(rating.classList).toContain('SummaryTooltip__rating--average');
36
-
37
- expect(root.getByText('9 audits passed / 11 audits run')).toBeTruthy();
45
+ expect(root.getByText('Average')).toBeTruthy();
46
+ expect(() => root.getByText(/^[0-9]+$/)).toThrow();
47
+ expect(root.getByText('2 audits passed / 3 passable audits')).toBeTruthy();
38
48
  });
39
49
 
40
50
  it('renders tooltip without rating', async () => {
41
- // Snapshot performance
42
- const reportResult = Util.prepareReportResult(flowResult.steps[2].lhr);
43
- const category = reportResult.categories['performance'];
51
+ const category: any = {
52
+ id: 'performance',
53
+ score: 1,
54
+ auditRefs: [
55
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 0, group: 'diagnostics'},
56
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 0, group: 'diagnostics'},
57
+ {result: {score: 0, scoreDisplayMode: 'binary'}, weight: 0, group: 'diagnostics'},
58
+ ],
59
+ };
44
60
 
45
61
  const root = render(
46
- <SummaryTooltip category={category} gatherMode={reportResult.gatherMode}/>
62
+ <SummaryTooltip category={category} gatherMode="snapshot"/>,
63
+ {wrapper}
47
64
  );
48
65
 
49
- expect(() => root.getByTestId('SummaryTooltip__rating')).toThrow();
50
- expect(root.getByText('2 audits passed / 4 audits run')).toBeTruthy();
66
+ expect(() => root.getByText(/^(Average|Good|Poor)$/)).toThrow();
67
+ expect(() => root.getByText(/^[0-9]+$/)).toThrow();
68
+ expect(root.getByText('2 audits passed / 3 passable audits')).toBeTruthy();
51
69
  });
52
70
 
53
71
  it('renders scored category tooltip with score', async () => {
54
- // Navigation performance
55
- const reportResult = Util.prepareReportResult(flowResult.steps[0].lhr);
56
- const category = reportResult.categories['performance'];
72
+ const category: any = {
73
+ id: 'performance',
74
+ score: 1,
75
+ auditRefs: [
76
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
77
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
78
+ {result: {score: 0, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
79
+ ],
80
+ };
57
81
 
58
82
  const root = render(
59
- <SummaryTooltip category={category} gatherMode={reportResult.gatherMode}/>
83
+ <SummaryTooltip category={category} gatherMode="navigation"/>,
84
+ {wrapper}
60
85
  );
61
86
 
62
- const rating = root.getByTestId('SummaryTooltip__rating');
63
- expect(rating.classList).toContain('SummaryTooltip__rating--pass');
64
- expect(rating.textContent).toEqual('Good · 94');
87
+ expect(root.getByText('Good')).toBeTruthy();
88
+ expect(root.getByText('100')).toBeTruthy();
89
+ expect(root.getByText('2 audits passed / 3 passable audits')).toBeTruthy();
90
+ });
91
+
92
+ it('renders informative audit count if any', async () => {
93
+ const category: any = {
94
+ id: 'performance',
95
+ score: 1,
96
+ auditRefs: [
97
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
98
+ {result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
99
+ {result: {score: 0, scoreDisplayMode: 'informative'}, weight: 1, group: 'diagnostics'},
100
+ ],
101
+ };
102
+
103
+ const root = render(
104
+ <SummaryTooltip category={category} gatherMode="navigation"/>,
105
+ {wrapper}
106
+ );
65
107
 
66
- expect(root.getByText('41 audits passed / 58 audits run')).toBeTruthy();
108
+ expect(root.getByText('Good')).toBeTruthy();
109
+ expect(root.getByText('100')).toBeTruthy();
110
+ expect(root.getByText('2 audits passed / 2 passable audits')).toBeTruthy();
111
+ expect(root.getByText('1 informative audits')).toBeTruthy();
67
112
  });
68
113
  });
@@ -4,24 +4,14 @@
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
-
11
7
  import {render} from '@testing-library/preact';
12
8
  import {FunctionComponent} from 'preact';
13
9
 
10
+ import {I18nProvider} from '../../src/i18n/i18n';
14
11
  import {SummaryHeader, SummaryFlowStep} from '../../src/summary/summary';
15
12
  import {FlowResultContext} from '../../src/util';
16
13
  import {ReportRendererProvider} from '../../src/wrappers/report-renderer';
17
-
18
- const flowResult: LH.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
- );
14
+ import {flowResult} from '../sample-flow';
25
15
 
26
16
  let wrapper: FunctionComponent;
27
17
 
@@ -29,7 +19,9 @@ beforeEach(() => {
29
19
  wrapper = ({children}) => (
30
20
  <FlowResultContext.Provider value={flowResult}>
31
21
  <ReportRendererProvider>
32
- {children}
22
+ <I18nProvider>
23
+ {children}
24
+ </I18nProvider>
33
25
  </ReportRendererProvider>
34
26
  </FlowResultContext.Provider>
35
27
  );
@@ -59,7 +51,8 @@ describe('SummaryFlowStep', () => {
59
51
 
60
52
  expect(root.getByText('Navigation (1)')).toBeTruthy();
61
53
 
62
- const screenshot = root.getByTestId('SummaryFlowStep__screenshot') as HTMLImageElement;
54
+ const screenshot =
55
+ root.getByAltText('Screenshot of a page tested by Lighthouse') as HTMLImageElement;
63
56
  expect(screenshot.src).toMatch(/data:image\/jpeg;base64/);
64
57
 
65
58
  const gauges = root.getAllByTestId('CategoryScore');
@@ -67,6 +60,7 @@ describe('SummaryFlowStep', () => {
67
60
 
68
61
  const links = root.getAllByRole('link') as HTMLAnchorElement[];
69
62
  expect(links.map(a => a.href)).toEqual([
63
+ 'https://www.mikescerealshack.co/',
70
64
  'file:///Users/example/report.html/#index=0',
71
65
  'file:///Users/example/report.html/#index=0&anchor=performance',
72
66
  'file:///Users/example/report.html/#index=0&anchor=accessibility',
@@ -86,8 +80,7 @@ describe('SummaryFlowStep', () => {
86
80
 
87
81
  expect(root.getByText('Timespan (1)')).toBeTruthy();
88
82
 
89
- const screenshot = root.getByTestId('SummaryFlowStep__screenshot') as HTMLImageElement;
90
- expect(screenshot.src).toBeFalsy();
83
+ expect(() => root.getByAltText('Screenshot of a page tested by Lighthouse')).toThrow();
91
84
 
92
85
  // Accessibility and SEO are missing in timespan.
93
86
  const nullCategories = root.getAllByTestId('SummaryCategory__null');
@@ -115,7 +108,8 @@ describe('SummaryFlowStep', () => {
115
108
 
116
109
  expect(root.getByText('Snapshot (1)')).toBeTruthy();
117
110
 
118
- const screenshot = root.getByTestId('SummaryFlowStep__screenshot') as HTMLImageElement;
111
+ const screenshot =
112
+ root.getByAltText('Screenshot of a page tested by Lighthouse') as HTMLImageElement;
119
113
  expect(screenshot.src).toMatch(/data:image\/jpeg;base64/);
120
114
 
121
115
  const gauges = root.getAllByTestId('CategoryScore');
@@ -0,0 +1,68 @@
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
+ */
6
+
7
+ import {jest} from '@jest/globals';
8
+ import {FunctionComponent} from 'preact';
9
+ import {act, render} from '@testing-library/preact';
10
+
11
+ import {Topbar} from '../src/topbar';
12
+ import {FlowResultContext} from '../src/util';
13
+ import {ReportRendererContext} from '../src/wrappers/report-renderer';
14
+ import {I18nProvider} from '../src/i18n/i18n';
15
+
16
+ const flowResult = {
17
+ name: 'User flow',
18
+ steps: [{lhr: {
19
+ fetchTime: '2021-09-14T22:24:22.462Z',
20
+ configSettings: {locale: 'en-US'},
21
+ }}],
22
+ } as any;
23
+
24
+ let wrapper: FunctionComponent;
25
+ let mockSaveFile = jest.fn();
26
+
27
+ beforeEach(() => {
28
+ mockSaveFile = jest.fn();
29
+ const reportRendererValue: any = {
30
+ dom: {
31
+ saveFile: mockSaveFile,
32
+ },
33
+ };
34
+ wrapper = ({children}) => (
35
+ <FlowResultContext.Provider value={flowResult}>
36
+ <ReportRendererContext.Provider value={reportRendererValue}>
37
+ <I18nProvider>
38
+ {children}
39
+ </I18nProvider>
40
+ </ReportRendererContext.Provider>
41
+ </FlowResultContext.Provider>
42
+ );
43
+ });
44
+
45
+ it('save button opens save dialog for HTML file', async () => {
46
+ const root = render(<Topbar onMenuClick={() => {}}/>, {wrapper});
47
+
48
+ const saveButton = root.getByText('Save');
49
+ saveButton.click();
50
+
51
+ expect(mockSaveFile).toHaveBeenCalledWith(
52
+ expect.any(Blob),
53
+ 'User-flow_2021-09-14_22-24-22'
54
+ );
55
+ });
56
+
57
+ it('toggles help dialog', async () => {
58
+ const root = render(<Topbar onMenuClick={() => {}}/>, {wrapper});
59
+
60
+ expect(root.queryByText(/Use Navigation reports to/)).toBeFalsy();
61
+ const helpButton = root.getByText('Understanding Flows');
62
+
63
+ await act(() => helpButton.click());
64
+ expect(root.getByText(/Use Navigation reports to/)).toBeTruthy();
65
+
66
+ await act(() => helpButton.click());
67
+ expect(root.queryByText(/Use Navigation reports to/)).toBeFalsy();
68
+ });
@@ -4,24 +4,13 @@
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
-
11
7
  import {jest} from '@jest/globals';
12
8
  import {renderHook} from '@testing-library/preact-hooks';
13
9
  import {FunctionComponent} from 'preact';
14
10
  import {act} from 'preact/test-utils';
15
11
 
16
12
  import {FlowResultContext, useCurrentLhr} from '../src/util';
17
-
18
- const flowResult: LH.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
- );
13
+ import {flowResult} from './sample-flow';
25
14
 
26
15
  let wrapper: FunctionComponent;
27
16
 
@@ -16,6 +16,7 @@
16
16
  {"path": "../report"},
17
17
  ],
18
18
  "include": [
19
+ "**/*.js",
19
20
  "**/*.ts",
20
21
  "**/*.tsx",
21
22
  "./types",
@@ -19,8 +19,9 @@ declare global {
19
19
 
20
20
  // Expose global types in LH namespace.
21
21
  module LH {
22
- export type FlowResult = FlowResult_;
23
22
  export type ConfigSettings = Settings.ConfigSettings;
23
+
24
+ export import FlowResult = FlowResult_;
24
25
  }
25
26
  }
26
27
 
package/jest.config.js CHANGED
@@ -6,18 +6,6 @@
6
6
  'use strict';
7
7
 
8
8
  module.exports = {
9
- collectCoverage: false,
10
- coverageReporters: ['none'],
11
- collectCoverageFrom: [
12
- '**/lighthouse-core/**/*.js',
13
- '**/lighthouse-cli/**/*.js',
14
- '**/report/**/*.js',
15
- '**/lighthouse-viewer/**/*.js',
16
- ],
17
- coveragePathIgnorePatterns: [
18
- '/test/',
19
- '/scripts/',
20
- ],
21
9
  setupFilesAfterEnv: ['./lighthouse-core/test/test-utils.js'],
22
10
  testEnvironment: 'node',
23
11
  testMatch: [
@@ -26,12 +14,10 @@ module.exports = {
26
14
  '**/report/**/*-test.js',
27
15
  '**/lighthouse-core/test/fraggle-rock/**/*-test-pptr.js',
28
16
  '**/lighthouse-treemap/**/*-test.js',
29
- '**/lighthouse-treemap/**/*-test-pptr.js',
30
17
  '**/lighthouse-viewer/**/*-test.js',
31
- '**/lighthouse-viewer/**/*-test-pptr.js',
32
18
  '**/third-party/**/*-test.js',
33
19
  '**/clients/test/**/*-test.js',
34
- '**/docs/**/*.test.js',
20
+ '**/shared/**/*-test.js',
35
21
  ],
36
22
  transform: {},
37
23
  prettierPath: null,
@@ -61,6 +61,11 @@ async function begin() {
61
61
  commands.listAudits();
62
62
  }
63
63
 
64
+ // Process terminating command
65
+ if (cliFlags.listLocales) {
66
+ commands.listLocales();
67
+ }
68
+
64
69
  // Process terminating command
65
70
  if (cliFlags.listTraceCategories) {
66
71
  commands.listTraceCategories();
@@ -12,7 +12,8 @@ import fs from 'fs';
12
12
  import yargs from 'yargs';
13
13
  import * as yargsHelpers from 'yargs/helpers';
14
14
 
15
- import {isObjectOfUnknownValues} from '../lighthouse-core/lib/type-verifiers.js';
15
+ import {LH_ROOT} from '../root.js';
16
+ import {isObjectOfUnknownValues} from '../shared/type-verifiers.js';
16
17
 
17
18
  /**
18
19
  * @param {string=} manualArgv
@@ -26,6 +27,7 @@ function getFlags(manualArgv, options = {}) {
26
27
  yargs(yargsHelpers.hideBin(process.argv));
27
28
 
28
29
  let parser = y.help('help')
30
+ .version(JSON.parse(fs.readFileSync(`${LH_ROOT}/package.json`, 'utf-8')).version)
29
31
  .showHelpOnFail(false, 'Specify --help for available options')
30
32
 
31
33
  .usage('lighthouse <url> <options>')
@@ -103,6 +105,11 @@ function getFlags(manualArgv, options = {}) {
103
105
  default: false,
104
106
  describe: 'Prints a list of all available audits and exits',
105
107
  },
108
+ 'list-locales': {
109
+ type: 'boolean',
110
+ default: false,
111
+ describe: 'Prints a list of all supported locales and exits',
112
+ },
106
113
  'list-trace-categories': {
107
114
  type: 'boolean',
108
115
  default: false,
@@ -208,7 +215,7 @@ function getFlags(manualArgv, options = {}) {
208
215
  },
209
216
  })
210
217
  .group([
211
- 'save-assets', 'list-all-audits', 'list-trace-categories', 'print-config', 'additional-trace-categories',
218
+ 'save-assets', 'list-all-audits', 'list-locales', 'list-trace-categories', 'print-config', 'additional-trace-categories',
212
219
  'config-path', 'preset', 'chrome-flags', 'port', 'hostname', 'form-factor', 'screenEmulation', 'emulatedUserAgent',
213
220
  'max-wait-for-load', 'enable-error-reporting', 'gather-mode', 'audit-mode',
214
221
  'only-audits', 'only-categories', 'skip-audits', 'budget-path',
@@ -312,7 +319,7 @@ function getFlags(manualArgv, options = {}) {
312
319
  // - We're just printing the config.
313
320
  // - We're in auditMode (and we have artifacts already)
314
321
  // If one of these don't apply, if no URL, stop the program and ask for one.
315
- const isPrintSomethingMode = argv.listAllAudits || argv.listTraceCategories || argv.printConfig;
322
+ const isPrintSomethingMode = argv.listAllAudits || argv.listLocales || argv.listTraceCategories || argv.printConfig;
316
323
  const isOnlyAuditMode = !!argv.auditMode && !argv.gatherMode;
317
324
  if (isPrintSomethingMode || isOnlyAuditMode) {
318
325
  return true;
@@ -7,3 +7,4 @@
7
7
 
8
8
  export {listAudits} from './list-audits.js';
9
9
  export {listTraceCategories} from './list-trace-categories.js';
10
+ export {listLocales} from './list-locales.js';
@@ -0,0 +1,16 @@
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
+ */
6
+ 'use strict';
7
+
8
+ import locales from '../../shared/localization/locales.js';
9
+
10
+ function listLocales() {
11
+ const localesList = Object.keys(locales);
12
+ process.stdout.write(JSON.stringify({locales: localesList}, null, 2));
13
+ process.exit(0);
14
+ }
15
+
16
+ export {listLocales};
@@ -17,7 +17,7 @@ import open from 'open';
17
17
 
18
18
  import * as Printer from './printer.js';
19
19
  import lighthouse from '../lighthouse-core/index.js';
20
- import {getFilenamePrefix} from '../report/generator/file-namer.js';
20
+ import {getLhrFilenamePrefix} from '../report/generator/file-namer.js';
21
21
  import * as assetSaver from '../lighthouse-core/lib/asset-saver.js';
22
22
  import URL from '../lighthouse-core/lib/url-shim.js';
23
23
 
@@ -137,7 +137,7 @@ async function saveResults(runnerResult, flags) {
137
137
  // Use the output path as the prefix for all generated files.
138
138
  // If no output path is set, generate a file prefix using the URL and date.
139
139
  const configuredPath = !flags.outputPath || flags.outputPath === 'stdout' ?
140
- getFilenamePrefix(lhr) :
140
+ getLhrFilenamePrefix(lhr) :
141
141
  flags.outputPath.replace(/\.\w{2,4}$/, '');
142
142
  const resolvedPath = path.resolve(cwd, configuredPath);
143
143
 
@@ -200,26 +200,42 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
200
200
  JSON.stringify(value, null, 2),
201
201
  `Actual Chromium version: ${getChromeVersion()}`,
202
202
  ].join(' '));
203
- delete obj[key];
203
+ if (Array.isArray(obj)) {
204
+ obj.splice(Number(key), 1);
205
+ } else {
206
+ delete obj[key];
207
+ }
204
208
  } else if (value._legacyOnly && isFraggleRock) {
205
209
  localConsole.log([
206
210
  `[${key}] marked legacy only but run is Fraggle Rock, pruning expectation:`,
207
211
  JSON.stringify(value, null, 2),
208
212
  ].join(' '));
209
- delete obj[key];
213
+ if (Array.isArray(obj)) {
214
+ obj.splice(Number(key), 1);
215
+ } else {
216
+ delete obj[key];
217
+ }
210
218
  } else if (value._fraggleRockOnly && !isFraggleRock) {
211
219
  localConsole.log([
212
220
  `[${key}] marked Fraggle Rock only but run is legacy, pruning expectation:`,
213
221
  JSON.stringify(value, null, 2),
214
222
  `Actual channel: ${lhr.configSettings.channel}`,
215
223
  ].join(' '));
216
- delete obj[key];
224
+ if (Array.isArray(obj)) {
225
+ obj.splice(Number(key), 1);
226
+ } else {
227
+ delete obj[key];
228
+ }
217
229
  } else if (value._skipInBundled && !isBundled) {
218
230
  localConsole.log([
219
231
  `[${key}] marked as skip in bundled and runner is bundled, pruning expectation:`,
220
232
  JSON.stringify(value, null, 2),
221
233
  ].join(' '));
222
- delete obj[key];
234
+ if (Array.isArray(obj)) {
235
+ obj.splice(Number(key), 1);
236
+ } else {
237
+ delete obj[key];
238
+ }
223
239
  } else {
224
240
  pruneRecursively(value);
225
241
  }
@@ -115,11 +115,12 @@ class UnusedBytes extends Audit {
115
115
  settings,
116
116
  };
117
117
  const networkRecords = await NetworkRecords.request(devtoolsLog, context);
118
+ const hasContentfulRecords = networkRecords.some(record => record.transferSize);
118
119
 
119
120
  // Requesting load simulator requires non-empty network records.
120
121
  // Timespans are not guaranteed to have any network activity.
121
122
  // There are no bytes to be saved if no bytes were downloaded, so mark N/A if empty.
122
- if (!networkRecords.length && gatherContext.gatherMode === 'timespan') {
123
+ if (!hasContentfulRecords && gatherContext.gatherMode === 'timespan') {
123
124
  return {
124
125
  score: 1,
125
126
  notApplicable: true,
@@ -128,7 +128,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
128
128
  })
129
129
  .filter(d => d.unused >= bundleSourceUnusedThreshold);
130
130
 
131
- const commonSourcePrefix = commonPrefix([...bundle.map._sourceInfos.keys()]);
131
+ const commonSourcePrefix = commonPrefix([...bundle.map.sourceInfos.keys()]);
132
132
  item.subItems = {
133
133
  type: 'subitems',
134
134
  items: topUnusedSourceSizes.map(({source, unused, total}) => {
@@ -31,7 +31,7 @@ class CrawlableAnchors extends Audit {
31
31
  title: str_(UIStrings.title),
32
32
  failureTitle: str_(UIStrings.failureTitle),
33
33
  description: str_(UIStrings.description),
34
- requiredArtifacts: ['AnchorElements'],
34
+ requiredArtifacts: ['AnchorElements', 'URL'],
35
35
  };
36
36
  }
37
37
 
@@ -39,15 +39,12 @@ class CrawlableAnchors extends Audit {
39
39
  * @param {LH.Artifacts} artifacts
40
40
  * @return {LH.Audit.Product}
41
41
  */
42
- static audit({AnchorElements: anchorElements}) {
42
+ static audit({AnchorElements: anchorElements, URL: url}) {
43
43
  const failingAnchors = anchorElements.filter(({
44
44
  rawHref,
45
- listeners = [],
46
- onclick = '',
47
45
  name = '',
48
46
  role = '',
49
47
  }) => {
50
- onclick = onclick.replace( /\s/g, '');
51
48
  rawHref = rawHref.replace( /\s/g, '');
52
49
  name = name.trim();
53
50
  role = role.trim();
@@ -56,19 +53,20 @@ class CrawlableAnchors extends Audit {
56
53
  // Ignore mailto links even if they use one of the failing patterns. See https://github.com/GoogleChrome/lighthouse/issues/11443#issuecomment-694898412
57
54
  if (rawHref.startsWith('mailto:')) return;
58
55
 
59
- const windowLocationRegExp = /window\.location=/;
60
- const windowOpenRegExp = /window\.open\(/;
61
56
  const javaScriptVoidRegExp = /javascript:void(\(|)0(\)|)/;
62
57
 
63
58
  if (rawHref.startsWith('file:')) return true;
64
- if (windowLocationRegExp.test(onclick)) return true;
65
- if (windowOpenRegExp.test(onclick)) return true;
66
-
67
- const hasClickHandler = listeners.some(({type}) => type === 'click');
68
- if (hasClickHandler || name.length > 0) return;
59
+ if (name.length > 0) return;
69
60
 
70
61
  if (rawHref === '') return true;
71
62
  if (javaScriptVoidRegExp.test(rawHref)) return true;
63
+
64
+ // checking if rawHref is a valid
65
+ try {
66
+ new URL(rawHref, url.finalUrl);
67
+ } catch (e) {
68
+ return true;
69
+ }
72
70
  });
73
71
 
74
72
  /** @type {LH.Audit.Details.Table['headings']} */
@@ -29,8 +29,7 @@ function computeGeneratedFileSizes(map, content) {
29
29
  const source = mapping.sourceURL;
30
30
  const lineNum = mapping.lineNumber;
31
31
  const colNum = mapping.columnNumber;
32
- // @ts-expect-error: `lastColumnNumber` is not on types yet. This will eventually be added to CDT.
33
- const lastColNum = /** @type {number=} */ (mapping.lastColumnNumber);
32
+ const lastColNum = mapping.lastColumnNumber;
34
33
 
35
34
  // Webpack sometimes emits null mappings.
36
35
  // https://github.com/mozilla/source-map/pull/303
@@ -127,9 +127,9 @@ class UnusedJavascriptSummary {
127
127
  let offset = lineOffsets[mapping.lineNumber];
128
128
 
129
129
  offset += mapping.columnNumber;
130
- const lastColumnOfMapping =
131
- // @ts-expect-error: We will upstream lastColumnNumber to CDT eventually.
132
- (mapping.lastColumnNumber - 1) || lineLengths[mapping.lineNumber];
130
+ const lastColumnOfMapping = mapping.lastColumnNumber !== undefined ?
131
+ mapping.lastColumnNumber - 1 :
132
+ lineLengths[mapping.lineNumber];
133
133
  for (let i = mapping.columnNumber; i <= lastColumnOfMapping; i++) {
134
134
  if (wasteData.every(data => data.unusedByIndex[offset] === 1)) {
135
135
  const key = mapping.sourceURL || '(unmapped)';
@@ -8,7 +8,7 @@
8
8
  const defaultConfigPath = './default-config.js';
9
9
  const defaultConfig = require('./default-config.js');
10
10
  const constants = require('./constants.js');
11
- const i18n = require('./../lib/i18n/i18n.js');
11
+ const format = require('../../shared/localization/format.js');
12
12
  const validation = require('./../fraggle-rock/config/validation.js');
13
13
 
14
14
  const log = require('lighthouse-logger');
@@ -248,7 +248,7 @@ class Config {
248
248
  }
249
249
 
250
250
  // Printed config is more useful with localized strings.
251
- i18n.replaceIcuMessages(jsonConfig, jsonConfig.settings.locale);
251
+ format.replaceIcuMessages(jsonConfig, jsonConfig.settings.locale);
252
252
 
253
253
  return JSON.stringify(jsonConfig, null, 2);
254
254
  }