]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
e4dd255b9e11554cfef268ec02034bcf8a9d0d20
[ceph.git] /
1 import { Component, NgModule, NO_ERRORS_SCHEMA, TemplateRef, ViewChild } from '@angular/core';
2 import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3 import { NgForm, ReactiveFormsModule } from '@angular/forms';
4
5 import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap';
6 import { Observable, Subscriber, timer as observableTimer } from 'rxjs';
7
8 import { configureTestBed } from '../../../../testing/unit-test-helper';
9 import { DeletionModalComponent } from './deletion-modal.component';
10
11 @NgModule({
12   entryComponents: [DeletionModalComponent]
13 })
14 export class MockModule {}
15
16 @Component({
17   template: `
18     <button type="button"
19         class="btn btn-sm btn-primary"
20         (click)="openCtrlDriven()">
21       <i class="fa fa-fw fa-trash"></i>Deletion Ctrl-Test
22       <ng-template #ctrlDescription>
23         The spinner is handled by the controller if you have use the modal as ViewChild in order to
24         use it's functions to stop the spinner or close the dialog.
25       </ng-template>
26     </button>
27
28     <button type="button"
29             class="btn btn-sm btn-primary"
30             (click)="openModalDriven()">
31       <i class="fa fa-fw fa-trash"></i>Deletion Modal-Test
32       <ng-template #modalDescription>
33         The spinner is handled by the modal if your given deletion function returns a Observable.
34       </ng-template>
35     </button>
36   `
37 })
38 class MockComponent {
39   @ViewChild('ctrlDescription')
40   ctrlDescription: TemplateRef<any>;
41   @ViewChild('modalDescription')
42   modalDescription: TemplateRef<any>;
43   someData = [1, 2, 3, 4, 5];
44   finished: number[];
45   ctrlRef: BsModalRef;
46   modalRef: BsModalRef;
47
48   // Normally private - public was needed for the tests
49   constructor(public modalService: BsModalService) {}
50
51   openCtrlDriven() {
52     this.ctrlRef = this.modalService.show(DeletionModalComponent);
53     this.ctrlRef.content.setUp({
54       metaType: 'Controller delete handling',
55       pattern: 'ctrl-test',
56       deletionMethod: this.fakeDeleteController.bind(this),
57       description: this.ctrlDescription,
58       modalRef: this.ctrlRef
59     });
60   }
61
62   openModalDriven() {
63     this.modalRef = this.modalService.show(DeletionModalComponent);
64     this.modalRef.content.setUp({
65       metaType: 'Modal delete handling',
66       pattern: 'modal-test',
67       deletionObserver: this.fakeDelete(),
68       description: this.modalDescription,
69       modalRef: this.modalRef
70     });
71   }
72
73   finish() {
74     this.finished = [6, 7, 8, 9];
75   }
76
77   fakeDelete() {
78     return (): Observable<any> => {
79       return new Observable((observer: Subscriber<any>) => {
80         observableTimer(100).subscribe(() => {
81           observer.next(this.finish());
82           observer.complete();
83         });
84       });
85     };
86   }
87
88   fakeDeleteController() {
89     observableTimer(100).subscribe(() => {
90       this.finish();
91       this.ctrlRef.hide();
92     });
93   }
94 }
95
96 describe('DeletionModalComponent', () => {
97   let mockComponent: MockComponent;
98   let component: DeletionModalComponent;
99   let mockFixture: ComponentFixture<MockComponent>;
100   let fixture: ComponentFixture<DeletionModalComponent>;
101
102   configureTestBed({
103     declarations: [MockComponent, DeletionModalComponent],
104     schemas: [NO_ERRORS_SCHEMA],
105     imports: [ModalModule.forRoot(), ReactiveFormsModule, MockModule]
106   });
107
108   beforeEach(() => {
109     mockFixture = TestBed.createComponent(MockComponent);
110     mockComponent = mockFixture.componentInstance;
111     // Mocking the modals as a lot would be left over
112     spyOn(mockComponent.modalService, 'show').and.callFake(() => {
113       const ref = new BsModalRef();
114       fixture = TestBed.createComponent(DeletionModalComponent);
115       component = fixture.componentInstance;
116       fixture.detectChanges();
117       ref.content = component;
118       return ref;
119     });
120     mockComponent.openCtrlDriven();
121     mockFixture.detectChanges();
122   });
123
124   it('should create', () => {
125     expect(component).toBeTruthy();
126   });
127
128   describe('setUp', () => {
129     const clearSetup = () => {
130       component.metaType = undefined;
131       component.pattern = 'yes';
132       component.deletionObserver = undefined;
133       component.description = undefined;
134       component.modalRef = undefined;
135     };
136
137     const expectSetup = (
138       metaType,
139       observer: boolean,
140       method: boolean,
141       pattern,
142       template: boolean
143     ) => {
144       expect(component.modalRef).toBeTruthy();
145       expect(component.metaType).toBe(metaType);
146       expect(!!component.deletionObserver).toBe(observer);
147       expect(!!component.deletionMethod).toBe(method);
148       expect(component.pattern).toBe(pattern);
149       expect(!!component.description).toBe(template);
150     };
151
152     beforeEach(() => {
153       clearSetup();
154     });
155
156     it('should throw error if no modal reference is given', () => {
157       expect(() =>
158         component.setUp({
159           metaType: undefined,
160           modalRef: undefined
161         })
162       ).toThrowError('No modal reference');
163     });
164
165     it('should throw error if no meta type is given', () => {
166       expect(() =>
167         component.setUp({
168           metaType: undefined,
169           modalRef: mockComponent.ctrlRef
170         })
171       ).toThrowError('No meta type');
172     });
173
174     it('should throw error if no deletion method is given', () => {
175       expect(() =>
176         component.setUp({
177           metaType: 'Sth',
178           modalRef: mockComponent.ctrlRef
179         })
180       ).toThrowError('No deletion method');
181     });
182
183     it('should throw no errors if metaType, modalRef and a deletion method were given', () => {
184       component.setUp({
185         metaType: 'Observer',
186         modalRef: mockComponent.ctrlRef,
187         deletionObserver: mockComponent.fakeDelete()
188       });
189       expectSetup('Observer', true, false, 'yes', false);
190       clearSetup();
191       component.setUp({
192         metaType: 'Controller',
193         modalRef: mockComponent.ctrlRef,
194         deletionMethod: mockComponent.fakeDeleteController
195       });
196       expectSetup('Controller', false, true, 'yes', false);
197     });
198
199     it('should test optional parameters - pattern and description', () => {
200       component.setUp({
201         metaType: 'Pattern only',
202         modalRef: mockComponent.ctrlRef,
203         deletionObserver: mockComponent.fakeDelete(),
204         pattern: '{sth/!$_8()'
205       });
206       expectSetup('Pattern only', true, false, '{sth/!$_8()', false);
207       clearSetup();
208       component.setUp({
209         metaType: 'Description only',
210         modalRef: mockComponent.ctrlRef,
211         deletionObserver: mockComponent.fakeDelete(),
212         description: mockComponent.modalDescription
213       });
214       expectSetup('Description only', true, false, 'yes', true);
215       clearSetup();
216       component.setUp({
217         metaType: 'Description and pattern',
218         modalRef: mockComponent.ctrlRef,
219         deletionObserver: mockComponent.fakeDelete(),
220         description: mockComponent.modalDescription,
221         pattern: '{sth/!$_8()'
222       });
223       expectSetup('Description and pattern', true, false, '{sth/!$_8()', true);
224     });
225   });
226
227   it('should test if the ctrl driven mock is set correctly through mock component', () => {
228     expect(component.metaType).toBe('Controller delete handling');
229     expect(component.pattern).toBe('ctrl-test');
230     expect(component.description).toBeTruthy();
231     expect(component.modalRef).toBeTruthy();
232     expect(component.deletionMethod).toBeTruthy();
233     expect(component.deletionObserver).not.toBeTruthy();
234   });
235
236   it('should test if the modal driven mock is set correctly through mock component', () => {
237     mockComponent.openModalDriven();
238     expect(component.metaType).toBe('Modal delete handling');
239     expect(component.pattern).toBe('modal-test');
240     expect(component.description).toBeTruthy();
241     expect(component.modalRef).toBeTruthy();
242     expect(component.deletionObserver).toBeTruthy();
243     expect(component.deletionMethod).not.toBeTruthy();
244   });
245
246   describe('component functions', () => {
247     const changeValue = (value) => {
248       component.confirmation.setValue(value);
249       component.confirmation.markAsDirty();
250       component.confirmation.updateValueAndValidity();
251       fixture.detectChanges();
252     };
253
254     it('should test hideModal', () => {
255       expect(component.modalRef).toBeTruthy();
256       expect(component.hideModal).toBeTruthy();
257       spyOn(component.modalRef, 'hide').and.callThrough();
258       expect(component.modalRef.hide).not.toHaveBeenCalled();
259       component.hideModal();
260       expect(component.modalRef.hide).toHaveBeenCalled();
261     });
262
263     describe('validate confirmation', () => {
264       const testValidation = (submitted: boolean, error: string, expected: boolean) => {
265         expect(
266           component.deletionForm.showError('confirmation', <NgForm>{ submitted: submitted }, error)
267         ).toBe(expected);
268       };
269
270       beforeEach(() => {
271         component.deletionForm.reset();
272       });
273
274       it('should test empty values', () => {
275         component.deletionForm.reset();
276         testValidation(false, undefined, false);
277         testValidation(true, 'required', true);
278         component.deletionForm.reset();
279         changeValue('let-me-pass');
280         changeValue('');
281         testValidation(true, 'required', true);
282       });
283
284       it('should test pattern', () => {
285         changeValue('let-me-pass');
286         testValidation(false, 'pattern', true);
287         changeValue('ctrl-test');
288         testValidation(false, undefined, false);
289         testValidation(true, undefined, false);
290       });
291
292       it('should test regex pattern', () => {
293         component.pattern = 'a+b';
294         changeValue('ab');
295         testValidation(false, 'pattern', true);
296         changeValue('a+b');
297         testValidation(false, 'pattern', false);
298       });
299     });
300
301     describe('deletion call', () => {
302       beforeEach(() => {
303         spyOn(component, 'stopLoadingSpinner').and.callThrough();
304         spyOn(component, 'hideModal').and.callThrough();
305       });
306
307       describe('Controller driven', () => {
308         beforeEach(() => {
309           spyOn(component, 'deletionMethod').and.callThrough();
310           spyOn(mockComponent.ctrlRef, 'hide').and.callThrough();
311         });
312
313         it('should test fake deletion that closes modal', <any>fakeAsync(() => {
314           // Before deletionCall
315           expect(component.deletionMethod).not.toHaveBeenCalled();
316           // During deletionCall
317           component.deletionCall();
318           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
319           expect(component.hideModal).not.toHaveBeenCalled();
320           expect(mockComponent.ctrlRef.hide).not.toHaveBeenCalled();
321           expect(component.deletionMethod).toHaveBeenCalled();
322           expect(mockComponent.finished).toBe(undefined);
323           // After deletionCall
324           tick(2000);
325           expect(component.hideModal).not.toHaveBeenCalled();
326           expect(mockComponent.ctrlRef.hide).toHaveBeenCalled();
327           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
328         }));
329       });
330
331       describe('Modal driven', () => {
332         beforeEach(() => {
333           mockComponent.openModalDriven();
334           spyOn(mockComponent.modalRef, 'hide').and.callThrough();
335           spyOn(component, 'stopLoadingSpinner').and.callThrough();
336           spyOn(component, 'hideModal').and.callThrough();
337           spyOn(mockComponent, 'fakeDelete').and.callThrough();
338         });
339
340         it('should delete and close modal', <any>fakeAsync(() => {
341           // During deletionCall
342           component.deletionCall();
343           expect(mockComponent.finished).toBe(undefined);
344           expect(component.hideModal).not.toHaveBeenCalled();
345           expect(mockComponent.modalRef.hide).not.toHaveBeenCalled();
346           // After deletionCall
347           tick(2000);
348           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
349           expect(mockComponent.modalRef.hide).toHaveBeenCalled();
350           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
351           expect(component.hideModal).toHaveBeenCalled();
352         }));
353       });
354     });
355   });
356 });