testaro 27.0.0 → 28.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +3 -1
- package/README.md +14 -3
- package/package.json +2 -1
- package/procs/aslint.js +83 -0
- package/procs/nav.js +56 -14
- package/procs/standardize.js +36 -0
- package/run.js +35 -22
- package/tests/aslint.js +83 -0
- package/tests/axe.js +4 -0
- package/tests/qualWeb.js +6 -0
- package/aslint/LICENSE +0 -362
- package/aslint/README.md +0 -260
- package/aslint/app/rules/abstract-rule.ts +0 -83
- package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.documentation.md +0 -36
- package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.test.ts +0 -113
- package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.ts +0 -103
- package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.documentation.md +0 -34
- package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.test.ts +0 -82
- package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.ts +0 -44
- package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.documentation.md +0 -40
- package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.test.ts +0 -48
- package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.ts +0 -37
- package/aslint/app/rules/aslint/links-language-destination/links-language-destination.test.ts +0 -50
- package/aslint/app/rules/aslint/links-language-destination/links-language-destination.ts +0 -70
- package/aslint/app/rules/aslint/main-element-only-one/main-element-only-one.test.ts +0 -55
- package/aslint/app/rules/aslint/main-element-only-one/main-element-only-one.ts +0 -83
- package/aslint/app/rules/aslint/main-landmark-must-be-top-level/main-landmark-must-be-top-level.test.ts +0 -12
- package/aslint/app/rules/aslint/main-landmark-must-be-top-level/main-landmark-must-be-top-level.ts +0 -73
- package/aslint/app/rules/aslint/minimum-font-size/minimum-font-size.test.ts +0 -12
- package/aslint/app/rules/aslint/minimum-font-size/minimum-font-size.ts +0 -87
- package/aslint/app/rules/aslint/missing-href-on-a/missing-href-on-a.test.ts +0 -48
- package/aslint/app/rules/aslint/missing-href-on-a/missing-href-on-a.ts +0 -40
- package/aslint/app/rules/aslint/misused-aria-on-focusable-element/misused-aria-on-focusable-element.test.ts +0 -12
- package/aslint/app/rules/aslint/misused-aria-on-focusable-element/misused-aria-on-focusable-element.ts +0 -66
- package/aslint/app/rules/aslint/misused-input-attribute/misused-input-attribute.test.ts +0 -12
- package/aslint/app/rules/aslint/misused-input-attribute/misused-input-attribute.ts +0 -134
- package/aslint/app/rules/aslint/misused-required-attribute/misused-required-attribute.test.ts +0 -12
- package/aslint/app/rules/aslint/misused-required-attribute/misused-required-attribute.ts +0 -90
- package/aslint/app/rules/aslint/navigation-landmark-restrictions/navigation-landmark-restrictions.test.ts +0 -12
- package/aslint/app/rules/aslint/navigation-landmark-restrictions/navigation-landmark-restrictions.ts +0 -48
- package/aslint/app/rules/aslint/obsolete-html-attributes/obsolete-html-attributes.test.ts +0 -12
- package/aslint/app/rules/aslint/obsolete-html-attributes/obsolete-html-attributes.ts +0 -148
- package/aslint/app/rules/aslint/obsolete-html-elements/obsolete-html-elements.test.ts +0 -12
- package/aslint/app/rules/aslint/obsolete-html-elements/obsolete-html-elements.ts +0 -66
- package/aslint/app/rules/aslint/outline-zero/outline-zero.test.ts +0 -12
- package/aslint/app/rules/aslint/outline-zero/outline-zero.ts +0 -85
- package/aslint/app/rules/aslint/overlay/overlay.test.ts +0 -122
- package/aslint/app/rules/aslint/overlay/overlay.ts +0 -141
- package/aslint/app/rules/aslint/redundant/aria-role-dialog/aria-role-dialog.documentation.md +0 -49
- package/aslint/app/rules/aslint/redundant/aria-role-dialog/aria-role-dialog.test.ts +0 -91
- package/aslint/app/rules/aslint/redundant/aria-role-dialog/aria-role-dialog.ts +0 -62
- package/aslint/app/rules/aslint/redundant/capital-letters-words/capital-letters-words.documentation.md +0 -44
- package/aslint/app/rules/aslint/redundant/capital-letters-words/capital-letters-words.test.ts +0 -111
- package/aslint/app/rules/aslint/redundant/capital-letters-words/capital-letters-words.ts +0 -120
- package/aslint/app/rules/aslint/redundant/contentinfo-landmark-only-one/contentinfo-landmark-only-one.documentation.md +0 -45
- package/aslint/app/rules/aslint/redundant/contentinfo-landmark-only-one/contentinfo-landmark-only-one.test.ts +0 -63
- package/aslint/app/rules/aslint/redundant/contentinfo-landmark-only-one/contentinfo-landmark-only-one.ts +0 -44
- package/aslint/app/rules/aslint/redundant/empty-title-attribute/empty-title-attribute.documentation.md +0 -55
- package/aslint/app/rules/aslint/redundant/empty-title-attribute/empty-title-attribute.test.ts +0 -80
- package/aslint/app/rules/aslint/redundant/empty-title-attribute/empty-title-attribute.ts +0 -58
- package/aslint/app/rules/aslint/redundant/flash-content/flash-content.documentation.md +0 -48
- package/aslint/app/rules/aslint/redundant/flash-content/flash-content.test.ts +0 -52
- package/aslint/app/rules/aslint/redundant/flash-content/flash-content.ts +0 -32
- package/aslint/app/rules/aslint/redundant/font-style-italic/font-style-italic.documentation.md +0 -44
- package/aslint/app/rules/aslint/redundant/font-style-italic/font-style-italic.test.ts +0 -12
- package/aslint/app/rules/aslint/redundant/font-style-italic/font-style-italic.ts +0 -83
- package/aslint/app/rules/aslint/redundant/h1-must-be/h1-must-be.documentation.md +0 -46
- package/aslint/app/rules/aslint/redundant/h1-must-be/h1-must-be.test.ts +0 -46
- package/aslint/app/rules/aslint/redundant/h1-must-be/h1-must-be.ts +0 -36
- package/aslint/app/rules/aslint/role-application/role-application.test.ts +0 -48
- package/aslint/app/rules/aslint/role-application/role-application.ts +0 -38
- package/aslint/app/rules/aslint/rtl-content/rtl-content.test.ts +0 -12
- package/aslint/app/rules/aslint/rtl-content/rtl-content.ts +0 -75
- package/aslint/app/rules/aslint/unclear-uri-on-a/unclear-anchor-uri.test.ts +0 -12
- package/aslint/app/rules/aslint/unclear-uri-on-a/unclear-anchor-uri.ts +0 -48
- package/aslint/app/rules/aslint/unimportant/aria-hidden-false/aria-hidden-false.test.ts +0 -73
- package/aslint/app/rules/aslint/unimportant/aria-hidden-false/aria-hidden-false.ts +0 -34
- package/aslint/app/rules/aslint/unimportant/aria-hidden-false/aria-hidden.documentation.md +0 -32
- package/aslint/app/rules/aslint/unimportant/content-editable-missing-attributes/content-editable-missing-attributes.docmentation.md +0 -48
- package/aslint/app/rules/aslint/unimportant/content-editable-missing-attributes/content-editable-missing-attributes.test.ts +0 -67
- package/aslint/app/rules/aslint/unimportant/content-editable-missing-attributes/content-editable-missing-attributes.ts +0 -63
- package/aslint/app/rules/aslint/unsupported-role-on-element/unsupported-role-on-element.test.ts +0 -12
- package/aslint/app/rules/aslint/unsupported-role-on-element/unsupported-role-on-element.ts +0 -63
- package/aslint/app/rules/aslint/used/elements-not-allowed-in-head/elements-not-allowed-in-head.documentation.md +0 -65
- package/aslint/app/rules/aslint/used/elements-not-allowed-in-head/elements-not-allowed-in-head.test.ts +0 -53
- package/aslint/app/rules/aslint/used/elements-not-allowed-in-head/elements-not-allowed-in-head.ts +0 -47
- package/aslint/app/rules/aslint/used/headings-sibling-unique/headings-sibling-unique.documentation.md +0 -57
- package/aslint/app/rules/aslint/used/headings-sibling-unique/headings-sibling-unique.test.ts +0 -52
- package/aslint/app/rules/aslint/used/headings-sibling-unique/headings-sibling-unique.ts +0 -63
- package/aslint/app/rules/aslint/used/horizontal-rule/horizontal-rule.documentation.md +0 -39
- package/aslint/app/rules/aslint/used/horizontal-rule/horizontal-rule.test.ts +0 -66
- package/aslint/app/rules/aslint/used/horizontal-rule/horizontal-rule.ts +0 -37
- package/aslint/utils/aria.test.ts +0 -12
- package/aslint/utils/aria.ts +0 -120
- package/aslint/utils/async.test.ts +0 -25
- package/aslint/utils/async.ts +0 -22
- package/aslint/utils/common.test.ts +0 -239
- package/aslint/utils/common.ts +0 -168
- package/aslint/utils/console.test.ts +0 -85
- package/aslint/utils/console.ts +0 -89
- package/aslint/utils/css.test.ts +0 -153
- package/aslint/utils/css.ts +0 -191
- package/aslint/utils/dom.test.ts +0 -627
- package/aslint/utils/dom.ts +0 -1051
- package/aslint/utils/env.test.ts +0 -14
- package/aslint/utils/env.ts +0 -8
- package/aslint/utils/func.test.ts +0 -160
- package/aslint/utils/func.ts +0 -70
- package/aslint/utils/global.test.ts +0 -12
- package/aslint/utils/global.ts +0 -25
- package/aslint/utils/object.test.ts +0 -524
- package/aslint/utils/object.ts +0 -278
- package/aslint/utils/report.test.ts +0 -56
- package/aslint/utils/report.ts +0 -36
- package/aslint/utils/text.test.ts +0 -270
- package/aslint/utils/text.ts +0 -165
- package/aslint/utils/time.test.ts +0 -43
- package/aslint/utils/time.ts +0 -33
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { ContentEditableMissingAttributes } from './content-editable-missing-attributes';
|
|
2
|
-
import { DomUtility } from '../../../utils/dom';
|
|
3
|
-
import { Validator } from '../../../validator';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('#content-editable-missing-attributes', () => {
|
|
8
|
-
|
|
9
|
-
let fakeDom;
|
|
10
|
-
|
|
11
|
-
new ContentEditableMissingAttributes().registerValidator();
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
fakeDom = document.createElement('div');
|
|
15
|
-
fakeDom.id = 'fakedom';
|
|
16
|
-
document.body.appendChild(fakeDom);
|
|
17
|
-
|
|
18
|
-
Validator.reset();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
DomUtility.remove(document.getElementById('fakedom'));
|
|
23
|
-
fakeDom = undefined;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should return 3 reports when there is an element with attribute [contenteditable] only', () => {
|
|
27
|
-
fakeDom.innerHTML = '<p contenteditable="true"></p>';
|
|
28
|
-
|
|
29
|
-
const nodes = DomUtility.querySelectorAllExclude('[contenteditable]', fakeDom);
|
|
30
|
-
|
|
31
|
-
new ContentEditableMissingAttributes().validate(nodes);
|
|
32
|
-
|
|
33
|
-
expect(Object.keys(Validator.getReports()).length).toBe(3);
|
|
34
|
-
|
|
35
|
-
expect(Validator.getReport('report_0').message).toBe('Missing attribute <code>role=\'textbox\'</code> on <code><p contenteditable="true"></p></code>');
|
|
36
|
-
expect(Validator.getReport('report_1').message).toBe('Missing attribute <code>aria-multiline=\'true\'</code> on <code><p contenteditable="true"></p></code>');
|
|
37
|
-
expect(Validator.getReport('report_2').message).toBe('Missing attribute <code>aria-labelledby</code> or <code>aria-label</code> on <code><p contenteditable="true"></p></code>');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should return 2 reports when there is an element with attribute [contenteditable] and defined attribute aria-label', () => {
|
|
41
|
-
fakeDom.innerHTML = '<p contenteditable="true" aria-label="test"></p><span id="test">test</span>';
|
|
42
|
-
|
|
43
|
-
const nodes = DomUtility.querySelectorAllExclude('[contenteditable]', fakeDom);
|
|
44
|
-
|
|
45
|
-
new ContentEditableMissingAttributes().validate(nodes);
|
|
46
|
-
|
|
47
|
-
expect(Object.keys(Validator.getReports()).length).toBe(2);
|
|
48
|
-
|
|
49
|
-
expect(Validator.getReport('report_0').message).toBe('Missing attribute <code>role=\'textbox\'</code> on <code><p contenteditable="true" aria-label="test"></p></code>');
|
|
50
|
-
expect(Validator.getReport('report_1').message).toBe('Missing attribute <code>aria-multiline=\'true\'</code> on <code><p contenteditable="true" aria-label="test"></p></code>');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should return 1 report for missing attribute role="textbox" when there is an element with attribute [contenteditable], defined attribute aria-label and aria-multiline', () => {
|
|
54
|
-
fakeDom.innerHTML = '<p contenteditable="true" aria-label="test" aria-multiline="true"></p><span id="test">test</span>';
|
|
55
|
-
|
|
56
|
-
const nodes = DomUtility.querySelectorAllExclude('[contenteditable]', fakeDom);
|
|
57
|
-
|
|
58
|
-
new ContentEditableMissingAttributes().validate(nodes);
|
|
59
|
-
|
|
60
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
61
|
-
|
|
62
|
-
expect(Validator.getReport('report_0').message).toBe('Missing attribute <code>role=\'textbox\'</code> on <code><p contenteditable="true" aria-label="test" aria-multiline="true"></p></code>');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
3
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
4
|
-
import { TextUtility } from '../../../utils/text';
|
|
5
|
-
import { TranslateService } from '../../../services/translate';
|
|
6
|
-
import { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
|
|
7
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
8
|
-
|
|
9
|
-
export class ContentEditableMissingAttributes extends AbstractRule {
|
|
10
|
-
protected selector: string = '[contenteditable]';
|
|
11
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
12
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.content_editable_missing_attributes),
|
|
13
|
-
links: [],
|
|
14
|
-
recommendations: [],
|
|
15
|
-
severity: $severity.low,
|
|
16
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
public validate(elements: Element[]): void {
|
|
20
|
-
const reportMissingAttributes = (element: Element): void => {
|
|
21
|
-
let problem: IIssueReport;
|
|
22
|
-
const roleValue: string | null = element.getAttribute('role');
|
|
23
|
-
|
|
24
|
-
if (roleValue !== 'textbox') {
|
|
25
|
-
const reportMessage: string = TranslateService.instant('content_editable_missing_attributes_report_message1', [DomUtility.getEscapedOuterHTML(element)]);
|
|
26
|
-
|
|
27
|
-
problem = {
|
|
28
|
-
message: reportMessage,
|
|
29
|
-
node: element,
|
|
30
|
-
ruleId: this.ruleConfig.id
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
this.validator.report(problem);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (element.getAttribute('aria-multiline') === null) {
|
|
37
|
-
const reportMessage: string = TranslateService.instant('content_editable_missing_attributes_report_message2', [DomUtility.getEscapedOuterHTML(element)]);
|
|
38
|
-
|
|
39
|
-
problem = {
|
|
40
|
-
message: reportMessage,
|
|
41
|
-
node: element,
|
|
42
|
-
ruleId: this.ruleConfig.id
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
this.validator.report(problem);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (element.getAttribute('aria-labelledby') === null && element.getAttribute('aria-label') === null) {
|
|
49
|
-
const reportMessage: string = TranslateService.instant('content_editable_missing_attributes_report_message3', [DomUtility.getEscapedOuterHTML(element)]);
|
|
50
|
-
|
|
51
|
-
problem = {
|
|
52
|
-
message: reportMessage,
|
|
53
|
-
node: element,
|
|
54
|
-
ruleId: this.ruleConfig.id
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
this.validator.report(problem);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
elements.forEach(reportMissingAttributes);
|
|
62
|
-
}
|
|
63
|
-
}
|
package/aslint/app/rules/aslint/unsupported-role-on-element/unsupported-role-on-element.test.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { UnsupportedRoleOnElement } from './unsupported-role-on-element';
|
|
2
|
-
|
|
3
|
-
describe('Rules', () => {
|
|
4
|
-
|
|
5
|
-
describe('UnsupportedRoleOnElement', () => {
|
|
6
|
-
|
|
7
|
-
it('should indicate that class exists', () => {
|
|
8
|
-
expect(UnsupportedRoleOnElement).toBeDefined();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
2
|
-
import { TAG_TO_IMPLICIT_SEMANTIC_INFO } from '../../../constants/aria';
|
|
3
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
4
|
-
import { TextUtility } from '../../../utils/text';
|
|
5
|
-
import { TranslateService } from '../../../services/translate';
|
|
6
|
-
import { $severity } from '../../../constants/accessibility';
|
|
7
|
-
import { $accessibilityAuditRules } from '../../../constants/accessibility';
|
|
8
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
9
|
-
|
|
10
|
-
export class UnsupportedRoleOnElement extends AbstractRule {
|
|
11
|
-
protected selector: string = 'a[href="#"], a[href*=javascript\\:], a[href=""]';
|
|
12
|
-
|
|
13
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
14
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.unsupported_role_on_element),
|
|
15
|
-
links: [],
|
|
16
|
-
recommendations: [],
|
|
17
|
-
severity: $severity.high,
|
|
18
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
public validate(anchorElements: HTMLAnchorElement[]): void {
|
|
22
|
-
const checkSingleNode = (anchorElement: HTMLAnchorElement): void => {
|
|
23
|
-
const tagName: string = anchorElement.nodeName.toUpperCase();
|
|
24
|
-
const roleValue: string | null = anchorElement.getAttribute('role');
|
|
25
|
-
|
|
26
|
-
if (typeof TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName] === 'undefined') {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (roleValue === null) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
Array.isArray(TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName][0].allowed) &&
|
|
36
|
-
(TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName][0].allowed as string[]).indexOf(
|
|
37
|
-
roleValue
|
|
38
|
-
) !== -1
|
|
39
|
-
) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const reportMessage: string = TranslateService.instant(
|
|
44
|
-
'unsupported_role_on_element_report_message',
|
|
45
|
-
[roleValue]
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const report: IIssueReport = {
|
|
49
|
-
message: reportMessage,
|
|
50
|
-
node: anchorElement,
|
|
51
|
-
ruleId: this.ruleConfig.id
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
this.validator.report(report);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const nodesLength: number = anchorElements.length;
|
|
58
|
-
|
|
59
|
-
if (nodesLength > 0) {
|
|
60
|
-
anchorElements.forEach(checkSingleNode);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# elements-not-allowed-in-head
|
|
2
|
-
|
|
3
|
-
## Rule id
|
|
4
|
-
|
|
5
|
-
`elements-not-allowed-in-head`
|
|
6
|
-
|
|
7
|
-
## Definition
|
|
8
|
-
|
|
9
|
-
This rule verifies if there are elements that shouldn't be in the `<head>` section.
|
|
10
|
-
|
|
11
|
-
## Purpose
|
|
12
|
-
|
|
13
|
-
`<head>` section contains elements that provides information of how the document should be perceived, and rendered, by web technologies. e.g. browsers, search engines, bots, screen readers, etc.
|
|
14
|
-
|
|
15
|
-
Non valid pages rely on the browser to auto-correct your code, and each browser does this differently. If your code is valid the browser has to do less processing as it doesn't have to correct any code, therefore the page will render faster and more predictable.
|
|
16
|
-
|
|
17
|
-
In order to make the code accurate processed and rendered it is recommended to have only valid HTML elements / tags in the `<head>` section.
|
|
18
|
-
|
|
19
|
-
Following elements are allowed to be in the `<head>` section:
|
|
20
|
-
|
|
21
|
-
<title>
|
|
22
|
-
<base>
|
|
23
|
-
<link>
|
|
24
|
-
<style>
|
|
25
|
-
<meta>
|
|
26
|
-
<script>
|
|
27
|
-
<noscript>
|
|
28
|
-
<template>
|
|
29
|
-
|
|
30
|
-
All other elements / tags are perceived as non-valid.
|
|
31
|
-
|
|
32
|
-
## Test cases
|
|
33
|
-
|
|
34
|
-
### Passed
|
|
35
|
-
|
|
36
|
-
The rule passes when only following elements / tags are specified in the `<head>` section:
|
|
37
|
-
|
|
38
|
-
<title>
|
|
39
|
-
<base>
|
|
40
|
-
<link>
|
|
41
|
-
<style>
|
|
42
|
-
<meta>
|
|
43
|
-
<script>
|
|
44
|
-
<noscript>
|
|
45
|
-
<template>
|
|
46
|
-
|
|
47
|
-
## WCAG Success Criteria
|
|
48
|
-
|
|
49
|
-
Not Applicable
|
|
50
|
-
|
|
51
|
-
## Best Practice
|
|
52
|
-
|
|
53
|
-
Yes
|
|
54
|
-
|
|
55
|
-
## User Impact
|
|
56
|
-
|
|
57
|
-
* **Severity**: low
|
|
58
|
-
* **Disabilities Affected**:
|
|
59
|
-
* Visual:
|
|
60
|
-
* blindness
|
|
61
|
-
|
|
62
|
-
## Resources
|
|
63
|
-
|
|
64
|
-
* https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML
|
|
65
|
-
* https://html.spec.whatwg.org/multipage/semantics.html#the-head-element
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { ElementsNotAllowed } from './elements-not-allowed-in-head';
|
|
2
|
-
import { DomUtility } from '../../../utils/dom';
|
|
3
|
-
import { Validator } from '../../../validator';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('ElementsNotAllowed#', () => {
|
|
8
|
-
|
|
9
|
-
let fakeDom;
|
|
10
|
-
const VALID_ELEMENTS: string[] = [':not(base)', ':not(link)', ':not(meta)', ':not(script)', ':not(style)', ':not(title)', ':not(noscript)', ':not(template)'];
|
|
11
|
-
const selector = `head *${VALID_ELEMENTS.join('')}`;
|
|
12
|
-
|
|
13
|
-
new ElementsNotAllowed().registerValidator();
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
Validator.reset();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
DomUtility.remove(document.getElementById('headfakedom'));
|
|
21
|
-
fakeDom = undefined;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should return one report when there is an element not supposed to be in head block', () => {
|
|
25
|
-
fakeDom = document.createElement('div');
|
|
26
|
-
fakeDom.id = 'headfakedom';
|
|
27
|
-
document.head.appendChild(fakeDom);
|
|
28
|
-
|
|
29
|
-
const nodes = DomUtility.querySelectorAllExclude(selector);
|
|
30
|
-
|
|
31
|
-
new ElementsNotAllowed().validate(nodes);
|
|
32
|
-
|
|
33
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
34
|
-
expect(Validator.getReport('report_0').message).toBe('Expected <code><div id="headfakedom"></div></code> not to be a child of <code><head></code>.');
|
|
35
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('div');
|
|
36
|
-
expect(Validator.getReport('report_0').ruleId).toBe('elements-not-allowed-in-head');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should return no reports when there is no invalid elements in head section', () => {
|
|
40
|
-
fakeDom = document.createElement('noscript');
|
|
41
|
-
fakeDom.id = 'headfakedom';
|
|
42
|
-
document.head.appendChild(fakeDom);
|
|
43
|
-
|
|
44
|
-
const nodes = DomUtility.querySelectorAllExclude(selector);
|
|
45
|
-
|
|
46
|
-
new ElementsNotAllowed().validate(nodes);
|
|
47
|
-
|
|
48
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
});
|
package/aslint/app/rules/aslint/used/elements-not-allowed-in-head/elements-not-allowed-in-head.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { TextUtility } from '../../../utils/text';
|
|
3
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
4
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
5
|
-
import { TranslateService } from '../../../services/translate';
|
|
6
|
-
import { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
|
|
7
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
8
|
-
|
|
9
|
-
export class ElementsNotAllowed extends AbstractRule {
|
|
10
|
-
protected selector: string = `head *${[
|
|
11
|
-
':not(base)',
|
|
12
|
-
':not(link)',
|
|
13
|
-
':not(meta)',
|
|
14
|
-
':not(script)',
|
|
15
|
-
':not(style)',
|
|
16
|
-
':not(title)',
|
|
17
|
-
':not(noscript)',
|
|
18
|
-
':not(template)'
|
|
19
|
-
].join('')}`;
|
|
20
|
-
|
|
21
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
22
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.elements_not_allowed_in_head),
|
|
23
|
-
links: [],
|
|
24
|
-
recommendations: [],
|
|
25
|
-
severity: $severity.low,
|
|
26
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
public validate(elements: Element[]): void {
|
|
30
|
-
const reportNode = (element: Element): void => {
|
|
31
|
-
const reportMessage: string = TranslateService.instant('elements_not_allowed_in_head_report_message', [
|
|
32
|
-
DomUtility.getEscapedOuterHTML(element),
|
|
33
|
-
TextUtility.escape('<head>')
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
const report: IIssueReport = {
|
|
37
|
-
message: reportMessage,
|
|
38
|
-
node: element,
|
|
39
|
-
ruleId: this.ruleConfig.id
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
this.validator.report(report);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
elements.forEach(reportNode);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# headings-sibling-unique
|
|
2
|
-
|
|
3
|
-
## Rule id
|
|
4
|
-
|
|
5
|
-
`headings-sibling-unique`
|
|
6
|
-
|
|
7
|
-
## Definition
|
|
8
|
-
|
|
9
|
-
This rule verifies if the accessible names of sibling heading elements of the same level are unique.
|
|
10
|
-
|
|
11
|
-
## Purpose
|
|
12
|
-
|
|
13
|
-
If section headings that share the same parent heading are not unique, users of assistive technologies will not be able to discern the differences among sibling sections of the web page.
|
|
14
|
-
|
|
15
|
-
This rule checks 1 case:
|
|
16
|
-
|
|
17
|
-
* Sibling headings accessible names must be unique.
|
|
18
|
-
|
|
19
|
-
### Example
|
|
20
|
-
|
|
21
|
-
#### Incorrect
|
|
22
|
-
|
|
23
|
-
<h2>Example</h2>
|
|
24
|
-
<h2>Example</h2>
|
|
25
|
-
|
|
26
|
-
#### Correct
|
|
27
|
-
|
|
28
|
-
<h2>Example 1</h2>
|
|
29
|
-
<h2>Example 2</h2>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Test cases
|
|
33
|
-
|
|
34
|
-
### Passed
|
|
35
|
-
|
|
36
|
-
The rule passes when sibiling headings are having unique accessible name.
|
|
37
|
-
|
|
38
|
-
## WCAG Success Criteria
|
|
39
|
-
|
|
40
|
-
Not Applicable
|
|
41
|
-
|
|
42
|
-
## Best Practice
|
|
43
|
-
|
|
44
|
-
Yes
|
|
45
|
-
|
|
46
|
-
## User Impact
|
|
47
|
-
|
|
48
|
-
* **Severity**: critical
|
|
49
|
-
* **Disabilities Affected**:
|
|
50
|
-
* Visual:
|
|
51
|
-
* blindness
|
|
52
|
-
|
|
53
|
-
## Resources
|
|
54
|
-
|
|
55
|
-
* http://www.w3.org/TR/WCAG20/#navigation-mechanisms-descriptive
|
|
56
|
-
* http://www.w3.org/TR/WCAG20-TECHS/G130
|
|
57
|
-
* https://www.w3.org/TR/WCAG20-TECHS/G141.html
|
package/aslint/app/rules/aslint/used/headings-sibling-unique/headings-sibling-unique.test.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { HeadingsSiblingUnique } from './headings-sibling-unique';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('HeadingsSiblingUnique', () => {
|
|
8
|
-
|
|
9
|
-
it('should indicate that class exists', () => {
|
|
10
|
-
expect(HeadingsSiblingUnique).toBeDefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
let fakeDom;
|
|
14
|
-
|
|
15
|
-
new HeadingsSiblingUnique().registerValidator();
|
|
16
|
-
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
fakeDom = document.createElement('div');
|
|
19
|
-
fakeDom.id = 'fakedom';
|
|
20
|
-
document.body.appendChild(fakeDom);
|
|
21
|
-
|
|
22
|
-
Validator.reset();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
afterEach(() => {
|
|
26
|
-
DomUtility.remove(document.getElementById('fakedom'));
|
|
27
|
-
fakeDom = undefined;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should return 1 report when there are non-unique siblings headings', () => {
|
|
31
|
-
fakeDom.innerHTML = '<h1>Test</h1><h1>Test</h1>';
|
|
32
|
-
const nodes = DomUtility.querySelectorAllExclude('h1', fakeDom) as HTMLHeadingElement[];
|
|
33
|
-
|
|
34
|
-
new HeadingsSiblingUnique().validate(nodes);
|
|
35
|
-
|
|
36
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
37
|
-
expect(Validator.getReport('report_0').message).toBe('The accessible names of sibling heading elements of the same level are not unique. If section headings that share the same parent heading are not unique, users of assistive technologies will not be able to discern the differences among sibling sections of the web page. Same level <code>h1</code> and same description: <q>Test</q>.');
|
|
38
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('h1');
|
|
39
|
-
expect(Validator.getReport('report_0').ruleId).toBe('headings-sibling-unique');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should return no reports when two headings description are different', () => {
|
|
43
|
-
fakeDom.innerHTML = '<h1>Test</h1><h1>Test 2</h1>';
|
|
44
|
-
const nodes = DomUtility.querySelectorAllExclude('h1', fakeDom) as HTMLHeadingElement[];
|
|
45
|
-
|
|
46
|
-
new HeadingsSiblingUnique().validate(nodes);
|
|
47
|
-
|
|
48
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { TextUtility } from '../../../utils/text';
|
|
2
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
3
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
4
|
-
import { TranslateService } from '../../../services/translate';
|
|
5
|
-
import { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
|
|
6
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
7
|
-
|
|
8
|
-
export class HeadingsSiblingUnique extends AbstractRule {
|
|
9
|
-
protected selector: string = 'h1, h2, h3, h4, h5, h6';
|
|
10
|
-
|
|
11
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
12
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.headings_sibling_unique),
|
|
13
|
-
links: [
|
|
14
|
-
{
|
|
15
|
-
content: '2.4.6 Headings and Labels (Level AA, Primary Success Criterion)',
|
|
16
|
-
url: 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-descriptive'
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
content: 'G130: Providing descriptive headings',
|
|
20
|
-
url: 'http://www.w3.org/TR/WCAG20-TECHS/G130'
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
content: 'G141: Organizing a page using headings',
|
|
24
|
-
url: 'https://www.w3.org/TR/WCAG20-TECHS/G141.html'
|
|
25
|
-
}
|
|
26
|
-
],
|
|
27
|
-
recommendations: [],
|
|
28
|
-
severity: $severity.high,
|
|
29
|
-
type: CATEGORY_TYPE.WCAG_AA
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
public validate(headings: HTMLHeadingElement[]): void {
|
|
33
|
-
|
|
34
|
-
const checkForSiblings = (headingElement: HTMLHeadingElement): void => {
|
|
35
|
-
const nextElementSibling: Element | null = headingElement.nextElementSibling;
|
|
36
|
-
|
|
37
|
-
if (nextElementSibling === null) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (nextElementSibling.nodeName.toLowerCase() !== headingElement.nodeName.toLowerCase()) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const headingText: string = typeof headingElement.textContent === 'string' ? TextUtility.safeTrim(headingElement.textContent) : '';
|
|
46
|
-
const nextElementSiblingText: string = typeof nextElementSibling.textContent === 'string' ? TextUtility.safeTrim(nextElementSibling.textContent) : '';
|
|
47
|
-
|
|
48
|
-
if (TextUtility.areStringsTheSame(headingText, nextElementSiblingText) === false) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const report: IIssueReport = {
|
|
53
|
-
message: TranslateService.instant('headings_sibling_unique_report_message', [headingElement.nodeName.toLowerCase(), nextElementSibling.textContent]),
|
|
54
|
-
node: headingElement,
|
|
55
|
-
ruleId: this.ruleConfig.id
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
this.validator.report(report);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
headings.forEach(checkForSiblings);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# horizontal-rule
|
|
2
|
-
|
|
3
|
-
## Rule id
|
|
4
|
-
|
|
5
|
-
`horizontal-rule`
|
|
6
|
-
|
|
7
|
-
## Definition
|
|
8
|
-
|
|
9
|
-
This rule determines `<hr/>` elements and gives a tip.
|
|
10
|
-
|
|
11
|
-
## Purpose
|
|
12
|
-
|
|
13
|
-
The `<hr/>` element adds extra "noise" and can be confusing. For example VoiceOver reads it as "dimmed collapsed on top, horizontal separator", Windows Narrator reads it as "end of line".
|
|
14
|
-
|
|
15
|
-
A better option is to replace `<hr/>` with `<div>` and use CSS for styling. Alternatively, `aria-hidden="true"` or `role="presentation"` can be applied to the `<hr/>` element.
|
|
16
|
-
|
|
17
|
-
## Test cases
|
|
18
|
-
|
|
19
|
-
### Passed
|
|
20
|
-
|
|
21
|
-
The rule passes when there are:
|
|
22
|
-
|
|
23
|
-
* no `<hr/>` elements or
|
|
24
|
-
* `<hr/>` use `aria-hidden="true"` or `role="presentation"`
|
|
25
|
-
|
|
26
|
-
## WCAG Success Criteria
|
|
27
|
-
|
|
28
|
-
Not Applicable
|
|
29
|
-
|
|
30
|
-
## Best Practice
|
|
31
|
-
|
|
32
|
-
Yes
|
|
33
|
-
|
|
34
|
-
## User Impact
|
|
35
|
-
|
|
36
|
-
* **Severity**: info
|
|
37
|
-
* **Disabilities Affected**:
|
|
38
|
-
* Visual:
|
|
39
|
-
* blindness
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { HorizontalRule } from './horizontal-rule';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { DomUtility } from '../../../utils/dom';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('HorizontalRule', () => {
|
|
8
|
-
|
|
9
|
-
it('should indicate that class exists', () => {
|
|
10
|
-
expect(HorizontalRule).toBeDefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const selector: string = `hr${[
|
|
14
|
-
':not([aria-hidden="true"])',
|
|
15
|
-
':not([role="presentation"])'
|
|
16
|
-
].join('')}`;
|
|
17
|
-
|
|
18
|
-
let fakeDom;
|
|
19
|
-
|
|
20
|
-
new HorizontalRule().registerValidator();
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
fakeDom = document.createElement('div');
|
|
24
|
-
fakeDom.id = 'fakedom';
|
|
25
|
-
document.body.appendChild(fakeDom);
|
|
26
|
-
|
|
27
|
-
Validator.reset();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
DomUtility.remove(document.getElementById('fakedom'));
|
|
32
|
-
fakeDom = undefined;
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should return one report when there is an element with hr', () => {
|
|
36
|
-
fakeDom.innerHTML = '<p>Text 1</p><hr/><p>Text 2</p>';
|
|
37
|
-
const nodes: HTMLHRElement[] = DomUtility.querySelectorAllExclude(selector, fakeDom) as HTMLHRElement[];
|
|
38
|
-
|
|
39
|
-
new HorizontalRule().validate(nodes);
|
|
40
|
-
|
|
41
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
42
|
-
expect(Validator.getReport('report_0').message).toBe('The <code><hr></code> element adds extra <q>noise</q> and can be confusing. For example VoiceOver reads it as <q>dimmed collapsed on top, horizontal separator</q>, Windows Narrator reads it as <q>end of line</q>. A better option is to replace <code><hr></code> with <code><div></code> and use CSS for styling. Alternatively, <code>aria-hidden=\'true\'</code> or <code>role=\'presentation\'</code> can be applied to the <code><hr></code> element.');
|
|
43
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('hr');
|
|
44
|
-
expect(Validator.getReport('report_0').ruleId).toBe('horizontal-rule');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should return no reports when there is no elements with hr', () => {
|
|
48
|
-
fakeDom.innerHTML = '<p>Text 1</p>hr<p>Text 2</p>';
|
|
49
|
-
const nodes: HTMLHRElement[] = DomUtility.querySelectorAllExclude(selector, fakeDom) as HTMLHRElement[];
|
|
50
|
-
|
|
51
|
-
new HorizontalRule().validate(nodes);
|
|
52
|
-
|
|
53
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should return no reports when there is an element hr, but with attribue aria-hidden="true" or role="presentation"', () => {
|
|
57
|
-
fakeDom.innerHTML = '<p>Text 1</p><hr aria-hidden="true"/><hr role="presentation"/><p>Text 2</p>';
|
|
58
|
-
const nodes: HTMLHRElement[] = DomUtility.querySelectorAllExclude(selector, fakeDom) as HTMLHRElement[];
|
|
59
|
-
|
|
60
|
-
new HorizontalRule().validate(nodes);
|
|
61
|
-
|
|
62
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
});
|