diff --git a/packages/toolkit/src/entities/tests/sorted_state_adapter.test.ts b/packages/toolkit/src/entities/tests/sorted_state_adapter.test.ts index c1a2ebaa88..b430574bf1 100644 --- a/packages/toolkit/src/entities/tests/sorted_state_adapter.test.ts +++ b/packages/toolkit/src/entities/tests/sorted_state_adapter.test.ts @@ -591,9 +591,16 @@ describe('Sorted State Adapter', () => { adapter.removeAll(draft) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object {}, - "ids": Array [], + { + "entities": { + "tgg": { + "id": "tgg", + "title": "The Great Gatsby", + }, + }, + "ids": [ + "tgg", + ], } `) }) @@ -604,14 +611,19 @@ describe('Sorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { + { + "entities": { + "af": { + "id": "af", + "title": "Animal Farm", + }, + "tgg": { "id": "tgg", "title": "The Great Gatsby", }, }, - "ids": Array [ + "ids": [ + "af", "tgg", ], } @@ -624,18 +636,18 @@ describe('Sorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { + { + "entities": { + "af": { "id": "af", "title": "Animal Farm", }, - "tgg": Object { + "tgg": { "id": "tgg", "title": "The Great Gatsby", }, }, - "ids": Array [ + "ids": [ "af", "tgg", ], @@ -679,14 +691,14 @@ describe('Sorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { + { + "entities": { + "tgg": { "id": "tgg", "title": "A New Hope", }, }, - "ids": Array [ + "ids": [ "tgg", ], } @@ -714,23 +726,23 @@ describe('Sorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "aco": Object { + { + "entities": { + "aco": { "id": "aco", "title": "Third Change", }, - "tgg": Object { + "tgg": { "id": "tgg", "title": "Second Change", }, - "th": Object { + "th": { "author": "Fourth Change", "id": "th", "title": "First Change", }, }, - "ids": Array [ + "ids": [ "th", "tgg", "aco", @@ -744,14 +756,14 @@ describe('Sorted State Adapter', () => { adapter.upsertOne(draft, TheGreatGatsby) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { + { + "entities": { + "tgg": { "id": "tgg", - "title": "The Great Gatsby", + "title": "A New Hope", }, }, - "ids": Array [ + "ids": [ "tgg", ], } @@ -767,15 +779,20 @@ describe('Sorted State Adapter', () => { }) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { + { + "entities": { + "af": { + "id": "af", + "title": "Animal Farm", + }, + "tgg": { "id": "tgg", "title": "A New Hope", }, }, - "ids": Array [ + "ids": [ "tgg", + "af", ], } `) @@ -793,20 +810,15 @@ describe('Sorted State Adapter', () => { ]) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { - "id": "af", - "title": "Animal Farm", - }, - "tgg": Object { + { + "entities": { + "tgg": { "id": "tgg", - "title": "A New Hope", + "title": "The Great Gatsby", }, }, - "ids": Array [ + "ids": [ "tgg", - "af", ], } `) @@ -817,15 +829,15 @@ describe('Sorted State Adapter', () => { adapter.setOne(draft, TheGreatGatsby) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { - "id": "tgg", - "title": "The Great Gatsby", + { + "entities": { + "th": { + "id": "th", + "title": "Silmarillion", }, }, - "ids": Array [ - "tgg", + "ids": [ + "th", ], } `) @@ -840,14 +852,19 @@ describe('Sorted State Adapter', () => { }) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "th": Object { + { + "entities": { + "af": { + "id": "af", + "title": "Animal Farm", + }, + "th": { "id": "th", "title": "Silmarillion", }, }, - "ids": Array [ + "ids": [ + "af", "th", ], } @@ -866,20 +883,15 @@ describe('Sorted State Adapter', () => { ]) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { + { + "entities": { + "af": { "id": "af", "title": "Animal Farm", }, - "th": Object { - "id": "th", - "title": "Silmarillion", - }, }, - "ids": Array [ + "ids": [ "af", - "th", ], } `) @@ -891,15 +903,15 @@ describe('Sorted State Adapter', () => { adapter.removeOne(draft, TheGreatGatsby.id) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { - "id": "af", - "title": "Animal Farm", + { + "entities": { + "aco": { + "id": "aco", + "title": "A Clockwork Orange", }, }, - "ids": Array [ - "af", + "ids": [ + "aco", ], } `) diff --git a/packages/toolkit/src/entities/tests/unsorted_state_adapter.test.ts b/packages/toolkit/src/entities/tests/unsorted_state_adapter.test.ts index 092522fff5..c58dafebd0 100644 --- a/packages/toolkit/src/entities/tests/unsorted_state_adapter.test.ts +++ b/packages/toolkit/src/entities/tests/unsorted_state_adapter.test.ts @@ -436,9 +436,16 @@ describe('Unsorted State Adapter', () => { adapter.removeAll(draft) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object {}, - "ids": Array [], + { + "entities": { + "tgg": { + "id": "tgg", + "title": "The Great Gatsby", + }, + }, + "ids": [ + "tgg", + ], } `) }) @@ -469,18 +476,18 @@ describe('Unsorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { + { + "entities": { + "af": { "id": "af", "title": "Animal Farm", }, - "tgg": Object { + "tgg": { "id": "tgg", "title": "The Great Gatsby", }, }, - "ids": Array [ + "ids": [ "tgg", "af", ], @@ -494,18 +501,18 @@ describe('Unsorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { + { + "entities": { + "af": { "id": "af", "title": "Animal Farm", }, - "tgg": Object { + "tgg": { "id": "tgg", "title": "The Great Gatsby", }, }, - "ids": Array [ + "ids": [ "tgg", "af", ], @@ -524,14 +531,14 @@ describe('Unsorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { + { + "entities": { + "tgg": { "id": "tgg", "title": "A New Hope", }, }, - "ids": Array [ + "ids": [ "tgg", ], } @@ -559,23 +566,23 @@ describe('Unsorted State Adapter', () => { }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "aco": Object { + { + "entities": { + "aco": { "id": "aco", "title": "Third Change", }, - "tgg": Object { + "tgg": { "id": "tgg", "title": "Second Change", }, - "th": Object { + "th": { "author": "Fourth Change", "id": "th", "title": "First Change", }, }, - "ids": Array [ + "ids": [ "tgg", "aco", "th", @@ -589,14 +596,14 @@ describe('Unsorted State Adapter', () => { adapter.upsertOne(draft, TheGreatGatsby) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { + { + "entities": { + "tgg": { "id": "tgg", "title": "The Great Gatsby", }, }, - "ids": Array [ + "ids": [ "tgg", ], } @@ -612,14 +619,14 @@ describe('Unsorted State Adapter', () => { }) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { + { + "entities": { + "tgg": { "id": "tgg", "title": "A New Hope", }, }, - "ids": Array [ + "ids": [ "tgg", ], } @@ -638,20 +645,15 @@ describe('Unsorted State Adapter', () => { ]) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { - "id": "af", - "title": "Animal Farm", - }, - "tgg": Object { + { + "entities": { + "tgg": { "id": "tgg", - "title": "A New Hope", + "title": "The Great Gatsby", }, }, - "ids": Array [ + "ids": [ "tgg", - "af", ], } `) @@ -662,15 +664,15 @@ describe('Unsorted State Adapter', () => { adapter.setOne(draft, TheGreatGatsby) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "tgg": Object { - "id": "tgg", - "title": "The Great Gatsby", + { + "entities": { + "th": { + "id": "th", + "title": "Silmarillion", }, }, - "ids": Array [ - "tgg", + "ids": [ + "th", ], } `) @@ -685,15 +687,20 @@ describe('Unsorted State Adapter', () => { }) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "th": Object { + { + "entities": { + "af": { + "id": "af", + "title": "Animal Farm", + }, + "th": { "id": "th", "title": "Silmarillion", }, }, - "ids": Array [ + "ids": [ "th", + "af", ], } `) @@ -711,19 +718,14 @@ describe('Unsorted State Adapter', () => { ]) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { + { + "entities": { + "af": { "id": "af", "title": "Animal Farm", }, - "th": Object { - "id": "th", - "title": "Silmarillion", - }, }, - "ids": Array [ - "th", + "ids": [ "af", ], } @@ -736,15 +738,15 @@ describe('Unsorted State Adapter', () => { adapter.removeOne(draft, TheGreatGatsby.id) }) expect(result).toMatchInlineSnapshot(` - Object { - "entities": Object { - "af": Object { - "id": "af", - "title": "Animal Farm", + { + "entities": { + "aco": { + "id": "aco", + "title": "A Clockwork Orange", }, }, - "ids": Array [ - "af", + "ids": [ + "aco", ], } `) diff --git a/packages/toolkit/src/entities/tests/utils.spec.ts b/packages/toolkit/src/entities/tests/utils.spec.ts index 3e72a03ec5..cc4053cd18 100644 --- a/packages/toolkit/src/entities/tests/utils.spec.ts +++ b/packages/toolkit/src/entities/tests/utils.spec.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import { AClockworkOrange } from './fixtures/book' describe('Entity utils', () => { @@ -5,35 +6,35 @@ describe('Entity utils', () => { const OLD_ENV = process.env beforeEach(() => { - jest.resetModules() // this is important - it clears the cache + vi.resetModules() // this is important - it clears the cache process.env = { ...OLD_ENV, NODE_ENV: 'development' } }) afterEach(() => { process.env = OLD_ENV - jest.resetAllMocks() + vi.resetAllMocks() }) - it('should not warn when key does exist', () => { - const { selectIdValue } = require('../utils') - const spy = jest.spyOn(console, 'warn') + it('should not warn when key does exist', async () => { + const { selectIdValue } = await import('../utils') + const spy = vi.spyOn(console, 'warn') selectIdValue(AClockworkOrange, (book: any) => book.id) expect(spy).not.toHaveBeenCalled() }) - it('should warn when key does not exist in dev mode', () => { - const { selectIdValue } = require('../utils') - const spy = jest.spyOn(console, 'warn') + it('should warn when key does not exist in dev mode', async () => { + const { selectIdValue } = await import('../utils') + const spy = vi.spyOn(console, 'warn') selectIdValue(AClockworkOrange, (book: any) => book.foo) expect(spy).toHaveBeenCalled() }) - it('should warn when key is undefined in dev mode', () => { - const { selectIdValue } = require('../utils') - const spy = jest.spyOn(console, 'warn') + it('should warn when key is undefined in dev mode', async () => { + const { selectIdValue } = await import('../utils') + const spy = vi.spyOn(console, 'warn') const undefinedAClockworkOrange = { ...AClockworkOrange, id: undefined } selectIdValue(undefinedAClockworkOrange, (book: any) => book.id) @@ -41,20 +42,20 @@ describe('Entity utils', () => { expect(spy).toHaveBeenCalled() }) - it('should not warn when key does not exist in prod mode', () => { + it('should not warn when key does not exist in prod mode', async () => { process.env.NODE_ENV = 'production' - const { selectIdValue } = require('../utils') - const spy = jest.spyOn(console, 'warn') + const { selectIdValue } = await import('../utils') + const spy = vi.spyOn(console, 'warn') selectIdValue(AClockworkOrange, (book: any) => book.foo) expect(spy).not.toHaveBeenCalled() }) - it('should not warn when key is undefined in prod mode', () => { + it('should not warn when key is undefined in prod mode', async () => { process.env.NODE_ENV = 'production' - const { selectIdValue } = require('../utils') - const spy = jest.spyOn(console, 'warn') + const { selectIdValue } = await import('../utils') + const spy = vi.spyOn(console, 'warn') const undefinedAClockworkOrange = { ...AClockworkOrange, id: undefined } selectIdValue(undefinedAClockworkOrange, (book: any) => book.id) diff --git a/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts b/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts index 87ed4b1eef..0084c01b04 100644 --- a/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts +++ b/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts @@ -4,6 +4,7 @@ import { createSlice, isAnyOf, } from '@reduxjs/toolkit' +import { vi } from 'vitest' import type { AnyAction, PayloadAction, Action } from '@reduxjs/toolkit' @@ -58,7 +59,7 @@ describe('Saga-style Effects Scenarios', () => { beforeAll(() => { const noop = () => {} - jest.spyOn(console, 'error').mockImplementation(noop) + vi.spyOn(console, 'error').mockImplementation(noop) }) beforeEach(() => { diff --git a/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts b/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts index bba8fd1662..5417383685 100644 --- a/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts +++ b/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts @@ -4,6 +4,7 @@ import { createSlice, isAnyOf, } from '@reduxjs/toolkit' +import { vi, Mock } from 'vitest' import type { AnyAction, PayloadAction, Action } from '@reduxjs/toolkit' @@ -141,7 +142,7 @@ describe('createListenerMiddleware', () => { return new Promise((resolve) => setTimeout(resolve, ms)) } - let reducer: jest.Mock + let reducer: Mock let listenerMiddleware = createListenerMiddleware() let { middleware, startListening, stopListening, clearListeners } = listenerMiddleware @@ -157,7 +158,7 @@ describe('createListenerMiddleware', () => { type TestAction3 = ReturnType beforeAll(() => { - jest.spyOn(console, 'error').mockImplementation(noop) + vi.spyOn(console, 'error').mockImplementation(noop) }) beforeEach(() => { @@ -166,7 +167,7 @@ describe('createListenerMiddleware', () => { startListening = listenerMiddleware.startListening stopListening = listenerMiddleware.stopListening clearListeners = listenerMiddleware.clearListeners - reducer = jest.fn(() => ({})) + reducer = vi.fn(() => ({})) store = configureStore({ reducer, middleware: (gDM) => gDM().prepend(middleware), @@ -214,7 +215,7 @@ describe('createListenerMiddleware', () => { describe('Subscription and unsubscription', () => { test('directly subscribing', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) startListening({ actionCreator: testAction1, @@ -232,7 +233,7 @@ describe('createListenerMiddleware', () => { }) test('stopListening returns true if an entry has been unsubscribed, false otherwise', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) startListening({ actionCreator: testAction1, @@ -244,7 +245,7 @@ describe('createListenerMiddleware', () => { }) test('dispatch(removeListener({...})) returns true if an entry has been unsubscribed, false otherwise', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) startListening({ actionCreator: testAction1, @@ -270,7 +271,7 @@ describe('createListenerMiddleware', () => { }) test('can subscribe with a string action type', () => { - const effect = jest.fn((_: AnyAction) => {}) + const effect = vi.fn((_: AnyAction) => {}) store.dispatch( addListener({ @@ -289,7 +290,7 @@ describe('createListenerMiddleware', () => { }) test('can subscribe with a matcher function', () => { - const effect = jest.fn((_: AnyAction) => {}) + const effect = vi.fn((_: AnyAction) => {}) const isAction1Or2 = isAnyOf(testAction1, testAction2) @@ -356,7 +357,7 @@ describe('createListenerMiddleware', () => { }) test('subscribing with the same listener will not make it trigger twice (like EventTarget.addEventListener())', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) startListening({ actionCreator: testAction1, @@ -378,7 +379,7 @@ describe('createListenerMiddleware', () => { }) test('unsubscribing via callback', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) const unsubscribe = startListening({ actionCreator: testAction1, @@ -394,7 +395,7 @@ describe('createListenerMiddleware', () => { }) test('directly unsubscribing', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) startListening({ actionCreator: testAction1, @@ -415,7 +416,7 @@ describe('createListenerMiddleware', () => { }) test('subscribing via action', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) store.dispatch( addListener({ @@ -435,7 +436,7 @@ describe('createListenerMiddleware', () => { }) test('unsubscribing via callback from dispatch', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) const unsubscribe = store.dispatch( addListener({ @@ -456,7 +457,7 @@ describe('createListenerMiddleware', () => { }) test('unsubscribing via action', () => { - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) startListening({ actionCreator: testAction1, @@ -575,7 +576,7 @@ describe('createListenerMiddleware', () => { AnyAction, typeof store.getState, typeof store.dispatch - > = jest.fn() + > = vi.fn() startListening({ ...params, effect } as any) @@ -646,7 +647,7 @@ describe('createListenerMiddleware', () => { }) test('"can unsubscribe via middleware api', () => { - const effect = jest.fn( + const effect = vi.fn( (action: TestAction1, api: ListenerEffectAPI) => { if (action.payload === 'b') { api.unsubscribe() @@ -847,7 +848,7 @@ describe('createListenerMiddleware', () => { }) test('getOriginalState can only be invoked synchronously', async () => { - const onError = jest.fn() + const onError = vi.fn() const listenerMiddleware = createListenerMiddleware({ onError, @@ -897,7 +898,7 @@ describe('createListenerMiddleware', () => { test('by default, actions are forwarded to the store', () => { reducer.mockClear() - const effect = jest.fn((_: TestAction1) => {}) + const effect = vi.fn((_: TestAction1) => {}) startListening({ actionCreator: testAction1, @@ -962,7 +963,7 @@ describe('createListenerMiddleware', () => { }, }) - const effect = jest.fn(() => {}) + const effect = vi.fn(() => {}) startListening({ matcher, effect }) store.dispatch(testAction1('a')) @@ -971,8 +972,8 @@ describe('createListenerMiddleware', () => { test('Continues running other listeners if a predicate raises an error', () => { const matcher = (action: any): action is any => true - const firstListener = jest.fn(() => {}) - const secondListener = jest.fn(() => {}) + const firstListener = vi.fn(() => {}) + const secondListener = vi.fn(() => {}) startListening({ // @ts-expect-error @@ -992,12 +993,12 @@ describe('createListenerMiddleware', () => { }) test('Notifies sync listener errors to `onError`, if provided', async () => { - const onError = jest.fn() + const onError = vi.fn() const listenerMiddleware = createListenerMiddleware({ onError, }) const { middleware, startListening } = listenerMiddleware - reducer = jest.fn(() => ({})) + reducer = vi.fn(() => ({})) store = configureStore({ reducer, middleware: (gDM) => gDM().prepend(middleware), @@ -1023,12 +1024,12 @@ describe('createListenerMiddleware', () => { }) test('Notifies async listeners errors to `onError`, if provided', async () => { - const onError = jest.fn() + const onError = vi.fn() const listenerMiddleware = createListenerMiddleware({ onError, }) const { middleware, startListening } = listenerMiddleware - reducer = jest.fn(() => ({})) + reducer = vi.fn(() => ({})) store = configureStore({ reducer, middleware: (gDM) => gDM().prepend(middleware), diff --git a/packages/toolkit/src/query/tests/buildHooks.test.tsx b/packages/toolkit/src/query/tests/buildHooks.test.tsx index 19e4659e84..743b122599 100644 --- a/packages/toolkit/src/query/tests/buildHooks.test.tsx +++ b/packages/toolkit/src/query/tests/buildHooks.test.tsx @@ -1,4 +1,5 @@ import * as React from 'react' +import { vi, SpyInstance } from 'vitest' import type { UseMutation, UseQuery, @@ -672,7 +673,7 @@ describe('hooks tests', () => { ) }) test('with `selectFromResult`', async () => { - const selectFromResult = jest.fn((x) => x) + const selectFromResult = vi.fn((x) => x) const { result } = renderHook( () => api.endpoints.getUser.useQuery(5, { selectFromResult }), { @@ -718,10 +719,10 @@ describe('hooks tests', () => { }) describe('Hook middleware requirements', () => { - let mock: jest.SpyInstance + let mock: SpyInstance beforeEach(() => { - mock = jest.spyOn(console, 'error').mockImplementation(() => {}) + mock = vi.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { diff --git a/packages/toolkit/src/query/tests/buildThunks.test.tsx b/packages/toolkit/src/query/tests/buildThunks.test.tsx index 70fc0cab87..ed40bd9342 100644 --- a/packages/toolkit/src/query/tests/buildThunks.test.tsx +++ b/packages/toolkit/src/query/tests/buildThunks.test.tsx @@ -1,4 +1,5 @@ import { configureStore } from '@reduxjs/toolkit' +import { vi } from 'vitest' import { createApi } from '@reduxjs/toolkit/query/react' import { renderHook, waitFor } from '@testing-library/react' import type { BaseQueryApi } from '../baseQueryTypes' @@ -86,7 +87,7 @@ describe('re-triggering behavior on arg change', () => { middleware: (gDM) => gDM().concat(api.middleware), }) - const spy = jest.spyOn(getUser, 'initiate') + const spy = vi.spyOn(getUser, 'initiate') beforeEach(() => void spy.mockClear()) test('re-trigger on literal value change', async () => { @@ -101,7 +102,7 @@ describe('re-triggering behavior on arg change', () => { await waitFor(() => { expect(result.current.status).not.toBe('pending') }) - + expect(spy).toHaveBeenCalledTimes(1) for (let x = 1; x < 3; x++) { diff --git a/packages/toolkit/src/query/tests/cacheCollection.test.ts b/packages/toolkit/src/query/tests/cacheCollection.test.ts index 29ccce1fb4..853ee4b1cd 100644 --- a/packages/toolkit/src/query/tests/cacheCollection.test.ts +++ b/packages/toolkit/src/query/tests/cacheCollection.test.ts @@ -1,5 +1,6 @@ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query' import { configureStore } from '@reduxjs/toolkit' +import { vi } from 'vitest' import { waitMs } from './helpers' import type { Middleware, Reducer } from 'redux' import { @@ -8,15 +9,17 @@ import { } from '../core/buildMiddleware/cacheCollection' beforeAll(() => { - jest.useFakeTimers('legacy') + vi.useFakeTimers() }) -const onCleanup = jest.fn() +const onCleanup = vi.fn() beforeEach(() => { onCleanup.mockClear() }) +test('abc', () => {}) + test(`query: await cleanup, defaults`, async () => { const { store, api } = storeForApi( createApi({ @@ -30,9 +33,9 @@ test(`query: await cleanup, defaults`, async () => { ) store.dispatch(api.endpoints.query.initiate('arg')).unsubscribe() - jest.advanceTimersByTime(59000), await waitMs() + vi.advanceTimersByTime(59000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + vi.advanceTimersByTime(2000) expect(onCleanup).toHaveBeenCalled() }) @@ -50,9 +53,9 @@ test(`query: await cleanup, keepUnusedDataFor set`, async () => { ) store.dispatch(api.endpoints.query.initiate('arg')).unsubscribe() - jest.advanceTimersByTime(28000), await waitMs() + vi.advanceTimersByTime(28000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + vi.advanceTimersByTime(2000) expect(onCleanup).toHaveBeenCalled() }) @@ -72,17 +75,16 @@ test(`query: handles large keepUnuseDataFor values over 32-bit ms`, async () => store.dispatch(api.endpoints.query.initiate('arg')).unsubscribe() // Shouldn't have been called right away - jest.advanceTimersByTime(1000), await waitMs() + vi.advanceTimersByTime(1000) expect(onCleanup).not.toHaveBeenCalled() // Shouldn't have been called any time in the next few minutes - jest.advanceTimersByTime(1_000_000), await waitMs() + vi.advanceTimersByTime(1_000_000) expect(onCleanup).not.toHaveBeenCalled() // _Should_ be called _wayyyy_ in the future (like 24.8 days from now) - jest.advanceTimersByTime(THIRTY_TWO_BIT_MAX_TIMER_SECONDS * 1000), - await waitMs() - expect(onCleanup).toHaveBeenCalled() + vi.advanceTimersByTime(THIRTY_TWO_BIT_MAX_TIMER_SECONDS * 1000), + expect(onCleanup).toHaveBeenCalled() }) describe(`query: await cleanup, keepUnusedDataFor set`, () => { @@ -112,17 +114,17 @@ describe(`query: await cleanup, keepUnusedDataFor set`, () => { test('global keepUnusedDataFor', async () => { store.dispatch(api.endpoints.query.initiate('arg')).unsubscribe() - jest.advanceTimersByTime(28000), await waitMs() + vi.advanceTimersByTime(28000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + vi.advanceTimersByTime(2000) expect(onCleanup).toHaveBeenCalled() }) test('endpoint keepUnusedDataFor', async () => { store.dispatch(api.endpoints.query2.initiate('arg')).unsubscribe() - jest.advanceTimersByTime(34000), await waitMs() + vi.advanceTimersByTime(34000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + vi.advanceTimersByTime(2000) expect(onCleanup).toHaveBeenCalled() }) @@ -130,8 +132,7 @@ describe(`query: await cleanup, keepUnusedDataFor set`, () => { expect(onCleanup).not.toHaveBeenCalled() store.dispatch(api.endpoints.query3.initiate('arg')).unsubscribe() expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(1) - await waitMs() + vi.advanceTimersByTime(1) expect(onCleanup).toHaveBeenCalled() }) @@ -139,7 +140,7 @@ describe(`query: await cleanup, keepUnusedDataFor set`, () => { expect(onCleanup).not.toHaveBeenCalled() store.dispatch(api.endpoints.query4.initiate('arg')).unsubscribe() expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(THIRTY_TWO_BIT_MAX_INT) + vi.advanceTimersByTime(THIRTY_TWO_BIT_MAX_INT) expect(onCleanup).not.toHaveBeenCalled() }) }) diff --git a/packages/toolkit/src/query/tests/cacheLifecycle.test.ts b/packages/toolkit/src/query/tests/cacheLifecycle.test.ts index 9b9284d857..f71acaab2d 100644 --- a/packages/toolkit/src/query/tests/cacheLifecycle.test.ts +++ b/packages/toolkit/src/query/tests/cacheLifecycle.test.ts @@ -1,10 +1,17 @@ import { createApi } from '@reduxjs/toolkit/query' import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query' +import { vi } from 'vitest' import { fetchBaseQuery } from '@reduxjs/toolkit/query' -import { expectType, fakeTimerWaitFor, setupApiStore, waitMs } from './helpers' +import { + expectType, + fakeTimerWaitFor, + setupApiStore, + // waitMs, + DEFAULT_DELAY_MS, +} from './helpers' beforeAll(() => { - jest.useFakeTimers('legacy') + vi.useFakeTimers() }) const api = createApi({ @@ -13,10 +20,10 @@ const api = createApi({ }) const storeRef = setupApiStore(api) -const onNewCacheEntry = jest.fn() -const gotFirstValue = jest.fn() -const onCleanup = jest.fn() -const onCatch = jest.fn() +const onNewCacheEntry = vi.fn() +const gotFirstValue = vi.fn() +const onCleanup = vi.fn() +const onCatch = vi.fn() beforeEach(() => { onNewCacheEntry.mockClear() @@ -68,11 +75,12 @@ describe.each([['query'], ['mutation']] as const)( expect(onNewCacheEntry).toHaveBeenCalledWith('arg') expect(onCleanup).not.toHaveBeenCalled() - promise.unsubscribe(), await waitMs() + promise.unsubscribe() + await vi.advanceTimersByTimeAsync(DEFAULT_DELAY_MS) if (type === 'query') { - jest.advanceTimersByTime(59000), await waitMs() + await vi.advanceTimersByTimeAsync(59000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + await vi.advanceTimersByTimeAsync(2000) } expect(onCleanup).toHaveBeenCalled() @@ -121,11 +129,12 @@ describe.each([['query'], ['mutation']] as const)( }) expect(onCleanup).not.toHaveBeenCalled() - promise.unsubscribe(), await waitMs() + promise.unsubscribe() + await vi.advanceTimersByTimeAsync(DEFAULT_DELAY_MS) if (type === 'query') { - jest.advanceTimersByTime(59000), await waitMs() + await vi.advanceTimersByTimeAsync(59000) //, await waitMs() expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + await vi.advanceTimersByTimeAsync(2000) //, await waitMs() } expect(onCleanup).toHaveBeenCalled() @@ -158,9 +167,10 @@ describe.each([['query'], ['mutation']] as const)( ) expect(onNewCacheEntry).toHaveBeenCalledWith('arg') - promise.unsubscribe(), await waitMs() + promise.unsubscribe() + await vi.advanceTimersByTimeAsync(DEFAULT_DELAY_MS) if (type === 'query') { - jest.advanceTimersByTime(120000), await waitMs() + await vi.advanceTimersByTimeAsync(120000) } expect(gotFirstValue).not.toHaveBeenCalled() expect(onCleanup).not.toHaveBeenCalled() @@ -196,11 +206,13 @@ describe.each([['query'], ['mutation']] as const)( ) expect(onNewCacheEntry).toHaveBeenCalledWith('arg') - promise.unsubscribe(), await waitMs() + promise.unsubscribe() + await vi.advanceTimersByTimeAsync(DEFAULT_DELAY_MS) + if (type === 'query') { - jest.advanceTimersByTime(59000), await waitMs() + await vi.advanceTimersByTimeAsync(59000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + await vi.advanceTimersByTimeAsync(2000) } expect(onCleanup).toHaveBeenCalled() @@ -242,11 +254,12 @@ describe.each([['query'], ['mutation']] as const)( expect(onNewCacheEntry).toHaveBeenCalledWith('arg') - promise.unsubscribe(), await waitMs() + promise.unsubscribe() + await vi.advanceTimersByTimeAsync(DEFAULT_DELAY_MS) if (type === 'query') { - jest.advanceTimersByTime(59000), await waitMs() + await vi.advanceTimersByTimeAsync(59000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + await vi.advanceTimersByTimeAsync(2000) } expect(onCleanup).not.toHaveBeenCalled() expect(gotFirstValue).not.toHaveBeenCalled() @@ -287,11 +300,12 @@ describe.each([['query'], ['mutation']] as const)( expect(onNewCacheEntry).toHaveBeenCalledWith('arg') - promise.unsubscribe(), await waitMs() + promise.unsubscribe() + await vi.advanceTimersByTimeAsync(DEFAULT_DELAY_MS) if (type === 'query') { - jest.advanceTimersByTime(59000), await waitMs() + await vi.advanceTimersByTimeAsync(59000) expect(onCleanup).not.toHaveBeenCalled() - jest.advanceTimersByTime(2000), await waitMs() + await vi.advanceTimersByTimeAsync(2000) } expect(onCleanup).toHaveBeenCalled() expect(gotFirstValue).not.toHaveBeenCalled() @@ -303,7 +317,7 @@ describe.each([['query'], ['mutation']] as const)( ) test(`query: getCacheEntry`, async () => { - const snapshot = jest.fn() + const snapshot = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, endpoints: (build) => ({ @@ -337,7 +351,7 @@ test(`query: getCacheEntry`, async () => { expect(gotFirstValue).toHaveBeenCalled() }) - jest.advanceTimersByTime(120000), await waitMs() + await vi.advanceTimersByTimeAsync(120000) expect(snapshot).toHaveBeenCalledTimes(3) expect(snapshot.mock.calls[0][0]).toMatchObject({ @@ -376,7 +390,7 @@ test(`query: getCacheEntry`, async () => { }) test(`mutation: getCacheEntry`, async () => { - const snapshot = jest.fn() + const snapshot = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, endpoints: (build) => ({ @@ -408,7 +422,8 @@ test(`mutation: getCacheEntry`, async () => { expect(gotFirstValue).toHaveBeenCalled() }) - promise.unsubscribe(), await waitMs() + promise.unsubscribe() + await vi.advanceTimersByTimeAsync(DEFAULT_DELAY_MS) expect(snapshot).toHaveBeenCalledTimes(3) expect(snapshot.mock.calls[0][0]).toMatchObject({ @@ -443,7 +458,7 @@ test(`mutation: getCacheEntry`, async () => { }) test('updateCachedData', async () => { - const trackCalls = jest.fn() + const trackCalls = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, @@ -505,7 +520,7 @@ test('updateCachedData', async () => { expect(gotFirstValue).toHaveBeenCalled() }) - jest.advanceTimersByTime(61000) + await vi.advanceTimersByTimeAsync(61000) await fakeTimerWaitFor(() => { expect(onCleanup).toHaveBeenCalled() diff --git a/packages/toolkit/src/query/tests/cleanup.test.tsx b/packages/toolkit/src/query/tests/cleanup.test.tsx index a9cf2b94c2..a71aa9f7e3 100644 --- a/packages/toolkit/src/query/tests/cleanup.test.tsx +++ b/packages/toolkit/src/query/tests/cleanup.test.tsx @@ -1,5 +1,5 @@ // tests for "cleanup-after-unsubscribe" behaviour - +import { vi } from 'vitest' import React, { Profiler, ProfilerOnRenderCallback } from 'react' import { createListenerMiddleware } from '@reduxjs/toolkit' @@ -10,7 +10,7 @@ import { delay } from '../../utils' const tick = () => new Promise((res) => setImmediate(res)) -export const runAllTimers = async () => jest.runAllTimers() && (await tick()) +export const runAllTimers = async () => vi.runAllTimers() && (await tick()) const api = createApi({ baseQuery: () => ({ data: 42 }), @@ -44,7 +44,7 @@ function UsingAB() { } beforeAll(() => { - jest.useFakeTimers('legacy') + vi.useFakeTimers() }) test('data stays in store when component stays rendered', async () => { @@ -55,11 +55,11 @@ test('data stays in store when component stays rendered', async () => { expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled) ) - jest.advanceTimersByTime(120000) + vi.advanceTimersByTime(120000) - await waitFor(() => - expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled) - ) + // await waitFor(() => + expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled) + // ) }) test('data is removed from store after 60 seconds', async () => { @@ -72,11 +72,11 @@ test('data is removed from store after 60 seconds', async () => { unmount() - jest.advanceTimersByTime(59000) + vi.advanceTimersByTime(59000) expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled) - jest.advanceTimersByTime(2000) + vi.advanceTimersByTime(2000) expect(getSubStateA()).toBeUndefined() }) @@ -106,10 +106,10 @@ test('data stays in store when component stays rendered while data for another c ) - jest.advanceTimersByTime(10) + vi.advanceTimersByTime(10) }) - jest.advanceTimersByTime(120000) + vi.advanceTimersByTime(120000) expect(getSubStateA()).toEqual(statusA) expect(getSubStateB()).toBeUndefined() @@ -140,20 +140,20 @@ test('data stays in store when one component requiring the data stays in the sto ) - jest.advanceTimersByTime(10) - jest.runAllTimers() + vi.advanceTimersByTime(10) + vi.runAllTimers() }) await act(async () => { - jest.advanceTimersByTime(120000) - jest.runAllTimers() + vi.advanceTimersByTime(120000) + vi.runAllTimers() }) expect(getSubStateA()).toEqual(statusA) expect(getSubStateB()).toEqual(statusB) }) -test('Minimizes the number of subscription dispatches when multiple components ask for the same data', async () => { +test.only('Minimizes the number of subscription dispatches when multiple components ask for the same data', async () => { const listenerMiddleware = createListenerMiddleware() const storeRef = setupApiStore(api, undefined, { middleware: { @@ -188,14 +188,15 @@ test('Minimizes the number of subscription dispatches when multiple components a wrapper: storeRef.wrapper, }) - jest.advanceTimersByTime(10) + await act(async () => { + vi.advanceTimersByTime(10) + vi.runAllTimers() + }) await waitFor(() => { return screen.getAllByText(/42/).length > 0 }) - await runAllTimers() - const subscriptions = getSubscriptionsA() expect(Object.keys(subscriptions!).length).toBe(NUM_LIST_ITEMS) diff --git a/packages/toolkit/src/query/tests/createApi.test.ts b/packages/toolkit/src/query/tests/createApi.test.ts index 75d7690ada..5539debc1d 100644 --- a/packages/toolkit/src/query/tests/createApi.test.ts +++ b/packages/toolkit/src/query/tests/createApi.test.ts @@ -1,4 +1,5 @@ import { configureStore, createAction, createReducer } from '@reduxjs/toolkit' +import { vi, SpyInstance } from 'vitest' import type { Api, MutationDefinition, @@ -24,9 +25,9 @@ const originalEnv = process.env.NODE_ENV beforeAll(() => void ((process.env as any).NODE_ENV = 'development')) afterAll(() => void ((process.env as any).NODE_ENV = originalEnv)) -let spy: jest.SpyInstance +let spy: SpyInstance beforeAll(() => { - spy = jest.spyOn(console, 'error').mockImplementation(() => {}) + spy = vi.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { spy.mockReset() @@ -75,7 +76,7 @@ test('sensible defaults', () => { }) describe('wrong tagTypes log errors', () => { - const baseQuery = jest.fn() + const baseQuery = vi.fn() const api = createApi({ baseQuery, tagTypes: ['User'], @@ -319,7 +320,7 @@ describe('endpoint definition typings', () => { }) describe('enhancing endpoint definitions', () => { - const baseQuery = jest.fn((x: string) => ({ data: 'success' })) + const baseQuery = vi.fn((x: string) => ({ data: 'success' })) const commonBaseQueryApi = { dispatch: expect.any(Function), endpoint: expect.any(String), @@ -864,7 +865,7 @@ describe('custom serializeQueryArgs per endpoint', () => { type SuccessResponse = { value: 'success' } - const serializer1 = jest.fn(customArgsSerializer) + const serializer1 = vi.fn(customArgsSerializer) interface MyApiClient { fetchPost: (id: string) => Promise @@ -971,7 +972,7 @@ describe('custom serializeQueryArgs per endpoint', () => { ).toBeTruthy() }) - const serializer2 = jest.fn(customArgsSerializer) + const serializer2 = vi.fn(customArgsSerializer) const injectedApi = api.injectEndpoints({ endpoints: (build) => ({ diff --git a/packages/toolkit/src/query/tests/devWarnings.test.tsx b/packages/toolkit/src/query/tests/devWarnings.test.tsx index ce50aa9080..404adf635d 100644 --- a/packages/toolkit/src/query/tests/devWarnings.test.tsx +++ b/packages/toolkit/src/query/tests/devWarnings.test.tsx @@ -6,7 +6,7 @@ import { } from 'console-testing-library/pure' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query' -let restore: () => void +let restore: () => void = () => {} let nodeEnv: string beforeEach(() => { diff --git a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx index eab78bd59b..91044869ac 100644 --- a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx +++ b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import { createSlice } from '@reduxjs/toolkit' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query' import { setupApiStore, waitMs } from './helpers' @@ -18,7 +19,7 @@ const defaultHeaders: Record = { const baseUrl = 'https://example.com' // @ts-ignore -const fetchFn = jest.fn, any[]>(global.fetch) +const fetchFn = vi.fn, any[]>(global.fetch) const baseQuery = fetchBaseQuery({ baseUrl, @@ -983,7 +984,7 @@ describe('fetchFn', () => { clone: () => fakeResponse, } - const spiedFetch = jest.spyOn(window, 'fetch') + const spiedFetch = vi.spyOn(window, 'fetch') spiedFetch.mockResolvedValueOnce(fakeResponse as any) const { data } = await baseQuery({ url: '/echo' }, commonBaseQueryApi, {}) diff --git a/packages/toolkit/src/query/tests/helpers.tsx b/packages/toolkit/src/query/tests/helpers.tsx index faad21e41b..c6f91d7668 100644 --- a/packages/toolkit/src/query/tests/helpers.tsx +++ b/packages/toolkit/src/query/tests/helpers.tsx @@ -1,13 +1,15 @@ +import React, { useCallback } from 'react' import type { AnyAction, EnhancedStore, Middleware, Store, + Reducer, } from '@reduxjs/toolkit' import { configureStore } from '@reduxjs/toolkit' import { setupListeners } from '@reduxjs/toolkit/query' -import type { Reducer } from 'react' -import React, { useCallback } from 'react' +// import type { Reducer } from 'react' + import { Provider } from 'react-redux' import { @@ -57,11 +59,29 @@ export const hookWaitFor = async (cb: () => void, time = 2000) => { if (Date.now() > startedAt + time) { throw e } - await act(() => waitMs(2)) + await act(async () => { + await waitMs(2) + }) + } + } +} +export const fakeTimerWaitFor = async (cb: () => void, time = 2000) => { + const startedAt = Date.now() + + while (true) { + try { + cb() + return true + } catch (e) { + if (Date.now() > startedAt + time) { + throw e + } + await act(async () => { + await vi.advanceTimersByTimeAsync(2) + }) } } } -export const fakeTimerWaitFor = hookWaitFor export const useRenderCounter = () => { const countRef = React.useRef(0) diff --git a/packages/toolkit/src/query/tests/optimisticUpdates.test.tsx b/packages/toolkit/src/query/tests/optimisticUpdates.test.tsx index 65237090da..51ee44fb44 100644 --- a/packages/toolkit/src/query/tests/optimisticUpdates.test.tsx +++ b/packages/toolkit/src/query/tests/optimisticUpdates.test.tsx @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import { createApi } from '@reduxjs/toolkit/query/react' import { actionsReducer, hookWaitFor, setupApiStore, waitMs } from './helpers' import { renderHook, act } from '@testing-library/react' @@ -8,7 +9,7 @@ interface Post { contents: string } -const baseQuery = jest.fn() +const baseQuery = vi.fn() beforeEach(() => baseQuery.mockReset()) const api = createApi({ @@ -50,9 +51,9 @@ const storeRef = setupApiStore(api, { }) describe('basic lifecycle', () => { - let onStart = jest.fn(), - onError = jest.fn(), - onSuccess = jest.fn() + let onStart = vi.fn(), + onError = vi.fn(), + onSuccess = vi.fn() const extendedApi = api.injectEndpoints({ endpoints: (build) => ({ @@ -109,14 +110,13 @@ describe('basic lifecycle', () => { } ) - baseQuery.mockRejectedValue('error') - + baseQuery.mockRejectedValueOnce('error') expect(onStart).not.toHaveBeenCalled() expect(baseQuery).not.toHaveBeenCalled() + act(() => void result.current[0]('arg')) expect(onStart).toHaveBeenCalledWith('arg') expect(baseQuery).toHaveBeenCalledWith('arg', expect.any(Object), undefined) - expect(onError).not.toHaveBeenCalled() expect(onSuccess).not.toHaveBeenCalled() await act(() => waitMs(5)) diff --git a/packages/toolkit/src/query/tests/optimisticUpserts.test.tsx b/packages/toolkit/src/query/tests/optimisticUpserts.test.tsx index c901202696..1d67f0f1a0 100644 --- a/packages/toolkit/src/query/tests/optimisticUpserts.test.tsx +++ b/packages/toolkit/src/query/tests/optimisticUpserts.test.tsx @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import { createApi } from '@reduxjs/toolkit/query/react' import { actionsReducer, hookWaitFor, setupApiStore, waitMs } from './helpers' import { skipToken } from '../core/buildSelectors' @@ -10,7 +11,7 @@ interface Post { contents: string } -const baseQuery = jest.fn() +const baseQuery = vi.fn() beforeEach(() => baseQuery.mockReset()) const api = createApi({ @@ -67,9 +68,9 @@ const storeRef = setupApiStore(api, { }) describe('basic lifecycle', () => { - let onStart = jest.fn(), - onError = jest.fn(), - onSuccess = jest.fn() + let onStart = vi.fn(), + onError = vi.fn(), + onSuccess = vi.fn() const extendedApi = api.injectEndpoints({ endpoints: (build) => ({ @@ -161,7 +162,7 @@ describe('basic lifecycle', () => { } ) - baseQuery.mockRejectedValue('error') + baseQuery.mockRejectedValueOnce('error') expect(onStart).not.toHaveBeenCalled() expect(baseQuery).not.toHaveBeenCalled() @@ -295,7 +296,7 @@ describe('upsertQueryData', () => { expect(state.data).toEqual(fetchedData) }) test('upsert while a normal query is running (rejected)', async () => { - baseQuery.mockImplementation(async () => { + baseQuery.mockImplementationOnce(async () => { await delay(20) // eslint-disable-next-line no-throw-literal throw 'Error!' diff --git a/packages/toolkit/src/query/tests/polling.test.tsx b/packages/toolkit/src/query/tests/polling.test.tsx index 357575b0da..7443d94048 100644 --- a/packages/toolkit/src/query/tests/polling.test.tsx +++ b/packages/toolkit/src/query/tests/polling.test.tsx @@ -1,8 +1,9 @@ +import { vi } from 'vitest' import { createApi } from '@reduxjs/toolkit/query' import { setupApiStore, waitMs } from './helpers' import { delay } from '../../utils' -const mockBaseQuery = jest +const mockBaseQuery = vi .fn() .mockImplementation((args: any) => ({ data: args })) diff --git a/packages/toolkit/src/query/tests/queryFn.test.tsx b/packages/toolkit/src/query/tests/queryFn.test.tsx index 1562153aff..daf751c6a3 100644 --- a/packages/toolkit/src/query/tests/queryFn.test.tsx +++ b/packages/toolkit/src/query/tests/queryFn.test.tsx @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import type { SerializedError } from '@reduxjs/toolkit' import { configureStore } from '@reduxjs/toolkit' import type { BaseQueryFn, FetchBaseQueryError } from '@reduxjs/toolkit/query' @@ -9,9 +10,11 @@ import type { QuerySubState } from '@reduxjs/toolkit/dist/query/core/apiState' describe('queryFn base implementation tests', () => { const baseQuery: BaseQueryFn = - jest.fn((arg: string) => arg.includes('withErrorQuery') - ? ({ error: `cut${arg}` }) - : ({ data: { wrappedByBaseQuery: arg } })) + vi.fn((arg: string) => + arg.includes('withErrorQuery') + ? { error: `cut${arg}` } + : { data: { wrappedByBaseQuery: arg } } + ) const api = createApi({ baseQuery, @@ -308,11 +311,11 @@ describe('usage scenario tests', () => { exists: () => true, data: () => mockData, } - const get = jest.fn(() => Promise.resolve(mockDocResult)) - const doc = jest.fn((name) => ({ + const get = vi.fn(() => Promise.resolve(mockDocResult)) + const doc = vi.fn((name) => ({ get, })) - const collection = jest.fn((name) => ({ get, doc })) + const collection = vi.fn((name) => ({ get, doc })) const firestore = () => { return { collection, doc } } diff --git a/packages/toolkit/src/query/tests/queryLifecycle.test.tsx b/packages/toolkit/src/query/tests/queryLifecycle.test.tsx index 8bf191943e..e549270113 100644 --- a/packages/toolkit/src/query/tests/queryLifecycle.test.tsx +++ b/packages/toolkit/src/query/tests/queryLifecycle.test.tsx @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import { createApi } from '@reduxjs/toolkit/query' import { waitFor } from '@testing-library/react' import type { @@ -15,9 +16,9 @@ const api = createApi({ }) const storeRef = setupApiStore(api) -const onStart = jest.fn() -const onSuccess = jest.fn() -const onError = jest.fn() +const onStart = vi.fn() +const onSuccess = vi.fn() +const onError = vi.fn() beforeEach(() => { onStart.mockClear() @@ -53,7 +54,7 @@ describe.each([['query'], ['mutation']] as const)( async onQueryStarted(arg, { queryFulfilled }) { onStart(arg) // awaiting without catching like this would result in an `unhandledRejection` exception if there was an error - // unfortunately we cannot test for that in jest. + // unfortunately we cannot test for that in vi. const result = await queryFulfilled expectType<{ data: number; meta?: FetchBaseQueryMeta }>(result) onSuccess(result) @@ -113,7 +114,7 @@ describe.each([['query'], ['mutation']] as const)( ) test('query: getCacheEntry (success)', async () => { - const snapshot = jest.fn() + const snapshot = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, endpoints: (build) => ({ @@ -174,7 +175,7 @@ test('query: getCacheEntry (success)', async () => { }) test('query: getCacheEntry (error)', async () => { - const snapshot = jest.fn() + const snapshot = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, endpoints: (build) => ({ @@ -234,7 +235,7 @@ test('query: getCacheEntry (error)', async () => { }) test('mutation: getCacheEntry (success)', async () => { - const snapshot = jest.fn() + const snapshot = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, endpoints: (build) => ({ @@ -291,7 +292,7 @@ test('mutation: getCacheEntry (success)', async () => { }) test('mutation: getCacheEntry (error)', async () => { - const snapshot = jest.fn() + const snapshot = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, endpoints: (build) => ({ @@ -347,7 +348,7 @@ test('mutation: getCacheEntry (error)', async () => { }) test('query: updateCachedData', async () => { - const trackCalls = jest.fn() + const trackCalls = vi.fn() const extended = api.injectEndpoints({ overrideExisting: true, diff --git a/packages/toolkit/src/query/tests/refetchingBehaviors.test.tsx b/packages/toolkit/src/query/tests/refetchingBehaviors.test.tsx index d531ae7b4e..061f94d7b0 100644 --- a/packages/toolkit/src/query/tests/refetchingBehaviors.test.tsx +++ b/packages/toolkit/src/query/tests/refetchingBehaviors.test.tsx @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import * as React from 'react' import { createApi, setupListeners } from '@reduxjs/toolkit/query/react' import { act, fireEvent, render, waitFor, screen } from '@testing-library/react' @@ -372,11 +373,11 @@ describe('customListenersHandler', () => { }) test('setupListeners accepts a custom callback and executes it', async () => { - const consoleSpy = jest.spyOn(console, 'log') + const consoleSpy = vi.spyOn(console, 'log') consoleSpy.mockImplementation((...args) => { // console.info(...args) }) - const dispatchSpy = jest.spyOn(storeRef.store, 'dispatch') + const dispatchSpy = vi.spyOn(storeRef.store, 'dispatch') let unsubscribe = () => {} unsubscribe = setupListeners( diff --git a/packages/toolkit/src/query/tests/retry.test.ts b/packages/toolkit/src/query/tests/retry.test.ts index 05e1fa7b83..2bb35e89ae 100644 --- a/packages/toolkit/src/query/tests/retry.test.ts +++ b/packages/toolkit/src/query/tests/retry.test.ts @@ -1,26 +1,29 @@ +import { vi } from 'vitest' import type { BaseQueryFn } from '@reduxjs/toolkit/query' import { createApi, retry } from '@reduxjs/toolkit/query' import { setupApiStore, waitMs } from './helpers' import type { RetryOptions } from '../retry' beforeEach(() => { - jest.useFakeTimers('legacy') + vi.useFakeTimers() }) const loopTimers = async (max: number = 12) => { let count = 0 while (count < max) { - await waitMs(1) - jest.advanceTimersByTime(120000) + await vi.advanceTimersByTimeAsync(1) + vi.advanceTimersByTime(120000) count++ } } +vi.fn() + describe('configuration', () => { test('retrying without any config options', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -45,9 +48,9 @@ describe('configuration', () => { }) test('retrying with baseQuery config that overrides default behavior (maxRetries: 5)', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -72,9 +75,9 @@ describe('configuration', () => { }) test('retrying with endpoint config that overrides baseQuery config', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -111,9 +114,9 @@ describe('configuration', () => { }) test('stops retrying a query after a success', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery .mockResolvedValueOnce({ error: 'rejected' }) @@ -141,9 +144,9 @@ describe('configuration', () => { }) test('retrying also works with mutations', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -169,9 +172,9 @@ describe('configuration', () => { }) test('retrying stops after a success from a mutation', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery .mockRejectedValueOnce(new Error('rejected')) @@ -199,9 +202,9 @@ describe('configuration', () => { expect(baseBaseQuery).toHaveBeenCalledTimes(3) }) test('non-error-cases should **not** retry', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ data: { success: true } }) @@ -228,9 +231,9 @@ describe('configuration', () => { test('calling retry.fail(error) will skip retrying and expose the error directly', async () => { const error = { message: 'banana' } - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockImplementation((input) => { retry.fail(error) @@ -276,9 +279,9 @@ describe('configuration', () => { * Note: * This will retry 16 total times because we try the initial + 3 retries (sum: 4), then retry that process 3 times (starting at 0 for a total of 4)... 4x4=16 (allegedly) */ - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -306,9 +309,9 @@ describe('configuration', () => { }) test('accepts a custom backoff fn', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -342,9 +345,9 @@ describe('configuration', () => { }) test('accepts a custom retryCondition fn', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -373,9 +376,9 @@ describe('configuration', () => { }) test('retryCondition with endpoint config that overrides baseQuery config', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -405,9 +408,9 @@ describe('configuration', () => { }) test('retryCondition also works with mutations', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery @@ -440,9 +443,9 @@ describe('configuration', () => { }) test('Specifying maxRetries as 0 in RetryOptions prevents retries', async () => { - const baseBaseQuery = jest.fn< - ReturnType, - Parameters + const baseBaseQuery = vi.fn< + Parameters, + ReturnType >() baseBaseQuery.mockResolvedValue({ error: 'rejected' }) @@ -464,7 +467,7 @@ describe('configuration', () => { await loopTimers(2) expect(baseBaseQuery).toHaveBeenCalledTimes(1) - }); + }) test.skip('RetryOptions only accepts one of maxRetries or retryCondition', () => { // @ts-expect-error Should complain if both exist at once diff --git a/packages/toolkit/src/query/tests/utils.test.ts b/packages/toolkit/src/query/tests/utils.test.ts index 25bec07f0b..1124f0294b 100644 --- a/packages/toolkit/src/query/tests/utils.test.ts +++ b/packages/toolkit/src/query/tests/utils.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import { isOnline, isDocumentVisible, @@ -6,68 +7,68 @@ import { } from '@internal/query/utils' afterAll(() => { - jest.restoreAllMocks() + vi.restoreAllMocks() }) describe('isOnline', () => { test('Assumes online=true in a node env', () => { - jest - .spyOn(window, 'navigator', 'get') - .mockImplementation(() => undefined as any) + vi.spyOn(window, 'navigator', 'get').mockImplementation( + () => undefined as any + ) expect(navigator).toBeUndefined() expect(isOnline()).toBe(true) }) test('Returns false if navigator isOnline=false', () => { - jest - .spyOn(window, 'navigator', 'get') - .mockImplementation(() => ({ onLine: false } as any)) + vi.spyOn(window, 'navigator', 'get').mockImplementation( + () => ({ onLine: false } as any) + ) expect(isOnline()).toBe(false) }) test('Returns true if navigator isOnline=true', () => { - jest - .spyOn(window, 'navigator', 'get') - .mockImplementation(() => ({ onLine: true } as any)) + vi.spyOn(window, 'navigator', 'get').mockImplementation( + () => ({ onLine: true } as any) + ) expect(isOnline()).toBe(true) }) }) describe('isDocumentVisible', () => { test('Assumes true when in a non-browser env', () => { - jest - .spyOn(window, 'document', 'get') - .mockImplementation(() => undefined as any) + vi.spyOn(window, 'document', 'get').mockImplementation( + () => undefined as any + ) expect(window.document).toBeUndefined() expect(isDocumentVisible()).toBe(true) }) test('Returns false when hidden=true', () => { - jest - .spyOn(window, 'document', 'get') - .mockImplementation(() => ({ visibilityState: 'hidden' } as any)) + vi.spyOn(window, 'document', 'get').mockImplementation( + () => ({ visibilityState: 'hidden' } as any) + ) expect(isDocumentVisible()).toBe(false) }) test('Returns true when visibilityState=prerender', () => { - jest - .spyOn(window, 'document', 'get') - .mockImplementation(() => ({ visibilityState: 'prerender' } as any)) + vi.spyOn(window, 'document', 'get').mockImplementation( + () => ({ visibilityState: 'prerender' } as any) + ) expect(document.visibilityState).toBe('prerender') expect(isDocumentVisible()).toBe(true) }) test('Returns true when visibilityState=visible', () => { - jest - .spyOn(window, 'document', 'get') - .mockImplementation(() => ({ visibilityState: 'visible' } as any)) + vi.spyOn(window, 'document', 'get').mockImplementation( + () => ({ visibilityState: 'visible' } as any) + ) expect(document.visibilityState).toBe('visible') expect(isDocumentVisible()).toBe(true) }) test('Returns true when visibilityState=undefined', () => { - jest - .spyOn(window, 'document', 'get') - .mockImplementation(() => ({ visibilityState: undefined } as any)) + vi.spyOn(window, 'document', 'get').mockImplementation( + () => ({ visibilityState: undefined } as any) + ) expect(document.visibilityState).toBeUndefined() expect(isDocumentVisible()).toBe(true) }) diff --git a/packages/toolkit/src/tests/__snapshots__/createAsyncThunk.test.ts.snap b/packages/toolkit/src/tests/__snapshots__/createAsyncThunk.test.ts.snap deleted file mode 100644 index 81895b595e..0000000000 --- a/packages/toolkit/src/tests/__snapshots__/createAsyncThunk.test.ts.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`createAsyncThunk with abortController behaviour with missing AbortController calling \`abort\` on an asyncThunk works with a FallbackAbortController if no global abortController is not available 1`] = ` -"This platform does not implement AbortController. -If you want to use the AbortController to react to \`abort\` events, please consider importing a polyfill like 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'." -`; - -exports[`non-serializable arguments are ignored by serializableStateInvariantMiddleware 1`] = `""`; diff --git a/packages/toolkit/src/tests/__snapshots__/serializableStateInvariantMiddleware.test.ts.snap b/packages/toolkit/src/tests/__snapshots__/serializableStateInvariantMiddleware.test.ts.snap deleted file mode 100644 index e4db1dd358..0000000000 --- a/packages/toolkit/src/tests/__snapshots__/serializableStateInvariantMiddleware.test.ts.snap +++ /dev/null @@ -1,52 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`serializableStateInvariantMiddleware Should log an error when a non-serializable action is dispatched 1`] = ` -"A non-serializable value was detected in an action, in the path: \`type\`. Value: Symbol(SOME_CONSTANT) -Take a look at the logic that dispatched this action: Object { - \\"type\\": Symbol(SOME_CONSTANT), -} -(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants) -(To allow non-serializable values see: https://redux-toolkit.js.org/usage/usage-guide#working-with-non-serializable-data)" -`; - -exports[`serializableStateInvariantMiddleware Should log an error when a non-serializable value is in state 1`] = ` -"A non-serializable value was detected in the state, in the path: \`testSlice.a\`. Value: Map {} -Take a look at the reducer(s) handling this action type: TEST_ACTION. -(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)" -`; - -exports[`serializableStateInvariantMiddleware allows ignoring state entirely 1`] = `""`; - -exports[`serializableStateInvariantMiddleware consumer tolerated structures Should log an error when a non-serializable value is nested in state 1`] = ` -"A non-serializable value was detected in the state, in the path: \`testSlice.a.entries\`. Value: [Function entries] -Take a look at the reducer(s) handling this action type: TEST_ACTION. -(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)" -`; - -exports[`serializableStateInvariantMiddleware consumer tolerated structures Should use consumer supplied isSerializable and getEntries options to tolerate certain structures 1`] = ` -"A non-serializable value was detected in the state, in the path: \`testSlice.a.third.bad-map-instance\`. Value: Map {} -Take a look at the reducer(s) handling this action type: TEST_ACTION. -(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)" -`; - -exports[`serializableStateInvariantMiddleware ignored action paths can specify (multiple) different values 1`] = `""`; - -exports[`serializableStateInvariantMiddleware ignored action paths default value can be overridden 1`] = ` -"A non-serializable value was detected in an action, in the path: \`meta.arg\`. Value: Map {} -Take a look at the logic that dispatched this action: Object { - \\"meta\\": Object { - \\"arg\\": Map {}, - }, - \\"type\\": \\"test\\", -} -(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants) -(To allow non-serializable values see: https://redux-toolkit.js.org/usage/usage-guide#working-with-non-serializable-data)" -`; - -exports[`serializableStateInvariantMiddleware ignored action paths default value: meta.arg 1`] = `""`; - -exports[`serializableStateInvariantMiddleware should not check serializability for ignored slice names 1`] = ` -"A non-serializable value was detected in the state, in the path: \`testSlice.b.d\`. Value: Map {} -Take a look at the reducer(s) handling this action type: TEST_ACTION. -(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)" -`; diff --git a/packages/toolkit/src/tests/configureStore.test.ts b/packages/toolkit/src/tests/configureStore.test.ts index 49831e51e6..a963a4c54a 100644 --- a/packages/toolkit/src/tests/configureStore.test.ts +++ b/packages/toolkit/src/tests/configureStore.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import type { StoreEnhancer, StoreEnhancerStoreCreator } from '@reduxjs/toolkit' import { configureStore } from '@reduxjs/toolkit' import * as RTK from '@reduxjs/toolkit' @@ -5,15 +6,17 @@ import * as redux from 'redux' import * as devtools from '@internal/devtoolsExtension' describe('configureStore', () => { - jest.spyOn(redux, 'applyMiddleware') - jest.spyOn(redux, 'combineReducers') - jest.spyOn(redux, 'compose') - jest.spyOn(redux, 'createStore') - jest.spyOn(devtools, 'composeWithDevTools') // @remap-prod-remove-line + vi.spyOn(redux, 'applyMiddleware') + vi.spyOn(redux, 'combineReducers') + vi.spyOn(redux, 'compose') + vi.spyOn(redux, 'createStore') + vi.spyOn(devtools, 'composeWithDevTools') // @remap-prod-remove-line const reducer: redux.Reducer = (state = {}, _action) => state - beforeEach(() => jest.clearAllMocks()) + beforeEach(() => { + vi.clearAllMocks() + }) describe('given a function reducer', () => { it('calls createStore with the reducer', () => { @@ -90,7 +93,7 @@ describe('configureStore', () => { describe('given a middleware creation function that returns undefined', () => { it('throws an error', () => { - const invalidBuilder = jest.fn((getDefaultMiddleware) => undefined as any) + const invalidBuilder = vi.fn((getDefaultMiddleware) => undefined as any) expect(() => configureStore({ middleware: invalidBuilder, reducer }) ).toThrow( @@ -101,7 +104,7 @@ describe('configureStore', () => { describe('given a middleware creation function that returns an array with non-functions', () => { it('throws an error', () => { - const invalidBuilder = jest.fn((getDefaultMiddleware) => [true] as any) + const invalidBuilder = vi.fn((getDefaultMiddleware) => [true] as any) expect(() => configureStore({ middleware: invalidBuilder, reducer }) ).toThrow('each middleware provided to configureStore must be a function') @@ -135,11 +138,11 @@ describe('configureStore', () => { describe('middleware builder notation', () => { it('calls builder, passes getDefaultMiddleware and uses returned middlewares', () => { - const thank = jest.fn( + const thank = vi.fn( ((_store) => (next) => (action) => 'foobar') as redux.Middleware ) - const builder = jest.fn((getDefaultMiddleware) => { + const builder = vi.fn((getDefaultMiddleware) => { expect(getDefaultMiddleware).toEqual(expect.any(Function)) expect(getDefaultMiddleware()).toEqual(expect.any(Array)) diff --git a/packages/toolkit/src/tests/createAsyncThunk.test.ts b/packages/toolkit/src/tests/createAsyncThunk.test.ts index 26550c93b4..8208d8693e 100644 --- a/packages/toolkit/src/tests/createAsyncThunk.test.ts +++ b/packages/toolkit/src/tests/createAsyncThunk.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import type { AnyAction } from '@reduxjs/toolkit' import { createAsyncThunk, @@ -58,7 +59,7 @@ describe('createAsyncThunk', () => { }) it('accepts arguments and dispatches the actions on resolve', async () => { - const dispatch = jest.fn() + const dispatch = vi.fn() let passedArg: any @@ -98,7 +99,7 @@ describe('createAsyncThunk', () => { }) it('accepts arguments and dispatches the actions on reject', async () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const args = 123 let generatedRequestId = '' @@ -134,7 +135,7 @@ describe('createAsyncThunk', () => { }) it('dispatches an empty error when throwing a random object without serializedError properties', async () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const args = 123 let generatedRequestId = '' @@ -169,7 +170,7 @@ describe('createAsyncThunk', () => { }) it('dispatches an action with a formatted error when throwing an object with known error keys', async () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const args = 123 let generatedRequestId = '' @@ -210,7 +211,7 @@ describe('createAsyncThunk', () => { }) it('dispatches a rejected action with a customized payload when a user returns rejectWithValue()', async () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const args = 123 let generatedRequestId = '' @@ -255,7 +256,7 @@ describe('createAsyncThunk', () => { }) it('dispatches a rejected action with a customized payload when a user throws rejectWithValue()', async () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const args = 123 let generatedRequestId = '' @@ -300,7 +301,7 @@ describe('createAsyncThunk', () => { }) it('dispatches a rejected action with a miniSerializeError when rejectWithValue conditions are not satisfied', async () => { - const dispatch = jest.fn() + const dispatch = vi.fn() const args = 123 let generatedRequestId = '' @@ -484,14 +485,14 @@ describe('createAsyncThunk with abortController', () => { describe('behaviour with missing AbortController', () => { let keepAbortController: typeof window['AbortController'] let freshlyLoadedModule: typeof import('../createAsyncThunk') - let restore: () => void + let restore: () => void = () => {} let nodeEnv: string - beforeEach(() => { + beforeEach(async () => { keepAbortController = window.AbortController delete (window as any).AbortController - jest.resetModules() - freshlyLoadedModule = require('../createAsyncThunk') + vi.resetModules() + freshlyLoadedModule = await import('../createAsyncThunk') restore = mockConsole(createConsole()) nodeEnv = process.env.NODE_ENV! ;(process.env as any).NODE_ENV = 'development' @@ -501,7 +502,7 @@ describe('createAsyncThunk with abortController', () => { ;(process.env as any).NODE_ENV = nodeEnv restore() window.AbortController = keepAbortController - jest.resetModules() + vi.resetModules() }) test('calling `abort` on an asyncThunk works with a FallbackAbortController if no global abortController is not available', async () => { @@ -539,10 +540,10 @@ test('non-serializable arguments are ignored by serializableStateInvariantMiddle describe('conditional skipping of asyncThunks', () => { const arg = {} - const getState = jest.fn(() => ({})) - const dispatch = jest.fn((x: any) => x) - const payloadCreator = jest.fn((x: typeof arg) => 10) - const condition = jest.fn(() => false) + const getState = vi.fn(() => ({})) + const dispatch = vi.fn((x: any) => x) + const payloadCreator = vi.fn((x: typeof arg) => 10) + const condition = vi.fn(() => false) const extra = {} beforeEach(() => { @@ -645,7 +646,7 @@ describe('conditional skipping of asyncThunks', () => { }) test('does not fail when attempting to abort a canceled promise', async () => { - const asyncPayloadCreator = jest.fn(async (x: typeof arg) => { + const asyncPayloadCreator = vi.fn(async (x: typeof arg) => { await delay(200) return 10 }) @@ -720,8 +721,8 @@ test('serializeError implementation', async () => { }) describe('unwrapResult', () => { - const getState = jest.fn(() => ({})) - const dispatch = jest.fn((x: any) => x) + const getState = vi.fn(() => ({})) + const dispatch = vi.fn((x: any) => x) const extra = {} test('fulfilled case', async () => { const asyncThunk = createAsyncThunk('test', () => { @@ -779,7 +780,7 @@ describe('idGenerator option', () => { test('idGenerator implementation - can customizes how request IDs are generated', async () => { function makeFakeIdGenerator() { let id = 0 - return jest.fn(() => { + return vi.fn(() => { id++ return `fake-random-id-${id}` }) @@ -833,7 +834,7 @@ describe('idGenerator option', () => { }) test('idGenerator should be called with thunkArg', async () => { - const customIdGenerator = jest.fn((seed) => `fake-unique-random-id-${seed}`) + const customIdGenerator = vi.fn((seed) => `fake-unique-random-id-${seed}`) let generatedRequestId = '' const asyncThunk = createAsyncThunk( 'test', @@ -855,7 +856,7 @@ describe('idGenerator option', () => { test('`condition` will see state changes from a synchronously invoked asyncThunk', () => { type State = ReturnType - const onStart = jest.fn() + const onStart = vi.fn() const asyncThunk = createAsyncThunk< void, { force?: boolean }, diff --git a/packages/toolkit/src/tests/createReducer.test.ts b/packages/toolkit/src/tests/createReducer.test.ts index 078f26618c..243cecf943 100644 --- a/packages/toolkit/src/tests/createReducer.test.ts +++ b/packages/toolkit/src/tests/createReducer.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import type { CaseReducer, PayloadAction, @@ -72,15 +73,15 @@ describe('createReducer', () => { let originalNodeEnv = process.env.NODE_ENV beforeEach(() => { - jest.resetModules() + vi.resetModules() }) afterEach(() => { process.env.NODE_ENV = originalNodeEnv }) - it('Throws an error if the legacy object notation is used', () => { - const { createReducer } = require('../createReducer') + it('Throws an error if the legacy object notation is used', async () => { + const { createReducer } = await import('../createReducer') const wrapper = () => { // @ts-ignore let dummyReducer = (createReducer as CreateReducer)([] as TodoState, {}) @@ -95,9 +96,9 @@ describe('createReducer', () => { ) }) - it('Crashes in production', () => { + it('Crashes in production', async () => { process.env.NODE_ENV = 'production' - const { createReducer } = require('../createReducer') + const { createReducer } = await import('../createReducer') const wrapper = () => { // @ts-ignore let dummyReducer = (createReducer as CreateReducer)([] as TodoState, {}) @@ -111,7 +112,7 @@ describe('createReducer', () => { let originalNodeEnv = process.env.NODE_ENV beforeEach(() => { - jest.resetModules() + vi.resetModules() process.env.NODE_ENV = 'production' }) @@ -119,9 +120,8 @@ describe('createReducer', () => { process.env.NODE_ENV = originalNodeEnv }) - test('Freezes data in production', () => { - const createReducer: CreateReducer = - require('../createReducer').createReducer + test('Freezes data in production', async () => { + const { createReducer } = await import('../createReducer') const addTodo: AddTodoReducer = (state, action) => { const { newTodo } = action.payload state.push({ ...newTodo, completed: false }) @@ -212,7 +212,7 @@ describe('createReducer', () => { behavesLikeReducer(todosReducer) it('Should only call the init function when `undefined` state is passed in', () => { - const spy = jest.fn().mockReturnValue(42) + const spy = vi.fn().mockReturnValue(42) const dummyReducer = createReducer(spy, () => {}) expect(spy).not.toHaveBeenCalled() diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts index 8cbe39340a..38038dbefc 100644 --- a/packages/toolkit/src/tests/createSlice.test.ts +++ b/packages/toolkit/src/tests/createSlice.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import type { PayloadAction } from '@reduxjs/toolkit' import { createSlice, createAction } from '@reduxjs/toolkit' import { @@ -280,7 +281,7 @@ describe('createSlice', () => { describe('behaviour with enhanced case reducers', () => { it('should pass all arguments to the prepare function', () => { - const prepare = jest.fn((payload, somethingElse) => ({ payload })) + const prepare = vi.fn((payload, somethingElse) => ({ payload })) const testSlice = createSlice({ name: 'test', @@ -301,7 +302,7 @@ describe('createSlice', () => { }) it('should call the reducer function', () => { - const reducer = jest.fn(() => 5) + const reducer = vi.fn(() => 5) const testSlice = createSlice({ name: 'test', @@ -380,7 +381,7 @@ describe('createSlice', () => { let originalNodeEnv = process.env.NODE_ENV beforeEach(() => { - jest.resetModules() + vi.resetModules() restore = mockConsole(createConsole()) }) @@ -389,8 +390,8 @@ describe('createSlice', () => { }) // NOTE: This needs to be in front of the later `createReducer` call to check the one-time warning - it('Throws an error if the legacy object notation is used', () => { - const { createSlice } = require('../createSlice') + it('Throws an error if the legacy object notation is used', async () => { + const { createSlice } = await import('../createSlice') let dummySlice = (createSlice as CreateSlice)({ name: 'dummy', diff --git a/packages/toolkit/src/tests/getDefaultMiddleware.test.ts b/packages/toolkit/src/tests/getDefaultMiddleware.test.ts index c47e3905cc..4da69101e3 100644 --- a/packages/toolkit/src/tests/getDefaultMiddleware.test.ts +++ b/packages/toolkit/src/tests/getDefaultMiddleware.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import type { AnyAction, Middleware, @@ -25,13 +26,13 @@ describe('getDefaultMiddleware', () => { describe('Production behavior', () => { beforeEach(() => { - jest.resetModules() + vi.resetModules() }) - it('returns an array with only redux-thunk in production', () => { + it('returns an array with only redux-thunk in production', async () => { process.env.NODE_ENV = 'production' - const { thunk } = require('redux-thunk') - const { getDefaultMiddleware } = require('@reduxjs/toolkit') + const { thunk } = await import('redux-thunk') + const { getDefaultMiddleware } = await import('@reduxjs/toolkit') const middleware = getDefaultMiddleware() expect(middleware).toContain(thunk) diff --git a/packages/toolkit/src/tests/immutableStateInvariantMiddleware.test.ts b/packages/toolkit/src/tests/immutableStateInvariantMiddleware.test.ts index bc90e4906a..a258de7961 100644 --- a/packages/toolkit/src/tests/immutableStateInvariantMiddleware.test.ts +++ b/packages/toolkit/src/tests/immutableStateInvariantMiddleware.test.ts @@ -10,7 +10,11 @@ import { } from '@reduxjs/toolkit' import { trackForMutations } from '@internal/immutableStateInvariantMiddleware' -import { mockConsole, createConsole, getLog } from 'console-testing-library' +import { + mockConsole, + createConsole, + getLog, +} from 'console-testing-library/pure' describe('createImmutableStateInvariantMiddleware', () => { let state: { foo: { bar: number[]; baz: string } } diff --git a/packages/toolkit/src/tests/matchers.test.ts b/packages/toolkit/src/tests/matchers.test.ts index b9307c97f3..7282c5fa65 100644 --- a/packages/toolkit/src/tests/matchers.test.ts +++ b/packages/toolkit/src/tests/matchers.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest' import type { ThunkAction, AnyAction } from '@reduxjs/toolkit' import { isAllOf, @@ -282,8 +283,8 @@ describe('isRejectedWithValue', () => { ) expect(isRejectedWithValue()(rejectedAction)).toBe(false) - const getState = jest.fn(() => ({})) - const dispatch = jest.fn((x: any) => x) + const getState = vi.fn(() => ({})) + const dispatch = vi.fn((x: any) => x) const extra = {} // note: doesn't throw because we don't unwrap it @@ -321,8 +322,8 @@ describe('isRejectedWithValue', () => { // rejected-with-value is a narrower requirement than rejected expect(matchAC(rejectedAction)).toBe(false) - const getState = jest.fn(() => ({})) - const dispatch = jest.fn((x: any) => x) + const getState = vi.fn(() => ({})) + const dispatch = vi.fn((x: any) => x) const extra = {} // note: doesn't throw because we don't unwrap it