feat(linter): support eslint-plugin-vitest/valid-describe-callback (#4185)

Rule detail:
[link](https://github.com/veritem/eslint-plugin-vitest/blob/main/src/rules/valid-describe-callback.ts)

---------

Co-authored-by: wenzhe <mysteryven@gmail.com>
This commit is contained in:
cinchen 2024-07-12 11:35:00 +08:00 committed by GitHub
parent aab7aaaa06
commit 126b66c4f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 430 additions and 10 deletions

View file

@ -10,15 +10,20 @@ use crate::{
context::LintContext,
rule::Rule,
utils::{
collect_possible_jest_call_node, parse_general_jest_fn_call, JestFnKind, JestGeneralFnKind,
PossibleJestNode,
collect_possible_jest_call_node, get_test_plugin_name, parse_general_jest_fn_call,
JestFnKind, JestGeneralFnKind, PossibleJestNode, TestPluginName,
},
};
fn valid_describe_callback_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("eslint-plugin-jest(valid-describe-callback): {x0:?}"))
.with_help(format!("{x1:?}"))
.with_label(span2)
fn valid_describe_callback_diagnostic(
x0: TestPluginName,
x1: &str,
x2: &str,
span3: Span,
) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("{x0}(valid-describe-callback): {x1:?}"))
.with_help(format!("{x2:?}"))
.with_label(span3)
}
#[derive(Debug, Default, Clone)]
@ -58,6 +63,17 @@ declare_oxc_lint!(
/// expect(myFunction()).toBeTruthy();
/// }));
/// ```
///
/// This rule is compatible with [eslint-plugin-vitest](https://github.com/veritem/eslint-plugin-vitest/blob/main/docs/rules/valid-describe-callback.md),
/// to use it, add the following configuration to your `.eslintrc.json`:
///
/// ```json
/// {
/// "rules": {
/// "vitest/valid-describe-callback": "error"
/// }
/// }
/// ```
ValidDescribeCallback,
correctness
);
@ -151,7 +167,8 @@ fn find_first_return_stmt_span(function_body: &FunctionBody) -> Option<Span> {
fn diagnostic(ctx: &LintContext, span: Span, message: Message) {
let (error, help) = message.details();
ctx.diagnostic(valid_describe_callback_diagnostic(error, help, span));
let plugin_name = get_test_plugin_name(ctx);
ctx.diagnostic(valid_describe_callback_diagnostic(plugin_name, error, help, span));
}
#[derive(Clone, Copy)]
@ -192,7 +209,7 @@ impl Message {
fn test() {
use crate::tester::Tester;
let pass = vec![
let mut pass = vec![
("describe.each([1, 2, 3])('%s', (a, b) => {});", None),
("describe('foo', function() {})", None),
("describe('foo', () => {})", None),
@ -235,7 +252,7 @@ fn test() {
),
];
let fail = vec![
let mut fail = vec![
("describe.each()()", None),
("describe['each']()()", None),
("describe.each(() => {})()", None),
@ -331,5 +348,157 @@ fn test() {
("describe('foo', async function (done) {})", None),
];
Tester::new(ValidDescribeCallback::NAME, pass, fail).with_jest_plugin(true).test_and_snapshot();
let pass_vitest = vec![
("describe.each([1, 2, 3])(\"%s\", (a, b) => {});", None),
("describe(\"foo\", function() {})", None),
("describe(\"foo\", () => {})", None),
("describe(`foo`, () => {})", None),
("xdescribe(\"foo\", () => {})", None),
("fdescribe(\"foo\", () => {})", None),
("describe.only(\"foo\", () => {})", None),
("describe.skip(\"foo\", () => {})", None),
("describe.todo(\"runPrettierFormat\");", None),
(
"
describe('foo', () => {
it('bar', () => {
return Promise.resolve(42).then(value => {
expect(value).toBe(42)
})
})
})
",
None,
),
(
"
describe('foo', () => {
it('bar', async () => {
expect(await Promise.resolve(42)).toBe(42)
})
})
",
None,
),
(
"
if (hasOwnProperty(obj, key)) {
}
",
None,
),
(
"
describe.each`
foo | foe
${1} | ${2}
`('$something', ({ foo, foe }) => {});
",
None,
),
];
let fail_vitest = vec![
("describe.each()()", None),
("describe[\"each\"]()()", None),
("describe.each(() => {})()", None),
("describe.each(() => {})(\"foo\")", None),
("describe.each()(() => {})", None),
("describe[\"each\"]()(() => {})", None),
("describe.each(\"foo\")(() => {})", None),
("describe.only.each(\"foo\")(() => {})", None),
("describe(() => {})", None),
("describe(\"foo\")", None),
("describe(\"foo\", \"foo2\")", None),
("describe()", None),
("describe(\"foo\", async () => {})", None),
("describe(\"foo\", async function () {})", None),
("xdescribe(\"foo\", async function () {})", None),
("fdescribe(\"foo\", async function () {})", None),
("describe.only(\"foo\", async function () {})", None),
("describe.skip(\"foo\", async function () {})", None),
(
"
describe('sample case', () => {
it('works', () => {
expect(true).toEqual(true);
});
describe('async', async () => {
await new Promise(setImmediate);
it('breaks', () => {
throw new Error('Fail');
});
});
});
",
None,
),
(
"
describe('foo', function () {
return Promise.resolve().then(() => {
it('breaks', () => {
throw new Error('Fail')
})
})
})
",
None,
),
(
"
describe('foo', () => {
return Promise.resolve().then(() => {
it('breaks', () => {
throw new Error('Fail')
})
})
describe('nested', () => {
return Promise.resolve().then(() => {
it('breaks', () => {
throw new Error('Fail')
})
})
})
})
",
None,
),
(
"
describe('foo', async () => {
await something()
it('does something')
describe('nested', () => {
return Promise.resolve().then(() => {
it('breaks', () => {
throw new Error('Fail')
})
})
})
})
",
None,
),
(
"
describe('foo', () =>
test('bar', () => {})
)
",
None,
),
("describe(\"foo\", done => {})", None),
("describe(\"foo\", function (done) {})", None),
("describe(\"foo\", function (one, two, three) {})", None),
("describe(\"foo\", async function (done) {})", None),
];
pass.extend(pass_vitest);
fail.extend(fail_vitest);
Tester::new(ValidDescribeCallback::NAME, pass, fail)
.with_jest_plugin(true)
.with_vitest_plugin(true)
.test_and_snapshot();
}

View file

@ -1,5 +1,6 @@
---
source: crates/oxc_linter/src/tester.rs
assertion_line: 216
---
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
@ -256,3 +257,252 @@ source: crates/oxc_linter/src/tester.rs
· ────────────────────────
╰────
help: "Remove argument(s) of describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe.each()()
· ─────────────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe["each"]()()
· ────────────────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe.each(() => {})()
· ─────────────────────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:25]
1 │ describe.each(() => {})("foo")
· ─────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe.each()(() => {})
· ────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:20]
1 │ describe["each"]()(() => {})
· ────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:22]
1 │ describe.each("foo")(() => {})
· ────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:27]
1 │ describe.only.each("foo")(() => {})
· ────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:10]
1 │ describe(() => {})
· ────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:10]
1 │ describe("foo")
· ─────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "Second argument must be a function"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", "foo2")
· ──────
╰────
help: "Replace second argument with a function"
⚠ eslint-plugin-jest(valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe()
· ──────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", async () => {})
· ──────────────
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:18]
1 │ xdescribe("foo", async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:18]
1 │ fdescribe("foo", async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:22]
1 │ describe.only("foo", async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:22]
1 │ describe.skip("foo", async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:6:39]
5 │ });
6 │ ╭─▶ describe('async', async () => {
7 │ │ await new Promise(setImmediate);
8 │ │ it('breaks', () => {
9 │ │ throw new Error('Fail');
10 │ │ });
11 │ ╰─▶ });
12 │ });
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:3:21]
2 │ describe('foo', function () {
3 │ ╭─▶ return Promise.resolve().then(() => {
4 │ │ it('breaks', () => {
5 │ │ throw new Error('Fail')
6 │ │ })
7 │ ╰─▶ })
8 │ })
╰────
help: "Remove return statement in your describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:9:25]
8 │ describe('nested', () => {
9 │ ╭─▶ return Promise.resolve().then(() => {
10 │ │ it('breaks', () => {
11 │ │ throw new Error('Fail')
12 │ │ })
13 │ ╰─▶ })
14 │ })
╰────
help: "Remove return statement in your describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:3:21]
2 │ describe('foo', () => {
3 │ ╭─▶ return Promise.resolve().then(() => {
4 │ │ it('breaks', () => {
5 │ │ throw new Error('Fail')
6 │ │ })
7 │ ╰─▶ })
8 │ describe('nested', () => {
╰────
help: "Remove return statement in your describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:6:25]
5 │ describe('nested', () => {
6 │ ╭─▶ return Promise.resolve().then(() => {
7 │ │ it('breaks', () => {
8 │ │ throw new Error('Fail')
9 │ │ })
10 │ ╰─▶ })
11 │ })
╰────
help: "Remove return statement in your describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:2:33]
1 │
2 │ ╭─▶ describe('foo', async () => {
3 │ │ await something()
4 │ │ it('does something')
5 │ │ describe('nested', () => {
6 │ │ return Promise.resolve().then(() => {
7 │ │ it('breaks', () => {
8 │ │ throw new Error('Fail')
9 │ │ })
10 │ │ })
11 │ │ })
12 │ ╰─▶ })
13 │
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:3:21]
2 │ describe('foo', () =>
3 │ test('bar', () => {})
· ─────────────────────
4 │ )
╰────
help: "Remove return statement in your describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", done => {})
· ──────────
╰────
help: "Remove argument(s) of describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", function (done) {})
· ──────────────────
╰────
help: "Remove argument(s) of describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", function (one, two, three) {})
· ─────────────────────────────
╰────
help: "Remove argument(s) of describe callback"
⚠ eslint-plugin-jest(valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", async function (done) {})
· ────────────────────────
╰────
help: "Remove `async` keyword"
⚠ eslint-plugin-jest(valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:17]
1 │ describe("foo", async function (done) {})
· ────────────────────────
╰────
help: "Remove argument(s) of describe callback"

View file

@ -22,6 +22,7 @@ pub fn is_jest_rule_adapted_to_vitest(rule_name: &str) -> bool {
"no-focused-tests",
"no-test-prefixes",
"prefer-hooks-in-order",
"valid-describe-callback",
"valid-expect",
];