@webitel/api-services 0.1.61 → 0.1.63
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/_shared/i18nIssue.ts +18 -0
- package/src/validations/calendar/__tests__/calendar.validations.spec.ts +612 -0
- package/src/validations/calendar/calendar.validations.ts +214 -0
- package/src/validations/index.ts +1 -0
- package/types/.tsbuildinfo +1 -1
- package/types/api/clients/calendars/calendars.d.ts +7 -0
- package/types/validations/_shared/i18nIssue.d.ts +18 -0
- package/types/validations/calendar/calendar.validations.d.ts +33 -0
- package/types/validations/index.d.ts +1 -0
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
calendarSchema,
|
|
5
|
+
getCalendarDayRangeIssues,
|
|
6
|
+
} from '../calendar.validations';
|
|
7
|
+
|
|
8
|
+
/** custom rules carry their message key in params, see `i18nIssue` */
|
|
9
|
+
const issueKey = (issue: z.core.$ZodIssue) =>
|
|
10
|
+
issue.code === 'custom' ? issue.params?.i18nKey : undefined;
|
|
11
|
+
|
|
12
|
+
const validTimezone = {
|
|
13
|
+
id: 'tz-1',
|
|
14
|
+
name: 'UTC',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const minimalValidInput = {
|
|
18
|
+
name: 'Support hours',
|
|
19
|
+
timezone: validTimezone,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('calendarSchema', () => {
|
|
23
|
+
describe('defaults', () => {
|
|
24
|
+
it('fills in description, expires, and excepts when omitted', () => {
|
|
25
|
+
const result = calendarSchema.parse(minimalValidInput);
|
|
26
|
+
|
|
27
|
+
expect(result.description).toBe('');
|
|
28
|
+
expect(result.expires).toBe(false);
|
|
29
|
+
expect(result.excepts).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('generates 7 default accepts entries (9:00-20:00, enabled) when omitted', () => {
|
|
33
|
+
const result = calendarSchema.parse(minimalValidInput);
|
|
34
|
+
|
|
35
|
+
expect(result.accepts).toHaveLength(7);
|
|
36
|
+
result.accepts.forEach((accept, day) => {
|
|
37
|
+
expect(accept).toEqual({
|
|
38
|
+
day,
|
|
39
|
+
disabled: false,
|
|
40
|
+
start: 9 * 60,
|
|
41
|
+
end: 20 * 60,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('generates 7 default specials entries (9:00-20:00, disabled) when omitted', () => {
|
|
47
|
+
const result = calendarSchema.parse(minimalValidInput);
|
|
48
|
+
|
|
49
|
+
expect(result.specials).toHaveLength(7);
|
|
50
|
+
result.specials.forEach((special, day) => {
|
|
51
|
+
expect(special).toEqual({
|
|
52
|
+
day,
|
|
53
|
+
disabled: true,
|
|
54
|
+
start: 9 * 60,
|
|
55
|
+
end: 20 * 60,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('preserves an explicitly provided description instead of defaulting', () => {
|
|
61
|
+
const result = calendarSchema.parse({
|
|
62
|
+
...minimalValidInput,
|
|
63
|
+
description: 'custom description',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(result.description).toBe('custom description');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('preserves an explicitly provided expires flag', () => {
|
|
70
|
+
const result = calendarSchema.parse({
|
|
71
|
+
...minimalValidInput,
|
|
72
|
+
expires: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(result.expires).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('produces fresh default accepts/specials across independent parses', () => {
|
|
79
|
+
const first = calendarSchema.parse(minimalValidInput);
|
|
80
|
+
const second = calendarSchema.parse(minimalValidInput);
|
|
81
|
+
|
|
82
|
+
expect(first.accepts).toEqual(second.accepts);
|
|
83
|
+
expect(first.specials).toEqual(second.specials);
|
|
84
|
+
expect(first.accepts).not.toBe(second.accepts);
|
|
85
|
+
expect(first.accepts?.[0]).not.toBe(second.accepts?.[0]);
|
|
86
|
+
expect(first.specials).not.toBe(second.specials);
|
|
87
|
+
expect(first.specials?.[0]).not.toBe(second.specials?.[0]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('name', () => {
|
|
92
|
+
it('rejects a missing name', () => {
|
|
93
|
+
const result = calendarSchema.safeParse({
|
|
94
|
+
timezone: validTimezone,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(result.success).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('rejects an empty name', () => {
|
|
101
|
+
const result = calendarSchema.safeParse({
|
|
102
|
+
...minimalValidInput,
|
|
103
|
+
name: '',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result.success).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('accepts a non-empty name', () => {
|
|
110
|
+
const result = calendarSchema.safeParse(minimalValidInput);
|
|
111
|
+
|
|
112
|
+
expect(result.success).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('timezone', () => {
|
|
117
|
+
it('rejects a missing timezone', () => {
|
|
118
|
+
const result = calendarSchema.safeParse({
|
|
119
|
+
name: 'Support hours',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result.success).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('rejects a timezone with an empty id', () => {
|
|
126
|
+
const result = calendarSchema.safeParse({
|
|
127
|
+
...minimalValidInput,
|
|
128
|
+
timezone: {
|
|
129
|
+
id: '',
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(result.success).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('rejects a timezone without id', () => {
|
|
137
|
+
const result = calendarSchema.safeParse({
|
|
138
|
+
...minimalValidInput,
|
|
139
|
+
timezone: {
|
|
140
|
+
name: 'UTC',
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(result.success).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('accepts a timezone with just a valid id', () => {
|
|
148
|
+
const result = calendarSchema.safeParse({
|
|
149
|
+
...minimalValidInput,
|
|
150
|
+
timezone: {
|
|
151
|
+
id: 'tz-2',
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(result.success).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('accepts / specials override', () => {
|
|
160
|
+
it('accepts a custom accepts array and defaults missing `disabled` to false', () => {
|
|
161
|
+
const result = calendarSchema.parse({
|
|
162
|
+
...minimalValidInput,
|
|
163
|
+
accepts: [
|
|
164
|
+
{
|
|
165
|
+
day: 0,
|
|
166
|
+
start: 0,
|
|
167
|
+
end: 100,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(result.accepts).toEqual([
|
|
173
|
+
{
|
|
174
|
+
day: 0,
|
|
175
|
+
disabled: false,
|
|
176
|
+
start: 0,
|
|
177
|
+
end: 100,
|
|
178
|
+
},
|
|
179
|
+
]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('accepts a custom specials array with explicit disabled values', () => {
|
|
183
|
+
const result = calendarSchema.parse({
|
|
184
|
+
...minimalValidInput,
|
|
185
|
+
specials: [
|
|
186
|
+
{
|
|
187
|
+
day: 3,
|
|
188
|
+
disabled: false,
|
|
189
|
+
start: 60,
|
|
190
|
+
end: 120,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(result.specials).toEqual([
|
|
196
|
+
{
|
|
197
|
+
day: 3,
|
|
198
|
+
disabled: false,
|
|
199
|
+
start: 60,
|
|
200
|
+
end: 120,
|
|
201
|
+
},
|
|
202
|
+
]);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('rejects an accept entry with a non-numeric day', () => {
|
|
206
|
+
const result = calendarSchema.safeParse({
|
|
207
|
+
...minimalValidInput,
|
|
208
|
+
accepts: [
|
|
209
|
+
{
|
|
210
|
+
day: 'monday',
|
|
211
|
+
start: 0,
|
|
212
|
+
end: 100,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(result.success).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('rejects an accept entry missing required start/end', () => {
|
|
221
|
+
const result = calendarSchema.safeParse({
|
|
222
|
+
...minimalValidInput,
|
|
223
|
+
accepts: [
|
|
224
|
+
{
|
|
225
|
+
day: 0,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
expect(result.success).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('rejects an accept entry with day outside 0-6', () => {
|
|
234
|
+
const result = calendarSchema.safeParse({
|
|
235
|
+
...minimalValidInput,
|
|
236
|
+
accepts: [
|
|
237
|
+
{
|
|
238
|
+
day: 7,
|
|
239
|
+
start: 0,
|
|
240
|
+
end: 100,
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(result.success).toBe(false);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('rejects an accept entry with start equal to end', () => {
|
|
249
|
+
const result = calendarSchema.safeParse({
|
|
250
|
+
...minimalValidInput,
|
|
251
|
+
accepts: [
|
|
252
|
+
{
|
|
253
|
+
day: 0,
|
|
254
|
+
disabled: false,
|
|
255
|
+
start: 600,
|
|
256
|
+
end: 600,
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
expect(result.success).toBe(false);
|
|
262
|
+
if (!result.success) {
|
|
263
|
+
expect(
|
|
264
|
+
result.error.issues.some(
|
|
265
|
+
(issue) =>
|
|
266
|
+
issueKey(issue) === 'timerangeStartLessThanEnd' &&
|
|
267
|
+
issue.path.join('.') === 'accepts.0.start',
|
|
268
|
+
),
|
|
269
|
+
).toBe(true);
|
|
270
|
+
expect(
|
|
271
|
+
result.error.issues.some(
|
|
272
|
+
(issue) =>
|
|
273
|
+
issueKey(issue) === 'timerangeStartLessThanEnd' &&
|
|
274
|
+
issue.path.join('.') === 'accepts.0.end',
|
|
275
|
+
),
|
|
276
|
+
).toBe(true);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('rejects an accept entry with start greater than end', () => {
|
|
281
|
+
const result = calendarSchema.safeParse({
|
|
282
|
+
...minimalValidInput,
|
|
283
|
+
accepts: [
|
|
284
|
+
{
|
|
285
|
+
day: 0,
|
|
286
|
+
disabled: false,
|
|
287
|
+
start: 600,
|
|
288
|
+
end: 540,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
expect(result.success).toBe(false);
|
|
294
|
+
if (!result.success) {
|
|
295
|
+
expect(
|
|
296
|
+
result.error.issues.some(
|
|
297
|
+
(issue) =>
|
|
298
|
+
issueKey(issue) === 'timerangeStartLessThanEnd' &&
|
|
299
|
+
issue.path.join('.') === 'accepts.0.start',
|
|
300
|
+
),
|
|
301
|
+
).toBe(true);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('rejects overlapping accept intervals on the same day', () => {
|
|
306
|
+
const result = calendarSchema.safeParse({
|
|
307
|
+
...minimalValidInput,
|
|
308
|
+
accepts: [
|
|
309
|
+
{
|
|
310
|
+
day: 1,
|
|
311
|
+
disabled: false,
|
|
312
|
+
start: 540,
|
|
313
|
+
end: 720,
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
day: 1,
|
|
317
|
+
disabled: false,
|
|
318
|
+
start: 600,
|
|
319
|
+
end: 780,
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
expect(result.success).toBe(false);
|
|
325
|
+
if (!result.success) {
|
|
326
|
+
const intersectIssues = result.error.issues.filter(
|
|
327
|
+
(issue) => issueKey(issue) === 'timerangeNotIntersect',
|
|
328
|
+
);
|
|
329
|
+
expect(intersectIssues.length).toBeGreaterThan(0);
|
|
330
|
+
expect(
|
|
331
|
+
intersectIssues.some(
|
|
332
|
+
(issue) => issue.path.join('.') === 'accepts.0.start',
|
|
333
|
+
),
|
|
334
|
+
).toBe(true);
|
|
335
|
+
expect(
|
|
336
|
+
intersectIssues.some(
|
|
337
|
+
(issue) => issue.path.join('.') === 'accepts.0.end',
|
|
338
|
+
),
|
|
339
|
+
).toBe(true);
|
|
340
|
+
expect(
|
|
341
|
+
intersectIssues.some(
|
|
342
|
+
(issue) => issue.path.join('.') === 'accepts.1.start',
|
|
343
|
+
),
|
|
344
|
+
).toBe(true);
|
|
345
|
+
expect(
|
|
346
|
+
intersectIssues.some(
|
|
347
|
+
(issue) => issue.path.join('.') === 'accepts.1.end',
|
|
348
|
+
),
|
|
349
|
+
).toBe(true);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('rejects overlapping accept intervals on the same day regardless of array order', () => {
|
|
354
|
+
const result = calendarSchema.safeParse({
|
|
355
|
+
...minimalValidInput,
|
|
356
|
+
accepts: [
|
|
357
|
+
{
|
|
358
|
+
day: 0,
|
|
359
|
+
disabled: false,
|
|
360
|
+
start: 540,
|
|
361
|
+
end: 600,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
day: 1,
|
|
365
|
+
disabled: false,
|
|
366
|
+
start: 540,
|
|
367
|
+
end: 600,
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
day: 0,
|
|
371
|
+
disabled: false,
|
|
372
|
+
start: 550,
|
|
373
|
+
end: 650,
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
expect(result.success).toBe(false);
|
|
379
|
+
if (!result.success) {
|
|
380
|
+
expect(
|
|
381
|
+
result.error.issues.some(
|
|
382
|
+
(issue) => issueKey(issue) === 'timerangeNotIntersect',
|
|
383
|
+
),
|
|
384
|
+
).toBe(true);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('accepts non-overlapping accept intervals on the same day', () => {
|
|
389
|
+
const result = calendarSchema.safeParse({
|
|
390
|
+
...minimalValidInput,
|
|
391
|
+
accepts: [
|
|
392
|
+
{
|
|
393
|
+
day: 1,
|
|
394
|
+
disabled: false,
|
|
395
|
+
start: 540,
|
|
396
|
+
end: 600,
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
day: 1,
|
|
400
|
+
disabled: false,
|
|
401
|
+
start: 660,
|
|
402
|
+
end: 720,
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
expect(result.success).toBe(true);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('does not treat accept intervals on different days as overlapping', () => {
|
|
411
|
+
const result = calendarSchema.safeParse({
|
|
412
|
+
...minimalValidInput,
|
|
413
|
+
accepts: [
|
|
414
|
+
{
|
|
415
|
+
day: 0,
|
|
416
|
+
disabled: false,
|
|
417
|
+
start: 540,
|
|
418
|
+
end: 720,
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
day: 2,
|
|
422
|
+
disabled: false,
|
|
423
|
+
start: 540,
|
|
424
|
+
end: 720,
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(result.success).toBe(true);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('rejects accept start/end minutes outside 0-1439', () => {
|
|
433
|
+
const result = calendarSchema.safeParse({
|
|
434
|
+
...minimalValidInput,
|
|
435
|
+
accepts: [
|
|
436
|
+
{
|
|
437
|
+
day: 0,
|
|
438
|
+
disabled: false,
|
|
439
|
+
start: 0,
|
|
440
|
+
end: 24 * 60,
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
expect(result.success).toBe(false);
|
|
446
|
+
if (!result.success) {
|
|
447
|
+
expect(
|
|
448
|
+
result.error.issues.some(
|
|
449
|
+
(issue) =>
|
|
450
|
+
issueKey(issue) === 'hourRange' &&
|
|
451
|
+
issue.path.join('.') === 'accepts.0.end',
|
|
452
|
+
),
|
|
453
|
+
).toBe(true);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe('excepts', () => {
|
|
459
|
+
it('accepts valid except objects', () => {
|
|
460
|
+
const excepts = [
|
|
461
|
+
{
|
|
462
|
+
name: 'holiday',
|
|
463
|
+
date: 123,
|
|
464
|
+
working: true,
|
|
465
|
+
workStart: 0,
|
|
466
|
+
workStop: 9 * 60,
|
|
467
|
+
},
|
|
468
|
+
];
|
|
469
|
+
|
|
470
|
+
const result = calendarSchema.parse({
|
|
471
|
+
...minimalValidInput,
|
|
472
|
+
excepts,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
expect(result.excepts).toEqual(excepts);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('rejects invalid except array entries', () => {
|
|
479
|
+
const result = calendarSchema.safeParse({
|
|
480
|
+
...minimalValidInput,
|
|
481
|
+
excepts: [
|
|
482
|
+
null,
|
|
483
|
+
],
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
expect(result.success).toBe(false);
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
describe('startAt / endAt', () => {
|
|
491
|
+
it('is optional and accepts any value type', () => {
|
|
492
|
+
const result = calendarSchema.parse({
|
|
493
|
+
...minimalValidInput,
|
|
494
|
+
startAt: 1234567890,
|
|
495
|
+
endAt: '2024-01-01',
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
expect(result.startAt).toBe(1234567890);
|
|
499
|
+
expect(result.endAt).toBe('2024-01-01');
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('does not throw when omitted', () => {
|
|
503
|
+
const result = calendarSchema.safeParse(minimalValidInput);
|
|
504
|
+
|
|
505
|
+
expect(result.success).toBe(true);
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
describe('negative cases', () => {
|
|
510
|
+
it('collects issues for multiple missing required fields at once', () => {
|
|
511
|
+
const result = calendarSchema.safeParse({});
|
|
512
|
+
|
|
513
|
+
expect(result.success).toBe(false);
|
|
514
|
+
if (!result.success) {
|
|
515
|
+
const paths = result.error.issues.map((issue) => issue.path[0]);
|
|
516
|
+
expect(paths).toEqual(
|
|
517
|
+
expect.arrayContaining([
|
|
518
|
+
'name',
|
|
519
|
+
'timezone',
|
|
520
|
+
]),
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
describe('validations barrel export', () => {
|
|
528
|
+
it('re-exports calendarSchema from the validations index', async () => {
|
|
529
|
+
const index = await import('../../index');
|
|
530
|
+
|
|
531
|
+
expect(index.calendarSchema).toBe(calendarSchema);
|
|
532
|
+
expect(typeof index.calendarSchema.parse).toBe('function');
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
describe('getCalendarDayRangeIssues', () => {
|
|
537
|
+
const row = (day: number, start: number, end: number) => ({
|
|
538
|
+
day,
|
|
539
|
+
disabled: false,
|
|
540
|
+
start,
|
|
541
|
+
end,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('reports nothing for valid rows', () => {
|
|
545
|
+
expect(
|
|
546
|
+
getCalendarDayRangeIssues([
|
|
547
|
+
row(0, 540, 541),
|
|
548
|
+
row(0, 600, 660),
|
|
549
|
+
row(1, 540, 1200),
|
|
550
|
+
]),
|
|
551
|
+
).toEqual([]);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('reports both ends of a row whose start is not before its end', () => {
|
|
555
|
+
expect(
|
|
556
|
+
getCalendarDayRangeIssues([
|
|
557
|
+
row(0, 600, 540),
|
|
558
|
+
]),
|
|
559
|
+
).toEqual([
|
|
560
|
+
{
|
|
561
|
+
index: 0,
|
|
562
|
+
prop: 'start',
|
|
563
|
+
key: 'timerangeStartLessThanEnd',
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
index: 0,
|
|
567
|
+
prop: 'end',
|
|
568
|
+
key: 'timerangeStartLessThanEnd',
|
|
569
|
+
},
|
|
570
|
+
]);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it('reports every row of an overlap on the same day', () => {
|
|
574
|
+
const issues = getCalendarDayRangeIssues([
|
|
575
|
+
row(0, 540, 720),
|
|
576
|
+
row(0, 600, 780),
|
|
577
|
+
]);
|
|
578
|
+
|
|
579
|
+
expect(issues.every(({ key }) => key === 'timerangeNotIntersect')).toBe(
|
|
580
|
+
true,
|
|
581
|
+
);
|
|
582
|
+
expect(issues.map(({ index, prop }) => `${index}.${prop}`).sort()).toEqual([
|
|
583
|
+
'0.end',
|
|
584
|
+
'0.start',
|
|
585
|
+
'1.end',
|
|
586
|
+
'1.start',
|
|
587
|
+
]);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('does not treat the same hours on different days as an overlap', () => {
|
|
591
|
+
expect(
|
|
592
|
+
getCalendarDayRangeIssues([
|
|
593
|
+
row(0, 540, 720),
|
|
594
|
+
row(2, 540, 720),
|
|
595
|
+
]),
|
|
596
|
+
).toEqual([]);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('reports a minute outside the day', () => {
|
|
600
|
+
expect(
|
|
601
|
+
getCalendarDayRangeIssues([
|
|
602
|
+
row(0, 0, 24 * 60),
|
|
603
|
+
]),
|
|
604
|
+
).toEqual([
|
|
605
|
+
{
|
|
606
|
+
index: 0,
|
|
607
|
+
prop: 'end',
|
|
608
|
+
key: 'hourRange',
|
|
609
|
+
},
|
|
610
|
+
]);
|
|
611
|
+
});
|
|
612
|
+
});
|