1
1
import fetchMock from 'fetch-mock-jest' ;
2
+ import { cloneDeep } from 'lodash' ;
2
3
import {
3
4
fireEvent ,
4
5
initializeMocks ,
@@ -8,41 +9,29 @@ import {
8
9
within ,
9
10
} from '../../testUtils' ;
10
11
import mockResult from '../__mocks__/collection-search.json' ;
11
- import mockEmptyResult from '../../search-modal/__mocks__/empty-search-result.json' ;
12
12
import {
13
- mockCollection , mockContentLibrary , mockLibraryBlockTypes , mockXBlockFields ,
13
+ mockContentLibrary , mockLibraryBlockTypes , mockXBlockFields ,
14
14
} from '../data/api.mocks' ;
15
15
import { mockContentSearchConfig } from '../../search-manager/data/api.mock' ;
16
16
import { mockBroadcastChannel } from '../../generic/data/api.mock' ;
17
17
import { LibraryLayout } from '..' ;
18
18
19
19
mockContentSearchConfig . applyMock ( ) ;
20
20
mockContentLibrary . applyMock ( ) ;
21
- mockCollection . applyMock ( ) ;
22
21
mockLibraryBlockTypes . applyMock ( ) ;
23
22
mockXBlockFields . applyMock ( ) ;
24
23
mockBroadcastChannel ( ) ;
25
24
26
25
const searchEndpoint = 'http://mock.meilisearch.local/multi-search' ;
27
-
28
- /**
29
- * Returns 0 components from the search query.
30
- */
31
- const returnEmptyResult = ( _url , req ) => {
32
- const requestData = JSON . parse ( req . body ?. toString ( ) ?? '' ) ;
33
- const query = requestData ?. queries [ 0 ] ?. q ?? '' ;
34
- // We have to replace the query (search keywords) in the mock results with the actual query,
35
- // because otherwise we may have an inconsistent state that causes more queries and unexpected results.
36
- mockEmptyResult . results [ 0 ] . query = query ;
37
- // And fake the required '_formatted' fields; it contains the highlighting <mark>...</mark> around matched words
38
- // eslint-disable-next-line no-underscore-dangle, no-param-reassign
39
- mockEmptyResult . results [ 0 ] ?. hits . forEach ( ( hit ) => { hit . _formatted = { ...hit } ; } ) ;
40
- return mockEmptyResult ;
41
- } ;
42
-
43
26
const path = '/library/:libraryId/*' ;
44
27
const libraryTitle = mockContentLibrary . libraryData . title ;
45
- const collectionTitle = mockCollection . collectionData . title ;
28
+ const mockCollection = {
29
+ collectionId : mockResult . results [ 2 ] . hits [ 0 ] . block_id ,
30
+ collectionNeverLoads : 'collection-always-loading' ,
31
+ collectionEmpty : 'collection-no-data' ,
32
+ collectionNoComponents : 'collection-no-components' ,
33
+ title : mockResult . results [ 2 ] . hits [ 0 ] . display_name ,
34
+ } ;
46
35
47
36
describe ( '<LibraryCollectionPage />' , ( ) => {
48
37
beforeEach ( ( ) => {
@@ -52,14 +41,33 @@ describe('<LibraryCollectionPage />', () => {
52
41
fetchMock . post ( searchEndpoint , ( _url , req ) => {
53
42
const requestData = JSON . parse ( req . body ?. toString ( ) ?? '' ) ;
54
43
const query = requestData ?. queries [ 0 ] ?. q ?? '' ;
44
+ const mockResultCopy = cloneDeep ( mockResult ) ;
55
45
// We have to replace the query (search keywords) in the mock results with the actual query,
56
46
// because otherwise Instantsearch will update the UI and change the query,
57
47
// leading to unexpected results in the test cases.
58
- mockResult . results [ 0 ] . query = query ;
48
+ mockResultCopy . results [ 0 ] . query = query ;
49
+ mockResultCopy . results [ 2 ] . query = query ;
59
50
// And fake the required '_formatted' fields; it contains the highlighting <mark>...</mark> around matched words
60
51
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
61
- mockResult . results [ 0 ] ?. hits . forEach ( ( hit ) => { hit . _formatted = { ...hit } ; } ) ;
62
- return mockResult ;
52
+ mockResultCopy . results [ 0 ] ?. hits . forEach ( ( hit ) => { hit . _formatted = { ...hit } ; } ) ;
53
+ const collectionQueryId = requestData ?. queries [ 2 ] ?. filter [ 2 ] ?. split ( 'block_id = "' ) [ 1 ] . split ( '"' ) [ 0 ] ;
54
+ switch ( collectionQueryId ) {
55
+ case mockCollection . collectionNeverLoads :
56
+ return new Promise < any > ( ( ) => { } ) ;
57
+ case mockCollection . collectionEmpty :
58
+ mockResultCopy . results [ 2 ] . hits = [ ] ;
59
+ mockResultCopy . results [ 2 ] . estimatedTotalHits = 0 ;
60
+ break ;
61
+ case mockCollection . collectionNoComponents :
62
+ mockResultCopy . results [ 0 ] . hits = [ ] ;
63
+ mockResultCopy . results [ 0 ] . estimatedTotalHits = 0 ;
64
+ mockResultCopy . results [ 1 ] . facetDistribution . block_type = { } ;
65
+ mockResultCopy . results [ 2 ] . hits [ 0 ] . num_children = 0 ;
66
+ break ;
67
+ default :
68
+ break ;
69
+ }
70
+ return mockResultCopy ;
63
71
} ) ;
64
72
} ) ;
65
73
@@ -69,34 +77,39 @@ describe('<LibraryCollectionPage />', () => {
69
77
} ) ;
70
78
71
79
const renderLibraryCollectionPage = async ( collectionId ?: string , libraryId ?: string ) => {
72
- const libId = libraryId || mockCollection . libraryId ;
80
+ const libId = libraryId || mockContentLibrary . libraryId ;
73
81
const colId = collectionId || mockCollection . collectionId ;
74
82
render ( < LibraryLayout /> , {
75
83
path,
76
84
routerProps : {
77
85
initialEntries : [ `/library/${ libId } /collection/${ colId } ` ] ,
78
86
} ,
79
87
} ) ;
88
+
89
+ if ( colId !== mockCollection . collectionNeverLoads ) {
90
+ await waitFor ( ( ) => { expect ( fetchMock ) . toHaveFetchedTimes ( 1 , searchEndpoint , 'post' ) ; } ) ;
91
+ }
80
92
} ;
81
93
82
94
it ( 'shows the spinner before the query is complete' , async ( ) => {
83
95
// This mock will never return data about the collection (it loads forever):
84
- await renderLibraryCollectionPage ( mockCollection . collectionIdThatNeverLoads ) ;
96
+ await renderLibraryCollectionPage ( mockCollection . collectionNeverLoads ) ;
85
97
const spinner = screen . getByRole ( 'status' ) ;
86
98
expect ( spinner . textContent ) . toEqual ( 'Loading...' ) ;
87
99
} ) ;
88
100
89
101
it ( 'shows an error component if no collection returned' , async ( ) => {
90
- // This mock will simulate a 404 error:
91
- await renderLibraryCollectionPage ( mockCollection . collection404 ) ;
102
+ // This mock will simulate incorrect collection id
103
+ await renderLibraryCollectionPage ( mockCollection . collectionEmpty ) ;
104
+ screen . debug ( ) ;
92
105
expect ( await screen . findByTestId ( 'notFoundAlert' ) ) . toBeInTheDocument ( ) ;
93
106
} ) ;
94
107
95
108
it ( 'shows collection data' , async ( ) => {
96
109
await renderLibraryCollectionPage ( ) ;
97
110
expect ( await screen . findByText ( 'All Collections' ) ) . toBeInTheDocument ( ) ;
98
111
expect ( ( await screen . findAllByText ( libraryTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
99
- expect ( ( await screen . findAllByText ( collectionTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
112
+ expect ( ( await screen . findAllByText ( mockCollection . title ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
100
113
101
114
expect ( screen . queryByText ( 'This collection is currently empty.' ) ) . not . toBeInTheDocument ( ) ;
102
115
@@ -108,12 +121,11 @@ describe('<LibraryCollectionPage />', () => {
108
121
} ) ;
109
122
110
123
it ( 'shows a collection without associated components' , async ( ) => {
111
- fetchMock . post ( searchEndpoint , returnEmptyResult , { overwriteRoutes : true } ) ;
112
- await renderLibraryCollectionPage ( ) ;
124
+ await renderLibraryCollectionPage ( mockCollection . collectionNoComponents ) ;
113
125
114
126
expect ( await screen . findByText ( 'All Collections' ) ) . toBeInTheDocument ( ) ;
115
127
expect ( ( await screen . findAllByText ( libraryTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
116
- expect ( ( await screen . findAllByText ( collectionTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
128
+ expect ( ( await screen . findAllByText ( mockCollection . title ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
117
129
118
130
expect ( screen . getByText ( 'This collection is currently empty.' ) ) . toBeInTheDocument ( ) ;
119
131
@@ -125,7 +137,7 @@ describe('<LibraryCollectionPage />', () => {
125
137
it ( 'shows the new content button' , async ( ) => {
126
138
await renderLibraryCollectionPage ( ) ;
127
139
128
- expect ( await screen . findByRole ( 'heading ') ) . toBeInTheDocument ( ) ;
140
+ expect ( await screen . findByText ( 'All Collections ') ) . toBeInTheDocument ( ) ;
129
141
expect ( await screen . findByText ( 'Content (5)' ) ) . toBeInTheDocument ( ) ;
130
142
expect ( screen . getByRole ( 'button' , { name : / n e w / i } ) ) . toBeInTheDocument ( ) ;
131
143
expect ( screen . queryByText ( 'Read Only' ) ) . not . toBeInTheDocument ( ) ;
@@ -135,9 +147,7 @@ describe('<LibraryCollectionPage />', () => {
135
147
// Use a library mock that is read-only:
136
148
const libraryId = mockContentLibrary . libraryIdReadOnly ;
137
149
// Update search mock so it returns no results:
138
- fetchMock . post ( searchEndpoint , returnEmptyResult , { overwriteRoutes : true } ) ;
139
- await renderLibraryCollectionPage ( mockCollection . collectionId , libraryId ) ;
140
- await waitFor ( ( ) => { expect ( fetchMock ) . toHaveFetchedTimes ( 1 , searchEndpoint , 'post' ) ; } ) ;
150
+ await renderLibraryCollectionPage ( mockCollection . collectionNoComponents , libraryId ) ;
141
151
142
152
expect ( await screen . findByText ( 'All Collections' ) ) . toBeInTheDocument ( ) ;
143
153
expect ( screen . getByText ( 'This collection is currently empty.' ) ) . toBeInTheDocument ( ) ;
@@ -147,27 +157,23 @@ describe('<LibraryCollectionPage />', () => {
147
157
148
158
it ( 'show a collection without search results' , async ( ) => {
149
159
// Update search mock so it returns no results:
150
- fetchMock . post ( searchEndpoint , returnEmptyResult , { overwriteRoutes : true } ) ;
151
- await renderLibraryCollectionPage ( ) ;
160
+ await renderLibraryCollectionPage ( mockCollection . collectionNoComponents ) ;
152
161
153
162
expect ( await screen . findByText ( 'All Collections' ) ) . toBeInTheDocument ( ) ;
154
163
expect ( ( await screen . findAllByText ( libraryTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
155
- expect ( ( await screen . findAllByText ( collectionTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
156
-
157
- await waitFor ( ( ) => { expect ( fetchMock ) . toHaveFetchedTimes ( 1 , searchEndpoint , 'post' ) ; } ) ;
164
+ expect ( ( await screen . findAllByText ( mockCollection . title ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
158
165
159
166
fireEvent . change ( screen . getByRole ( 'searchbox' ) , { target : { value : 'noresults' } } ) ;
160
167
161
168
// Ensure the search endpoint is called again, only once more since the recently modified call
162
169
// should not be impacted by the search
163
170
await waitFor ( ( ) => { expect ( fetchMock ) . toHaveFetchedTimes ( 2 , searchEndpoint , 'post' ) ; } ) ;
164
171
165
- expect ( screen . getByText ( 'No matching components found in this collections.' ) ) . toBeInTheDocument ( ) ;
172
+ expect ( screen . queryByText ( 'No matching components found in this collections.' ) ) . toBeInTheDocument ( ) ;
166
173
} ) ;
167
174
168
175
it ( 'should open and close new content sidebar' , async ( ) => {
169
176
await renderLibraryCollectionPage ( ) ;
170
- await waitFor ( ( ) => { expect ( fetchMock ) . toHaveFetchedTimes ( 1 , searchEndpoint , 'post' ) ; } ) ;
171
177
172
178
expect ( await screen . findByText ( 'All Collections' ) ) . toBeInTheDocument ( ) ;
173
179
expect ( screen . queryByText ( / a d d c o n t e n t / i) ) . not . toBeInTheDocument ( ) ;
@@ -188,8 +194,8 @@ describe('<LibraryCollectionPage />', () => {
188
194
189
195
expect ( await screen . findByText ( 'All Collections' ) ) . toBeInTheDocument ( ) ;
190
196
expect ( ( await screen . findAllByText ( libraryTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
191
- expect ( ( await screen . findAllByText ( collectionTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
192
- expect ( ( await screen . findAllByText ( collectionTitle ) ) [ 1 ] ) . toBeInTheDocument ( ) ;
197
+ expect ( ( await screen . findAllByText ( mockCollection . title ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
198
+ expect ( ( await screen . findAllByText ( mockCollection . title ) ) [ 1 ] ) . toBeInTheDocument ( ) ;
193
199
194
200
expect ( screen . getByText ( 'Manage' ) ) . toBeInTheDocument ( ) ;
195
201
expect ( screen . getByText ( 'Details' ) ) . toBeInTheDocument ( ) ;
@@ -200,8 +206,8 @@ describe('<LibraryCollectionPage />', () => {
200
206
201
207
expect ( await screen . findByText ( 'All Collections' ) ) . toBeInTheDocument ( ) ;
202
208
expect ( ( await screen . findAllByText ( libraryTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
203
- expect ( ( await screen . findAllByText ( collectionTitle ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
204
- expect ( ( await screen . findAllByText ( collectionTitle ) ) [ 1 ] ) . toBeInTheDocument ( ) ;
209
+ expect ( ( await screen . findAllByText ( mockCollection . title ) ) [ 0 ] ) . toBeInTheDocument ( ) ;
210
+ expect ( ( await screen . findAllByText ( mockCollection . title ) ) [ 1 ] ) . toBeInTheDocument ( ) ;
205
211
206
212
// Open by default; close the library info sidebar
207
213
const closeButton = screen . getByRole ( 'button' , { name : / c l o s e / i } ) ;
@@ -218,7 +224,6 @@ describe('<LibraryCollectionPage />', () => {
218
224
219
225
it ( 'sorts collection components' , async ( ) => {
220
226
await renderLibraryCollectionPage ( ) ;
221
- await waitFor ( ( ) => { expect ( fetchMock ) . toHaveFetchedTimes ( 1 , searchEndpoint , 'post' ) ; } ) ;
222
227
223
228
expect ( await screen . findByTitle ( 'Sort search results' ) ) . toBeInTheDocument ( ) ;
224
229
@@ -310,9 +315,7 @@ describe('<LibraryCollectionPage />', () => {
310
315
} ) ;
311
316
312
317
it ( 'has an empty type filter when there are no results' , async ( ) => {
313
- fetchMock . post ( searchEndpoint , returnEmptyResult , { overwriteRoutes : true } ) ;
314
- await renderLibraryCollectionPage ( ) ;
315
- await waitFor ( ( ) => { expect ( fetchMock ) . toHaveFetchedTimes ( 1 , searchEndpoint , 'post' ) ; } ) ;
318
+ await renderLibraryCollectionPage ( mockCollection . collectionNoComponents ) ;
316
319
317
320
const filterButton = screen . getByRole ( 'button' , { name : / t y p e / i } ) ;
318
321
fireEvent . click ( filterButton ) ;
0 commit comments