refactor(linter): extract is_valid_jest_call (#781)

This commit is contained in:
Wenzhe Wang 2023-08-24 23:05:03 +08:00 committed by GitHub
parent 2f0ff28f47
commit 31d56698f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 140 deletions

View file

@ -1,4 +1,4 @@
use std::borrow::Cow;
use std::{borrow::Cow, cmp::Ordering};
use oxc_ast::{
ast::{
@ -86,30 +86,9 @@ pub fn parse_jest_fn_call<'a>(
members.push(member);
}
let is_valid_jest_call = if members.is_empty() {
VALID_JEST_FN_CALL_CHAINS.iter().any(|chain| chain[0] == name)
} else if members.len() == 1 {
VALID_JEST_FN_CALL_CHAINS_2
.iter()
.any(|chain| chain[0] == name && members[0].is_name_equal(chain[1]))
} else if members.len() == 2 {
VALID_JEST_FN_CALL_CHAINS_3.iter().any(|chain| {
chain[0] == name
&& members[0].is_name_equal(chain[1])
&& members[1].is_name_equal(chain[2])
})
} else if members.len() == 3 {
VALID_JEST_FN_CALL_CHAINS_4.iter().any(|chain| {
chain[0] == name
&& members[0].is_name_equal(chain[1])
&& members[1].is_name_equal(chain[2])
&& members[2].is_name_equal(chain[3])
})
} else {
false
};
if !is_valid_jest_call {
let mut call_chains = Vec::from([Cow::Borrowed(name)]);
call_chains.extend(members.iter().filter_map(KnownMemberExpressionProperty::name));
if !is_valid_jest_call(&call_chains) {
return None;
}
@ -123,6 +102,25 @@ pub fn parse_jest_fn_call<'a>(
None
}
// If find a match in `VALID_JEST_FN_CALL_CHAINS`, return true.
fn is_valid_jest_call(members: &[Cow<str>]) -> bool {
VALID_JEST_FN_CALL_CHAINS
.binary_search_by(|chain| {
chain
.iter()
.zip(members.iter())
.find_map(|(&chain, member)| {
let ordering = chain.cmp(member.as_ref());
if ordering != Ordering::Equal {
return Some(ordering);
}
None
})
.unwrap_or(Ordering::Equal)
})
.is_ok()
}
fn resolve_to_jest_fn<'a>(
call_expr: &'a CallExpression<'a>,
ctx: &LintContext<'a>,
@ -360,65 +358,57 @@ fn get_node_chain<'a>(
chain
}
const VALID_JEST_FN_CALL_CHAINS: [[&str; 1]; 12] = [
["afterAll"],
["afterEach"],
["beforeAll"],
["beforeEach"],
["describe"],
["fdescribe"],
["xdescribe"],
["it"],
["fit"],
["xit"],
["test"],
["xtest"],
];
const VALID_JEST_FN_CALL_CHAINS_2: [[&str; 2]; 23] = [
["describe", "each"],
["describe", "only"],
["describe", "skip"],
["fdescribe", "each"],
["xdescribe", "each"],
["it", "concurrent"],
["it", "each"],
["it", "failing"],
["it", "only"],
["it", "skip"],
["it", "todo"],
["fit", "each"],
["fit", "failing"],
["xit", "each"],
["xit", "failing"],
["test", "concurrent"],
["test", "each"],
["test", "failing"],
["test", "only"],
["test", "skip"],
["test", "todo"],
["xtest", "each"],
["xtest", "failing"],
];
const VALID_JEST_FN_CALL_CHAINS_3: [[&str; 3]; 12] = [
["describe", "only", "each"],
["describe", "skip", "each"],
["it", "concurrent", "each"],
["it", "only", "each"],
["it", "only", "failing"],
["it", "skip", "each"],
["it", "skip", "failing"],
["test", "concurrent", "each"],
["test", "only", "each"],
["test", "only", "failing"],
["test", "skip", "each"],
["test", "skip", "failing"],
];
const VALID_JEST_FN_CALL_CHAINS_4: [[&str; 4]; 4] = [
// sorted list for binary search.
const VALID_JEST_FN_CALL_CHAINS: [[&str; 4]; 51] = [
["afterAll", "", "", ""],
["afterEach", "", "", ""],
["beforeAll", "", "", ""],
["beforeEach", "", "", ""],
["describe", "", "", ""],
["describe", "each", "", ""],
["describe", "only", "", ""],
["describe", "only", "each", ""],
["describe", "skip", "", ""],
["describe", "skip", "each", ""],
["fdescribe", "", "", ""],
["fdescribe", "each", "", ""],
["fit", "", "", ""],
["fit", "each", "", ""],
["fit", "failing", "", ""],
["it", "", "", ""],
["it", "concurrent", "", ""],
["it", "concurrent", "each", ""],
["it", "concurrent", "only", "each"],
["it", "concurrent", "skip", "each"],
["it", "each", "", ""],
["it", "failing", "", ""],
["it", "only", "", ""],
["it", "only", "each", ""],
["it", "only", "failing", ""],
["it", "skip", "", ""],
["it", "skip", "each", ""],
["it", "skip", "failing", ""],
["it", "todo", "", ""],
["test", "", "", ""],
["test", "concurrent", "", ""],
["test", "concurrent", "each", ""],
["test", "concurrent", "only", "each"],
["test", "concurrent", "skip", "each"],
["test", "each", "", ""],
["test", "failing", "", ""],
["test", "only", "", ""],
["test", "only", "each", ""],
["test", "only", "failing", ""],
["test", "skip", "", ""],
["test", "skip", "each", ""],
["test", "skip", "failing", ""],
["test", "todo", "", ""],
["xdescribe", "", "", ""],
["xdescribe", "each", "", ""],
["xit", "", "", ""],
["xit", "each", "", ""],
["xit", "failing", "", ""],
["xtest", "", "", ""],
["xtest", "each", "", ""],
["xtest", "failing", "", ""],
];

View file

@ -18,7 +18,7 @@ use crate::{
};
#[derive(Debug, Error, Diagnostic)]
#[error("Unexpected focused test.")]
#[error("eslint(jest/no-focused-tests): Unexpected focused test.")]
#[diagnostic(severity(warning), help("Remove focus from test."))]
struct NoFocusedTestsDiagnostic(#[label] pub Span);

View file

@ -17,7 +17,7 @@ use crate::{
};
#[derive(Debug, Error, Diagnostic)]
#[error("{0:?}")]
#[error("eslint(jest/valid-describe-callback): {0:?}")]
#[diagnostic(severity(warning), help("{1:?}"))]
struct ValidDescribeCallbackDiagnostic(&'static str, &'static str, #[label] pub Span);
@ -253,7 +253,7 @@ fn test() {
"
import { fdescribe } from '@jest/globals';
fdescribe('foo', async function () {})
",
",
None,
),
("describe.only('foo', async function () {})", None),
@ -271,7 +271,7 @@ fn test() {
});
});
});
",
",
None,
),
(
@ -283,7 +283,7 @@ fn test() {
})
})
})
",
",
None,
),
(
@ -302,7 +302,7 @@ fn test() {
})
})
})
",
",
None,
),
(
@ -318,7 +318,7 @@ fn test() {
})
})
})
",
",
None,
),
("describe('foo', () => test('bar', () => {})) ", None),

View file

@ -2,126 +2,126 @@
source: crates/oxc_linter/src/tester.rs
expression: no_focused_tests
---
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ describe.only()
· ───────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ describe.only.each()()
· ──────────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ describe.only.each`table`()
· ───────────────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ describe["only"]()
· ──────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ it.only()
· ─────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ it.concurrent.only.each``()
· ───────────────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ it.only.each()()
· ────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ it.only.each`table`()
· ─────────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ it["only"]()
· ────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ test.only()
· ───────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ test.concurrent.only.each()()
· ─────────────────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ test.only.each()()
· ──────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ test.only.each`table`()
· ───────────────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ test["only"]()
· ──────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ fdescribe()
· ───────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ fit()
· ─────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ fit.each()()
· ────────────
╰────
help: Remove focus from test.
⚠ Unexpected focused test.
eslint(jest/no-focused-tests): Unexpected focused test.
╭─[no_focused_tests.tsx:1:1]
1 │ fit.each`table`()
· ─────────────────

View file

@ -2,142 +2,142 @@
source: crates/oxc_linter/src/tester.rs
expression: valid_describe_callback
---
⚠ "Describe requires name and callback arguments"
eslint(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"
⚠ "Describe requires name and callback arguments"
eslint(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"
⚠ "Describe requires name and callback arguments"
eslint(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"
⚠ "Describe requires name and callback arguments"
eslint(jest/valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe.each(() => {})('foo')
· ─────
╰────
help: "Add name as first argument and callback as second argument"
⚠ "Describe requires name and callback arguments"
eslint(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"
⚠ "Describe requires name and callback arguments"
eslint(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"
⚠ "Describe requires name and callback arguments"
eslint(jest/valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe.each('foo')(() => {})
· ────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ "Describe requires name and callback arguments"
eslint(jest/valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe.only.each('foo')(() => {})
· ────────
╰────
help: "Add name as first argument and callback as second argument"
⚠ "Describe requires name and callback arguments"
eslint(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"
⚠ "Describe requires name and callback arguments"
eslint(jest/valid-describe-callback): "Describe requires name and callback arguments"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo')
· ─────
╰────
help: "Add name as first argument and callback as second argument"
⚠ "Second argument must be a function"
eslint(jest/valid-describe-callback): "Second argument must be a function"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', 'foo2')
· ──────
╰────
help: "Replace second argument with a function"
⚠ "Describe requires name and callback arguments"
eslint(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"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', async () => {})
· ──────────────
╰────
help: "Remove `async` keyword"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ xdescribe('foo', async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ fdescribe('foo', async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:2:1]
2 │ import { fdescribe } from '@jest/globals';
3 │ fdescribe('foo', async function () {})
· ────────────────────
4 │
4 │
╰────
help: "Remove `async` keyword"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe.only('foo', async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe.skip('foo', async function () {})
· ────────────────────
╰────
help: "Remove `async` keyword"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:5:1]
5 │ });
6 │ ╭─▶ describe('async', async () => {
@ -150,7 +150,7 @@ expression: valid_describe_callback
╰────
help: "Remove `async` keyword"
⚠ "Unexpected return statement in describe callback"
eslint(jest/valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:2:1]
2 │ describe('foo', function () {
3 │ ╭─▶ return Promise.resolve().then(() => {
@ -162,7 +162,7 @@ expression: valid_describe_callback
╰────
help: "Remove return statement in your describe callback"
⚠ "Unexpected return statement in describe callback"
eslint(jest/valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:2:1]
2 │ describe('foo', () => {
3 │ ╭─▶ return Promise.resolve().then(() => {
@ -174,7 +174,7 @@ expression: valid_describe_callback
╰────
help: "Remove return statement in your describe callback"
⚠ "Unexpected return statement in describe callback"
eslint(jest/valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:8:1]
8 │ describe('nested', () => {
9 │ ╭─▶ return Promise.resolve().then(() => {
@ -186,7 +186,7 @@ expression: valid_describe_callback
╰────
help: "Remove return statement in your describe callback"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │
2 │ ╭─▶ describe('foo', async () => {
@ -200,11 +200,11 @@ expression: valid_describe_callback
10 │ │ })
11 │ │ })
12 │ ╰─▶ })
13 │
13 │
╰────
help: "Remove `async` keyword"
⚠ "Unexpected return statement in describe callback"
eslint(jest/valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:5:1]
5 │ describe('nested', () => {
6 │ ╭─▶ return Promise.resolve().then(() => {
@ -216,42 +216,42 @@ expression: valid_describe_callback
╰────
help: "Remove return statement in your describe callback"
⚠ "Unexpected return statement in describe callback"
eslint(jest/valid-describe-callback): "Unexpected return statement in describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', () => test('bar', () => {}))
· ─────────────────────
╰────
help: "Remove return statement in your describe callback"
⚠ "Unexpected argument(s) in describe callback"
eslint(jest/valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', done => {})
· ──────────
╰────
help: "Remove argument(s) of describe callback"
⚠ "Unexpected argument(s) in describe callback"
eslint(jest/valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', function (done) {})
· ──────────────────
╰────
help: "Remove argument(s) of describe callback"
⚠ "Unexpected argument(s) in describe callback"
eslint(jest/valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', function (one, two, three) {})
· ─────────────────────────────
╰────
help: "Remove argument(s) of describe callback"
⚠ "No async describe callback"
eslint(jest/valid-describe-callback): "No async describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', async function (done) {})
· ────────────────────────
╰────
help: "Remove `async` keyword"
⚠ "Unexpected argument(s) in describe callback"
eslint(jest/valid-describe-callback): "Unexpected argument(s) in describe callback"
╭─[valid_describe_callback.tsx:1:1]
1 │ describe('foo', async function (done) {})
· ────────────────────────