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