]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
d749a233c590807bf511e6a9233676cc3b15153a
[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 } 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"
21         class="btn btn-sm btn-primary"
22         (click)="openCtrlDriven()">
23       <i class="fa fa-fw fa-times"></i>Deletion Ctrl-Test
24       <ng-template #ctrlDescription>
25         The spinner is handled by the controller if you have use the modal as ViewChild in order to
26         use it's functions to stop the spinner or close the dialog.
27       </ng-template>
28     </button>
29
30     <button type="button"
31             class="btn btn-sm btn-primary"
32             (click)="openModalDriven()">
33       <i class="fa fa-fw fa-times"></i>Deletion Modal-Test
34       <ng-template #modalDescription>
35         The spinner is handled by the modal if your given deletion function returns a Observable.
36       </ng-template>
37     </button>
38   `
39 })
40 class MockComponent {
41   @ViewChild('ctrlDescription')
42   ctrlDescription: TemplateRef<any>;
43   @ViewChild('modalDescription')
44   modalDescription: TemplateRef<any>;
45   someData = [1, 2, 3, 4, 5];
46   finished: number[];
47   ctrlRef: BsModalRef;
48   modalRef: BsModalRef;
49
50   // Normally private - public was needed for the tests
51   constructor(public modalService: BsModalService) {}
52
53   openCtrlDriven() {
54     this.ctrlRef = this.modalService.show(CriticalConfirmationModalComponent, {
55       initialState: {
56         submitAction: this.fakeDeleteController.bind(this),
57         bodyTemplate: this.ctrlDescription
58       }
59     });
60   }
61
62   openModalDriven() {
63     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
64       initialState: {
65         submitActionObservable: this.fakeDelete(),
66         bodyTemplate: this.modalDescription
67       }
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('CriticalConfirmationModalComponent', () => {
95   let mockComponent: MockComponent;
96   let component: CriticalConfirmationModalComponent;
97   let mockFixture: ComponentFixture<MockComponent>;
98   let fixture: ComponentFixture<CriticalConfirmationModalComponent>;
99
100   configureTestBed({
101     declarations: [MockComponent, CriticalConfirmationModalComponent],
102     schemas: [NO_ERRORS_SCHEMA],
103     imports: [ModalModule.forRoot(), ReactiveFormsModule, MockModule, DirectivesModule],
104     providers: [BsModalRef]
105   });
106
107   beforeEach(() => {
108     mockFixture = TestBed.createComponent(MockComponent);
109     mockComponent = mockFixture.componentInstance;
110     // Mocking the modals as a lot would be left over
111     spyOn(mockComponent.modalService, 'show').and.callFake((modalComp, config) => {
112       const ref = new BsModalRef();
113       fixture = TestBed.createComponent(CriticalConfirmationModalComponent);
114       component = fixture.componentInstance;
115       if (config.initialState) {
116         component = Object.assign(component, config.initialState);
117       }
118       fixture.detectChanges();
119       ref.content = component;
120       return ref;
121     });
122     mockComponent.openCtrlDriven();
123     mockFixture.detectChanges();
124   });
125
126   it('should create', () => {
127     expect(component).toBeTruthy();
128   });
129
130   it('should focus the checkbox form field', () => {
131     fixture.detectChanges();
132     fixture.whenStable().then(() => {
133       const focused = fixture.debugElement.query(By.css(':focus'));
134       expect(focused.attributes.id).toBe('confirmation');
135       expect(focused.attributes.type).toBe('checkbox');
136       const element = document.getElementById('confirmation');
137       expect(element === document.activeElement).toBeTruthy();
138     });
139   });
140
141   it('should throw an error if no action is defined', () => {
142     component = Object.assign(component, {
143       submitAction: null,
144       submitActionObservable: null
145     });
146     expect(() => component.ngOnInit()).toThrowError('No submit action defined');
147   });
148
149   it('should test if the ctrl driven mock is set correctly through mock component', () => {
150     expect(component.bodyTemplate).toBeTruthy();
151     expect(component.submitAction).toBeTruthy();
152     expect(component.submitActionObservable).not.toBeTruthy();
153   });
154
155   it('should test if the modal driven mock is set correctly through mock component', () => {
156     mockComponent.openModalDriven();
157     expect(component.bodyTemplate).toBeTruthy();
158     expect(component.submitActionObservable).toBeTruthy();
159     expect(component.submitAction).not.toBeTruthy();
160   });
161
162   describe('component functions', () => {
163     const changeValue = (value) => {
164       const ctrl = component.deletionForm.get('confirmation');
165       ctrl.setValue(value);
166       ctrl.markAsDirty();
167       ctrl.updateValueAndValidity();
168       fixture.detectChanges();
169     };
170
171     it('should test hideModal', () => {
172       expect(component.modalRef).toBeTruthy();
173       expect(component.hideModal).toBeTruthy();
174       spyOn(component.modalRef, 'hide').and.callThrough();
175       expect(component.modalRef.hide).not.toHaveBeenCalled();
176       component.hideModal();
177       expect(component.modalRef.hide).toHaveBeenCalled();
178     });
179
180     describe('validate confirmation', () => {
181       const testValidation = (submitted: boolean, error: string, expected: boolean) => {
182         expect(
183           component.deletionForm.showError('confirmation', <NgForm>{ submitted: submitted }, error)
184         ).toBe(expected);
185       };
186
187       beforeEach(() => {
188         component.deletionForm.reset();
189       });
190
191       it('should test empty values', () => {
192         component.deletionForm.reset();
193         testValidation(false, undefined, false);
194         testValidation(true, 'required', true);
195         component.deletionForm.reset();
196         changeValue(true);
197         changeValue(false);
198         testValidation(true, 'required', true);
199       });
200     });
201
202     describe('deletion call', () => {
203       beforeEach(() => {
204         spyOn(component, 'stopLoadingSpinner').and.callThrough();
205         spyOn(component, 'hideModal').and.callThrough();
206       });
207
208       describe('Controller driven', () => {
209         beforeEach(() => {
210           spyOn(component, 'submitAction').and.callThrough();
211           spyOn(mockComponent.ctrlRef, 'hide').and.callThrough();
212         });
213
214         it('should test fake deletion that closes modal', <any>fakeAsync(() => {
215           // Before deletionCall
216           expect(component.submitAction).not.toHaveBeenCalled();
217           // During deletionCall
218           component.callSubmitAction();
219           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
220           expect(component.hideModal).not.toHaveBeenCalled();
221           expect(mockComponent.ctrlRef.hide).not.toHaveBeenCalled();
222           expect(component.submitAction).toHaveBeenCalled();
223           expect(mockComponent.finished).toBe(undefined);
224           // After deletionCall
225           tick(2000);
226           expect(component.hideModal).not.toHaveBeenCalled();
227           expect(mockComponent.ctrlRef.hide).toHaveBeenCalled();
228           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
229         }));
230       });
231
232       describe('Modal driven', () => {
233         beforeEach(() => {
234           mockComponent.openModalDriven();
235           spyOn(component, 'stopLoadingSpinner').and.callThrough();
236           spyOn(component, 'hideModal').and.callThrough();
237           spyOn(mockComponent, 'fakeDelete').and.callThrough();
238         });
239
240         it('should delete and close modal', <any>fakeAsync(() => {
241           // During deletionCall
242           component.callSubmitAction();
243           expect(mockComponent.finished).toBe(undefined);
244           expect(component.hideModal).not.toHaveBeenCalled();
245           // After deletionCall
246           tick(2000);
247           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
248           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
249           expect(component.hideModal).toHaveBeenCalled();
250         }));
251       });
252     });
253   });
254 });