]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { fakeAsync, tick } from '@angular/core/testing'; |
2 | import { FormControl, Validators } from '@angular/forms'; | |
3 | ||
4 | import { of as observableOf } from 'rxjs'; | |
5 | ||
6 | import { FormHelper } from '../../../testing/unit-test-helper'; | |
7 | import { CdFormGroup } from './cd-form-group'; | |
8 | import { CdValidators } from './cd-validators'; | |
9 | ||
10 | describe('CdValidators', () => { | |
11 | let formHelper: FormHelper; | |
12 | let form: CdFormGroup; | |
13 | ||
9f95a23c TL |
14 | const expectValid = (value: any) => formHelper.expectValidChange('x', value); |
15 | const expectPatternError = (value: any) => formHelper.expectErrorChange('x', value, 'pattern'); | |
16 | const updateValidity = (controlName: string) => form.get(controlName).updateValueAndValidity(); | |
11fdf7f2 TL |
17 | |
18 | beforeEach(() => { | |
19 | form = new CdFormGroup({ | |
20 | x: new FormControl() | |
21 | }); | |
22 | formHelper = new FormHelper(form); | |
23 | }); | |
24 | ||
25 | describe('email', () => { | |
26 | beforeEach(() => { | |
27 | form.get('x').setValidators(CdValidators.email); | |
28 | }); | |
29 | ||
30 | it('should not error on an empty email address', () => { | |
31 | expectValid(''); | |
32 | }); | |
33 | ||
34 | it('should not error on valid email address', () => { | |
35 | expectValid('dashboard@ceph.com'); | |
36 | }); | |
37 | ||
38 | it('should error on invalid email address', () => { | |
39 | formHelper.expectErrorChange('x', 'xyz', 'email'); | |
40 | }); | |
41 | }); | |
42 | ||
43 | describe('ip validator', () => { | |
44 | describe('IPv4', () => { | |
45 | beforeEach(() => { | |
46 | form.get('x').setValidators(CdValidators.ip(4)); | |
47 | }); | |
48 | ||
49 | it('should not error on empty addresses', () => { | |
50 | expectValid(''); | |
51 | }); | |
52 | ||
53 | it('should accept valid address', () => { | |
54 | expectValid('19.117.23.141'); | |
55 | }); | |
56 | ||
57 | it('should error containing whitespace', () => { | |
58 | expectPatternError('155.144.133.122 '); | |
59 | expectPatternError('155. 144.133 .122'); | |
60 | expectPatternError(' 155.144.133.122'); | |
61 | }); | |
62 | ||
63 | it('should error containing invalid char', () => { | |
64 | expectPatternError('155.144.eee.122 '); | |
65 | expectPatternError('155.1?.133 .1&2'); | |
66 | }); | |
67 | ||
68 | it('should error containing blocks higher than 255', () => { | |
69 | expectPatternError('155.270.133.122'); | |
70 | expectPatternError('155.144.133.290'); | |
71 | }); | |
72 | }); | |
73 | ||
74 | describe('IPv4', () => { | |
75 | beforeEach(() => { | |
76 | form.get('x').setValidators(CdValidators.ip(6)); | |
77 | }); | |
78 | ||
79 | it('should not error on empty IPv6 addresses', () => { | |
80 | expectValid(''); | |
81 | }); | |
82 | ||
83 | it('should accept valid IPv6 address', () => { | |
84 | expectValid('c4dc:1475:cb0b:24ed:3c80:468b:70cd:1a95'); | |
85 | }); | |
86 | ||
87 | it('should error on IPv6 address containing too many blocks', () => { | |
88 | formHelper.expectErrorChange( | |
89 | 'x', | |
90 | 'c4dc:14753:cb0b:24ed:3c80:468b:70cd:1a95:a3f3', | |
91 | 'pattern' | |
92 | ); | |
93 | }); | |
94 | ||
95 | it('should error on IPv6 address containing more than 4 digits per block', () => { | |
96 | expectPatternError('c4dc:14753:cb0b:24ed:3c80:468b:70cd:1a95'); | |
97 | }); | |
98 | ||
99 | it('should error on IPv6 address containing whitespace', () => { | |
100 | expectPatternError('c4dc:14753:cb0b:24ed:3c80:468b:70cd:1a95 '); | |
101 | expectPatternError('c4dc:14753 :cb0b:24ed:3c80 :468b:70cd :1a95'); | |
102 | expectPatternError(' c4dc:14753:cb0b:24ed:3c80:468b:70cd:1a95'); | |
103 | }); | |
104 | ||
105 | it('should error on IPv6 address containing invalid char', () => { | |
106 | expectPatternError('c4dx:14753:cb0b:24ed:3c80:468b:70cd:1a95'); | |
107 | expectPatternError('c4da:14753:cb0b:24ed:3$80:468b:70cd:1a95'); | |
108 | }); | |
109 | }); | |
110 | ||
111 | it('should accept valid IPv4/6 addresses if not protocol version is given', () => { | |
112 | const x = form.get('x'); | |
113 | x.setValidators(CdValidators.ip()); | |
114 | expectValid('19.117.23.141'); | |
115 | expectValid('c4dc:1475:cb0b:24ed:3c80:468b:70cd:1a95'); | |
116 | }); | |
117 | }); | |
118 | ||
119 | describe('uuid validator', () => { | |
9f95a23c | 120 | const expectUuidError = (value: string) => |
11fdf7f2 TL |
121 | formHelper.expectErrorChange('x', value, 'invalidUuid', true); |
122 | beforeEach(() => { | |
123 | form.get('x').setValidators(CdValidators.uuid()); | |
124 | }); | |
125 | ||
126 | it('should accept empty value', () => { | |
127 | expectValid(''); | |
128 | }); | |
129 | ||
130 | it('should accept valid version 1 uuid', () => { | |
131 | expectValid('171af0b2-c305-11e8-a355-529269fb1459'); | |
132 | }); | |
133 | ||
134 | it('should accept valid version 4 uuid', () => { | |
135 | expectValid('e33bbcb6-fcc3-40b1-ae81-3f81706a35d5'); | |
136 | }); | |
137 | ||
138 | it('should error on uuid containing too many blocks', () => { | |
139 | expectUuidError('e33bbcb6-fcc3-40b1-ae81-3f81706a35d5-23d3'); | |
140 | }); | |
141 | ||
142 | it('should error on uuid containing too many chars in block', () => { | |
143 | expectUuidError('aae33bbcb6-fcc3-40b1-ae81-3f81706a35d5'); | |
144 | }); | |
145 | ||
146 | it('should error on uuid containing invalid char', () => { | |
147 | expectUuidError('x33bbcb6-fcc3-40b1-ae81-3f81706a35d5'); | |
148 | expectUuidError('$33bbcb6-fcc3-40b1-ae81-3f81706a35d5'); | |
149 | }); | |
150 | }); | |
151 | ||
152 | describe('number validator', () => { | |
153 | beforeEach(() => { | |
154 | form.get('x').setValidators(CdValidators.number()); | |
155 | }); | |
156 | ||
157 | it('should accept empty value', () => { | |
158 | expectValid(''); | |
159 | }); | |
160 | ||
161 | it('should accept numbers', () => { | |
162 | expectValid(42); | |
163 | expectValid(-42); | |
164 | expectValid('42'); | |
165 | }); | |
166 | ||
167 | it('should error on decimal numbers', () => { | |
168 | expectPatternError(42.3); | |
169 | expectPatternError(-42.3); | |
170 | expectPatternError('42.3'); | |
171 | }); | |
172 | ||
173 | it('should error on chars', () => { | |
174 | expectPatternError('char'); | |
175 | expectPatternError('42char'); | |
176 | }); | |
177 | ||
178 | it('should error on whitespaces', () => { | |
179 | expectPatternError('42 '); | |
180 | expectPatternError('4 2'); | |
181 | }); | |
182 | }); | |
183 | ||
184 | describe('number validator (without negative values)', () => { | |
185 | beforeEach(() => { | |
186 | form.get('x').setValidators(CdValidators.number(false)); | |
187 | }); | |
188 | ||
189 | it('should accept positive numbers', () => { | |
190 | expectValid(42); | |
191 | expectValid('42'); | |
192 | }); | |
193 | ||
194 | it('should error on negative numbers', () => { | |
195 | expectPatternError(-42); | |
196 | expectPatternError('-42'); | |
197 | }); | |
198 | }); | |
199 | ||
200 | describe('decimal number validator', () => { | |
201 | beforeEach(() => { | |
202 | form.get('x').setValidators(CdValidators.decimalNumber()); | |
203 | }); | |
204 | ||
205 | it('should accept empty value', () => { | |
206 | expectValid(''); | |
207 | }); | |
208 | ||
209 | it('should accept numbers and decimal numbers', () => { | |
210 | expectValid(42); | |
211 | expectValid(-42); | |
212 | expectValid(42.3); | |
213 | expectValid(-42.3); | |
214 | expectValid('42'); | |
215 | expectValid('42.3'); | |
216 | }); | |
217 | ||
218 | it('should error on chars', () => { | |
219 | expectPatternError('42e'); | |
220 | expectPatternError('e42.3'); | |
221 | }); | |
222 | ||
223 | it('should error on whitespaces', () => { | |
224 | expectPatternError('42.3 '); | |
225 | expectPatternError('42 .3'); | |
226 | }); | |
227 | }); | |
228 | ||
229 | describe('decimal number validator (without negative values)', () => { | |
230 | beforeEach(() => { | |
231 | form.get('x').setValidators(CdValidators.decimalNumber(false)); | |
232 | }); | |
233 | ||
234 | it('should accept positive numbers and decimals', () => { | |
235 | expectValid(42); | |
236 | expectValid(42.3); | |
237 | expectValid('42'); | |
238 | expectValid('42.3'); | |
239 | }); | |
240 | ||
241 | it('should error on negative numbers and decimals', () => { | |
242 | expectPatternError(-42); | |
243 | expectPatternError('-42'); | |
244 | expectPatternError(-42.3); | |
245 | expectPatternError('-42.3'); | |
246 | }); | |
247 | }); | |
248 | ||
249 | describe('requiredIf', () => { | |
250 | beforeEach(() => { | |
251 | form = new CdFormGroup({ | |
252 | x: new FormControl(true), | |
253 | y: new FormControl('abc'), | |
254 | z: new FormControl('') | |
255 | }); | |
256 | formHelper = new FormHelper(form); | |
257 | }); | |
258 | ||
259 | it('should not error because all conditions are fulfilled', () => { | |
260 | formHelper.setValue('z', 'zyx'); | |
261 | const validatorFn = CdValidators.requiredIf({ | |
262 | x: true, | |
263 | y: 'abc' | |
264 | }); | |
265 | expect(validatorFn(form.get('z'))).toBeNull(); | |
266 | }); | |
267 | ||
268 | it('should not error because of unmet prerequisites', () => { | |
269 | // Define prereqs that do not match the current values of the form fields. | |
270 | const validatorFn = CdValidators.requiredIf({ | |
271 | x: false, | |
272 | y: 'xyz' | |
273 | }); | |
274 | // The validator must succeed because the prereqs do not match, so the | |
275 | // validation of the 'z' control will be skipped. | |
276 | expect(validatorFn(form.get('z'))).toBeNull(); | |
277 | }); | |
278 | ||
279 | it('should error because of an empty value', () => { | |
280 | // Define prereqs that force the validator to validate the value of | |
281 | // the 'z' control. | |
282 | const validatorFn = CdValidators.requiredIf({ | |
283 | x: true, | |
284 | y: 'abc' | |
285 | }); | |
286 | // The validator must fail because the value of control 'z' is empty. | |
287 | expect(validatorFn(form.get('z'))).toEqual({ required: true }); | |
288 | }); | |
289 | ||
290 | it('should not error because of unsuccessful condition', () => { | |
291 | formHelper.setValue('z', 'zyx'); | |
292 | // Define prereqs that force the validator to validate the value of | |
293 | // the 'z' control. | |
294 | const validatorFn = CdValidators.requiredIf( | |
295 | { | |
296 | x: true, | |
297 | z: 'zyx' | |
298 | }, | |
299 | () => false | |
300 | ); | |
301 | expect(validatorFn(form.get('z'))).toBeNull(); | |
302 | }); | |
303 | ||
304 | it('should error because of successful condition', () => { | |
9f95a23c | 305 | const conditionFn = (value: string) => { |
11fdf7f2 TL |
306 | return value === 'abc'; |
307 | }; | |
308 | // Define prereqs that force the validator to validate the value of | |
309 | // the 'y' control. | |
310 | const validatorFn = CdValidators.requiredIf( | |
311 | { | |
312 | x: true, | |
313 | z: '' | |
314 | }, | |
315 | conditionFn | |
316 | ); | |
317 | expect(validatorFn(form.get('y'))).toEqual({ required: true }); | |
318 | }); | |
319 | }); | |
320 | ||
321 | describe('custom validation', () => { | |
322 | beforeEach(() => { | |
323 | form = new CdFormGroup({ | |
801d1391 TL |
324 | x: new FormControl( |
325 | 3, | |
326 | CdValidators.custom('odd', (x: number) => x % 2 === 1) | |
327 | ), | |
11fdf7f2 TL |
328 | y: new FormControl( |
329 | 5, | |
9f95a23c | 330 | CdValidators.custom('not-dividable-by-x', (y: number) => { |
11fdf7f2 TL |
331 | const x = (form && form.get('x').value) || 1; |
332 | return y % x !== 0; | |
333 | }) | |
334 | ) | |
335 | }); | |
336 | formHelper = new FormHelper(form); | |
337 | }); | |
338 | ||
339 | it('should test error and valid condition for odd x', () => { | |
340 | formHelper.expectError('x', 'odd'); | |
341 | expectValid(4); | |
342 | }); | |
343 | ||
344 | it('should test error and valid condition for y if its dividable by x', () => { | |
345 | updateValidity('y'); | |
346 | formHelper.expectError('y', 'not-dividable-by-x'); | |
347 | formHelper.expectValidChange('y', 6); | |
348 | }); | |
349 | }); | |
350 | ||
351 | describe('validate if condition', () => { | |
352 | beforeEach(() => { | |
353 | form = new CdFormGroup({ | |
354 | x: new FormControl(3), | |
355 | y: new FormControl(5) | |
356 | }); | |
357 | CdValidators.validateIf(form.get('x'), () => ((form && form.get('y').value) || 0) > 10, [ | |
9f95a23c TL |
358 | CdValidators.custom('min', (x: number) => x < 7), |
359 | CdValidators.custom('max', (x: number) => x > 12) | |
11fdf7f2 TL |
360 | ]); |
361 | formHelper = new FormHelper(form); | |
362 | }); | |
363 | ||
364 | it('should test min error', () => { | |
365 | formHelper.setValue('y', 11); | |
366 | updateValidity('x'); | |
367 | formHelper.expectError('x', 'min'); | |
368 | }); | |
369 | ||
370 | it('should test max error', () => { | |
371 | formHelper.setValue('y', 11); | |
372 | formHelper.setValue('x', 13); | |
373 | formHelper.expectError('x', 'max'); | |
374 | }); | |
375 | ||
376 | it('should test valid number with validation', () => { | |
377 | formHelper.setValue('y', 11); | |
378 | formHelper.setValue('x', 12); | |
379 | formHelper.expectValid('x'); | |
380 | }); | |
381 | ||
382 | it('should validate automatically if dependency controls are defined', () => { | |
383 | CdValidators.validateIf( | |
384 | form.get('x'), | |
385 | () => ((form && form.getValue('y')) || 0) > 10, | |
386 | [Validators.min(7), Validators.max(12)], | |
387 | undefined, | |
388 | [form.get('y')] | |
389 | ); | |
390 | ||
391 | formHelper.expectValid('x'); | |
392 | formHelper.setValue('y', 13); | |
393 | formHelper.expectError('x', 'min'); | |
394 | }); | |
395 | ||
396 | it('should always validate the permanentValidators', () => { | |
397 | CdValidators.validateIf( | |
398 | form.get('x'), | |
399 | () => ((form && form.getValue('y')) || 0) > 10, | |
400 | [Validators.min(7), Validators.max(12)], | |
401 | [Validators.required], | |
402 | [form.get('y')] | |
403 | ); | |
404 | ||
405 | formHelper.expectValid('x'); | |
406 | formHelper.setValue('x', ''); | |
407 | formHelper.expectError('x', 'required'); | |
408 | }); | |
409 | }); | |
410 | ||
411 | describe('match', () => { | |
412 | let y: FormControl; | |
413 | ||
414 | beforeEach(() => { | |
415 | y = new FormControl('aaa'); | |
416 | form = new CdFormGroup({ | |
417 | x: new FormControl('aaa'), | |
418 | y: y | |
419 | }); | |
420 | formHelper = new FormHelper(form); | |
421 | }); | |
422 | ||
423 | it('should error when values are different', () => { | |
424 | formHelper.setValue('y', 'aab'); | |
425 | CdValidators.match('x', 'y')(form); | |
426 | formHelper.expectValid('x'); | |
427 | formHelper.expectError('y', 'match'); | |
428 | }); | |
429 | ||
430 | it('should not error when values are equal', () => { | |
431 | CdValidators.match('x', 'y')(form); | |
432 | formHelper.expectValid('x'); | |
433 | formHelper.expectValid('y'); | |
434 | }); | |
435 | ||
436 | it('should unset error when values are equal', () => { | |
437 | y.setErrors({ match: true }); | |
438 | CdValidators.match('x', 'y')(form); | |
439 | formHelper.expectValid('x'); | |
440 | formHelper.expectValid('y'); | |
441 | }); | |
442 | ||
443 | it('should keep other existing errors', () => { | |
444 | y.setErrors({ match: true, notUnique: true }); | |
445 | CdValidators.match('x', 'y')(form); | |
446 | formHelper.expectValid('x'); | |
447 | formHelper.expectError('y', 'notUnique'); | |
448 | }); | |
449 | }); | |
450 | ||
451 | describe('unique', () => { | |
452 | beforeEach(() => { | |
453 | form = new CdFormGroup({ | |
454 | x: new FormControl( | |
455 | '', | |
456 | null, | |
457 | CdValidators.unique((value) => { | |
458 | return observableOf('xyz' === value); | |
459 | }) | |
460 | ) | |
461 | }); | |
462 | formHelper = new FormHelper(form); | |
463 | }); | |
464 | ||
465 | it('should not error because of empty input', () => { | |
466 | expectValid(''); | |
467 | }); | |
468 | ||
469 | it('should not error because of not existing input', fakeAsync(() => { | |
470 | formHelper.setValue('x', 'abc', true); | |
471 | tick(500); | |
472 | formHelper.expectValid('x'); | |
473 | })); | |
474 | ||
475 | it('should error because of already existing input', fakeAsync(() => { | |
476 | formHelper.setValue('x', 'xyz', true); | |
477 | tick(500); | |
478 | formHelper.expectError('x', 'notUnique'); | |
479 | })); | |
480 | }); | |
9f95a23c TL |
481 | |
482 | describe('composeIf', () => { | |
483 | beforeEach(() => { | |
484 | form = new CdFormGroup({ | |
485 | x: new FormControl(true), | |
486 | y: new FormControl('abc'), | |
487 | z: new FormControl('') | |
488 | }); | |
489 | formHelper = new FormHelper(form); | |
490 | }); | |
491 | ||
492 | it('should not error because all conditions are fulfilled', () => { | |
493 | formHelper.setValue('z', 'zyx'); | |
494 | const validatorFn = CdValidators.composeIf( | |
495 | { | |
496 | x: true, | |
497 | y: 'abc' | |
498 | }, | |
499 | [Validators.required] | |
500 | ); | |
501 | expect(validatorFn(form.get('z'))).toBeNull(); | |
502 | }); | |
503 | ||
504 | it('should not error because of unmet prerequisites', () => { | |
505 | // Define prereqs that do not match the current values of the form fields. | |
506 | const validatorFn = CdValidators.composeIf( | |
507 | { | |
508 | x: false, | |
509 | y: 'xyz' | |
510 | }, | |
511 | [Validators.required] | |
512 | ); | |
513 | // The validator must succeed because the prereqs do not match, so the | |
514 | // validation of the 'z' control will be skipped. | |
515 | expect(validatorFn(form.get('z'))).toBeNull(); | |
516 | }); | |
517 | ||
518 | it('should error because of an empty value', () => { | |
519 | // Define prereqs that force the validator to validate the value of | |
520 | // the 'z' control. | |
521 | const validatorFn = CdValidators.composeIf( | |
522 | { | |
523 | x: true, | |
524 | y: 'abc' | |
525 | }, | |
526 | [Validators.required] | |
527 | ); | |
528 | // The validator must fail because the value of control 'z' is empty. | |
529 | expect(validatorFn(form.get('z'))).toEqual({ required: true }); | |
530 | }); | |
531 | }); | |
532 | ||
533 | describe('dimmlessBinary validators', () => { | |
534 | const i18nMock = (a: string, b: { value: string }) => a.replace('{{value}}', b.value); | |
535 | ||
536 | beforeEach(() => { | |
537 | form = new CdFormGroup({ | |
538 | x: new FormControl('2 KiB', [CdValidators.binaryMin(1024), CdValidators.binaryMax(3072)]) | |
539 | }); | |
540 | formHelper = new FormHelper(form); | |
541 | }); | |
542 | ||
543 | it('should not raise exception an exception for valid change', () => { | |
544 | formHelper.expectValidChange('x', '2.5 KiB'); | |
545 | }); | |
546 | ||
547 | it('should not raise minimum error', () => { | |
548 | formHelper.expectErrorChange('x', '0.5 KiB', 'binaryMin'); | |
549 | expect(form.get('x').getError('binaryMin')(i18nMock)).toBe( | |
550 | 'Size has to be at least 1 KiB or more' | |
551 | ); | |
552 | }); | |
553 | ||
554 | it('should not raise maximum error', () => { | |
555 | formHelper.expectErrorChange('x', '4 KiB', 'binaryMax'); | |
556 | expect(form.get('x').getError('binaryMax')(i18nMock)).toBe( | |
557 | 'Size has to be at most 3 KiB or less' | |
558 | ); | |
559 | }); | |
560 | }); | |
561 | ||
562 | describe('passwordPolicy', () => { | |
563 | let valid: boolean; | |
564 | let callbackCalled: boolean; | |
565 | ||
566 | const fakeUserService = { | |
567 | validatePassword: () => { | |
568 | return observableOf({ valid: valid, credits: 17, valuation: 'foo' }); | |
569 | } | |
570 | }; | |
571 | ||
572 | beforeEach(() => { | |
573 | callbackCalled = false; | |
574 | form = new CdFormGroup({ | |
575 | x: new FormControl( | |
576 | '', | |
577 | null, | |
578 | CdValidators.passwordPolicy( | |
579 | fakeUserService, | |
580 | () => 'admin', | |
581 | () => { | |
582 | callbackCalled = true; | |
583 | } | |
584 | ) | |
585 | ) | |
586 | }); | |
587 | formHelper = new FormHelper(form); | |
588 | }); | |
589 | ||
590 | it('should not error because of empty input', () => { | |
591 | expectValid(''); | |
592 | expect(callbackCalled).toBeTruthy(); | |
593 | }); | |
594 | ||
595 | it('should not error because password matches the policy', fakeAsync(() => { | |
596 | valid = true; | |
597 | formHelper.setValue('x', 'abc', true); | |
598 | tick(500); | |
599 | formHelper.expectValid('x'); | |
600 | })); | |
601 | ||
602 | it('should error because password does not match the policy', fakeAsync(() => { | |
603 | valid = false; | |
604 | formHelper.setValue('x', 'xyz', true); | |
605 | tick(500); | |
606 | formHelper.expectError('x', 'passwordPolicy'); | |
607 | })); | |
608 | ||
609 | it('should call the callback function', fakeAsync(() => { | |
610 | formHelper.setValue('x', 'xyz', true); | |
611 | tick(500); | |
612 | expect(callbackCalled).toBeTruthy(); | |
613 | })); | |
614 | }); | |
11fdf7f2 | 615 | }); |