1
1
import { QueryClient , QueryClientProvider } from '@tanstack/react-query'
2
2
import { render , screen , waitFor } from '@testing-library/react'
3
3
import userEvent from '@testing-library/user-event'
4
- import { Component , useState } from 'react'
5
- import { MemoryRouter , useHistory } from 'react-router-dom'
4
+ import { graphql , HttpResponse } from 'msw'
5
+ import { setupServer } from 'msw/node'
6
+ import qs from 'qs'
7
+ import { Component , Suspense , useState } from 'react'
8
+ import { MemoryRouter , Route , useHistory } from 'react-router-dom'
6
9
import { vi } from 'vitest'
7
10
8
11
import config from 'config'
@@ -13,15 +16,67 @@ import NetworkErrorBoundary from './NetworkErrorBoundary'
13
16
vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => undefined )
14
17
vi . mock ( 'config' )
15
18
16
- const queryClient = new QueryClient ( {
17
- defaultOptions : { queries : { retry : false } } ,
19
+ const mockUser = {
20
+ me : {
21
+ owner : {
22
+ defaultOrgUsername : 'codecov' ,
23
+ } ,
24
+
25
+ privateAccess : true ,
26
+ onboardingCompleted : true ,
27
+ businessEmail :
'[email protected] ' ,
28
+ termsAgreement : true ,
29
+ user : {
30
+ name : 'Jane Doe' ,
31
+ username : 'janedoe' ,
32
+ avatarUrl : 'http://127.0.0.1/avatar-url' ,
33
+ avatar : 'http://127.0.0.1/avatar-url' ,
34
+ student : false ,
35
+ studentCreatedAt : null ,
36
+ studentUpdatedAt : null ,
37
+ } ,
38
+ trackingMetadata : {
39
+ service : 'github' ,
40
+ ownerid : 123 ,
41
+ serviceId : '123' ,
42
+ plan : 'users-basic' ,
43
+ staff : false ,
44
+ hasYaml : false ,
45
+ bot : null ,
46
+ delinquent : null ,
47
+ didTrial : null ,
48
+ planProvider : null ,
49
+ planUserCount : 1 ,
50
+ createdAt : 'timestamp' ,
51
+ updatedAt : 'timestamp' ,
52
+ profile : {
53
+ createdAt : 'timestamp' ,
54
+ otherGoal : null ,
55
+ typeProjects : [ ] ,
56
+ goals : [ ] ,
57
+ } ,
58
+ } ,
59
+ } ,
60
+ }
61
+
62
+ const nullUser = {
63
+ me : null ,
64
+ }
65
+
66
+ const server = setupServer ( )
67
+ beforeAll ( ( ) => {
68
+ server . listen ( )
18
69
} )
19
70
20
71
afterEach ( ( ) => {
21
- queryClient . clear ( )
72
+ server . resetHandlers ( )
22
73
vi . clearAllMocks ( )
23
74
} )
24
75
76
+ afterAll ( ( ) => {
77
+ server . close ( )
78
+ } )
79
+
25
80
class TestErrorBoundary extends Component {
26
81
constructor ( props ) {
27
82
super ( props )
@@ -48,9 +103,7 @@ function ErrorComponent({ status, detail, typename, dev, error }) {
48
103
// eslint-disable-next-line no-throw-literal
49
104
throw {
50
105
status,
51
- data : {
52
- detail,
53
- } ,
106
+ data : { detail } ,
54
107
dev,
55
108
error,
56
109
__typename : typename ,
@@ -105,16 +158,37 @@ function App({ status, detail, typename, dev, error }) {
105
158
106
159
const wrapper =
107
160
( initialEntries = [ '/gh/codecov' , '/gh' ] ) =>
108
- ( { children } ) => (
109
- < QueryClientProvider client = { queryClient } >
110
- < MemoryRouter initialEntries = { initialEntries } > { children } </ MemoryRouter >
111
- </ QueryClientProvider >
112
- )
161
+ ( { children } ) => {
162
+ const queryClient = new QueryClient ( {
163
+ defaultOptions : { queries : { retry : false , suspense : true } } ,
164
+ } )
165
+
166
+ return (
167
+ < QueryClientProvider client = { queryClient } >
168
+ < MemoryRouter initialEntries = { initialEntries } >
169
+ < Route path = "/:provider" >
170
+ < Suspense fallback = { < div > Loading</ div > } > { children } </ Suspense >
171
+ </ Route >
172
+ </ MemoryRouter >
173
+ </ QueryClientProvider >
174
+ )
175
+ }
113
176
114
177
describe ( 'NetworkErrorBoundary' , ( ) => {
115
- function setup ( { isSelfHosted = false } = { isSelfHosted : false } ) {
178
+ function setup (
179
+ { isSelfHosted = false , user = nullUser } = {
180
+ isSelfHosted : false ,
181
+ user : nullUser ,
182
+ }
183
+ ) {
116
184
config . IS_SELF_HOSTED = isSelfHosted
117
185
186
+ server . use (
187
+ graphql . query ( 'CurrentUser' , ( ) => {
188
+ return HttpResponse . json ( { data : user } )
189
+ } )
190
+ )
191
+
118
192
return { user : userEvent . setup ( ) }
119
193
}
120
194
@@ -310,6 +384,41 @@ describe('NetworkErrorBoundary', () => {
310
384
const button = await screen . findByText ( 'Return to previous page' )
311
385
expect ( button ) . toBeInTheDocument ( )
312
386
} )
387
+
388
+ describe ( 'user is not logged in' , ( ) => {
389
+ it ( 'renders a login button' , async ( ) => {
390
+ const { user } = setup ( { user : nullUser } )
391
+ render ( < App status = { 404 } /> , {
392
+ wrapper : wrapper ( ) ,
393
+ } )
394
+
395
+ const textBox = await screen . findByRole ( 'textbox' )
396
+ await user . type ( textBox , 'fail' )
397
+
398
+ const queryString = qs . stringify ( { to : '/gh/codecov' } )
399
+ const loginButton = await screen . findByText ( / L o g i n / )
400
+ expect ( loginButton ) . toBeInTheDocument ( )
401
+ expect ( loginButton ) . toHaveAttribute ( 'href' , `/login?${ queryString } ` )
402
+ } )
403
+ } )
404
+
405
+ describe ( 'user is logged in' , ( ) => {
406
+ it ( 'does not render a login button' , async ( ) => {
407
+ const { user } = setup ( { user : mockUser } )
408
+ render ( < App status = { 404 } /> , {
409
+ wrapper : wrapper ( ) ,
410
+ } )
411
+
412
+ const textBox = await screen . findByRole ( 'textbox' )
413
+ await user . type ( textBox , 'fail' )
414
+
415
+ const notFound = await screen . findByText ( / N o t f o u n d / )
416
+ expect ( notFound ) . toBeInTheDocument ( )
417
+
418
+ const loginButton = screen . queryByText ( / L o g i n / )
419
+ expect ( loginButton ) . not . toBeInTheDocument ( )
420
+ } )
421
+ } )
313
422
} )
314
423
315
424
describe ( 'when running in self hosted mode' , ( ) => {
@@ -322,7 +431,7 @@ describe('NetworkErrorBoundary', () => {
322
431
const textBox = await screen . findByRole ( 'textbox' )
323
432
await user . type ( textBox , 'fail' )
324
433
325
- const pleaseSee = await screen . findByText ( / P l e a s e s e e / )
434
+ const pleaseSee = await screen . findByText ( / p l e a s e s e e / )
326
435
expect ( pleaseSee ) . toBeInTheDocument ( )
327
436
} )
328
437
@@ -338,6 +447,41 @@ describe('NetworkErrorBoundary', () => {
338
447
const button = await screen . findByText ( 'Return to previous page' )
339
448
expect ( button ) . toBeInTheDocument ( )
340
449
} )
450
+
451
+ describe ( 'user is not logged in' , ( ) => {
452
+ it ( 'renders a login button' , async ( ) => {
453
+ const { user } = setup ( { user : nullUser , isSelfHosted : true } )
454
+ render ( < App status = { 404 } /> , {
455
+ wrapper : wrapper ( ) ,
456
+ } )
457
+
458
+ const textBox = await screen . findByRole ( 'textbox' )
459
+ await user . type ( textBox , 'fail' )
460
+
461
+ const queryString = qs . stringify ( { to : '/gh/codecov' } )
462
+ const loginButton = await screen . findByText ( / L o g i n / )
463
+ expect ( loginButton ) . toBeInTheDocument ( )
464
+ expect ( loginButton ) . toHaveAttribute ( 'href' , `/?${ queryString } ` )
465
+ } )
466
+ } )
467
+
468
+ describe ( 'user is logged in' , ( ) => {
469
+ it ( 'does not render a login button' , async ( ) => {
470
+ const { user } = setup ( { user : mockUser , isSelfHosted : true } )
471
+ render ( < App status = { 404 } /> , {
472
+ wrapper : wrapper ( ) ,
473
+ } )
474
+
475
+ const textBox = await screen . findByRole ( 'textbox' )
476
+ await user . type ( textBox , 'fail' )
477
+
478
+ const notFound = await screen . findByText ( / N o t f o u n d / )
479
+ expect ( notFound ) . toBeInTheDocument ( )
480
+
481
+ const loginButton = screen . queryByText ( / L o g i n / )
482
+ expect ( loginButton ) . not . toBeInTheDocument ( )
483
+ } )
484
+ } )
341
485
} )
342
486
} )
343
487
@@ -366,7 +510,7 @@ describe('NetworkErrorBoundary', () => {
366
510
global . fetch = vi . fn ( ( ) =>
367
511
Promise . resolve ( {
368
512
ok : true ,
369
- json : ( ) => Promise . resolve ( { } ) ,
513
+ json : ( ) => Promise . resolve ( { data : nullUser } ) ,
370
514
} )
371
515
)
372
516
0 commit comments