Skip to content

Commit

Permalink
feat(linter/eslint-plugin-vitest): implement no-conditional-tests (#4955
Browse files Browse the repository at this point in the history
)
  • Loading branch information
shulaoda committed Aug 19, 2024
1 parent 6800e69 commit 7859f58
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ mod promise {
}

mod vitest {
pub mod no_conditional_tests;
pub mod no_import_node_test;
pub mod prefer_to_be_falsy;
pub mod prefer_to_be_truthy;
Expand Down Expand Up @@ -866,4 +867,5 @@ oxc_macros::declare_all_lint_rules! {
vitest::no_import_node_test,
vitest::prefer_to_be_falsy,
vitest::prefer_to_be_truthy,
vitest::no_conditional_tests,
}
139 changes: 139 additions & 0 deletions crates/oxc_linter/src/rules/vitest/no_conditional_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
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::{
collect_possible_jest_call_node, is_type_of_jest_fn_call, JestFnKind, JestGeneralFnKind,
PossibleJestNode,
},
};

fn no_conditional_tests(span0: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Avoid having conditionals in tests")
.with_help("Remove the surrounding if statement.")
.with_label(span0)
}

#[derive(Debug, Default, Clone)]
pub struct NoConditionalTests;

declare_oxc_lint!(
/// ### What it does
/// The rule disallows the use of conditional statements within test cases to ensure that tests are deterministic and clearly readable.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```js
/// describe('my tests', () => {
/// if (true) {
/// it('is awesome', () => {
/// doTheThing()
/// })
/// }
/// })
/// ```
///
/// Examples of **correct** code for this rule:
/// ```js
/// describe('my tests', () => {
/// it('is awesome', () => {
/// doTheThing()
/// })
/// })
/// ```
NoConditionalTests,
correctness,
);

impl Rule for NoConditionalTests {
fn run_once(&self, ctx: &LintContext) {
for node in &collect_possible_jest_call_node(ctx) {
run(node, ctx);
}
}
}

fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) {
let node = possible_jest_node.node;
if let AstKind::CallExpression(call_expr) = node.kind() {
if is_type_of_jest_fn_call(
call_expr,
possible_jest_node,
ctx,
&[
JestFnKind::General(JestGeneralFnKind::Describe),
JestFnKind::General(JestGeneralFnKind::Test),
],
) {
let if_statement_node = ctx
.nodes()
.iter_parents(node.id())
.find(|node| matches!(node.kind(), AstKind::IfStatement(_)));

let Some(node) = if_statement_node else { return };

if let AstKind::IfStatement(if_statement) = node.kind() {
ctx.diagnostic(no_conditional_tests(if_statement.span));
}
}
}
}

#[test]
fn test() {
use crate::tester::Tester;

let pass = vec![
r#"test("shows error", () => {});"#,
r#"it("foo", function () {})"#,
"it('foo', () => {}); function myTest() { if ('bar') {} }",
r#"function myFunc(str: string) {
return str;
}
describe("myTest", () => {
it("convert shortened equal filter", () => {
expect(
myFunc("5")
).toEqual("5");
});
});"#,
r#"describe("shows error", () => {
if (1 === 2) {
myFunc();
}
expect(true).toBe(false);
});"#,
];

let fail = vec![
r#"describe("shows error", () => {
if(true) {
test("shows error", () => {
expect(true).toBe(true);
})
}
})"#,
r#"
describe("shows error", () => {
if(true) {
it("shows error", () => {
expect(true).toBe(true);
})
}
})"#,
r#"describe("errors", () => {
if (Math.random() > 0) {
test("test2", () => {
expect(true).toBeTruthy();
});
}
});"#,
];

Tester::new(NoConditionalTests::NAME, pass, fail).test_and_snapshot();
}
38 changes: 38 additions & 0 deletions crates/oxc_linter/src/snapshots/no_conditional_tests.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
source: crates/oxc_linter/src/tester.rs
---
eslint-plugin-vitest(no-conditional-tests): Avoid having conditionals in tests
╭─[no_conditional_tests.tsx:2:8]
1describe("shows error", () => {
2 │ ╭─▶ if(true) {
3 │ │ test("shows error", () => {
4 │ │ expect(true).toBe(true);
5 │ │ })
6 │ ╰─▶ }
7 │ })
╰────
help: Remove the surrounding if statement.

eslint-plugin-vitest(no-conditional-tests): Avoid having conditionals in tests
╭─[no_conditional_tests.tsx:3:8]
2describe("shows error", () => {
3 │ ╭─▶ if(true) {
4 │ │ it("shows error", () => {
5 │ │ expect(true).toBe(true);
6 │ │ })
7 │ ╰─▶ }
8 │ })
╰────
help: Remove the surrounding if statement.

eslint-plugin-vitest(no-conditional-tests): Avoid having conditionals in tests
╭─[no_conditional_tests.tsx:2:8]
1describe("errors", () => {
2 │ ╭─▶ if (Math.random() > 0) {
3 │ │ test("test2", () => {
4 │ │ expect(true).toBeTruthy();
5 │ │ });
6 │ ╰─▶ }
7 │ });
╰────
help: Remove the surrounding if statement.

0 comments on commit 7859f58

Please sign in to comment.