diff --git a/README.md b/README.md index 5ef4ccd..0f7ce58 100644 --- a/README.md +++ b/README.md @@ -356,11 +356,13 @@ We are looking for contributors to help build these rules out! See [`CONTRIBUTIN List Header - :white_large_square: + :white_check_mark: ion-list-header-ion-label-required - + + @dwieeb + diff --git a/src/ionItemIonLabelRequiredRule.ts b/src/ionItemIonLabelRequiredRule.ts index a7e70d2..c602e59 100644 --- a/src/ionItemIonLabelRequiredRule.ts +++ b/src/ionItemIonLabelRequiredRule.ts @@ -12,7 +12,7 @@ export class Rule extends Lint.Rules.AbstractRule { public static metadata: Lint.IRuleMetadata = { ruleName: ruleName, type: 'functionality', - description: 'The ion-item requires an ion-label component. It is no longer automatically added.', + description: 'The ion-item component requires an ion-label component. It is no longer automatically added.', options: null, optionsDescription: 'Not configurable.', typescriptOnly: false, diff --git a/src/ionListHeaderIonLabelRequiredRule.ts b/src/ionListHeaderIonLabelRequiredRule.ts new file mode 100644 index 0000000..70ec58a --- /dev/null +++ b/src/ionListHeaderIonLabelRequiredRule.ts @@ -0,0 +1,29 @@ +import { NgWalker } from 'codelyzer/angular/ngWalker'; +import * as Lint from 'tslint'; +import * as ts from 'typescript'; + +import { createIonLabelRequiredTemplateVisitorClass } from './helpers/ionLabelRequired'; + +export const ruleName = 'ion-list-header-ion-label-required'; + +const TemplateVisitor = createIonLabelRequiredTemplateVisitorClass('ion-list-header'); + +export class Rule extends Lint.Rules.AbstractRule { + public static metadata: Lint.IRuleMetadata = { + ruleName: ruleName, + type: 'functionality', + description: 'The ion-list-header component requires an ion-label component. It is no longer automatically added.', + 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/ionListHeaderIonLabelRequired.spec.ts b/test/ionListHeaderIonLabelRequired.spec.ts new file mode 100644 index 0000000..a69b52a --- /dev/null +++ b/test/ionListHeaderIonLabelRequired.spec.ts @@ -0,0 +1,80 @@ +import { ruleName } from '../src/ionListHeaderIonLabelRequiredRule'; +import { assertAnnotated, assertSuccess } from './testHelper'; + +describe(ruleName, () => { + describe('success', () => { + it('should work with ion-label child', () => { + let source = ` + @Component({ + template: \` + + + + + Dog + + \` + }) + class Bar{} + `; + assertSuccess(ruleName, source); + }); + + it('should work with single ion-label child', () => { + let source = ` + @Component({ + template: \` + + Dog + + \` + }) + class Bar{} + `; + assertSuccess(ruleName, source); + }); + }); + + describe('failure', () => { + it('should fail when ion-label missing', () => { + let source = ` + @Component({ + template: \` + + ~~~~~~~~~~~~~~~ + + + + Dog + + \` + }) + class Bar{} + `; + + assertAnnotated({ + ruleName, + message: 'The ion-list-header requires an ion-label component. It is no longer automatically added.', + source + }); + }); + + it('should fail with only text', () => { + let source = ` + @Component({ + template: \` + Dog + ~~~~~~~~~~~~~~~ + \` + }) + class Bar{} + `; + + assertAnnotated({ + ruleName, + message: 'The ion-list-header requires an ion-label component. It is no longer automatically added.', + source + }); + }); + }); +});