Skip to content

Commit

Permalink
feat(rule): add special case to no-input-rename rule (#561)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelss95 authored and wKoza committed Apr 18, 2018
1 parent 174ed46 commit f3a53bd
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 42 deletions.
35 changes: 19 additions & 16 deletions src/noInputRenameRule.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as Lint from 'tslint';
import * as ts from 'typescript';
import { sprintf } from 'sprintf-js';
import { DirectiveMetadata } from './angular/metadata';
import { NgWalker } from './angular/ngWalker';

export class Rule extends Lint.Rules.AbstractRule {
Expand All @@ -15,29 +16,31 @@ export class Rule extends Lint.Rules.AbstractRule {
typescriptOnly: true,
};

static FAILURE_STRING: string = 'In the class "%s", the directive ' +
'input property "%s" should not be renamed.' +
'Please, consider the following use "@Input() %s: string"';
static FAILURE_STRING: string = 'In the class "%s", the directive input property "%s" should not be renamed.';

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(
new InputMetadataWalker(sourceFile,
this.getOptions()));
return this.applyWithWalker(new InputMetadataWalker(sourceFile, this.getOptions()));
}
}

export class InputMetadataWalker extends NgWalker {
private directiveSelector: DirectiveMetadata['selector'][];

visitNgDirective(metadata: DirectiveMetadata): void {
this.directiveSelector =
(metadata.selector || '').replace(/[\[\]\s]/g, '').split(',');
}

visitNgInput(property: ts.PropertyDeclaration, input: ts.Decorator, args: string[]) {
let className = (<any>property).parent.name.text;
let memberName = (<any>property.name).text;
if (args.length !== 0 && memberName !== args[0]) {
let failureConfig: string[] = [className, memberName, memberName];
failureConfig.unshift(Rule.FAILURE_STRING);
this.addFailure(
this.createFailure(
property.getStart(),
property.getWidth(),
sprintf.apply(this, failureConfig)));
const className = (property.parent as any).name.text;
const memberName = (property.name as any).text;

if (args.length === 0 ||
(this.directiveSelector && this.directiveSelector.indexOf(memberName) !== -1)) {
return;
}

const failureConfig = [Rule.FAILURE_STRING, className, memberName];
this.addFailureAtNode(property, sprintf.apply(this, failureConfig));
}
}
126 changes: 100 additions & 26 deletions test/noInputRenameRule.spec.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,111 @@
import { assertSuccess, assertAnnotated } from './testHelper';

describe('no-input-rename', () => {
describe('invalid directive input property', () => {
it('should fail, when a directive input property is renamed', () => {
let source = `
class ButtonComponent {
@Input('labelAttribute') label: string;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}`;
assertAnnotated({
ruleName: 'no-input-rename',
message: 'In the class "ButtonComponent", the directive input property "label" should not be renamed.' +
'Please, consider the following use "@Input() label: string"',
source
const ruleName = 'no-input-rename';

const getMessage = (className: string, propertyName: string): string => {
return `In the class "${className}", the directive input property "${propertyName}" should not be renamed.`;
};

describe(ruleName, () => {
describe('failure', () => {
describe('Component', () => {
it('should fail when a input property is renamed', () => {
const source = `
@Component
class TestComponent {
@Input('labelAttribute') label: string;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
`;

assertAnnotated({
ruleName,
message: getMessage('TestComponent', 'label'),
source
});
});

it('should fail when input property is fake renamed', () => {
const source = `
@Component
class TestComponent {
@Input('label') label: string;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
`;

assertAnnotated({
ruleName,
message: getMessage('TestComponent', 'label'),
source
});
});
});

describe('Directive', () => {
it('should fail when a input property is renamed', () => {
const source = `
@Directive
class TestDirective {
@Input('labelText') label: string;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
`;

assertAnnotated({
ruleName,
message: getMessage('TestDirective', 'label'),
source
});
});

it(`should fail when input property is renamed and it's different from directive's selector`, () => {
const source = `
@Directive({
selector: '[label], label2'
})
class TestDirective {
@Input('label') labelText: string;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
`;

assertAnnotated({
ruleName,
message: getMessage('TestDirective', `labelText`),
source
});
});
});
});

describe('valid directive input property', () => {
it('should succeed, when a directive input property is properly used', () => {
let source = `
class ButtonComponent {
@Input() label: string;
}`;
assertSuccess('no-input-rename', source);
describe('success', () => {
describe('Component', () => {
it('should succeed when a input property is not renamed', () => {
const source = `
@Component
class TestComponent {
@Input() label: string;
}
`;

assertSuccess(ruleName, source);
});
});

it('should succeed, when a directive input property rename is the same as the name of the property', () => {
let source = `
class ButtonComponent {
@Input('label') label: string;
}`;
assertSuccess('no-input-rename', source);
describe('Directive', () => {
it('should succeed when the directive name is also an input property', () => {
const source = `
@Directive({
selector: '[label], label2'
})
class TestDirective {
@Input('labelText') label: string;
}
`;

assertSuccess(ruleName, source);
});
});
});
});

0 comments on commit f3a53bd

Please sign in to comment.