]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
adcb95632cd4c725e4f42434cb3c97e3f8bc231c
[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 import { By } from '@angular/platform-browser';
5
6 import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal';
7 import { Observable, Subscriber, timer as observableTimer } from 'rxjs';
8
9 import { configureTestBed, modalServiceShow } from '../../../../testing/unit-test-helper';
10 import { DirectivesModule } from '../../directives/directives.module';
11 import { CriticalConfirmationModalComponent } from './critical-confirmation-modal.component';
12
13 @NgModule({
14   entryComponents: [CriticalConfirmationModalComponent]
15 })
16 export class MockModule {}
17
18 @Component({
19   template: `
20     <button type="button" class="btn btn-secondary" (click)="openCtrlDriven()">
21       <i class="fa fa-times"></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" class="btn btn-secondary" (click)="openModalDriven()">
29       <i class="fa fa-times"></i>Deletion Modal-Test
30       <ng-template #modalDescription>
31         The spinner is handled by the modal if your given deletion function returns a Observable.
32       </ng-template>
33     </button>
34   `
35 })
36 class MockComponent {
37   @ViewChild('ctrlDescription', { static: true })
38   ctrlDescription: TemplateRef<any>;
39   @ViewChild('modalDescription', { static: true })
40   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(CriticalConfirmationModalComponent, {
51       initialState: {
52         submitAction: this.fakeDeleteController.bind(this),
53         bodyTemplate: this.ctrlDescription
54       }
55     });
56   }
57
58   openModalDriven() {
59     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
60       initialState: {
61         submitActionObservable: this.fakeDelete(),
62         bodyTemplate: this.modalDescription
63       }
64     });
65   }
66
67   finish() {
68     this.finished = [6, 7, 8, 9];
69   }
70
71   fakeDelete() {
72     return (): Observable<any> => {
73       return new Observable((observer: Subscriber<any>) => {
74         observableTimer(100).subscribe(() => {
75           observer.next(this.finish());
76           observer.complete();
77         });
78       });
79     };
80   }
81
82   fakeDeleteController() {
83     observableTimer(100).subscribe(() => {
84       this.finish();
85       this.ctrlRef.hide();
86     });
87   }
88 }
89
90 describe('CriticalConfirmationModalComponent', () => {
91   let mockComponent: MockComponent;
92   let component: CriticalConfirmationModalComponent;
93   let mockFixture: ComponentFixture<MockComponent>;
94   let fixture: ComponentFixture<CriticalConfirmationModalComponent>;
95
96   configureTestBed({
97     declarations: [MockComponent, CriticalConfirmationModalComponent],
98     schemas: [NO_ERRORS_SCHEMA],
99     imports: [ModalModule.forRoot(), ReactiveFormsModule, MockModule, DirectivesModule],
100     providers: [BsModalRef]
101   });
102
103   beforeEach(() => {
104     mockFixture = TestBed.createComponent(MockComponent);
105     mockComponent = mockFixture.componentInstance;
106     spyOn(mockComponent.modalService, 'show').and.callFake((_modalComp, config) => {
107       const data = modalServiceShow(CriticalConfirmationModalComponent, config);
108       fixture = data.fixture;
109       component = data.component;
110       return data.ref;
111     });
112     mockComponent.openCtrlDriven();
113     mockFixture.detectChanges();
114   });
115
116   it('should create', () => {
117     expect(component).toBeTruthy();
118   });
119
120   it('should focus the checkbox form field', (done) => {
121     fixture.detectChanges();
122     fixture.whenStable().then(() => {
123       const focused = fixture.debugElement.query(By.css(':focus'));
124       expect(focused.attributes.id).toBe('confirmation');
125       expect(focused.attributes.type).toBe('checkbox');
126       const element = document.getElementById('confirmation');
127       expect(element === document.activeElement).toBeTruthy();
128       done();
129     });
130   });
131
132   it('should throw an error if no action is defined', () => {
133     component = Object.assign(component, {
134       submitAction: null,
135       submitActionObservable: null
136     });
137     expect(() => component.ngOnInit()).toThrowError('No submit action defined');
138   });
139
140   it('should test if the ctrl driven mock is set correctly through mock component', () => {
141     expect(component.bodyTemplate).toBeTruthy();
142     expect(component.submitAction).toBeTruthy();
143     expect(component.submitActionObservable).not.toBeTruthy();
144   });
145
146   it('should test if the modal driven mock is set correctly through mock component', () => {
147     mockComponent.openModalDriven();
148     expect(component.bodyTemplate).toBeTruthy();
149     expect(component.submitActionObservable).toBeTruthy();
150     expect(component.submitAction).not.toBeTruthy();
151   });
152
153   describe('component functions', () => {
154     const changeValue = (value: boolean) => {
155       const ctrl = component.deletionForm.get('confirmation');
156       ctrl.setValue(value);
157       ctrl.markAsDirty();
158       ctrl.updateValueAndValidity();
159       fixture.detectChanges();
160     };
161
162     it('should test hideModal', () => {
163       expect(component.modalRef).toBeTruthy();
164       expect(component.hideModal).toBeTruthy();
165       spyOn(component.modalRef, 'hide').and.callThrough();
166       expect(component.modalRef.hide).not.toHaveBeenCalled();
167       component.hideModal();
168       expect(component.modalRef.hide).toHaveBeenCalled();
169     });
170
171     describe('validate confirmation', () => {
172       const testValidation = (submitted: boolean, error: string, expected: boolean) => {
173         expect(
174           component.deletionForm.showError('confirmation', <NgForm>{ submitted: submitted }, error)
175         ).toBe(expected);
176       };
177
178       beforeEach(() => {
179         component.deletionForm.reset();
180       });
181
182       it('should test empty values', () => {
183         component.deletionForm.reset();
184         testValidation(false, undefined, false);
185         testValidation(true, 'required', true);
186         component.deletionForm.reset();
187         changeValue(true);
188         changeValue(false);
189         testValidation(true, 'required', true);
190       });
191     });
192
193     describe('deletion call', () => {
194       beforeEach(() => {
195         spyOn(component, 'stopLoadingSpinner').and.callThrough();
196         spyOn(component, 'hideModal').and.callThrough();
197       });
198
199       describe('Controller driven', () => {
200         beforeEach(() => {
201           spyOn(component, 'submitAction').and.callThrough();
202           spyOn(mockComponent.ctrlRef, 'hide').and.callThrough();
203         });
204
205         it('should test fake deletion that closes modal', <any>fakeAsync(() => {
206           // Before deletionCall
207           expect(component.submitAction).not.toHaveBeenCalled();
208           // During deletionCall
209           component.callSubmitAction();
210           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
211           expect(component.hideModal).not.toHaveBeenCalled();
212           expect(mockComponent.ctrlRef.hide).not.toHaveBeenCalled();
213           expect(component.submitAction).toHaveBeenCalled();
214           expect(mockComponent.finished).toBe(undefined);
215           // After deletionCall
216           tick(2000);
217           expect(component.hideModal).not.toHaveBeenCalled();
218           expect(mockComponent.ctrlRef.hide).toHaveBeenCalled();
219           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
220         }));
221       });
222
223       describe('Modal driven', () => {
224         beforeEach(() => {
225           mockComponent.openModalDriven();
226           spyOn(component, 'stopLoadingSpinner').and.callThrough();
227           spyOn(component, 'hideModal').and.callThrough();
228           spyOn(mockComponent, 'fakeDelete').and.callThrough();
229         });
230
231         it('should delete and close modal', <any>fakeAsync(() => {
232           // During deletionCall
233           component.callSubmitAction();
234           expect(mockComponent.finished).toBe(undefined);
235           expect(component.hideModal).not.toHaveBeenCalled();
236           // After deletionCall
237           tick(2000);
238           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
239           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
240           expect(component.hideModal).toHaveBeenCalled();
241         }));
242       });
243     });
244   });
245 });