Skip to content

Commit

Permalink
feat(linter): add vitest/no-focused-tests rule (#4178)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysteryven committed Jul 11, 2024
1 parent cc7e893 commit fb549e1
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 67 deletions.
13 changes: 9 additions & 4 deletions crates/oxc_linter/src/rules/jest/consistent_test_it.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,24 @@ use crate::{
rule::Rule,
utils::{
collect_possible_jest_call_node, get_test_plugin_name, parse_jest_fn_call, JestFnKind,
JestGeneralFnKind, ParsedJestFnCallNew, PossibleJestNode,
JestGeneralFnKind, ParsedJestFnCallNew, PossibleJestNode, TestPluginName,
},
};

fn consistent_method(x0: &str, x1: &str, x2: &str, span0: Span) -> OxcDiagnostic {
fn consistent_method(x0: TestPluginName, x1: &str, x2: &str, span0: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"{x0}(consistent-test-it): Enforce `test` and `it` usage conventions",
))
.with_help(format!("Prefer using {x1:?} instead of {x2:?}"))
.with_label(span0)
}

fn consistent_method_within_describe(x0: &str, x1: &str, x2: &str, span0: Span) -> OxcDiagnostic {
fn consistent_method_within_describe(
x0: TestPluginName,
x1: &str,
x2: &str,
span0: Span,
) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"{x0}(consistent-test-it): Enforce `test` and `it` usage conventions",
))
Expand Down Expand Up @@ -211,7 +216,7 @@ impl ConsistentTestIt {
fn run<'a>(
&self,
describe_nesting_hash: &mut FxHashMap<ScopeId, i32>,
plugin_name: &str,
plugin_name: TestPluginName,
possible_jest_node: &PossibleJestNode<'a, '_>,
ctx: &LintContext<'a>,
) {
Expand Down
11 changes: 8 additions & 3 deletions crates/oxc_linter/src/rules/jest/no_disabled_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
rule::Rule,
utils::{
collect_possible_jest_call_node, get_test_plugin_name, parse_general_jest_fn_call,
JestFnKind, JestGeneralFnKind, ParsedGeneralJestFnCall, PossibleJestNode,
JestFnKind, JestGeneralFnKind, ParsedGeneralJestFnCall, PossibleJestNode, TestPluginName,
},
};

Expand Down Expand Up @@ -63,7 +63,12 @@ declare_oxc_lint!(
correctness
);

fn no_disabled_tests_diagnostic(x0: &str, x1: &str, x2: &str, span3: Span) -> OxcDiagnostic {
fn no_disabled_tests_diagnostic(
x0: TestPluginName,
x1: &str,
x2: &str,
span3: Span,
) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("{x0}(no-disabled-tests): {x1:?}"))
.with_help(format!("{x2:?}"))
.with_label(span3)
Expand Down Expand Up @@ -103,7 +108,7 @@ impl Rule for NoDisabledTests {

fn run<'a>(
possible_jest_node: &PossibleJestNode<'a, '_>,
plugin_name: &str,
plugin_name: TestPluginName,
ctx: &LintContext<'a>,
) {
let node = possible_jest_node.node;
Expand Down
112 changes: 91 additions & 21 deletions crates/oxc_linter/src/rules/jest/no_focused_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,24 @@ use crate::{
context::LintContext,
rule::Rule,
utils::{
collect_possible_jest_call_node, parse_general_jest_fn_call, JestFnKind, JestGeneralFnKind,
MemberExpressionElement, ParsedGeneralJestFnCall, PossibleJestNode,
collect_possible_jest_call_node, get_test_plugin_name, parse_general_jest_fn_call,
JestFnKind, JestGeneralFnKind, MemberExpressionElement, ParsedGeneralJestFnCall,
PossibleJestNode, TestPluginName,
},
};

fn no_focused_tests_diagnostic(span0: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("eslint-plugin-jest(no-focused-tests): Unexpected focused test.")
.with_help("Remove focus from test.")
.with_label(span0)
fn no_focused_tests_diagnostic(span0: Span, x1: TestPluginName) -> OxcDiagnostic {
match x1 {
TestPluginName::Jest => {
OxcDiagnostic::warn(format!("{x1}(no-focused-tests): Unexpected focused test."))
.with_help("Remove focus from test.")
.with_label(span0)
}
TestPluginName::Vitest => {
OxcDiagnostic::warn(format!("{x1}(no-focused-tests): Focused tests are not allowed."))
.with_label(span0)
}
}
}

#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -49,19 +58,35 @@ declare_oxc_lint!(
/// table
/// `();
/// ```
///
/// This rule is compatible with [eslint-plugin-vitest](https://github.com/veritem/eslint-plugin-vitest/blob/main/docs/rules/no-focused-tests.md),
/// to use it, add the following configuration to your `.eslintrc.json`:
///
/// ```json
/// {
/// "rules": {
/// "vitest/no-focused-tests": "error"
/// }
/// }
/// ```
NoFocusedTests,
correctness
);

impl Rule for NoFocusedTests {
fn run_once(&self, ctx: &LintContext) {
let plugin_name = get_test_plugin_name(ctx);
for node in &collect_possible_jest_call_node(ctx) {
run(node, ctx);
run(node, plugin_name, ctx);
}
}
}

fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) {
fn run<'a>(
possible_jest_node: &PossibleJestNode<'a, '_>,
plugin_name: TestPluginName,
ctx: &LintContext<'a>,
) {
let node = possible_jest_node.node;
let AstKind::CallExpression(call_expr) = node.kind() else {
return;
Expand All @@ -75,30 +100,40 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>)
}

if name.starts_with('f') {
ctx.diagnostic_with_fix(no_focused_tests_diagnostic(call_expr.span), |fixer| {
fixer.delete_range(Span::sized(call_expr.span.start, 1))
});
ctx.diagnostic_with_fix(
no_focused_tests_diagnostic(
Span::new(
call_expr.span.start,
call_expr.span.start + u32::try_from(name.len()).unwrap_or(1),
),
plugin_name,
),
|fixer| fixer.delete_range(Span::sized(call_expr.span.start, 1)),
);

return;
}

let only_node = members.iter().find(|member| member.is_name_equal("only"));
if let Some(only_node) = only_node {
ctx.diagnostic_with_fix(no_focused_tests_diagnostic(call_expr.span), |fixer| {
let mut span = only_node.span.expand_left(1);
if !matches!(only_node.element, MemberExpressionElement::IdentName(_)) {
span = span.expand_right(1);
}
fixer.delete_range(span)
});
ctx.diagnostic_with_fix(
no_focused_tests_diagnostic(only_node.span, plugin_name),
|fixer| {
let mut span = only_node.span.expand_left(1);
if !matches!(only_node.element, MemberExpressionElement::IdentName(_)) {
span = span.expand_right(1);
}
fixer.delete_range(span)
},
);
}
}

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

let pass = vec![
let mut pass = vec![
("describe()", None),
("it()", None),
("describe.skip()", None),
Expand All @@ -114,7 +149,7 @@ fn test() {
("test.concurrent()", None),
];

let fail = vec![
let mut fail = vec![
("describe.only()", None),
// TODO: this need set setting like `settings: { jest: { globalAliases: { describe: ['context'] } } },`
// ("context.only()", None),
Expand All @@ -137,12 +172,47 @@ fn test() {
("fit.each`table`()", None),
];

let fix = vec![
let mut fix = vec![
("describe.only('foo', () => {})", "describe('foo', () => {})", None),
("describe['only']('foo', () => {})", "describe('foo', () => {})", None),
("fdescribe('foo', () => {})", "describe('foo', () => {})", None),
];

let pass_vitest = vec![
(r#"it("test", () => {});"#, None),
(r#"describe("test group", () => {});"#, None),
(r#"it("test", () => {});"#, None),
(r#"describe("test group", () => {});"#, None),
];

let fail_vitest = vec![
(
r#"
import { it } from 'vitest';
it.only("test", () => {});
"#,
None,
),
(r#"describe.only("test", () => {});"#, None),
(r#"test.only("test", () => {});"#, None),
(r#"it.only.each([])("test", () => {});"#, None),
(r#"test.only.each``("test", () => {});"#, None),
(r#"it.only.each``("test", () => {});"#, None),
];

let fix_vitest = vec![
(r#"it.only("test", () => {});"#, r#"it("test", () => {});"#, None),
(r#"describe.only("test", () => {});"#, r#"describe("test", () => {});"#, None),
(r#"test.only("test", () => {});"#, r#"test("test", () => {});"#, None),
(r#"it.only.each([])("test", () => {});"#, r#"it.each([])("test", () => {});"#, None),
(r#"test.only.each``("test", () => {});"#, r#"test.each``("test", () => {});"#, None),
(r#"it.only.each``("test", () => {});"#, r#"it.each``("test", () => {});"#, None),
];

pass.extend(pass_vitest);
fail.extend(fail_vitest);
fix.extend(fix_vitest);

Tester::new(NoFocusedTests::NAME, pass, fail)
.with_jest_plugin(true)
.expect_fix(fix)
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/jest/prefer_hooks_in_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use crate::{
rule::Rule,
utils::{
get_test_plugin_name, parse_jest_fn_call, JestFnKind, JestGeneralFnKind,
ParsedJestFnCallNew, PossibleJestNode,
ParsedJestFnCallNew, PossibleJestNode, TestPluginName,
},
};

fn reorder_hooks(x0: &str, x1: &str, x2: &str, span0: Span) -> OxcDiagnostic {
fn reorder_hooks(x0: TestPluginName, x1: &str, x2: &str, span0: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"{x0}(prefer-hooks-in-order): Prefer having hooks in a consistent order.",
))
Expand Down
Loading

0 comments on commit fb549e1

Please sign in to comment.