Skip to content

Commit

Permalink
fix: handle ts as expression in marchers (#403)
Browse files Browse the repository at this point in the history
  • Loading branch information
G-Rath authored and SimenB committed Aug 21, 2019
1 parent a29f993 commit 41d44d0
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 6 deletions.
20 changes: 20 additions & 0 deletions src/rules/__tests__/prefer-to-be-null.test.ts
Expand Up @@ -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();',
},
],
});
20 changes: 20 additions & 0 deletions src/rules/__tests__/prefer-to-be-undefined.test.ts
Expand Up @@ -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();',
},
],
});
16 changes: 16 additions & 0 deletions src/rules/__tests__/prefer-to-contain.test.ts
Expand Up @@ -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);',
},
],
});
9 changes: 6 additions & 3 deletions src/rules/prefer-to-be-null.ts
Expand Up @@ -3,9 +3,11 @@ import {
TSESTree,
} from '@typescript-eslint/experimental-utils';
import {
MaybeTypeCast,
ParsedEqualityMatcherCall,
ParsedExpectMatcher,
createRule,
followTypeAssertionChain,
isExpectCall,
isParsedEqualityMatcherCall,
parseExpectCall,
Expand All @@ -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,
Expand Down
5 changes: 3 additions & 2 deletions src/rules/prefer-to-be-undefined.ts
Expand Up @@ -6,6 +6,7 @@ import {
ParsedEqualityMatcherCall,
ParsedExpectMatcher,
createRule,
followTypeAssertionChain,
isExpectCall,
isParsedEqualityMatcherCall,
parseExpectCall,
Expand All @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion src/rules/prefer-to-contain.ts
Expand Up @@ -11,6 +11,7 @@ import {
ParsedEqualityMatcherCall,
ParsedExpectMatcher,
createRule,
followTypeAssertionChain,
hasOnlyOneArgument,
isExpectCall,
isParsedEqualityMatcherCall,
Expand Down Expand Up @@ -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;
Expand Down
29 changes: 29 additions & 0 deletions src/rules/utils.ts
Expand Up @@ -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`.
*/
Expand Down

0 comments on commit 41d44d0

Please sign in to comment.