testaro 18.1.1 → 18.2.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/aslint/LICENSE +362 -0
- package/aslint/README.md +260 -0
- package/aslint/app/rules/abstract-rule.ts +83 -0
- package/aslint/app/rules/aslint/aria-hidden-false/aria-hidden-false.test.ts +73 -0
- package/aslint/app/rules/aslint/aria-hidden-false/aria-hidden-false.ts +34 -0
- package/aslint/app/rules/aslint/aria-hidden-false/aria-hidden.documentation.md +32 -0
- package/aslint/app/rules/aslint/aria-role-dialog/aria-role-dialog.documentation.md +49 -0
- package/aslint/app/rules/aslint/aria-role-dialog/aria-role-dialog.test.ts +91 -0
- package/aslint/app/rules/aslint/aria-role-dialog/aria-role-dialog.ts +62 -0
- package/aslint/app/rules/aslint/capital-letters-words/capital-letters-words.documentation.md +44 -0
- package/aslint/app/rules/aslint/capital-letters-words/capital-letters-words.test.ts +111 -0
- package/aslint/app/rules/aslint/capital-letters-words/capital-letters-words.ts +120 -0
- package/aslint/app/rules/aslint/content-editable-missing-attributes/content-editable-missing-attributes.docmentation.md +48 -0
- package/aslint/app/rules/aslint/content-editable-missing-attributes/content-editable-missing-attributes.test.ts +67 -0
- package/aslint/app/rules/aslint/content-editable-missing-attributes/content-editable-missing-attributes.ts +63 -0
- package/aslint/app/rules/aslint/contentinfo-landmark-only-one/contentinfo-landmark-only-one.documentation.md +45 -0
- package/aslint/app/rules/aslint/contentinfo-landmark-only-one/contentinfo-landmark-only-one.test.ts +63 -0
- package/aslint/app/rules/aslint/contentinfo-landmark-only-one/contentinfo-landmark-only-one.ts +44 -0
- package/aslint/app/rules/aslint/elements-not-allowed-in-head/elements-not-allowed-in-head.documentation.md +65 -0
- package/aslint/app/rules/aslint/elements-not-allowed-in-head/elements-not-allowed-in-head.test.ts +53 -0
- package/aslint/app/rules/aslint/elements-not-allowed-in-head/elements-not-allowed-in-head.ts +47 -0
- package/aslint/app/rules/aslint/empty-title-attribute/empty-title-attribute.documentation.md +55 -0
- package/aslint/app/rules/aslint/empty-title-attribute/empty-title-attribute.test.ts +80 -0
- package/aslint/app/rules/aslint/empty-title-attribute/empty-title-attribute.ts +58 -0
- package/aslint/app/rules/aslint/flash-content/flash-content.documentation.md +48 -0
- package/aslint/app/rules/aslint/flash-content/flash-content.test.ts +52 -0
- package/aslint/app/rules/aslint/flash-content/flash-content.ts +32 -0
- package/aslint/app/rules/aslint/font-style-italic/font-style-italic.documentation.md +44 -0
- package/aslint/app/rules/aslint/font-style-italic/font-style-italic.test.ts +12 -0
- package/aslint/app/rules/aslint/font-style-italic/font-style-italic.ts +83 -0
- package/aslint/app/rules/aslint/h1-must-be/h1-must-be.documentation.md +46 -0
- package/aslint/app/rules/aslint/h1-must-be/h1-must-be.test.ts +46 -0
- package/aslint/app/rules/aslint/h1-must-be/h1-must-be.ts +36 -0
- package/aslint/app/rules/aslint/headings-sibling-unique/headings-sibling-unique.documentation.md +57 -0
- package/aslint/app/rules/aslint/headings-sibling-unique/headings-sibling-unique.test.ts +52 -0
- package/aslint/app/rules/aslint/headings-sibling-unique/headings-sibling-unique.ts +63 -0
- package/aslint/app/rules/aslint/horizontal-rule/horizontal-rule.documentation.md +39 -0
- package/aslint/app/rules/aslint/horizontal-rule/horizontal-rule.test.ts +66 -0
- package/aslint/app/rules/aslint/horizontal-rule/horizontal-rule.ts +37 -0
- package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.documentation.md +36 -0
- package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.test.ts +113 -0
- package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.ts +103 -0
- package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.documentation.md +34 -0
- package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.test.ts +82 -0
- package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.ts +44 -0
- package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.documentation.md +40 -0
- package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.test.ts +48 -0
- package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.ts +37 -0
- package/aslint/app/rules/aslint/links-language-destination/links-language-destination.test.ts +50 -0
- package/aslint/app/rules/aslint/links-language-destination/links-language-destination.ts +70 -0
- package/aslint/app/rules/aslint/main-element-only-one/main-element-only-one.test.ts +55 -0
- package/aslint/app/rules/aslint/main-element-only-one/main-element-only-one.ts +83 -0
- package/aslint/app/rules/aslint/main-landmark-must-be-top-level/main-landmark-must-be-top-level.test.ts +12 -0
- package/aslint/app/rules/aslint/main-landmark-must-be-top-level/main-landmark-must-be-top-level.ts +73 -0
- package/aslint/app/rules/aslint/minimum-font-size/minimum-font-size.test.ts +12 -0
- package/aslint/app/rules/aslint/minimum-font-size/minimum-font-size.ts +87 -0
- package/aslint/app/rules/aslint/missing-href-on-a/missing-href-on-a.test.ts +48 -0
- package/aslint/app/rules/aslint/missing-href-on-a/missing-href-on-a.ts +40 -0
- package/aslint/app/rules/aslint/misused-aria-on-focusable-element/misused-aria-on-focusable-element.test.ts +12 -0
- package/aslint/app/rules/aslint/misused-aria-on-focusable-element/misused-aria-on-focusable-element.ts +66 -0
- package/aslint/app/rules/aslint/misused-input-attribute/misused-input-attribute.test.ts +12 -0
- package/aslint/app/rules/aslint/misused-input-attribute/misused-input-attribute.ts +134 -0
- package/aslint/app/rules/aslint/misused-required-attribute/misused-required-attribute.test.ts +12 -0
- package/aslint/app/rules/aslint/misused-required-attribute/misused-required-attribute.ts +90 -0
- package/aslint/app/rules/aslint/navigation-landmark-restrictions/navigation-landmark-restrictions.test.ts +12 -0
- package/aslint/app/rules/aslint/navigation-landmark-restrictions/navigation-landmark-restrictions.ts +48 -0
- package/aslint/app/rules/aslint/obsolete-html-attributes/obsolete-html-attributes.test.ts +12 -0
- package/aslint/app/rules/aslint/obsolete-html-attributes/obsolete-html-attributes.ts +148 -0
- package/aslint/app/rules/aslint/obsolete-html-elements/obsolete-html-elements.test.ts +12 -0
- package/aslint/app/rules/aslint/obsolete-html-elements/obsolete-html-elements.ts +66 -0
- package/aslint/app/rules/aslint/outline-zero/outline-zero.test.ts +12 -0
- package/aslint/app/rules/aslint/outline-zero/outline-zero.ts +85 -0
- package/aslint/app/rules/aslint/overlay/overlay.test.ts +122 -0
- package/aslint/app/rules/aslint/overlay/overlay.ts +141 -0
- package/aslint/app/rules/aslint/role-application/role-application.test.ts +48 -0
- package/aslint/app/rules/aslint/role-application/role-application.ts +38 -0
- package/aslint/app/rules/aslint/rtl-content/rtl-content.test.ts +12 -0
- package/aslint/app/rules/aslint/rtl-content/rtl-content.ts +75 -0
- package/aslint/app/rules/aslint/unclear-uri-on-a/unclear-anchor-uri.test.ts +12 -0
- package/aslint/app/rules/aslint/unclear-uri-on-a/unclear-anchor-uri.ts +48 -0
- package/aslint/app/rules/aslint/unsupported-role-on-element/unsupported-role-on-element.test.ts +12 -0
- package/aslint/app/rules/aslint/unsupported-role-on-element/unsupported-role-on-element.ts +63 -0
- package/package.json +1 -1
- package/testaro/headingAmb.js +103 -0
- package/testaro/template.js +78 -0
- package/tests/testaro.js +1 -0
- package/validation/tests/jobs/headingAmb.json +129 -0
- package/validation/tests/targets/headingAmb/index.html +26 -0
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
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 MisusedInputAttribute extends AbstractRule {
|
|
10
|
+
protected selector: string = 'input';
|
|
11
|
+
protected ruleConfig: IAbstractRuleConfig = {
|
|
12
|
+
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.misused_input_attribute),
|
|
13
|
+
links: [
|
|
14
|
+
{
|
|
15
|
+
content: 'HTML5 form additions',
|
|
16
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
content: 'Common input element attributes',
|
|
20
|
+
url: 'https://www.w3.org/TR/html5/forms.html#common-input-element-attributes'
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
recommendations: [],
|
|
24
|
+
severity: $severity.high,
|
|
25
|
+
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
public validate(elements: Element[]): void {
|
|
29
|
+
const SUPPORTED_INPUT_ATTRIBUTES: string[] = [
|
|
30
|
+
'accept',
|
|
31
|
+
'accessKey',
|
|
32
|
+
'autocapitalize',
|
|
33
|
+
'autocomplete',
|
|
34
|
+
'autofocus',
|
|
35
|
+
'checked',
|
|
36
|
+
'defaultChecked',
|
|
37
|
+
'defaultValue',
|
|
38
|
+
'dirName',
|
|
39
|
+
'disabled',
|
|
40
|
+
'indeterminate',
|
|
41
|
+
'list',
|
|
42
|
+
'maxLength',
|
|
43
|
+
'minLength',
|
|
44
|
+
'min',
|
|
45
|
+
'max',
|
|
46
|
+
'multiple',
|
|
47
|
+
'name',
|
|
48
|
+
'pattern',
|
|
49
|
+
'placeholder',
|
|
50
|
+
'readOnly',
|
|
51
|
+
'required',
|
|
52
|
+
'selectionDirection',
|
|
53
|
+
'selectionEnd',
|
|
54
|
+
'selectionStart',
|
|
55
|
+
'size',
|
|
56
|
+
'src',
|
|
57
|
+
'step',
|
|
58
|
+
'tabIndex',
|
|
59
|
+
'type',
|
|
60
|
+
'useMap',
|
|
61
|
+
'value',
|
|
62
|
+
'valueAsDate',
|
|
63
|
+
'valueAsNumber',
|
|
64
|
+
'width',
|
|
65
|
+
'willValidate'
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const attributeAliases: any = {
|
|
69
|
+
accesskey: 'accessKey',
|
|
70
|
+
cellpadding: 'cellPadding',
|
|
71
|
+
cellspacing: 'cellSpacing',
|
|
72
|
+
class: 'className',
|
|
73
|
+
codebase: 'codeBase',
|
|
74
|
+
colspan: 'colSpan',
|
|
75
|
+
defaultchecked: 'defaultChecked',
|
|
76
|
+
defaultvalue: 'defaultValue',
|
|
77
|
+
dirname: 'dirName',
|
|
78
|
+
for: 'htmlFor',
|
|
79
|
+
formaction: 'formAction',
|
|
80
|
+
formenctype: 'formEncType',
|
|
81
|
+
formmethod: 'formMethod',
|
|
82
|
+
formnovalidate: 'formNoValidate',
|
|
83
|
+
formtarget: 'formTarget',
|
|
84
|
+
frameborder: 'frameBorder',
|
|
85
|
+
framespacing: 'frameSpacing',
|
|
86
|
+
ismap: 'isMap',
|
|
87
|
+
longdesc: 'longDesc',
|
|
88
|
+
maxlength: 'maxLength',
|
|
89
|
+
nowrap: 'noWrap',
|
|
90
|
+
placeholder: 'placeholder',
|
|
91
|
+
readonly: 'readOnly',
|
|
92
|
+
rowspan: 'rowSpan',
|
|
93
|
+
selectiondirection: 'selectionDirection',
|
|
94
|
+
selectionend: 'selectionEnd',
|
|
95
|
+
selectionstart: 'selectionStart',
|
|
96
|
+
tabindex: 'tabIndex',
|
|
97
|
+
usemap: 'useMap',
|
|
98
|
+
validationmessage: 'validationMessage',
|
|
99
|
+
valueasdate: 'valueAsDate',
|
|
100
|
+
valueasnumber: 'valueAsNumber',
|
|
101
|
+
willvalidate: 'willValidate'
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const isSupportedAttribute = (element: Element, attribute: string): boolean => {
|
|
105
|
+
const alias: string = attributeAliases[attribute.toLowerCase()] || attribute;
|
|
106
|
+
|
|
107
|
+
return alias in element;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const checkSupportForInputAttribute = (element: Element): void => {
|
|
111
|
+
let nodeAttrValue: string | null;
|
|
112
|
+
|
|
113
|
+
const checkAttributes = (attr: string): void => {
|
|
114
|
+
nodeAttrValue = element.getAttribute(attr);
|
|
115
|
+
|
|
116
|
+
if (typeof nodeAttrValue === 'string' && isSupportedAttribute(element, attr) === false) {
|
|
117
|
+
const reportMessage: string = TranslateService.instant('misused_input_attribute_report_message', [attr]);
|
|
118
|
+
|
|
119
|
+
const report: IIssueReport = {
|
|
120
|
+
message: reportMessage,
|
|
121
|
+
node: element,
|
|
122
|
+
ruleId: this.ruleConfig.id
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
this.validator.report(report);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
SUPPORTED_INPUT_ATTRIBUTES.forEach(checkAttributes);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
elements.forEach(checkSupportForInputAttribute);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { MisusedRequiredAttribute } from './misused-required-attribute';
|
|
2
|
+
|
|
3
|
+
describe('Rules', () => {
|
|
4
|
+
|
|
5
|
+
describe('MisusedRequiredAttribute', () => {
|
|
6
|
+
|
|
7
|
+
it('should indicate that class exists', () => {
|
|
8
|
+
expect(MisusedRequiredAttribute).toBeDefined();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
export class MisusedRequiredAttribute extends AbstractRule {
|
|
9
|
+
protected selector: string = '[required][aria-required]';
|
|
10
|
+
|
|
11
|
+
protected ruleConfig: IAbstractRuleConfig = {
|
|
12
|
+
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.misused_required_attribute),
|
|
13
|
+
links: [
|
|
14
|
+
{
|
|
15
|
+
content: 'Attribute required',
|
|
16
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-required'
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
recommendations: [],
|
|
20
|
+
severity: $severity.high,
|
|
21
|
+
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
public validate(elements: HTMLElement[]): void {
|
|
25
|
+
const requiredElms: HTMLElement[] = elements;
|
|
26
|
+
const notUseOnType: string[] = [
|
|
27
|
+
'hidden',
|
|
28
|
+
'image'
|
|
29
|
+
];
|
|
30
|
+
const notUseOnButtonType: string[] = [
|
|
31
|
+
'submit',
|
|
32
|
+
'reset',
|
|
33
|
+
'button'
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const showMessage = (element: HTMLElement): void => {
|
|
37
|
+
let report: IIssueReport | undefined;
|
|
38
|
+
const ariaRequiredValue: string | null = element.getAttribute('aria-required');
|
|
39
|
+
const elementRequiredValue: string | null = element.getAttribute('required');
|
|
40
|
+
const nodeName: string = element.nodeName.toLowerCase();
|
|
41
|
+
|
|
42
|
+
if (ariaRequiredValue && elementRequiredValue === null ||
|
|
43
|
+
(typeof (element as any).type === 'string' && notUseOnType.indexOf((element as any).type) !== -1 ||
|
|
44
|
+
nodeName === 'button' && notUseOnButtonType.indexOf((element as HTMLButtonElement).type) !== -1)
|
|
45
|
+
) {
|
|
46
|
+
const reportMessage: string = TranslateService.instant('misused_required_attribute_report_message1', [ariaRequiredValue]);
|
|
47
|
+
|
|
48
|
+
report = {
|
|
49
|
+
message: reportMessage,
|
|
50
|
+
node: element,
|
|
51
|
+
ruleId: this.ruleConfig.id
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.validator.report(report);
|
|
55
|
+
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (elementRequiredValue === null) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (elementRequiredValue === ariaRequiredValue) {
|
|
64
|
+
const reportMessage: string = TranslateService.instant('misused_required_attribute_report_message2', [ariaRequiredValue]);
|
|
65
|
+
|
|
66
|
+
report = {
|
|
67
|
+
message: reportMessage,
|
|
68
|
+
node: element,
|
|
69
|
+
ruleId: this.ruleConfig.id
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
const reportMessage: string = TranslateService.instant('misused_required_attribute_report_message3', [ariaRequiredValue, element.getAttribute('required')]);
|
|
73
|
+
|
|
74
|
+
report = {
|
|
75
|
+
message: reportMessage,
|
|
76
|
+
node: element,
|
|
77
|
+
ruleId: this.ruleConfig.id
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof report === 'undefined') {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.validator.report(report);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
requiredElms.forEach(showMessage);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NavigationLandmarkRestrictions } from './navigation-landmark-restrictions';
|
|
2
|
+
|
|
3
|
+
describe('Rules', () => {
|
|
4
|
+
|
|
5
|
+
describe('NavigationLandmarkRestrictions', () => {
|
|
6
|
+
|
|
7
|
+
it('should indicate that class exists', () => {
|
|
8
|
+
expect(NavigationLandmarkRestrictions).toBeDefined();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
});
|
|
12
|
+
});
|
package/aslint/app/rules/aslint/navigation-landmark-restrictions/navigation-landmark-restrictions.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
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 NavigationLandmarkRestrictions extends AbstractRule {
|
|
11
|
+
protected selector: string = '[role="navigation"]';
|
|
12
|
+
|
|
13
|
+
protected ruleConfig: IAbstractRuleConfig = {
|
|
14
|
+
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.navigation_landmark_restrictions),
|
|
15
|
+
links: [
|
|
16
|
+
{
|
|
17
|
+
content: 'Accessible Rich Internet Applications (WAI-ARIA) 1.0 Specification: navigation role',
|
|
18
|
+
url: 'https://www.w3.org/TR/wai-aria-1.1/#navigation'
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
recommendations: [],
|
|
22
|
+
severity: $severity.high,
|
|
23
|
+
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
public validate(elements: Element[]): void {
|
|
27
|
+
const selector: string = '[role]:not([role="region"]):not([role="search"])';
|
|
28
|
+
const reportMessage: string = TranslateService.instant('navigation_landmark_restrictions_report_message');
|
|
29
|
+
|
|
30
|
+
const checkRoleNavigationContent = (htmlElement: Element): void => {
|
|
31
|
+
const childs: HTMLElement[] = DomUtility.querySelectorAll(selector, htmlElement);
|
|
32
|
+
|
|
33
|
+
const reportIssue = (element: Element): void => {
|
|
34
|
+
const report: IIssueReport = {
|
|
35
|
+
message: reportMessage,
|
|
36
|
+
node: element,
|
|
37
|
+
ruleId: this.ruleConfig.id
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
this.validator.report(report);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
childs.forEach(reportIssue);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
elements.forEach(checkRoleNavigationContent);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ObsoleteHtmlAttributes } from './obsolete-html-attributes';
|
|
2
|
+
|
|
3
|
+
describe('Rules', () => {
|
|
4
|
+
|
|
5
|
+
describe('ObsoleteHtmlAttributes', () => {
|
|
6
|
+
|
|
7
|
+
it('should indicate that class exists', () => {
|
|
8
|
+
expect(ObsoleteHtmlAttributes).toBeDefined();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
const OBSOLETE_HTML_ATTRIBUTES: { [key: string]: string[] } = {
|
|
10
|
+
accept: ['form'],
|
|
11
|
+
align: ['caption', 'col', 'div', 'embed', 'hr', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'iframe', 'input', 'img', 'legend', 'object', 'p', 'table', 'tbody', 'thead', 'tfoot', 'td', 'th', 'tr'],
|
|
12
|
+
alink: ['body'],
|
|
13
|
+
allowtransparency: ['iframe'],
|
|
14
|
+
archive: ['object'],
|
|
15
|
+
axis: ['td', 'th'],
|
|
16
|
+
background: ['body', 'table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th'],
|
|
17
|
+
bgcolor: ['body', 'table', 'td', 'th', 'tr'],
|
|
18
|
+
border: ['img', 'object'],
|
|
19
|
+
bordercolor: ['table'],
|
|
20
|
+
cellpadding: ['table'],
|
|
21
|
+
cellspacing: ['table'],
|
|
22
|
+
char: ['col', 'tbody', 'thead', 'tfoot', 'td', 'th', 'tr'],
|
|
23
|
+
charoff: ['col', 'tbody', 'thead', 'tfoot', 'td', 'th', 'tr'],
|
|
24
|
+
charset: ['a', 'link'],
|
|
25
|
+
classid: ['object'],
|
|
26
|
+
clear: ['br'],
|
|
27
|
+
code: ['object'],
|
|
28
|
+
codebase: ['object'],
|
|
29
|
+
codetype: ['object'],
|
|
30
|
+
color: ['hr'],
|
|
31
|
+
compact: ['dl', 'ol', 'ul'],
|
|
32
|
+
coords: ['a'],
|
|
33
|
+
datafld: ['a', 'applet', 'button', 'div', 'fieldset', 'frame', 'iframe', 'img', 'input', 'label', 'legend', 'marquee', 'object', 'param', 'select', 'span', 'textarea'],
|
|
34
|
+
dataformatas: ['button', 'div', 'input', 'label', 'legend', 'marquee', 'object', 'option', 'select', 'span', 'table'],
|
|
35
|
+
datapagesize: ['table'],
|
|
36
|
+
datasrc: ['a', 'applet', 'button', 'div', 'frame', 'iframe', 'img', 'input', 'label', 'legend', 'marquee', 'object', 'option', 'select', 'span', 'table', 'textarea'],
|
|
37
|
+
declare: ['object'],
|
|
38
|
+
event: ['script'],
|
|
39
|
+
for: ['script'],
|
|
40
|
+
frame: ['table'],
|
|
41
|
+
frameborder: ['iframe'],
|
|
42
|
+
height: ['td', 'th'],
|
|
43
|
+
hspace: ['embed', 'iframe', 'input', 'img', 'object'],
|
|
44
|
+
ismap: ['input'],
|
|
45
|
+
language: ['script'],
|
|
46
|
+
link: ['body'],
|
|
47
|
+
lowsrc: ['img'],
|
|
48
|
+
marginbottom: ['body'],
|
|
49
|
+
marginheight: ['body', 'iframe'],
|
|
50
|
+
marginleft: ['body'],
|
|
51
|
+
marginright: ['body'],
|
|
52
|
+
margintop: ['body'],
|
|
53
|
+
marginwidth: ['body', 'iframe'],
|
|
54
|
+
methods: ['a', 'link'],
|
|
55
|
+
name: ['a', 'embed', 'img', 'option'],
|
|
56
|
+
nohref: ['area'],
|
|
57
|
+
noshade: ['hr'],
|
|
58
|
+
nowrap: ['td', 'th'],
|
|
59
|
+
profile: ['head'],
|
|
60
|
+
rules: ['table'],
|
|
61
|
+
scheme: ['meta'],
|
|
62
|
+
scope: ['td'],
|
|
63
|
+
scrolling: ['iframe'],
|
|
64
|
+
shape: ['a'],
|
|
65
|
+
size: ['hr'],
|
|
66
|
+
standby: ['object'],
|
|
67
|
+
summary: ['table'],
|
|
68
|
+
target: ['link'],
|
|
69
|
+
text: ['body'],
|
|
70
|
+
type: ['param', 'li', 'ul'],
|
|
71
|
+
urn: ['a', 'link'],
|
|
72
|
+
usemap: ['input'],
|
|
73
|
+
valign: ['col', 'tbody', 'thead', 'tfoot', 'td', 'th', 'tr'],
|
|
74
|
+
valuetype: ['param'],
|
|
75
|
+
version: ['html'],
|
|
76
|
+
vlink: ['body'],
|
|
77
|
+
vspace: ['embed', 'iframe', 'input', 'img', 'object'],
|
|
78
|
+
width: ['col', 'hr', 'pre', 'table', 'td', 'th']
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export class ObsoleteHtmlAttributes extends AbstractRule {
|
|
82
|
+
protected selector: string = (() => {
|
|
83
|
+
const createSelector = (elementData: [string, string[]]): string => {
|
|
84
|
+
const attribute: string = elementData[0];
|
|
85
|
+
const elements: string[] = elementData[1];
|
|
86
|
+
|
|
87
|
+
const create = (elementName: string): string => {
|
|
88
|
+
return `${elementName}[${attribute}]`;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return elements.map(create).join(',');
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const selector: string = Object.entries(OBSOLETE_HTML_ATTRIBUTES).map(createSelector)
|
|
95
|
+
.join(',');
|
|
96
|
+
|
|
97
|
+
return selector;
|
|
98
|
+
})();
|
|
99
|
+
|
|
100
|
+
protected ruleConfig: IAbstractRuleConfig = {
|
|
101
|
+
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.obsolete_html_attributes),
|
|
102
|
+
links: [
|
|
103
|
+
{
|
|
104
|
+
content: 'Non-conforming features',
|
|
105
|
+
url: 'https://www.w3.org/TR/html5/obsolete.html#non-conforming-features'
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
recommendations: [],
|
|
109
|
+
severity: $severity.high,
|
|
110
|
+
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
public validate(elements: HTMLElement[]): void {
|
|
114
|
+
const reportNode = (element: HTMLElement): void => {
|
|
115
|
+
|
|
116
|
+
const attributes: Attr[] = Array.from(element.attributes);
|
|
117
|
+
const foundedAttributes: string[] = [];
|
|
118
|
+
|
|
119
|
+
const checkAttribute = (attr: Attr): void => {
|
|
120
|
+
const elms: string[] | undefined = OBSOLETE_HTML_ATTRIBUTES[attr.localName];
|
|
121
|
+
|
|
122
|
+
if (Array.isArray(elms)) {
|
|
123
|
+
if (elms.includes(element.nodeName.toLowerCase())) {
|
|
124
|
+
foundedAttributes.push(attr.localName);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
attributes.forEach(checkAttribute);
|
|
130
|
+
|
|
131
|
+
if (foundedAttributes.length === 0) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const reportMessage: string = TranslateService.instant('obsolete_html_attributes_report_message', foundedAttributes.join(', '));
|
|
136
|
+
|
|
137
|
+
const report: IIssueReport = {
|
|
138
|
+
message: reportMessage,
|
|
139
|
+
node: element,
|
|
140
|
+
ruleId: this.ruleConfig.id
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
this.validator.report(report);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
elements.forEach(reportNode);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ObsoleteHtmlElements } from './obsolete-html-elements';
|
|
2
|
+
|
|
3
|
+
describe('Rules', () => {
|
|
4
|
+
|
|
5
|
+
describe('ObsoleteHtmlElements', () => {
|
|
6
|
+
|
|
7
|
+
it('should indicate that class exists', () => {
|
|
8
|
+
expect(ObsoleteHtmlElements).toBeDefined();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
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 ObsoleteHtmlElements extends AbstractRule {
|
|
10
|
+
protected selector: string = [
|
|
11
|
+
'acronym',
|
|
12
|
+
'applet',
|
|
13
|
+
'basefont',
|
|
14
|
+
'bgsound',
|
|
15
|
+
'big',
|
|
16
|
+
'blink',
|
|
17
|
+
'center',
|
|
18
|
+
'dir',
|
|
19
|
+
'font',
|
|
20
|
+
'frame',
|
|
21
|
+
'frameset',
|
|
22
|
+
'hgroup',
|
|
23
|
+
'isindex',
|
|
24
|
+
'listing',
|
|
25
|
+
'marquee',
|
|
26
|
+
'multicol',
|
|
27
|
+
'nextid',
|
|
28
|
+
'nobr',
|
|
29
|
+
'noembed',
|
|
30
|
+
'noframes',
|
|
31
|
+
'plaintext',
|
|
32
|
+
's',
|
|
33
|
+
'spacer',
|
|
34
|
+
'strike',
|
|
35
|
+
'tt',
|
|
36
|
+
'u',
|
|
37
|
+
'xmp'
|
|
38
|
+
].join(', ');
|
|
39
|
+
|
|
40
|
+
protected ruleConfig: IAbstractRuleConfig = {
|
|
41
|
+
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.obsolete_html_elements),
|
|
42
|
+
links: [
|
|
43
|
+
{
|
|
44
|
+
content: 'Non-conforming features',
|
|
45
|
+
url: 'https://www.w3.org/TR/html5/obsolete.html#non-conforming-features'
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
recommendations: [],
|
|
49
|
+
severity: $severity.high,
|
|
50
|
+
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
public validate(elements: HTMLElement[]): void {
|
|
54
|
+
const reportNode = (element: HTMLElement): void => {
|
|
55
|
+
const problem: IIssueReport = {
|
|
56
|
+
message: TranslateService.instant('obsolete_html_elements_report_message', [element.nodeName.toLowerCase()]),
|
|
57
|
+
node: element,
|
|
58
|
+
ruleId: this.ruleConfig.id
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
this.validator.report(problem);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
elements.forEach(reportNode);
|
|
65
|
+
}
|
|
66
|
+
}
|