@webitel/api-services 0.1.60 → 0.1.62
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/package.json +1 -1
- package/src/api/clients/calendars/__tests__/calendars.spec.ts +795 -0
- package/src/api/clients/calendars/calendars.ts +89 -62
- package/src/validations/bucket/bucket.validations.ts +8 -0
- package/src/validations/calendar/__tests__/calendar.validations.spec.ts +526 -0
- package/src/validations/calendar/calendar.validations.ts +181 -0
- package/src/validations/index.ts +2 -0
- package/types/.tsbuildinfo +1 -1
- package/types/api/clients/calendars/calendars.d.ts +7 -0
- package/types/validations/bucket/bucket.validations.d.ts +6 -0
- package/types/validations/calendar/calendar.validations.d.ts +26 -0
- package/types/validations/index.d.ts +2 -0
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const mockCalendarService = {
|
|
4
|
+
searchCalendar: vi.fn(),
|
|
5
|
+
createCalendar: vi.fn(),
|
|
6
|
+
searchTimezones: vi.fn(),
|
|
7
|
+
deleteCalendar: vi.fn(),
|
|
8
|
+
readCalendar: vi.fn(),
|
|
9
|
+
updateCalendar: vi.fn(),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const mockPermissionsApi = {
|
|
13
|
+
getPermissionsList: vi.fn(),
|
|
14
|
+
patchPermissions: vi.fn(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const generatePermissionsApiMock = vi.fn(() => mockPermissionsApi);
|
|
18
|
+
|
|
19
|
+
vi.mock('@webitel/api-services/gen', () => ({
|
|
20
|
+
getCalendarService: () => mockCalendarService,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
vi.mock('../../_shared/generatePermissionsApi', () => ({
|
|
24
|
+
generatePermissionsApi: generatePermissionsApiMock,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const { CalendarsAPI } = await import('../calendars');
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
generatePermissionsApi(baseUrl) is invoked once, at module-evaluation time,
|
|
31
|
+
when CalendarsAPI is built - capture that call now, before any `beforeEach`
|
|
32
|
+
has a chance to clear the mock's history.
|
|
33
|
+
*/
|
|
34
|
+
const permissionsApiCalls = generatePermissionsApiMock.mock.calls;
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('CalendarsAPI', () => {
|
|
41
|
+
describe('permissions API composition', () => {
|
|
42
|
+
it('builds the permissions API with the calendars base url', () => {
|
|
43
|
+
expect(permissionsApiCalls).toEqual([
|
|
44
|
+
[
|
|
45
|
+
'/calendars',
|
|
46
|
+
],
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('spreads the generated permissions methods onto the public API', () => {
|
|
51
|
+
expect(CalendarsAPI.getPermissionsList).toBe(
|
|
52
|
+
mockPermissionsApi.getPermissionsList,
|
|
53
|
+
);
|
|
54
|
+
expect(CalendarsAPI.patchPermissions).toBe(
|
|
55
|
+
mockPermissionsApi.patchPermissions,
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('getList', () => {
|
|
61
|
+
it('applies default pagination and appends a wildcard to a plain search term', async () => {
|
|
62
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
63
|
+
data: {},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await CalendarsAPI.getList({
|
|
67
|
+
search: 'foo',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(mockCalendarService.searchCalendar).toHaveBeenCalledWith({
|
|
71
|
+
page: 1,
|
|
72
|
+
size: 10,
|
|
73
|
+
q: 'foo*',
|
|
74
|
+
sort: undefined,
|
|
75
|
+
fields: undefined,
|
|
76
|
+
id: undefined,
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('prefers an explicit q over search and does not double the wildcard', async () => {
|
|
81
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
82
|
+
data: {},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await CalendarsAPI.getList({
|
|
86
|
+
q: 'bar*',
|
|
87
|
+
search: 'ignored',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(mockCalendarService.searchCalendar).toHaveBeenCalledWith(
|
|
91
|
+
expect.objectContaining({
|
|
92
|
+
q: 'bar*',
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('leaves a bare wildcard search untouched', async () => {
|
|
98
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
99
|
+
data: {},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await CalendarsAPI.getList({
|
|
103
|
+
search: '*',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(mockCalendarService.searchCalendar).toHaveBeenCalledWith(
|
|
107
|
+
expect.objectContaining({
|
|
108
|
+
q: '*',
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('lets explicit page/size override the defaults', async () => {
|
|
114
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
115
|
+
data: {},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await CalendarsAPI.getList({
|
|
119
|
+
page: 3,
|
|
120
|
+
size: 25,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(mockCalendarService.searchCalendar).toHaveBeenCalledWith(
|
|
124
|
+
expect.objectContaining({
|
|
125
|
+
page: 3,
|
|
126
|
+
size: 25,
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('normalizes the response to camelCase items merged with list defaults', async () => {
|
|
132
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
133
|
+
data: {
|
|
134
|
+
items: [
|
|
135
|
+
{
|
|
136
|
+
id: '1',
|
|
137
|
+
display_name: 'Support Hours',
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
next: true,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const result = await CalendarsAPI.getList({});
|
|
145
|
+
|
|
146
|
+
expect(result).toEqual({
|
|
147
|
+
items: [
|
|
148
|
+
{
|
|
149
|
+
id: '1',
|
|
150
|
+
displayName: 'Support Hours',
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
next: true,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('falls back to empty items/next=false when the response has none', async () => {
|
|
158
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
159
|
+
data: {},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const result = await CalendarsAPI.getList({});
|
|
163
|
+
|
|
164
|
+
expect(result).toEqual({
|
|
165
|
+
items: [],
|
|
166
|
+
next: false,
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('get', () => {
|
|
172
|
+
it('reads the calendar by string id', async () => {
|
|
173
|
+
mockCalendarService.readCalendar.mockResolvedValueOnce({
|
|
174
|
+
data: {
|
|
175
|
+
name: 'Cal',
|
|
176
|
+
timezone: {
|
|
177
|
+
id: 'tz-1',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await CalendarsAPI.get({
|
|
183
|
+
itemId: 42,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
expect(mockCalendarService.readCalendar).toHaveBeenCalledWith('42');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('maps accepts/specials/excepts from snake_case API shape to the UI shape', async () => {
|
|
190
|
+
mockCalendarService.readCalendar.mockResolvedValueOnce({
|
|
191
|
+
data: {
|
|
192
|
+
name: 'Cal',
|
|
193
|
+
description: 'desc',
|
|
194
|
+
timezone: {
|
|
195
|
+
id: 'tz-1',
|
|
196
|
+
},
|
|
197
|
+
start_at: 100,
|
|
198
|
+
end_at: 200,
|
|
199
|
+
accepts: [
|
|
200
|
+
{
|
|
201
|
+
day: 1,
|
|
202
|
+
disabled: false,
|
|
203
|
+
start_time_of_day: 540,
|
|
204
|
+
end_time_of_day: 1200,
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
specials: [
|
|
208
|
+
{
|
|
209
|
+
day: 2,
|
|
210
|
+
disabled: true,
|
|
211
|
+
start_time_of_day: 0,
|
|
212
|
+
end_time_of_day: 60,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
excepts: [
|
|
216
|
+
{
|
|
217
|
+
name: 'holiday',
|
|
218
|
+
date: 123,
|
|
219
|
+
repeat: true,
|
|
220
|
+
working: false,
|
|
221
|
+
work_start: 10,
|
|
222
|
+
work_stop: 20,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const result = await CalendarsAPI.get({
|
|
229
|
+
itemId: 1,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(result).toMatchObject({
|
|
233
|
+
name: 'Cal',
|
|
234
|
+
description: 'desc',
|
|
235
|
+
expires: true,
|
|
236
|
+
accepts: [
|
|
237
|
+
{
|
|
238
|
+
day: 1,
|
|
239
|
+
disabled: false,
|
|
240
|
+
start: 540,
|
|
241
|
+
end: 1200,
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
specials: [
|
|
245
|
+
{
|
|
246
|
+
day: 2,
|
|
247
|
+
disabled: true,
|
|
248
|
+
start: 0,
|
|
249
|
+
end: 60,
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
excepts: [
|
|
253
|
+
{
|
|
254
|
+
name: 'holiday',
|
|
255
|
+
date: 123,
|
|
256
|
+
repeat: true,
|
|
257
|
+
working: false,
|
|
258
|
+
workStart: 10,
|
|
259
|
+
workStop: 20,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('defaults accepts to an empty array instead of throwing when missing', async () => {
|
|
266
|
+
mockCalendarService.readCalendar.mockResolvedValueOnce({
|
|
267
|
+
data: {
|
|
268
|
+
name: 'Cal',
|
|
269
|
+
timezone: {
|
|
270
|
+
id: 'tz-1',
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const result = await CalendarsAPI.get({
|
|
276
|
+
itemId: 1,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
expect(result.accepts).toEqual([]);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('defaults specials/excepts to empty arrays when absent from the response', async () => {
|
|
283
|
+
mockCalendarService.readCalendar.mockResolvedValueOnce({
|
|
284
|
+
data: {
|
|
285
|
+
name: 'Cal',
|
|
286
|
+
timezone: {
|
|
287
|
+
id: 'tz-1',
|
|
288
|
+
},
|
|
289
|
+
accepts: [],
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const result = await CalendarsAPI.get({
|
|
294
|
+
itemId: 1,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
expect(result.specials).toEqual([]);
|
|
298
|
+
expect(result.excepts).toEqual([]);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('fills missing per-day fields on special entries with falsy defaults', async () => {
|
|
302
|
+
mockCalendarService.readCalendar.mockResolvedValueOnce({
|
|
303
|
+
data: {
|
|
304
|
+
name: 'Cal',
|
|
305
|
+
timezone: {
|
|
306
|
+
id: 'tz-1',
|
|
307
|
+
},
|
|
308
|
+
specials: [
|
|
309
|
+
{
|
|
310
|
+
day: 3,
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const result = await CalendarsAPI.get({
|
|
317
|
+
itemId: 1,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
expect(result.specials).toEqual([
|
|
321
|
+
{
|
|
322
|
+
day: 3,
|
|
323
|
+
disabled: false,
|
|
324
|
+
start: 0,
|
|
325
|
+
end: 0,
|
|
326
|
+
},
|
|
327
|
+
]);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('marks expires false and applies default description when the API omits both', async () => {
|
|
331
|
+
mockCalendarService.readCalendar.mockResolvedValueOnce({
|
|
332
|
+
data: {
|
|
333
|
+
name: 'Cal',
|
|
334
|
+
timezone: {
|
|
335
|
+
id: 'tz-1',
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const result = await CalendarsAPI.get({
|
|
341
|
+
itemId: 1,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
expect(result.expires).toBe(false);
|
|
345
|
+
expect(result.description).toBe('');
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
describe('add', () => {
|
|
350
|
+
const baseItem = {
|
|
351
|
+
name: 'Test Calendar',
|
|
352
|
+
description: 'A calendar',
|
|
353
|
+
timezone: {
|
|
354
|
+
id: 'tz-1',
|
|
355
|
+
name: 'UTC',
|
|
356
|
+
offset: 60,
|
|
357
|
+
},
|
|
358
|
+
expires: true,
|
|
359
|
+
startAt: 1000,
|
|
360
|
+
endAt: 2000,
|
|
361
|
+
accepts: [
|
|
362
|
+
{
|
|
363
|
+
day: 0,
|
|
364
|
+
disabled: false,
|
|
365
|
+
start: 540,
|
|
366
|
+
end: 1200,
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
specials: [
|
|
370
|
+
{
|
|
371
|
+
day: 1,
|
|
372
|
+
disabled: true,
|
|
373
|
+
start: 0,
|
|
374
|
+
end: 60,
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
excepts: [
|
|
378
|
+
{
|
|
379
|
+
name: 'holiday',
|
|
380
|
+
date: 123,
|
|
381
|
+
repeat: false,
|
|
382
|
+
working: true,
|
|
383
|
+
workStart: 10,
|
|
384
|
+
workStop: 20,
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
notInFieldList: 'should be stripped',
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
it('sanitizes, converts to snake_case and sends the calendar payload', async () => {
|
|
391
|
+
mockCalendarService.createCalendar.mockResolvedValueOnce({
|
|
392
|
+
data: {},
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
await CalendarsAPI.add({
|
|
396
|
+
itemInstance: baseItem,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
expect(mockCalendarService.createCalendar).toHaveBeenCalledTimes(1);
|
|
400
|
+
const sentItem = mockCalendarService.createCalendar.mock.calls[0][0];
|
|
401
|
+
|
|
402
|
+
expect(sentItem).not.toHaveProperty('notInFieldList');
|
|
403
|
+
expect(sentItem).not.toHaveProperty('expires');
|
|
404
|
+
expect(sentItem).toMatchObject({
|
|
405
|
+
name: 'Test Calendar',
|
|
406
|
+
description: 'A calendar',
|
|
407
|
+
start_at: 1000,
|
|
408
|
+
end_at: 2000,
|
|
409
|
+
accepts: [
|
|
410
|
+
{
|
|
411
|
+
day: 0,
|
|
412
|
+
disabled: false,
|
|
413
|
+
start_time_of_day: 540,
|
|
414
|
+
end_time_of_day: 1200,
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
specials: [
|
|
418
|
+
{
|
|
419
|
+
day: 1,
|
|
420
|
+
disabled: true,
|
|
421
|
+
start_time_of_day: 0,
|
|
422
|
+
end_time_of_day: 60,
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
excepts: [
|
|
426
|
+
{
|
|
427
|
+
name: 'holiday',
|
|
428
|
+
date: 123,
|
|
429
|
+
repeat: false,
|
|
430
|
+
working: true,
|
|
431
|
+
work_start: 10,
|
|
432
|
+
work_stop: 20,
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
});
|
|
436
|
+
expect(sentItem.timezone.id).toBe('tz-1');
|
|
437
|
+
expect(sentItem.timezone.offset).toBeUndefined();
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('strips startAt/endAt and defaults accepts/specials to [] when expires is false', async () => {
|
|
441
|
+
mockCalendarService.createCalendar.mockResolvedValueOnce({
|
|
442
|
+
data: {},
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
await CalendarsAPI.add({
|
|
446
|
+
itemInstance: {
|
|
447
|
+
name: 'No expiry',
|
|
448
|
+
timezone: {
|
|
449
|
+
id: 'tz-1',
|
|
450
|
+
},
|
|
451
|
+
expires: false,
|
|
452
|
+
startAt: 500,
|
|
453
|
+
endAt: 600,
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const sentItem = mockCalendarService.createCalendar.mock.calls[0][0];
|
|
458
|
+
|
|
459
|
+
expect(sentItem.start_at).toBeUndefined();
|
|
460
|
+
expect(sentItem.end_at).toBeUndefined();
|
|
461
|
+
expect(sentItem.accepts).toEqual([]);
|
|
462
|
+
expect(sentItem.specials).toEqual([]);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('does not throw when the item has no timezone', async () => {
|
|
466
|
+
mockCalendarService.createCalendar.mockResolvedValueOnce({
|
|
467
|
+
data: {},
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
await expect(
|
|
471
|
+
CalendarsAPI.add({
|
|
472
|
+
itemInstance: {
|
|
473
|
+
name: 'No timezone',
|
|
474
|
+
expires: false,
|
|
475
|
+
},
|
|
476
|
+
}),
|
|
477
|
+
).resolves.toBeDefined();
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('converts the created item back to camelCase', async () => {
|
|
481
|
+
mockCalendarService.createCalendar.mockResolvedValueOnce({
|
|
482
|
+
data: {
|
|
483
|
+
id: 'cal-1',
|
|
484
|
+
display_name: 'Created',
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
const result = await CalendarsAPI.add({
|
|
489
|
+
itemInstance: baseItem,
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
expect(result).toMatchObject({
|
|
493
|
+
id: 'cal-1',
|
|
494
|
+
displayName: 'Created',
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('maps wire schedule fields to UI start/end on create response', async () => {
|
|
499
|
+
mockCalendarService.createCalendar.mockResolvedValueOnce({
|
|
500
|
+
data: {
|
|
501
|
+
id: 'cal-1',
|
|
502
|
+
accepts: [
|
|
503
|
+
{
|
|
504
|
+
day: 0,
|
|
505
|
+
disabled: false,
|
|
506
|
+
start_time_of_day: 540,
|
|
507
|
+
end_time_of_day: 1200,
|
|
508
|
+
},
|
|
509
|
+
],
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
const result = await CalendarsAPI.add({
|
|
514
|
+
itemInstance: baseItem,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
expect(result.accepts).toEqual([
|
|
518
|
+
{
|
|
519
|
+
day: 0,
|
|
520
|
+
disabled: false,
|
|
521
|
+
start: 540,
|
|
522
|
+
end: 1200,
|
|
523
|
+
},
|
|
524
|
+
]);
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
describe('update', () => {
|
|
529
|
+
it('sends the sanitized payload to updateCalendar with a string id', async () => {
|
|
530
|
+
mockCalendarService.updateCalendar.mockResolvedValueOnce({
|
|
531
|
+
data: {},
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
await CalendarsAPI.update({
|
|
535
|
+
itemId: 7,
|
|
536
|
+
itemInstance: {
|
|
537
|
+
name: 'Updated Calendar',
|
|
538
|
+
timezone: {
|
|
539
|
+
id: 'tz-1',
|
|
540
|
+
},
|
|
541
|
+
expires: false,
|
|
542
|
+
},
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
expect(mockCalendarService.updateCalendar).toHaveBeenCalledTimes(1);
|
|
546
|
+
const [id, sentItem] = mockCalendarService.updateCalendar.mock.calls[0];
|
|
547
|
+
expect(id).toBe('7');
|
|
548
|
+
expect(sentItem.name).toBe('Updated Calendar');
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('converts the updated item back to camelCase', async () => {
|
|
552
|
+
mockCalendarService.updateCalendar.mockResolvedValueOnce({
|
|
553
|
+
data: {
|
|
554
|
+
updated_at: 123,
|
|
555
|
+
},
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const result = await CalendarsAPI.update({
|
|
559
|
+
itemId: 7,
|
|
560
|
+
itemInstance: {
|
|
561
|
+
name: 'Updated Calendar',
|
|
562
|
+
timezone: {
|
|
563
|
+
id: 'tz-1',
|
|
564
|
+
},
|
|
565
|
+
expires: false,
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
expect(result).toMatchObject({
|
|
570
|
+
updatedAt: 123,
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('maps wire schedule fields to UI start/end on update response', async () => {
|
|
575
|
+
mockCalendarService.updateCalendar.mockResolvedValueOnce({
|
|
576
|
+
data: {
|
|
577
|
+
id: '7',
|
|
578
|
+
accepts: [
|
|
579
|
+
{
|
|
580
|
+
day: 2,
|
|
581
|
+
disabled: false,
|
|
582
|
+
start_time_of_day: 600,
|
|
583
|
+
end_time_of_day: 1080,
|
|
584
|
+
},
|
|
585
|
+
],
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
const result = await CalendarsAPI.update({
|
|
590
|
+
itemId: 7,
|
|
591
|
+
itemInstance: {
|
|
592
|
+
name: 'Updated Calendar',
|
|
593
|
+
timezone: {
|
|
594
|
+
id: 'tz-1',
|
|
595
|
+
},
|
|
596
|
+
expires: false,
|
|
597
|
+
accepts: [
|
|
598
|
+
{
|
|
599
|
+
day: 2,
|
|
600
|
+
disabled: false,
|
|
601
|
+
start: 600,
|
|
602
|
+
end: 1080,
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
expect(result.accepts).toEqual([
|
|
609
|
+
{
|
|
610
|
+
day: 2,
|
|
611
|
+
disabled: false,
|
|
612
|
+
start: 600,
|
|
613
|
+
end: 1080,
|
|
614
|
+
},
|
|
615
|
+
]);
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
describe('delete', () => {
|
|
620
|
+
it('deletes the calendar by string id and returns the raw response data', async () => {
|
|
621
|
+
mockCalendarService.deleteCalendar.mockResolvedValueOnce({
|
|
622
|
+
data: {
|
|
623
|
+
deleted: true,
|
|
624
|
+
},
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
const result = await CalendarsAPI.delete({
|
|
628
|
+
id: 9,
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
expect(mockCalendarService.deleteCalendar).toHaveBeenCalledWith('9');
|
|
632
|
+
expect(result).toEqual({
|
|
633
|
+
deleted: true,
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
describe('getLookup', () => {
|
|
639
|
+
it('defaults fields to id/name when not provided', async () => {
|
|
640
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
641
|
+
data: {},
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
await CalendarsAPI.getLookup({});
|
|
645
|
+
|
|
646
|
+
expect(mockCalendarService.searchCalendar).toHaveBeenCalledWith(
|
|
647
|
+
expect.objectContaining({
|
|
648
|
+
fields: [
|
|
649
|
+
'id',
|
|
650
|
+
'name',
|
|
651
|
+
],
|
|
652
|
+
}),
|
|
653
|
+
);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it('respects explicitly provided fields', async () => {
|
|
657
|
+
mockCalendarService.searchCalendar.mockResolvedValueOnce({
|
|
658
|
+
data: {},
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
await CalendarsAPI.getLookup({
|
|
662
|
+
fields: [
|
|
663
|
+
'id',
|
|
664
|
+
'custom',
|
|
665
|
+
],
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
expect(mockCalendarService.searchCalendar).toHaveBeenCalledWith(
|
|
669
|
+
expect.objectContaining({
|
|
670
|
+
fields: [
|
|
671
|
+
'id',
|
|
672
|
+
'custom',
|
|
673
|
+
],
|
|
674
|
+
}),
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
describe('getTimezonesLookup', () => {
|
|
680
|
+
it('applies default pagination and search-to-wildcard transform', async () => {
|
|
681
|
+
mockCalendarService.searchTimezones.mockResolvedValueOnce({
|
|
682
|
+
data: {},
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
await CalendarsAPI.getTimezonesLookup({
|
|
686
|
+
search: 'euro',
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
expect(mockCalendarService.searchTimezones).toHaveBeenCalledWith({
|
|
690
|
+
page: 1,
|
|
691
|
+
size: 10,
|
|
692
|
+
q: 'euro*',
|
|
693
|
+
sort: undefined,
|
|
694
|
+
fields: undefined,
|
|
695
|
+
id: undefined,
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it('normalizes the response to camelCase items merged with list defaults', async () => {
|
|
700
|
+
mockCalendarService.searchTimezones.mockResolvedValueOnce({
|
|
701
|
+
data: {
|
|
702
|
+
items: [
|
|
703
|
+
{
|
|
704
|
+
time_zone_name: 'UTC',
|
|
705
|
+
},
|
|
706
|
+
],
|
|
707
|
+
next: false,
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
const result = await CalendarsAPI.getTimezonesLookup({});
|
|
712
|
+
|
|
713
|
+
expect(result).toEqual({
|
|
714
|
+
items: [
|
|
715
|
+
{
|
|
716
|
+
timeZoneName: 'UTC',
|
|
717
|
+
},
|
|
718
|
+
],
|
|
719
|
+
next: false,
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
describe('error handling', () => {
|
|
725
|
+
const cases = [
|
|
726
|
+
{
|
|
727
|
+
name: 'getList',
|
|
728
|
+
serviceFn: () => mockCalendarService.searchCalendar,
|
|
729
|
+
call: () => CalendarsAPI.getList({}),
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
name: 'get',
|
|
733
|
+
serviceFn: () => mockCalendarService.readCalendar,
|
|
734
|
+
call: () =>
|
|
735
|
+
CalendarsAPI.get({
|
|
736
|
+
itemId: 1,
|
|
737
|
+
}),
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
name: 'add',
|
|
741
|
+
serviceFn: () => mockCalendarService.createCalendar,
|
|
742
|
+
call: () =>
|
|
743
|
+
CalendarsAPI.add({
|
|
744
|
+
itemInstance: {
|
|
745
|
+
name: 'x',
|
|
746
|
+
timezone: {
|
|
747
|
+
id: 'tz-1',
|
|
748
|
+
},
|
|
749
|
+
expires: false,
|
|
750
|
+
},
|
|
751
|
+
}),
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
name: 'update',
|
|
755
|
+
serviceFn: () => mockCalendarService.updateCalendar,
|
|
756
|
+
call: () =>
|
|
757
|
+
CalendarsAPI.update({
|
|
758
|
+
itemId: 1,
|
|
759
|
+
itemInstance: {
|
|
760
|
+
name: 'x',
|
|
761
|
+
timezone: {
|
|
762
|
+
id: 'tz-1',
|
|
763
|
+
},
|
|
764
|
+
expires: false,
|
|
765
|
+
},
|
|
766
|
+
}),
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
name: 'delete',
|
|
770
|
+
serviceFn: () => mockCalendarService.deleteCalendar,
|
|
771
|
+
call: () =>
|
|
772
|
+
CalendarsAPI.delete({
|
|
773
|
+
id: 1,
|
|
774
|
+
}),
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: 'getTimezonesLookup',
|
|
778
|
+
serviceFn: () => mockCalendarService.searchTimezones,
|
|
779
|
+
call: () => CalendarsAPI.getTimezonesLookup({}),
|
|
780
|
+
},
|
|
781
|
+
];
|
|
782
|
+
|
|
783
|
+
it.each(
|
|
784
|
+
cases,
|
|
785
|
+
)('$name propagates the original error when the service call rejects', async ({
|
|
786
|
+
serviceFn,
|
|
787
|
+
call,
|
|
788
|
+
}) => {
|
|
789
|
+
const error = new Error('service failure');
|
|
790
|
+
serviceFn().mockRejectedValueOnce(error);
|
|
791
|
+
|
|
792
|
+
await expect(call()).rejects.toBe(error);
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
});
|