@dotcms/client 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +18 -0
- package/CLAUDE.md +253 -0
- package/MIGRATION.md +329 -0
- package/README.md +250 -434
- package/jest.config.ts +15 -0
- package/package.json +8 -25
- package/project.json +73 -0
- package/src/lib/client/client.spec.ts +147 -0
- package/src/lib/client/client.ts +125 -0
- package/src/lib/client/content/builders/collection/collection.spec.ts +514 -0
- package/src/lib/client/content/builders/collection/{collection.d.ts → collection.ts} +210 -19
- package/src/lib/client/content/builders/query/lucene-syntax/{Equals.d.ts → Equals.ts} +45 -11
- package/src/lib/client/content/builders/query/lucene-syntax/{Field.d.ts → Field.ts} +13 -5
- package/src/lib/client/content/builders/query/lucene-syntax/{NotOperand.d.ts → NotOperand.ts} +13 -5
- package/src/lib/client/content/builders/query/lucene-syntax/{Operand.d.ts → Operand.ts} +21 -7
- package/src/lib/client/content/builders/query/query.spec.ts +159 -0
- package/src/lib/client/content/builders/query/{query.d.ts → query.ts} +16 -5
- package/src/lib/client/content/builders/query/utils/{index.d.ts → index.ts} +49 -12
- package/src/lib/client/content/{content-api.d.ts → content-api.ts} +14 -4
- package/src/lib/client/content/shared/{const.d.ts → const.ts} +5 -3
- package/src/lib/client/content/shared/{types.d.ts → types.ts} +18 -2
- package/src/lib/client/content/shared/{utils.d.ts → utils.ts} +9 -1
- package/src/lib/client/models/{index.d.ts → index.ts} +8 -1
- package/src/lib/client/navigation/navigation-api.spec.ts +167 -0
- package/src/lib/client/navigation/navigation-api.ts +62 -0
- package/src/lib/client/page/page-api.spec.ts +359 -0
- package/src/lib/client/page/page-api.ts +197 -0
- package/src/lib/client/page/utils.ts +291 -0
- package/src/lib/utils/graphql/transforms.spec.ts +250 -0
- package/src/lib/utils/graphql/transforms.ts +128 -0
- package/tsconfig.json +22 -0
- package/tsconfig.lib.json +13 -0
- package/tsconfig.spec.json +9 -0
- package/index.cjs.d.ts +0 -1
- package/index.cjs.default.js +0 -1
- package/index.cjs.js +0 -1591
- package/index.cjs.mjs +0 -2
- package/index.esm.d.ts +0 -1
- package/index.esm.js +0 -1589
- package/internal.cjs.d.ts +0 -1
- package/internal.cjs.default.js +0 -1
- package/internal.cjs.js +0 -85
- package/internal.cjs.mjs +0 -2
- package/internal.esm.d.ts +0 -1
- package/internal.esm.js +0 -83
- package/src/lib/client/client.d.ts +0 -56
- package/src/lib/client/navigation/navigation-api.d.ts +0 -14
- package/src/lib/client/page/page-api.d.ts +0 -95
- package/src/lib/client/page/utils.d.ts +0 -41
- package/src/lib/utils/graphql/transforms.d.ts +0 -13
- /package/src/{index.d.ts → index.ts} +0 -0
- /package/src/{internal.d.ts → internal.ts} +0 -0
- /package/src/lib/client/content/builders/query/lucene-syntax/{index.d.ts → index.ts} +0 -0
- /package/src/lib/utils/{index.d.ts → index.ts} +0 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
|
|
3
|
+
import { ClientOptions, CollectionBuilder } from './collection';
|
|
4
|
+
|
|
5
|
+
import { CONTENT_API_URL } from '../../shared/const';
|
|
6
|
+
import { SortBy } from '../../shared/types';
|
|
7
|
+
import { Equals } from '../query/lucene-syntax';
|
|
8
|
+
|
|
9
|
+
global.fetch = jest.fn().mockReturnValue(
|
|
10
|
+
Promise.resolve({
|
|
11
|
+
ok: true,
|
|
12
|
+
json: () =>
|
|
13
|
+
Promise.resolve({
|
|
14
|
+
entity: {
|
|
15
|
+
jsonObjectView: {
|
|
16
|
+
contentlets: []
|
|
17
|
+
},
|
|
18
|
+
resultsSize: 0
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
describe('CollectionBuilder', () => {
|
|
25
|
+
const requestOptions: ClientOptions = {
|
|
26
|
+
cache: 'no-cache' // To simulate a valid request
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const serverUrl = 'http://localhost:8080';
|
|
30
|
+
|
|
31
|
+
const baseRequest = {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json'
|
|
35
|
+
},
|
|
36
|
+
...requestOptions
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const requestURL = `${serverUrl}${CONTENT_API_URL}`;
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
(fetch as jest.Mock).mockClear();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should initialize with valid configuration', async () => {
|
|
46
|
+
const contentType = 'my-content-type';
|
|
47
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
48
|
+
expect(collectionBuilder).toBeDefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('successful requests', () => {
|
|
52
|
+
it('should build a query for a basic collection', async () => {
|
|
53
|
+
const contentType = 'song';
|
|
54
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
55
|
+
|
|
56
|
+
await collectionBuilder;
|
|
57
|
+
|
|
58
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
59
|
+
...baseRequest,
|
|
60
|
+
body: JSON.stringify({
|
|
61
|
+
query: '+contentType:song +languageId:1 +live:true',
|
|
62
|
+
render: false,
|
|
63
|
+
limit: 10,
|
|
64
|
+
offset: 0,
|
|
65
|
+
depth: 0
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return the contentlets in the mapped response', async () => {
|
|
71
|
+
const contentType = 'song';
|
|
72
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
73
|
+
|
|
74
|
+
const response = await collectionBuilder;
|
|
75
|
+
|
|
76
|
+
expect(response).toEqual({
|
|
77
|
+
contentlets: [],
|
|
78
|
+
page: 1,
|
|
79
|
+
size: 0,
|
|
80
|
+
total: 0
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should return the contentlets in the mapped response with sort', async () => {
|
|
85
|
+
const contentType = 'song';
|
|
86
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
87
|
+
|
|
88
|
+
const sortBy: SortBy[] = [
|
|
89
|
+
{
|
|
90
|
+
field: 'title',
|
|
91
|
+
order: 'asc'
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
field: 'duration',
|
|
95
|
+
order: 'desc'
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const response = await collectionBuilder.sortBy(sortBy);
|
|
100
|
+
|
|
101
|
+
expect(response).toEqual({
|
|
102
|
+
contentlets: [],
|
|
103
|
+
page: 1,
|
|
104
|
+
size: 0,
|
|
105
|
+
total: 0,
|
|
106
|
+
sortedBy: sortBy
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should build a query for a collection with a specific language', async () => {
|
|
111
|
+
const contentType = 'ringsOfPower';
|
|
112
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
113
|
+
|
|
114
|
+
await collectionBuilder.language(13);
|
|
115
|
+
|
|
116
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
117
|
+
...baseRequest,
|
|
118
|
+
body: JSON.stringify({
|
|
119
|
+
query: '+contentType:ringsOfPower +languageId:13 +live:true',
|
|
120
|
+
render: false,
|
|
121
|
+
limit: 10,
|
|
122
|
+
offset: 0,
|
|
123
|
+
depth: 0
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should build a query for a collection with render on true', async () => {
|
|
129
|
+
const contentType = 'boringContentType';
|
|
130
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
131
|
+
|
|
132
|
+
await collectionBuilder.render();
|
|
133
|
+
|
|
134
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
135
|
+
...baseRequest,
|
|
136
|
+
body: JSON.stringify({
|
|
137
|
+
query: '+contentType:boringContentType +languageId:1 +live:true',
|
|
138
|
+
render: true,
|
|
139
|
+
limit: 10,
|
|
140
|
+
offset: 0,
|
|
141
|
+
depth: 0
|
|
142
|
+
})
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should build a query with multiply sortBy's", async () => {
|
|
147
|
+
const contentType = 'jedi';
|
|
148
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
149
|
+
|
|
150
|
+
await collectionBuilder.sortBy([
|
|
151
|
+
{
|
|
152
|
+
field: 'name',
|
|
153
|
+
order: 'asc'
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
field: 'force',
|
|
157
|
+
order: 'desc'
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
field: 'midichlorians',
|
|
161
|
+
order: 'desc'
|
|
162
|
+
}
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
166
|
+
...baseRequest,
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
query: '+contentType:jedi +languageId:1 +live:true',
|
|
169
|
+
render: false,
|
|
170
|
+
sort: 'name asc,force desc,midichlorians desc',
|
|
171
|
+
limit: 10,
|
|
172
|
+
offset: 0,
|
|
173
|
+
depth: 0
|
|
174
|
+
})
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should build a query with a specific depth', async () => {
|
|
179
|
+
const contentType = 'droid';
|
|
180
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
181
|
+
|
|
182
|
+
await collectionBuilder.depth(2);
|
|
183
|
+
|
|
184
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
185
|
+
...baseRequest,
|
|
186
|
+
body: JSON.stringify({
|
|
187
|
+
query: '+contentType:droid +languageId:1 +live:true',
|
|
188
|
+
render: false,
|
|
189
|
+
limit: 10,
|
|
190
|
+
offset: 0,
|
|
191
|
+
depth: 2
|
|
192
|
+
})
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should build a query with a specific limit and page', async () => {
|
|
197
|
+
const contentType = 'ship';
|
|
198
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
199
|
+
|
|
200
|
+
await collectionBuilder.limit(20).page(3);
|
|
201
|
+
|
|
202
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
203
|
+
...baseRequest,
|
|
204
|
+
body: JSON.stringify({
|
|
205
|
+
query: '+contentType:ship +languageId:1 +live:true',
|
|
206
|
+
render: false,
|
|
207
|
+
limit: 20,
|
|
208
|
+
offset: 40,
|
|
209
|
+
depth: 0
|
|
210
|
+
})
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should build a query with an specific query with main fields and custom fields of the content type', async () => {
|
|
215
|
+
const contentType = 'lightsaber';
|
|
216
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
217
|
+
|
|
218
|
+
await collectionBuilder
|
|
219
|
+
.query(
|
|
220
|
+
(
|
|
221
|
+
qb // kyberCrystal is a custom field
|
|
222
|
+
) => qb.field('kyberCrystal').equals('red')
|
|
223
|
+
)
|
|
224
|
+
.query('+modDate:2024-05-28'); // modDate is a main field so it doesn't need to specify the content type
|
|
225
|
+
|
|
226
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
227
|
+
...baseRequest,
|
|
228
|
+
body: JSON.stringify({
|
|
229
|
+
query: '+lightsaber.kyberCrystal:red +contentType:lightsaber +languageId:1 +live:true +modDate:2024-05-28',
|
|
230
|
+
render: false,
|
|
231
|
+
limit: 10,
|
|
232
|
+
offset: 0,
|
|
233
|
+
depth: 0
|
|
234
|
+
})
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("should throw an error if the query doesn't end in an instance of Equals", async () => {
|
|
239
|
+
const contentType = 'jedi';
|
|
240
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
// Force the error
|
|
244
|
+
await collectionBuilder.query((qb) => qb.field('name') as unknown as Equals);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
expect(error).toEqual(
|
|
247
|
+
new Error(
|
|
248
|
+
'Provided query is not valid. A query should end in an equals method call.\nExample:\n(queryBuilder) => queryBuilder.field("title").equals("Hello World")\nSee documentation for more information.'
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
expect(fetch).not.toHaveBeenCalled();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should throw an error if the parameter for query is not a function or string', async () => {
|
|
257
|
+
const contentType = 'jedi';
|
|
258
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
// Force the error
|
|
262
|
+
await collectionBuilder.query({} as string);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
expect(error).toEqual(
|
|
265
|
+
new Error(
|
|
266
|
+
`Parameter for query method should be a buildQuery function or a string.\nExample:\nclient.content.getCollection('Activity').query((queryBuilder) => queryBuilder.field('title').equals('Hello World'))\nor\nclient.content.getCollection('Activity').query('+Activity.title:"Hello World"') \nSee documentation for more information.`
|
|
267
|
+
)
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
expect(fetch).not.toHaveBeenCalled();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should throw an error if the depth is out of range (positive value)', async () => {
|
|
275
|
+
const contentType = 'jedi';
|
|
276
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
// Force the error
|
|
280
|
+
await collectionBuilder.depth(5);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
expect(error).toEqual(new Error('Depth value must be between 0 and 3'));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
expect(fetch).not.toHaveBeenCalled();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should throw an error if the depth is out of range (negative value)', async () => {
|
|
289
|
+
const contentType = 'jedi';
|
|
290
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
// Force the error
|
|
294
|
+
await collectionBuilder.depth(-5);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
expect(error).toEqual(new Error('Depth value must be between 0 and 3'));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
expect(fetch).not.toHaveBeenCalled();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should build a query for draft content', async () => {
|
|
303
|
+
const contentType = 'draftContent';
|
|
304
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
305
|
+
|
|
306
|
+
await collectionBuilder.draft();
|
|
307
|
+
|
|
308
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
309
|
+
...baseRequest,
|
|
310
|
+
body: JSON.stringify({
|
|
311
|
+
query: '+contentType:draftContent +languageId:1 +live:false',
|
|
312
|
+
render: false,
|
|
313
|
+
limit: 10,
|
|
314
|
+
offset: 0,
|
|
315
|
+
depth: 0
|
|
316
|
+
})
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should build a query for a collection with a specific variant', async () => {
|
|
321
|
+
const contentType = 'adventure';
|
|
322
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
323
|
+
|
|
324
|
+
await collectionBuilder.variant('dimension-1334-adventure');
|
|
325
|
+
|
|
326
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
327
|
+
...baseRequest,
|
|
328
|
+
body: JSON.stringify({
|
|
329
|
+
query: '+contentType:adventure +variant:dimension-1334-adventure +languageId:1 +live:true',
|
|
330
|
+
render: false,
|
|
331
|
+
limit: 10,
|
|
332
|
+
offset: 0,
|
|
333
|
+
depth: 0
|
|
334
|
+
})
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should handle all the query methods on GetCollection', async () => {
|
|
339
|
+
const contentType = 'forceSensitive';
|
|
340
|
+
const collectionBuilder = new CollectionBuilder(requestOptions, serverUrl, contentType);
|
|
341
|
+
|
|
342
|
+
// be sure that this test is updated when new methods are added
|
|
343
|
+
let methods = Object.getOwnPropertyNames(
|
|
344
|
+
Object.getPrototypeOf(collectionBuilder)
|
|
345
|
+
) as Array<keyof CollectionBuilder>;
|
|
346
|
+
|
|
347
|
+
// Remove the constructor and the methods that are not part of the query builder.
|
|
348
|
+
// Fetch method is removed because it is the one that makes the request and we already test that
|
|
349
|
+
// For example: ["constructor", "thisMethodIsPrivate", "thisMethodIsNotAQueryMethod", "formatQuery"]
|
|
350
|
+
const methodsToIgnore = ['constructor', 'formatResponse', 'fetchContentApi'];
|
|
351
|
+
|
|
352
|
+
// Filter to take only the methods that are part of the query builder
|
|
353
|
+
methods = methods.filter((method) => {
|
|
354
|
+
return (
|
|
355
|
+
!methodsToIgnore.includes(method) &&
|
|
356
|
+
typeof collectionBuilder[method] === 'function'
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Spy on all the methods
|
|
361
|
+
methods.forEach((method) => {
|
|
362
|
+
jest.spyOn(collectionBuilder, method as keyof CollectionBuilder);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Start of the test
|
|
366
|
+
|
|
367
|
+
// Call all the methods and fetch the content
|
|
368
|
+
await collectionBuilder
|
|
369
|
+
.language(13) // Language Id
|
|
370
|
+
.render() // To retrieve the content with the render
|
|
371
|
+
.sortBy([
|
|
372
|
+
// Sort by multiple fields
|
|
373
|
+
{
|
|
374
|
+
field: 'name',
|
|
375
|
+
order: 'asc'
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
field: 'midichlorians',
|
|
379
|
+
order: 'desc'
|
|
380
|
+
}
|
|
381
|
+
])
|
|
382
|
+
.depth(2) // Depth of the content for relationships
|
|
383
|
+
.limit(20) // Limit of content per page
|
|
384
|
+
.page(3) // Page to fetch
|
|
385
|
+
.query(
|
|
386
|
+
(
|
|
387
|
+
qb // Lucene query to append to the main query for more complex queries
|
|
388
|
+
) =>
|
|
389
|
+
qb
|
|
390
|
+
.field('kyberCrystal')
|
|
391
|
+
.equals('red')
|
|
392
|
+
.and()
|
|
393
|
+
.equals('blue')
|
|
394
|
+
.field('master')
|
|
395
|
+
.equals('Yoda')
|
|
396
|
+
.or()
|
|
397
|
+
.equals('Obi-Wan')
|
|
398
|
+
)
|
|
399
|
+
.draft() // To retrieve the draft content
|
|
400
|
+
.variant('legends-forceSensitive') // Variant of the content
|
|
401
|
+
.query('+modDate:2024-05-28 +conhost:MyCoolSite'); // Raw query to append to the main query // Fetch the content
|
|
402
|
+
|
|
403
|
+
// Check that the request was made with the correct query
|
|
404
|
+
expect(fetch).toHaveBeenCalledWith(requestURL, {
|
|
405
|
+
...baseRequest,
|
|
406
|
+
body: JSON.stringify({
|
|
407
|
+
query: '+forceSensitive.kyberCrystal:red AND blue +forceSensitive.master:Yoda OR Obi-Wan +contentType:forceSensitive +variant:legends-forceSensitive +languageId:13 +live:false +modDate:2024-05-28 +conhost:MyCoolSite',
|
|
408
|
+
render: true,
|
|
409
|
+
sort: 'name asc,midichlorians desc',
|
|
410
|
+
limit: 20,
|
|
411
|
+
offset: 40,
|
|
412
|
+
depth: 2
|
|
413
|
+
})
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// Chech that all functions for the queryBuilder were called
|
|
417
|
+
methods.forEach((method) => {
|
|
418
|
+
expect(collectionBuilder[method]).toHaveBeenCalled();
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
describe('fetch is rejected', () => {
|
|
424
|
+
it('should trigger onrejected callback', (done) => {
|
|
425
|
+
const contentType = 'song';
|
|
426
|
+
const collectionBuilder = new CollectionBuilder(
|
|
427
|
+
requestOptions,
|
|
428
|
+
serverUrl,
|
|
429
|
+
contentType
|
|
430
|
+
).language(13);
|
|
431
|
+
|
|
432
|
+
// Mock the fetch to return a rejected promise
|
|
433
|
+
(fetch as jest.Mock).mockRejectedValue(new Error('URL is invalid'));
|
|
434
|
+
|
|
435
|
+
collectionBuilder.then(
|
|
436
|
+
() => {
|
|
437
|
+
/* */
|
|
438
|
+
},
|
|
439
|
+
(error) => {
|
|
440
|
+
expect(error).toEqual(new Error('URL is invalid'));
|
|
441
|
+
done();
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('should trigger catch method', (done) => {
|
|
447
|
+
const contentType = 'song';
|
|
448
|
+
const collectionBuilder = new CollectionBuilder(
|
|
449
|
+
requestOptions,
|
|
450
|
+
serverUrl,
|
|
451
|
+
contentType
|
|
452
|
+
).query((dotQuery) => dotQuery.field('author').equals('Linkin Park'));
|
|
453
|
+
|
|
454
|
+
// Mock the fetch to return a rejected promise
|
|
455
|
+
(fetch as jest.Mock).mockRejectedValue(new Error('DNS are not resolving'));
|
|
456
|
+
|
|
457
|
+
collectionBuilder.then().catch((error) => {
|
|
458
|
+
expect(error).toEqual(new Error('DNS are not resolving'));
|
|
459
|
+
done();
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should trigger catch of try catch block', async () => {
|
|
464
|
+
const contentType = 'song';
|
|
465
|
+
const collectionBuilder = new CollectionBuilder(
|
|
466
|
+
requestOptions,
|
|
467
|
+
serverUrl,
|
|
468
|
+
contentType
|
|
469
|
+
).query((dotQuery) => dotQuery.field('author').equals('Linkin Park'));
|
|
470
|
+
|
|
471
|
+
// Mock a network error
|
|
472
|
+
(fetch as jest.Mock).mockRejectedValue(new Error('Network error'));
|
|
473
|
+
|
|
474
|
+
try {
|
|
475
|
+
await collectionBuilder;
|
|
476
|
+
} catch (e) {
|
|
477
|
+
expect(e).toEqual(new Error('Network error'));
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
describe('fetch resolves on error', () => {
|
|
483
|
+
it('should have the error content on then', async () => {
|
|
484
|
+
const contentType = 'song';
|
|
485
|
+
const collectionBuilder = new CollectionBuilder(
|
|
486
|
+
requestOptions,
|
|
487
|
+
serverUrl,
|
|
488
|
+
contentType
|
|
489
|
+
).limit(10);
|
|
490
|
+
|
|
491
|
+
const error = {
|
|
492
|
+
message: 'Internal server error',
|
|
493
|
+
buffer: {
|
|
494
|
+
stacktrace: 'Some really long server stacktrace'
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// Mock the fetch to return a rejected promise
|
|
499
|
+
(fetch as jest.Mock).mockReturnValue(
|
|
500
|
+
Promise.resolve({
|
|
501
|
+
status: 500,
|
|
502
|
+
json: () => Promise.resolve(error)
|
|
503
|
+
})
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
collectionBuilder.then((response) => {
|
|
507
|
+
expect(response).toEqual({
|
|
508
|
+
status: 500,
|
|
509
|
+
...error
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
});
|