11
11
import {
12
12
DoesNotUseKey ,
13
13
FragmentWithProp ,
14
- ManualConsoleError ,
15
- ManualConsoleErrorWithStack ,
16
14
} from './__fixtures__/ReactWarningFixtures' ;
17
15
import * as React from 'react' ;
18
16
19
17
const LogBoxData = require ( '../Data/LogBoxData' ) ;
20
18
const TestRenderer = require ( 'react-test-renderer' ) ;
21
19
22
- const ExceptionsManager = require ( '../../Core/ExceptionsManager.js' ) ;
23
-
24
20
const installLogBox = ( ) => {
25
- const LogBox = require ( '../LogBox' ) . default ;
21
+ const LogBox = require ( '../LogBox' ) ;
22
+
26
23
LogBox . install ( ) ;
27
24
} ;
28
25
29
26
const uninstallLogBox = ( ) => {
30
- const LogBox = require ( '../LogBox' ) . default ;
27
+ const LogBox = require ( '../LogBox' ) ;
31
28
LogBox . uninstall ( ) ;
32
29
} ;
33
30
34
- // TODO: we can remove all the symetric matchers once OSS lands component stack frames.
35
- // For now, the component stack parsing differs in ways we can't easily detect in this test.
36
- describe ( 'LogBox' , ( ) => {
31
+ const BEFORE_SLASH_RE = / (?: \/ [ a - z A - Z ] + \/ ) ( .+ ?) (?: \/ .+ ) \/ / ;
32
+
33
+ const cleanPath = message => {
34
+ return message . replace ( BEFORE_SLASH_RE , '/path/to/' ) ;
35
+ } ;
36
+
37
+ const cleanLog = logs => {
38
+ return logs . map ( log => {
39
+ return {
40
+ ...log ,
41
+ componentStack : log . componentStack . map ( stack => ( {
42
+ ...stack ,
43
+ fileName : cleanPath ( stack . fileName ) ,
44
+ } ) ) ,
45
+ } ;
46
+ } ) ;
47
+ } ;
48
+
49
+ // TODO(T71117418): Re-enable skipped LogBox integration tests once React component
50
+ // stack frames are the same internally and in open source.
51
+ // eslint-disable-next-line jest/no-disabled-tests
52
+ describe . skip ( 'LogBox' , ( ) => {
37
53
const { error, warn} = console ;
38
54
const mockError = jest . fn ( ) ;
39
55
const mockWarn = jest . fn ( ) ;
40
56
41
57
beforeEach ( ( ) => {
42
58
jest . resetModules ( ) ;
43
59
jest . restoreAllMocks ( ) ;
44
- jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
45
60
46
61
mockError . mockClear ( ) ;
47
62
mockWarn . mockClear ( ) ;
48
- // Reset ExceptionManager patching.
49
- if ( console . _errorOriginal ) {
50
- console . _errorOriginal = null ;
51
- }
63
+
52
64
( console : any ) . error = mockError ;
53
65
( console : any ) . warn = mockWarn ;
54
66
} ) ;
@@ -67,10 +79,7 @@ describe('LogBox', () => {
67
79
// so we can assert on what React logs.
68
80
jest . spyOn ( console , 'error' ) ;
69
81
70
- let output ;
71
- TestRenderer . act ( ( ) => {
72
- output = TestRenderer . create ( < DoesNotUseKey /> ) ;
73
- } ) ;
82
+ const output = TestRenderer . create ( < DoesNotUseKey /> ) ;
74
83
75
84
// The key error should always be the highest severity.
76
85
// In LogBox, we expect these errors to:
@@ -79,37 +88,16 @@ describe('LogBox', () => {
79
88
// - Pass to console.error, with a "Warning" prefix so it does not pop a RedBox.
80
89
expect ( output ) . toBeDefined ( ) ;
81
90
expect ( mockWarn ) . not . toBeCalled ( ) ;
82
- expect ( console . error ) . toBeCalledTimes ( 1 ) ;
83
- expect ( console . error . mock . calls [ 0 ] ) . toEqual ( [
84
- 'Each child in a list should have a unique "key" prop.%s%s See https://react.dev/link/warning-keys for more information.%s' ,
85
- '\n\nCheck the render method of `DoesNotUseKey`.' ,
86
- '' ,
87
- expect . stringMatching ( 'at DoesNotUseKey' ) ,
88
- ] ) ;
89
- expect ( spy ) . toHaveBeenCalledWith ( {
90
- level : 'error' ,
91
- category : expect . stringContaining (
92
- 'Warning: Each child in a list should have a unique' ,
93
- ) ,
94
- componentStack : expect . anything ( ) ,
95
- componentStackType : 'stack' ,
96
- message : {
97
- content :
98
- 'Warning: Each child in a list should have a unique "key" prop.\n\nCheck the render method of `DoesNotUseKey`. See https://react.dev/link/warning-keys for more information.' ,
99
- substitutions : [
100
- { length : 45 , offset : 62 } ,
101
- { length : 0 , offset : 107 } ,
102
- ] ,
103
- } ,
104
- } ) ;
91
+ expect ( console . error . mock . calls [ 0 ] . map ( cleanPath ) ) . toMatchSnapshot (
92
+ 'Log sent from React' ,
93
+ ) ;
94
+ expect ( cleanLog ( spy . mock . calls [ 0 ] ) ) . toMatchSnapshot ( 'Log added to LogBox' ) ;
95
+ expect ( mockError . mock . calls [ 0 ] . map ( cleanPath ) ) . toMatchSnapshot (
96
+ 'Log passed to console error' ,
97
+ ) ;
105
98
106
99
// The Warning: prefix is added due to a hack in LogBox to prevent double logging.
107
- // We also interpolate the string before passing to the underlying console method.
108
- expect ( mockError . mock . calls [ 0 ] ) . toEqual ( [
109
- expect . stringMatching (
110
- 'Warning: Each child in a list should have a unique "key" prop.\n\nCheck the render method of `DoesNotUseKey`. See https://react.dev/link/warning-keys for more information.\n at ' ,
111
- ) ,
112
- ] ) ;
100
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] . startsWith ( 'Warning: ' ) ) . toBe ( true ) ;
113
101
} ) ;
114
102
115
103
it ( 'integrates with React and handles a fragment warning in LogBox' , ( ) => {
@@ -120,10 +108,7 @@ describe('LogBox', () => {
120
108
// so we can assert on what React logs.
121
109
jest . spyOn ( console , 'error' ) ;
122
110
123
- let output ;
124
- TestRenderer . act ( ( ) => {
125
- output = TestRenderer . create ( < FragmentWithProp /> ) ;
126
- } ) ;
111
+ const output = TestRenderer . create ( < FragmentWithProp /> ) ;
127
112
128
113
// The fragment warning is not as severe. For this warning we don't want to
129
114
// pop open a dialog, so we show a collapsed error UI.
@@ -133,125 +118,15 @@ describe('LogBox', () => {
133
118
// - Pass to console.error, with a "Warning" prefix so it does not pop a RedBox.
134
119
expect ( output ) . toBeDefined ( ) ;
135
120
expect ( mockWarn ) . not . toBeCalled ( ) ;
136
- expect ( console . error ) . toBeCalledTimes ( 1 ) ;
137
- expect ( console . error . mock . calls [ 0 ] ) . toEqual ( [
138
- 'Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.%s' ,
139
- 'invalid' ,
140
- expect . stringMatching ( 'at FragmentWithProp' ) ,
141
- ] ) ;
142
- expect ( spy ) . toHaveBeenCalledWith ( {
143
- level : 'error' ,
144
- category : expect . stringContaining ( 'Warning: Invalid prop' ) ,
145
- componentStack : expect . anything ( ) ,
146
- componentStackType : expect . stringMatching ( / ( s t a c k | l e g a c y ) / ) ,
147
- message : {
148
- content :
149
- 'Warning: Invalid prop `invalid` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.' ,
150
- substitutions : [ { length : 7 , offset : 23 } ] ,
151
- } ,
152
- } ) ;
153
-
154
- // The Warning: prefix is added due to a hack in LogBox to prevent double logging.
155
- // We also interpolate the string before passing to the underlying console method.
156
- expect ( mockError . mock . calls [ 0 ] ) . toEqual ( [
157
- expect . stringMatching (
158
- 'Warning: Invalid prop `invalid` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.\n at FragmentWithProp' ,
159
- ) ,
160
- ] ) ;
161
- } ) ;
162
-
163
- it ( 'handles a manual console.error without a component stack in LogBox' , ( ) => {
164
- const LogBox = require ( '../LogBox' ) . default ;
165
- const spy = jest . spyOn ( LogBox , 'addException' ) ;
166
- installLogBox ( ) ;
167
-
168
- // console.error handling depends on installing the ExceptionsManager error reporter.
169
- ExceptionsManager . installConsoleErrorReporter ( ) ;
170
-
171
- // Spy console.error after LogBox is installed
172
- // so we can assert on what React logs.
173
- jest . spyOn ( console , 'error' ) ;
174
-
175
- let output ;
176
- TestRenderer . act ( ( ) => {
177
- output = TestRenderer . create ( < ManualConsoleError /> ) ;
178
- } ) ;
179
-
180
- // Manual console errors should show a collapsed error dialog.
181
- // When there is no component stack, we expect these errors to:
182
- // - Go to the LogBox patch and fall through to console.error.
183
- // - Get picked up by the ExceptionsManager console.error override.
184
- // - Get passed back to LogBox via addException (non-fatal).
185
- expect ( output ) . toBeDefined ( ) ;
186
- expect ( mockWarn ) . not . toBeCalled ( ) ;
187
- expect ( spy ) . toBeCalledTimes ( 1 ) ;
188
- expect ( console . error ) . toBeCalledTimes ( 1 ) ;
189
- expect ( console . error . mock . calls [ 0 ] ) . toEqual ( [ 'Manual console error' ] ) ;
190
- expect ( spy ) . toHaveBeenCalledWith ( {
191
- id : 1 ,
192
- isComponentError : false ,
193
- isFatal : false ,
194
- name : 'console.error' ,
195
- originalMessage : 'Manual console error' ,
196
- message : 'console.error: Manual console error' ,
197
- extraData : expect . anything ( ) ,
198
- componentStack : null ,
199
- stack : expect . anything ( ) ,
200
- } ) ;
201
-
202
- // No Warning: prefix is added due since this is falling through.
203
- expect ( mockError . mock . calls [ 0 ] ) . toEqual ( [ 'Manual console error' ] ) ;
204
- } ) ;
205
-
206
- it ( 'handles a manual console.error with a component stack in LogBox' , ( ) => {
207
- const spy = jest . spyOn ( LogBoxData , 'addLog' ) ;
208
- installLogBox ( ) ;
209
-
210
- // console.error handling depends on installing the ExceptionsManager error reporter.
211
- ExceptionsManager . installConsoleErrorReporter ( ) ;
212
-
213
- // Spy console.error after LogBox is installed
214
- // so we can assert on what React logs.
215
- jest . spyOn ( console , 'error' ) ;
216
-
217
- let output ;
218
- TestRenderer . act ( ( ) => {
219
- output = TestRenderer . create ( < ManualConsoleErrorWithStack /> ) ;
220
- } ) ;
221
-
222
- // Manual console errors should show a collapsed error dialog.
223
- // When there is a component stack, we expect these errors to:
224
- // - Go to the LogBox patch and be detected as a React error.
225
- // - Check the warning filter to see if there is a fiter setting.
226
- // - Call console.error with the parsed error.
227
- // - Get picked up by ExceptionsManager console.error override.
228
- // - Log to console.error.
229
- expect ( output ) . toBeDefined ( ) ;
230
- expect ( mockWarn ) . not . toBeCalled ( ) ;
231
- expect ( console . error ) . toBeCalledTimes ( 1 ) ;
232
- expect ( spy ) . toBeCalledTimes ( 1 ) ;
233
- expect ( console . error . mock . calls [ 0 ] ) . toEqual ( [
234
- expect . stringContaining (
235
- 'Manual console error\n at ManualConsoleErrorWithStack' ,
236
- ) ,
237
- ] ) ;
238
- expect ( spy ) . toHaveBeenCalledWith ( {
239
- level : 'error' ,
240
- category : expect . stringContaining ( 'Warning: Manual console error' ) ,
241
- componentStack : expect . anything ( ) ,
242
- componentStackType : 'stack' ,
243
- message : {
244
- content : 'Warning: Manual console error' ,
245
- substitutions : [ ] ,
246
- } ,
247
- } ) ;
121
+ expect ( console . error . mock . calls [ 0 ] . map ( cleanPath ) ) . toMatchSnapshot (
122
+ 'Log sent from React' ,
123
+ ) ;
124
+ expect ( cleanLog ( spy . mock . calls [ 0 ] ) ) . toMatchSnapshot ( 'Log added to LogBox' ) ;
125
+ expect ( mockError . mock . calls [ 0 ] . map ( cleanPath ) ) . toMatchSnapshot (
126
+ 'Log passed to console error' ,
127
+ ) ;
248
128
249
129
// The Warning: prefix is added due to a hack in LogBox to prevent double logging.
250
- // We also interpolate the string before passing to the underlying console method.
251
- expect ( mockError . mock . calls [ 0 ] ) . toEqual ( [
252
- expect . stringMatching (
253
- 'Warning: Manual console error\n at ManualConsoleErrorWithStack' ,
254
- ) ,
255
- ] ) ;
130
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] . startsWith ( 'Warning: ' ) ) . toBe ( true ) ;
256
131
} ) ;
257
132
} ) ;
0 commit comments