Skip to content

Commit 799382f

Browse files
mohammedzamakhanmgechev
authored andcommitted
feat(rule): autofocus attribute should not be used (#749)
1 parent 512a5bd commit 799382f

File tree

3 files changed

+114
-0
lines changed

3 files changed

+114
-0
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export { Rule as TemplateConditionalComplexityRule } from './templateConditional
3030
export { Rule as TemplateCyclomaticComplexityRule } from './templateCyclomaticComplexityRule';
3131
export { Rule as TemplateAccessibilityTabindexNoPositiveRule } from './templateAccessibilityTabindexNoPositiveRule';
3232
export { Rule as TemplatesNoNegatedAsync } from './templatesNoNegatedAsyncRule';
33+
export { Rule as TemplateNoAutofocusRule } from './templateNoAutofocusRule';
3334
export { Rule as TrackByFunctionRule } from './trackByFunctionRule';
3435
export { Rule as UseHostPropertyDecoratorRule } from './useHostPropertyDecoratorRule';
3536
export { Rule as UseInputPropertyDecoratorRule } from './useInputPropertyDecoratorRule';

src/templateNoAutofocusRule.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { AttrAst, BoundElementPropertyAst } from '@angular/compiler';
2+
import { IRuleMetadata, RuleFailure, Rules } from 'tslint/lib';
3+
import { SourceFile } from 'typescript/lib/typescript';
4+
import { NgWalker } from './angular/ngWalker';
5+
import { BasicTemplateAstVisitor } from './angular/templates/basicTemplateAstVisitor';
6+
7+
export class Rule extends Rules.AbstractRule {
8+
static readonly metadata: IRuleMetadata = {
9+
description: 'Ensure that autofocus property is not used',
10+
options: null,
11+
optionsDescription: 'Not configurable.',
12+
rationale: 'autofocus attribute reduces usability and accessibility for users.',
13+
ruleName: 'template-no-autofocus',
14+
type: 'functionality',
15+
typescriptOnly: true
16+
};
17+
18+
static readonly FAILURE_STRING = 'autofocus attribute should not be used, as it reduces usability and accessibility for users.';
19+
20+
apply(sourceFile: SourceFile): RuleFailure[] {
21+
return this.applyWithWalker(
22+
new NgWalker(sourceFile, this.getOptions(), {
23+
templateVisitorCtrl: TemplateConditionalComplexityVisitor
24+
})
25+
);
26+
}
27+
}
28+
29+
class TemplateConditionalComplexityVisitor extends BasicTemplateAstVisitor {
30+
visitAttr(ast: AttrAst, context: any) {
31+
this.validateAttribute(ast);
32+
super.visitAttr(ast, context);
33+
}
34+
35+
visitElementProperty(ast: BoundElementPropertyAst) {
36+
this.validateAttribute(ast);
37+
super.visitElementProperty(ast, context);
38+
}
39+
40+
validateAttribute(ast: AttrAst | BoundElementPropertyAst) {
41+
if (ast.name === 'autofocus') {
42+
const {
43+
sourceSpan: {
44+
end: { offset: endOffset },
45+
start: { offset: startOffset }
46+
}
47+
} = ast;
48+
this.addFailureFromStartToEnd(startOffset, endOffset, Rule.FAILURE_STRING);
49+
}
50+
}
51+
}

test/templateNoAutofocusRule.spec.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { assertAnnotated, assertMultipleAnnotated, assertSuccess } from './testHelper';
2+
import { Rule } from '../src/templateNoAutofocusRule';
3+
4+
const {
5+
FAILURE_STRING,
6+
metadata: { ruleName }
7+
} = Rule;
8+
9+
describe(ruleName, () => {
10+
describe('failure', () => {
11+
it('should fail if autofocus attribute is used', () => {
12+
const source = `
13+
@Component({
14+
selector: 'test',
15+
template: '<div autofocus>Autofocus</div>'
16+
~~~~~~~~~
17+
})
18+
class Test {
19+
constructor(foo: Observable<Boolean>) {}
20+
}
21+
`;
22+
assertAnnotated({
23+
message: FAILURE_STRING,
24+
ruleName: ruleName,
25+
source
26+
});
27+
});
28+
29+
it('should fail if autofocus input is used', () => {
30+
const source = `
31+
@Component({
32+
selector: 'test',
33+
template: '<div [attr.autofocus]="false">Autofocus</div>'
34+
~~~~~~~~~~~~~~~~~~~~~~~~
35+
})
36+
class Test {
37+
constructor(foo: Observable<Boolean>) {}
38+
}
39+
`;
40+
assertAnnotated({
41+
message: FAILURE_STRING,
42+
ruleName: ruleName,
43+
source
44+
});
45+
});
46+
});
47+
48+
describe('success', () => {
49+
it('should succeed if autofocus is not used', () => {
50+
const source = `
51+
@Component({
52+
selector: 'test',
53+
template: '<div>No Autofocus</div>'
54+
})
55+
class Test {
56+
constructor(foo: Observable<Boolean>) {}
57+
}
58+
`;
59+
assertSuccess(ruleName, source);
60+
});
61+
});
62+
});

0 commit comments

Comments
 (0)