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.
Files changed (88) hide show
  1. package/aslint/LICENSE +362 -0
  2. package/aslint/README.md +260 -0
  3. package/aslint/app/rules/abstract-rule.ts +83 -0
  4. package/aslint/app/rules/aslint/aria-hidden-false/aria-hidden-false.test.ts +73 -0
  5. package/aslint/app/rules/aslint/aria-hidden-false/aria-hidden-false.ts +34 -0
  6. package/aslint/app/rules/aslint/aria-hidden-false/aria-hidden.documentation.md +32 -0
  7. package/aslint/app/rules/aslint/aria-role-dialog/aria-role-dialog.documentation.md +49 -0
  8. package/aslint/app/rules/aslint/aria-role-dialog/aria-role-dialog.test.ts +91 -0
  9. package/aslint/app/rules/aslint/aria-role-dialog/aria-role-dialog.ts +62 -0
  10. package/aslint/app/rules/aslint/capital-letters-words/capital-letters-words.documentation.md +44 -0
  11. package/aslint/app/rules/aslint/capital-letters-words/capital-letters-words.test.ts +111 -0
  12. package/aslint/app/rules/aslint/capital-letters-words/capital-letters-words.ts +120 -0
  13. package/aslint/app/rules/aslint/content-editable-missing-attributes/content-editable-missing-attributes.docmentation.md +48 -0
  14. package/aslint/app/rules/aslint/content-editable-missing-attributes/content-editable-missing-attributes.test.ts +67 -0
  15. package/aslint/app/rules/aslint/content-editable-missing-attributes/content-editable-missing-attributes.ts +63 -0
  16. package/aslint/app/rules/aslint/contentinfo-landmark-only-one/contentinfo-landmark-only-one.documentation.md +45 -0
  17. package/aslint/app/rules/aslint/contentinfo-landmark-only-one/contentinfo-landmark-only-one.test.ts +63 -0
  18. package/aslint/app/rules/aslint/contentinfo-landmark-only-one/contentinfo-landmark-only-one.ts +44 -0
  19. package/aslint/app/rules/aslint/elements-not-allowed-in-head/elements-not-allowed-in-head.documentation.md +65 -0
  20. package/aslint/app/rules/aslint/elements-not-allowed-in-head/elements-not-allowed-in-head.test.ts +53 -0
  21. package/aslint/app/rules/aslint/elements-not-allowed-in-head/elements-not-allowed-in-head.ts +47 -0
  22. package/aslint/app/rules/aslint/empty-title-attribute/empty-title-attribute.documentation.md +55 -0
  23. package/aslint/app/rules/aslint/empty-title-attribute/empty-title-attribute.test.ts +80 -0
  24. package/aslint/app/rules/aslint/empty-title-attribute/empty-title-attribute.ts +58 -0
  25. package/aslint/app/rules/aslint/flash-content/flash-content.documentation.md +48 -0
  26. package/aslint/app/rules/aslint/flash-content/flash-content.test.ts +52 -0
  27. package/aslint/app/rules/aslint/flash-content/flash-content.ts +32 -0
  28. package/aslint/app/rules/aslint/font-style-italic/font-style-italic.documentation.md +44 -0
  29. package/aslint/app/rules/aslint/font-style-italic/font-style-italic.test.ts +12 -0
  30. package/aslint/app/rules/aslint/font-style-italic/font-style-italic.ts +83 -0
  31. package/aslint/app/rules/aslint/h1-must-be/h1-must-be.documentation.md +46 -0
  32. package/aslint/app/rules/aslint/h1-must-be/h1-must-be.test.ts +46 -0
  33. package/aslint/app/rules/aslint/h1-must-be/h1-must-be.ts +36 -0
  34. package/aslint/app/rules/aslint/headings-sibling-unique/headings-sibling-unique.documentation.md +57 -0
  35. package/aslint/app/rules/aslint/headings-sibling-unique/headings-sibling-unique.test.ts +52 -0
  36. package/aslint/app/rules/aslint/headings-sibling-unique/headings-sibling-unique.ts +63 -0
  37. package/aslint/app/rules/aslint/horizontal-rule/horizontal-rule.documentation.md +39 -0
  38. package/aslint/app/rules/aslint/horizontal-rule/horizontal-rule.test.ts +66 -0
  39. package/aslint/app/rules/aslint/horizontal-rule/horizontal-rule.ts +37 -0
  40. package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.documentation.md +36 -0
  41. package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.test.ts +113 -0
  42. package/aslint/app/rules/aslint/incorrect-technique-for-hiding-content/incorrect-technique-for-hiding-content.ts +103 -0
  43. package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.documentation.md +34 -0
  44. package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.test.ts +82 -0
  45. package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.ts +44 -0
  46. package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.documentation.md +40 -0
  47. package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.test.ts +48 -0
  48. package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.ts +37 -0
  49. package/aslint/app/rules/aslint/links-language-destination/links-language-destination.test.ts +50 -0
  50. package/aslint/app/rules/aslint/links-language-destination/links-language-destination.ts +70 -0
  51. package/aslint/app/rules/aslint/main-element-only-one/main-element-only-one.test.ts +55 -0
  52. package/aslint/app/rules/aslint/main-element-only-one/main-element-only-one.ts +83 -0
  53. package/aslint/app/rules/aslint/main-landmark-must-be-top-level/main-landmark-must-be-top-level.test.ts +12 -0
  54. package/aslint/app/rules/aslint/main-landmark-must-be-top-level/main-landmark-must-be-top-level.ts +73 -0
  55. package/aslint/app/rules/aslint/minimum-font-size/minimum-font-size.test.ts +12 -0
  56. package/aslint/app/rules/aslint/minimum-font-size/minimum-font-size.ts +87 -0
  57. package/aslint/app/rules/aslint/missing-href-on-a/missing-href-on-a.test.ts +48 -0
  58. package/aslint/app/rules/aslint/missing-href-on-a/missing-href-on-a.ts +40 -0
  59. package/aslint/app/rules/aslint/misused-aria-on-focusable-element/misused-aria-on-focusable-element.test.ts +12 -0
  60. package/aslint/app/rules/aslint/misused-aria-on-focusable-element/misused-aria-on-focusable-element.ts +66 -0
  61. package/aslint/app/rules/aslint/misused-input-attribute/misused-input-attribute.test.ts +12 -0
  62. package/aslint/app/rules/aslint/misused-input-attribute/misused-input-attribute.ts +134 -0
  63. package/aslint/app/rules/aslint/misused-required-attribute/misused-required-attribute.test.ts +12 -0
  64. package/aslint/app/rules/aslint/misused-required-attribute/misused-required-attribute.ts +90 -0
  65. package/aslint/app/rules/aslint/navigation-landmark-restrictions/navigation-landmark-restrictions.test.ts +12 -0
  66. package/aslint/app/rules/aslint/navigation-landmark-restrictions/navigation-landmark-restrictions.ts +48 -0
  67. package/aslint/app/rules/aslint/obsolete-html-attributes/obsolete-html-attributes.test.ts +12 -0
  68. package/aslint/app/rules/aslint/obsolete-html-attributes/obsolete-html-attributes.ts +148 -0
  69. package/aslint/app/rules/aslint/obsolete-html-elements/obsolete-html-elements.test.ts +12 -0
  70. package/aslint/app/rules/aslint/obsolete-html-elements/obsolete-html-elements.ts +66 -0
  71. package/aslint/app/rules/aslint/outline-zero/outline-zero.test.ts +12 -0
  72. package/aslint/app/rules/aslint/outline-zero/outline-zero.ts +85 -0
  73. package/aslint/app/rules/aslint/overlay/overlay.test.ts +122 -0
  74. package/aslint/app/rules/aslint/overlay/overlay.ts +141 -0
  75. package/aslint/app/rules/aslint/role-application/role-application.test.ts +48 -0
  76. package/aslint/app/rules/aslint/role-application/role-application.ts +38 -0
  77. package/aslint/app/rules/aslint/rtl-content/rtl-content.test.ts +12 -0
  78. package/aslint/app/rules/aslint/rtl-content/rtl-content.ts +75 -0
  79. package/aslint/app/rules/aslint/unclear-uri-on-a/unclear-anchor-uri.test.ts +12 -0
  80. package/aslint/app/rules/aslint/unclear-uri-on-a/unclear-anchor-uri.ts +48 -0
  81. package/aslint/app/rules/aslint/unsupported-role-on-element/unsupported-role-on-element.test.ts +12 -0
  82. package/aslint/app/rules/aslint/unsupported-role-on-element/unsupported-role-on-element.ts +63 -0
  83. package/package.json +1 -1
  84. package/testaro/headingAmb.js +103 -0
  85. package/testaro/template.js +78 -0
  86. package/tests/testaro.js +1 -0
  87. package/validation/tests/jobs/headingAmb.json +129 -0
  88. 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
+ });
@@ -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
+ }
@@ -0,0 +1,12 @@
1
+ import { OutlineZero } from './outline-zero';
2
+
3
+ describe('Rules', () => {
4
+
5
+ describe('OutlineZero', () => {
6
+
7
+ it('should indicate that class exists', () => {
8
+ expect(OutlineZero).toBeDefined();
9
+ });
10
+
11
+ });
12
+ });