Skip to content

Commit

Permalink
feat(rules): add ion-back-button-not-added-by-default rule (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
imhoffd authored and cwoolum committed Jul 5, 2018
1 parent 7fbff99 commit 65a168f
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ We are looking for contributors to help build these rules out! See [`CONTRIBUTIN
"rules": {
"ion-action-sheet-method-create-parameters-renamed": true,
"ion-alert-method-create-parameters-renamed": true,
"ion-back-button-not-added-by-default": { "options": [true], "severity": "warning" },
"ion-button-attributes-renamed": true,
"ion-button-is-now-an-element": true,
"ion-chip-markup-has-changed": true,
Expand Down
60 changes: 60 additions & 0 deletions src/ionBackButtonNotAddedByDefaultRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as ast from '@angular/compiler';
import { NgWalker } from 'codelyzer/angular/ngWalker';
import { BasicTemplateAstVisitor } from 'codelyzer/angular/templates/basicTemplateAstVisitor';
import * as Lint from 'tslint';
import * as tsutils from 'tsutils';
import * as ts from 'typescript';

export const ruleName = 'ion-back-button-not-added-by-default';

function isElementAst(node: ast.TemplateAst): node is ast.ElementAst {
const n = node as ast.ElementAst;
return n && typeof n.children === 'object' && typeof n.name === 'string' && typeof n.attrs === 'object';
}

class TemplateVisitor extends BasicTemplateAstVisitor {
visitElement(element: ast.ElementAst, context: any): any {
if (element.name && element.name === 'ion-toolbar') {
let found = false;
const ionButtonsElement = element.children.find((e): e is ast.ElementAst => isElementAst(e) && e.name === 'ion-buttons');

if (ionButtonsElement) {
const ionBackButtonElement = ionButtonsElement.children.find(e => isElementAst(e) && e.name === 'ion-back-button');

if (ionBackButtonElement) {
found = true;
}
}

if (!found) {
const start = element.sourceSpan.start.offset;
const length = element.name.length;
const position = this.getSourcePosition(start) + length + 1;

this.addFailureAt(start + 1, length, 'The back button in an ion-toolbar is no longer automatically added.');
}
}

super.visitElement(element, context);
}
}

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
ruleName: ruleName,
type: 'functionality',
description: 'The ion-back-button is not added by default to an ion-toolbar.',
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
})
);
}
}
67 changes: 67 additions & 0 deletions test/ionBackButtonNotAddedByDefault.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ruleName } from '../src/ionBackButtonNotAddedByDefaultRule';
import { assertAnnotated, assertSuccess } from './testHelper';

describe(ruleName, () => {
describe('success', () => {
it('should work with proper style', () => {
let source = `
@Component({
template: \`
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Back Button Example</ion-title>
</ion-toolbar>
\`
})
class Bar{}
`;
assertSuccess(ruleName, source);
});
});

describe('failure', () => {
it('should fail when ion-buttons is missing', () => {
let source = `
@Component({
template: \`
<ion-toolbar>
~~~~~~~~~~~
<ion-title>Back Button Example</ion-title>
</ion-toolbar>
\`
})
class Bar{}
`;

assertAnnotated({
ruleName,
message: 'The back button in an ion-toolbar is no longer automatically added.',
source
});
});

it('should fail when ion-back-button is missing in ion-buttons', () => {
let source = `
@Component({
template: \`
<ion-toolbar>
~~~~~~~~~~~
<ion-buttons slot="start">
</ion-buttons>
<ion-title>Back Button Example</ion-title>
</ion-toolbar>
\`
})
class Bar{}
`;

assertAnnotated({
ruleName,
message: 'The back button in an ion-toolbar is no longer automatically added.',
source
});
});
});
});

0 comments on commit 65a168f

Please sign in to comment.