diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e1947c46b..9f1797a9cb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,42 @@ Change Log === + + v4.5.1 --- diff --git a/docs/_data/formatters.json b/docs/_data/formatters.json index 1617406a14b..dec1dacbcce 100644 --- a/docs/_data/formatters.json +++ b/docs/_data/formatters.json @@ -36,13 +36,13 @@ "formatterName": "pmd", "description": "Formats errors as through they were PMD output.", "descriptionDetails": "Imitates the XML output from PMD. All errors have a priority of 1.", - "sample": "\n\n \n \n \n", + "sample": "\n\n \n \n \n", "consumer": "machine" }, { "formatterName": "prose", "description": "The default formatter which outputs simple human-readable messages.", - "sample": "myFile.ts[1, 14]: Missing semicolon", + "sample": "ERROR: myFile.ts[1, 14]: Missing semicolon", "consumer": "human" }, { @@ -56,7 +56,7 @@ "formatterName": "verbose", "description": "The human-readable formatter which includes the rule name in messages.", "descriptionDetails": "The output is the same as the prose formatter with the rule name included", - "sample": "(semicolon) myFile.ts[1, 14]: Missing semicolon", + "sample": "ERROR: (semicolon) myFile.ts[1, 14]: Missing semicolon", "consumer": "human" }, { diff --git a/docs/formatters/pmd/index.html b/docs/formatters/pmd/index.html index 6ebb32c7f4c..1b7a53b8d21 100644 --- a/docs/formatters/pmd/index.html +++ b/docs/formatters/pmd/index.html @@ -6,7 +6,7 @@ - + consumer: machine diff --git a/docs/formatters/prose/index.html b/docs/formatters/prose/index.html index 054e16dd3dd..f01f4afd6f7 100644 --- a/docs/formatters/prose/index.html +++ b/docs/formatters/prose/index.html @@ -1,7 +1,7 @@ --- formatterName: prose description: The default formatter which outputs simple human-readable messages. -sample: 'myFile.ts[1, 14]: Missing semicolon' +sample: 'ERROR: myFile.ts[1, 14]: Missing semicolon' consumer: human layout: formatter title: 'Formatter: prose' diff --git a/docs/formatters/verbose/index.html b/docs/formatters/verbose/index.html index ad2fd1d4c29..5a7de802b60 100644 --- a/docs/formatters/verbose/index.html +++ b/docs/formatters/verbose/index.html @@ -2,7 +2,7 @@ formatterName: verbose description: The human-readable formatter which includes the rule name in messages. descriptionDetails: The output is the same as the prose formatter with the rule name included -sample: '(semicolon) myFile.ts[1, 14]: Missing semicolon' +sample: 'ERROR: (semicolon) myFile.ts[1, 14]: Missing semicolon' consumer: human layout: formatter title: 'Formatter: verbose' diff --git a/src/configuration.ts b/src/configuration.ts index 8e48c507e47..2039dea15f5 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -21,7 +21,7 @@ import * as path from "path"; import * as resolve from "resolve"; import { FatalError } from "./error"; -import {arrayify, objectify, stripComments} from "./utils"; +import { arrayify, objectify, stripComments } from "./utils"; export interface IConfigurationFile { extends?: string | string[]; @@ -39,11 +39,9 @@ export interface IConfigurationLoadResult { } export const CONFIG_FILENAME = "tslint.json"; -/* tslint:disable:object-literal-key-quotes */ export const DEFAULT_CONFIG = { - "extends": "tslint:recommended", + extends: "tslint:recommended", }; -/* tslint:enable:object-literal-key-quotes */ const BUILT_IN_CONFIG = /^tslint:(.*)$/; @@ -244,3 +242,30 @@ export function getRulesDirectories(directories?: string | string[], relativeTo? return rulesDirectories; } + +export function isRuleEnabled(ruleConfigValue: any): boolean { + if (typeof ruleConfigValue === "boolean") { + return ruleConfigValue; + } + + if (Array.isArray(ruleConfigValue) && ruleConfigValue.length > 0) { + return ruleConfigValue[0]; + } + + if (ruleConfigValue.severity !== "off" && ruleConfigValue.severity !== "none") { + return true; + } + + return false; +} + +export function getRuleSeverity(ruleConfigValue: any) { + if (ruleConfigValue.severity && + (ruleConfigValue.severity.toLowerCase() === "warn" || + ruleConfigValue.severity.toLowerCase() === "warning")) { + + return "warning"; + } + + return "error"; +} diff --git a/src/enableDisableRules.ts b/src/enableDisableRules.ts index 7f6cb0279e9..ac585208ec5 100644 --- a/src/enableDisableRules.ts +++ b/src/enableDisableRules.ts @@ -18,7 +18,7 @@ import * as utils from "tsutils"; import * as ts from "typescript"; -import {AbstractRule} from "./language/rule/abstractRule"; +import {isRuleEnabled} from "./configuration"; import {IEnableDisablePosition} from "./ruleLoader"; export class EnableDisableRulesWalker { @@ -30,7 +30,7 @@ export class EnableDisableRulesWalker { this.enabledRules = []; if (rules) { for (const rule of Object.keys(rules)) { - if (AbstractRule.isRuleEnabled(rules[rule])) { + if (isRuleEnabled(rules[rule])) { this.enabledRules.push(rule); this.enableDisableRuleMap[rule] = [{ isEnabled: true, diff --git a/src/formatters/checkstyleFormatter.ts b/src/formatters/checkstyleFormatter.ts index c38405391b6..5d8912ebc55 100644 --- a/src/formatters/checkstyleFormatter.ts +++ b/src/formatters/checkstyleFormatter.ts @@ -17,7 +17,7 @@ import {AbstractFormatter} from "../language/formatter/abstractFormatter"; import {IFormatterMetadata} from "../language/formatter/formatter"; -import {RuleFailure} from "../language/rule/rule"; +import { RuleFailure } from "../language/rule/rule"; import * as Utils from "../utils"; @@ -48,6 +48,7 @@ export class Formatter extends AbstractFormatter { }); let previousFilename: string | null = null; for (const failure of failuresSorted) { + const severity = failure.getRuleSeverity(); if (failure.getFileName() !== previousFilename) { if (previousFilename) { output += ""; @@ -57,7 +58,7 @@ export class Formatter extends AbstractFormatter { } output += "dotdot output += "source=\"failure.tslint." + this.escapeXml(failure.getRuleName()) + "\" />"; diff --git a/src/formatters/msbuildFormatter.ts b/src/formatters/msbuildFormatter.ts index 8af372d9602..0afb73216a8 100644 --- a/src/formatters/msbuildFormatter.ts +++ b/src/formatters/msbuildFormatter.ts @@ -17,7 +17,7 @@ import {AbstractFormatter} from "../language/formatter/abstractFormatter"; import {IFormatterMetadata} from "../language/formatter/formatter"; -import {RuleFailure} from "../language/rule/rule"; +import { RuleFailure } from "../language/rule/rule"; import {camelize, dedent} from "../utils"; @@ -42,8 +42,9 @@ export class Formatter extends AbstractFormatter { const lineAndCharacter = failure.getStartPosition().getLineAndCharacter(); const positionTuple = `(${lineAndCharacter.line + 1},${lineAndCharacter.character + 1})`; + const severity = failure.getRuleSeverity(); - return `${fileName}${positionTuple}: warning ${camelizedRule}: ${failureString}`; + return `${fileName}${positionTuple}: ${severity} ${camelizedRule}: ${failureString}`; }); return outputLines.join("\n") + "\n"; diff --git a/src/formatters/pmdFormatter.ts b/src/formatters/pmdFormatter.ts index 1caa3595502..fe475df7bca 100644 --- a/src/formatters/pmdFormatter.ts +++ b/src/formatters/pmdFormatter.ts @@ -17,7 +17,7 @@ import {AbstractFormatter} from "../language/formatter/abstractFormatter"; import {IFormatterMetadata} from "../language/formatter/formatter"; -import {RuleFailure} from "../language/rule/rule"; +import { RuleFailure } from "../language/rule/rule"; import * as Utils from "../utils"; @@ -30,7 +30,7 @@ export class Formatter extends AbstractFormatter { sample: Utils.dedent` - + `, consumer: "machine", @@ -49,11 +49,12 @@ export class Formatter extends AbstractFormatter { .replace(/"/g, """); const lineAndCharacter = failure.getStartPosition().getLineAndCharacter(); + const priority = failure.getRuleSeverity() === "warning" ? 4 : 3; output += " "; } diff --git a/src/formatters/proseFormatter.ts b/src/formatters/proseFormatter.ts index 997412c388f..bc750aca49a 100644 --- a/src/formatters/proseFormatter.ts +++ b/src/formatters/proseFormatter.ts @@ -24,14 +24,14 @@ export class Formatter extends AbstractFormatter { public static metadata: IFormatterMetadata = { formatterName: "prose", description: "The default formatter which outputs simple human-readable messages.", - sample: "myFile.ts[1, 14]: Missing semicolon", + sample: "ERROR: myFile.ts[1, 14]: Missing semicolon", consumer: "human", }; /* tslint:enable:object-literal-sort-keys */ public format(failures: RuleFailure[], fixes?: RuleFailure[]): string { if (failures.length === 0 && (!fixes || fixes.length === 0)) { - return ""; + return "\n"; } const fixLines: string[] = []; @@ -54,7 +54,7 @@ export class Formatter extends AbstractFormatter { const lineAndCharacter = failure.getStartPosition().getLineAndCharacter(); const positionTuple = `[${lineAndCharacter.line + 1}, ${lineAndCharacter.character + 1}]`; - return `${fileName}${positionTuple}: ${failureString}`; + return `${failure.getRuleSeverity().toUpperCase()}: ${fileName}${positionTuple}: ${failureString}`; }); return fixLines.concat(errorLines).join("\n") + "\n"; diff --git a/src/formatters/stylishFormatter.ts b/src/formatters/stylishFormatter.ts index 771fcb61cb6..36a240dd0f5 100644 --- a/src/formatters/stylishFormatter.ts +++ b/src/formatters/stylishFormatter.ts @@ -17,7 +17,7 @@ import {AbstractFormatter} from "../language/formatter/abstractFormatter"; import {IFormatterMetadata} from "../language/formatter/formatter"; -import {RuleFailure} from "../language/rule/rule"; +import { RuleFailure } from "../language/rule/rule"; import * as colors from "colors"; @@ -39,10 +39,20 @@ export class Formatter extends AbstractFormatter { /* tslint:enable:object-literal-sort-keys */ public format(failures: RuleFailure[]): string { - if (typeof failures[0] === "undefined") { - return "\n"; + const outputLines = this.mapToMessages(failures); + + // Removes initial blank line + if (outputLines[0] === "") { + outputLines.shift(); } + return outputLines.join("\n") + "\n"; + } + + private mapToMessages(failures: RuleFailure[]): string[] { + if (!failures) { + return []; + } const outputLines: string[] = []; const positionMaxSize = this.getPositionMaxSize(failures); const ruleMaxSize = this.getRuleMaxSize(failures); @@ -71,21 +81,20 @@ export class Formatter extends AbstractFormatter { const lineAndCharacter = failure.getStartPosition().getLineAndCharacter(); let positionTuple = `${lineAndCharacter.line + 1}:${lineAndCharacter.character + 1}`; - positionTuple = this.pad(positionTuple, positionMaxSize); - positionTuple = colors.red(positionTuple); + positionTuple = this.pad(positionTuple, positionMaxSize); - // Ouput + if (failure.getRuleSeverity() === "warning") { + positionTuple = colors.blue(failure.getRuleSeverity().toUpperCase() + ": " + positionTuple); + } else { + positionTuple = colors.red(failure.getRuleSeverity().toUpperCase() + ": " + positionTuple); + } + + // Output const output = `${positionTuple} ${ruleName} ${failureString}`; outputLines.push(output); } - - // Removes initial blank line - if (outputLines[0] === "") { - outputLines.shift(); - } - - return outputLines.join("\n") + "\n\n"; + return outputLines; } private pad(str: string, len: number): string { diff --git a/src/formatters/verboseFormatter.ts b/src/formatters/verboseFormatter.ts index fad23a9fad0..2ea696f64af 100644 --- a/src/formatters/verboseFormatter.ts +++ b/src/formatters/verboseFormatter.ts @@ -17,7 +17,7 @@ import {AbstractFormatter} from "../language/formatter/abstractFormatter"; import {IFormatterMetadata} from "../language/formatter/formatter"; -import {RuleFailure} from "../language/rule/rule"; +import { RuleFailure } from "../language/rule/rule"; export class Formatter extends AbstractFormatter { /* tslint:disable:object-literal-sort-keys */ @@ -25,13 +25,19 @@ export class Formatter extends AbstractFormatter { formatterName: "verbose", description: "The human-readable formatter which includes the rule name in messages.", descriptionDetails: "The output is the same as the prose formatter with the rule name included", - sample: "(semicolon) myFile.ts[1, 14]: Missing semicolon", + sample: "ERROR: (semicolon) myFile.ts[1, 14]: Missing semicolon", consumer: "human", }; /* tslint:enable:object-literal-sort-keys */ public format(failures: RuleFailure[]): string { - const outputLines = failures.map((failure: RuleFailure) => { + + return this.mapToMessages(failures) + .join("\n") + "\n"; + } + + private mapToMessages(failures: RuleFailure[]): string[] { + return failures.map((failure: RuleFailure) => { const fileName = failure.getFileName(); const failureString = failure.getFailure(); const ruleName = failure.getRuleName(); @@ -39,9 +45,8 @@ export class Formatter extends AbstractFormatter { const lineAndCharacter = failure.getStartPosition().getLineAndCharacter(); const positionTuple = "[" + (lineAndCharacter.line + 1) + ", " + (lineAndCharacter.character + 1) + "]"; - return `(${ruleName}) ${fileName}${positionTuple}: ${failureString}`; + return `${failure.getRuleSeverity().toUpperCase()}: (${ruleName}) ${fileName}${positionTuple}: ${failureString}`; }); - return outputLines.join("\n") + "\n"; } } diff --git a/src/formatters/vsoFormatter.ts b/src/formatters/vsoFormatter.ts index d7763f5b7b0..90949496be4 100644 --- a/src/formatters/vsoFormatter.ts +++ b/src/formatters/vsoFormatter.ts @@ -34,8 +34,10 @@ export class Formatter extends AbstractFormatter { }; /* tslint:enable:object-literal-sort-keys */ - public format(failures: RuleFailure[]): string { - const outputLines = failures.map((failure: RuleFailure) => { + public format(failures: RuleFailure[], warnings: RuleFailure[] = []): string { + const all = failures.concat(warnings); + + const outputLines = all.map((failure: RuleFailure) => { const fileName = failure.getFileName(); const failureString = failure.getFailure(); const lineAndCharacter = failure.getStartPosition().getLineAndCharacter(); diff --git a/src/index.ts b/src/index.ts index 835ac72fc15..df8cd5c934f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,7 +35,8 @@ export * from "./language/walker"; export * from "./language/formatter/formatter"; export interface LintResult { - failureCount: number; + errorCount: number; + warningCount: number; failures: RuleFailure[]; fixes?: RuleFailure[]; format: string | FormatterFunction; diff --git a/src/language/formatter/formatter.ts b/src/language/formatter/formatter.ts index 7801a9a510b..ff3cecbf2d2 100644 --- a/src/language/formatter/formatter.ts +++ b/src/language/formatter/formatter.ts @@ -49,8 +49,8 @@ export type ConsumerType = "human" | "machine"; export interface IFormatter { /** * Formats linter results - * @param {RuleFailure[]} failures Linter errors that were not fixed - * @param {RuleFailure[]} fixes Fixed linter errors. Available when the `--fix` argument is used on the command line + * @param {RuleFailure[]} failures Linter failures that were not fixed + * @param {RuleFailure[]} fixes Fixed linter failures. Available when the `--fix` argument is used on the command line */ format(failures: RuleFailure[], fixes?: RuleFailure[]): string; } diff --git a/src/language/rule/abstractRule.ts b/src/language/rule/abstractRule.ts index 3a051a6ecbd..33bff73978e 100644 --- a/src/language/rule/abstractRule.ts +++ b/src/language/rule/abstractRule.ts @@ -17,25 +17,16 @@ import * as ts from "typescript"; +import { getRuleSeverity, isRuleEnabled } from "../../configuration"; +import {arrayify} from "../../utils"; import {doesIntersect} from "../utils"; import {IWalker, WalkContext} from "../walker"; -import {IDisabledInterval, IOptions, IRule, IRuleMetadata, RuleFailure} from "./rule"; +import { IDisabledInterval, IOptions, IRule, IRuleMetadata, RuleFailure, RuleSeverity } from "./rule"; export abstract class AbstractRule implements IRule { public static metadata: IRuleMetadata; protected readonly ruleArguments: any[]; - - public static isRuleEnabled(ruleConfigValue: any): boolean { - if (typeof ruleConfigValue === "boolean") { - return ruleConfigValue; - } - - if (Array.isArray(ruleConfigValue) && ruleConfigValue.length > 0) { - return ruleConfigValue[0]; - } - - return false; - } + protected readonly ruleSeverity: RuleSeverity; constructor(public readonly ruleName: string, private value: any, private disabledIntervals: IDisabledInterval[]) { if (Array.isArray(value) && value.length > 1) { @@ -43,6 +34,12 @@ export abstract class AbstractRule implements IRule { } else { this.ruleArguments = []; } + + if (value.options) { + this.ruleArguments = arrayify(value.options); + } + + this.ruleSeverity = getRuleSeverity(value); } public getOptions(): IOptions { @@ -50,6 +47,7 @@ export abstract class AbstractRule implements IRule { disabledIntervals: this.disabledIntervals, ruleArguments: this.ruleArguments, ruleName: this.ruleName, + ruleSeverity: this.ruleSeverity, }; } @@ -61,7 +59,7 @@ export abstract class AbstractRule implements IRule { } public isEnabled(): boolean { - return AbstractRule.isRuleEnabled(this.value); + return isRuleEnabled(this.value); } protected applyWithFunction(sourceFile: ts.SourceFile, walkFn: (ctx: WalkContext) => void): RuleFailure[]; diff --git a/src/language/rule/rule.ts b/src/language/rule/rule.ts index 26ec45b85a3..f9b2637baf3 100644 --- a/src/language/rule/rule.ts +++ b/src/language/rule/rule.ts @@ -86,8 +86,12 @@ export interface IRuleMetadata { export type RuleType = "functionality" | "maintainability" | "style" | "typescript"; +export type RuleSeverity = "warning" | "error"; + export interface IOptions { ruleArguments: any[]; + ruleSeverity: RuleSeverity; + ruleName: string; disabledIntervals: IDisabledInterval[]; } @@ -109,6 +113,7 @@ export interface IRuleFailureJson { failure: string; fix?: Fix; name: string; + ruleSeverity: string; ruleName: string; startPosition: IRuleFailurePositionJson; } @@ -227,6 +232,7 @@ export class RuleFailure { private startPosition: RuleFailurePosition; private endPosition: RuleFailurePosition; private rawLines: string; + private ruleSeverity: RuleSeverity; constructor(private sourceFile: ts.SourceFile, start: number, @@ -239,6 +245,7 @@ export class RuleFailure { this.startPosition = this.createFailurePosition(start); this.endPosition = this.createFailurePosition(end); this.rawLines = sourceFile.text; + this.ruleSeverity = "warning"; } public getFileName() { @@ -273,6 +280,14 @@ export class RuleFailure { return this.rawLines; } + public getRuleSeverity() { + return this.ruleSeverity; + } + + public setRuleSeverity(value: RuleSeverity) { + this.ruleSeverity = value; + } + public toJson(): IRuleFailureJson { return { endPosition: this.endPosition.toJson(), @@ -280,6 +295,7 @@ export class RuleFailure { fix: this.fix, name: this.fileName, ruleName: this.ruleName, + ruleSeverity: this.ruleSeverity.toUpperCase(), startPosition: this.startPosition.toJson(), }; } diff --git a/src/language/walker/walkContext.ts b/src/language/walker/walkContext.ts index 3af20a74c4b..f93b480c192 100644 --- a/src/language/walker/walkContext.ts +++ b/src/language/walker/walkContext.ts @@ -17,7 +17,7 @@ import * as ts from "typescript"; -import {Fix, Replacement, RuleFailure} from "../rule/rule"; +import { Fix, Replacement, RuleFailure } from "../rule/rule"; export class WalkContext { public readonly failures: RuleFailure[] = []; diff --git a/src/linter.ts b/src/linter.ts index 4e947e8f734..dc725a833ce 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -34,7 +34,7 @@ import { findFormatter } from "./formatterLoader"; import { ILinterOptions, LintResult } from "./index"; import { IFormatter } from "./language/formatter/formatter"; import { createLanguageService, wrapProgram } from "./language/languageServiceHost"; -import { Fix, IRule, RuleFailure } from "./language/rule/rule"; +import { Fix, IRule, RuleFailure, RuleSeverity } from "./language/rule/rule"; import { TypedRule } from "./language/rule/typedRule"; import * as utils from "./language/utils"; import { loadRules } from "./ruleLoader"; @@ -136,6 +136,18 @@ class Linter { } } this.failures = this.failures.concat(fileFailures); + + // add rule severity to failures + const ruleSeverityMap = new Map(enabledRules.map((rule) => { + return [rule.getOptions().ruleName, rule.getOptions().ruleSeverity] as [string, RuleSeverity]; + })); + for (const failure of this.failures) { + const severity = ruleSeverityMap.get(failure.getRuleName()); + if (severity === undefined) { + throw new Error(`Severity for rule '${failure.getRuleName()} not found`); + } + failure.setRuleSeverity(severity); + } } public getResult(): LintResult { @@ -152,12 +164,14 @@ class Linter { const output = formatter.format(this.failures, this.fixes); + const errorCount = this.failures.filter((failure) => failure.getRuleSeverity() === "error").length; return { - failureCount: this.failures.length, + errorCount, failures: this.failures, fixes: this.fixes, format: formatterName, output, + warningCount: this.failures.length - errorCount, }; } diff --git a/src/ruleLoader.ts b/src/ruleLoader.ts index f95f728a499..4ace0397ab8 100644 --- a/src/ruleLoader.ts +++ b/src/ruleLoader.ts @@ -18,7 +18,7 @@ import * as fs from "fs"; import * as path from "path"; -import { getRelativePath } from "./configuration"; +import { getRelativePath, isRuleEnabled } from "./configuration"; import { showWarningOnce } from "./error"; import { AbstractRule } from "./language/rule/abstractRule"; import { IDisabledInterval, IRule } from "./language/rule/rule"; @@ -44,7 +44,7 @@ export function loadRules(ruleConfiguration: {[name: string]: any}, for (const ruleName in ruleConfiguration) { if (ruleConfiguration.hasOwnProperty(ruleName)) { const ruleValue = ruleConfiguration[ruleName]; - if (AbstractRule.isRuleEnabled(ruleValue) || enableDisableRuleMap.hasOwnProperty(ruleName)) { + if (isRuleEnabled(ruleValue) || enableDisableRuleMap.hasOwnProperty(ruleName)) { const Rule: (typeof AbstractRule) | null = findRule(ruleName, rulesDirectories); if (Rule == null) { notFoundRules.push(ruleName); diff --git a/src/rules/eoflineRule.ts b/src/rules/eoflineRule.ts index 2442b943b0e..9331c9ae4a1 100644 --- a/src/rules/eoflineRule.ts +++ b/src/rules/eoflineRule.ts @@ -42,7 +42,8 @@ export class Rule extends Lint.Rules.AbstractRule { } return this.filterFailures([ - new Lint.RuleFailure(sourceFile, length, length, Rule.FAILURE_STRING, this.getOptions().ruleName), + new Lint.RuleFailure(sourceFile, length, length, Rule.FAILURE_STRING, + this.getOptions().ruleName), ]); } } diff --git a/src/rules/maxFileLineCountRule.ts b/src/rules/maxFileLineCountRule.ts index 2b3f8905354..0ae00b1abfe 100644 --- a/src/rules/maxFileLineCountRule.ts +++ b/src/rules/maxFileLineCountRule.ts @@ -24,7 +24,7 @@ export class Rule extends Lint.Rules.AbstractRule { ruleName: "max-file-line-count", description: "Requires files to remain under a certain number of lines", rationale: Lint.Utils.dedent` - Limiting the number of lines allowed in a file allows files to remain small, + Limiting the number of lines allowed in a file allows files to remain small, single purpose, and maintainable.`, optionsDescription: "An integer indicating the maximum number of lines.", options: { @@ -63,7 +63,8 @@ export class Rule extends Lint.Rules.AbstractRule { if (lineCount > lineLimit && disabledIntervals.length === 0) { const errorString = Rule.FAILURE_STRING_FACTORY(lineCount, lineLimit); - ruleFailures.push(new Lint.RuleFailure(sourceFile, 0, 1, errorString, this.getOptions().ruleName)); + ruleFailures.push(new Lint.RuleFailure(sourceFile, 0, 1, errorString, + this.getOptions().ruleName)); } return ruleFailures; } diff --git a/src/rules/noImportSideEffectRule.ts b/src/rules/noImportSideEffectRule.ts index b5e628325dc..899d77ae790 100644 --- a/src/rules/noImportSideEffectRule.ts +++ b/src/rules/noImportSideEffectRule.ts @@ -17,7 +17,7 @@ import * as ts from "typescript"; -import * as Lint from "tslint"; +import * as Lint from "../index"; const OPTION_IGNORE_MODULE = "ignore-module"; diff --git a/src/runner.ts b/src/runner.ts index af8c1c24a58..deaba9a0c33 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -251,10 +251,10 @@ export class Runner { const lintResult = linter.getResult(); this.outputStream.write(lintResult.output, () => { - if (lintResult.failureCount > 0) { - onComplete(this.options.force ? 0 : 2); - } else { + if (this.options.force || lintResult.errorCount === 0) { onComplete(0); + } else { + onComplete(2); } }); diff --git a/test/config/tslint-custom-rules-with-dir.json b/test/config/tslint-custom-rules-with-dir.json index 8c4ae31b1e5..6467b260767 100644 --- a/test/config/tslint-custom-rules-with-dir.json +++ b/test/config/tslint-custom-rules-with-dir.json @@ -1,9 +1,13 @@ { "rulesDirectory": "../files/custom-rules/", "jsRules": { - "always-fail": true + "always-fail": { + "severity": "error" + } }, "rules": { - "always-fail": true + "always-fail": { + "severity": "error" + } } } diff --git a/test/config/tslint-custom-rules-with-two-dirs.json b/test/config/tslint-custom-rules-with-two-dirs.json index a43cf3db773..76f2508e997 100644 --- a/test/config/tslint-custom-rules-with-two-dirs.json +++ b/test/config/tslint-custom-rules-with-two-dirs.json @@ -1,13 +1,25 @@ { "rulesDirectory": ["../files/custom-rules-2", "../files/custom-rules/"], "jsRules": { - "always-fail": true, - "no-fail": true, - "rule-two": true + "always-fail": { + "severity": "error" + }, + "no-fail": { + "severity": "error" + }, + "rule-two": { + "severity": "error" + } }, "rules": { - "always-fail": true, - "no-fail": true, - "rule-two": true + "always-fail": { + "severity": "error" + }, + "no-fail": { + "severity": "error" + }, + "rule-two": { + "severity": "error" + } } } diff --git a/test/config/tslint-custom-rules.json b/test/config/tslint-custom-rules.json index 250eafcd974..ce5d5273980 100644 --- a/test/config/tslint-custom-rules.json +++ b/test/config/tslint-custom-rules.json @@ -1,8 +1,12 @@ { "jsRules": { - "always-fail": true + "always-fail": { + "severity": "error" + } }, "rules": { - "always-fail": true + "always-fail": { + "severity": "error" + } } } diff --git a/test/config/tslint-extends-builtin.json b/test/config/tslint-extends-builtin.json index 4daad33a5c8..690e68c4ebb 100644 --- a/test/config/tslint-extends-builtin.json +++ b/test/config/tslint-extends-builtin.json @@ -1,9 +1,13 @@ { "extends": "tslint:latest", "jsRules": { - "no-eval": false + "no-eval": { + "severity": "none" + } }, "rules": { - "no-eval": false + "no-eval": { + "severity": "none" + } } } diff --git a/test/config/tslint-extends-package-array.json b/test/config/tslint-extends-package-array.json index e52fb098a1d..31b053a7a79 100644 --- a/test/config/tslint-extends-package-array.json +++ b/test/config/tslint-extends-package-array.json @@ -4,9 +4,13 @@ "./tslint-custom-rules-with-two-dirs.json" ], "jsRules": { - "always-fail": false + "always-fail": { + "severity": "none" + } }, "rules": { - "always-fail": false + "always-fail": { + "severity": "none" + } } } diff --git a/test/config/tslint-extends-package-boolean.json b/test/config/tslint-extends-package-boolean.json new file mode 100644 index 00000000000..ed5eced16c4 --- /dev/null +++ b/test/config/tslint-extends-package-boolean.json @@ -0,0 +1,11 @@ +{ + "extends": "tslint-test-custom-rules", + "jsRules": { + "rule-two": true, + "rule-three": false + }, + "rules": { + "rule-two": true, + "rule-three": false + } +} diff --git a/test/config/tslint-extends-package-two-levels.json b/test/config/tslint-extends-package-two-levels.json index 2e2a12b63ac..2afc4f1cd0a 100644 --- a/test/config/tslint-extends-package-two-levels.json +++ b/test/config/tslint-extends-package-two-levels.json @@ -2,9 +2,13 @@ "extends": "tslint-test-config/tslint.json", "rulesDirectory": "../files/custom-rules", "jsRules": { - "always-fail": false + "always-fail": { + "severity": "none" + } }, "rules": { - "always-fail": false + "always-fail": { + "severity": "none" + } } } diff --git a/test/config/tslint-extends-package.json b/test/config/tslint-extends-package.json index ed5eced16c4..8be210769c4 100644 --- a/test/config/tslint-extends-package.json +++ b/test/config/tslint-extends-package.json @@ -1,11 +1,19 @@ { "extends": "tslint-test-custom-rules", "jsRules": { - "rule-two": true, - "rule-three": false + "rule-two": { + "severity": "error" + }, + "rule-three": { + "severity": "none" + } }, "rules": { - "rule-two": true, - "rule-three": false + "rule-two": { + "severity": "error" + }, + "rule-three": { + "severity": "none" + } } } diff --git a/test/config/tslint-extends-relative.json b/test/config/tslint-extends-relative.json index c53505e38af..d9cecc98f78 100644 --- a/test/config/tslint-extends-relative.json +++ b/test/config/tslint-extends-relative.json @@ -1,9 +1,13 @@ { "extends": "./tslint-custom-rules-with-two-dirs.json", "jsRules": { - "always-fail": false + "always-fail": { + "severity": "none" + } }, "rules": { - "always-fail": false + "always-fail": { + "severity": "none" + } } } diff --git a/test/config/tslint-with-comments.json b/test/config/tslint-with-comments.json index ef12ab77da1..157a5f63515 100644 --- a/test/config/tslint-with-comments.json +++ b/test/config/tslint-with-comments.json @@ -4,7 +4,9 @@ /* "rule-one": true, */ - "rule-two": true, + "rule-two": { + "severity": "error" // after comment + }, "rule-three": "//not a comment", "rule-four": "/*also not a comment*/" }, @@ -13,7 +15,10 @@ /* "rule-one": true, */ - "rule-two": true, + "rule-two": { + "severity": "error" + // after comment + }, "rule-three": "//not a comment", "rule-four": "/*also not a comment*/" } diff --git a/test/config/tslint-with-jsrules.json b/test/config/tslint-with-jsrules.json index dc4f5463b1c..5ce2f002bfd 100644 --- a/test/config/tslint-with-jsrules.json +++ b/test/config/tslint-with-jsrules.json @@ -1,5 +1,7 @@ { "jsRules": { - "rule": true + "rule": { + "severity": "error" + } } } \ No newline at end of file diff --git a/test/configurationTests.ts b/test/configurationTests.ts index 819bf5871c2..e2c716e16cf 100644 --- a/test/configurationTests.ts +++ b/test/configurationTests.ts @@ -81,15 +81,41 @@ describe("Configuration", () => { const config = loadConfigurationFromPath("./test/config/tslint-extends-relative.json"); assert.isArray(config.rulesDirectory); - assert.isTrue(config.rules["no-fail"], "did not pick up 'no-fail' in base config"); - assert.isFalse(config.rules["always-fail"], "did not set 'always-fail' in top config"); - assert.isTrue(config.jsRules["no-fail"]); - assert.isFalse(config.jsRules["always-fail"]); + assert.equal("error", config.rules["no-fail"].severity, "did not pick up 'no-fail' in base config"); + assert.equal("none", config.rules["always-fail"].severity, "did not set 'always-fail' in top config"); + assert.equal("error", config.jsRules["no-fail"].severity); + assert.equal("none", config.jsRules["always-fail"].severity); }); it("extends with package", () => { const config = loadConfigurationFromPath("./test/config/tslint-extends-package.json"); + assert.isArray(config.rulesDirectory); + /* tslint:disable:object-literal-sort-keys */ + assert.deepEqual(config.jsRules, { + "rule-one": true, + "rule-three": { + severity: "none", + }, + "rule-two": { + severity: "error", + }, + }); + assert.deepEqual(config.rules, { + "rule-one": true, + "rule-three": { + severity: "none", + }, + "rule-two": { + severity: "error", + }, + }); + /* tslint:enable:object-literal-sort-keys */ + }); + + it("extends with package - boolean configuration", () => { + const config = loadConfigurationFromPath("./test/config/tslint-extends-package-boolean.json"); + assert.isArray(config.rulesDirectory); /* tslint:disable:object-literal-sort-keys */ assert.deepEqual(config.jsRules, { @@ -122,9 +148,9 @@ describe("Configuration", () => { it("extends with builtin", () => { const config = loadConfigurationFromPath("./test/config/tslint-extends-builtin.json"); assert.isUndefined(config.jsRules["no-var-keyword"]); - assert.isFalse(config.jsRules["no-eval"]); + assert.equal("none", config.jsRules["no-eval"].severity); assert.isTrue(config.rules["no-var-keyword"]); - assert.isFalse(config.rules["no-eval"]); + assert.equal("none", config.rules["no-eval"].severity); }); describe("with config not relative to tslint", () => { @@ -158,13 +184,17 @@ describe("Configuration", () => { assert.isTrue(fs.existsSync(config.rulesDirectory![1])); /* tslint:disable:object-literal-sort-keys */ assert.deepEqual(config.jsRules, { - "always-fail": false, + "always-fail": { + severity: "none", + }, "rule-one": true, "rule-two": true, "rule-four": true, }); assert.deepEqual(config.rules, { - "always-fail": false, + "always-fail": { + severity: "none", + }, "rule-one": true, "rule-two": true, "rule-four": true, @@ -177,16 +207,28 @@ describe("Configuration", () => { assert.isArray(config.rulesDirectory); assert.deepEqual(config.jsRules, { - "always-fail": false, - "no-fail": true, + "always-fail": { + severity: "none", + }, + "no-fail": { + severity: "error", + }, "rule-one": true, - "rule-two": true, + "rule-two": { + severity: "error", + }, }); assert.deepEqual(config.rules, { - "always-fail": false, - "no-fail": true, + "always-fail": { + severity: "none", + }, + "no-fail": { + severity: "error", + }, "rule-one": true, - "rule-two": true, + "rule-two": { + severity: "error", + }, }); }); @@ -195,12 +237,16 @@ describe("Configuration", () => { /* tslint:disable:object-literal-sort-keys */ assert.deepEqual(config.jsRules, { - "rule-two": true, + "rule-two": { + severity: "error", + }, "rule-three": "//not a comment", "rule-four": "/*also not a comment*/", }); assert.deepEqual(config.rules, { - "rule-two": true, + "rule-two": { + severity: "error", + }, "rule-three": "//not a comment", "rule-four": "/*also not a comment*/", }); diff --git a/test/files/custom-rules/alwaysFailRule.js b/test/files/custom-rules/alwaysFailRule.js index b6d3d25921c..4f1b0bf7f86 100644 --- a/test/files/custom-rules/alwaysFailRule.js +++ b/test/files/custom-rules/alwaysFailRule.js @@ -3,7 +3,7 @@ var __extends = (this && this.__extends) || function (d, b) { function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; -var Lint = require("tslint"); +var Lint = require('../../../lib/index'); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { diff --git a/test/formatters/checkstyleFormatterTests.ts b/test/formatters/checkstyleFormatterTests.ts index d59749a19a9..be3a9a9cd52 100644 --- a/test/formatters/checkstyleFormatterTests.ts +++ b/test/formatters/checkstyleFormatterTests.ts @@ -17,7 +17,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("Checkstyle Formatter", () => { const TEST_FILE_1 = "formatters/jsonFormatter.test.ts"; // reuse existing sample file @@ -38,23 +39,23 @@ describe("Checkstyle Formatter", () => { const maxPosition2 = sourceFile2.getFullWidth(); const failures = [ - new RuleFailure(sourceFile1, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile1, 2, 3, "&<>'\" should be escaped", "escape"), - new RuleFailure(sourceFile1, maxPosition1 - 1, maxPosition1, "last failure", "last-name"), - new RuleFailure(sourceFile2, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile2, 2, 3, "&<>'\" should be escaped", "escape"), - new RuleFailure(sourceFile2, maxPosition2 - 1, maxPosition2, "last failure", "last-name"), + createFailure(sourceFile1, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile1, 2, 3, "&<>'\" should be escaped", "escape", undefined, "error"), + createFailure(sourceFile1, maxPosition1 - 1, maxPosition1, "last failure", "last-name", undefined, "error"), + createFailure(sourceFile2, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile2, 2, 3, "&<>'\" should be escaped", "escape", undefined, "warning"), + createFailure(sourceFile2, maxPosition2 - 1, maxPosition2, "last failure", "last-name", undefined, "warning"), ]; const expectedResult = '' + `` + - '' + - '' + + '' + - '' + + '' + "" + `` + - '' + + '' + '' + '' + diff --git a/test/formatters/codeFrameFormatterTests.ts b/test/formatters/codeFrameFormatterTests.ts index fb2e6820244..38f43db9d64 100644 --- a/test/formatters/codeFrameFormatterTests.ts +++ b/test/formatters/codeFrameFormatterTests.ts @@ -18,7 +18,8 @@ import * as colors from "colors"; import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import {IFormatter, TestUtils} from "../lint"; +import { createFailure } from "./utils"; describe("CodeFrame Formatter", () => { const TEST_FILE = "formatters/codeFrameFormatter.test.ts"; @@ -36,10 +37,10 @@ describe("CodeFrame Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 2, 3, "&<>'\" should be escaped", "escape"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), - new RuleFailure(sourceFile, 0, maxPosition, "full failure", "full-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 2, 3, "&<>'\" should be escaped", "escape", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "error"), + createFailure(sourceFile, 0, maxPosition, "full failure", "full-name", undefined, "error"), ]; const expectedResultPlain = diff --git a/test/formatters/externalFormatterTests.ts b/test/formatters/externalFormatterTests.ts index 12733cf73e5..e99b5ff8292 100644 --- a/test/formatters/externalFormatterTests.ts +++ b/test/formatters/externalFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("External Formatter", () => { const TEST_FILE = "formatters/externalFormatter.test.ts"; @@ -33,9 +34,9 @@ describe("External Formatter", () => { it("formats failures", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 32, 36, "mid failure", "mid-name"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 32, 36, "mid failure", "mid-name", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "error"), ]; const expectedResult = diff --git a/test/formatters/fileslistFormatterTests.ts b/test/formatters/fileslistFormatterTests.ts index e77e444afe6..b2ec3304bda 100644 --- a/test/formatters/fileslistFormatterTests.ts +++ b/test/formatters/fileslistFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("Files-list Formatter", () => { const TEST_FILE = "formatters/fileslistFormatter.test.ts"; @@ -32,8 +33,8 @@ describe("Files-list Formatter", () => { it("formats failures", () => { // this part really doesn't matter, as long as we get some failure` const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 32, 36, "last failure", "last-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 32, 36, "last failure", "last-name", undefined, "error"), ]; // we only print file-names in this formatter diff --git a/test/formatters/jsonFormatterTests.ts b/test/formatters/jsonFormatterTests.ts index 1986e61572e..d485e22ec01 100644 --- a/test/formatters/jsonFormatterTests.ts +++ b/test/formatters/jsonFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {Fix, IFormatter, Replacement, RuleFailure, TestUtils} from "../lint"; +import { Fix, IFormatter, Replacement, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("JSON Formatter", () => { const TEST_FILE = "formatters/jsonFormatter.test.ts"; @@ -33,12 +34,13 @@ describe("JSON Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), - new RuleFailure(sourceFile, 0, maxPosition, "full failure", "full-name", + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "error"), + createFailure(sourceFile, 0, maxPosition, "full failure", "full-name", new Fix("full-name", [ new Replacement(0, 0, ""), - ])), + ]), + "error"), ]; /* tslint:disable:object-literal-sort-keys */ @@ -56,6 +58,7 @@ describe("JSON Formatter", () => { character: 1, }, ruleName: "first-name", + ruleSeverity: "ERROR", }, { name: TEST_FILE, @@ -71,6 +74,7 @@ describe("JSON Formatter", () => { character: 0, }, ruleName: "last-name", + ruleSeverity: "ERROR", }, { name: TEST_FILE, @@ -96,6 +100,7 @@ describe("JSON Formatter", () => { character: 0, }, ruleName: "full-name", + ruleSeverity: "ERROR", }]; /* tslint:enable:object-literal-sort-keys */ diff --git a/test/formatters/msbuildFormatterTests.ts b/test/formatters/msbuildFormatterTests.ts index ef2fd8f4344..8de8d2aff3c 100644 --- a/test/formatters/msbuildFormatterTests.ts +++ b/test/formatters/msbuildFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("MSBuild Formatter", () => { const TEST_FILE = "formatters/msbuildFormatter.test.ts"; @@ -33,15 +34,15 @@ describe("MSBuild Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 32, 36, "mid failure", "mid-name"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 32, 36, "mid failure", "mid-name", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "warning"), ]; const expectedResult = - getFailureString(TEST_FILE, 1, 1, "first failure", "firstName") + - getFailureString(TEST_FILE, 2, 12, "mid failure", "midName") + - getFailureString(TEST_FILE, 9, 2, "last failure", "lastName"); + getFailureString(TEST_FILE, 1, 1, "first failure", "firstName", "error") + + getFailureString(TEST_FILE, 2, 12, "mid failure", "midName", "error") + + getFailureString(TEST_FILE, 9, 2, "last failure", "lastName", "warning"); const actualResult = formatter.format(failures); assert.equal(actualResult, expectedResult); @@ -52,7 +53,7 @@ describe("MSBuild Formatter", () => { assert.equal(result, "\n"); }); - function getFailureString(file: string, line: number, character: number, reason: string, ruleCamelCase: string) { - return `${file}(${line},${character}): warning ${ruleCamelCase}: ${reason}\n`; + function getFailureString(file: string, line: number, character: number, reason: string, ruleCamelCase: string, severity: string) { + return `${file}(${line},${character}): ${severity} ${ruleCamelCase}: ${reason}\n`; } }); diff --git a/test/formatters/pmdFormatterTests.ts b/test/formatters/pmdFormatterTests.ts index 2e95f243b39..eeae2d209ad 100644 --- a/test/formatters/pmdFormatterTests.ts +++ b/test/formatters/pmdFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("PMD Formatter", () => { const TEST_FILE = "formatters/pmdFormatter.test.ts"; @@ -33,27 +34,27 @@ describe("PMD Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 2, 3, "&<>'\" should be escaped", "escape"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), - new RuleFailure(sourceFile, 0, maxPosition, "full failure", "full-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 2, 3, "&<>'\" should be escaped", "escape", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "warning"), + createFailure(sourceFile, 0, maxPosition, "full failure", "full-name", undefined, "warning"), ]; const expectedResult = "" + "" + - " " + + " " + "" + "" + "" + - " " + + " " + "" + "" + "" + - " " + + " " + "" + "" + "" + - " " + + " " + "" + "" + ""; diff --git a/test/formatters/proseFormatterTests.ts b/test/formatters/proseFormatterTests.ts index b83e550a6f1..fb5569b4c20 100644 --- a/test/formatters/proseFormatterTests.ts +++ b/test/formatters/proseFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("Prose Formatter", () => { const TEST_FILE = "formatters/proseFormatter.test.ts"; @@ -33,15 +34,15 @@ describe("Prose Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 32, 36, "mid failure", "mid-name"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 32, 36, "mid failure", "mid-name", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "warning"), ]; const expectedResult = - TEST_FILE + getPositionString(1, 1) + "first failure\n" + - TEST_FILE + getPositionString(2, 12) + "mid failure\n" + - TEST_FILE + getPositionString(9, 2) + "last failure\n"; + "ERROR: " + TEST_FILE + getPositionString(1, 1) + "first failure\n" + + "ERROR: " + TEST_FILE + getPositionString(2, 12) + "mid failure\n" + + "WARNING: " + TEST_FILE + getPositionString(9, 2) + "last failure\n"; const actualResult = formatter.format(failures); assert.equal(actualResult, expectedResult); @@ -49,21 +50,21 @@ describe("Prose Formatter", () => { it("formats fixes", () => { const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), ]; const mockFix = { getFileName: () => "file2" } as any; const fixes = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 32, 36, "mid failure", "mid-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 32, 36, "mid failure", "mid-name", undefined, "error"), mockFix, ]; const expectedResult = `Fixed 2 error(s) in ${TEST_FILE}\n` + `Fixed 1 error(s) in file2\n\n` + - `${TEST_FILE}${getPositionString(1, 1)}first failure\n`; + `ERROR: ${TEST_FILE}${getPositionString(1, 1)}first failure\n`; const actualResult = formatter.format(failures, fixes); assert.equal(actualResult, expectedResult); @@ -71,7 +72,7 @@ describe("Prose Formatter", () => { it("handles no failures", () => { const result = formatter.format([]); - assert.equal(result, ""); + assert.equal(result, "\n"); }); function getPositionString(line: number, character: number) { diff --git a/test/formatters/stylishFormatterTests.ts b/test/formatters/stylishFormatterTests.ts index a694ccf3e25..e20c777b749 100644 --- a/test/formatters/stylishFormatterTests.ts +++ b/test/formatters/stylishFormatterTests.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import * as colors from "colors"; - import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("Stylish Formatter", () => { const TEST_FILE = "formatters/stylishFormatter.test.ts"; @@ -35,29 +34,21 @@ describe("Stylish Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 2, 3, "&<>'\" should be escaped", "escape"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), - new RuleFailure(sourceFile, 0, maxPosition, "full failure", "full-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 2, 3, "&<>'\" should be escaped", "escape", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "error"), + createFailure(sourceFile, 0, maxPosition, "full failure", "full-name", undefined, "error"), ]; const maxPositionObj = sourceFile.getLineAndCharacterOfPosition(maxPosition - 1); const maxPositionTuple = `${maxPositionObj.line + 1}:${maxPositionObj.character + 1}`; - const expectedResult = colors.enabled ? - "formatters/stylishFormatter.test.ts" + "\n" + - "\u001b[31m1:1\u001b[39m \u001b[90mfirst-name\u001b[39m \u001b[33mfirst failure\u001b[39m" + "\n" + - "\u001b[31m1:3\u001b[39m \u001b[90mescape \u001b[39m \u001b[33m&<>'\" should be escaped\u001b[39m" + "\n" + - `\u001b[31m${maxPositionTuple}\u001b[39m \u001b[90mlast-name \u001b[39m \u001b[33mlast failure\u001b[39m` + "\n" + - "\u001b[31m1:1\u001b[39m \u001b[90mfull-name \u001b[39m \u001b[33mfull failure\u001b[39m" + "\n" + - "\n" : - "formatters/stylishFormatter.test.ts" + "\n" + - "1:1 first-name first failure" + "\n" + - "1:3 escape &<>\'\" should be escaped" + "\n" + - `${maxPositionTuple} last-name last failure` + "\n" + - "1:1 full-name full failure" + "\n" + - "\n"; + const expectedResult = "formatters/stylishFormatter.test.ts" + "\n" + + "\u001b[31mERROR: 1:1\u001b[39m \u001b[90mfirst-name\u001b[39m \u001b[33mfirst failure\u001b[39m" + "\n" + + "\u001b[31mERROR: 1:3\u001b[39m \u001b[90mescape \u001b[39m \u001b[33m&<>'\" should be escaped\u001b[39m" + "\n" + + `\u001b[31mERROR: ${maxPositionTuple}\u001b[39m \u001b[90mlast-name \u001b[39m \u001b[33mlast failure\u001b[39m` + "\n" + + "\u001b[31mERROR: 1:1\u001b[39m \u001b[90mfull-name \u001b[39m \u001b[33mfull failure\u001b[39m" + "\n"; assert.equal(formatter.format(failures), expectedResult); }); diff --git a/test/formatters/utils.ts b/test/formatters/utils.ts new file mode 100644 index 00000000000..afa9f4d1c15 --- /dev/null +++ b/test/formatters/utils.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2017 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as ts from "typescript"; +import { Fix, RuleFailure, RuleSeverity } from "../../src/language/rule/rule"; + +export function createFailure(sourceFile: ts.SourceFile, + start: number, + end: number, + failure: string, + ruleName: string, + fix?: Fix, + ruleSeverity: RuleSeverity = "warning") { + + const rule = new RuleFailure(sourceFile, start, end, failure, ruleName, fix); + rule.setRuleSeverity(ruleSeverity); + return rule; +} diff --git a/test/formatters/verboseFormatterTests.ts b/test/formatters/verboseFormatterTests.ts index 2cb494fa845..dab9237d705 100644 --- a/test/formatters/verboseFormatterTests.ts +++ b/test/formatters/verboseFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("Verbose Formatter", () => { const TEST_FILE = "formatters/proseFormatter.test.ts"; @@ -33,15 +34,15 @@ describe("Verbose Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 32, 36, "mid failure", "mid-name"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 32, 36, "mid failure", "mid-name", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "error"), ]; const expectedResult = - "(first-name) " + TEST_FILE + getPositionString(1, 1) + "first failure\n" + - "(mid-name) " + TEST_FILE + getPositionString(2, 12) + "mid failure\n" + - "(last-name) " + TEST_FILE + getPositionString(9, 2) + "last failure\n"; + "ERROR: (first-name) " + TEST_FILE + getPositionString(1, 1) + "first failure\n" + + "ERROR: (mid-name) " + TEST_FILE + getPositionString(2, 12) + "mid failure\n" + + "ERROR: (last-name) " + TEST_FILE + getPositionString(9, 2) + "last failure\n"; const actualResult = formatter.format(failures); assert.equal(actualResult, expectedResult); diff --git a/test/formatters/vsoFormatterTests.ts b/test/formatters/vsoFormatterTests.ts index 77d01d26371..3f34bbe01c6 100644 --- a/test/formatters/vsoFormatterTests.ts +++ b/test/formatters/vsoFormatterTests.ts @@ -16,7 +16,8 @@ import * as ts from "typescript"; -import {IFormatter, RuleFailure, TestUtils} from "../lint"; +import { IFormatter, TestUtils } from "../lint"; +import { createFailure } from "./utils"; describe("VSO Formatter", () => { const TEST_FILE = "formatters/vsoFormatter.test.ts"; @@ -33,9 +34,9 @@ describe("VSO Formatter", () => { const maxPosition = sourceFile.getFullWidth(); const failures = [ - new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), - new RuleFailure(sourceFile, 32, 36, "mid failure", "mid-name"), - new RuleFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name"), + createFailure(sourceFile, 0, 1, "first failure", "first-name", undefined, "error"), + createFailure(sourceFile, 32, 36, "mid failure", "mid-name", undefined, "error"), + createFailure(sourceFile, maxPosition - 1, maxPosition, "last failure", "last-name", undefined, "error"), ]; const expectedResult = diff --git a/test/ruleLoaderTests.ts b/test/ruleLoaderTests.ts index 2c81f20b18f..4a2778189f0 100644 --- a/test/ruleLoaderTests.ts +++ b/test/ruleLoaderTests.ts @@ -50,6 +50,49 @@ describe("Rule Loader", () => { assert.equal(rules.length, 1); }); + it("properly sets rule severity with options", () => { + const withOptions = { + "callable-types": true, + "max-line-length": { + options: 140, + severity: "warning", + }, + }; + + const rules = loadRules(withOptions, {}, [builtRulesDir]); + assert.equal(rules.length, 2); + assert.equal(rules[0].getOptions().ruleSeverity, "error"); + assert.equal(rules[1].getOptions().ruleSeverity, "warning"); + }); + + it("properly sets rule severity with no options", () => { + const noOptions = { + "callable-types": true, + "interface-name": { + severity: "warning", + }, + }; + + const rules = loadRules(noOptions, {}, [builtRulesDir]); + assert.equal(rules.length, 2); + assert.equal(rules[0].getOptions().ruleSeverity, "error"); + assert.equal(rules[1].getOptions().ruleSeverity, "warning"); + }); + + it("properly sets rule severity with options but no severity", () => { + const noSeverity = { + "callable-types": true, + "max-line-length": { + options: 140, + }, + }; + + const rules = loadRules(noSeverity, {}, [builtRulesDir]); + assert.equal(rules.length, 2); + assert.equal(rules[0].getOptions().ruleSeverity, "error"); + assert.equal(rules[1].getOptions().ruleSeverity, "error"); + }); + it("loads disabled rules if rule in enableDisableRuleMap", () => { const validConfiguration: {[name: string]: any} = { "class-name": true,