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,83 +0,0 @@
|
|
|
1
|
-
import { IRule } from '../interfaces/rule.interface';
|
|
2
|
-
import { CATEGORY_TYPE } from '../constants/categoryType';
|
|
3
|
-
import { $severity } from '../constants/accessibility';
|
|
4
|
-
import { DomUtility } from '../utils/dom';
|
|
5
|
-
import { Validator } from '../validator';
|
|
6
|
-
import { Config } from '../config';
|
|
7
|
-
import { Context } from '../interfaces/context.interface';
|
|
8
|
-
|
|
9
|
-
type Nodes = Document[] | HTMLElement[] | Element[] | StyleSheetList[] | CSSStyleSheet[];
|
|
10
|
-
|
|
11
|
-
export interface IAbstractRuleConfig {
|
|
12
|
-
id: string;
|
|
13
|
-
type: CATEGORY_TYPE;
|
|
14
|
-
severity: $severity;
|
|
15
|
-
links: { content: string; url: string }[];
|
|
16
|
-
recommendations: any[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export abstract class AbstractRule implements IRule {
|
|
20
|
-
protected context: Context;
|
|
21
|
-
protected options: any;
|
|
22
|
-
protected validator: typeof Validator;
|
|
23
|
-
protected selector: string | (() => Nodes);
|
|
24
|
-
protected abstract ruleConfig: IAbstractRuleConfig;
|
|
25
|
-
|
|
26
|
-
constructor() {
|
|
27
|
-
this.validator = Validator;
|
|
28
|
-
this.selector = 'html';
|
|
29
|
-
this.context = document.documentElement;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
protected collectElements(): Nodes | null {
|
|
33
|
-
if (typeof this.selector === 'undefined') {
|
|
34
|
-
throw new Error(`[AbstractRule] The selector must be a string or function, but there is type of ${typeof this.selector}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (typeof this.selector === 'function') {
|
|
38
|
-
return this.selector();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return DomUtility.querySelectorAllExclude(
|
|
42
|
-
this.selector,
|
|
43
|
-
this.context,
|
|
44
|
-
Config.excludeContainers,
|
|
45
|
-
[]
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public abstract validate(nodes: Document[] | HTMLElement[] | Element[] | StyleSheetList | StyleSheetList[] | CSSStyleSheet[] | null): void;
|
|
50
|
-
|
|
51
|
-
public get config(): IAbstractRuleConfig {
|
|
52
|
-
return this.ruleConfig;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public get id(): string {
|
|
56
|
-
return this.config.id;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// eslint-disable-next-line require-await
|
|
60
|
-
public async run(context: Context, validator?: typeof Validator, options?: any): Promise<void> {
|
|
61
|
-
this.context = context;
|
|
62
|
-
this.options = (typeof options === 'object') ? options : {};
|
|
63
|
-
this.validator = (typeof validator !== 'undefined') ? validator : Validator;
|
|
64
|
-
|
|
65
|
-
let nodes: Nodes | null = this.collectElements();
|
|
66
|
-
const totalElementsEvaluated: number = nodes === null ? 0 : nodes.length;
|
|
67
|
-
|
|
68
|
-
this.validator.setTotalElementsEvaluated(this.ruleConfig.id, totalElementsEvaluated);
|
|
69
|
-
|
|
70
|
-
const getFilter = (filter: Function): void => {
|
|
71
|
-
nodes = filter(this.ruleConfig.id, nodes);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
Config.getFilters('before-rule-validate').forEach(getFilter);
|
|
75
|
-
|
|
76
|
-
this.validate(nodes);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public registerValidator(): void {
|
|
80
|
-
this.validator.register(this.ruleConfig, this.run.bind(this));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# incorrect-technique-for-hiding-content
|
|
2
|
-
|
|
3
|
-
## Rule id
|
|
4
|
-
|
|
5
|
-
`incorrect-technique-for-hiding-content`
|
|
6
|
-
|
|
7
|
-
## Definition
|
|
8
|
-
|
|
9
|
-
This rule determines if there is defined style `text-indent` with the value so that the element is being positioned out of the viewport.
|
|
10
|
-
|
|
11
|
-
## Purpose
|
|
12
|
-
|
|
13
|
-
The technique e.g. `text-indent: -10000px` is still being used to hide the content visually. However, the downside of that technique is that the screen reader focus follows that and it gets out of the visible area.
|
|
14
|
-
|
|
15
|
-
**Tip**: to avoid that use tecnique that hides the content visually, but does not move the element outside of the visible viewport. Example: https://github.com/twbs/bootstrap/blob/main/scss/mixins/_visually-hidden.scss
|
|
16
|
-
|
|
17
|
-
## Test cases
|
|
18
|
-
|
|
19
|
-
### Passed
|
|
20
|
-
|
|
21
|
-
The rule passes when there is no defined style `text-indent` that hides the content outside of viewport.
|
|
22
|
-
|
|
23
|
-
## WCAG Success Criteria
|
|
24
|
-
|
|
25
|
-
Not Applicable
|
|
26
|
-
|
|
27
|
-
## Best Practice
|
|
28
|
-
|
|
29
|
-
Yes
|
|
30
|
-
|
|
31
|
-
## User Impact
|
|
32
|
-
|
|
33
|
-
* **Severity**: info
|
|
34
|
-
* **Disabilities Affected**:
|
|
35
|
-
* Visual:
|
|
36
|
-
* keyboard users
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { IncorrectTechniqueForHidingContent } from './incorrect-technique-for-hiding-content';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('IncorrectTechniqueForHidingContent', () => {
|
|
8
|
-
|
|
9
|
-
it('should indicate that class exists', () => {
|
|
10
|
-
expect(IncorrectTechniqueForHidingContent).toBeDefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
let fakeDom;
|
|
14
|
-
|
|
15
|
-
new IncorrectTechniqueForHidingContent().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
|
-
// Note: untestable until https://github.com/facebook/jest/issues/11291 gets resolved
|
|
31
|
-
|
|
32
|
-
/*
|
|
33
|
-
*
|
|
34
|
-
*it('should return 1 report when there is an element with defined style text-indent: -1000px;', () => {
|
|
35
|
-
* fakeDom.innerHTML = '<p style="text-indent: -1000px">Example text</p>';
|
|
36
|
-
* const nodes: HTMLParagraphElement[] = DomUtility.querySelectorAllExclude('p', fakeDom) as HTMLParagraphElement[];
|
|
37
|
-
*
|
|
38
|
-
* new IncorrectTechniqueForHidingContent().validate(nodes);
|
|
39
|
-
*
|
|
40
|
-
* expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
41
|
-
* expect(Validator.getReport('report_0').message).toBe('CSS technique <code>text-indent: -1000px </code> is used to hide text. However, it causes problems for right-to-left language and also keep focus for screen reader outside of visible area.');
|
|
42
|
-
* expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('p');
|
|
43
|
-
* expect(Validator.getReport('report_0').ruleId).toBe('incorrect-technique-for-hiding-content');
|
|
44
|
-
*});
|
|
45
|
-
*
|
|
46
|
-
*it('should return 1 report when there is an element with defined style text-indent: 1000px;', () => {
|
|
47
|
-
* fakeDom.innerHTML = '<p style="text-indent: 1000px">Example text</p>';
|
|
48
|
-
* const nodes: HTMLParagraphElement[] = DomUtility.querySelectorAllExclude('p', fakeDom) as HTMLParagraphElement[];
|
|
49
|
-
*
|
|
50
|
-
* new IncorrectTechniqueForHidingContent().validate(nodes);
|
|
51
|
-
*
|
|
52
|
-
* expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
53
|
-
* expect(Validator.getReport('report_0').message).toBe('CSS technique <code>text-indent: 1000px </code> is used to hide text. However, it causes problems for right-to-left language and also keep focus for screen reader outside of visible area.');
|
|
54
|
-
* expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('p');
|
|
55
|
-
* expect(Validator.getReport('report_0').ruleId).toBe('incorrect-technique-for-hiding-content');
|
|
56
|
-
*});
|
|
57
|
-
*
|
|
58
|
-
*it('should return no report when there is an element with no defined style text-indent', () => {
|
|
59
|
-
* fakeDom.innerHTML = '<p>Example text</p>';
|
|
60
|
-
* const nodes: HTMLParagraphElement[] = DomUtility.querySelectorAllExclude('p', fakeDom) as HTMLParagraphElement[];
|
|
61
|
-
*
|
|
62
|
-
* new IncorrectTechniqueForHidingContent().validate(nodes);
|
|
63
|
-
*
|
|
64
|
-
* expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
65
|
-
*});
|
|
66
|
-
*
|
|
67
|
-
*it('should return no report when there is an element with defined style text-indent that doesn\'t move content outside of visible area', () => {
|
|
68
|
-
* const createMockDiv = (width, height) => {
|
|
69
|
-
* const div = document.createElement('div');
|
|
70
|
-
*
|
|
71
|
-
* Object.assign(div.style, {
|
|
72
|
-
* height: `${height}px`,
|
|
73
|
-
* width: `${width}px`
|
|
74
|
-
* });
|
|
75
|
-
*
|
|
76
|
-
* div.getBoundingClientRect = () => {
|
|
77
|
-
* return {
|
|
78
|
-
* bottom: height,
|
|
79
|
-
* height,
|
|
80
|
-
* left: 0,
|
|
81
|
-
* right: width,
|
|
82
|
-
* toJSON: () => { },
|
|
83
|
-
* top: 0,
|
|
84
|
-
* width,
|
|
85
|
-
* x: 0,
|
|
86
|
-
* y: 0
|
|
87
|
-
* };
|
|
88
|
-
* };
|
|
89
|
-
*
|
|
90
|
-
* return div;
|
|
91
|
-
* };
|
|
92
|
-
*
|
|
93
|
-
* const div = createMockDiv(100, 100);
|
|
94
|
-
*
|
|
95
|
-
* div.innerText = 'Example text';
|
|
96
|
-
* div.style.textIndent = '-1rem';
|
|
97
|
-
*
|
|
98
|
-
* fakeDom.appendChild(div);
|
|
99
|
-
*
|
|
100
|
-
* const nodes: HTMLDivElement[] = DomUtility.querySelectorAllExclude('div', fakeDom) as HTMLDivElement[];
|
|
101
|
-
*
|
|
102
|
-
* global.innerHeight = 500;
|
|
103
|
-
* global.innerWidth = 500;
|
|
104
|
-
*
|
|
105
|
-
* new IncorrectTechniqueForHidingContent().validate(nodes);
|
|
106
|
-
*
|
|
107
|
-
* expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
108
|
-
*});
|
|
109
|
-
*
|
|
110
|
-
*/
|
|
111
|
-
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { Css } from '../../../utils/css';
|
|
2
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
3
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
4
|
-
import { TextUtility } from '../../../utils/text';
|
|
5
|
-
import { TranslateService } from '../../../services/translate';
|
|
6
|
-
import { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
|
|
7
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
8
|
-
import { DomUtility } from '../../../utils/dom';
|
|
9
|
-
import { ObjectUtility } from '../../../utils/object';
|
|
10
|
-
|
|
11
|
-
export class IncorrectTechniqueForHidingContent 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.incorrect_technique_for_hiding_content),
|
|
40
|
-
links: [
|
|
41
|
-
{
|
|
42
|
-
content: 'Hiding Content for Accessibility',
|
|
43
|
-
url: 'https://snook.ca/archives/html_and_css/hiding-content-for-accessibility'
|
|
44
|
-
}
|
|
45
|
-
],
|
|
46
|
-
recommendations: [],
|
|
47
|
-
severity: $severity.high,
|
|
48
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
public validate(elements: HTMLElement[]): void {
|
|
52
|
-
|
|
53
|
-
if (ObjectUtility.isHostMethod(document, 'getSelection') === false && ObjectUtility.isHostMethod(document, 'createRange') === false) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const reportNode = (element: HTMLElement): void => {
|
|
58
|
-
const styles: CSSStyleDeclaration | null = Css.getComputedStyle(element);
|
|
59
|
-
|
|
60
|
-
if (styles === null) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const textIndentValue: string = styles.getPropertyValue('text-indent');
|
|
65
|
-
const indent: number = Number(textIndentValue.replace(/[^0-9]/g, ''));
|
|
66
|
-
|
|
67
|
-
if (indent === 0) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const selection: Selection | null = document.getSelection();
|
|
72
|
-
|
|
73
|
-
if (selection === null) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const range: Range = document.createRange();
|
|
78
|
-
|
|
79
|
-
range.selectNodeContents(element);
|
|
80
|
-
|
|
81
|
-
selection.removeAllRanges();
|
|
82
|
-
selection.addRange(range);
|
|
83
|
-
|
|
84
|
-
for (let i: number = 0; i < selection.rangeCount; i += 1) {
|
|
85
|
-
if (DomUtility.isRangeOffPage(selection.getRangeAt(i))) {
|
|
86
|
-
const reportMessage: string = TranslateService.instant('incorrect_technique_for_hiding_content_report_message', [textIndentValue]);
|
|
87
|
-
|
|
88
|
-
const report: IIssueReport = {
|
|
89
|
-
message: reportMessage,
|
|
90
|
-
node: element,
|
|
91
|
-
ruleId: this.ruleConfig.id
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
this.validator.report(report);
|
|
95
|
-
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
elements.forEach(reportNode);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# invalid-attribute-dir-value
|
|
2
|
-
|
|
3
|
-
## Rule id
|
|
4
|
-
|
|
5
|
-
`invalid-attribute-dir-value`
|
|
6
|
-
|
|
7
|
-
## Definition
|
|
8
|
-
|
|
9
|
-
This rule determines if a given attribute `dir` have the correct value: `rtl`, `ltr` or `auto`.
|
|
10
|
-
|
|
11
|
-
## Purpose
|
|
12
|
-
|
|
13
|
-
The dir global attribute is an enumerated attribute that indicates the directionality of the element's text. To avoid unexpected behaviour the value of `dir` should be correct and should contains one of following string: `rtl`, `ltr` or `auto`.
|
|
14
|
-
|
|
15
|
-
## Test cases
|
|
16
|
-
|
|
17
|
-
### Passed
|
|
18
|
-
|
|
19
|
-
The rule passes when there is no `dir` attribute with value other than `rtl`, `ltr` or `auto`.
|
|
20
|
-
|
|
21
|
-
## WCAG Success Criteria
|
|
22
|
-
|
|
23
|
-
Not Applicable
|
|
24
|
-
|
|
25
|
-
## Best Practice
|
|
26
|
-
|
|
27
|
-
Yes
|
|
28
|
-
|
|
29
|
-
## User Impact
|
|
30
|
-
|
|
31
|
-
* **Severity**: info
|
|
32
|
-
* **Disabilities Affected**:
|
|
33
|
-
* Visual:
|
|
34
|
-
* blindness
|
package/aslint/app/rules/aslint/invalid-attribute-dir-value/invalid-attribute-dir-value.test.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { InvalidAttributeDirValue } from './invalid-attribute-dir-value';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { DomUtility } from '../../../utils/dom';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('InvalidAttributeDirValue', () => {
|
|
8
|
-
|
|
9
|
-
it('should indicate that class exists', () => {
|
|
10
|
-
expect(InvalidAttributeDirValue).toBeDefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
let fakeDom;
|
|
14
|
-
|
|
15
|
-
new InvalidAttributeDirValue().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 correct data for dir with wrong attribute value', () => {
|
|
31
|
-
fakeDom.innerHTML = '<p dir="wrong">Wrong attribute</p>';
|
|
32
|
-
const nodes = DomUtility.querySelectorAllExclude('[dir]:not([dir="rtl"]):not([dir="ltr"]):not([dir="auto"])', fakeDom) as HTMLElement[];
|
|
33
|
-
|
|
34
|
-
new InvalidAttributeDirValue().validate(nodes);
|
|
35
|
-
|
|
36
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
37
|
-
expect(Validator.getReport('report_0').message).toBe('Attribute <code>dir</code> has invalid value <code>wrong</code>.');
|
|
38
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('p');
|
|
39
|
-
expect(Validator.getReport('report_0').ruleId).toBe('invalid-attribute-dir-value');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should return correct data for dir with empty attribute value', () => {
|
|
43
|
-
fakeDom.innerHTML = '<p dir>Wrong attribute</p>';
|
|
44
|
-
const nodes = DomUtility.querySelectorAllExclude('[dir]:not([dir="rtl"]):not([dir="ltr"]):not([dir="auto"])', fakeDom) as HTMLElement[];
|
|
45
|
-
|
|
46
|
-
new InvalidAttributeDirValue().validate(nodes);
|
|
47
|
-
|
|
48
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
49
|
-
expect(Validator.getReport('report_0').message).toBe('Attribute <code>dir</code> has invalid value <code></code>.');
|
|
50
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('p');
|
|
51
|
-
expect(Validator.getReport('report_0').ruleId).toBe('invalid-attribute-dir-value');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should return no reports in case of dir is ltr', () => {
|
|
55
|
-
fakeDom.innerHTML = '<p dir="ltr">Left to right(English)</p>';
|
|
56
|
-
const nodes = DomUtility.querySelectorAllExclude('[dir]:not([dir="rtl"]):not([dir="ltr"]):not([dir="auto"])', fakeDom) as HTMLElement[];
|
|
57
|
-
|
|
58
|
-
new InvalidAttributeDirValue().validate(nodes);
|
|
59
|
-
|
|
60
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should return no reports in case of dir is rtl', () => {
|
|
64
|
-
fakeDom.innerHTML = '<p dir="rtl">Right to left(Arabic)</p>';
|
|
65
|
-
const nodes = DomUtility.querySelectorAllExclude('[dir]:not([dir="rtl"]):not([dir="ltr"]):not([dir="auto"])', fakeDom) as HTMLElement[];
|
|
66
|
-
|
|
67
|
-
new InvalidAttributeDirValue().validate(nodes);
|
|
68
|
-
|
|
69
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should return no reports in case of dir is auto', () => {
|
|
73
|
-
fakeDom.innerHTML = '<p dir="auto">Auto</p>';
|
|
74
|
-
const nodes = DomUtility.querySelectorAllExclude('[dir]:not([dir="rtl"]):not([dir="ltr"]):not([dir="auto"])', fakeDom) as HTMLElement[];
|
|
75
|
-
|
|
76
|
-
new InvalidAttributeDirValue().validate(nodes);
|
|
77
|
-
|
|
78
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1,44 +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 { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
|
|
6
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
7
|
-
|
|
8
|
-
export class InvalidAttributeDirValue extends AbstractRule {
|
|
9
|
-
protected selector: string = '[dir]:not([dir="rtl"]):not([dir="ltr"]):not([dir="auto"])';
|
|
10
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
11
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.invalid_attribute_dir_value),
|
|
12
|
-
links: [
|
|
13
|
-
{
|
|
14
|
-
content: 'H56: Using the dir attribute on an inline element to resolve problems with nested directional runs',
|
|
15
|
-
url: 'https://www.w3.org/TR/WCAG20-TECHS/H56.html'
|
|
16
|
-
}
|
|
17
|
-
],
|
|
18
|
-
recommendations: [],
|
|
19
|
-
severity: $severity.critical,
|
|
20
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
public validate(elements: HTMLElement[]): void {
|
|
24
|
-
const checkValue = (element: HTMLElement): void => {
|
|
25
|
-
const dirAttribute: string | null = element.getAttribute('dir');
|
|
26
|
-
|
|
27
|
-
if (dirAttribute === null) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const reportMessage: string = TranslateService.instant('invalid_attribute_dir_value_report_message', [TextUtility.escape(dirAttribute)]);
|
|
32
|
-
|
|
33
|
-
const report: IIssueReport = {
|
|
34
|
-
message: reportMessage,
|
|
35
|
-
node: element,
|
|
36
|
-
ruleId: this.ruleConfig.id
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
this.validator.report(report);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
elements.forEach(checkValue);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# label-duplicated-content-title
|
|
2
|
-
|
|
3
|
-
## Rule id
|
|
4
|
-
|
|
5
|
-
`label-duplicated-content-title`
|
|
6
|
-
|
|
7
|
-
## Definition
|
|
8
|
-
|
|
9
|
-
This rule determines if a given element `<label>` contains attribute `title` with the same accessible name.
|
|
10
|
-
|
|
11
|
-
## Purpose
|
|
12
|
-
|
|
13
|
-
Let's examine following example:
|
|
14
|
-
|
|
15
|
-
`<label for="example" title="This is an example">This is an example</label>`
|
|
16
|
-
|
|
17
|
-
Assuming screen reader can read `title`<sup>*</sup>. In above case then `This is an example` will be read twice. To avoid that this rule helps to find such cases.
|
|
18
|
-
|
|
19
|
-
`*` - by default, for example, VoiceOver doesn't read `title` until Hints are turned on.
|
|
20
|
-
|
|
21
|
-
## Test cases
|
|
22
|
-
|
|
23
|
-
### Passed
|
|
24
|
-
|
|
25
|
-
The rule passes when there is no `<label>` with an attribute `title` that contains same content as `<label>`'s accessible name.
|
|
26
|
-
|
|
27
|
-
## WCAG Success Criteria
|
|
28
|
-
|
|
29
|
-
Not Applicable
|
|
30
|
-
|
|
31
|
-
## Best Practice
|
|
32
|
-
|
|
33
|
-
Yes
|
|
34
|
-
|
|
35
|
-
## User Impact
|
|
36
|
-
|
|
37
|
-
* **Severity**: info
|
|
38
|
-
* **Disabilities Affected**:
|
|
39
|
-
* Visual:
|
|
40
|
-
* blindness
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { LabelDuplicatedContentTitle } from './label-duplicated-content-title';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { DomUtility } from '../../../utils/dom';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('LabelDuplicatedContentTitle', () => {
|
|
8
|
-
|
|
9
|
-
let fakeDom;
|
|
10
|
-
|
|
11
|
-
new LabelDuplicatedContentTitle().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 = '<label title="main">main</label>';
|
|
28
|
-
const nodes = DomUtility.querySelectorAllExclude('[title=main]', fakeDom) as HTMLElement[];
|
|
29
|
-
|
|
30
|
-
new LabelDuplicatedContentTitle().validate(nodes);
|
|
31
|
-
|
|
32
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
33
|
-
expect(Validator.getReport('report_0').message).toBe('This element has the same content as its <code>title</code> attribute. Consider removing <code>title</code> as some screen readers read both.');
|
|
34
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('label');
|
|
35
|
-
expect(Validator.getReport('report_0').ruleId).toBe('label-duplicated-content-title');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should return no reports in case of a element with href attribute', () => {
|
|
39
|
-
fakeDom.innerHTML = '<label title="main">main1</label>';
|
|
40
|
-
const nodes = DomUtility.querySelectorAllExclude('[title=main]', fakeDom) as HTMLElement[];
|
|
41
|
-
|
|
42
|
-
new LabelDuplicatedContentTitle().validate(nodes);
|
|
43
|
-
|
|
44
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
});
|
package/aslint/app/rules/aslint/label-duplicated-content-title/label-duplicated-content-title.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { CATEGORY_TYPE } from '../../../constants/categoryType';
|
|
3
|
-
import { IIssueReport } from '../../../interfaces/rule-issue.interface';
|
|
4
|
-
import { TextUtility } from '../../../utils/text';
|
|
5
|
-
import { TranslateService } from '../../../services/translate';
|
|
6
|
-
import { $accessibilityAuditRules, $severity } from '../../../constants/accessibility';
|
|
7
|
-
import { AbstractRule, IAbstractRuleConfig } from '../../abstract-rule';
|
|
8
|
-
|
|
9
|
-
export class LabelDuplicatedContentTitle extends AbstractRule {
|
|
10
|
-
protected selector: string = 'label[title]';
|
|
11
|
-
protected ruleConfig: IAbstractRuleConfig = {
|
|
12
|
-
id: TextUtility.convertUnderscoresToDashes($accessibilityAuditRules.label_duplicated_content_title),
|
|
13
|
-
links: [],
|
|
14
|
-
recommendations: [],
|
|
15
|
-
severity: $severity.high,
|
|
16
|
-
type: CATEGORY_TYPE.BEST_PRACTICE
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
public validate(elements: HTMLElement[]): void {
|
|
20
|
-
const checkLabel = (element: HTMLElement): void => {
|
|
21
|
-
const labelContent: string = DomUtility.getText(element, true);
|
|
22
|
-
const labelTitle: string = element.title;
|
|
23
|
-
|
|
24
|
-
if (labelContent === labelTitle) {
|
|
25
|
-
const report: IIssueReport = {
|
|
26
|
-
message: TranslateService.instant('label_duplicated_content_title_report_message'),
|
|
27
|
-
node: element,
|
|
28
|
-
ruleId: this.ruleConfig.id
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
this.validator.report(report);
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
elements.forEach(checkLabel);
|
|
36
|
-
}
|
|
37
|
-
}
|
package/aslint/app/rules/aslint/links-language-destination/links-language-destination.test.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { DomUtility } from '../../../utils/dom';
|
|
2
|
-
import { Validator } from '../../../validator';
|
|
3
|
-
import { LinksLanguageDestination } from './links-language-destination';
|
|
4
|
-
|
|
5
|
-
describe('Rules', () => {
|
|
6
|
-
|
|
7
|
-
describe('LinksLanguageDestination', () => {
|
|
8
|
-
|
|
9
|
-
it('should indicate that class exists', () => {
|
|
10
|
-
expect(LinksLanguageDestination).toBeDefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
let fakeDom;
|
|
14
|
-
|
|
15
|
-
new LinksLanguageDestination().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 no reports when there is an anchor element with url pointed within page base uri', () => {
|
|
31
|
-
fakeDom.innerHTML = '<a href="http://localhost/example">example</a>';
|
|
32
|
-
|
|
33
|
-
new LinksLanguageDestination().validate(Array.from(document.links));
|
|
34
|
-
|
|
35
|
-
expect(Object.keys(Validator.getReports()).length).toBe(0);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should return 1 report when there is an anchor element with url pointed outside of base uri', () => {
|
|
39
|
-
fakeDom.innerHTML = '<a href="http://example.com/example">example</a>';
|
|
40
|
-
|
|
41
|
-
new LinksLanguageDestination().validate(Array.from(document.links));
|
|
42
|
-
|
|
43
|
-
expect(Object.keys(Validator.getReports()).length).toBe(1);
|
|
44
|
-
expect(Validator.getReport('report_0').message).toBe('Following url <code>http://example.com/example</code> points to an external resource. If the content behind the link is in a different language then consider add some text or graphic to the link indicating that the target document is in another language.');
|
|
45
|
-
expect(Validator.getReport('report_0').node.nodeName.toLowerCase()).toBe('a');
|
|
46
|
-
expect(Validator.getReport('report_0').ruleId).toBe('links-language-destination');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
-
});
|