From d3306e80072b506143a0c92113e0607556fa91ff Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 6 Jul 2018 18:42:03 -0500 Subject: [PATCH] feat(rules): add ion-item-options-attribute-values-renamed rule --- README.md | 8 +- ...onItemOptionsAttributeValuesRenamedRule.ts | 31 +++ ...nItemOptionsAttributeValuesRenamed.spec.ts | 199 ++++++++++++++++++ 3 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 src/ionItemOptionsAttributeValuesRenamedRule.ts create mode 100644 test/ionItemOptionsAttributeValuesRenamed.spec.ts diff --git a/README.md b/README.md index fccb79a..75be7c0 100644 --- a/README.md +++ b/README.md @@ -318,13 +318,13 @@ We are looking for contributors to help build these rules out! See [`CONTRIBUTIN Item Options - - :white_large_square: + :wrench: + :white_check_mark: - ion-item-options-attributes-renamed + ion-item-options-attribute-values-renamed - @mhartington + @dwieeb diff --git a/src/ionItemOptionsAttributeValuesRenamedRule.ts b/src/ionItemOptionsAttributeValuesRenamedRule.ts new file mode 100644 index 0000000..a1f9493 --- /dev/null +++ b/src/ionItemOptionsAttributeValuesRenamedRule.ts @@ -0,0 +1,31 @@ +import { NgWalker } from 'codelyzer/angular/ngWalker'; +import * as Lint from 'tslint'; +import * as ts from 'typescript'; + +import { createAttributeValuesRenamedTemplateVisitorClass } from './helpers/attributeValuesRenamed'; + +export const ruleName = 'ion-item-options-attribute-values-renamed'; + +const replacementMap = new Map([['side', new Map([['left', 'start'], ['right', 'end']])]]); + +const TemplateVisitor = createAttributeValuesRenamedTemplateVisitorClass(['ion-item-options'], replacementMap); + +export class Rule extends Lint.Rules.AbstractRule { + public static metadata: Lint.IRuleMetadata = { + ruleName: ruleName, + type: 'functionality', + description: `Attribute values of ion-item-options have been renamed.`, + options: null, + optionsDescription: 'Not configurable.', + typescriptOnly: false, + hasFix: true + }; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker( + new NgWalker(sourceFile, this.getOptions(), { + templateVisitorCtrl: TemplateVisitor + }) + ); + } +} diff --git a/test/ionItemOptionsAttributeValuesRenamed.spec.ts b/test/ionItemOptionsAttributeValuesRenamed.spec.ts new file mode 100644 index 0000000..02cf879 --- /dev/null +++ b/test/ionItemOptionsAttributeValuesRenamed.spec.ts @@ -0,0 +1,199 @@ +import { expect } from 'chai'; +import { Replacement, Utils } from 'tslint'; +import { ruleName } from '../src/ionItemOptionsAttributeValuesRenamedRule'; +import { assertAnnotated, assertFailure, assertFailures, assertMultipleAnnotated, assertSuccess } from './testHelper'; + +describe(ruleName, () => { + describe('success', () => { + it('should work with proper style', () => { + let source = ` + @Component({ + template: \` + + + Item 1 + + + + + + + + \` + }) + class Bar{} + `; + assertSuccess(ruleName, source); + }); + }); + + describe('failure', () => { + it('should fail when side="left" is used', () => { + let source = ` + @Component({ + template: \` + + + Item 1 + + + ~~~~~~~~~~~ + + + + + + \` + }) + class Bar{} + `; + + assertAnnotated({ + ruleName, + message: 'The side="left" attribute/value of ion-item-options should be written as side="start".', + source + }); + }); + + it('should fail when side="right" is used', () => { + let source = ` + @Component({ + template: \` + + + Item 1 + + + ~~~~~~~~~~~~ + + + + + + \` + }) + class Bar{} + `; + + assertAnnotated({ + ruleName, + message: 'The side="right" attribute/value of ion-item-options should be written as side="end".', + source + }); + }); + }); + + describe('replacements', () => { + it('should replace side="left" with side="start"', () => { + let source = ` + @Component({ + template: \` + + + Item 1 + + + + + + + + \` + }) + class Bar {} + `; + + const fail = { + message: 'The side="left" attribute/value of ion-item-options should be written as side="start".', + startPosition: { + line: 7, + character: 32 + }, + endPosition: { + line: 7, + character: 43 + } + }; + + const failures = assertFailure(ruleName, source, fail); + const fixes = failures.map(f => f.getFix()); + const res = Replacement.applyAll(source, Utils.flatMap(fixes, Utils.arrayify)); + + let expected = ` + @Component({ + template: \` + + + Item 1 + + + + + + + + \` + }) + class Bar {} + `; + + expect(res).to.eq(expected); + }); + + it('should replace side="right" with side="end"', () => { + let source = ` + @Component({ + template: \` + + + Item 1 + + + + + + + + \` + }) + class Bar {} + `; + + const fail = { + message: 'The side="right" attribute/value of ion-item-options should be written as side="end".', + startPosition: { + line: 7, + character: 32 + }, + endPosition: { + line: 7, + character: 44 + } + }; + + const failures = assertFailure(ruleName, source, fail); + const fixes = failures.map(f => f.getFix()); + const res = Replacement.applyAll(source, Utils.flatMap(fixes, Utils.arrayify)); + + let expected = ` + @Component({ + template: \` + + + Item 1 + + + + + + + + \` + }) + class Bar {} + `; + + expect(res).to.eq(expected); + }); + }); +});