From 7ab14cc41c3b101d8ac6ebacbbb958e08d3c8ebb Mon Sep 17 00:00:00 2001 From: Anson Heung <57580593+AnsonH@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:59:37 +0800 Subject: [PATCH] feat(linter): add more Vitest compatible Jest rules (#8445) There are many `eslint-plugin-vitest` rules that have an equivalent Rust implementation for the Jest plugin. This PR ports all Jest rules that passes the Vitest plugin's unit tests. Some existing Jest rule implementations are not included in the compatible list, for example: - `no-large-snapshots` - difference in config initial values - `no-mocks-import` - mention of the word "Jest" in diagnostics - `prefer-called-with` - Vitest plugin implements a fix but Jest plugin didn't - `prefer-spy-on` - mention of the word "Jest" in diagnostics - `prefer-to-contain` - the fix is missing - `valid-title` - configuration options are slightly different To get a list of already implemented Jest rules, simply run: ```bash ls crates/oxc_linter/src/rules/jest | sed 's/\.rs$//;s/_/-/g' ``` --- .../oxc_linter/src/rules/jest/max_expects.rs | 86 +++++++- .../src/rules/jest/max_nested_describe.rs | 89 ++++++++- .../src/rules/jest/no_duplicate_hooks.rs | 187 +++++++++++++++++- crates/oxc_linter/src/rules/jest/no_hooks.rs | 37 +++- .../jest/no_interpolation_in_snapshots.rs | 2 + .../src/rules/jest/no_restricted_matchers.rs | 2 + .../rules/jest/no_test_return_statement.rs | 3 + .../rules/jest/prefer_comparison_matcher.rs | 41 ++++ .../src/rules/jest/prefer_equality_matcher.rs | 46 ++++- .../src/rules/jest/prefer_expect_resolves.rs | 3 + .../src/rules/jest/prefer_hooks_on_top.rs | 2 + .../jest/prefer_mock_promise_shorthand.rs | 95 ++++++++- .../src/rules/jest/prefer_strict_equal.rs | 2 + .../src/rules/jest/prefer_to_have_length.rs | 4 + .../oxc_linter/src/rules/jest/prefer_todo.rs | 4 + .../rules/jest/require_to_throw_message.rs | 2 + .../rules/jest/require_top_level_describe.rs | 2 + .../src/snapshots/jest_max_expects.snap | 36 ++++ .../snapshots/jest_max_nested_describe.snap | 28 +++ .../snapshots/jest_no_duplicate_hooks.snap | 54 +++++ .../src/snapshots/jest_no_hooks.snap | 38 ++++ .../jest_prefer_comparison_matcher.snap | 49 +++++ .../jest_prefer_equality_matcher.snap | 133 +++++++++++++ .../jest_prefer_mock_promise_shorthand.snap | 182 +++++++++++++++++ crates/oxc_linter/src/utils/mod.rs | 19 +- 25 files changed, 1132 insertions(+), 14 deletions(-) diff --git a/crates/oxc_linter/src/rules/jest/max_expects.rs b/crates/oxc_linter/src/rules/jest/max_expects.rs index 4f7aed218..acf967587 100644 --- a/crates/oxc_linter/src/rules/jest/max_expects.rs +++ b/crates/oxc_linter/src/rules/jest/max_expects.rs @@ -121,7 +121,7 @@ impl MaxExpects { fn test() { use crate::tester::Tester; - let pass = vec![ + let mut pass = vec![ ("test('should pass')", None), ("test('should pass', () => {})", None), ("test.skip('should pass', () => {})", None), @@ -358,7 +358,7 @@ fn test() { ), ]; - let fail = vec![ + let mut fail = vec![ ( " test('should not pass', function () { @@ -471,6 +471,88 @@ fn test() { ), ]; + let pass_vitest = vec![ + ("test('should pass')", None), + ("test('should pass', () => {})", None), + ("test.skip('should pass', () => {})", None), + ( + "test('should pass', () => { + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + });", + None, + ), + ( + "test('should pass', () => { + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + });", + None, + ), + ( + " test('should pass', async () => { + expect.hasAssertions(); + + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toEqual(expect.any(Boolean)); + });", + None, + ), + ]; + + let fail_vitest = vec![ + ( + "test('should not pass', function () { + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + }); + ", + None, + ), + ( + "test('should not pass', () => { + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + }); + test('should not pass', () => { + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + expect(true).toBeDefined(); + });", + None, + ), + ( + "test('should not pass', () => { + expect(true).toBeDefined(); + expect(true).toBeDefined(); + });", + Some(serde_json::json!([{ "max": 1 }])), + ), + ]; + + pass.extend(pass_vitest); + fail.extend(fail_vitest); + Tester::new(MaxExpects::NAME, MaxExpects::PLUGIN, pass, fail) .with_jest_plugin(true) .test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jest/max_nested_describe.rs b/crates/oxc_linter/src/rules/jest/max_nested_describe.rs index 6f181bd1f..09addf14e 100644 --- a/crates/oxc_linter/src/rules/jest/max_nested_describe.rs +++ b/crates/oxc_linter/src/rules/jest/max_nested_describe.rs @@ -181,7 +181,7 @@ impl MaxNestedDescribe { fn test() { use crate::tester::Tester; - let pass = vec![ + let mut pass = vec![ ( " describe('foo', function() { @@ -287,7 +287,7 @@ fn test() { ), ]; - let fail = vec![ + let mut fail = vec![ ( " describe('foo', function() { @@ -397,6 +397,91 @@ fn test() { ), ]; + let pass_vitest = vec![ + ( + " + describe('another suite', () => { + describe('another suite', () => { + it('skipped test', () => { + // Test skipped, as tests are running in Only mode + assert.equal(Math.sqrt(4), 3) + }) + + it.only('test', () => { + // Only this test (and others marked with only) are run + assert.equal(Math.sqrt(4), 2) + }) + }) + }) + ", + None, + ), + ( + " + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + + }) + }) + }) + }) + ", + None, + ), + ]; + + let fail_vitest = vec![ + ( + " + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + + }) + }) + }) + }) + }) + }) + ", + None, + ), + ( + " + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + describe('another suite', () => { + it('skipped test', () => { + // Test skipped, as tests are running in Only mode + assert.equal(Math.sqrt(4), 3) + }) + + it.only('test', () => { + // Only this test (and others marked with only) are run + assert.equal(Math.sqrt(4), 2) + }) + }) + }) + }) + }) + }) + }) + ", + None, + ), + ]; + + pass.extend(pass_vitest); + fail.extend(fail_vitest); + Tester::new(MaxNestedDescribe::NAME, MaxNestedDescribe::PLUGIN, pass, fail) .with_jest_plugin(true) .test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs b/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs index 6e083842b..5b67ce040 100644 --- a/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs +++ b/crates/oxc_linter/src/rules/jest/no_duplicate_hooks.rs @@ -189,7 +189,7 @@ impl NoDuplicateHooks { fn test() { use crate::tester::Tester; - let pass = vec![ + let mut pass = vec![ ( " describe(\"foo\", () => { @@ -319,7 +319,7 @@ fn test() { ), ]; - let fail = vec![ + let mut fail = vec![ ( " describe(\"foo\", () => { @@ -555,6 +555,189 @@ fn test() { ), ]; + let pass_vitest = vec![ + ( + r#" + describe("foo", () => { + beforeEach(() => {}) + test("bar", () => { + someFn(); + }) + }) + "#, + None, + ), + ( + r#" + beforeEach(() => {}) + test("bar", () => { + someFn(); + }) + "#, + None, + ), + ( + r#" + describe("foo", () => { + beforeAll(() => {}), + beforeEach(() => {}) + afterEach(() => {}) + afterAll(() => {}) + + test("bar", () => { + someFn(); + }) + }) + "#, + None, + ), + ( + r#" + describe.skip("foo", () => { + beforeEach(() => {}), + beforeAll(() => {}), + test("bar", () => { + someFn(); + }) + }) + describe("foo", () => { + beforeEach(() => {}), + beforeAll(() => {}), + test("bar", () => { + someFn(); + }) + }) + "#, + None, + ), + ( + r#" + describe("foo", () => { + beforeEach(() => {}), + test("bar", () => { + someFn(); + }) + describe("inner_foo", () => { + beforeEach(() => {}) + test("inner bar", () => { + someFn(); + }) + }) + }) + "#, + None, + ), + ( + " + describe.each(['hello'])('%s', () => { + beforeEach(() => {}); + it('is fine', () => {}); + }); + ", + None, + ), + ]; + + let fail_vitest = vec![ + ( + r#" + describe("foo", () => { + beforeEach(() => {}), + beforeEach(() => {}), + test("bar", () => { + someFn(); + }) + }) + "#, + None, + ), + ( + r#" + describe.skip("foo", () => { + afterEach(() => {}), + afterEach(() => {}), + test("bar", () => { + someFn(); + }) + }) + "#, + None, + ), + ( + r#" + describe.skip("foo", () => { + beforeEach(() => {}), + beforeAll(() => {}), + test("bar", () => { + someFn(); + }) + }) + describe("foo", () => { + beforeEach(() => {}), + beforeEach(() => {}), + beforeAll(() => {}), + test("bar", () => { + someFn(); + }) + }) + "#, + None, + ), + ( + r#" + describe.skip("foo", () => { + beforeEach(() => {}), + beforeAll(() => {}), + test("bar", () => { + someFn(); + }) + }) + describe("foo", () => { + beforeEach(() => {}), + beforeEach(() => {}), + beforeAll(() => {}), + test("bar", () => { + someFn(); + }) + }) + "#, + None, + ), + ( + " + describe.each(['hello'])('%s', () => { + beforeEach(() => {}); + beforeEach(() => {}); + + it('is not fine', () => {}); + }); + ", + None, + ), + ( + " + describe('something', () => { + describe.each(['hello'])('%s', () => { + beforeEach(() => {}); + + it('is fine', () => {}); + }); + + describe.each(['world'])('%s', () => { + beforeEach(() => {}); + beforeEach(() => {}); + + it('is not fine', () => {}); + }); + }); + ", + None, + ), + ]; + + pass.extend(pass_vitest); + fail.extend(fail_vitest); + Tester::new(NoDuplicateHooks::NAME, NoDuplicateHooks::PLUGIN, pass, fail) .with_jest_plugin(true) .test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jest/no_hooks.rs b/crates/oxc_linter/src/rules/jest/no_hooks.rs index 28aa6e23e..46adcac92 100644 --- a/crates/oxc_linter/src/rules/jest/no_hooks.rs +++ b/crates/oxc_linter/src/rules/jest/no_hooks.rs @@ -131,7 +131,7 @@ impl NoHooks { fn test() { use crate::tester::Tester; - let pass = vec![ + let mut pass = vec![ ("test(\"foo\")", None), ("describe(\"foo\", () => { it(\"bar\") })", None), ("test(\"foo\", () => { expect(subject.beforeEach()).toBe(true) })", None), @@ -142,7 +142,7 @@ fn test() { ("test(\"foo\")", Some(serde_json::json!([{ "allow": "undefined" }]))), ]; - let fail = vec![ + let mut fail = vec![ ("beforeAll(() => {})", None), ("beforeEach(() => {})", None), ("afterAll(() => {})", None), @@ -162,6 +162,39 @@ fn test() { ), ]; + let pass_vitest = vec![ + (r#"test("foo")"#, None), + (r#"describe("foo", () => { it("bar") })"#, None), + (r#"test("foo", () => { expect(subject.beforeEach()).toBe(true) })"#, None), + ( + "afterEach(() => {}); afterAll(() => {});", + Some(serde_json::json!([{ "allow": ["afterEach", "afterAll"] }])), + ), + (r#"test("foo")"#, Some(serde_json::json!([{ "allow": null }]))), + ]; + + let fail_vitest = vec![ + ("beforeAll(() => {})", None), + ("beforeEach(() => {})", None), + ("afterAll(() => {})", None), + ("afterEach(() => {})", None), + ( + "beforeEach(() => {}); afterEach(() => { vi.resetModules() });", + Some(serde_json::json!([{ "allow": ["afterEach"] }])), + ), + ( + " + import { beforeEach as afterEach, afterEach as beforeEach, vi } from 'vitest'; + afterEach(() => {}); + beforeEach(() => { vi.resetModules() }); + ", + Some(serde_json::json!([{ "allow": ["afterEach"] }])), + ), // { "parserOptions": { "sourceType": "module" } } + ]; + + pass.extend(pass_vitest); + fail.extend(fail_vitest); + Tester::new(NoHooks::NAME, NoHooks::PLUGIN, pass, fail) .with_jest_plugin(true) .test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs b/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs index 5320efe26..8a991c6f5 100644 --- a/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs +++ b/crates/oxc_linter/src/rules/jest/no_interpolation_in_snapshots.rs @@ -98,6 +98,8 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) fn test() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ("expect('something').toEqual('else');", None), ("expect(something).toMatchInlineSnapshot();", None), diff --git a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs index 31bb51c18..cb2f3cb44 100644 --- a/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs +++ b/crates/oxc_linter/src/rules/jest/no_restricted_matchers.rs @@ -170,6 +170,8 @@ impl NoRestrictedMatchers { fn test() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ("expect(a).toHaveBeenCalled()", None), ("expect(a).not.toHaveBeenCalled()", None), diff --git a/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs b/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs index 3a91b4dcf..12058129c 100644 --- a/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs +++ b/crates/oxc_linter/src/rules/jest/no_test_return_statement.rs @@ -130,7 +130,10 @@ fn check_test_return_statement<'a>(func_body: &OBox<'_, FunctionBody<'a>>, ctx: fn test() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ + ("it('noop', () => {});", None), ("test('noop', () => {});", None), ("test('one', () => expect(1).toBe(1));", None), ("test('empty')", None), diff --git a/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs b/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs index ef89db166..03dfaa54b 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_comparison_matcher.rs @@ -469,6 +469,47 @@ fn test() { fix.push((case.as_str(), fixer.as_str(), None)); } + let pass_vitest = vec![ + ("expect.hasAssertions", None), + ("expect.hasAssertions()", None), + ("expect.assertions(1)", None), + ("expect(true).toBe(...true)", None), + ("expect()", None), + ("expect({}).toStrictEqual({})", None), + ("expect(a === b).toBe(true)", None), + ("expect(a !== 2).toStrictEqual(true)", None), + ("expect(a === b).not.toEqual(true)", None), + (r#"expect(a !== "string").toStrictEqual(true)"#, None), + ("expect(5 != a).toBe(true)", None), + (r#"expect(a == "string").toBe(true)"#, None), + (r#"expect(a == "string").not.toBe(true)"#, None), + ("expect().fail('Should not succeed a HTTPS proxy request.');", None), + ]; + + let fail_vitest = vec![ + ("expect(a > b).toBe(true)", None), + ("expect(a < b).toBe(true)", None), + ("expect(a >= b).toBe(true)", None), + ("expect(a <= b).toBe(true)", None), + ("expect(a > b).not.toBe(true)", None), + ("expect(a < b).not.toBe(true)", None), + ("expect(a >= b).not.toBe(true)", None), + ]; + + let fix_vitest = vec![ + ("expect(a > b).toBe(true)", "expect(a).toBeGreaterThan(b)", None), + ("expect(a < b).toBe(true)", "expect(a).toBeLessThan(b)", None), + ("expect(a >= b).toBe(true)", "expect(a).toBeGreaterThanOrEqual(b)", None), + ("expect(a <= b).toBe(true)", "expect(a).toBeLessThanOrEqual(b)", None), + ("expect(a > b).not.toBe(true)", "expect(a).toBeLessThanOrEqual(b)", None), + ("expect(a < b).not.toBe(true)", "expect(a).toBeGreaterThanOrEqual(b)", None), + ("expect(a >= b).not.toBe(true)", "expect(a).toBeLessThan(b)", None), + ]; + + pass.extend(pass_vitest); + fail.extend(fail_vitest); + fix.extend(fix_vitest); + Tester::new(PreferComparisonMatcher::NAME, PreferComparisonMatcher::PLUGIN, pass, fail) .with_jest_plugin(true) .expect_fix(fix) diff --git a/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs b/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs index 3fee8deca..75d62c6f0 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_equality_matcher.rs @@ -99,7 +99,7 @@ impl PreferEqualityMatcher { fn test() { use crate::tester::Tester; - let pass = vec![ + let mut pass = vec![ ("expect.hasAssertions", None), ("expect.hasAssertions()", None), ("expect.assertions(1)", None), @@ -109,7 +109,7 @@ fn test() { ("expect(a == b).toBe(true)", None), ]; - let fail = vec![ + let mut fail = vec![ ("expect(a !== b).toBe(true)", None), ("expect(a !== b).toBe(false)", None), ("expect(a !== b).resolves.toBe(true)", None), @@ -120,6 +120,48 @@ fn test() { ("expect(a !== b).resolves.not.toBe(false)", None), ]; + let pass_vitest = vec![ + ("expect.hasAssertions", None), + ("expect.hasAssertions()", None), + ("expect.assertions(1)", None), + ("expect(true).toBe(...true)", None), + ("expect(a == 1).toBe(true)", None), + ("expect(1 == a).toBe(true)", None), + ("expect(a == b).toBe(true)", None), + ("expect.hasAssertions", None), + ("expect.hasAssertions()", None), + ("expect.assertions(1)", None), + ("expect(true).toBe(...true)", None), + ("expect(a != 1).toBe(true)", None), + ("expect(1 != a).toBe(true)", None), + ("expect(a != b).toBe(true)", None), + ]; + + let fail_vitest = vec![ + ("expect(a === b).toBe(true);", None), + ("expect(a === b,).toBe(true,);", None), // { "parserOptions": { "ecmaVersion": 2017 } }, + ("expect(a === b).toBe(false);", None), + ("expect(a === b).resolves.toBe(true);", None), + ("expect(a === b).resolves.toBe(false);", None), + ("expect(a === b).not.toBe(true);", None), + ("expect(a === b).not.toBe(false);", None), + ("expect(a === b).resolves.not.toBe(true);", None), + ("expect(a === b).resolves.not.toBe(false);", None), + (r#"expect(a === b)["resolves"].not.toBe(false);"#, None), + (r#"expect(a === b)["resolves"]["not"]["toBe"](false);"#, None), + ("expect(a !== b).toBe(true);", None), + ("expect(a !== b).toBe(false);", None), + ("expect(a !== b).resolves.toBe(true);", None), + ("expect(a !== b).resolves.toBe(false);", None), + ("expect(a !== b).not.toBe(true);", None), + ("expect(a !== b).not.toBe(false);", None), + ("expect(a !== b).resolves.not.toBe(true);", None), + ("expect(a !== b).resolves.not.toBe(false);", None), + ]; + + pass.extend(pass_vitest); + fail.extend(fail_vitest); + Tester::new(PreferEqualityMatcher::NAME, PreferEqualityMatcher::PLUGIN, pass, fail) .with_jest_plugin(true) .test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs b/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs index 1236e1eb5..f6afa711f 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_expect_resolves.rs @@ -150,6 +150,8 @@ impl PreferExpectResolves { fn tests() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ("expect.hasAssertions()", None), ( @@ -179,6 +181,7 @@ fn tests() { ", None, ), + ("expect().nothing();", None), ]; let fail = vec![ diff --git a/crates/oxc_linter/src/rules/jest/prefer_hooks_on_top.rs b/crates/oxc_linter/src/rules/jest/prefer_hooks_on_top.rs index 4522ad64c..7cdd4fc28 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_hooks_on_top.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_hooks_on_top.rs @@ -192,6 +192,8 @@ impl PreferHooksOnTop { fn test() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ( " diff --git a/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs b/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs index 48fadca70..c356d05da 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_mock_promise_shorthand.rs @@ -189,7 +189,7 @@ impl PreferMockPromiseShorthand { fn test() { use crate::tester::Tester; - let pass = vec![ + let mut pass = vec![ ("describe()", None), ("it()", None), ("describe.skip()", None), @@ -248,7 +248,7 @@ fn test() { ("aVariable.mockReturnValue(Promise.all([1, 2, 3]));", None), ]; - let fail = vec![ + let mut fail = vec![ ("aVariable.mockImplementation(() => Promise.reject(42, 25))", None), ("jest.fn().mockImplementation(() => Promise.reject(42))", None), ("aVariable.mockImplementation(() => Promise.resolve(42))", None), @@ -310,7 +310,7 @@ fn test() { ), ]; - let fix = vec![ + let mut fix = vec![ ( "jest.fn().mockImplementation(() => Promise.resolve(42))", "jest.fn().mockResolvedValue(42)", @@ -431,6 +431,95 @@ fn test() { // ), ]; + let pass_vitest = vec![ + ("describe()", None), + ("it()", None), + ("describe.skip()", None), + ("it.skip()", None), + ("test()", None), + ("test.skip()", None), + ("var appliedOnly = describe.only; appliedOnly.apply(describe)", None), + ("var calledOnly = it.only; calledOnly.call(it)", None), + ("it.each()()", None), + ("it.each`table`()", None), + ("test.each()()", None), + ("test.each`table`()", None), + ("test.concurrent()", None), + ("vi.fn().mockResolvedValue(42)", None), + ("vi.fn(() => Promise.resolve(42))", None), + ("vi.fn(() => Promise.reject(42))", None), + ("aVariable.mockImplementation", None), + ("aVariable.mockImplementation()", None), + ("aVariable.mockImplementation([])", None), + ("aVariable.mockImplementation(() => {})", None), + ("aVariable.mockImplementation(() => [])", None), + ("aVariable.mockReturnValue(() => Promise.resolve(1))", None), + ("aVariable.mockReturnValue(Promise.resolve(1).then(() => 1))", None), + ("aVariable.mockReturnValue(Promise.reject(1).then(() => 1))", None), + ("aVariable.mockReturnValue(Promise.reject().then(() => 1))", None), + ("aVariable.mockReturnValue(new Promise(resolve => resolve(1)))", None), + ("aVariable.mockReturnValue(new Promise((_, reject) => reject(1)))", None), + ("vi.spyOn(Thingy, 'method').mockImplementation(param => Promise.resolve(param));", None), + ]; + + let fail_vitest = vec![ + ("vi.fn().mockImplementation(() => Promise.resolve(42))", None), + ("vi.fn().mockImplementation(() => Promise.reject(42))", None), + ("aVariable.mockImplementation(() => Promise.resolve(42))", None), + ("aVariable.mockImplementation(() => { return Promise.resolve(42) })", None), + ("aVariable.mockImplementation(() => Promise.reject(42))", None), + ("aVariable.mockImplementation(() => Promise.reject(42),)", None), // { "parserOptions": { "ecmaVersion": 2017 } }, + ("aVariable.mockImplementationOnce(() => Promise.resolve(42))", None), + ("aVariable.mockImplementationOnce(() => Promise.reject(42))", None), + ("vi.fn().mockReturnValue(Promise.resolve(42))", None), + ("vi.fn().mockReturnValue(Promise.reject(42))", None), + ("aVariable.mockReturnValue(Promise.resolve(42))", None), + ("aVariable.mockReturnValue(Promise.reject(42))", None), + ("aVariable.mockReturnValueOnce(Promise.resolve(42))", None), + ("aVariable.mockReturnValueOnce(Promise.reject(42))", None), + ("aVariable.mockReturnValue(Promise.resolve({ target: 'world', message: 'hello' }))", None), + ("aVariable.mockImplementation(() => Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValue(Promise.reject(42))", None), + ("aVariable.mockReturnValueOnce(Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValueOnce(Promise.reject(42))", None), + ("aVariable.mockReturnValueOnce(Promise.reject(new Error('oh noes!')))", None), + ("vi.fn().mockReturnValue(Promise.resolve(42), xyz)", None), + ("vi.fn().mockImplementation(() => Promise.reject(42), xyz)", None), + ("aVariable.mockReturnValueOnce(Promise.resolve(42, xyz))", None), + ("aVariable.mockReturnValueOnce(Promise.resolve())", None), + ]; + + let fix_vitest = vec![ + ("vi.fn().mockImplementation(() => Promise.resolve(42))", "vi.fn().mockResolvedValue(42)", None), + ("vi.fn().mockImplementation(() => Promise.reject(42))", "vi.fn().mockRejectedValue(42)", None), + ("aVariable.mockImplementation(() => Promise.resolve(42))", "aVariable.mockResolvedValue(42)", None), + ("aVariable.mockImplementation(() => { return Promise.resolve(42) })", "aVariable.mockResolvedValue(42)", None), + ("aVariable.mockImplementation(() => Promise.reject(42))", "aVariable.mockRejectedValue(42)", None), + ("aVariable.mockImplementation(() => Promise.reject(42),)", "aVariable.mockRejectedValue(42,)", None), + ("aVariable.mockImplementationOnce(() => Promise.resolve(42))", "aVariable.mockResolvedValueOnce(42)", None), + ("aVariable.mockImplementationOnce(() => Promise.reject(42))", "aVariable.mockRejectedValueOnce(42)", None), + ("vi.fn().mockReturnValue(Promise.resolve(42))", "vi.fn().mockResolvedValue(42)", None), + ("vi.fn().mockReturnValue(Promise.reject(42))", "vi.fn().mockRejectedValue(42)", None), + ("aVariable.mockReturnValue(Promise.resolve(42))", "aVariable.mockResolvedValue(42)", None), + ("aVariable.mockReturnValue(Promise.reject(42))", "aVariable.mockRejectedValue(42)", None), + ("aVariable.mockReturnValueOnce(Promise.resolve(42))", "aVariable.mockResolvedValueOnce(42)", None), + ("aVariable.mockReturnValueOnce(Promise.reject(42))", "aVariable.mockRejectedValueOnce(42)", None), + // Todo: Fixed + // ( + // "aVariable.mockReturnValue(Promise.resolve({ target: 'world', message: 'hello' }))", + // "aVariable.mockResolvedValue({ target: 'world', message: 'hello' })", + // None, + // ), + ("aVariable.mockImplementation(() => Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValue(Promise.reject(42))", "aVariable.mockRejectedValue(42).mockResolvedValue(42).mockRejectedValue(42)", None), + ("aVariable.mockReturnValueOnce(Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValueOnce(Promise.reject(42))", "aVariable.mockRejectedValueOnce(42).mockResolvedValue(42).mockRejectedValueOnce(42)", None), + ("aVariable.mockReturnValueOnce(Promise.reject(new Error('oh noes!')))", "aVariable.mockRejectedValueOnce(new Error('oh noes!'))", None), + ("vi.fn().mockReturnValue(Promise.resolve(42), xyz)", "vi.fn().mockResolvedValue(42, xyz)", None), + ("vi.fn().mockImplementation(() => Promise.reject(42), xyz)", "vi.fn().mockRejectedValue(42, xyz)", None), + ("aVariable.mockReturnValueOnce(Promise.resolve())", "aVariable.mockResolvedValueOnce(undefined)", None) + ]; + + pass.extend(pass_vitest); + fail.extend(fail_vitest); + fix.extend(fix_vitest); + Tester::new(PreferMockPromiseShorthand::NAME, PreferMockPromiseShorthand::PLUGIN, pass, fail) .with_jest_plugin(true) .expect_fix(fix) diff --git a/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs b/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs index 8f8b70a43..b638bd4d4 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_strict_equal.rs @@ -75,6 +75,8 @@ impl PreferStrictEqual { fn test() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ("expect(something).toStrictEqual(somethingElse);", None), ("a().toEqual('b')", None), diff --git a/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs b/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs index bad5020dd..1576827fa 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_to_have_length.rs @@ -191,6 +191,8 @@ impl PreferToHaveLength { fn tests() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ("expect.hasAssertions", None), ("expect.hasAssertions()", None), @@ -202,6 +204,7 @@ fn tests() { ("expect(user.getUserName(5)).resolves.toEqual('Paul')", None), ("expect(user.getUserName(5)).rejects.toEqual('Paul')", None), ("expect(a);", None), + ("expect().toBe();", None), ]; let fail = vec![ @@ -229,6 +232,7 @@ fn tests() { let fix = vec![ ("expect(files[\"length\"]).not.toBe(1);", "expect(files).not.toHaveLength(1);", None), + (r#"expect(files["length"]).toBe(1,);"#, "expect(files).toHaveLength(1,);", None), ( "expect(files[\"length\"])[\"resolves\"].toBe(1,);", "expect(files)[\"resolves\"].toHaveLength(1,);", diff --git a/crates/oxc_linter/src/rules/jest/prefer_todo.rs b/crates/oxc_linter/src/rules/jest/prefer_todo.rs index 28eaa2ce0..6e0ee4d6d 100644 --- a/crates/oxc_linter/src/rules/jest/prefer_todo.rs +++ b/crates/oxc_linter/src/rules/jest/prefer_todo.rs @@ -183,6 +183,8 @@ fn build_code<'a>(fixer: RuleFixer<'_, 'a>, expr: &CallExpression<'a>) -> RuleFi fn tests() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ("test()", None), ("test.concurrent()", None), @@ -227,6 +229,8 @@ fn tests() { ("test(`i need to write this test`);", "test.todo(`i need to write this test`);", None), ("it.skip('foo', function () {})", "it.todo('foo')", None), ("it(`i need to write this test`, () => {})", "it.todo(`i need to write this test`)", None), + ("it('foo', function () {})", "it.todo('foo')", None), + ("it('foo', () => {})", "it.todo('foo')", None), ( "test.skip('i need to write this test', () => {});", "test.todo('i need to write this test');", diff --git a/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs b/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs index 50e20c600..a7b24bd52 100644 --- a/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs +++ b/crates/oxc_linter/src/rules/jest/require_to_throw_message.rs @@ -91,6 +91,8 @@ impl RequireToThrowMessage { fn test() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ // String ("expect(() => { throw new Error('a'); }).toThrow('a');", None), diff --git a/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs b/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs index 7d7722209..8699ea65e 100644 --- a/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs +++ b/crates/oxc_linter/src/rules/jest/require_top_level_describe.rs @@ -194,6 +194,8 @@ impl RequireTopLevelDescribe { fn test() { use crate::tester::Tester; + // Note: Both Jest and Vitest share the same unit tests + let pass = vec![ ("it.each()", None), ("describe(\"test suite\", () => { test(\"my test\") });", None), diff --git a/crates/oxc_linter/src/snapshots/jest_max_expects.snap b/crates/oxc_linter/src/snapshots/jest_max_expects.snap index 5f9465828..9cb7e6e91 100644 --- a/crates/oxc_linter/src/snapshots/jest_max_expects.snap +++ b/crates/oxc_linter/src/snapshots/jest_max_expects.snap @@ -82,3 +82,39 @@ snapshot_kind: text 5 │ }); ╰──── help: Too many assertion calls (2) - maximum allowed is 1 + + ⚠ eslint-plugin-jest(max-expects): Enforces a maximum number assertion calls in a test body. + ╭─[max_expects.tsx:7:11] + 6 │ expect(true).toBeDefined(); + 7 │ expect(true).toBeDefined(); + · ────── + 8 │ }); + ╰──── + help: Too many assertion calls (6) - maximum allowed is 5 + + ⚠ eslint-plugin-jest(max-expects): Enforces a maximum number assertion calls in a test body. + ╭─[max_expects.tsx:7:10] + 6 │ expect(true).toBeDefined(); + 7 │ expect(true).toBeDefined(); + · ────── + 8 │ }); + ╰──── + help: Too many assertion calls (6) - maximum allowed is 5 + + ⚠ eslint-plugin-jest(max-expects): Enforces a maximum number assertion calls in a test body. + ╭─[max_expects.tsx:15:10] + 14 │ expect(true).toBeDefined(); + 15 │ expect(true).toBeDefined(); + · ────── + 16 │ }); + ╰──── + help: Too many assertion calls (6) - maximum allowed is 5 + + ⚠ eslint-plugin-jest(max-expects): Enforces a maximum number assertion calls in a test body. + ╭─[max_expects.tsx:3:10] + 2 │ expect(true).toBeDefined(); + 3 │ expect(true).toBeDefined(); + · ────── + 4 │ }); + ╰──── + help: Too many assertion calls (2) - maximum allowed is 1 diff --git a/crates/oxc_linter/src/snapshots/jest_max_nested_describe.snap b/crates/oxc_linter/src/snapshots/jest_max_nested_describe.snap index 47444b8b9..9e128348e 100644 --- a/crates/oxc_linter/src/snapshots/jest_max_nested_describe.snap +++ b/crates/oxc_linter/src/snapshots/jest_max_nested_describe.snap @@ -117,3 +117,31 @@ snapshot_kind: text 7 │ }); ╰──── help: Too many nested describe calls (2) - maximum allowed is 1 + + ⚠ eslint-plugin-jest(max-nested-describe): Enforces a maximum depth to nested describe calls. + ╭─[max_nested_describe.tsx:7:37] + 6 │ describe('another suite', () => { + 7 │ ╭─▶ describe('another suite', () => { + 8 │ │ + 9 │ ╰─▶ }) + 10 │ }) + ╰──── + help: Too many nested describe calls (6) - maximum allowed is 5 + + ⚠ eslint-plugin-jest(max-nested-describe): Enforces a maximum depth to nested describe calls. + ╭─[max_nested_describe.tsx:7:37] + 6 │ describe('another suite', () => { + 7 │ ╭─▶ describe('another suite', () => { + 8 │ │ it('skipped test', () => { + 9 │ │ // Test skipped, as tests are running in Only mode + 10 │ │ assert.equal(Math.sqrt(4), 3) + 11 │ │ }) + 12 │ │ + 13 │ │ it.only('test', () => { + 14 │ │ // Only this test (and others marked with only) are run + 15 │ │ assert.equal(Math.sqrt(4), 2) + 16 │ │ }) + 17 │ ╰─▶ }) + 18 │ }) + ╰──── + help: Too many nested describe calls (6) - maximum allowed is 5 diff --git a/crates/oxc_linter/src/snapshots/jest_no_duplicate_hooks.snap b/crates/oxc_linter/src/snapshots/jest_no_duplicate_hooks.snap index 6fcbccad9..891dc6e63 100644 --- a/crates/oxc_linter/src/snapshots/jest_no_duplicate_hooks.snap +++ b/crates/oxc_linter/src/snapshots/jest_no_duplicate_hooks.snap @@ -163,3 +163,57 @@ snapshot_kind: text 12 │ ╰──── help: Describe blocks can only have one of each hook. Consider consolidating the duplicate hooks into a single call. + + ⚠ eslint-plugin-jest(no-duplicate-hooks): Duplicate "beforeEach" in describe block. + ╭─[no_duplicate_hooks.tsx:4:21] + 3 │ beforeEach(() => {}), + 4 │ beforeEach(() => {}), + · ──────────────────── + 5 │ test("bar", () => { + ╰──── + help: Describe blocks can only have one of each hook. Consider consolidating the duplicate hooks into a single call. + + ⚠ eslint-plugin-jest(no-duplicate-hooks): Duplicate "afterEach" in describe block. + ╭─[no_duplicate_hooks.tsx:4:21] + 3 │ afterEach(() => {}), + 4 │ afterEach(() => {}), + · ─────────────────── + 5 │ test("bar", () => { + ╰──── + help: Describe blocks can only have one of each hook. Consider consolidating the duplicate hooks into a single call. + + ⚠ eslint-plugin-jest(no-duplicate-hooks): Duplicate "beforeEach" in describe block. + ╭─[no_duplicate_hooks.tsx:11:21] + 10 │ beforeEach(() => {}), + 11 │ beforeEach(() => {}), + · ──────────────────── + 12 │ beforeAll(() => {}), + ╰──── + help: Describe blocks can only have one of each hook. Consider consolidating the duplicate hooks into a single call. + + ⚠ eslint-plugin-jest(no-duplicate-hooks): Duplicate "beforeEach" in describe block. + ╭─[no_duplicate_hooks.tsx:11:21] + 10 │ beforeEach(() => {}), + 11 │ beforeEach(() => {}), + · ──────────────────── + 12 │ beforeAll(() => {}), + ╰──── + help: Describe blocks can only have one of each hook. Consider consolidating the duplicate hooks into a single call. + + ⚠ eslint-plugin-jest(no-duplicate-hooks): Duplicate "beforeEach" in describe block. + ╭─[no_duplicate_hooks.tsx:4:21] + 3 │ beforeEach(() => {}); + 4 │ beforeEach(() => {}); + · ──────────────────── + 5 │ + ╰──── + help: Describe blocks can only have one of each hook. Consider consolidating the duplicate hooks into a single call. + + ⚠ eslint-plugin-jest(no-duplicate-hooks): Duplicate "beforeEach" in describe block. + ╭─[no_duplicate_hooks.tsx:11:25] + 10 │ beforeEach(() => {}); + 11 │ beforeEach(() => {}); + · ──────────────────── + 12 │ + ╰──── + help: Describe blocks can only have one of each hook. Consider consolidating the duplicate hooks into a single call. diff --git a/crates/oxc_linter/src/snapshots/jest_no_hooks.snap b/crates/oxc_linter/src/snapshots/jest_no_hooks.snap index 1222b6c35..0d22da2c3 100644 --- a/crates/oxc_linter/src/snapshots/jest_no_hooks.snap +++ b/crates/oxc_linter/src/snapshots/jest_no_hooks.snap @@ -39,3 +39,41 @@ snapshot_kind: text · ────────── 6 │ ╰──── + + ⚠ eslint-plugin-jest(no-hooks): Do not use setup or teardown hooks + ╭─[no_hooks.tsx:1:1] + 1 │ beforeAll(() => {}) + · ───────── + ╰──── + + ⚠ eslint-plugin-jest(no-hooks): Do not use setup or teardown hooks + ╭─[no_hooks.tsx:1:1] + 1 │ beforeEach(() => {}) + · ────────── + ╰──── + + ⚠ eslint-plugin-jest(no-hooks): Do not use setup or teardown hooks + ╭─[no_hooks.tsx:1:1] + 1 │ afterAll(() => {}) + · ──────── + ╰──── + + ⚠ eslint-plugin-jest(no-hooks): Do not use setup or teardown hooks + ╭─[no_hooks.tsx:1:1] + 1 │ afterEach(() => {}) + · ───────── + ╰──── + + ⚠ eslint-plugin-jest(no-hooks): Do not use setup or teardown hooks + ╭─[no_hooks.tsx:1:1] + 1 │ beforeEach(() => {}); afterEach(() => { vi.resetModules() }); + · ────────── + ╰──── + + ⚠ eslint-plugin-jest(no-hooks): Do not use setup or teardown hooks + ╭─[no_hooks.tsx:4:8] + 3 │ afterEach(() => {}); + 4 │ beforeEach(() => { vi.resetModules() }); + · ────────── + 5 │ + ╰──── diff --git a/crates/oxc_linter/src/snapshots/jest_prefer_comparison_matcher.snap b/crates/oxc_linter/src/snapshots/jest_prefer_comparison_matcher.snap index 7bc8b5fc3..6578bc20f 100644 --- a/crates/oxc_linter/src/snapshots/jest_prefer_comparison_matcher.snap +++ b/crates/oxc_linter/src/snapshots/jest_prefer_comparison_matcher.snap @@ -1261,3 +1261,52 @@ snapshot_kind: text · ─────────────── ╰──── help: Prefer using `"toBeLessThanOrEqual"` instead + + ⚠ eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers + ╭─[prefer_comparison_matcher.tsx:1:15] + 1 │ expect(a > b).toBe(true) + · ──── + ╰──── + help: Prefer using `"toBeGreaterThan"` instead + + ⚠ eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers + ╭─[prefer_comparison_matcher.tsx:1:15] + 1 │ expect(a < b).toBe(true) + · ──── + ╰──── + help: Prefer using `"toBeLessThan"` instead + + ⚠ eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers + ╭─[prefer_comparison_matcher.tsx:1:16] + 1 │ expect(a >= b).toBe(true) + · ──── + ╰──── + help: Prefer using `"toBeGreaterThanOrEqual"` instead + + ⚠ eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers + ╭─[prefer_comparison_matcher.tsx:1:16] + 1 │ expect(a <= b).toBe(true) + · ──── + ╰──── + help: Prefer using `"toBeLessThanOrEqual"` instead + + ⚠ eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers + ╭─[prefer_comparison_matcher.tsx:1:19] + 1 │ expect(a > b).not.toBe(true) + · ──── + ╰──── + help: Prefer using `"toBeLessThanOrEqual"` instead + + ⚠ eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers + ╭─[prefer_comparison_matcher.tsx:1:19] + 1 │ expect(a < b).not.toBe(true) + · ──── + ╰──── + help: Prefer using `"toBeGreaterThanOrEqual"` instead + + ⚠ eslint-plugin-jest(prefer-comparison-matcher): Suggest using the built-in comparison matchers + ╭─[prefer_comparison_matcher.tsx:1:20] + 1 │ expect(a >= b).not.toBe(true) + · ──── + ╰──── + help: Prefer using `"toBeLessThan"` instead diff --git a/crates/oxc_linter/src/snapshots/jest_prefer_equality_matcher.snap b/crates/oxc_linter/src/snapshots/jest_prefer_equality_matcher.snap index 1125db4c2..a78199746 100644 --- a/crates/oxc_linter/src/snapshots/jest_prefer_equality_matcher.snap +++ b/crates/oxc_linter/src/snapshots/jest_prefer_equality_matcher.snap @@ -57,3 +57,136 @@ snapshot_kind: text · ──── ╰──── help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:17] + 1 │ expect(a === b).toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:18] + 1 │ expect(a === b,).toBe(true,); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:17] + 1 │ expect(a === b).toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:26] + 1 │ expect(a === b).resolves.toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:26] + 1 │ expect(a === b).resolves.toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:21] + 1 │ expect(a === b).not.toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:21] + 1 │ expect(a === b).not.toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:30] + 1 │ expect(a === b).resolves.not.toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:30] + 1 │ expect(a === b).resolves.not.toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:33] + 1 │ expect(a === b)["resolves"].not.toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:36] + 1 │ expect(a === b)["resolves"]["not"]["toBe"](false); + · ────── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:17] + 1 │ expect(a !== b).toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:17] + 1 │ expect(a !== b).toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:26] + 1 │ expect(a !== b).resolves.toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:26] + 1 │ expect(a !== b).resolves.toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:21] + 1 │ expect(a !== b).not.toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:21] + 1 │ expect(a !== b).not.toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:30] + 1 │ expect(a !== b).resolves.not.toBe(true); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead + + ⚠ eslint-plugin-jest(prefer-equality-matcher): Suggest using the built-in equality matchers. + ╭─[prefer_equality_matcher.tsx:1:30] + 1 │ expect(a !== b).resolves.not.toBe(false); + · ──── + ╰──── + help: Prefer using one of the equality matchers instead diff --git a/crates/oxc_linter/src/snapshots/jest_prefer_mock_promise_shorthand.snap b/crates/oxc_linter/src/snapshots/jest_prefer_mock_promise_shorthand.snap index ee8092050..129d50656 100644 --- a/crates/oxc_linter/src/snapshots/jest_prefer_mock_promise_shorthand.snap +++ b/crates/oxc_linter/src/snapshots/jest_prefer_mock_promise_shorthand.snap @@ -206,3 +206,185 @@ snapshot_kind: text · ─────────────── ╰──── help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:9] + 1 │ vi.fn().mockImplementation(() => Promise.resolve(42)) + · ────────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:9] + 1 │ vi.fn().mockImplementation(() => Promise.reject(42)) + · ────────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockImplementation(() => Promise.resolve(42)) + · ────────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockImplementation(() => { return Promise.resolve(42) }) + · ────────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockImplementation(() => Promise.reject(42)) + · ────────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockImplementation(() => Promise.reject(42),) + · ────────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockImplementationOnce(() => Promise.resolve(42)) + · ────────────────────── + ╰──── + help: Prefer "mockResolvedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockImplementationOnce(() => Promise.reject(42)) + · ────────────────────── + ╰──── + help: Prefer "mockRejectedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:9] + 1 │ vi.fn().mockReturnValue(Promise.resolve(42)) + · ─────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:9] + 1 │ vi.fn().mockReturnValue(Promise.reject(42)) + · ─────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValue(Promise.resolve(42)) + · ─────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValue(Promise.reject(42)) + · ─────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValueOnce(Promise.resolve(42)) + · ─────────────────── + ╰──── + help: Prefer "mockResolvedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValueOnce(Promise.reject(42)) + · ─────────────────── + ╰──── + help: Prefer "mockRejectedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValue(Promise.resolve({ target: 'world', message: 'hello' })) + · ─────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:102] + 1 │ aVariable.mockImplementation(() => Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValue(Promise.reject(42)) + · ─────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:56] + 1 │ aVariable.mockImplementation(() => Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValue(Promise.reject(42)) + · ────────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockImplementation(() => Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValue(Promise.reject(42)) + · ────────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:97] + 1 │ aVariable.mockReturnValueOnce(Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValueOnce(Promise.reject(42)) + · ─────────────────── + ╰──── + help: Prefer "mockRejectedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:51] + 1 │ aVariable.mockReturnValueOnce(Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValueOnce(Promise.reject(42)) + · ────────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValueOnce(Promise.reject(42)).mockImplementation(() => Promise.resolve(42)).mockReturnValueOnce(Promise.reject(42)) + · ─────────────────── + ╰──── + help: Prefer "mockRejectedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValueOnce(Promise.reject(new Error('oh noes!'))) + · ─────────────────── + ╰──── + help: Prefer "mockRejectedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:9] + 1 │ vi.fn().mockReturnValue(Promise.resolve(42), xyz) + · ─────────────── + ╰──── + help: Prefer "mockResolvedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:9] + 1 │ vi.fn().mockImplementation(() => Promise.reject(42), xyz) + · ────────────────── + ╰──── + help: Prefer "mockRejectedValue" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValueOnce(Promise.resolve(42, xyz)) + · ─────────────────── + ╰──── + help: Prefer "mockResolvedValueOnce" + + ⚠ eslint-plugin-jest(prefer-mock-promise-shorthand): Prefer mock resolved/rejected shorthands for promises + ╭─[prefer_mock_promise_shorthand.tsx:1:11] + 1 │ aVariable.mockReturnValueOnce(Promise.resolve()) + · ─────────────────── + ╰──── + help: Prefer "mockResolvedValueOnce" diff --git a/crates/oxc_linter/src/utils/mod.rs b/crates/oxc_linter/src/utils/mod.rs index 457338cee..42347d2de 100644 --- a/crates/oxc_linter/src/utils/mod.rs +++ b/crates/oxc_linter/src/utils/mod.rs @@ -20,17 +20,34 @@ pub use self::{ const VITEST_COMPATIBLE_JEST_RULES: phf::Set<&'static str> = phf::phf_set! { "consistent-test-it", "expect-expect", + "max-expects", + "max-nested-describe", "no-alias-methods", + "no-commented-out-tests", "no-conditional-expect", "no-conditional-in-test", - "no-commented-out-tests", "no-disabled-tests", + "no-duplicate-hooks", "no-focused-tests", + "no-hooks", "no-identical-title", + "no-interpolation-in-snapshots", "no-restricted-jest-methods", + "no-restricted-matchers", "no-test-prefixes", + "no-test-return-statement", + "prefer-comparison-matcher", "prefer-each", + "prefer-equality-matcher", + "prefer-expect-resolves", "prefer-hooks-in-order", + "prefer-hooks-on-top", + "prefer-mock-promise-shorthand", + "prefer-strict-equal", + "prefer-to-have-length", + "prefer-todo", + "require-to-throw-message", + "require-top-level-describe", "valid-describe-callback", "valid-expect", };