Skip to content

Commit a3226cd

Browse files
kevin940726markerikson
authored andcommitted
Inline values in error messages (#257)
* Rewrite error message and mock console properly in tests * Remove not used code
1 parent 92691e7 commit a3226cd

File tree

2 files changed

+67
-68
lines changed

2 files changed

+67
-68
lines changed

src/serializableStateInvariantMiddleware.test.ts

Lines changed: 53 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { Reducer } from 'redux'
2+
import { Console } from 'console'
3+
import { Writable } from 'stream'
24
import { configureStore } from './configureStore'
35

46
import {
@@ -79,8 +81,33 @@ describe('findNonSerializableValue', () => {
7981
})
8082

8183
describe('serializableStateInvariantMiddleware', () => {
84+
let log = ''
85+
const originalConsole = window.console
86+
8287
beforeEach(() => {
83-
console.error = jest.fn()
88+
log = ''
89+
90+
const writable = new Writable({
91+
write(chunk, encoding, callback) {
92+
log += chunk
93+
callback()
94+
}
95+
})
96+
97+
const mockConsole = new Console({
98+
stdout: writable,
99+
stderr: writable
100+
})
101+
102+
Object.defineProperty(window, 'console', {
103+
value: mockConsole
104+
})
105+
})
106+
107+
afterEach(() => {
108+
Object.defineProperty(window, 'console', {
109+
value: originalConsole
110+
})
84111
})
85112

86113
it('Should log an error when a non-serializable action is dispatched', () => {
@@ -98,19 +125,12 @@ describe('serializableStateInvariantMiddleware', () => {
98125

99126
store.dispatch(dispatchedAction)
100127

101-
expect(console.error).toHaveBeenCalled()
102-
103-
const [
104-
message,
105-
keyPath,
106-
value,
107-
action
108-
] = (console.error as jest.Mock).mock.calls[0]
109-
110-
expect(message).toContain('detected in an action, in the path: `%s`')
111-
expect(keyPath).toBe('type')
112-
expect(value).toBe(type)
113-
expect(action).toBe(dispatchedAction)
128+
expect(log).toMatchInlineSnapshot(`
129+
"A non-serializable value was detected in an action, in the path: \`type\`. Value: Symbol(SOME_CONSTANT)
130+
Take a look at the logic that dispatched this action: { type: Symbol(SOME_CONSTANT) }
131+
(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)
132+
"
133+
`)
114134
})
115135

116136
it('Should log an error when a non-serializable value is in state', () => {
@@ -145,19 +165,12 @@ describe('serializableStateInvariantMiddleware', () => {
145165

146166
store.dispatch({ type: ACTION_TYPE })
147167

148-
expect(console.error).toHaveBeenCalled()
149-
150-
const [
151-
message,
152-
keyPath,
153-
value,
154-
actionType
155-
] = (console.error as jest.Mock).mock.calls[0]
156-
157-
expect(message).toContain('detected in the state, in the path: `%s`')
158-
expect(keyPath).toBe('testSlice.a')
159-
expect(value).toBe(badValue)
160-
expect(actionType).toBe(ACTION_TYPE)
168+
expect(log).toMatchInlineSnapshot(`
169+
"A non-serializable value was detected in the state, in the path: \`testSlice.a\`. Value: Map {}
170+
Take a look at the reducer(s) handling this action type: TEST_ACTION.
171+
(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)
172+
"
173+
`)
161174
})
162175

163176
describe('consumer tolerated structures', () => {
@@ -212,20 +225,13 @@ describe('serializableStateInvariantMiddleware', () => {
212225

213226
store.dispatch({ type: ACTION_TYPE })
214227

215-
expect(console.error).toHaveBeenCalled()
216-
217-
const [
218-
message,
219-
keyPath,
220-
value,
221-
actionType
222-
] = (console.error as jest.Mock).mock.calls[0]
223-
224228
// since default options are used, the `entries` function in `serializableObject` will cause the error
225-
expect(message).toContain('detected in the state, in the path: `%s`')
226-
expect(keyPath).toBe('testSlice.a.entries')
227-
expect(value).toBe(serializableObject.entries)
228-
expect(actionType).toBe(ACTION_TYPE)
229+
expect(log).toMatchInlineSnapshot(`
230+
"A non-serializable value was detected in the state, in the path: \`testSlice.a.entries\`. Value: [Function: entries]
231+
Take a look at the reducer(s) handling this action type: TEST_ACTION.
232+
(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)
233+
"
234+
`)
229235
})
230236

231237
it('Should use consumer supplied isSerializable and getEntries options to tolerate certain structures', () => {
@@ -265,20 +271,13 @@ describe('serializableStateInvariantMiddleware', () => {
265271

266272
store.dispatch({ type: ACTION_TYPE })
267273

268-
expect(console.error).toHaveBeenCalled()
269-
270-
const [
271-
message,
272-
keyPath,
273-
value,
274-
actionType
275-
] = (console.error as jest.Mock).mock.calls[0]
276-
277274
// error reported is from a nested class instance, rather than the `entries` function `serializableObject`
278-
expect(message).toContain('detected in the state, in the path: `%s`')
279-
expect(keyPath).toBe('testSlice.a.third.bad-map-instance')
280-
expect(value).toBe(nonSerializableValue)
281-
expect(actionType).toBe(ACTION_TYPE)
275+
expect(log).toMatchInlineSnapshot(`
276+
"A non-serializable value was detected in the state, in the path: \`testSlice.a.third.bad-map-instance\`. Value: Map {}
277+
Take a look at the reducer(s) handling this action type: TEST_ACTION.
278+
(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)
279+
"
280+
`)
282281
})
283282
})
284283

@@ -320,7 +319,7 @@ describe('serializableStateInvariantMiddleware', () => {
320319

321320
// Supplied 'isSerializable' considers all values serializable, hence
322321
// no error logging is expected:
323-
expect(console.error).not.toHaveBeenCalled()
322+
expect(log).toBe('')
324323
})
325324

326325
it('should not check serializability for ignored action types', () => {

src/serializableStateInvariantMiddleware.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,6 @@ export function isPlain(val: any) {
2020
)
2121
}
2222

23-
const NON_SERIALIZABLE_STATE_MESSAGE = [
24-
'A non-serializable value was detected in the state, in the path: `%s`. Value: %o',
25-
'Take a look at the reducer(s) handling this action type: %s.',
26-
'(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)'
27-
].join('\n')
28-
29-
const NON_SERIALIZABLE_ACTION_MESSAGE = [
30-
'A non-serializable value was detected in an action, in the path: `%s`. Value: %o',
31-
'Take a look at the logic that dispatched this action: %o.',
32-
'(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)'
33-
].join('\n')
34-
3523
interface NonSerializableValue {
3624
keyPath: string
3725
value: unknown
@@ -135,7 +123,13 @@ export function createSerializableStateInvariantMiddleware(
135123
if (foundActionNonSerializableValue) {
136124
const { keyPath, value } = foundActionNonSerializableValue
137125

138-
console.error(NON_SERIALIZABLE_ACTION_MESSAGE, keyPath, value, action)
126+
console.error(
127+
`A non-serializable value was detected in an action, in the path: \`${keyPath}\`. Value:`,
128+
value,
129+
'\nTake a look at the logic that dispatched this action: ',
130+
action,
131+
'\n(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)'
132+
)
139133
}
140134

141135
const result = next(action)
@@ -152,7 +146,13 @@ export function createSerializableStateInvariantMiddleware(
152146
if (foundStateNonSerializableValue) {
153147
const { keyPath, value } = foundStateNonSerializableValue
154148

155-
console.error(NON_SERIALIZABLE_STATE_MESSAGE, keyPath, value, action.type)
149+
console.error(
150+
`A non-serializable value was detected in the state, in the path: \`${keyPath}\`. Value:`,
151+
value,
152+
`
153+
Take a look at the reducer(s) handling this action type: ${action.type}.
154+
(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)`
155+
)
156156
}
157157

158158
return result

0 commit comments

Comments
 (0)