mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter): eslint-plugin-jest: prefer-to-be (#2702)
Rule Detail: [link](https://github.com/jest-community/eslint-plugin-jest/blob/main/src/rules/prefer-to-be.ts)
This commit is contained in:
parent
b00d4b8110
commit
f8fe3af3cf
5 changed files with 863 additions and 10 deletions
|
|
@ -155,6 +155,7 @@ mod jest {
|
|||
pub mod prefer_equality_matcher;
|
||||
pub mod prefer_spy_on;
|
||||
pub mod prefer_strict_equal;
|
||||
pub mod prefer_to_be;
|
||||
pub mod prefer_to_have_length;
|
||||
pub mod prefer_todo;
|
||||
pub mod require_to_throw_message;
|
||||
|
|
@ -459,6 +460,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
jest::prefer_equality_matcher,
|
||||
jest::prefer_spy_on,
|
||||
jest::prefer_strict_equal,
|
||||
jest::prefer_to_be,
|
||||
jest::prefer_to_have_length,
|
||||
jest::prefer_todo,
|
||||
jest::require_to_throw_message,
|
||||
|
|
|
|||
553
crates/oxc_linter/src/rules/jest/prefer_to_be.rs
Normal file
553
crates/oxc_linter/src/rules/jest/prefer_to_be.rs
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
use oxc_ast::{
|
||||
ast::{Argument, CallExpression, Expression, MemberExpression},
|
||||
AstKind,
|
||||
};
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::Error,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
fixer::Fix,
|
||||
rule::Rule,
|
||||
utils::{
|
||||
collect_possible_jest_call_node, is_equality_matcher, parse_expect_jest_fn_call,
|
||||
KnownMemberExpressionProperty, ParsedExpectFnCall, PossibleJestNode,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
enum PreferToBeDiagnostic {
|
||||
#[error("eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.")]
|
||||
#[diagnostic(severity(warning))]
|
||||
UseToBe(#[label] Span),
|
||||
#[error("eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.")]
|
||||
#[diagnostic(severity(warning))]
|
||||
UseToBeUndefined(#[label] Span),
|
||||
#[error("eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.")]
|
||||
#[diagnostic(severity(warning))]
|
||||
UseToBeDefined(#[label] Span),
|
||||
#[error("eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.")]
|
||||
#[diagnostic(severity(warning))]
|
||||
UseToBeNull(#[label] Span),
|
||||
#[error("eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.")]
|
||||
#[diagnostic(severity(warning))]
|
||||
UseToBeNaN(#[label] Span),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PreferToBe;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// When asserting against primitive literals such as numbers and strings, the
|
||||
/// equality matchers all operate the same, but read slightly differently in code.
|
||||
///
|
||||
/// This rule recommends using the `toBe` matcher in these situations, as it forms
|
||||
/// the most grammatically natural sentence. For `null`, `undefined`, and `NaN` this
|
||||
/// rule recommends using their specific `toBe` matchers, as they give better error
|
||||
/// messages as well.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```javascript
|
||||
/// // valid
|
||||
/// expect(value).not.toBe(5);
|
||||
/// expect(getMessage()).toBe('hello world');
|
||||
/// expect(loadMessage()).resolves.toBe('hello world');
|
||||
/// expect(didError).not.toBe(true);
|
||||
/// expect(catchError()).toStrictEqual({ message: 'oh noes!' });
|
||||
///
|
||||
/// // invalid
|
||||
/// expect(value).not.toEqual(5);
|
||||
/// expect(getMessage()).toStrictEqual('hello world');
|
||||
/// expect(loadMessage()).resolves.toEqual('hello world');
|
||||
/// ```
|
||||
PreferToBe,
|
||||
style,
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum PreferToBeKind {
|
||||
Defined,
|
||||
NaN,
|
||||
Null,
|
||||
ToBe,
|
||||
Undefined,
|
||||
}
|
||||
|
||||
impl Rule for PreferToBe {
|
||||
fn run_once(&self, ctx: &LintContext) {
|
||||
for possible_jest_node in &collect_possible_jest_call_node(ctx) {
|
||||
Self::run(possible_jest_node, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PreferToBe {
|
||||
fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) {
|
||||
let node = possible_jest_node.node;
|
||||
let AstKind::CallExpression(call_expr) = node.kind() else {
|
||||
return;
|
||||
};
|
||||
let Some(jest_expect_fn_call) =
|
||||
parse_expect_jest_fn_call(call_expr, possible_jest_node, ctx)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(matcher) = jest_expect_fn_call.matcher() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let has_not_modifier = jest_expect_fn_call
|
||||
.modifiers()
|
||||
.iter()
|
||||
.filter(|modifier| modifier.is_name_equal("not"))
|
||||
.count()
|
||||
> 0;
|
||||
|
||||
if has_not_modifier {
|
||||
if matcher.is_name_equal("toBeUndefined") {
|
||||
Self::check_and_fix(
|
||||
&PreferToBeKind::Defined,
|
||||
call_expr,
|
||||
matcher,
|
||||
&jest_expect_fn_call,
|
||||
ctx,
|
||||
);
|
||||
return;
|
||||
} else if matcher.is_name_equal("toBeDefined") {
|
||||
Self::check_and_fix(
|
||||
&PreferToBeKind::Undefined,
|
||||
call_expr,
|
||||
matcher,
|
||||
&jest_expect_fn_call,
|
||||
ctx,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !is_equality_matcher(matcher) || jest_expect_fn_call.args.len() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(Argument::Expression(arg_expr)) = jest_expect_fn_call.args.first() else {
|
||||
return;
|
||||
};
|
||||
let first_matcher_arg = arg_expr.get_inner_expression();
|
||||
|
||||
if first_matcher_arg.is_undefined() {
|
||||
let kind =
|
||||
if has_not_modifier { PreferToBeKind::Defined } else { PreferToBeKind::Undefined };
|
||||
Self::check_and_fix(&kind, call_expr, matcher, &jest_expect_fn_call, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if first_matcher_arg.is_nan() {
|
||||
Self::check_and_fix(
|
||||
&PreferToBeKind::NaN,
|
||||
call_expr,
|
||||
matcher,
|
||||
&jest_expect_fn_call,
|
||||
ctx,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if first_matcher_arg.is_null() {
|
||||
Self::check_and_fix(
|
||||
&PreferToBeKind::Null,
|
||||
call_expr,
|
||||
matcher,
|
||||
&jest_expect_fn_call,
|
||||
ctx,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if Self::should_use_tobe(first_matcher_arg) && !matcher.is_name_equal("toBe") {
|
||||
Self::check_and_fix(
|
||||
&PreferToBeKind::ToBe,
|
||||
call_expr,
|
||||
matcher,
|
||||
&jest_expect_fn_call,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn should_use_tobe(first_matcher_arg: &Expression) -> bool {
|
||||
let mut expr = first_matcher_arg;
|
||||
|
||||
if let Expression::UnaryExpression(unary_expr) = expr {
|
||||
if unary_expr.operator.as_str() == "-" {
|
||||
expr = &unary_expr.argument;
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(expr, Expression::RegExpLiteral(_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
matches!(
|
||||
expr,
|
||||
Expression::BigintLiteral(_)
|
||||
| Expression::BooleanLiteral(_)
|
||||
| Expression::NumericLiteral(_)
|
||||
| Expression::NullLiteral(_)
|
||||
| Expression::TemplateLiteral(_)
|
||||
| Expression::StringLiteral(_)
|
||||
)
|
||||
}
|
||||
|
||||
fn check_and_fix(
|
||||
kind: &PreferToBeKind,
|
||||
call_expr: &CallExpression,
|
||||
matcher: &KnownMemberExpressionProperty,
|
||||
jest_expect_fn_call: &ParsedExpectFnCall,
|
||||
ctx: &LintContext,
|
||||
) {
|
||||
let span = matcher.span;
|
||||
let end = call_expr.span.end;
|
||||
let Some(Expression::MemberExpression(mem_expr)) = matcher.parent else {
|
||||
return;
|
||||
};
|
||||
let is_cmp_mem_expr = matches!(mem_expr.0, MemberExpression::ComputedMemberExpression(_));
|
||||
let modifiers = jest_expect_fn_call.modifiers();
|
||||
let maybe_not_modifier = modifiers.iter().find(|modifier| modifier.is_name_equal("not"));
|
||||
|
||||
if kind == &PreferToBeKind::Undefined {
|
||||
ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeUndefined(span), || {
|
||||
let new_matcher =
|
||||
if is_cmp_mem_expr { "[\"toBeUndefined\"]()" } else { "toBeUndefined()" };
|
||||
if let Some(not_modifier) = maybe_not_modifier {
|
||||
Fix::new(new_matcher.to_string(), Span::new(not_modifier.span.start, end))
|
||||
} else {
|
||||
Fix::new(new_matcher.to_string(), Span::new(span.start, end))
|
||||
}
|
||||
});
|
||||
} else if kind == &PreferToBeKind::Defined {
|
||||
ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeDefined(span), || {
|
||||
let (new_matcher, start) = if is_cmp_mem_expr {
|
||||
("[\"toBeDefined\"]()", modifiers.first().unwrap().span.end)
|
||||
} else {
|
||||
("toBeDefined()", maybe_not_modifier.unwrap().span.start)
|
||||
};
|
||||
|
||||
Fix::new(new_matcher.to_string(), Span::new(start, end))
|
||||
});
|
||||
} else if kind == &PreferToBeKind::Null {
|
||||
ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeNull(span), || {
|
||||
let new_matcher = if is_cmp_mem_expr { "\"toBeNull\"]()" } else { "toBeNull()" };
|
||||
Fix::new(new_matcher.to_string(), Span::new(span.start, end))
|
||||
});
|
||||
} else if kind == &PreferToBeKind::NaN {
|
||||
ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBeNaN(span), || {
|
||||
let new_matcher = if is_cmp_mem_expr { "\"toBeNaN\"]()" } else { "toBeNaN()" };
|
||||
Fix::new(new_matcher.to_string(), Span::new(span.start, end))
|
||||
});
|
||||
} else {
|
||||
ctx.diagnostic_with_fix(PreferToBeDiagnostic::UseToBe(span), || {
|
||||
let new_matcher = if is_cmp_mem_expr { "\"toBe\"" } else { "toBe" };
|
||||
Fix::new(new_matcher.to_string(), span)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tests() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
("expect(null).toBeNull();", None),
|
||||
("expect(null).not.toBeNull();", None),
|
||||
("expect(null).toBe(1);", None),
|
||||
("expect(null).toBe(-1);", None),
|
||||
("expect(null).toBe(...1);", None),
|
||||
("expect(obj).toStrictEqual([ x, 1 ]);", None),
|
||||
("expect(obj).toStrictEqual({ x: 1 });", None),
|
||||
("expect(obj).not.toStrictEqual({ x: 1 });", None),
|
||||
("expect(value).toMatchSnapshot();", None),
|
||||
("expect(catchError()).toStrictEqual({ message: 'oh noes!' })", None),
|
||||
("expect(\"something\");", None),
|
||||
("expect(token).toStrictEqual(/[abc]+/g);", None),
|
||||
("expect(token).toStrictEqual(new RegExp('[abc]+', 'g'));", None),
|
||||
("expect(value).toEqual(dedent`my string`);", None),
|
||||
// null
|
||||
("expect(null).toBeNull();", None),
|
||||
("expect(null).not.toBeNull();", None),
|
||||
("expect(null).toBe(1);", None),
|
||||
("expect(obj).toStrictEqual([ x, 1 ]);", None),
|
||||
("expect(obj).toStrictEqual({ x: 1 });", None),
|
||||
("expect(obj).not.toStrictEqual({ x: 1 });", None),
|
||||
("expect(value).toMatchSnapshot();", None),
|
||||
("expect(catchError()).toStrictEqual({ message: 'oh noes!' })", None),
|
||||
("expect(\"something\");", None),
|
||||
("expect(null).not.toEqual();", None),
|
||||
("expect(null).toBe();", None),
|
||||
("expect(null).toMatchSnapshot();", None),
|
||||
("expect(\"a string\").toMatchSnapshot(null);", None),
|
||||
("expect(\"a string\").not.toMatchSnapshot();", None),
|
||||
("expect(null).toBe", None),
|
||||
// undefined
|
||||
("expect(undefined).toBeUndefined();", None),
|
||||
("expect(true).toBeDefined();", None),
|
||||
("expect({}).toEqual({});", None),
|
||||
("expect(something).toBe()", None),
|
||||
("expect(something).toBe(somethingElse)", None),
|
||||
("expect(something).toEqual(somethingElse)", None),
|
||||
("expect(something).not.toBe(somethingElse)", None),
|
||||
("expect(something).not.toEqual(somethingElse)", None),
|
||||
("expect(undefined).toBe", None),
|
||||
("expect(\"something\");", None),
|
||||
// NaN
|
||||
("expect(NaN).toBeNaN();", None),
|
||||
("expect(true).not.toBeNaN();", None),
|
||||
("expect({}).toEqual({});", None),
|
||||
("expect(something).toBe()", None),
|
||||
("expect(something).toBe(somethingElse)", None),
|
||||
("expect(something).toEqual(somethingElse)", None),
|
||||
("expect(something).not.toBe(somethingElse)", None),
|
||||
("expect(something).not.toEqual(somethingElse)", None),
|
||||
("expect(undefined).toBe", None),
|
||||
("expect(\"something\");", None),
|
||||
// undefined vs defined
|
||||
("expect(NaN).toBeNaN();", None),
|
||||
("expect(true).not.toBeNaN();", None),
|
||||
("expect({}).toEqual({});", None),
|
||||
("expect(something).toBe()", None),
|
||||
("expect(something).toBe(somethingElse)", None),
|
||||
("expect(something).toEqual(somethingElse)", None),
|
||||
("expect(something).not.toBe(somethingElse)", None),
|
||||
("expect(something).not.toEqual(somethingElse)", None),
|
||||
("expect(undefined).toBe", None),
|
||||
("expect(\"something\");", None),
|
||||
// typescript edition
|
||||
("(expect('Model must be bound to an array if the multiple property is true') as any).toHaveBeenTipped()", None),
|
||||
];
|
||||
|
||||
let fail = vec;", None),
|
||||
("expect(value).toStrictEqual(`my ${string}`);", None),
|
||||
("expect(loadMessage()).resolves.toStrictEqual(\"hello world\");", None),
|
||||
("expect(loadMessage()).resolves[\"toStrictEqual\"](\"hello world\");", None),
|
||||
("expect(loadMessage())[\"resolves\"].toStrictEqual(\"hello world\");", None),
|
||||
("expect(loadMessage()).resolves.toStrictEqual(false);", None),
|
||||
// null
|
||||
("expect(null).toBe(null);", None),
|
||||
("expect(null).toEqual(null);", None),
|
||||
("expect(null).toEqual(null,);", None),
|
||||
("expect(null).toStrictEqual(null);", None),
|
||||
("expect(\"a string\").not.toBe(null);", None),
|
||||
("expect(\"a string\").not[\"toBe\"](null);", None),
|
||||
("expect(\"a string\")[\"not\"][\"toBe\"](null);", None),
|
||||
("expect(\"a string\").not.toEqual(null);", None),
|
||||
("expect(\"a string\").not.toStrictEqual(null);", None),
|
||||
// undefined
|
||||
("expect(undefined).toBe(undefined);", None),
|
||||
("expect(undefined).toEqual(undefined);", None),
|
||||
("expect(undefined).toStrictEqual(undefined);", None),
|
||||
("expect(\"a string\").not.toBe(undefined);", None),
|
||||
("expect(\"a string\").rejects.not.toBe(undefined);", None),
|
||||
("expect(\"a string\").rejects.not[\"toBe\"](undefined);", None),
|
||||
("expect(\"a string\").not.toEqual(undefined);", None),
|
||||
("expect(\"a string\").not.toStrictEqual(undefined);", None),
|
||||
// NaN
|
||||
("expect(NaN).toBe(NaN);", None),
|
||||
("expect(NaN).toEqual(NaN);", None),
|
||||
("expect(NaN).toStrictEqual(NaN);", None),
|
||||
("expect(\"a string\").not.toBe(NaN);", None),
|
||||
("expect(\"a string\").rejects.not.toBe(NaN);", None),
|
||||
("expect(\"a string\")[\"rejects\"].not.toBe(NaN);", None),
|
||||
("expect(\"a string\").not.toEqual(NaN);", None),
|
||||
("expect(\"a string\").not.toStrictEqual(NaN);", None),
|
||||
// undefined vs defined
|
||||
("expect(undefined).not.toBeDefined();", None),
|
||||
("expect(undefined).resolves.not.toBeDefined();", None),
|
||||
("expect(undefined).resolves.toBe(undefined);", None),
|
||||
("expect(\"a string\").not.toBeUndefined();", None),
|
||||
("expect(\"a string\").rejects.not.toBeUndefined();", None),
|
||||
// typescript edition
|
||||
("expect(null).toEqual(1 as unknown as string as unknown as any);", None),
|
||||
("expect(null).toEqual(-1 as unknown as string as unknown as any);", None),
|
||||
("expect(\"a string\").not.toStrictEqual(\"string\" as number);", None),
|
||||
("expect(null).toBe(null as unknown as string as unknown as any);", None),
|
||||
("expect(\"a string\").not.toEqual(null as number);", None),
|
||||
("expect(undefined).toBe(undefined as unknown as string as any);", None),
|
||||
("expect(\"a string\").toEqual(undefined as number);", None),
|
||||
];
|
||||
|
||||
let fix = vec;", "expect(value)[\"toBe\"](`my string`);", None),
|
||||
(
|
||||
"expect(value).toStrictEqual(`my ${string}`);",
|
||||
"expect(value).toBe(`my ${string}`);",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(loadMessage()).resolves.toStrictEqual(\"hello world\");",
|
||||
"expect(loadMessage()).resolves.toBe(\"hello world\");",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(loadMessage()).resolves[\"toStrictEqual\"](\"hello world\");",
|
||||
"expect(loadMessage()).resolves[\"toBe\"](\"hello world\");",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(loadMessage())[\"resolves\"].toStrictEqual(\"hello world\");",
|
||||
"expect(loadMessage())[\"resolves\"].toBe(\"hello world\");",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(loadMessage()).resolves.toStrictEqual(false);",
|
||||
"expect(loadMessage()).resolves.toBe(false);",
|
||||
None,
|
||||
),
|
||||
// null
|
||||
("expect(null).toBe(null);", "expect(null).toBeNull();", None),
|
||||
("expect(null).toEqual(null);", "expect(null).toBeNull();", None),
|
||||
("expect(null).toEqual(null,);", "expect(null).toBeNull();", None),
|
||||
("expect(null).toStrictEqual(null);", "expect(null).toBeNull();", None),
|
||||
("expect(\"a string\").not.toBe(null);", "expect(\"a string\").not.toBeNull();", None),
|
||||
(
|
||||
"expect(\"a string\").not[\"toBe\"](null);",
|
||||
"expect(\"a string\").not[\"toBeNull\"]();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\")[\"not\"][\"toBe\"](null);",
|
||||
"expect(\"a string\")[\"not\"][\"toBeNull\"]();",
|
||||
None,
|
||||
),
|
||||
("expect(\"a string\").not.toEqual(null);", "expect(\"a string\").not.toBeNull();", None),
|
||||
(
|
||||
"expect(\"a string\").not.toStrictEqual(null);",
|
||||
"expect(\"a string\").not.toBeNull();",
|
||||
None,
|
||||
),
|
||||
// undefined
|
||||
("expect(undefined).toBe(undefined);", "expect(undefined).toBeUndefined();", None),
|
||||
("expect(undefined).toEqual(undefined);", "expect(undefined).toBeUndefined();", None),
|
||||
("expect(undefined).toStrictEqual(undefined);", "expect(undefined).toBeUndefined();", None),
|
||||
("expect(\"a string\").not.toBe(undefined);", "expect(\"a string\").toBeDefined();", None),
|
||||
(
|
||||
"expect(\"a string\").rejects.not.toBe(undefined);",
|
||||
"expect(\"a string\").rejects.toBeDefined();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\").rejects.not[\"toBe\"](undefined);",
|
||||
"expect(\"a string\").rejects[\"toBeDefined\"]();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\").not.toEqual(undefined);",
|
||||
"expect(\"a string\").toBeDefined();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\").not.toStrictEqual(undefined);",
|
||||
"expect(\"a string\").toBeDefined();",
|
||||
None,
|
||||
),
|
||||
// NaN
|
||||
("expect(NaN).toBe(NaN);", "expect(NaN).toBeNaN();", None),
|
||||
("expect(NaN).toEqual(NaN);", "expect(NaN).toBeNaN();", None),
|
||||
("expect(NaN).toStrictEqual(NaN);", "expect(NaN).toBeNaN();", None),
|
||||
("expect(\"a string\").not.toBe(NaN);", "expect(\"a string\").not.toBeNaN();", None),
|
||||
(
|
||||
"expect(\"a string\").rejects.not.toBe(NaN);",
|
||||
"expect(\"a string\").rejects.not.toBeNaN();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\")[\"rejects\"].not.toBe(NaN);",
|
||||
"expect(\"a string\")[\"rejects\"].not.toBeNaN();",
|
||||
None,
|
||||
),
|
||||
("expect(\"a string\").not.toEqual(NaN);", "expect(\"a string\").not.toBeNaN();", None),
|
||||
(
|
||||
"expect(\"a string\").not.toStrictEqual(NaN);",
|
||||
"expect(\"a string\").not.toBeNaN();",
|
||||
None,
|
||||
),
|
||||
// undefined vs defined
|
||||
("expect(undefined).not.toBeDefined();", "expect(undefined).toBeUndefined();", None),
|
||||
(
|
||||
"expect(undefined).resolves.not.toBeDefined();",
|
||||
"expect(undefined).resolves.toBeUndefined();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(undefined).resolves.toBe(undefined);",
|
||||
"expect(undefined).resolves.toBeUndefined();",
|
||||
None,
|
||||
),
|
||||
("expect(\"a string\").not.toBeUndefined();", "expect(\"a string\").toBeDefined();", None),
|
||||
(
|
||||
"expect(\"a string\").rejects.not.toBeUndefined();",
|
||||
"expect(\"a string\").rejects.toBeDefined();",
|
||||
None,
|
||||
),
|
||||
// typescript edition
|
||||
(
|
||||
"expect(null).toEqual(1 as unknown as string as unknown as any);",
|
||||
"expect(null).toBe(1 as unknown as string as unknown as any);",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(null).toEqual(-1 as unknown as string as unknown as any);",
|
||||
"expect(null).toBe(-1 as unknown as string as unknown as any);",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\").not.toStrictEqual(\"string\" as number);",
|
||||
"expect(\"a string\").not.toBe(\"string\" as number);",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(null).toBe(null as unknown as string as unknown as any);",
|
||||
"expect(null).toBeNull();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\").not.toEqual(null as number);",
|
||||
"expect(\"a string\").not.toBeNull();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(undefined).toBe(undefined as unknown as string as any);",
|
||||
"expect(undefined).toBeUndefined();",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"expect(\"a string\").toEqual(undefined as number);",
|
||||
"expect(\"a string\").toBeUndefined();",
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
||||
Tester::new(PreferToBe::NAME, pass, fail)
|
||||
.with_jest_plugin(true)
|
||||
.expect_fix(fix)
|
||||
.test_and_snapshot();
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@ use crate::{
|
|||
fixer::Fix,
|
||||
rule::Rule,
|
||||
utils::{
|
||||
collect_possible_jest_call_node, parse_expect_jest_fn_call, ParsedExpectFnCall,
|
||||
PossibleJestNode,
|
||||
collect_possible_jest_call_node, is_equality_matcher, parse_expect_jest_fn_call,
|
||||
ParsedExpectFnCall, PossibleJestNode,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -140,11 +140,8 @@ impl PreferToHaveLength {
|
|||
let Some(matcher) = parsed_expect_call.matcher() else {
|
||||
return;
|
||||
};
|
||||
let Some(matcher_name) = matcher.name() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if expect_property_name != "length" || !Self::is_equality_matcher(&matcher_name) {
|
||||
if expect_property_name != "length" || !is_equality_matcher(matcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -159,10 +156,6 @@ impl PreferToHaveLength {
|
|||
});
|
||||
}
|
||||
|
||||
fn is_equality_matcher(matcher_name: &str) -> bool {
|
||||
matcher_name == "toBe" || matcher_name == "toEqual" || matcher_name == "toStrictEqual"
|
||||
}
|
||||
|
||||
fn build_code(
|
||||
mem_expr: &MemberExpression,
|
||||
kind: Option<&str>,
|
||||
|
|
|
|||
298
crates/oxc_linter/src/snapshots/prefer_to_be.snap
Normal file
298
crates/oxc_linter/src/snapshots/prefer_to_be.snap
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
assertion_line: 151
|
||||
expression: prefer_to_be
|
||||
---
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value).toEqual("my string");
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value).toStrictEqual("my string");
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value).toStrictEqual(1);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value).toStrictEqual(1,);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value).toStrictEqual(-1);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value).toEqual(`my string`);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value)["toEqual"](`my string`);
|
||||
· ─────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:15]
|
||||
1 │ expect(value).toStrictEqual(`my ${string}`);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect(loadMessage()).resolves.toStrictEqual("hello world");
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect(loadMessage()).resolves["toStrictEqual"]("hello world");
|
||||
· ───────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:35]
|
||||
1 │ expect(loadMessage())["resolves"].toStrictEqual("hello world");
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect(loadMessage()).resolves.toStrictEqual(false);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:14]
|
||||
1 │ expect(null).toBe(null);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:14]
|
||||
1 │ expect(null).toEqual(null);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:14]
|
||||
1 │ expect(null).toEqual(null,);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:14]
|
||||
1 │ expect(null).toStrictEqual(null);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toBe(null);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not["toBe"](null);
|
||||
· ──────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:27]
|
||||
1 │ expect("a string")["not"]["toBe"](null);
|
||||
· ──────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toEqual(null);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toStrictEqual(null);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:19]
|
||||
1 │ expect(undefined).toBe(undefined);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:19]
|
||||
1 │ expect(undefined).toEqual(undefined);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:19]
|
||||
1 │ expect(undefined).toStrictEqual(undefined);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toBe(undefined);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect("a string").rejects.not.toBe(undefined);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect("a string").rejects.not["toBe"](undefined);
|
||||
· ──────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toEqual(undefined);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toStrictEqual(undefined);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:13]
|
||||
1 │ expect(NaN).toBe(NaN);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:13]
|
||||
1 │ expect(NaN).toEqual(NaN);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:13]
|
||||
1 │ expect(NaN).toStrictEqual(NaN);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toBe(NaN);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect("a string").rejects.not.toBe(NaN);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:35]
|
||||
1 │ expect("a string")["rejects"].not.toBe(NaN);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toEqual(NaN);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNaN` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toStrictEqual(NaN);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:23]
|
||||
1 │ expect(undefined).not.toBeDefined();
|
||||
· ───────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect(undefined).resolves.not.toBeDefined();
|
||||
· ───────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:28]
|
||||
1 │ expect(undefined).resolves.toBe(undefined);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toBeUndefined();
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeDefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:32]
|
||||
1 │ expect("a string").rejects.not.toBeUndefined();
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:14]
|
||||
1 │ expect(null).toEqual(1 as unknown as string as unknown as any);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:14]
|
||||
1 │ expect(null).toEqual(-1 as unknown as string as unknown as any);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBe` when expecting primitive literals.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toStrictEqual("string" as number);
|
||||
· ─────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:14]
|
||||
1 │ expect(null).toBe(null as unknown as string as unknown as any);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeNull` instead.
|
||||
╭─[prefer_to_be.tsx:1:24]
|
||||
1 │ expect("a string").not.toEqual(null as number);
|
||||
· ───────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:19]
|
||||
1 │ expect(undefined).toBe(undefined as unknown as string as any);
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-jest(prefer-to-be): Use `toBeUndefined` instead.
|
||||
╭─[prefer_to_be.tsx:1:20]
|
||||
1 │ expect("a string").toEqual(undefined as number);
|
||||
· ───────
|
||||
╰────
|
||||
|
|
@ -289,6 +289,13 @@ pub fn get_node_name_vec<'a>(expr: &'a Expression<'a>) -> Vec<Cow<'a, str>> {
|
|||
fn is_pure_string(template_literal: &TemplateLiteral) -> bool {
|
||||
template_literal.expressions.is_empty() && template_literal.quasis.len() == 1
|
||||
}
|
||||
|
||||
pub fn is_equality_matcher(matcher: &KnownMemberExpressionProperty) -> bool {
|
||||
matcher.is_name_equal("toBe")
|
||||
|| matcher.is_name_equal("toEqual")
|
||||
|| matcher.is_name_equal("toStrictEqual")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::LintContext;
|
||||
|
|
|
|||
Loading…
Reference in a new issue