From 720d6373fcee7c04b459d12c786bff2a96c779cf Mon Sep 17 00:00:00 2001 From: Ulrich Stark Date: Sun, 25 May 2025 21:22:03 +0200 Subject: [PATCH 1/2] fix(linter): improve `jest/no-restricted-matchers` --- .../src/rules/jest/no_restricted_matchers.rs | 57 ++++++++++++------- .../jest_no_restricted_matchers.snap | 42 ++++++-------- 2 files changed, 54 insertions(+), 45 deletions(-) diff --git a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs index dc60eab4e7479..1cdfcda9b4b6f 100644 --- a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs +++ b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs @@ -15,17 +15,14 @@ use crate::{ }, }; -// TODO: re-word diagnostic messages -fn restricted_chain(x0: &str, span1: Span) -> OxcDiagnostic { - OxcDiagnostic::warn("Disallow specific matchers & modifiers") - .with_help(format!("Use of `{x0:?}` is disallowed`")) - .with_label(span1) +fn restricted_chain(chain_call: &str, span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Use of `{chain_call}` is disallowed`")).with_label(span) } -fn restricted_chain_with_message(x0: &str, span1: Span) -> OxcDiagnostic { - OxcDiagnostic::warn("Disallow specific matchers & modifiers") - .with_help(format!("{x0:?}")) - .with_label(span1) +fn restricted_chain_with_message(chain_call: &str, message: &str, span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Use of `{chain_call}` is disallowed`")) + .with_help(message.to_string()) + .with_label(span) } #[derive(Debug, Default, Clone)] @@ -51,9 +48,33 @@ declare_oxc_lint!( /// /// ### Examples /// - /// Examples of **incorrect** code for this rule: - /// ```javascript + /// Bans are expressed in the form of a map, with the value being either a string message to be shown, + /// or null if only the default rule message should be used. Bans are checked against the start of + /// the expect chain - this means that to ban a specific matcher entirely you must specify all + /// six permutations, but allows you to ban modifiers as well. By default, this map is empty, meaning + /// no matchers or modifiers are banned. + /// + /// Example configuration: + /// ```json + /// { + /// "jest/no-restricted-matchers": [ + /// "error", + /// { + /// "toBeFalsy": null, + /// "resolves": "Use `expect(await promise)` instead.", + /// "toHaveBeenCalledWith": null, + /// "not.toHaveBeenCalledWith": null, + /// "resolves.toHaveBeenCalledWith": null, + /// "rejects.toHaveBeenCalledWith": null, + /// "resolves.not.toHaveBeenCalledWith": null, + /// "rejects.not.toHaveBeenCalledWith": null + /// } + /// ] + /// } + /// ``` /// + /// Examples of **incorrect** code for this rule with the above configuration: + /// ```javascript /// it('is false', () => { /// // if this has a modifier (i.e. `not.toBeFalsy`), it would be considered fine /// expect(a).toBeFalsy(); @@ -80,15 +101,13 @@ const MODIFIER_NAME: [&str; 3] = ["not", "rejects", "resolves"]; impl Rule for NoRestrictedMatchers { fn from_configuration(value: serde_json::Value) -> Self { - let restricted_matchers = &value + let restricted_matchers = value .get(0) .and_then(serde_json::Value::as_object) .map(Self::compile_restricted_matchers) .unwrap_or_default(); - Self(Box::new(NoRestrictedMatchersConfig { - restricted_matchers: restricted_matchers.clone(), - })) + Self(Box::new(NoRestrictedMatchersConfig { restricted_matchers })) } fn run_on_jest_node<'a, 'c>( @@ -135,7 +154,7 @@ impl NoRestrictedMatchers { if message.is_empty() { ctx.diagnostic(restricted_chain(&chain_call, span)); } else { - ctx.diagnostic(restricted_chain_with_message(message, span)); + ctx.diagnostic(restricted_chain_with_message(&chain_call, message, span)); } } } @@ -145,10 +164,10 @@ impl NoRestrictedMatchers { if MODIFIER_NAME.contains(&restriction) || Path::new(restriction).extension().is_some_and(|ext| ext.eq_ignore_ascii_case("not")) { - return chain_call.starts_with(restriction); + chain_call.starts_with(restriction) + } else { + chain_call == restriction } - - chain_call == restriction } pub fn compile_restricted_matchers( diff --git a/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap b/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap index fd1dabe0811cb..076a78fa28793 100644 --- a/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap +++ b/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap @@ -1,95 +1,85 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).toBe(b) · ──── ╰──── - help: Use of `"toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a)["toBe"](b) · ────── ╰──── - help: Use of `"toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).not[x]() · ─── ╰──── - help: Use of `"not"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).not.toBe(b) · ──────── ╰──── - help: Use of `"not.toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.toBe(b) · ───────────── ╰──── - help: Use of `"resolves.toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.not.toBe(b) · ───────────────── ╰──── - help: Use of `"resolves.not.toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.not.toBe(b) · ───────────────── ╰──── - help: Use of `"resolves.not.toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).not.toBe(b) · ──────── ╰──── - help: Use of `"not.toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.not.toBe(b) · ───────────────── ╰──── - help: Use of `"resolves.not.toBe"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed` ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).toBe(b) · ──── ╰──── - help: "Prefer `toStrictEqual` instead" + help: Prefer `toStrictEqual` instead - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.toBe` is disallowed` ╭─[no_restricted_matchers.tsx:3:54] 2 │ test('some test', async () => { 3 │ await expect(Promise.resolve(1)).resolves.toBe(1); · ───────────── 4 │ }); ╰──── - help: "Use `expect(await promise)` instead." + help: Use `expect(await promise)` instead. - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `rejects.toBeFalsy` is disallowed` ╭─[no_restricted_matchers.tsx:1:29] 1 │ expect(Promise.resolve({})).rejects.toBeFalsy() · ───────────────── ╰──── - help: Use of `"rejects.toBeFalsy"` is disallowed` - ⚠ eslint-plugin-jest(no-restricted-matchers): Disallow specific matchers & modifiers + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toHaveBeenCalledWith` is disallowed` ╭─[no_restricted_matchers.tsx:1:24] 1 │ expect(uploadFileMock).not.toHaveBeenCalledWith('file.name') · ──────────────────────── ╰──── - help: "Use not.toHaveBeenCalled instead" + help: Use not.toHaveBeenCalled instead From fd604494948e470453e76511ba3682613ab72331 Mon Sep 17 00:00:00 2001 From: Ulrich Stark Date: Sun, 25 May 2025 21:33:54 +0200 Subject: [PATCH 2/2] remove leftover backtick --- .../src/rules/jest/no_restricted_matchers.rs | 4 +-- .../jest_no_restricted_matchers.snap | 26 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs index 1cdfcda9b4b6f..315458a9c7fc7 100644 --- a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs +++ b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs @@ -16,11 +16,11 @@ use crate::{ }; fn restricted_chain(chain_call: &str, span: Span) -> OxcDiagnostic { - OxcDiagnostic::warn(format!("Use of `{chain_call}` is disallowed`")).with_label(span) + OxcDiagnostic::warn(format!("Use of `{chain_call}` is disallowed")).with_label(span) } fn restricted_chain_with_message(chain_call: &str, message: &str, span: Span) -> OxcDiagnostic { - OxcDiagnostic::warn(format!("Use of `{chain_call}` is disallowed`")) + OxcDiagnostic::warn(format!("Use of `{chain_call}` is disallowed")) .with_help(message.to_string()) .with_label(span) } diff --git a/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap b/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap index 076a78fa28793..f669596a6ca35 100644 --- a/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap +++ b/crates/oxc_linter/src/snapshots/jest_no_restricted_matchers.snap @@ -1,68 +1,68 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).toBe(b) · ──── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a)["toBe"](b) · ────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).not[x]() · ─── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).not.toBe(b) · ──────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.toBe(b) · ───────────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.not.toBe(b) · ───────────────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.not.toBe(b) · ───────────────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).not.toBe(b) · ──────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.not.toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).resolves.not.toBe(b) · ───────────────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `toBe` is disallowed ╭─[no_restricted_matchers.tsx:1:11] 1 │ expect(a).toBe(b) · ──── ╰──── help: Prefer `toStrictEqual` instead - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.toBe` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `resolves.toBe` is disallowed ╭─[no_restricted_matchers.tsx:3:54] 2 │ test('some test', async () => { 3 │ await expect(Promise.resolve(1)).resolves.toBe(1); @@ -71,13 +71,13 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Use `expect(await promise)` instead. - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `rejects.toBeFalsy` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `rejects.toBeFalsy` is disallowed ╭─[no_restricted_matchers.tsx:1:29] 1 │ expect(Promise.resolve({})).rejects.toBeFalsy() · ───────────────── ╰──── - ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toHaveBeenCalledWith` is disallowed` + ⚠ eslint-plugin-jest(no-restricted-matchers): Use of `not.toHaveBeenCalledWith` is disallowed ╭─[no_restricted_matchers.tsx:1:24] 1 │ expect(uploadFileMock).not.toHaveBeenCalledWith('file.name') · ────────────────────────