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,70 +0,0 @@
|
|
|
1
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
2
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
3
|
-
import { TextUtility } from '../../../utils/text';
|
|
4
|
-
import { TranslateService } from '../../../services/translate';
|
|
5
|
-
import { $severity } from '../../../constants/accessibility';
|
|
6
|
-
import { $accessibilityAuditRules } from '../../../constants/accessibility';
|
|
7
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
8
|
-
|
|
9
|
-
export class LinksLanguageDestination extends AbstractRule {
|
|
10
|
-
protected selector: () => (HTMLAnchorElement | HTMLAreaElement)[] = (): (HTMLAnchorElement | HTMLAreaElement)[] => {
|
|
11
|
-
return Array.from(document.links);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
15
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.links_language_destination),
|
|
16
|
-
links: [
|
|
17
|
-
{
|
|
18
|
-
content: 'Indicating the language of a link destination',
|
|
19
|
-
url: 'https://www.w3.org/International/questions/qa-link-lang'
|
|
20
|
-
}
|
|
21
|
-
],
|
|
22
|
-
recommendations: [],
|
|
23
|
-
severity: $severity.info,
|
|
24
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
public validate(links: (HTMLAnchorElement | HTMLAreaElement)[]): void {
|
|
28
|
-
let baseURI: string;
|
|
29
|
-
|
|
30
|
-
if (typeof document.baseURI === 'string') {
|
|
31
|
-
baseURI = document.baseURI;
|
|
32
|
-
} else {
|
|
33
|
-
baseURI = document.URL || document.location.href;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const onlyIncludeExternalLinks = (link: HTMLAnchorElement | HTMLAreaElement): boolean => {
|
|
37
|
-
if (link.hostname.length > 0 && link.href.length > 0) {
|
|
38
|
-
let baseHostname: string;
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const url: URL = new URL(link.href, baseURI);
|
|
42
|
-
|
|
43
|
-
baseHostname = url.hostname;
|
|
44
|
-
} catch (_) {
|
|
45
|
-
baseHostname = document.location.hostname;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return baseHostname !== window.location.hostname;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return false;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const externalLinks: (HTMLAnchorElement | HTMLAreaElement)[] = links.filter(onlyIncludeExternalLinks);
|
|
55
|
-
|
|
56
|
-
const reportIssue = (anchorElement: (HTMLAnchorElement | HTMLAreaElement)): void => {
|
|
57
|
-
const reportMessage: string = TranslateService.instant('links_language_destination_report_message', [TextUtility.escape(anchorElement.href)]);
|
|
58
|
-
|
|
59
|
-
const report: IIssueReport = {
|
|
60
|
-
message: reportMessage,
|
|
61
|
-
node: anchorElement,
|
|
62
|
-
ruleId: this.ruleConfig.id
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
this.validator.report(report);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
externalLinks.forEach(reportIssue);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { MainElementOnlyOne } from './main-element-only-one';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { DomUtility } from '../../../utils/dom';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('MainElementOnlyOne', () => {
|
|
8
|
-
|
|
9
|
-
it('should indicate that class exists', () => {
|
|
10
|
-
expect(MainElementOnlyOne).toBeDefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
let fakeDom;
|
|
14
|
-
|
|
15
|
-
new MainElementOnlyOne().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 one report when there is more than 1 main element', () => {
|
|
31
|
-
fakeDom.innerHTML = '<main>Text1</main><main>Text2</main>';
|
|
32
|
-
const nodes = DomUtility.querySelectorAllExclude('main', fakeDom) as HTMLElement[];
|
|
33
|
-
|
|
34
|
-
new MainElementOnlyOne().validate(nodes);
|
|
35
|
-
|
|
36
|
-
expect(Object.keys(Validator.getReports()).length).toBe(2);
|
|
37
|
-
expect(Validator.getReport('report_0').message).toBe('You have defined multiple (2) <code><main></code> elements. Assistive technology users expect one main content block and may miss subsequent <code><main></code> blocks.');
|
|
38
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('main');
|
|
39
|
-
expect(Validator.getReport('report_0').ruleId).toBe('main-element-only-one');
|
|
40
|
-
expect(Validator.getReport('report_1').message).toBe('You have defined multiple (2) <code><main></code> elements. Assistive technology users expect one main content block and may miss subsequent <code><main></code> blocks.');
|
|
41
|
-
expect(Validator.getReport('report_1').node.nodeName.toLowerCase()).toBe('main');
|
|
42
|
-
expect(Validator.getReport('report_1').ruleId).toBe('main-element-only-one');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should return no reports when there is 1 main element', () => {
|
|
46
|
-
fakeDom.innerHTML = '<main>Text1</main>';
|
|
47
|
-
const nodes = DomUtility.querySelectorAllExclude('main', fakeDom) as HTMLElement[];
|
|
48
|
-
|
|
49
|
-
new MainElementOnlyOne().validate(nodes);
|
|
50
|
-
|
|
51
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
});
|
|
55
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
2
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
3
|
-
import { TextUtility } from '../../../utils/text';
|
|
4
|
-
import { TranslateService } from '../../../services/translate';
|
|
5
|
-
import { $auditRuleNodeSkipReason, $severity } from '../../../constants/accessibility';
|
|
6
|
-
import { $accessibilityAuditRules } from '../../../constants/accessibility';
|
|
7
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
8
|
-
import { DomUtility } from '../../../utils/dom';
|
|
9
|
-
import { Config } from '../../../config';
|
|
10
|
-
import { $runnerSettings } from '../../../constants/aslint';
|
|
11
|
-
import { Css } from '../../../utils/css';
|
|
12
|
-
|
|
13
|
-
export class MainElementOnlyOne extends AbstractRule {
|
|
14
|
-
private appConfig: Config = Config.getInstance();
|
|
15
|
-
|
|
16
|
-
protected selector: string = 'main';
|
|
17
|
-
|
|
18
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
19
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.main_element_only_one),
|
|
20
|
-
links: [
|
|
21
|
-
{
|
|
22
|
-
content: 'HTML/Elements/main',
|
|
23
|
-
url: 'https://www.w3.org/wiki/HTML/Elements/main#Point'
|
|
24
|
-
}
|
|
25
|
-
],
|
|
26
|
-
recommendations: [],
|
|
27
|
-
severity: $severity.high,
|
|
28
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
private elementShouldBeSkipped(element: HTMLElement): boolean {
|
|
32
|
-
const styles: CSSStyleDeclaration | null = Css.getComputedStyle(element);
|
|
33
|
-
const ariaHidden: Attr | null = DomUtility.getElementAttribute(element, 'aria-hidden');
|
|
34
|
-
let elementShouldBeSkipped: boolean = true;
|
|
35
|
-
|
|
36
|
-
if (ariaHidden && ariaHidden.value === 'true') {
|
|
37
|
-
return elementShouldBeSkipped;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (this.appConfig.get($runnerSettings.includeHidden)) {
|
|
41
|
-
elementShouldBeSkipped = false;
|
|
42
|
-
|
|
43
|
-
return elementShouldBeSkipped;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (DomUtility.hasElementSemiOpacity(element, styles)) {
|
|
47
|
-
return elementShouldBeSkipped;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public validate(elements: HTMLElement[]): void {
|
|
54
|
-
if (elements.length < 2) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const checkForIssue = (element: HTMLElement): void => {
|
|
59
|
-
if (this.elementShouldBeSkipped(element)) {
|
|
60
|
-
this.validator.report({
|
|
61
|
-
message: '',
|
|
62
|
-
node: element,
|
|
63
|
-
ruleId: this.ruleConfig.id,
|
|
64
|
-
skipReason: $auditRuleNodeSkipReason.excludedFromScanning
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const reportMessage: string = TranslateService.instant('main_element_only_one_report_message', [elements.length, TextUtility.escape('main')]);
|
|
71
|
-
|
|
72
|
-
const report: IIssueReport = {
|
|
73
|
-
message: reportMessage,
|
|
74
|
-
node: element,
|
|
75
|
-
ruleId: this.ruleConfig.id
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
this.validator.report(report);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
elements.forEach(checkForIssue);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { MainLandmarkMustBeTopLevel } from './main-landmark-must-be-top-level';
|
|
2
|
-
|
|
3
|
-
describe('Rules', () => {
|
|
4
|
-
|
|
5
|
-
describe('MainLandmarkMustBeTopLevel', () => {
|
|
6
|
-
|
|
7
|
-
it('should indicate that class exists', () => {
|
|
8
|
-
expect(MainLandmarkMustBeTopLevel).toBeDefined();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
});
|
package/aslint/app/rules/aslint/main-landmark-must-be-top-level/main-landmark-must-be-top-level.ts
DELETED
|
@@ -1,73 +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 { $severity } from '../../../constants/accessibility';
|
|
7
|
-
import { $accessibilityAuditRules } from '../../../constants/accessibility';
|
|
8
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
9
|
-
|
|
10
|
-
export class MainLandmarkMustBeTopLevel extends AbstractRule {
|
|
11
|
-
protected selector: string = '[role="main"]';
|
|
12
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
13
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.main_landmark_must_be_top_level),
|
|
14
|
-
links: [
|
|
15
|
-
{
|
|
16
|
-
content: 'Accessible Rich Internet Applications (WAI-ARIA) 1.0 Specification: main role',
|
|
17
|
-
url: 'http://www.w3.org/TR/wai-aria/roles#main'
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
content: 'ARIA11: Using ARIA landmarks to identify regions of a page',
|
|
21
|
-
url: 'http://www.w3.org/TR/WCAG20-TECHS/ARIA11'
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
content: 'HTML5: The MAIN element',
|
|
25
|
-
url: 'http://www.w3.org/TR/html5/sections.html#the-main-element'
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
recommendations: [],
|
|
29
|
-
severity: $severity.high,
|
|
30
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
public validate(elements: Element[]): void {
|
|
34
|
-
let parent: HTMLElement | null;
|
|
35
|
-
let parentNodeWithRole: Element | null = null;
|
|
36
|
-
let role: string | null;
|
|
37
|
-
const PARENT_ROLE_EXCEPTION: string[] = [
|
|
38
|
-
'application',
|
|
39
|
-
'document'
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
if (elements.length === 0 || elements[0].parentElement === null) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
parent = elements[0].parentElement;
|
|
47
|
-
|
|
48
|
-
while (parent && parent.getAttribute) {
|
|
49
|
-
role = parent.getAttribute('role');
|
|
50
|
-
|
|
51
|
-
if (typeof role === 'string' && PARENT_ROLE_EXCEPTION.indexOf(role) === -1) {
|
|
52
|
-
parentNodeWithRole = parent;
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
parent = parent.parentElement;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (parentNodeWithRole === null) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const reportMessage: string = TranslateService.instant('main_landmark_must_be_top_level_report_message', [DomUtility.getEscapedOuterHTML(parentNodeWithRole)]);
|
|
64
|
-
|
|
65
|
-
const report: IIssueReport = {
|
|
66
|
-
message: reportMessage,
|
|
67
|
-
node: parentNodeWithRole,
|
|
68
|
-
ruleId: this.ruleConfig.id
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
this.validator.report(report);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { Css } from '../../../utils/css';
|
|
3
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
4
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
5
|
-
import { TextUtility } from '../../../utils/text';
|
|
6
|
-
import { TranslateService } from '../../../services/translate';
|
|
7
|
-
import { $severity } from '../../../constants/accessibility';
|
|
8
|
-
import { $accessibilityAuditRules } from '../../../constants/accessibility';
|
|
9
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
10
|
-
|
|
11
|
-
export class MinimumFontSize extends AbstractRule {
|
|
12
|
-
protected selector: string = `*${[
|
|
13
|
-
':root',
|
|
14
|
-
'head',
|
|
15
|
-
'title',
|
|
16
|
-
'style',
|
|
17
|
-
'script',
|
|
18
|
-
'noscript',
|
|
19
|
-
'meta',
|
|
20
|
-
'link',
|
|
21
|
-
'br',
|
|
22
|
-
'hr',
|
|
23
|
-
'object',
|
|
24
|
-
'path',
|
|
25
|
-
'g',
|
|
26
|
-
'desc',
|
|
27
|
-
'filter',
|
|
28
|
-
'img',
|
|
29
|
-
'input',
|
|
30
|
-
'iframe',
|
|
31
|
-
'code',
|
|
32
|
-
'defs',
|
|
33
|
-
':empty'
|
|
34
|
-
].map((i: string): string => {
|
|
35
|
-
return `:not(${i})`;
|
|
36
|
-
}).join('')}`;
|
|
37
|
-
|
|
38
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
39
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.minimum_font_size),
|
|
40
|
-
links: [],
|
|
41
|
-
recommendations: [],
|
|
42
|
-
severity: $severity.info,
|
|
43
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
public validate(elements: HTMLElement[]): void {
|
|
47
|
-
const checkFontSize = (element: HTMLElement): void => {
|
|
48
|
-
if (DomUtility.hasDirectTextDescendant(element) === false) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const fontSizeStyle: string | null = Css.getStyle(element, 'font-size');
|
|
53
|
-
|
|
54
|
-
if (fontSizeStyle === null) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const fontSize: number = parseInt(fontSizeStyle, 10);
|
|
59
|
-
|
|
60
|
-
if (fontSize < 10) {
|
|
61
|
-
let report: IIssueReport;
|
|
62
|
-
|
|
63
|
-
if (DomUtility.isElementVisible(element) === false) {
|
|
64
|
-
const reportMessage: string = TranslateService.instant('minimum_font_size_report_message_1', [fontSize]);
|
|
65
|
-
|
|
66
|
-
report = {
|
|
67
|
-
message: reportMessage,
|
|
68
|
-
node: element,
|
|
69
|
-
ruleId: this.ruleConfig.id
|
|
70
|
-
};
|
|
71
|
-
} else {
|
|
72
|
-
const reportMessage: string = TranslateService.instant('minimum_font_size_report_message_2', [fontSize]);
|
|
73
|
-
|
|
74
|
-
report = {
|
|
75
|
-
message: reportMessage,
|
|
76
|
-
node: element,
|
|
77
|
-
ruleId: this.ruleConfig.id
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
this.validator.report(report);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
elements.forEach(checkFontSize);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { MissingHrefOnA } from './missing-href-on-a';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { DomUtility } from '../../../utils/dom';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('MissingHrefOnA', () => {
|
|
8
|
-
|
|
9
|
-
let fakeDom;
|
|
10
|
-
|
|
11
|
-
new MissingHrefOnA().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 correct data for a element without href attribute', () => {
|
|
27
|
-
fakeDom.innerHTML = '<a>example.com</a>';
|
|
28
|
-
const nodes = DomUtility.querySelectorAllExclude('a:not([href])', fakeDom) as HTMLAnchorElement[];
|
|
29
|
-
|
|
30
|
-
new MissingHrefOnA().validate(nodes);
|
|
31
|
-
|
|
32
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
33
|
-
expect(Validator.getReport('report_0').message).toBe('Missing attribute <code>href</code> on link. The user cannot navigate to this element using the keyboard. A better option here is to use a <code><a></code> element instead.');
|
|
34
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('a');
|
|
35
|
-
expect(Validator.getReport('report_0').ruleId).toBe('missing-href-on-a');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should return no reports in case of a element with href attribute', () => {
|
|
39
|
-
fakeDom.innerHTML = '<a href="http://example.com">example.com</a>';
|
|
40
|
-
const nodes = DomUtility.querySelectorAllExclude('a:not([href])', fakeDom) as HTMLAnchorElement[];
|
|
41
|
-
|
|
42
|
-
new MissingHrefOnA().validate(nodes);
|
|
43
|
-
|
|
44
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
2
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
3
|
-
import { TextUtility } from '../../../utils/text';
|
|
4
|
-
import { TranslateService } from '../../../services/translate';
|
|
5
|
-
import { $severity } from '../../../constants/accessibility';
|
|
6
|
-
import { $accessibilityAuditRules } from '../../../constants/accessibility';
|
|
7
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
8
|
-
|
|
9
|
-
export class MissingHrefOnA extends AbstractRule {
|
|
10
|
-
protected selector: string = 'a:not([href])';
|
|
11
|
-
|
|
12
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
13
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.missing_href_on_a),
|
|
14
|
-
links: [
|
|
15
|
-
{
|
|
16
|
-
content: 'F42: Failure of Success Criteria 1.3.1, 2.1.1, 2.1.3, or 4.1.2 when emulating links',
|
|
17
|
-
url: 'https://www.w3.org/TR/WCAG20-TECHS/F42.html'
|
|
18
|
-
}
|
|
19
|
-
],
|
|
20
|
-
recommendations: [],
|
|
21
|
-
severity: $severity.critical,
|
|
22
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
public validate(anchorElements: HTMLAnchorElement[]): void {
|
|
26
|
-
const reportNodeWithoutHref = (anchorElement: HTMLAnchorElement): void => {
|
|
27
|
-
const reportMessage: string = TranslateService.instant('missing_href_on_a_report_message', [TextUtility.escape('<a>')]);
|
|
28
|
-
|
|
29
|
-
const report: IIssueReport = {
|
|
30
|
-
message: reportMessage,
|
|
31
|
-
node: anchorElement,
|
|
32
|
-
ruleId: this.ruleConfig.id
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
this.validator.report(report);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
anchorElements.forEach(reportNodeWithoutHref);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { MisusedAriaOnFocusableElement } from './misused-aria-on-focusable-element';
|
|
2
|
-
|
|
3
|
-
describe('Rules', () => {
|
|
4
|
-
|
|
5
|
-
describe('MisusedAriaOnFocusableElement', () => {
|
|
6
|
-
|
|
7
|
-
it('should indicate that class exists', () => {
|
|
8
|
-
expect(MisusedAriaOnFocusableElement).toBeDefined();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
3
|
-
import { FOCUSABLE_ELEMENTS } from '../../../constants/focusableElements';
|
|
4
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
5
|
-
import { TextUtility } from '../../../utils/text';
|
|
6
|
-
import { TranslateService } from '../../../services/translate';
|
|
7
|
-
import { $severity } from '../../../constants/accessibility';
|
|
8
|
-
import { $accessibilityAuditRules } from '../../../constants/accessibility';
|
|
9
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
10
|
-
|
|
11
|
-
export class MisusedAriaOnFocusableElement extends AbstractRule {
|
|
12
|
-
protected selector: string = FOCUSABLE_ELEMENTS
|
|
13
|
-
.map((i: string): string => {
|
|
14
|
-
return `${i}[role="presentation"], ${i}[aria-hidden="true"]`;
|
|
15
|
-
})
|
|
16
|
-
.join(', ');
|
|
17
|
-
|
|
18
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
19
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.misused_aria_on_focusable_element),
|
|
20
|
-
links: [
|
|
21
|
-
{
|
|
22
|
-
content: 'Do not use role="presentation" or aria-hidden="true" on a visible focusable element',
|
|
23
|
-
url: 'https://w3c.github.io/using-aria/#4thrule'
|
|
24
|
-
}
|
|
25
|
-
],
|
|
26
|
-
recommendations: [],
|
|
27
|
-
severity: $severity.high,
|
|
28
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
public validate(elements: HTMLElement[]): void {
|
|
32
|
-
const checkSupportForTabindex = (element: HTMLElement): void => {
|
|
33
|
-
const rolePresentation: string | null = element.getAttribute('role');
|
|
34
|
-
const ariaHiddenTrue: string | null = element.getAttribute('aria-hidden');
|
|
35
|
-
const attributesToReport: string[] = [];
|
|
36
|
-
|
|
37
|
-
if (DomUtility.isElementHidden(element)) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (rolePresentation) {
|
|
42
|
-
const attribute: string = TranslateService.instant('misused_aria_on_focusable_element_attribute_1');
|
|
43
|
-
|
|
44
|
-
attributesToReport.push(attribute);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (ariaHiddenTrue === 'true') {
|
|
48
|
-
const attribute: string = TranslateService.instant('misused_aria_on_focusable_element_attribute_2');
|
|
49
|
-
|
|
50
|
-
attributesToReport.push(attribute);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const reportMessage: string = TranslateService.instant('misused_aria_on_focusable_element_report_message', [attributesToReport.join(', ')]);
|
|
54
|
-
|
|
55
|
-
const report: IIssueReport = {
|
|
56
|
-
message: reportMessage,
|
|
57
|
-
node: element,
|
|
58
|
-
ruleId: this.ruleConfig.id
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
this.validator.report(report);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
elements.forEach(checkSupportForTabindex);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { MisusedInputAttribute } from './misused-input-attribute';
|
|
2
|
-
|
|
3
|
-
describe('Rules', () => {
|
|
4
|
-
|
|
5
|
-
describe('MisusedInputAttribute', () => {
|
|
6
|
-
|
|
7
|
-
it('should indicate that class exists', () => {
|
|
8
|
-
expect(MisusedInputAttribute).toBeDefined();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
});
|