lighthouse 10.2.0 → 10.3.0-dev.20230614
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/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +4 -6
- package/core/audits/accessibility/html-xml-lang-mismatch.d.ts +10 -0
- package/core/audits/accessibility/html-xml-lang-mismatch.js +47 -0
- package/core/audits/accessibility/input-button-name.d.ts +10 -0
- package/core/audits/accessibility/input-button-name.js +44 -0
- package/core/audits/accessibility/table-fake-caption.d.ts +10 -0
- package/core/audits/accessibility/table-fake-caption.js +47 -0
- package/core/audits/accessibility/td-has-header.d.ts +10 -0
- package/core/audits/accessibility/td-has-header.js +45 -0
- package/core/audits/audit.d.ts +2 -1
- package/core/audits/audit.js +5 -2
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +3 -3
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +14 -19
- package/core/audits/deprecations.js +1 -1
- package/core/audits/largest-contentful-paint-element.d.ts +14 -2
- package/core/audits/largest-contentful-paint-element.js +49 -31
- package/core/audits/layout-shift-elements.d.ts +3 -2
- package/core/audits/layout-shift-elements.js +10 -2
- package/core/audits/metrics.js +0 -2
- package/core/audits/network-requests.js +6 -0
- package/core/audits/seo/crawlable-anchors.js +4 -0
- package/core/audits/server-response-time.d.ts +2 -1
- package/core/audits/server-response-time.js +17 -3
- package/core/audits/uses-rel-preconnect.js +20 -4
- package/core/computed/entity-classification.d.ts +14 -1
- package/core/computed/entity-classification.js +61 -6
- package/core/computed/metrics/cumulative-layout-shift.d.ts +2 -9
- package/core/computed/metrics/cumulative-layout-shift.js +3 -16
- package/core/computed/metrics/time-to-first-byte.js +10 -7
- package/core/computed/metrics/timing-summary.js +0 -3
- package/core/config/default-config.js +8 -0
- package/core/gather/driver/execution-context.js +13 -5
- package/core/gather/driver/target-manager.d.ts +13 -0
- package/core/gather/driver/target-manager.js +49 -0
- package/core/gather/gatherers/accessibility.js +19 -20
- package/core/gather/gatherers/anchor-elements.js +2 -0
- package/core/gather/gatherers/global-listeners.js +38 -21
- package/core/gather/gatherers/inspector-issues.js +2 -1
- package/core/gather/gatherers/trace-elements.d.ts +3 -2
- package/core/gather/gatherers/trace-elements.js +10 -19
- package/core/legacy/config/config.d.ts +1 -1
- package/core/legacy/config/config.js +1 -1
- package/core/legacy/gather/driver.d.ts +1 -0
- package/core/legacy/gather/driver.js +11 -0
- package/core/lib/asset-saver.d.ts +4 -0
- package/core/lib/asset-saver.js +19 -0
- package/core/lib/bf-cache-strings.d.ts +3 -0
- package/core/lib/bf-cache-strings.js +248 -232
- package/core/lib/cdt/generated/SourceMap.d.ts +18 -31
- package/core/lib/cdt/generated/SourceMap.js +51 -101
- package/core/lib/dependency-graph/simulator/network-analyzer.d.ts +6 -2
- package/core/lib/dependency-graph/simulator/network-analyzer.js +45 -13
- package/core/lib/deprecation-description.d.ts +17 -0
- package/core/lib/deprecation-description.js +71 -0
- package/core/lib/deprecations-strings.d.ts +282 -63
- package/core/lib/deprecations-strings.js +282 -551
- package/core/lib/lh-error.d.ts +9 -4
- package/core/lib/lh-error.js +15 -11
- package/core/lib/network-request.d.ts +13 -6
- package/core/lib/network-request.js +39 -14
- package/core/lib/stack-packs.js +4 -0
- package/core/lib/tracehouse/trace-processor.js +1 -1
- package/core/lib/url-utils.js +4 -0
- package/core/runner.js +4 -2
- package/dist/report/bundle.esm.js +24 -7
- package/dist/report/flow.js +3 -3
- package/dist/report/standalone.js +8 -8
- package/package.json +13 -13
- package/readme.md +7 -0
- package/report/assets/styles.css +18 -2
- package/report/assets/templates.html +4 -0
- package/report/renderer/category-renderer.js +5 -2
- package/report/renderer/components.js +2 -2
- package/report/renderer/dom.d.ts +4 -1
- package/report/renderer/dom.js +3 -2
- package/shared/localization/locales/ar-XB.json +20 -170
- package/shared/localization/locales/ar.json +20 -170
- package/shared/localization/locales/bg.json +19 -169
- package/shared/localization/locales/ca.json +19 -169
- package/shared/localization/locales/cs.json +19 -169
- package/shared/localization/locales/da.json +19 -169
- package/shared/localization/locales/de.json +19 -169
- package/shared/localization/locales/el.json +19 -169
- package/shared/localization/locales/en-GB.json +9 -159
- package/shared/localization/locales/en-US.json +166 -70
- package/shared/localization/locales/en-XA.json +20 -170
- package/shared/localization/locales/en-XL.json +166 -70
- package/shared/localization/locales/es-419.json +19 -169
- package/shared/localization/locales/es.json +19 -169
- package/shared/localization/locales/fi.json +19 -169
- package/shared/localization/locales/fil.json +17 -167
- package/shared/localization/locales/fr.json +18 -168
- package/shared/localization/locales/he.json +19 -169
- package/shared/localization/locales/hi.json +20 -170
- package/shared/localization/locales/hr.json +19 -169
- package/shared/localization/locales/hu.json +19 -169
- package/shared/localization/locales/id.json +19 -169
- package/shared/localization/locales/it.json +19 -169
- package/shared/localization/locales/ja.json +19 -169
- package/shared/localization/locales/ko.json +19 -169
- package/shared/localization/locales/lt.json +20 -170
- package/shared/localization/locales/lv.json +20 -170
- package/shared/localization/locales/nl.json +18 -168
- package/shared/localization/locales/no.json +19 -169
- package/shared/localization/locales/pl.json +19 -169
- package/shared/localization/locales/pt-PT.json +19 -169
- package/shared/localization/locales/pt.json +19 -169
- package/shared/localization/locales/ro.json +19 -169
- package/shared/localization/locales/ru.json +19 -169
- package/shared/localization/locales/sk.json +20 -170
- package/shared/localization/locales/sl.json +19 -169
- package/shared/localization/locales/sr-Latn.json +19 -169
- package/shared/localization/locales/sr.json +19 -169
- package/shared/localization/locales/sv.json +19 -169
- package/shared/localization/locales/ta.json +19 -169
- package/shared/localization/locales/te.json +19 -169
- package/shared/localization/locales/th.json +19 -169
- package/shared/localization/locales/tr.json +19 -169
- package/shared/localization/locales/uk.json +19 -169
- package/shared/localization/locales/vi.json +20 -170
- package/shared/localization/locales/zh-HK.json +19 -169
- package/shared/localization/locales/zh-TW.json +19 -169
- package/shared/localization/locales/zh.json +19 -169
- package/shared/util.d.ts +6 -0
- package/shared/util.js +14 -1
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +2 -1
- package/tsconfig-base.json +1 -1
- package/types/artifacts.d.ts +10 -8
- package/types/audit.d.ts +5 -0
- package/types/gatherer.d.ts +1 -0
- package/types/lhr/audit-details.d.ts +1 -0
- package/types/lhr/audit-result.d.ts +10 -0
- package/types/internal/es-main.d.ts +0 -18
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
4
4
|
"X difference at cumulative-layout-shift audit.details.items.length
|
|
5
5
|
expected: []
|
|
6
|
-
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444
|
|
6
|
+
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444}]
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
X difference at cumulative-layout-shift audit.details.blah
|
|
@@ -24,8 +24,7 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
|
24
24
|
\\"type\\": \\"debugdata\\",
|
|
25
25
|
\\"items\\": [
|
|
26
26
|
{
|
|
27
|
-
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
|
|
28
|
-
\\"totalCumulativeLayoutShift\\": 0.13570762803819444
|
|
27
|
+
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
|
|
29
28
|
}
|
|
30
29
|
]
|
|
31
30
|
}
|
|
@@ -35,7 +34,7 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
|
35
34
|
exports[`getAssertionReport works (trivial failing) 1`] = `
|
|
36
35
|
"X difference at cumulative-layout-shift audit.details.items.length
|
|
37
36
|
expected: []
|
|
38
|
-
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444
|
|
37
|
+
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444}]
|
|
39
38
|
|
|
40
39
|
found result:
|
|
41
40
|
{
|
|
@@ -51,8 +50,7 @@ exports[`getAssertionReport works (trivial failing) 1`] = `
|
|
|
51
50
|
\\"type\\": \\"debugdata\\",
|
|
52
51
|
\\"items\\": [
|
|
53
52
|
{
|
|
54
|
-
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
|
|
55
|
-
\\"totalCumulativeLayoutShift\\": 0.13570762803819444
|
|
53
|
+
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
|
|
56
54
|
}
|
|
57
55
|
]
|
|
58
56
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default HTMLXMLLangMismatch;
|
|
2
|
+
declare class HTMLXMLLangMismatch extends AxeAudit {
|
|
3
|
+
}
|
|
4
|
+
export namespace UIStrings {
|
|
5
|
+
const title: string;
|
|
6
|
+
const failureTitle: string;
|
|
7
|
+
const description: string;
|
|
8
|
+
}
|
|
9
|
+
import AxeAudit from './axe-audit.js';
|
|
10
|
+
//# sourceMappingURL=html-xml-lang-mismatch.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2023 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
|
+
/**
|
|
8
|
+
* @fileoverview Ensures that, if present, the `[xml:lang]` attribute value in an HTML document has
|
|
9
|
+
* the same base language as the `[lang]` attribute.
|
|
10
|
+
* See base class in axe-audit.js for audit() implementation.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import AxeAudit from './axe-audit.js';
|
|
14
|
+
import * as i18n from '../../lib/i18n/i18n.js';
|
|
15
|
+
|
|
16
|
+
const UIStrings = {
|
|
17
|
+
/** Title of an accesibility audit that evaluates if the xml:lang attribute, if present, has the same base language as the `lang` attribute. This title is descriptive of the successful state and is shown to users when no user action is required. */
|
|
18
|
+
title: '`<html>` element has an `[xml:lang]` attribute with the same base language as the ' +
|
|
19
|
+
'`[lang]` attribute.',
|
|
20
|
+
/** Title of an accesibility audit that evaluates if the xml:lang attribute, if present, has the same base language as the `lang` attribute. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed. */
|
|
21
|
+
failureTitle: '`<html>` element does not have an `[xml:lang]` attribute with the same base ' +
|
|
22
|
+
'language as the `[lang]` attribute.',
|
|
23
|
+
/** Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
24
|
+
description: 'If the webpage does not specify a consistent language, then the screen ' +
|
|
25
|
+
'reader might not announce the page\'s text correctly. ' +
|
|
26
|
+
'[Learn more about the `lang` attribute](https://dequeuniversity.com/rules/axe/4.7/html-xml-lang-mismatch).',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
30
|
+
|
|
31
|
+
class HTMLXMLLangMismatch extends AxeAudit {
|
|
32
|
+
/**
|
|
33
|
+
* @return {LH.Audit.Meta}
|
|
34
|
+
*/
|
|
35
|
+
static get meta() {
|
|
36
|
+
return {
|
|
37
|
+
id: 'html-xml-lang-mismatch',
|
|
38
|
+
title: str_(UIStrings.title),
|
|
39
|
+
failureTitle: str_(UIStrings.failureTitle),
|
|
40
|
+
description: str_(UIStrings.description),
|
|
41
|
+
requiredArtifacts: ['Accessibility'],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default HTMLXMLLangMismatch;
|
|
47
|
+
export {UIStrings};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default InputButtonName;
|
|
2
|
+
declare class InputButtonName extends AxeAudit {
|
|
3
|
+
}
|
|
4
|
+
export namespace UIStrings {
|
|
5
|
+
const title: string;
|
|
6
|
+
const failureTitle: string;
|
|
7
|
+
const description: string;
|
|
8
|
+
}
|
|
9
|
+
import AxeAudit from './axe-audit.js';
|
|
10
|
+
//# sourceMappingURL=input-button-name.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2023 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
|
+
/**
|
|
8
|
+
* @fileoverview Ensures input buttons have discernible text.
|
|
9
|
+
* See base class in axe-audit.js for audit() implementation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import AxeAudit from './axe-audit.js';
|
|
13
|
+
import * as i18n from '../../lib/i18n/i18n.js';
|
|
14
|
+
|
|
15
|
+
const UIStrings = {
|
|
16
|
+
/** Title of an accesibility audit that evaluates if all input buttons have discernible text. This title is descriptive of the successful state and is shown to users when no user action is required. */
|
|
17
|
+
title: 'Input buttons have discernible text.',
|
|
18
|
+
/** Title of an accesibility audit that evaluates if all input buttons have discernible text. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed. */
|
|
19
|
+
failureTitle: 'Input buttons do not have discernible text.',
|
|
20
|
+
/** Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
21
|
+
description: 'Adding discernable and accessible text to input buttons may help screen reader ' +
|
|
22
|
+
'users understand the purpose of the input button. ' +
|
|
23
|
+
'[Learn more about input buttons](https://dequeuniversity.com/rules/axe/4.7/input-button-name).',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
27
|
+
|
|
28
|
+
class InputButtonName extends AxeAudit {
|
|
29
|
+
/**
|
|
30
|
+
* @return {LH.Audit.Meta}
|
|
31
|
+
*/
|
|
32
|
+
static get meta() {
|
|
33
|
+
return {
|
|
34
|
+
id: 'input-button-name',
|
|
35
|
+
title: str_(UIStrings.title),
|
|
36
|
+
failureTitle: str_(UIStrings.failureTitle),
|
|
37
|
+
description: str_(UIStrings.description),
|
|
38
|
+
requiredArtifacts: ['Accessibility'],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default InputButtonName;
|
|
44
|
+
export {UIStrings};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default TableFakeCaption;
|
|
2
|
+
declare class TableFakeCaption extends AxeAudit {
|
|
3
|
+
}
|
|
4
|
+
export namespace UIStrings {
|
|
5
|
+
const title: string;
|
|
6
|
+
const failureTitle: string;
|
|
7
|
+
const description: string;
|
|
8
|
+
}
|
|
9
|
+
import AxeAudit from './axe-audit.js';
|
|
10
|
+
//# sourceMappingURL=table-fake-caption.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2023 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
|
+
/**
|
|
8
|
+
* @fileoverview Ensure that tables use `<caption>` instead of colspan for a caption.
|
|
9
|
+
* See base class in axe-audit.js for audit() implementation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import AxeAudit from './axe-audit.js';
|
|
13
|
+
import * as i18n from '../../lib/i18n/i18n.js';
|
|
14
|
+
|
|
15
|
+
const UIStrings = {
|
|
16
|
+
/** Title of an accesibility audit that evaluates if all tables use caption instead of colspan to indicate a caption. This title is descriptive of the successful state and is shown to users when no user action is required. */
|
|
17
|
+
title: 'Tables use `<caption>` instead of cells with the `[colspan]` attribute to indicate a ' +
|
|
18
|
+
'caption.',
|
|
19
|
+
/** Title of an accesibility audit that evaluates if all tables use caption instead of colspan to indicate a caption. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed. */
|
|
20
|
+
failureTitle: 'Tables do not use `<caption>` instead of cells with the `[colspan]` attribute ' +
|
|
21
|
+
'to indicate a caption.',
|
|
22
|
+
/** Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
23
|
+
description: 'Screen readers have features to make navigating tables easier. Ensuring ' +
|
|
24
|
+
'that tables use the actual caption element instead of cells with the `[colspan]` ' +
|
|
25
|
+
'attribute may improve the experience for screen reader users. ' +
|
|
26
|
+
'[Learn more about captions](https://dequeuniversity.com/rules/axe/4.7/table-fake-caption).',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
30
|
+
|
|
31
|
+
class TableFakeCaption extends AxeAudit {
|
|
32
|
+
/**
|
|
33
|
+
* @return {LH.Audit.Meta}
|
|
34
|
+
*/
|
|
35
|
+
static get meta() {
|
|
36
|
+
return {
|
|
37
|
+
id: 'table-fake-caption',
|
|
38
|
+
title: str_(UIStrings.title),
|
|
39
|
+
failureTitle: str_(UIStrings.failureTitle),
|
|
40
|
+
description: str_(UIStrings.description),
|
|
41
|
+
requiredArtifacts: ['Accessibility'],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default TableFakeCaption;
|
|
47
|
+
export {UIStrings};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default TDHasHeader;
|
|
2
|
+
declare class TDHasHeader extends AxeAudit {
|
|
3
|
+
}
|
|
4
|
+
export namespace UIStrings {
|
|
5
|
+
const title: string;
|
|
6
|
+
const failureTitle: string;
|
|
7
|
+
const description: string;
|
|
8
|
+
}
|
|
9
|
+
import AxeAudit from './axe-audit.js';
|
|
10
|
+
//# sourceMappingURL=td-has-header.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2023 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
|
+
/**
|
|
8
|
+
* @fileoverview Ensure that large tables have `[header]` attributes.
|
|
9
|
+
* See base class in axe-audit.js for audit() implementation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import AxeAudit from './axe-audit.js';
|
|
13
|
+
import * as i18n from '../../lib/i18n/i18n.js';
|
|
14
|
+
|
|
15
|
+
const UIStrings = {
|
|
16
|
+
/** Title of an accesibility audit that evaluates if all large table elements use the headers HTML attribute. This title is descriptive of the successful state and is shown to users when no user action is required. */
|
|
17
|
+
title: '`<td>` elements in a large `<table>` have one or more table headers.',
|
|
18
|
+
/** Title of an accesibility audit that evaluates if all large table elements use the headers HTML attribute. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed. */
|
|
19
|
+
failureTitle: '`<td>` elements in a large `<table>` do not have table headers.',
|
|
20
|
+
/** Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
21
|
+
description: 'Screen readers have features to make navigating tables easier. Ensuring ' +
|
|
22
|
+
'that `<td>` elements in a large table (3 or more cells in width and height) have an ' +
|
|
23
|
+
'associated table header may improve the experience for screen reader users. ' +
|
|
24
|
+
'[Learn more about table headers](https://dequeuniversity.com/rules/axe/4.7/td-has-header).',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
28
|
+
|
|
29
|
+
class TDHasHeader extends AxeAudit {
|
|
30
|
+
/**
|
|
31
|
+
* @return {LH.Audit.Meta}
|
|
32
|
+
*/
|
|
33
|
+
static get meta() {
|
|
34
|
+
return {
|
|
35
|
+
id: 'td-has-header',
|
|
36
|
+
title: str_(UIStrings.title),
|
|
37
|
+
failureTitle: str_(UIStrings.failureTitle),
|
|
38
|
+
description: str_(UIStrings.description),
|
|
39
|
+
requiredArtifacts: ['Accessibility'],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default TDHasHeader;
|
|
45
|
+
export {UIStrings};
|
package/core/audits/audit.d.ts
CHANGED
|
@@ -149,9 +149,10 @@ export class Audit {
|
|
|
149
149
|
/**
|
|
150
150
|
* @param {typeof Audit} audit
|
|
151
151
|
* @param {string | LH.IcuMessage} errorMessage
|
|
152
|
+
* @param {string=} errorStack
|
|
152
153
|
* @return {LH.RawIcu<LH.Audit.Result>}
|
|
153
154
|
*/
|
|
154
|
-
static generateErrorAuditResult(audit: typeof Audit, errorMessage: string | LH.IcuMessage): LH.RawIcu<LH.Audit.Result>;
|
|
155
|
+
static generateErrorAuditResult(audit: typeof Audit, errorMessage: string | LH.IcuMessage, errorStack?: string | undefined): LH.RawIcu<LH.Audit.Result>;
|
|
155
156
|
/**
|
|
156
157
|
* @param {typeof Audit} audit
|
|
157
158
|
* @param {LH.Audit.Product} product
|
package/core/audits/audit.js
CHANGED
|
@@ -348,12 +348,14 @@ class Audit {
|
|
|
348
348
|
/**
|
|
349
349
|
* @param {typeof Audit} audit
|
|
350
350
|
* @param {string | LH.IcuMessage} errorMessage
|
|
351
|
+
* @param {string=} errorStack
|
|
351
352
|
* @return {LH.RawIcu<LH.Audit.Result>}
|
|
352
353
|
*/
|
|
353
|
-
static generateErrorAuditResult(audit, errorMessage) {
|
|
354
|
+
static generateErrorAuditResult(audit, errorMessage, errorStack) {
|
|
354
355
|
return Audit.generateAuditResult(audit, {
|
|
355
356
|
score: null,
|
|
356
357
|
errorMessage,
|
|
358
|
+
errorStack,
|
|
357
359
|
});
|
|
358
360
|
}
|
|
359
361
|
|
|
@@ -371,7 +373,7 @@ class Audit {
|
|
|
371
373
|
let scoreDisplayMode = audit.meta.scoreDisplayMode || Audit.SCORING_MODES.BINARY;
|
|
372
374
|
|
|
373
375
|
// But override if product contents require it.
|
|
374
|
-
if (product.errorMessage) {
|
|
376
|
+
if (product.errorMessage !== undefined) {
|
|
375
377
|
// Error result.
|
|
376
378
|
scoreDisplayMode = Audit.SCORING_MODES.ERROR;
|
|
377
379
|
} else if (product.notApplicable) {
|
|
@@ -407,6 +409,7 @@ class Audit {
|
|
|
407
409
|
displayValue: product.displayValue,
|
|
408
410
|
explanation: product.explanation,
|
|
409
411
|
errorMessage: product.errorMessage,
|
|
412
|
+
errorStack: product.errorStack,
|
|
410
413
|
warnings: product.warnings,
|
|
411
414
|
|
|
412
415
|
details: product.details,
|
|
@@ -25,9 +25,9 @@ export type ByteEfficiencyProduct = {
|
|
|
25
25
|
*/
|
|
26
26
|
export class ByteEfficiencyAudit extends Audit {
|
|
27
27
|
/**
|
|
28
|
-
* Creates a score based on the wastedMs value using
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* Creates a score based on the wastedMs value using log-normal distribution scoring. A negative
|
|
29
|
+
* wastedMs will be scored as 1, assuming time is not being wasted with respect to the opportunity
|
|
30
|
+
* being measured.
|
|
31
31
|
*
|
|
32
32
|
* @param {number} wastedMs
|
|
33
33
|
* @return {number}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {Audit} from '../audit.js';
|
|
8
|
-
import {linearInterpolation} from '../../lib/statistics.js';
|
|
9
8
|
import {LanternInteractive} from '../../computed/metrics/lantern-interactive.js';
|
|
10
9
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
11
10
|
import {NetworkRecords} from '../../computed/network-records.js';
|
|
@@ -17,9 +16,13 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, {});
|
|
|
17
16
|
/** @typedef {import('../../lib/dependency-graph/simulator/simulator').Simulator} Simulator */
|
|
18
17
|
/** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// Parameters for log-normal distribution scoring. These values were determined by fitting the
|
|
20
|
+
// log-normal cumulative distribution function curve to the former method of linear interpolation
|
|
21
|
+
// scoring between the control points {average = 300 ms, poor = 750 ms, zero = 5000 ms} using the
|
|
22
|
+
// curve-fit tool at https://mycurvefit.com/ rounded to the nearest integer. See
|
|
23
|
+
// https://www.desmos.com/calculator/gcexiyesdi for an interactive visualization of the curve fit.
|
|
24
|
+
const WASTED_MS_P10 = 150;
|
|
25
|
+
const WASTED_MS_MEDIAN = 935;
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* @typedef {object} ByteEfficiencyProduct
|
|
@@ -38,26 +41,18 @@ const WASTED_MS_FOR_SCORE_OF_ZERO = 5000;
|
|
|
38
41
|
*/
|
|
39
42
|
class ByteEfficiencyAudit extends Audit {
|
|
40
43
|
/**
|
|
41
|
-
* Creates a score based on the wastedMs value using
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
+
* Creates a score based on the wastedMs value using log-normal distribution scoring. A negative
|
|
45
|
+
* wastedMs will be scored as 1, assuming time is not being wasted with respect to the opportunity
|
|
46
|
+
* being measured.
|
|
44
47
|
*
|
|
45
48
|
* @param {number} wastedMs
|
|
46
49
|
* @return {number}
|
|
47
50
|
*/
|
|
48
51
|
static scoreForWastedMs(wastedMs) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} else if (wastedMs < WASTED_MS_FOR_POOR) {
|
|
54
|
-
return linearInterpolation(WASTED_MS_FOR_AVERAGE, 0.75, WASTED_MS_FOR_POOR, 0.5, wastedMs);
|
|
55
|
-
} else {
|
|
56
|
-
return Math.max(
|
|
57
|
-
0,
|
|
58
|
-
linearInterpolation(WASTED_MS_FOR_POOR, 0.5, WASTED_MS_FOR_SCORE_OF_ZERO, 0, wastedMs)
|
|
59
|
-
);
|
|
60
|
-
}
|
|
52
|
+
return Audit.computeLogNormalScore(
|
|
53
|
+
{p10: WASTED_MS_P10, median: WASTED_MS_MEDIAN},
|
|
54
|
+
wastedMs
|
|
55
|
+
);
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
/**
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import {Audit} from './audit.js';
|
|
12
12
|
import {JSBundles} from '../computed/js-bundles.js';
|
|
13
13
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
14
|
-
import {getIssueDetailDescription} from '../lib/
|
|
14
|
+
import {getIssueDetailDescription} from '../lib/deprecation-description.js';
|
|
15
15
|
|
|
16
16
|
/* eslint-disable max-len */
|
|
17
17
|
const UIStrings = {
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
export default LargestContentfulPaintElement;
|
|
2
2
|
declare class LargestContentfulPaintElement extends Audit {
|
|
3
|
+
/**
|
|
4
|
+
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
|
|
5
|
+
* @param {LH.Audit.Context} context
|
|
6
|
+
* @return {Promise<number|undefined>}
|
|
7
|
+
*/
|
|
8
|
+
static getOptionalLCPMetric(metricComputationData: LH.Artifacts.MetricComputationDataInput, context: LH.Audit.Context): Promise<number | undefined>;
|
|
3
9
|
/**
|
|
4
10
|
* @param {LH.Artifacts} artifacts
|
|
11
|
+
* @return {LH.Audit.Details.Table|undefined}
|
|
12
|
+
*/
|
|
13
|
+
static makeElementTable(artifacts: LH.Artifacts): LH.Audit.Details.Table | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* @param {number} metricLcp
|
|
16
|
+
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
|
|
5
17
|
* @param {LH.Audit.Context} context
|
|
6
|
-
* @return {Promise<LH.Audit.Details.Table
|
|
18
|
+
* @return {Promise<LH.Audit.Details.Table>}
|
|
7
19
|
*/
|
|
8
|
-
static makePhaseTable(
|
|
20
|
+
static makePhaseTable(metricLcp: number, metricComputationData: LH.Artifacts.MetricComputationDataInput, context: LH.Audit.Context): Promise<LH.Audit.Details.Table>;
|
|
9
21
|
/**
|
|
10
22
|
* @param {LH.Artifacts} artifacts
|
|
11
23
|
* @param {LH.Audit.Context} context
|
|
@@ -50,19 +50,44 @@ class LargestContentfulPaintElement extends Audit {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* @param {LH.Artifacts}
|
|
53
|
+
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
|
|
54
54
|
* @param {LH.Audit.Context} context
|
|
55
|
-
* @return {Promise<
|
|
55
|
+
* @return {Promise<number|undefined>}
|
|
56
56
|
*/
|
|
57
|
-
static async
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
static async getOptionalLCPMetric(metricComputationData, context) {
|
|
58
|
+
try {
|
|
59
|
+
const {timing: metricLcp} =
|
|
60
|
+
await LargestContentfulPaint.request(metricComputationData, context);
|
|
61
|
+
return metricLcp;
|
|
62
|
+
} catch {}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {LH.Artifacts} artifacts
|
|
67
|
+
* @return {LH.Audit.Details.Table|undefined}
|
|
68
|
+
*/
|
|
69
|
+
static makeElementTable(artifacts) {
|
|
70
|
+
const lcpElement = artifacts.TraceElements
|
|
71
|
+
.find(element => element.traceEventType === 'largest-contentful-paint');
|
|
72
|
+
if (!lcpElement) return;
|
|
73
|
+
|
|
74
|
+
/** @type {LH.Audit.Details.Table['headings']} */
|
|
75
|
+
const headings = [
|
|
76
|
+
{key: 'node', valueType: 'node', label: str_(i18n.UIStrings.columnElement)},
|
|
77
|
+
];
|
|
63
78
|
|
|
64
|
-
const {
|
|
65
|
-
|
|
79
|
+
const lcpElementDetails = [{node: Audit.makeNodeItem(lcpElement.node)}];
|
|
80
|
+
|
|
81
|
+
return Audit.makeTableDetails(headings, lcpElementDetails);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {number} metricLcp
|
|
86
|
+
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
|
|
87
|
+
* @param {LH.Audit.Context} context
|
|
88
|
+
* @return {Promise<LH.Audit.Details.Table>}
|
|
89
|
+
*/
|
|
90
|
+
static async makePhaseTable(metricLcp, metricComputationData, context) {
|
|
66
91
|
const {ttfb, loadStart, loadEnd} = await LCPBreakdown.request(metricComputationData, context);
|
|
67
92
|
|
|
68
93
|
let loadDelay = 0;
|
|
@@ -102,36 +127,29 @@ class LargestContentfulPaintElement extends Audit {
|
|
|
102
127
|
* @return {Promise<LH.Audit.Product>}
|
|
103
128
|
*/
|
|
104
129
|
static async audit(artifacts, context) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
node: Audit.makeNodeItem(lcpElement.node),
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** @type {LH.Audit.Details.Table['headings']} */
|
|
115
|
-
const headings = [
|
|
116
|
-
{key: 'node', valueType: 'node', label: str_(i18n.UIStrings.columnElement)},
|
|
117
|
-
];
|
|
130
|
+
const trace = artifacts.traces[Audit.DEFAULT_PASS];
|
|
131
|
+
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
132
|
+
const gatherContext = artifacts.GatherContext;
|
|
133
|
+
const metricComputationData = {trace, devtoolsLog, gatherContext,
|
|
134
|
+
settings: context.settings, URL: artifacts.URL};
|
|
118
135
|
|
|
119
|
-
const elementTable =
|
|
136
|
+
const elementTable = this.makeElementTable(artifacts);
|
|
137
|
+
if (!elementTable) return {score: null, notApplicable: true};
|
|
120
138
|
|
|
121
139
|
const items = [elementTable];
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
140
|
+
let displayValue;
|
|
141
|
+
|
|
142
|
+
const metricLcp = await this.getOptionalLCPMetric(metricComputationData, context);
|
|
143
|
+
if (metricLcp) {
|
|
144
|
+
displayValue = str_(i18n.UIStrings.ms, {timeInMs: metricLcp});
|
|
145
|
+
const phaseTable = await this.makePhaseTable(metricLcp, metricComputationData, context);
|
|
146
|
+
items.push(phaseTable);
|
|
125
147
|
}
|
|
126
148
|
|
|
127
149
|
const details = Audit.makeListDetails(items);
|
|
128
150
|
|
|
129
|
-
const displayValue = str_(i18n.UIStrings.displayValueElementsFound,
|
|
130
|
-
{nodeCount: lcpElementDetails.length});
|
|
131
|
-
|
|
132
151
|
return {
|
|
133
152
|
score: 1,
|
|
134
|
-
notApplicable: lcpElementDetails.length === 0,
|
|
135
153
|
displayValue,
|
|
136
154
|
details,
|
|
137
155
|
};
|
|
@@ -2,9 +2,10 @@ export default LayoutShiftElements;
|
|
|
2
2
|
declare class LayoutShiftElements extends Audit {
|
|
3
3
|
/**
|
|
4
4
|
* @param {LH.Artifacts} artifacts
|
|
5
|
-
* @
|
|
5
|
+
* @param {LH.Audit.Context} context
|
|
6
|
+
* @return {Promise<LH.Audit.Product>}
|
|
6
7
|
*/
|
|
7
|
-
static audit(artifacts: LH.Artifacts): LH.Audit.Product
|
|
8
|
+
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
8
9
|
}
|
|
9
10
|
export namespace UIStrings {
|
|
10
11
|
const title: string;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {Audit} from './audit.js';
|
|
8
8
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
9
|
+
import {CumulativeLayoutShift} from '../computed/metrics/cumulative-layout-shift.js';
|
|
9
10
|
|
|
10
11
|
const UIStrings = {
|
|
11
12
|
/** Descriptive title of a diagnostic audit that provides up to the top five elements contributing to Cumulative Layout Shift. */
|
|
@@ -34,9 +35,10 @@ class LayoutShiftElements extends Audit {
|
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* @param {LH.Artifacts} artifacts
|
|
37
|
-
* @
|
|
38
|
+
* @param {LH.Audit.Context} context
|
|
39
|
+
* @return {Promise<LH.Audit.Product>}
|
|
38
40
|
*/
|
|
39
|
-
static audit(artifacts) {
|
|
41
|
+
static async audit(artifacts, context) {
|
|
40
42
|
const clsElements = artifacts.TraceElements
|
|
41
43
|
.filter(element => element.traceEventType === 'layout-shift');
|
|
42
44
|
|
|
@@ -61,8 +63,14 @@ class LayoutShiftElements extends Audit {
|
|
|
61
63
|
{nodeCount: clsElementData.length});
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
const {cumulativeLayoutShift: clsSavings} =
|
|
67
|
+
await CumulativeLayoutShift.request(artifacts.traces[Audit.DEFAULT_PASS], context);
|
|
68
|
+
|
|
64
69
|
return {
|
|
65
70
|
score: 1,
|
|
71
|
+
metricSavings: {
|
|
72
|
+
CLS: clsSavings,
|
|
73
|
+
},
|
|
66
74
|
notApplicable: details.items.length === 0,
|
|
67
75
|
displayValue,
|
|
68
76
|
details,
|
package/core/audits/metrics.js
CHANGED
|
@@ -11,10 +11,8 @@ import {TimingSummary} from '../computed/metrics/timing-summary.js';
|
|
|
11
11
|
const DECIMAL_METRIC_KEYS = new Set([
|
|
12
12
|
'cumulativeLayoutShift',
|
|
13
13
|
'cumulativeLayoutShiftMainFrame',
|
|
14
|
-
'totalCumulativeLayoutShift',
|
|
15
14
|
'observedCumulativeLayoutShift',
|
|
16
15
|
'observedCumulativeLayoutShiftMainFrame',
|
|
17
|
-
'observedTotalCumulativeLayoutShift',
|
|
18
16
|
]);
|
|
19
17
|
|
|
20
18
|
class Metrics extends Audit {
|
|
@@ -8,6 +8,7 @@ import {Audit} from './audit.js';
|
|
|
8
8
|
import UrlUtils from '../lib/url-utils.js';
|
|
9
9
|
import {NetworkRecords} from '../computed/network-records.js';
|
|
10
10
|
import {MainResource} from '../computed/main-resource.js';
|
|
11
|
+
import {EntityClassification} from '../computed/entity-classification.js';
|
|
11
12
|
|
|
12
13
|
class NetworkRequests extends Audit {
|
|
13
14
|
/**
|
|
@@ -31,6 +32,8 @@ class NetworkRequests extends Audit {
|
|
|
31
32
|
static async audit(artifacts, context) {
|
|
32
33
|
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
33
34
|
const records = await NetworkRecords.request(devtoolsLog, context);
|
|
35
|
+
const classifiedEntities = await EntityClassification.request(
|
|
36
|
+
{URL: artifacts.URL, devtoolsLog}, context);
|
|
34
37
|
const earliestRendererStartTime = records.reduce(
|
|
35
38
|
(min, record) => Math.min(min, record.rendererStartTime),
|
|
36
39
|
Infinity
|
|
@@ -61,6 +64,8 @@ class NetworkRequests extends Audit {
|
|
|
61
64
|
((record.frameId === mainFrameId) || undefined) :
|
|
62
65
|
undefined;
|
|
63
66
|
|
|
67
|
+
const entity = classifiedEntities.entityByUrl.get(record.url);
|
|
68
|
+
|
|
64
69
|
return {
|
|
65
70
|
url: UrlUtils.elideDataURI(record.url),
|
|
66
71
|
sessionTargetType: record.sessionTargetType,
|
|
@@ -77,6 +82,7 @@ class NetworkRequests extends Audit {
|
|
|
77
82
|
priority: record.priority,
|
|
78
83
|
isLinkPreload,
|
|
79
84
|
experimentalFromMainFrame,
|
|
85
|
+
entity: entity?.name,
|
|
80
86
|
lrEndTimeDeltaMs: endTimeDeltaMs, // Only exists on Lightrider runs
|
|
81
87
|
lrTCPMs: TCPMs, // Only exists on Lightrider runs
|
|
82
88
|
lrRequestMs: requestMs, // Only exists on Lightrider runs
|
|
@@ -43,6 +43,7 @@ class CrawlableAnchors extends Audit {
|
|
|
43
43
|
rawHref,
|
|
44
44
|
name = '',
|
|
45
45
|
role = '',
|
|
46
|
+
id,
|
|
46
47
|
}) => {
|
|
47
48
|
rawHref = rawHref.replace( /\s/g, '');
|
|
48
49
|
name = name.trim();
|
|
@@ -52,6 +53,9 @@ class CrawlableAnchors extends Audit {
|
|
|
52
53
|
// Ignore mailto links even if they use one of the failing patterns. See https://github.com/GoogleChrome/lighthouse/issues/11443#issuecomment-694898412
|
|
53
54
|
if (rawHref.startsWith('mailto:')) return;
|
|
54
55
|
|
|
56
|
+
// Ignore `<a id="something">` elements acting as an anchor.
|
|
57
|
+
if (rawHref === '' && id) return;
|
|
58
|
+
|
|
55
59
|
const javaScriptVoidRegExp = /javascript:void(\(|)0(\)|)/;
|
|
56
60
|
|
|
57
61
|
if (rawHref.startsWith('file:')) return true;
|