diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 22224a641bc94..95ecc79aca949 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -152,6 +152,7 @@ mod jest { pub mod no_test_return_statement; pub mod prefer_called_with; pub mod prefer_equality_matcher; + pub mod prefer_strict_equal; pub mod prefer_todo; pub mod require_to_throw_message; pub mod valid_describe_callback; @@ -448,6 +449,7 @@ oxc_macros::declare_all_lint_rules! { jest::no_test_return_statement, jest::prefer_called_with, jest::prefer_equality_matcher, + jest::prefer_strict_equal, jest::prefer_todo, jest::require_to_throw_message, jest::valid_describe_callback, diff --git a/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs b/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs new file mode 100644 index 0000000000000..db69e1a9ed7ee --- /dev/null +++ b/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs @@ -0,0 +1,123 @@ +use crate::{ + context::LintContext, + fixer::Fix, + rule::Rule, + utils::{collect_possible_jest_call_node, parse_expect_jest_fn_call, PossibleJestNode}, +}; + +use oxc_ast::AstKind; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-jest(prefer-strict-equal): Suggest using `toStrictEqual()`.")] +#[diagnostic(severity(warning), help("Use `toStrictEqual()` instead"))] +struct UseToStrictEqual(#[label] Span); + +#[derive(Debug, Default, Clone)] +pub struct PreferStrictEqual; + +declare_oxc_lint!( + /// ### What it does + /// + /// This rule triggers a warning if `toEqual()` is used to assert equality. + /// + /// ### Example + /// + /// ```javascript + /// // invalid + /// expect({ a: 'a', b: undefined }).toEqual({ a: 'a' }); + /// + /// // valid + /// expect({ a: 'a', b: undefined }).toStrictEqual({ a: 'a' }); + /// ``` + /// + PreferStrictEqual, + style, +); + +impl Rule for PreferStrictEqual { + fn run_once(&self, ctx: &LintContext) { + for possible_jest_node in &collect_possible_jest_call_node(ctx) { + Self::run(possible_jest_node, ctx); + } + } +} + +impl PreferStrictEqual { + 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(parse_jest_expect_fn_call) = + parse_expect_jest_fn_call(call_expr, possible_jest_node, ctx) + else { + return; + }; + let Some(matcher) = parse_jest_expect_fn_call.matcher() else { + return; + }; + let Some(matcher_name) = matcher.name() else { + return; + }; + + if matcher_name.eq("toEqual") { + ctx.diagnostic_with_fix(UseToStrictEqual(matcher.span), || { + let mut formatter = ctx.codegen(); + formatter.print_str( + matcher + .span + .source_text(ctx.source_text()) + .replace(matcher_name.to_string().as_str(), "toStrictEqual") + .as_bytes(), + ); + Fix::new(formatter.into_code(), matcher.span) + }); + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("expect(something).toStrictEqual(somethingElse);", None), + ("a().toEqual('b')", None), + ("expect(a);", None), + ]; + + let fail = vec![ + ("expect(something).toEqual(somethingElse);", None), + ("expect(something).toEqual(somethingElse,);", None), + ("expect(something)[\"toEqual\"](somethingElse);", None), + ]; + + let fix = vec![ + ( + "expect(something).toEqual(somethingElse);", + "expect(something).toStrictEqual(somethingElse);", + None, + ), + ( + "expect(something).toEqual(somethingElse,);", + "expect(something).toStrictEqual(somethingElse,);", + None, + ), + ( + "expect(something)[\"toEqual\"](somethingElse);", + "expect(something)[\"toStrictEqual\"](somethingElse);", + None, + ), + ]; + + Tester::new(PreferStrictEqual::NAME, pass, fail) + .with_jest_plugin(true) + .expect_fix(fix) + .test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/prefer_strict_equal.snap b/crates/oxc_linter/src/snapshots/prefer_strict_equal.snap new file mode 100644 index 0000000000000..633f2582935a4 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/prefer_strict_equal.snap @@ -0,0 +1,25 @@ +--- +source: crates/oxc_linter/src/tester.rs +assertion_line: 151 +expression: prefer_strict_equal +--- + ⚠ eslint-plugin-jest(prefer-strict-equal): Suggest using `toStrictEqual()`. + ╭─[prefer_strict_equal.tsx:1:19] + 1 │ expect(something).toEqual(somethingElse); + · ─────── + ╰──── + help: Use `toStrictEqual()` instead + + ⚠ eslint-plugin-jest(prefer-strict-equal): Suggest using `toStrictEqual()`. + ╭─[prefer_strict_equal.tsx:1:19] + 1 │ expect(something).toEqual(somethingElse,); + · ─────── + ╰──── + help: Use `toStrictEqual()` instead + + ⚠ eslint-plugin-jest(prefer-strict-equal): Suggest using `toStrictEqual()`. + ╭─[prefer_strict_equal.tsx:1:19] + 1 │ expect(something)["toEqual"](somethingElse); + · ───────── + ╰──── + help: Use `toStrictEqual()` instead