lighthouse 8.5.1 → 8.6.0-dev.20211016
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.
- package/changelog.md +5490 -0
- package/dist/report/flow.js +35 -12
- package/dist/report/standalone.js +20 -21
- package/flow-report/.eslintrc.cjs +49 -0
- package/flow-report/assets/styles.css +272 -9
- package/flow-report/jest.config.js +1 -1
- package/flow-report/package.json +4 -0
- package/flow-report/src/app.tsx +31 -9
- package/flow-report/src/common.tsx +86 -7
- package/flow-report/src/header.tsx +88 -0
- package/flow-report/src/help-dialog.tsx +120 -0
- package/flow-report/src/i18n/i18n.tsx +52 -0
- package/flow-report/src/i18n/localized-strings.js +11 -0
- package/flow-report/src/i18n/ui-strings.js +134 -0
- package/flow-report/src/icons.tsx +20 -3
- package/flow-report/src/sidebar/sidebar.tsx +11 -16
- package/flow-report/src/summary/category.tsx +39 -24
- package/flow-report/src/summary/summary.tsx +35 -32
- package/flow-report/src/topbar.tsx +54 -4
- package/flow-report/src/util.ts +26 -1
- package/flow-report/src/wrappers/report-renderer.tsx +1 -1
- package/flow-report/src/wrappers/report.tsx +6 -21
- package/flow-report/test/app-test.tsx +17 -13
- package/flow-report/test/common-test.tsx +89 -0
- package/flow-report/test/header-test.tsx +55 -0
- package/flow-report/test/sample-flow.ts +17 -0
- package/flow-report/test/setup/env-setup.ts +1 -0
- package/flow-report/test/sidebar/flow-test.tsx +6 -12
- package/flow-report/test/sidebar/sidebar-test.tsx +7 -13
- package/flow-report/test/summary/category-test.tsx +83 -34
- package/flow-report/test/summary/summary-test.tsx +12 -18
- package/flow-report/test/topbar-test.tsx +68 -0
- package/flow-report/test/util-test.tsx +1 -12
- package/flow-report/tsconfig.json +2 -0
- package/flow-report/types/flow-report.d.ts +2 -1
- package/jest.config.js +1 -15
- package/lighthouse-cli/bin.js +5 -0
- package/lighthouse-cli/cli-flags.js +10 -3
- package/lighthouse-cli/commands/commands.js +1 -0
- package/lighthouse-cli/commands/list-locales.js +16 -0
- package/lighthouse-cli/run.js +2 -2
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +0 -0
- package/lighthouse-cli/test/smokehouse/report-assert.js +20 -4
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -1
- package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
- package/lighthouse-core/audits/seo/crawlable-anchors.js +10 -12
- package/lighthouse-core/computed/js-bundles.js +1 -2
- package/lighthouse-core/computed/unused-javascript-summary.js +3 -3
- package/lighthouse-core/config/config-helpers.js +1 -0
- package/lighthouse-core/config/config.js +2 -2
- package/lighthouse-core/config/default-config.js +5 -0
- package/lighthouse-core/fraggle-rock/api.js +10 -0
- package/lighthouse-core/fraggle-rock/config/filters.js +22 -3
- package/lighthouse-core/fraggle-rock/gather/driver.js +4 -0
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +2 -2
- package/lighthouse-core/fraggle-rock/user-flow.js +35 -6
- package/lighthouse-core/gather/driver/navigation.js +5 -0
- package/lighthouse-core/gather/driver/prepare.js +14 -0
- package/lighthouse-core/gather/driver/storage.js +5 -0
- package/lighthouse-core/gather/gather-runner.js +2 -2
- package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +5 -1
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +30 -24
- package/lighthouse-core/lib/cdt/Platform.js +55 -0
- package/lighthouse-core/lib/cdt/SDK.js +3 -123
- package/lighthouse-core/lib/cdt/generated/SourceMap.js +148 -312
- package/lighthouse-core/lib/csp-evaluator.js +2 -1
- package/lighthouse-core/lib/i18n/README.md +6 -6
- package/lighthouse-core/lib/i18n/i18n.js +26 -348
- package/lighthouse-core/lib/page-functions.js +4 -4
- package/lighthouse-core/lib/stack-packs.js +1 -13
- package/lighthouse-core/runner.js +6 -6
- package/lighthouse-core/scripts/manual-chrome-launcher.js +4 -1
- package/lighthouse-core/scripts/package.json +4 -0
- package/lighthouse-core/util-commonjs.js +20 -4
- package/package.json +19 -16
- package/readme.md +1 -1
- package/report/assets/standalone-template.html +0 -1
- package/report/assets/styles.css +16 -41
- package/report/assets/templates.html +42 -4
- package/report/clients/psi.js +1 -0
- package/report/clients/standalone.js +1 -2
- package/report/generator/file-namer.js +17 -8
- package/report/generator/report-generator.js +0 -1
- package/report/renderer/category-renderer.js +4 -16
- package/report/renderer/components.js +107 -71
- package/report/renderer/dom.js +19 -0
- package/report/renderer/logger.js +35 -2
- package/report/renderer/performance-category-renderer.js +23 -21
- package/report/renderer/pwa-category-renderer.js +1 -2
- package/report/renderer/report-renderer.js +21 -0
- package/report/renderer/report-ui-features.js +7 -19
- package/report/renderer/swap-locale-feature.js +82 -0
- package/report/renderer/util.js +20 -4
- package/report/test/clients/psi-test.js +0 -4
- package/report/test/generator/file-namer-test.js +2 -2
- package/report/test/renderer/category-renderer-test.js +1 -1
- package/report/test/renderer/performance-category-renderer-test.js +12 -0
- package/report/test/renderer/report-renderer-axe-test.js +86 -0
- package/report/test/renderer/report-renderer-test.js +0 -55
- package/report/test/renderer/util-test.js +68 -8
- package/report/test-assets/faux-psi-template.html +0 -1
- package/report/tsconfig.json +2 -1
- package/report/types/html-renderer.d.ts +2 -1
- package/root.js +18 -0
- package/shared/localization/format.js +455 -0
- package/{lighthouse-core/lib/cdt/Common.js → shared/localization/i18n-module.js} +3 -8
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ar-XB.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ar.json +141 -6
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/bg.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ca.json +140 -5
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/cs.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/da.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/de.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/el.json +141 -6
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-GB.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-US.json +129 -0
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-XA.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-XL.json +129 -0
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/es-419.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/es.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/fi.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/fil.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/fr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/he.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/hi.json +142 -7
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/hr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/hu.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/id.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/it.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ja.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ko.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/lt.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/lv.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/nl.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/no.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/pl.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/pt-PT.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/pt.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ro.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ru.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sk.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sl.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sr-Latn.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sv.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ta.json +146 -11
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/te.json +179 -44
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/th.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/tr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/uk.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/vi.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh-HK.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh-TW.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales.js +8 -1
- package/shared/localization/swap-flow-locale.js +20 -0
- package/{lighthouse-core/lib/i18n → shared/localization}/swap-locale.js +9 -7
- package/shared/test/localization/format-test.js +421 -0
- package/shared/test/localization/locales-test.js +47 -0
- package/shared/test/localization/swap-locale-test.js +140 -0
- package/shared/tsconfig.json +22 -0
- package/{lighthouse-core/lib → shared}/type-verifiers.js +0 -0
- package/shared/types/shared.d.ts +24 -0
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +5 -3
- package/third-party/chromium-synchronization/installability-errors-test.js +2 -1
- package/tsconfig-all.json +1 -0
- package/tsconfig-base.json +1 -1
- package/tsconfig.json +2 -3
- package/types/config.d.ts +1 -0
- package/types/es-main.d.ts +18 -0
- package/types/externs.d.ts +2 -0
- package/types/lhr/flow.d.ts +7 -0
- package/types/lhr/i18n.d.ts +0 -3
|
@@ -4,65 +4,114 @@
|
|
|
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
|
|
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 {
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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=
|
|
41
|
+
<SummaryTooltip category={category} gatherMode="snapshot"/>,
|
|
42
|
+
{wrapper}
|
|
32
43
|
);
|
|
33
44
|
|
|
34
|
-
|
|
35
|
-
expect(
|
|
36
|
-
|
|
37
|
-
expect(root.getByText('
|
|
45
|
+
expect(root.getByText('Average')).toBeTruthy();
|
|
46
|
+
expect(() => root.getByText(/^[0-9]+$/)).toThrow();
|
|
47
|
+
expect(root.getByText('2 audits passed')).toBeTruthy();
|
|
48
|
+
expect(root.getByText('3 passable audits')).toBeTruthy();
|
|
38
49
|
});
|
|
39
50
|
|
|
40
51
|
it('renders tooltip without rating', async () => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
const category: any = {
|
|
53
|
+
id: 'performance',
|
|
54
|
+
score: 1,
|
|
55
|
+
auditRefs: [
|
|
56
|
+
{result: {score: 1, scoreDisplayMode: 'binary'}, weight: 0, group: 'diagnostics'},
|
|
57
|
+
{result: {score: 1, scoreDisplayMode: 'binary'}, weight: 0, group: 'diagnostics'},
|
|
58
|
+
{result: {score: 0, scoreDisplayMode: 'binary'}, weight: 0, group: 'diagnostics'},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
44
61
|
|
|
45
62
|
const root = render(
|
|
46
|
-
<SummaryTooltip category={category} gatherMode=
|
|
63
|
+
<SummaryTooltip category={category} gatherMode="snapshot"/>,
|
|
64
|
+
{wrapper}
|
|
47
65
|
);
|
|
48
66
|
|
|
49
|
-
expect(() => root.
|
|
50
|
-
expect(root.getByText(
|
|
67
|
+
expect(() => root.getByText(/^(Average|Good|Poor)$/)).toThrow();
|
|
68
|
+
expect(() => root.getByText(/^[0-9]+$/)).toThrow();
|
|
69
|
+
expect(root.getByText('2 audits passed')).toBeTruthy();
|
|
70
|
+
expect(root.getByText('3 passable audits')).toBeTruthy();
|
|
51
71
|
});
|
|
52
72
|
|
|
53
73
|
it('renders scored category tooltip with score', async () => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
const category: any = {
|
|
75
|
+
id: 'performance',
|
|
76
|
+
score: 1,
|
|
77
|
+
auditRefs: [
|
|
78
|
+
{result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
|
|
79
|
+
{result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
|
|
80
|
+
{result: {score: 0, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
57
83
|
|
|
58
84
|
const root = render(
|
|
59
|
-
<SummaryTooltip category={category} gatherMode=
|
|
85
|
+
<SummaryTooltip category={category} gatherMode="navigation"/>,
|
|
86
|
+
{wrapper}
|
|
60
87
|
);
|
|
61
88
|
|
|
62
|
-
|
|
63
|
-
expect(
|
|
64
|
-
expect(
|
|
89
|
+
expect(root.getByText('Good')).toBeTruthy();
|
|
90
|
+
expect(root.getByText('100')).toBeTruthy();
|
|
91
|
+
expect(root.getByText('2 audits passed')).toBeTruthy();
|
|
92
|
+
expect(root.getByText('3 passable audits')).toBeTruthy();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('renders informative audit count if any', async () => {
|
|
96
|
+
const category: any = {
|
|
97
|
+
id: 'performance',
|
|
98
|
+
score: 1,
|
|
99
|
+
auditRefs: [
|
|
100
|
+
{result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
|
|
101
|
+
{result: {score: 1, scoreDisplayMode: 'binary'}, weight: 1, group: 'diagnostics'},
|
|
102
|
+
{result: {score: 0, scoreDisplayMode: 'informative'}, weight: 1, group: 'diagnostics'},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const root = render(
|
|
107
|
+
<SummaryTooltip category={category} gatherMode="navigation"/>,
|
|
108
|
+
{wrapper}
|
|
109
|
+
);
|
|
65
110
|
|
|
66
|
-
expect(root.getByText('
|
|
111
|
+
expect(root.getByText('Good')).toBeTruthy();
|
|
112
|
+
expect(root.getByText('100')).toBeTruthy();
|
|
113
|
+
expect(root.getByText('2 audits passed')).toBeTruthy();
|
|
114
|
+
expect(root.getByText('2 passable audits')).toBeTruthy();
|
|
115
|
+
expect(root.getByText('1 informative audit')).toBeTruthy();
|
|
67
116
|
});
|
|
68
117
|
});
|
|
@@ -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
|
-
|
|
22
|
+
<I18nProvider>
|
|
23
|
+
{children}
|
|
24
|
+
</I18nProvider>
|
|
33
25
|
</ReportRendererProvider>
|
|
34
26
|
</FlowResultContext.Provider>
|
|
35
27
|
);
|
|
@@ -42,7 +34,7 @@ describe('SummaryHeader', () => {
|
|
|
42
34
|
const lhrCounts = root.getByText(/·/);
|
|
43
35
|
expect(root.getByText('Summary')).toBeTruthy();
|
|
44
36
|
expect(lhrCounts.textContent).toEqual(
|
|
45
|
-
'2 navigation reports · 1 timespan
|
|
37
|
+
'2 navigation reports · 1 timespan report · 1 snapshot report'
|
|
46
38
|
);
|
|
47
39
|
});
|
|
48
40
|
});
|
|
@@ -59,7 +51,8 @@ describe('SummaryFlowStep', () => {
|
|
|
59
51
|
|
|
60
52
|
expect(root.getByText('Navigation (1)')).toBeTruthy();
|
|
61
53
|
|
|
62
|
-
const screenshot =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
'**/
|
|
20
|
+
'**/shared/**/*-test.js',
|
|
35
21
|
],
|
|
36
22
|
transform: {},
|
|
37
23
|
prettierPath: null,
|
package/lighthouse-cli/bin.js
CHANGED
|
@@ -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 {
|
|
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;
|
|
@@ -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};
|
package/lighthouse-cli/run.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
140
|
+
getLhrFilenamePrefix(lhr) :
|
|
141
141
|
flags.outputPath.replace(/\.\w{2,4}$/, '');
|
|
142
142
|
const resolvedPath = path.resolve(cwd, configuredPath);
|
|
143
143
|
|
|
File without changes
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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.
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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)';
|
|
@@ -283,6 +283,7 @@ function resolveSettings(settingsJson = {}, overrides = undefined) {
|
|
|
283
283
|
// If a locale is requested in flags or settings, use it. A typical CLI run will not have one,
|
|
284
284
|
// however `lookupLocale` will always determine which of our supported locales to use (falling
|
|
285
285
|
// back if necessary).
|
|
286
|
+
// TODO: could do more work to sniff out the user's locale
|
|
286
287
|
const locale = i18n.lookupLocale((overrides && overrides.locale) || settingsJson.locale);
|
|
287
288
|
|
|
288
289
|
// Fill in missing settings with defaults
|