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