tslint-no-unused-expression-chai
Version:
Custom tslint no-unused-expression rule supports chai's expect assertion
146 lines • 6.79 kB
JavaScript
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var Lint = require("tslint");
var tsutils = require("tsutils");
var noUnusedExpressionRule_1 = require("tslint/lib/rules/noUnusedExpressionRule");
var OPTION_SHOULD = 'should';
// START copied from noUnusedExpression.ts
var ALLOW_FAST_NULL_CHECKS = 'allow-fast-null-checks';
var ALLOW_NEW = 'allow-new';
var ALLOW_TAGGED_TEMPLATE = 'allow-tagged-template';
var TSLINT_META = {
optionsDescription: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\nThe following arguments may be optionally provided:\n\n* `", "` allows to use logical operators to perform fast null checks\n and perform method or function calls for side effects (e.g. `e && e.preventDefault()`).\n* `", "` allows 'new' expressions for side effects (e.g. `new ModifyGlobalState();`.\n* `", "` allows tagged templates for side effects\n (e.g. `this.add\\`foo\\`;`).\n* `", "` supports chai assertions using `should` in addition to `expect`;`.\n"], ["\nThe following arguments may be optionally provided:\n\n* \\`", "\\` allows to use logical operators to perform fast null checks\n and perform method or function calls for side effects (e.g. \\`e && e.preventDefault()\\`).\n* \\`", "\\` allows 'new' expressions for side effects (e.g. \\`new ModifyGlobalState();\\`.\n* \\`", "\\` allows tagged templates for side effects\n (e.g. \\`this.add\\\\\\`foo\\\\\\`;\\`).\n* \\`", "\\` supports chai assertions using \\`should\\` in addition to \\`expect\\`;\\`.\n"])), ALLOW_FAST_NULL_CHECKS, ALLOW_NEW, ALLOW_TAGGED_TEMPLATE, OPTION_SHOULD),
options: {
type: 'array',
items: {
type: 'string',
enum: [ALLOW_FAST_NULL_CHECKS, ALLOW_NEW, ALLOW_TAGGED_TEMPLATE, OPTION_SHOULD]
},
minLength: 0,
maxLength: 4
},
optionExamples: [
true,
[true, ALLOW_FAST_NULL_CHECKS],
[true, OPTION_SHOULD],
[true, ALLOW_FAST_NULL_CHECKS, OPTION_SHOULD]
]
};
// END copied from noUnusedExpression.ts
/**
* Predicate to determine given failure is chai's `expect` assertion.
* It relies on naive assumptions based on chai assertion syntax.
*/
var chaiAssertionPredicate = function (failure, source) {
var failurePosition = failure.getStartPosition();
var token = tsutils.getTokenAtPosition(source, failurePosition.getPosition());
//for any reason locating token is not available, falls back to default rule
if (!token) {
return true;
}
//check very exact token is identifier, `expect`
var isTokenIdentifier = tsutils.isIdentifier(token);
if (!isTokenIdentifier) {
return true;
}
var parentToken = token.parent;
//same as token. located chai assertion should have parent token
if (!parentToken) {
return true;
}
//traverse up one parent, check it's call expression
var isParentTokenCallExpression = tsutils.isCallExpression(parentToken);
if (!isParentTokenCallExpression) {
return true;
}
//finally compare actual token text to chai assertion, return false if given token is `expect`
return token.getText() !== 'expect';
};
/**
* Predicate to determine given failure is chai's `should` assertion.
* It relies on naive assumptions based on chai assertion syntax.
*/
var withoutShouldAssertions = function (failure, source) {
var failurePosition = failure.getStartPosition();
var token = tsutils.getTokenAtPosition(source, failurePosition.getPosition());
// for any reason locating token is not available, falls back to default rule
if (!token) {
return true;
}
var current = token;
// scan through parents for a property access expression
// stop when hitting expression statement
while (current && !tsutils.isExpressionStatement(current)) {
if (tsutils.isPropertyAccessExpression(current)) {
if (current.name.text === 'should') {
// make sure there is at least one more property access after should
return current.parent && !tsutils.isPropertyAccessExpression(current.parent);
}
}
current = current.parent;
}
return true;
};
/**
* Implements no-unused-expression-chai rules
* To honor base rule's behavior, it inherits from default no-unused-expression rule
* and override specific failure only
*
*/
var Rule = /** @class */ (function (_super) {
__extends(Rule, _super);
function Rule() {
return _super !== null && _super.apply(this, arguments) || this;
}
/**
* Apply rules. Simply walk source by default rule first, and filter out chai expression
*
*/
Rule.prototype.apply = function (sourceFile) {
var failures = _super.prototype.apply.call(this, sourceFile);
if (failures && failures.length > 0) {
var filterShould_1 = this.ruleArguments.indexOf(OPTION_SHOULD) > -1;
return failures.filter(function (x) {
var expectResult = chaiAssertionPredicate(x, sourceFile);
if (expectResult && filterShould_1) {
return withoutShouldAssertions(x, sourceFile);
}
return expectResult;
});
}
return failures;
};
Rule.metadata = __assign({}, noUnusedExpressionRule_1.Rule.metadata, TSLINT_META, { ruleName: 'no-unused-expression-chai' });
return Rule;
}(noUnusedExpressionRule_1.Rule));
exports.Rule = Rule;
var templateObject_1;
//# sourceMappingURL=noUnusedExpressionChaiRule.js.map
;