mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter/eslint-plugin-vitest): implement require-local-test-context-for-concurrent-snapshots (#4951)
Related to #4656 --------- Co-authored-by: Wang Wenzhe <mysteryven@gmail.com>
This commit is contained in:
parent
8d3f61bb54
commit
ed9a1c499b
3 changed files with 259 additions and 0 deletions
|
|
@ -457,6 +457,7 @@ mod vitest {
|
|||
pub mod no_import_node_test;
|
||||
pub mod prefer_to_be_falsy;
|
||||
pub mod prefer_to_be_truthy;
|
||||
pub mod require_local_test_context_for_concurrent_snapshots;
|
||||
}
|
||||
|
||||
oxc_macros::declare_all_lint_rules! {
|
||||
|
|
@ -870,4 +871,5 @@ oxc_macros::declare_all_lint_rules! {
|
|||
vitest::prefer_to_be_falsy,
|
||||
vitest::prefer_to_be_truthy,
|
||||
vitest::no_conditional_tests,
|
||||
vitest::require_local_test_context_for_concurrent_snapshots,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,172 @@
|
|||
use oxc_ast::{ast::MemberExpression, AstKind};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
rule::Rule,
|
||||
utils::{
|
||||
collect_possible_jest_call_node, is_type_of_jest_fn_call, JestFnKind, JestGeneralFnKind,
|
||||
PossibleJestNode,
|
||||
},
|
||||
};
|
||||
|
||||
#[inline]
|
||||
fn is_snapshot_method(property_name: &str) -> bool {
|
||||
matches!(
|
||||
property_name,
|
||||
"toMatchSnapshot"
|
||||
| "toMatchInlineSnapshot"
|
||||
| "toMatchFileSnapshot"
|
||||
| "toThrowErrorMatchingSnapshot"
|
||||
| "toThrowErrorMatchingInlineSnapshot"
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_test_or_describe_node(member_expr: &MemberExpression) -> bool {
|
||||
if let Some(id) = member_expr.object().get_identifier_reference() {
|
||||
if matches!(
|
||||
JestFnKind::from(id.name.as_str()),
|
||||
JestFnKind::General(JestGeneralFnKind::Describe | JestGeneralFnKind::Test)
|
||||
) {
|
||||
if let Some(property_name) = member_expr.static_property_name() {
|
||||
return property_name == "concurrent";
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn require_local_test_context_for_concurrent_snapshots_diagnostic(span0: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Require local Test Context for concurrent snapshot tests")
|
||||
.with_help("Use local Test Context instead")
|
||||
.with_label(span0)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct RequireLocalTestContextForConcurrentSnapshots;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
/// The rule is intended to ensure that concurrent snapshot tests are executed within a properly configured local test context.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// Examples of **incorrect** code for this rule:
|
||||
/// ```js
|
||||
/// test.concurrent('myLogic', () => {
|
||||
/// expect(true).toMatchSnapshot();
|
||||
/// })
|
||||
///
|
||||
/// describe.concurrent('something', () => {
|
||||
/// test('myLogic', () => {
|
||||
/// expect(true).toMatchInlineSnapshot();
|
||||
/// })
|
||||
/// })
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Examples of **correct** code for this rule:
|
||||
/// ```js
|
||||
/// test.concurrent('myLogic', ({ expect }) => {
|
||||
/// expect(true).toMatchSnapshot();
|
||||
/// })
|
||||
///
|
||||
/// test.concurrent('myLogic', (context) => {
|
||||
/// context.expect(true).toMatchSnapshot();
|
||||
/// })
|
||||
/// ```
|
||||
RequireLocalTestContextForConcurrentSnapshots,
|
||||
correctness
|
||||
);
|
||||
|
||||
impl Rule for RequireLocalTestContextForConcurrentSnapshots {
|
||||
fn run_once(&self, ctx: &LintContext) {
|
||||
for possible_jest_node in &collect_possible_jest_call_node(ctx) {
|
||||
Self::run(possible_jest_node, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireLocalTestContextForConcurrentSnapshots {
|
||||
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::Expect]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(member_expr) = call_expr.callee.as_member_expression() else { return };
|
||||
|
||||
let Some(property_name) = member_expr.static_property_name() else { return };
|
||||
|
||||
if !is_snapshot_method(property_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_or_describe_node_found =
|
||||
ctx.nodes().iter_parents(possible_jest_node.node.id()).any(|node| {
|
||||
if let AstKind::CallExpression(ancestor_call_expr) = node.kind() {
|
||||
if let Some(ancestor_member_expr) =
|
||||
ancestor_call_expr.callee.as_member_expression()
|
||||
{
|
||||
return is_test_or_describe_node(ancestor_member_expr);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
if test_or_describe_node_found {
|
||||
ctx.diagnostic(require_local_test_context_for_concurrent_snapshots_diagnostic(
|
||||
call_expr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
r#"it("something", () => { expect(true).toBe(true) })"#,
|
||||
r#"it.concurrent("something", () => { expect(true).toBe(true) })"#,
|
||||
r#"it("something", () => { expect(1).toMatchSnapshot() })"#,
|
||||
r#"it.concurrent("something", ({ expect }) => { expect(1).toMatchSnapshot() })"#,
|
||||
r#"it.concurrent("something", ({ expect }) => { expect(1).toMatchInlineSnapshot("1") })"#,
|
||||
r#"describe.concurrent("something", () => { it("something", () => { expect(true).toBe(true) }) })"#,
|
||||
r#"describe.concurrent("something", () => { it("something", ({ expect }) => { expect(1).toMatchSnapshot() }) })"#,
|
||||
r#"describe.concurrent("something", () => { it("something", ({ expect }) => { expect(1).toMatchInlineSnapshot() }) })"#,
|
||||
r#"describe("something", () => { it("something", (context) => { context.expect(1).toMatchInlineSnapshot() }) })"#,
|
||||
r#"describe("something", () => { it("something", (context) => { expect(1).toMatchInlineSnapshot() }) })"#,
|
||||
r#"it.concurrent("something", (context) => { context.expect(1).toMatchSnapshot() })"#,
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
r#"it.concurrent("should fail", () => { expect(true).toMatchSnapshot() })"#,
|
||||
r#"it.concurrent("should fail", () => { expect(true).toMatchInlineSnapshot("true") })"#,
|
||||
r#"describe.concurrent("failing", () => { it("should fail", () => { expect(true).toMatchSnapshot() }) })"#,
|
||||
r#"describe.concurrent("failing", () => { it("should fail", () => { expect(true).toMatchInlineSnapshot("true") }) })"#,
|
||||
r#"it.concurrent("something", (context) => { expect(true).toMatchSnapshot() })"#,
|
||||
r#"it.concurrent("something", () => {
|
||||
expect(true).toMatchSnapshot();
|
||||
|
||||
expect(true).toMatchSnapshot();
|
||||
})"#,
|
||||
r#"it.concurrent("something", () => {
|
||||
expect(true).toBe(true);
|
||||
|
||||
expect(true).toMatchSnapshot();
|
||||
})"#,
|
||||
r#"it.concurrent("should fail", () => { expect(true).toMatchFileSnapshot("./test/basic.output.html") })"#,
|
||||
r#"it.concurrent("should fail", () => { expect(foo()).toThrowErrorMatchingSnapshot() })"#,
|
||||
r#"it.concurrent("should fail", () => { expect(foo()).toThrowErrorMatchingInlineSnapshot("bar") })"#,
|
||||
];
|
||||
|
||||
Tester::new(RequireLocalTestContextForConcurrentSnapshots::NAME, pass, fail)
|
||||
.test_and_snapshot();
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
---
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:38]
|
||||
1 │ it.concurrent("should fail", () => { expect(true).toMatchSnapshot() })
|
||||
· ──────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:38]
|
||||
1 │ it.concurrent("should fail", () => { expect(true).toMatchInlineSnapshot("true") })
|
||||
· ──────────────────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:66]
|
||||
1 │ describe.concurrent("failing", () => { it("should fail", () => { expect(true).toMatchSnapshot() }) })
|
||||
· ──────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:66]
|
||||
1 │ describe.concurrent("failing", () => { it("should fail", () => { expect(true).toMatchInlineSnapshot("true") }) })
|
||||
· ──────────────────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:43]
|
||||
1 │ it.concurrent("something", (context) => { expect(true).toMatchSnapshot() })
|
||||
· ──────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:2:21]
|
||||
1 │ it.concurrent("something", () => {
|
||||
2 │ expect(true).toMatchSnapshot();
|
||||
· ──────────────────────────────
|
||||
3 │
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:4:21]
|
||||
3 │
|
||||
4 │ expect(true).toMatchSnapshot();
|
||||
· ──────────────────────────────
|
||||
5 │ })
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:4:21]
|
||||
3 │
|
||||
4 │ expect(true).toMatchSnapshot();
|
||||
· ──────────────────────────────
|
||||
5 │ })
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:38]
|
||||
1 │ it.concurrent("should fail", () => { expect(true).toMatchFileSnapshot("./test/basic.output.html") })
|
||||
· ────────────────────────────────────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:38]
|
||||
1 │ it.concurrent("should fail", () => { expect(foo()).toThrowErrorMatchingSnapshot() })
|
||||
· ────────────────────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
|
||||
⚠ eslint-plugin-vitest(require-local-test-context-for-concurrent-snapshots): Require local Test Context for concurrent snapshot tests
|
||||
╭─[require_local_test_context_for_concurrent_snapshots.tsx:1:38]
|
||||
1 │ it.concurrent("should fail", () => { expect(foo()).toThrowErrorMatchingInlineSnapshot("bar") })
|
||||
· ───────────────────────────────────────────────────────
|
||||
╰────
|
||||
help: Use local Test Context instead
|
||||
Loading…
Reference in a new issue