Skip to content
Permalink
Browse files
fix: handle ts as expression in marchers (#403)
  • Loading branch information
G-Rath authored and SimenB committed Aug 21, 2019
1 parent a29f993 commit 41d44d060be5bfcbcdfb01d35b4fb19db25003f0
@@ -43,3 +43,23 @@ ruleTester.run('prefer-to-be-null', rule, {
},
],
});

new TSESLint.RuleTester({
parser: '@typescript-eslint/parser',
}).run('prefer-to-be-null: typescript edition', rule, {
valid: [
"(expect('Model must be bound to an array if the multiple property is true') as any).toHaveBeenTipped()",
],
invalid: [
{
code: 'expect(null).toBe(null as unknown as string as unknown as any);',
errors: [{ messageId: 'useToBeNull', column: 14, line: 1 }],
output: 'expect(null).toBeNull();',
},
{
code: 'expect("a string").not.toEqual(null as number);',
errors: [{ messageId: 'useToBeNull', column: 24, line: 1 }],
output: 'expect("a string").not.toBeNull();',
},
],
});
@@ -41,3 +41,23 @@ ruleTester.run('prefer-to-be-undefined', rule, {
},
],
});

new TSESLint.RuleTester({
parser: '@typescript-eslint/parser',
}).run('prefer-to-be-undefined: typescript edition', rule, {
valid: [
"(expect('Model must be bound to an array if the multiple property is true') as any).toHaveBeenTipped()",
],
invalid: [
{
code: 'expect(undefined).toBe(undefined as unknown as string as any);',
errors: [{ messageId: 'useToBeUndefined', column: 19, line: 1 }],
output: 'expect(undefined).toBeUndefined();',
},
{
code: 'expect("a string").not.toEqual(undefined as number);',
errors: [{ messageId: 'useToBeUndefined', column: 24, line: 1 }],
output: 'expect("a string").not.toBeUndefined();',
},
],
});
@@ -114,3 +114,19 @@ ruleTester.run('prefer-to-contain', rule, {
},
],
});

new TSESLint.RuleTester({
parser: '@typescript-eslint/parser',
}).run('prefer-to-be-null: typescript edition', rule, {
valid: [
"(expect('Model must be bound to an array if the multiple property is true') as any).toHaveBeenTipped()",
'expect(a.includes(b)).toEqual(0 as boolean);',
],
invalid: [
{
code: 'expect(a.includes(b)).toEqual(false as boolean);',
errors: [{ messageId: 'useToContain', column: 23, line: 1 }],
output: 'expect(a).not.toContain(b);',
},
],
});
@@ -3,9 +3,11 @@ import {
TSESTree,
} from '@typescript-eslint/experimental-utils';
import {
MaybeTypeCast,
ParsedEqualityMatcherCall,
ParsedExpectMatcher,
createRule,
followTypeAssertionChain,
isExpectCall,
isParsedEqualityMatcherCall,
parseExpectCall,
@@ -24,12 +26,13 @@ const isNullLiteral = (node: TSESTree.Node): node is NullLiteral =>
*
* @param {ParsedExpectMatcher} matcher
*
* @return {matcher is ParsedEqualityMatcherCall<NullLiteral>}
* @return {matcher is ParsedEqualityMatcherCall<MaybeTypeCast<NullLiteral>>}
*/
const isNullEqualityMatcher = (
matcher: ParsedExpectMatcher,
): matcher is ParsedEqualityMatcherCall<NullLiteral> =>
isParsedEqualityMatcherCall(matcher) && isNullLiteral(matcher.arguments[0]);
): matcher is ParsedEqualityMatcherCall<MaybeTypeCast<NullLiteral>> =>
isParsedEqualityMatcherCall(matcher) &&
isNullLiteral(followTypeAssertionChain(matcher.arguments[0]));

export default createRule({
name: __filename,
@@ -6,6 +6,7 @@ import {
ParsedEqualityMatcherCall,
ParsedExpectMatcher,
createRule,
followTypeAssertionChain,
isExpectCall,
isParsedEqualityMatcherCall,
parseExpectCall,
@@ -26,13 +27,13 @@ const isUndefinedIdentifier = (
*
* @param {ParsedExpectMatcher} matcher
*
* @return {matcher is ParsedEqualityMatcherCall<UndefinedIdentifier>}
* @return {matcher is ParsedEqualityMatcherCall<MaybeTypeCast<UndefinedIdentifier>>}
*/
const isUndefinedEqualityMatcher = (
matcher: ParsedExpectMatcher,
): matcher is ParsedEqualityMatcherCall<UndefinedIdentifier> =>
isParsedEqualityMatcherCall(matcher) &&
isUndefinedIdentifier(matcher.arguments[0]);
isUndefinedIdentifier(followTypeAssertionChain(matcher.arguments[0]));

export default createRule({
name: __filename,
@@ -11,6 +11,7 @@ import {
ParsedEqualityMatcherCall,
ParsedExpectMatcher,
createRule,
followTypeAssertionChain,
hasOnlyOneArgument,
isExpectCall,
isParsedEqualityMatcherCall,
@@ -45,7 +46,7 @@ const isBooleanEqualityMatcher = (
matcher: ParsedExpectMatcher,
): matcher is ParsedBooleanEqualityMatcherCall =>
isParsedEqualityMatcherCall(matcher) &&
isBooleanLiteral(matcher.arguments[0]);
isBooleanLiteral(followTypeAssertionChain(matcher.arguments[0]));

type FixableIncludesCallExpression = KnownCallExpression<'includes'> &
CallExpressionWithSingleArgument;
@@ -15,6 +15,35 @@ export const createRule = ESLintUtils.RuleCreator(name => {
return `${REPO_URL}/blob/v${version}/docs/rules/${ruleName}.md`;
});

export type MaybeTypeCast<Expression extends TSESTree.Expression> =
| TSTypeCastExpression<Expression>
| Expression;

export type TSTypeCastExpression<
Expression extends TSESTree.Expression = TSESTree.Expression
> = AsExpressionChain<Expression> | TypeAssertionChain<Expression>;

interface AsExpressionChain<
Expression extends TSESTree.Expression = TSESTree.Expression
> extends TSESTree.TSAsExpression {
expression: AsExpressionChain<Expression> | Expression;
}

interface TypeAssertionChain<
Expression extends TSESTree.Expression = TSESTree.Expression
> extends TSESTree.TSTypeAssertion {
// expression: TypeAssertionChain<Expression> | Expression;
expression: any; // https://github.com/typescript-eslint/typescript-eslint/issues/802
}

export const followTypeAssertionChain = (
expression: TSESTree.Expression | TSTypeCastExpression,
): TSESTree.Expression =>
expression.type === AST_NODE_TYPES.TSAsExpression ||
expression.type === AST_NODE_TYPES.TSTypeAssertion
? followTypeAssertionChain(expression.expression)
: expression;

/**
* A `Literal` with a `value` of type `string`.
*/

0 comments on commit 41d44d0

Please sign in to comment.