From e04e283b74e340d8c388707a2b9de5978687d464 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Mon, 19 Aug 2024 16:18:41 +0800 Subject: [PATCH 1/2] feat(linter/eslint-plugin-vitest): implement no-conditional-in-test --- crates/oxc_linter/src/rules.rs | 2 + .../rules/vitest/no_conditional_in_test.rs | 135 ++++++++++++++++++ .../src/snapshots/no_conditional_in_test.snap | 58 ++++++++ 3 files changed, 195 insertions(+) create mode 100644 crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs create mode 100644 crates/oxc_linter/src/snapshots/no_conditional_in_test.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 3db35d40aba20..73371d3e6e136 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -454,6 +454,7 @@ mod promise { } mod vitest { + pub mod no_conditional_in_test; pub mod no_conditional_tests; pub mod no_import_node_test; pub mod prefer_to_be_falsy; @@ -873,5 +874,6 @@ oxc_macros::declare_all_lint_rules! { vitest::prefer_to_be_falsy, vitest::prefer_to_be_truthy, vitest::no_conditional_tests, + vitest::no_conditional_in_test, vitest::require_local_test_context_for_concurrent_snapshots, } diff --git a/crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs b/crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs new file mode 100644 index 0000000000000..751f00d06a251 --- /dev/null +++ b/crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs @@ -0,0 +1,135 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{ + context::LintContext, + rule::Rule, + utils::{is_type_of_jest_fn_call, JestFnKind, JestGeneralFnKind, PossibleJestNode}, +}; + +fn no_conditional_in_test(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Avoid having conditionals in test") + .with_help("Remove the surrounding if statement.") + .with_label(span0) +} + +#[derive(Debug, Default, Clone)] +pub struct NoConditionalInTest; + +declare_oxc_lint!( + /// ### What it does + /// This rule aims to prevent conditional tests. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// test('my test', () => { + /// if (true) { + /// doTheThing() + /// } + /// }) + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// test('my test', () => { + /// expect(true).toBe(true) + /// }) + /// ``` + NoConditionalInTest, + pedantic, +); + +impl Rule for NoConditionalInTest { + fn run<'a>(&self, node: &oxc_semantic::AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::IfStatement(if_statement) = node.kind() { + let is_if_statement_in_test = ctx.nodes().iter_parents(node.id()).any(|node| { + let AstKind::CallExpression(call_expr) = node.kind() else { return false }; + let vitest_node = PossibleJestNode { node, original: None }; + + is_type_of_jest_fn_call( + call_expr, + &vitest_node, + ctx, + &[JestFnKind::General(JestGeneralFnKind::Test)], + ) + }); + + if is_if_statement_in_test { + ctx.diagnostic(no_conditional_in_test(if_statement.span)); + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "const x = y ? 1 : 0", + "const foo = function (bar) { + return foo ? bar : null; + }; + it('foo', () => { + foo(); + });", + "it.concurrent('foo', () => { + switch('bar') {} + })", + "if (foo) {}", + "it('foo', () => {})", + r#"it("foo", function () {})"#, + "it('foo', () => {}); function myTest() { if ('bar') {} }", + "describe.each``('foo', () => { + afterEach(() => { + if ('bar') {} + }); + })", + "const values = something.map((thing) => { + if (thing.isFoo) { + return thing.foo + } else { + return thing.bar; + } + }); + + describe('valid', () => { + it('still valid', () => { + expect(values).toStrictEqual(['foo']); + }); + });", + ]; + + let fail = vec![ + "it('foo', function () { + if('bar') {} + });", + " describe('foo', () => { + it('bar', () => { + if ('bar') {} + }) + it('baz', () => { + if ('qux') {} + if ('quux') {} + }) + })", + r#"test("shows error", () => { + if (1 === 2) { + expect(true).toBe(false); + } + }); + + test("does not show error", () => { + setTimeout(() => console.log("noop")); + if (1 === 2) { + expect(true).toBe(false); + } + });"#, + ]; + + Tester::new(NoConditionalInTest::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_conditional_in_test.snap b/crates/oxc_linter/src/snapshots/no_conditional_in_test.snap new file mode 100644 index 0000000000000..b96aa4275d14d --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_conditional_in_test.snap @@ -0,0 +1,58 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test + ╭─[no_conditional_in_test.tsx:2:10] + 1 │ it('foo', function () { + 2 │ if('bar') {} + · ──────────── + 3 │ }); + ╰──── + help: Remove the surrounding if statement. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test + ╭─[no_conditional_in_test.tsx:3:12] + 2 │ it('bar', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Remove the surrounding if statement. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test + ╭─[no_conditional_in_test.tsx:6:12] + 5 │ it('baz', () => { + 6 │ if ('qux') {} + · ───────────── + 7 │ if ('quux') {} + ╰──── + help: Remove the surrounding if statement. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test + ╭─[no_conditional_in_test.tsx:7:12] + 6 │ if ('qux') {} + 7 │ if ('quux') {} + · ────────────── + 8 │ }) + ╰──── + help: Remove the surrounding if statement. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test + ╭─[no_conditional_in_test.tsx:2:10] + 1 │ test("shows error", () => { + 2 │ ╭─▶ if (1 === 2) { + 3 │ │ expect(true).toBe(false); + 4 │ ╰─▶ } + 5 │ }); + ╰──── + help: Remove the surrounding if statement. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test + ╭─[no_conditional_in_test.tsx:9:10] + 8 │ setTimeout(() => console.log("noop")); + 9 │ ╭─▶ if (1 === 2) { + 10 │ │ expect(true).toBe(false); + 11 │ ╰─▶ } + 12 │ }); + ╰──── + help: Remove the surrounding if statement. From 7cf50f57c938ad60850cb297dc6034ae1c926423 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Wed, 21 Aug 2024 14:53:22 +0800 Subject: [PATCH 2/2] chore: follow the suggestion --- crates/oxc_linter/src/rules.rs | 4 +- .../src/rules/jest/no_conditional_in_test.rs | 648 ++++++++++++++++++ .../rules/vitest/no_conditional_in_test.rs | 135 ---- .../src/snapshots/no_conditional_in_test.snap | 525 ++++++++++++-- crates/oxc_linter/src/utils/mod.rs | 1 + 5 files changed, 1122 insertions(+), 191 deletions(-) create mode 100644 crates/oxc_linter/src/rules/jest/no_conditional_in_test.rs delete mode 100644 crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 73371d3e6e136..e21dc30e2899d 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -175,6 +175,7 @@ mod jest { pub mod no_alias_methods; pub mod no_commented_out_tests; pub mod no_conditional_expect; + pub mod no_conditional_in_test; pub mod no_confusing_set_timeout; pub mod no_deprecated_functions; pub mod no_disabled_tests; @@ -454,7 +455,6 @@ mod promise { } mod vitest { - pub mod no_conditional_in_test; pub mod no_conditional_tests; pub mod no_import_node_test; pub mod prefer_to_be_falsy; @@ -607,6 +607,7 @@ oxc_macros::declare_all_lint_rules! { jest::no_alias_methods, jest::no_commented_out_tests, jest::no_conditional_expect, + jest::no_conditional_in_test, jest::no_confusing_set_timeout, jest::no_deprecated_functions, jest::no_disabled_tests, @@ -874,6 +875,5 @@ oxc_macros::declare_all_lint_rules! { vitest::prefer_to_be_falsy, vitest::prefer_to_be_truthy, vitest::no_conditional_tests, - vitest::no_conditional_in_test, vitest::require_local_test_context_for_concurrent_snapshots, } diff --git a/crates/oxc_linter/src/rules/jest/no_conditional_in_test.rs b/crates/oxc_linter/src/rules/jest/no_conditional_in_test.rs new file mode 100644 index 0000000000000..36cc76ad0b0f1 --- /dev/null +++ b/crates/oxc_linter/src/rules/jest/no_conditional_in_test.rs @@ -0,0 +1,648 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{ + context::LintContext, + rule::Rule, + utils::{is_type_of_jest_fn_call, JestFnKind, PossibleJestNode}, +}; + +fn no_conditional_in_test(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Disallow conditional logic in tests") + .with_help("Avoid having conditionals in tests.") + .with_label(span0) +} + +#[derive(Debug, Default, Clone)] +pub struct NoConditionalInTest; + +declare_oxc_lint!( + /// ### What it does + /// This rule reports on any use of a conditional statement such as if, switch, and ternary expressions. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// it('foo', () => { + /// if (true) { + /// doTheThing(); + /// } + /// }); + /// + /// it('bar', () => { + /// switch (mode) { + /// case 'none': + /// generateNone(); + /// case 'single': + /// generateOne(); + /// case 'multiple': + /// generateMany(); + /// } + /// + /// expect(fixtures.length).toBeGreaterThan(-1); + /// }); + /// + /// it('baz', async () => { + /// const promiseValue = () => { + /// return something instanceof Promise + /// ? something + /// : Promise.resolve(something); + /// }; + /// + /// await expect(promiseValue()).resolves.toBe(1); + /// }); + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// describe('my tests', () => { + /// if (true) { + /// it('foo', () => { + /// doTheThing(); + /// }); + /// } + /// }); + /// + /// beforeEach(() => { + /// switch (mode) { + /// case 'none': + /// generateNone(); + /// case 'single': + /// generateOne(); + /// case 'multiple': + /// generateMany(); + /// } + /// }); + /// + /// it('bar', () => { + /// expect(fixtures.length).toBeGreaterThan(-1); + /// }); + /// + /// const promiseValue = something => { + /// return something instanceof Promise ? something : Promise.resolve(something); + /// }; + /// + /// it('baz', async () => { + /// await expect(promiseValue()).resolves.toBe(1); + /// }); + /// ``` + NoConditionalInTest, + pedantic, +); + +impl Rule for NoConditionalInTest { + fn run<'a>(&self, node: &oxc_semantic::AstNode<'a>, ctx: &LintContext<'a>) { + if matches!( + node.kind(), + AstKind::IfStatement(_) + | AstKind::SwitchStatement(_) + | AstKind::ConditionalExpression(_) + | AstKind::LogicalExpression(_) + ) { + let is_if_statement_in_test = ctx.nodes().iter_parents(node.id()).any(|node| { + let AstKind::CallExpression(call_expr) = node.kind() else { return false }; + let vitest_node = PossibleJestNode { node, original: None }; + + is_type_of_jest_fn_call( + call_expr, + &vitest_node, + ctx, + &[JestFnKind::General(crate::utils::JestGeneralFnKind::Test)], + ) + }); + + if is_if_statement_in_test { + let span = match node.kind() { + AstKind::IfStatement(stmt) => stmt.span, + AstKind::SwitchStatement(stmt) => stmt.span, + AstKind::ConditionalExpression(expr) => expr.span, + AstKind::LogicalExpression(expr) => expr.span, + _ => unreachable!(), + }; + + ctx.diagnostic(no_conditional_in_test(span)); + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "const x = y ? 1 : 0", + " + const foo = function (bar) { + return foo ? bar : null; + }; + + it('foo', () => { + foo(); + }); + ", + " + const foo = function (bar) { + return foo ? bar : null; + }; + + it.each()('foo', function () { + foo(); + }); + ", + " + fit.concurrent('foo', () => { + switch('bar') {} + }) + ", + "it('foo', () => {})", + " + switch (true) { + case true: {} + } + ", + " + it('foo', () => {}); + function myTest() { + switch ('bar') { + } + } + ", + " + foo('bar', () => { + switch(baz) {} + }) + ", + " + describe('foo', () => { + switch('bar') {} + }) + ", + " + describe.skip('foo', () => { + switch('bar') {} + }) + ", + " + describe.skip.each()('foo', () => { + switch('bar') {} + }) + ", + " + xdescribe('foo', () => { + switch('bar') {} + }) + ", + " + fdescribe('foo', () => { + switch('bar') {} + }) + ", + " + describe('foo', () => { + switch('bar') {} + }) + switch('bar') {} + ", + " + describe('foo', () => { + afterEach(() => { + switch('bar') {} + }); + }); + ", + " + const values = something.map(thing => { + switch (thing.isFoo) { + case true: + return thing.foo; + default: + return thing.bar; + } + }); + + it('valid', () => { + expect(values).toStrictEqual(['foo']); + }); + ", + " + describe('valid', () => { + const values = something.map(thing => { + switch (thing.isFoo) { + case true: + return thing.foo; + default: + return thing.bar; + } + }); + it('still valid', () => { + expect(values).toStrictEqual(['foo']); + }); + }); + ", + "if (foo) {}", + "it('foo', () => {})", + r#"it("foo", function () {})"#, + "it('foo', () => {}); function myTest() { if ('bar') {} }", + " + foo('bar', () => { + if (baz) {} + }) + ", + " + describe('foo', () => { + if ('bar') {} + }) + ", + " + describe.skip('foo', () => { + if ('bar') {} + }) + ", + " + xdescribe('foo', () => { + if ('bar') {} + }) + ", + " + fdescribe('foo', () => { + if ('bar') {} + }) + ", + " + describe('foo', () => { + if ('bar') {} + }) + if ('baz') {} + ", + " + describe('foo', () => { + afterEach(() => { + if ('bar') {} + }); + }) + ", + " + describe.each``('foo', () => { + afterEach(() => { + if ('bar') {} + }); + }) + ", + " + describe('foo', () => { + beforeEach(() => { + if ('bar') {} + }); + }) + ", + "const foo = bar ? foo : baz;", + " + const values = something.map((thing) => { + if (thing.isFoo) { + return thing.foo + } else { + return thing.bar; + } + }); + + describe('valid', () => { + it('still valid', () => { + expect(values).toStrictEqual(['foo']); + }); + }); + ", + " + describe('valid', () => { + const values = something.map((thing) => { + if (thing.isFoo) { + return thing.foo + } else { + return thing.bar; + } + }); + + describe('still valid', () => { + it('really still valid', () => { + expect(values).toStrictEqual(['foo']); + }); + }); + }); + ", + " + fit.concurrent('foo', () => { + if ('bar') {} + }) + ", + ]; + + let fail = vec![ + " + it('foo', () => { + expect(bar ? foo : baz).toBe(boo); + }) + ", + " + it('foo', function () { + const foo = function (bar) { + return foo ? bar : null; + }; + }); + ", + " + it('foo', () => { + const foo = bar ? foo : baz; + }) + ", + " + it('foo', () => { + const foo = bar ? foo : baz; + }) + const foo = bar ? foo : baz; + ", + " + it('foo', () => { + const foo = bar ? foo : baz; + const anotherFoo = anotherBar ? anotherFoo : anotherBaz; + }) + ", + " + it('is invalid', () => { + const values = something.map(thing => { + switch (thing.isFoo) { + case true: + return thing.foo; + default: + return thing.bar; + } + }); + + expect(values).toStrictEqual(['foo']); + }); + ", + " + it('foo', () => { + switch (true) { + case true: {} + } + }) + ", + " + it('foo', () => { + switch('bar') {} + }) + ", + " + it.skip('foo', () => { + switch('bar') {} + }) + ", + " + it.only('foo', () => { + switch('bar') {} + }) + ", + " + xit('foo', () => { + switch('bar') {} + }) + ", + " + fit('foo', () => { + switch('bar') {} + }) + ", + " + test('foo', () => { + switch('bar') {} + }) + ", + " + test.skip('foo', () => { + switch('bar') {} + }) + ", + " + test.only('foo', () => { + switch('bar') {} + }) + ", + " + xtest('foo', () => { + switch('bar') {} + }) + ", + " + xtest('foo', function () { + switch('bar') {} + }) + ", + " + describe('foo', () => { + it('bar', () => { + + switch('bar') {} + }) + }) + ", + " + describe('foo', () => { + it('bar', () => { + switch('bar') {} + }) + it('baz', () => { + switch('qux') {} + switch('quux') {} + }) + }) + ", + " + it('foo', () => { + callExpression() + switch ('bar') {} + }) + ", + " + describe('valid', () => { + describe('still valid', () => { + it('is not valid', () => { + const values = something.map((thing) => { + switch (thing.isFoo) { + case true: + return thing.foo; + default: + return thing.bar; + } + }); + + switch('invalid') { + case true: + expect(values).toStrictEqual(['foo']); + } + }); + }); + }); + ", + " + it('foo', () => { + const foo = function(bar) { + if (bar) { + return 1; + } else { + return 2; + } + }; + }); + ", + " + it('foo', () => { + function foo(bar) { + if (bar) { + return 1; + } else { + return 2; + } + }; + }); + ", + " + it('foo', () => { + if ('bar') {} + }) + ", + " + it.skip('foo', () => { + if ('bar') {} + }) + ", + " + it.skip('foo', function () { + if ('bar') {} + }) + ", + " + it.only('foo', () => { + if ('bar') {} + }) + ", + " + xit('foo', () => { + if ('bar') {} + }) + ", + " + fit('foo', () => { + if ('bar') {} + }) + ", + " + test('foo', () => { + if ('bar') {} + }) + ", + " + test.skip('foo', () => { + if ('bar') {} + }) + ", + " + test.only('foo', () => { + if ('bar') {} + }) + ", + " + xtest('foo', () => { + if ('bar') {} + }) + ", + " + describe('foo', () => { + it('bar', () => { + if ('bar') {} + }) + }) + ", + " + describe('foo', () => { + it('bar', () => { + if ('bar') {} + }) + it('baz', () => { + if ('qux') {} + if ('quux') {} + }) + }) + ", + " + it('foo', () => { + callExpression() + if ('bar') {} + }) + ", + " + it.each``('foo', () => { + callExpression() + if ('bar') {} + }) + ", + " + it.each()('foo', () => { + callExpression() + if ('bar') {} + }) + ", + " + it.only.each``('foo', () => { + callExpression() + if ('bar') {} + }) + ", + " + it.only.each()('foo', () => { + callExpression() + if ('bar') {} + }) + ", + " + describe('valid', () => { + describe('still valid', () => { + it('is invalid', () => { + const values = something.map((thing) => { + if (thing.isFoo) { + return thing.foo + } else { + return thing.bar; + } + }); + + if ('invalid') { + expect(values).toStrictEqual(['foo']); + } + }); + }); + }); + ", + r#" + test("shows error", () => { + if (1 === 2) { + expect(true).toBe(false); + } + }); + + test("does not show error", () => { + setTimeout(() => console.log("noop")); + if (1 === 2) { + expect(true).toBe(false); + } + }); + "#, + ]; + + Tester::new(NoConditionalInTest::NAME, pass, fail) + .with_jest_plugin(true) + .with_vitest_plugin(true) + .test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs b/crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs deleted file mode 100644 index 751f00d06a251..0000000000000 --- a/crates/oxc_linter/src/rules/vitest/no_conditional_in_test.rs +++ /dev/null @@ -1,135 +0,0 @@ -use oxc_ast::AstKind; -use oxc_diagnostics::OxcDiagnostic; -use oxc_macros::declare_oxc_lint; -use oxc_span::Span; - -use crate::{ - context::LintContext, - rule::Rule, - utils::{is_type_of_jest_fn_call, JestFnKind, JestGeneralFnKind, PossibleJestNode}, -}; - -fn no_conditional_in_test(span0: Span) -> OxcDiagnostic { - OxcDiagnostic::warn("Avoid having conditionals in test") - .with_help("Remove the surrounding if statement.") - .with_label(span0) -} - -#[derive(Debug, Default, Clone)] -pub struct NoConditionalInTest; - -declare_oxc_lint!( - /// ### What it does - /// This rule aims to prevent conditional tests. - /// - /// ### Examples - /// - /// Examples of **incorrect** code for this rule: - /// ```js - /// test('my test', () => { - /// if (true) { - /// doTheThing() - /// } - /// }) - /// ``` - /// - /// Examples of **correct** code for this rule: - /// ```js - /// test('my test', () => { - /// expect(true).toBe(true) - /// }) - /// ``` - NoConditionalInTest, - pedantic, -); - -impl Rule for NoConditionalInTest { - fn run<'a>(&self, node: &oxc_semantic::AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::IfStatement(if_statement) = node.kind() { - let is_if_statement_in_test = ctx.nodes().iter_parents(node.id()).any(|node| { - let AstKind::CallExpression(call_expr) = node.kind() else { return false }; - let vitest_node = PossibleJestNode { node, original: None }; - - is_type_of_jest_fn_call( - call_expr, - &vitest_node, - ctx, - &[JestFnKind::General(JestGeneralFnKind::Test)], - ) - }); - - if is_if_statement_in_test { - ctx.diagnostic(no_conditional_in_test(if_statement.span)); - } - } - } -} - -#[test] -fn test() { - use crate::tester::Tester; - - let pass = vec![ - "const x = y ? 1 : 0", - "const foo = function (bar) { - return foo ? bar : null; - }; - it('foo', () => { - foo(); - });", - "it.concurrent('foo', () => { - switch('bar') {} - })", - "if (foo) {}", - "it('foo', () => {})", - r#"it("foo", function () {})"#, - "it('foo', () => {}); function myTest() { if ('bar') {} }", - "describe.each``('foo', () => { - afterEach(() => { - if ('bar') {} - }); - })", - "const values = something.map((thing) => { - if (thing.isFoo) { - return thing.foo - } else { - return thing.bar; - } - }); - - describe('valid', () => { - it('still valid', () => { - expect(values).toStrictEqual(['foo']); - }); - });", - ]; - - let fail = vec![ - "it('foo', function () { - if('bar') {} - });", - " describe('foo', () => { - it('bar', () => { - if ('bar') {} - }) - it('baz', () => { - if ('qux') {} - if ('quux') {} - }) - })", - r#"test("shows error", () => { - if (1 === 2) { - expect(true).toBe(false); - } - }); - - test("does not show error", () => { - setTimeout(() => console.log("noop")); - if (1 === 2) { - expect(true).toBe(false); - } - });"#, - ]; - - Tester::new(NoConditionalInTest::NAME, pass, fail).test_and_snapshot(); -} diff --git a/crates/oxc_linter/src/snapshots/no_conditional_in_test.snap b/crates/oxc_linter/src/snapshots/no_conditional_in_test.snap index b96aa4275d14d..48c9890f84293 100644 --- a/crates/oxc_linter/src/snapshots/no_conditional_in_test.snap +++ b/crates/oxc_linter/src/snapshots/no_conditional_in_test.snap @@ -1,58 +1,475 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test - ╭─[no_conditional_in_test.tsx:2:10] - 1 │ it('foo', function () { - 2 │ if('bar') {} - · ──────────── - 3 │ }); - ╰──── - help: Remove the surrounding if statement. - - ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test - ╭─[no_conditional_in_test.tsx:3:12] - 2 │ it('bar', () => { - 3 │ if ('bar') {} - · ───────────── - 4 │ }) - ╰──── - help: Remove the surrounding if statement. - - ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test - ╭─[no_conditional_in_test.tsx:6:12] - 5 │ it('baz', () => { - 6 │ if ('qux') {} - · ───────────── - 7 │ if ('quux') {} - ╰──── - help: Remove the surrounding if statement. - - ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test - ╭─[no_conditional_in_test.tsx:7:12] - 6 │ if ('qux') {} - 7 │ if ('quux') {} - · ────────────── - 8 │ }) - ╰──── - help: Remove the surrounding if statement. - - ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test - ╭─[no_conditional_in_test.tsx:2:10] - 1 │ test("shows error", () => { - 2 │ ╭─▶ if (1 === 2) { - 3 │ │ expect(true).toBe(false); - 4 │ ╰─▶ } - 5 │ }); - ╰──── - help: Remove the surrounding if statement. - - ⚠ eslint-plugin-vitest(no-conditional-in-test): Avoid having conditionals in test - ╭─[no_conditional_in_test.tsx:9:10] - 8 │ setTimeout(() => console.log("noop")); - 9 │ ╭─▶ if (1 === 2) { - 10 │ │ expect(true).toBe(false); - 11 │ ╰─▶ } - 12 │ }); + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:21] + 2 │ it('foo', () => { + 3 │ expect(bar ? foo : baz).toBe(boo); + · ─────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:23] + 3 │ const foo = function (bar) { + 4 │ return foo ? bar : null; + · ──────────────── + 5 │ }; + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:26] + 2 │ it('foo', () => { + 3 │ const foo = bar ? foo : baz; + · ─────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:26] + 2 │ it('foo', () => { + 3 │ const foo = bar ? foo : baz; + · ─────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:26] + 2 │ it('foo', () => { + 3 │ const foo = bar ? foo : baz; + · ─────────────── + 4 │ const anotherFoo = anotherBar ? anotherFoo : anotherBaz; + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:33] + 3 │ const foo = bar ? foo : baz; + 4 │ const anotherFoo = anotherBar ? anotherFoo : anotherBaz; + · ──────────────────────────────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:16] + 3 │ const values = something.map(thing => { + 4 │ ╭─▶ switch (thing.isFoo) { + 5 │ │ case true: + 6 │ │ return thing.foo; + 7 │ │ default: + 8 │ │ return thing.bar; + 9 │ ╰─▶ } + 10 │ }); + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it('foo', () => { + 3 │ ╭─▶ switch (true) { + 4 │ │ case true: {} + 5 │ ╰─▶ } + 6 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it.skip('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it.only('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ xit('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ fit('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ test('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ test.skip('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ test.only('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ xtest('foo', () => { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ xtest('foo', function () { + 3 │ switch('bar') {} + · ──────────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:5:16] + 4 │ + 5 │ switch('bar') {} + · ──────────────── + 6 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:16] + 3 │ it('bar', () => { + 4 │ switch('bar') {} + · ──────────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:7:16] + 6 │ it('baz', () => { + 7 │ switch('qux') {} + · ──────────────── + 8 │ switch('quux') {} + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:8:16] + 7 │ switch('qux') {} + 8 │ switch('quux') {} + · ───────────────── + 9 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:14] + 3 │ callExpression() + 4 │ switch ('bar') {} + · ───────────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:6:20] + 5 │ const values = something.map((thing) => { + 6 │ ╭─▶ switch (thing.isFoo) { + 7 │ │ case true: + 8 │ │ return thing.foo; + 9 │ │ default: + 10 │ │ return thing.bar; + 11 │ ╰─▶ } + 12 │ }); + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:14:18] + 13 │ + 14 │ ╭─▶ switch('invalid') { + 15 │ │ case true: + 16 │ │ expect(values).toStrictEqual(['foo']); + 17 │ ╰─▶ } + 18 │ }); + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:16] + 3 │ const foo = function(bar) { + 4 │ ╭─▶ if (bar) { + 5 │ │ return 1; + 6 │ │ } else { + 7 │ │ return 2; + 8 │ ╰─▶ } + 9 │ }; + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:16] + 3 │ function foo(bar) { + 4 │ ╭─▶ if (bar) { + 5 │ │ return 1; + 6 │ │ } else { + 7 │ │ return 2; + 8 │ ╰─▶ } + 9 │ }; + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it.skip('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it.skip('foo', function () { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ it.only('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ xit('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ fit('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ test('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ test.skip('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ test.only('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ xtest('foo', () => { + 3 │ if ('bar') {} + · ───────────── + 4 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:16] + 3 │ it('bar', () => { + 4 │ if ('bar') {} + · ───────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:16] + 3 │ it('bar', () => { + 4 │ if ('bar') {} + · ───────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:7:16] + 6 │ it('baz', () => { + 7 │ if ('qux') {} + · ───────────── + 8 │ if ('quux') {} + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:8:16] + 7 │ if ('qux') {} + 8 │ if ('quux') {} + · ────────────── + 9 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:14] + 3 │ callExpression() + 4 │ if ('bar') {} + · ───────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:14] + 3 │ callExpression() + 4 │ if ('bar') {} + · ───────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:14] + 3 │ callExpression() + 4 │ if ('bar') {} + · ───────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:14] + 3 │ callExpression() + 4 │ if ('bar') {} + · ───────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:4:14] + 3 │ callExpression() + 4 │ if ('bar') {} + · ───────────── + 5 │ }) + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:6:20] + 5 │ const values = something.map((thing) => { + 6 │ ╭─▶ if (thing.isFoo) { + 7 │ │ return thing.foo + 8 │ │ } else { + 9 │ │ return thing.bar; + 10 │ ╰─▶ } + 11 │ }); + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:13:18] + 12 │ + 13 │ ╭─▶ if ('invalid') { + 14 │ │ expect(values).toStrictEqual(['foo']); + 15 │ ╰─▶ } + 16 │ }); + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:3:14] + 2 │ test("shows error", () => { + 3 │ ╭─▶ if (1 === 2) { + 4 │ │ expect(true).toBe(false); + 5 │ ╰─▶ } + 6 │ }); + ╰──── + help: Avoid having conditionals in tests. + + ⚠ eslint-plugin-vitest(no-conditional-in-test): Disallow conditional logic in tests + ╭─[no_conditional_in_test.tsx:10:14] + 9 │ setTimeout(() => console.log("noop")); + 10 │ ╭─▶ if (1 === 2) { + 11 │ │ expect(true).toBe(false); + 12 │ ╰─▶ } + 13 │ }); ╰──── - help: Remove the surrounding if statement. + help: Avoid having conditionals in tests. diff --git a/crates/oxc_linter/src/utils/mod.rs b/crates/oxc_linter/src/utils/mod.rs index 5a9586f78015b..485fc7d625f7c 100644 --- a/crates/oxc_linter/src/utils/mod.rs +++ b/crates/oxc_linter/src/utils/mod.rs @@ -23,6 +23,7 @@ pub fn is_jest_rule_adapted_to_vitest(rule_name: &str) -> bool { "expect-expect", "no-alias-methods", "no-conditional-expect", + "no-conditional-in-test", "no-commented-out-tests", "no-disabled-tests", "no-focused-tests",