Skip to content

Commit 441319a

Browse files
committed
Add importWithEnv util and disposable console spy
1 parent e848a55 commit 441319a

File tree

5 files changed

+75
-65
lines changed

5 files changed

+75
-65
lines changed

packages/toolkit/src/entities/tests/utils.spec.ts

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,35 @@
1-
import { vi } from 'vitest'
21
import { AClockworkOrange } from './fixtures/book'
2+
import { consoleSpy, makeImportWithEnv } from '@internal/tests/utils/helpers'
33

44
describe('Entity utils', () => {
55
describe(`selectIdValue()`, () => {
6-
const OLD_ENV = process.env
6+
const importWithEnv = makeImportWithEnv(() =>
7+
import('../utils').then((m) => m.selectIdValue),
8+
)
79

8-
beforeEach(() => {
9-
vi.resetModules() // this is important - it clears the cache
10-
process.env = { ...OLD_ENV, NODE_ENV: 'development' }
11-
})
10+
using spy = consoleSpy('warn')
1211

1312
afterEach(() => {
14-
process.env = OLD_ENV
15-
vi.resetAllMocks()
13+
spy.mockReset()
1614
})
1715

1816
it('should not warn when key does exist', async () => {
19-
const { selectIdValue } = await import('../utils')
20-
const spy = vi.spyOn(console, 'warn')
17+
using selectIdValue = await importWithEnv('development')
2118

2219
selectIdValue(AClockworkOrange, (book: any) => book.id)
2320
expect(spy).not.toHaveBeenCalled()
2421
})
2522

2623
it('should warn when key does not exist in dev mode', async () => {
27-
const { selectIdValue } = await import('../utils')
28-
const spy = vi.spyOn(console, 'warn')
24+
using selectIdValue = await importWithEnv('development')
2925

3026
selectIdValue(AClockworkOrange, (book: any) => book.foo)
3127

3228
expect(spy).toHaveBeenCalled()
3329
})
3430

3531
it('should warn when key is undefined in dev mode', async () => {
36-
const { selectIdValue } = await import('../utils')
37-
const spy = vi.spyOn(console, 'warn')
32+
using selectIdValue = await importWithEnv('development')
3833

3934
const undefinedAClockworkOrange = { ...AClockworkOrange, id: undefined }
4035
selectIdValue(undefinedAClockworkOrange, (book: any) => book.id)
@@ -43,19 +38,15 @@ describe('Entity utils', () => {
4338
})
4439

4540
it('should not warn when key does not exist in prod mode', async () => {
46-
process.env.NODE_ENV = 'production'
47-
const { selectIdValue } = await import('../utils')
48-
const spy = vi.spyOn(console, 'warn')
41+
using selectIdValue = await importWithEnv('production')
4942

5043
selectIdValue(AClockworkOrange, (book: any) => book.foo)
5144

5245
expect(spy).not.toHaveBeenCalled()
5346
})
5447

5548
it('should not warn when key is undefined in prod mode', async () => {
56-
process.env.NODE_ENV = 'production'
57-
const { selectIdValue } = await import('../utils')
58-
const spy = vi.spyOn(console, 'warn')
49+
using selectIdValue = await importWithEnv('production')
5950

6051
const undefinedAClockworkOrange = { ...AClockworkOrange, id: undefined }
6152
selectIdValue(undefinedAClockworkOrange, (book: any) => book.id)

packages/toolkit/src/tests/createReducer.test.ts

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
createConsole,
1414
getLog,
1515
} from 'console-testing-library/pure'
16+
import { makeImportWithEnv } from './utils/helpers'
1617

1718
interface Todo {
1819
text: string
@@ -39,7 +40,9 @@ type ToggleTodoReducer = CaseReducer<
3940
PayloadAction<ToggleTodoPayload, 'TOGGLE_TODO'>
4041
>
4142

42-
type CreateReducer = typeof createReducer
43+
const importWithEnv = makeImportWithEnv(() =>
44+
import('../createReducer').then((m) => m.createReducer),
45+
)
4346

4447
describe('createReducer', () => {
4548
let restore: () => void
@@ -71,21 +74,12 @@ describe('createReducer', () => {
7174
})
7275

7376
describe('Deprecation warnings', () => {
74-
let originalNodeEnv = process.env.NODE_ENV
75-
76-
beforeEach(() => {
77-
vi.resetModules()
78-
})
79-
80-
afterEach(() => {
81-
process.env.NODE_ENV = originalNodeEnv
82-
})
83-
8477
it('Throws an error if the legacy object notation is used', async () => {
85-
const { createReducer } = await import('../createReducer')
78+
using createReducer = await importWithEnv('development')
79+
8680
const wrapper = () => {
8781
// @ts-ignore
88-
let dummyReducer = (createReducer as CreateReducer)([] as TodoState, {})
82+
let dummyReducer = createReducer([], {})
8983
}
9084

9185
expect(wrapper).toThrowError(
@@ -98,31 +92,21 @@ describe('createReducer', () => {
9892
})
9993

10094
it('Crashes in production', async () => {
101-
process.env.NODE_ENV = 'production'
102-
const { createReducer } = await import('../createReducer')
95+
using createReducer = await importWithEnv('production')
96+
10397
const wrapper = () => {
10498
// @ts-ignore
105-
let dummyReducer = (createReducer as CreateReducer)([] as TodoState, {})
99+
let dummyReducer = createReducer([], {})
106100
}
107101

108102
expect(wrapper).toThrowError()
109103
})
110104
})
111105

112106
describe('Immer in a production environment', () => {
113-
let originalNodeEnv = process.env.NODE_ENV
114-
115-
beforeEach(() => {
116-
vi.resetModules()
117-
process.env.NODE_ENV = 'production'
118-
})
119-
120-
afterEach(() => {
121-
process.env.NODE_ENV = originalNodeEnv
122-
})
123-
124107
test('Freezes data in production', async () => {
125-
const { createReducer } = await import('../createReducer')
108+
using createReducer = await importWithEnv('production')
109+
126110
const addTodo: AddTodoReducer = (state, action) => {
127111
const { newTodo } = action.payload
128112
state.push({ ...newTodo, completed: false })

packages/toolkit/src/tests/getDefaultMiddleware.test.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Tuple } from '@internal/utils'
1+
import { promiseOwnProperties, Tuple } from '@internal/utils'
22
import type {
33
Action,
44
Middleware,
@@ -7,30 +7,26 @@ import type {
77
} from '@reduxjs/toolkit'
88
import { configureStore } from '@reduxjs/toolkit'
99
import { thunk } from 'redux-thunk'
10-
import { vi } from 'vitest'
10+
import { makeImportWithEnv } from './utils/helpers'
1111

1212
import { buildGetDefaultMiddleware } from '@internal/getDefaultMiddleware'
1313

1414
const getDefaultMiddleware = buildGetDefaultMiddleware()
1515

16-
describe('getDefaultMiddleware', () => {
17-
const ORIGINAL_NODE_ENV = process.env.NODE_ENV
18-
19-
afterEach(() => {
20-
process.env.NODE_ENV = ORIGINAL_NODE_ENV
21-
})
16+
const importWithEnv = makeImportWithEnv(() =>
17+
promiseOwnProperties({
18+
buildGetDefaultMiddleware: import('../getDefaultMiddleware').then(
19+
(m) => m.buildGetDefaultMiddleware,
20+
),
21+
thunk: import('redux-thunk').then((m) => m.thunk),
22+
}),
23+
)
2224

25+
describe('getDefaultMiddleware', () => {
2326
describe('Production behavior', () => {
24-
beforeEach(() => {
25-
vi.resetModules()
26-
})
27-
2827
it('returns an array with only redux-thunk in production', async () => {
29-
process.env.NODE_ENV = 'production'
30-
const { thunk } = await import('redux-thunk')
31-
const { buildGetDefaultMiddleware } = await import(
32-
'@internal/getDefaultMiddleware'
33-
)
28+
using imports = await importWithEnv('production')
29+
const { buildGetDefaultMiddleware, thunk } = imports
3430

3531
const middleware = buildGetDefaultMiddleware()()
3632
expect(middleware).toContain(thunk)

packages/toolkit/src/tests/utils/helpers.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,26 @@ export function setupApiStore<
268268

269269
return refObj
270270
}
271+
272+
export const makeImportWithEnv =
273+
<T extends {}>(getModule: () => Promise<T>) =>
274+
async (env: string) => {
275+
const originalEnv = process.env.NODE_ENV
276+
vi.stubEnv('NODE_ENV', env)
277+
vi.resetModules()
278+
const result = await getModule()
279+
return Object.assign(result, {
280+
[Symbol.dispose]() {
281+
process.env.NODE_ENV = originalEnv
282+
},
283+
} satisfies Disposable)
284+
}
285+
286+
export const consoleSpy = <K extends keyof Console>(key: K) => {
287+
const spy = vi.spyOn(console, key)
288+
return Object.assign(spy, {
289+
[Symbol.dispose]() {
290+
spy.mockRestore()
291+
},
292+
} satisfies Disposable)
293+
}

packages/toolkit/src/utils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,19 @@ export function getOrInsertComputed<K extends object, V>(
109109

110110
return map.set(key, compute(key)).get(key) as V
111111
}
112+
113+
export async function promiseFromEntries<T>(
114+
entries: Iterable<readonly [PropertyKey, T]>,
115+
) {
116+
return Object.fromEntries(
117+
await Promise.all(
118+
Array.from(entries, async ([key, value]) => [key, await value] as const),
119+
),
120+
)
121+
}
122+
123+
export async function promiseOwnProperties<T extends {}>(obj: T) {
124+
return promiseFromEntries(Object.entries(obj)) as Promise<{
125+
[K in keyof T]: Awaited<T[K]>
126+
}>
127+
}

0 commit comments

Comments
 (0)