testaro 18.1.0 → 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 +2 -2
  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,80 @@
1
+ import { DomUtility } from '../../../utils/dom';
2
+ import { Validator } from '../../../validator';
3
+ import { EmptyTitleAttribute } from './empty-title-attribute';
4
+
5
+ describe('Rules', () => {
6
+
7
+ describe('EmptyTitleAttribute', () => {
8
+
9
+ const selector: string = `[title]${[
10
+ ':not(img)',
11
+ ':not(html)',
12
+ ':not(head)',
13
+ ':not(title)',
14
+ ':not(body)',
15
+ ':not(link)',
16
+ ':not(meta)',
17
+ ':not(title)',
18
+ ':not(style)',
19
+ ':not(script)',
20
+ ':not(noscript)',
21
+ ':not(iframe)',
22
+ ':not(br)',
23
+ ':not(hr)'
24
+ ].join('')}`;
25
+
26
+ let fakeDom;
27
+
28
+ new EmptyTitleAttribute().registerValidator();
29
+
30
+ beforeEach(() => {
31
+ fakeDom = document.createElement('div');
32
+ fakeDom.id = 'fakedom';
33
+ document.body.appendChild(fakeDom);
34
+
35
+ Validator.reset();
36
+ });
37
+
38
+ afterEach(() => {
39
+ DomUtility.remove(document.getElementById('fakedom'));
40
+ fakeDom = undefined;
41
+ });
42
+
43
+ it('should return one report when there is an element with defined an attribute title with an empty value', () => {
44
+ fakeDom.innerHTML = '<div title="">test</div>';
45
+
46
+ const nodes = DomUtility.querySelectorAllExclude(selector, fakeDom);
47
+
48
+ new EmptyTitleAttribute().validate(nodes);
49
+
50
+ expect(Object.keys(Validator.getReports()).length).toBe(1);
51
+ expect(Validator.getReport('report_0').message).toBe('You have an attribute <code>title</code> with an empty content.');
52
+ expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('div');
53
+ expect(Validator.getReport('report_0').ruleId).toBe('empty-title-attribute');
54
+ });
55
+
56
+ it('should return one report when there is an element with defined an attribute title with an empty value (only spaces)', () => {
57
+ fakeDom.innerHTML = '<div title=" ">test</div>';
58
+
59
+ const nodes = DomUtility.querySelectorAllExclude(selector, fakeDom);
60
+
61
+ new EmptyTitleAttribute().validate(nodes);
62
+
63
+ expect(Object.keys(Validator.getReports()).length).toBe(1);
64
+ expect(Validator.getReport('report_0').message).toBe('You have an attribute <code>title</code> with an empty content.');
65
+ expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('div');
66
+ expect(Validator.getReport('report_0').ruleId).toBe('empty-title-attribute');
67
+ });
68
+
69
+ it('should return no reports when there is an element with attribute title and non-empty value', () => {
70
+ fakeDom.innerHTML = '<div title="some title">test</div>';
71
+
72
+ const nodes = DomUtility.querySelectorAllExclude(selector, fakeDom);
73
+
74
+ new EmptyTitleAttribute().validate(nodes);
75
+
76
+ expect(Object.keys(Validator.getReports()).length).toBe(0);
77
+ });
78
+
79
+ });
80
+ });
@@ -0,0 +1,58 @@
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 { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
6
+ import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
7
+
8
+ export class EmptyTitleAttribute extends AbstractRule {
9
+ protected selector: string = `[title]${[
10
+ ':not(img)',
11
+ ':not(html)',
12
+ ':not(head)',
13
+ ':not(title)',
14
+ ':not(body)',
15
+ ':not(link)',
16
+ ':not(meta)',
17
+ ':not(title)',
18
+ ':not(style)',
19
+ ':not(script)',
20
+ ':not(noscript)',
21
+ ':not(iframe)',
22
+ ':not(br)',
23
+ ':not(hr)'
24
+ ].join('')}`;
25
+
26
+ protected ruleConfig: IAbstractRuleConfig = {
27
+ id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.empty_title_attribute),
28
+ links: [
29
+ {
30
+ content: 'H67: Using null alt text and no title attribute on img elements for images that AT should ignore',
31
+ url: 'https://www.w3.org/TR/WCAG20-TECHS/H67.html'
32
+ }
33
+ ],
34
+ recommendations: [],
35
+ severity: $severity.low,
36
+ type: CATEGORY_TYPE.BEST_PRACTICE
37
+ };
38
+
39
+ public validate(elements: Element[]): void {
40
+ const reportEmptyTitle = (element: Element): void => {
41
+ const titleAttribute: string | null = element.getAttribute('title');
42
+
43
+ if (titleAttribute === null || titleAttribute.trim().length > 0) {
44
+ return;
45
+ }
46
+
47
+ const report: IIssueReport = {
48
+ message: TranslateService.instant('empty_title_attribute_report_message'),
49
+ node: element,
50
+ ruleId: this.ruleConfig.id
51
+ };
52
+
53
+ this.validator.report(report);
54
+ };
55
+
56
+ elements.forEach(reportEmptyTitle);
57
+ }
58
+ }
@@ -0,0 +1,48 @@
1
+ # flash-content
2
+
3
+ ## Rule id
4
+
5
+ `flash-content`
6
+
7
+ ## Definition
8
+
9
+ This rule verifies if there are Adobe Flash Player components.
10
+
11
+ ## Purpose
12
+
13
+ > Since Adobe no longer supports Flash Player after December 31, 2020 and blocked Flash content from running in Flash Player beginning January 12, 2021, Adobe strongly recommends all users immediately uninstall Flash Player to help protect their systems.
14
+
15
+ Source: https://www.adobe.com/products/flashplayer/end-of-life.html
16
+
17
+ Due to above security reasons and poor an accessibility support Adobe Flash player should be removed.
18
+
19
+ Following elements are being considered as Adobe Flash Player while evaluating this rule:
20
+
21
+ CSS selector:
22
+
23
+ [classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"], embed[type="application/x-shockwave-flash"], object[type="application/x-shockwave-flash"]
24
+
25
+ ## Test cases
26
+
27
+ ### Passed
28
+
29
+ The rule passes when there are no elements that refers to Adobe Flash Player.
30
+
31
+ ## WCAG Success Criteria
32
+
33
+ Not Applicable
34
+
35
+ ## Best Practice
36
+
37
+ Yes
38
+
39
+ ## User Impact
40
+
41
+ * **Severity**: low
42
+ * **Disabilities Affected**:
43
+ * Visual:
44
+ * blindness
45
+
46
+ ## Resources
47
+
48
+ * https://www.adobe.com/products/flashplayer/end-of-life.html
@@ -0,0 +1,52 @@
1
+ import { FlashContent } from './flash-content';
2
+ import { DomUtility } from '../../../utils/dom';
3
+ import { Validator } from '../../../validator';
4
+
5
+ describe('Rules', () => {
6
+
7
+ describe('#flash-content', () => {
8
+
9
+ let fakeDom;
10
+
11
+ new FlashContent().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 one report for one flash object', () => {
27
+ const objectEl = document.createElement('object');
28
+
29
+ objectEl.setAttribute('classid', 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000');
30
+ objectEl.setAttribute('codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0');
31
+ fakeDom.appendChild(objectEl);
32
+
33
+ const nodes = DomUtility.querySelectorAllExclude('[classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"]', fakeDom);
34
+
35
+ new FlashContent().validate(nodes);
36
+
37
+ expect(Object.keys(Validator.getReports()).length).toBe(1);
38
+ });
39
+
40
+ it('should return no reports where there are no flash objects', () => {
41
+ fakeDom.innerHTML = '<a href="#">test</a>';
42
+
43
+ const nodes = DomUtility.querySelectorAllExclude('[classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"]', fakeDom);
44
+
45
+ new FlashContent().validate(nodes);
46
+
47
+ expect(Object.keys(Validator.getReports()).length).toBe(0);
48
+ });
49
+
50
+ });
51
+
52
+ });
@@ -0,0 +1,32 @@
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 FlashContent extends AbstractRule {
10
+ protected selector: string = '[classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"], embed[type="application/x-shockwave-flash"], object[type="application/x-shockwave-flash"]';
11
+ protected ruleConfig: IAbstractRuleConfig = {
12
+ id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.flash_content),
13
+ links: [],
14
+ recommendations: [],
15
+ severity: $severity.critical,
16
+ type: CATEGORY_TYPE.BEST_PRACTICE
17
+ };
18
+
19
+ public validate(elements: Element[]): void {
20
+ const reportFlash = (element: Element): void => {
21
+ const problem: IIssueReport = {
22
+ message: TranslateService.instant('flash_content_report_message'),
23
+ node: element,
24
+ ruleId: this.ruleConfig.id
25
+ };
26
+
27
+ this.validator.report(problem);
28
+ };
29
+
30
+ elements.forEach(reportFlash);
31
+ }
32
+ }
@@ -0,0 +1,44 @@
1
+ # font-style-italic
2
+
3
+ ## Rule id
4
+
5
+ `font-style-italic`
6
+
7
+ ## Definition
8
+
9
+ This rule verifies if there is defined `font-style` type `italic` for a text with length > 80 chars.
10
+
11
+ ## Purpose
12
+
13
+ [WCAG Understanding Guideline 3.1](https://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning.html) includes an advisory technique for "avoiding chunks of italic text". Similarly [WebAIM advises](https://webaim.org/articles/evaluatingcognitive/#readability) as follows: "Do not use italics or bold on long sections of text", but at the same time "use various stylistic elements (italics, bold, color, brief animation, or differently-styled content) to highlight important content".
14
+
15
+ This rule checks 2 cases:
16
+
17
+ * Determine reasonable text length - must be < 80 chars.
18
+ * Determine if element has defined `font-style` type `italic`.
19
+
20
+ ## Test cases
21
+
22
+ ### Passed
23
+
24
+ The rule passes when specified element contains text with length > 80 chars and have no defined style `font-style` type `italic`.
25
+
26
+ ## WCAG Success Criteria
27
+
28
+ Not Applicable
29
+
30
+ ## Best Practice
31
+
32
+ Yes
33
+
34
+ ## User Impact
35
+
36
+ * **Severity**: low
37
+ * **Disabilities Affected**:
38
+ * Visual:
39
+ * blindness
40
+
41
+ ## Resources
42
+
43
+ * https://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning.html
44
+ * https://webaim.org/articles/evaluatingcognitive/#readability
@@ -0,0 +1,12 @@
1
+ import { FontStyleItalic } from './font-style-italic';
2
+
3
+ describe('Rules', () => {
4
+
5
+ describe('FontStyleItalic', () => {
6
+
7
+ it('should indicate that class exists', () => {
8
+ expect(FontStyleItalic).toBeDefined();
9
+ });
10
+
11
+ });
12
+ });
@@ -0,0 +1,83 @@
1
+ import { DomUtility } from '../../../utils/dom';
2
+ import { Css } from '../../../utils/css';
3
+ import { TextUtility } from '../../../utils/text';
4
+ import { CATEGORY_TYPE } from '../../../constants/categoryType';
5
+ import { IIssueReport } from '../../../interfaces/rule-issue.interface';
6
+ import { TranslateService } from '../../../services/translate';
7
+ import { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
8
+ import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
9
+
10
+ export class FontStyleItalic extends AbstractRule {
11
+
12
+ protected selector: string = `*${[
13
+ ':root',
14
+ 'head',
15
+ 'style',
16
+ 'script',
17
+ 'noscript',
18
+ 'meta',
19
+ 'link',
20
+ 'br',
21
+ 'hr',
22
+ 'object',
23
+ 'svg',
24
+ 'path',
25
+ 'defs',
26
+ 'rect',
27
+ 'clippath',
28
+ 'use',
29
+ 'g',
30
+ 'b',
31
+ 'filter',
32
+ 'img',
33
+ 'picture',
34
+ 'input',
35
+ 'iframe',
36
+ 'code',
37
+ 'metadata',
38
+ ':empty'
39
+ ].map((i: string): string => {
40
+ return `:not(${i})`;
41
+ }).join('')}`;
42
+
43
+ protected ruleConfig: IAbstractRuleConfig = {
44
+ id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.font_style_italic),
45
+ links: [
46
+ {
47
+ content: 'Avoiding chunks of italic text',
48
+ url: 'https://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning.html'
49
+ }
50
+ ],
51
+ recommendations: [],
52
+ severity: $severity.info,
53
+ type: CATEGORY_TYPE.BEST_PRACTICE
54
+ };
55
+
56
+ public validate(elements: HTMLElement[]): void {
57
+ const reportNode = (element: HTMLElement): void => {
58
+ let isItalic: boolean = false;
59
+ const textContent: string = DomUtility.getTextFromDescendantContent(element).trim();
60
+ const textContentLength: number = textContent.length;
61
+ const REASONABLE_LONG_TEXT: number = 80;
62
+
63
+ // Note: element.style may not exists, e.g. for an element in a different namespace
64
+ if (Css.getStyle(element, 'font-style') === 'italic' || typeof element.style === 'object' && element.style.fontStyle === 'italic') {
65
+ isItalic = true;
66
+ }
67
+
68
+ if (isItalic === false || textContentLength < REASONABLE_LONG_TEXT) {
69
+ return;
70
+ }
71
+
72
+ const report: IIssueReport = {
73
+ message: TranslateService.instant('font_style_italic_report_message', [textContentLength]),
74
+ node: element,
75
+ ruleId: this.ruleConfig.id
76
+ };
77
+
78
+ this.validator.report(report);
79
+ };
80
+
81
+ elements.forEach(reportNode);
82
+ }
83
+ }
@@ -0,0 +1,46 @@
1
+ # h1-must-be
2
+
3
+ ## Rule id
4
+
5
+ `h1-must-be`
6
+
7
+ ## Definition
8
+
9
+ This rule verifies if there is defined heading `h1` on the page.
10
+
11
+ ## Purpose
12
+
13
+ Most content on web pages should be organized into sections. When pages are organized into sections, a heading should be present.
14
+
15
+ All pages should at least have a `<h1>` level heading giving the title of the page.
16
+
17
+ This rule checks 1 case:
18
+
19
+ * Determine if there is at least 1 element `<h1>`.
20
+
21
+ **Note**: the rule does not check if the content of `<h1>` is empty.
22
+
23
+ ## Test cases
24
+
25
+ ### Passed
26
+
27
+ The rule passes when there is at least 1 `<h1>` element.
28
+
29
+ ## WCAG Success Criteria
30
+
31
+ Not Applicable
32
+
33
+ ## Best Practice
34
+
35
+ Yes
36
+
37
+ ## User Impact
38
+
39
+ * **Severity**: critical
40
+ * **Disabilities Affected**:
41
+ * Visual:
42
+ * blindness
43
+
44
+ ## Resources
45
+
46
+ * https://www.w3.org/WAI/tutorials/page-structure/headings/
@@ -0,0 +1,46 @@
1
+ import { H1MustBe } from './h1-must-be';
2
+ import { Validator } from '../../../validator';
3
+ import { DomUtility } from '../../../utils/dom';
4
+
5
+ describe('Rules', () => {
6
+
7
+ describe('H1MustBe', () => {
8
+
9
+ let fakeDom;
10
+
11
+ new H1MustBe().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 one report when there is no elements with h1', () => {
27
+ fakeDom.innerHTML = '<div><h2>h1</h2></div>';
28
+
29
+ new H1MustBe().run(fakeDom);
30
+
31
+ expect(Object.keys(Validator.getReports()).length).toBe(1);
32
+ expect(Validator.getReport('report_0').message).toBe('Expected at least one heading <code>h1</code> element, but found none.');
33
+ expect(Validator.getReport('report_0').node).toBe(null);
34
+ expect(Validator.getReport('report_0').ruleId).toBe('h1-must-be');
35
+ });
36
+
37
+ it('should return no reports when there is element with h1', () => {
38
+ fakeDom.innerHTML = '<div><h1>h1</h1></div>';
39
+
40
+ new H1MustBe().run(fakeDom);
41
+
42
+ expect(Object.keys(Validator.getReports()).length).toBe(0);
43
+ });
44
+
45
+ });
46
+ });
@@ -0,0 +1,36 @@
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 { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
6
+ import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
7
+
8
+ export class H1MustBe extends AbstractRule {
9
+ protected selector: string = 'h1';
10
+ protected ruleConfig: IAbstractRuleConfig = {
11
+ id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.h1_must_be),
12
+ links: [
13
+ {
14
+ content: 'Web Accessibility Tutorials: Headings',
15
+ url: 'https://www.w3.org/WAI/tutorials/page-structure/headings/'
16
+ }
17
+ ],
18
+ recommendations: [],
19
+ severity: $severity.critical,
20
+ type: CATEGORY_TYPE.BEST_PRACTICE
21
+ };
22
+
23
+ public validate(elements: Element[]): void {
24
+ if (elements.length > 0) {
25
+ return;
26
+ }
27
+
28
+ const report: IIssueReport = {
29
+ message: TranslateService.instant('h1_must_report_message'),
30
+ node: null,
31
+ ruleId: this.ruleConfig.id
32
+ };
33
+
34
+ this.validator.report(report);
35
+ }
36
+ }
@@ -0,0 +1,57 @@
1
+ # headings-sibling-unique
2
+
3
+ ## Rule id
4
+
5
+ `headings-sibling-unique`
6
+
7
+ ## Definition
8
+
9
+ This rule verifies if the accessible names of sibling heading elements of the same level are unique.
10
+
11
+ ## Purpose
12
+
13
+ If section headings that share the same parent heading are not unique, users of assistive technologies will not be able to discern the differences among sibling sections of the web page.
14
+
15
+ This rule checks 1 case:
16
+
17
+ * Sibling headings accessible names must be unique.
18
+
19
+ ### Example
20
+
21
+ #### Incorrect
22
+
23
+ <h2>Example</h2>
24
+ <h2>Example</h2>
25
+
26
+ #### Correct
27
+
28
+ <h2>Example 1</h2>
29
+ <h2>Example 2</h2>
30
+
31
+
32
+ ## Test cases
33
+
34
+ ### Passed
35
+
36
+ The rule passes when sibiling headings are having unique accessible name.
37
+
38
+ ## WCAG Success Criteria
39
+
40
+ Not Applicable
41
+
42
+ ## Best Practice
43
+
44
+ Yes
45
+
46
+ ## User Impact
47
+
48
+ * **Severity**: critical
49
+ * **Disabilities Affected**:
50
+ * Visual:
51
+ * blindness
52
+
53
+ ## Resources
54
+
55
+ * http://www.w3.org/TR/WCAG20/#navigation-mechanisms-descriptive
56
+ * http://www.w3.org/TR/WCAG20-TECHS/G130
57
+ * https://www.w3.org/TR/WCAG20-TECHS/G141.html
@@ -0,0 +1,52 @@
1
+ import { DomUtility } from '../../../utils/dom';
2
+ import { Validator } from '../../../validator';
3
+ import { HeadingsSiblingUnique } from './headings-sibling-unique';
4
+
5
+ describe('Rules', () => {
6
+
7
+ describe('HeadingsSiblingUnique', () => {
8
+
9
+ it('should indicate that class exists', () => {
10
+ expect(HeadingsSiblingUnique).toBeDefined();
11
+ });
12
+
13
+ let fakeDom;
14
+
15
+ new HeadingsSiblingUnique().registerValidator();
16
+
17
+ beforeEach(() => {
18
+ fakeDom = document.createElement('div');
19
+ fakeDom.id = 'fakedom';
20
+ document.body.appendChild(fakeDom);
21
+
22
+ Validator.reset();
23
+ });
24
+
25
+ afterEach(() => {
26
+ DomUtility.remove(document.getElementById('fakedom'));
27
+ fakeDom = undefined;
28
+ });
29
+
30
+ it('should return 1 report when there are non-unique siblings headings', () => {
31
+ fakeDom.innerHTML = '<h1>Test</h1><h1>Test</h1>';
32
+ const nodes = DomUtility.querySelectorAllExclude('h1', fakeDom) as HTMLHeadingElement[];
33
+
34
+ new HeadingsSiblingUnique().validate(nodes);
35
+
36
+ expect(Object.keys(Validator.getReports()).length).toBe(1);
37
+ expect(Validator.getReport('report_0').message).toBe('The accessible names of sibling heading elements of the same level are not unique. If section headings that share the same parent heading are not unique, users of assistive technologies will not be able to discern the differences among sibling sections of the web page. Same level <code>h1</code> and same description: <q>Test</q>.');
38
+ expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('h1');
39
+ expect(Validator.getReport('report_0').ruleId).toBe('headings-sibling-unique');
40
+ });
41
+
42
+ it('should return no reports when two headings description are different', () => {
43
+ fakeDom.innerHTML = '<h1>Test</h1><h1>Test 2</h1>';
44
+ const nodes = DomUtility.querySelectorAllExclude('h1', fakeDom) as HTMLHeadingElement[];
45
+
46
+ new HeadingsSiblingUnique().validate(nodes);
47
+
48
+ expect(Object.keys(Validator.getReports()).length).toBe(0);
49
+ });
50
+
51
+ });
52
+ });