]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
391c5416440f07031b915f5a9aaa0c8ffc258753
[ceph-ci.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" 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')
38   ctrlDescription: TemplateRef<any>;
39   @ViewChild('modalDescription')
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     // Mocking the modals as a lot would be left over
107     spyOn(mockComponent.modalService, 'show').and.callFake((_modalComp, config) => {
108       const ref = new BsModalRef();
109       fixture = TestBed.createComponent(CriticalConfirmationModalComponent);
110       component = fixture.componentInstance;
111       if (config.initialState) {
112         component = Object.assign(component, config.initialState);
113       }
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   it('should focus the checkbox form field', () => {
127     fixture.detectChanges();
128     fixture.whenStable().then(() => {
129       const focused = fixture.debugElement.query(By.css(':focus'));
130       expect(focused.attributes.id).toBe('confirmation');
131       expect(focused.attributes.type).toBe('checkbox');
132       const element = document.getElementById('confirmation');
133       expect(element === document.activeElement).toBeTruthy();
134     });
135   });
136
137   it('should throw an error if no action is defined', () => {
138     component = Object.assign(component, {
139       submitAction: null,
140       submitActionObservable: null
141     });
142     expect(() => component.ngOnInit()).toThrowError('No submit action defined');
143   });
144
145   it('should test if the ctrl driven mock is set correctly through mock component', () => {
146     expect(component.bodyTemplate).toBeTruthy();
147     expect(component.submitAction).toBeTruthy();
148     expect(component.submitActionObservable).not.toBeTruthy();
149   });
150
151   it('should test if the modal driven mock is set correctly through mock component', () => {
152     mockComponent.openModalDriven();
153     expect(component.bodyTemplate).toBeTruthy();
154     expect(component.submitActionObservable).toBeTruthy();
155     expect(component.submitAction).not.toBeTruthy();
156   });
157
158   describe('component functions', () => {
159     const changeValue = (value) => {
160       const ctrl = component.deletionForm.get('confirmation');
161       ctrl.setValue(value);
162       ctrl.markAsDirty();
163       ctrl.updateValueAndValidity();
164       fixture.detectChanges();
165     };
166
167     it('should test hideModal', () => {
168       expect(component.modalRef).toBeTruthy();
169       expect(component.hideModal).toBeTruthy();
170       spyOn(component.modalRef, 'hide').and.callThrough();
171       expect(component.modalRef.hide).not.toHaveBeenCalled();
172       component.hideModal();
173       expect(component.modalRef.hide).toHaveBeenCalled();
174     });
175
176     describe('validate confirmation', () => {
177       const testValidation = (submitted: boolean, error: string, expected: boolean) => {
178         expect(
179           component.deletionForm.showError('confirmation', <NgForm>{ submitted: submitted }, error)
180         ).toBe(expected);
181       };
182
183       beforeEach(() => {
184         component.deletionForm.reset();
185       });
186
187       it('should test empty values', () => {
188         component.deletionForm.reset();
189         testValidation(false, undefined, false);
190         testValidation(true, 'required', true);
191         component.deletionForm.reset();
192         changeValue(true);
193         changeValue(false);
194         testValidation(true, 'required', true);
195       });
196     });
197
198     describe('deletion call', () => {
199       beforeEach(() => {
200         spyOn(component, 'stopLoadingSpinner').and.callThrough();
201         spyOn(component, 'hideModal').and.callThrough();
202       });
203
204       describe('Controller driven', () => {
205         beforeEach(() => {
206           spyOn(component, 'submitAction').and.callThrough();
207           spyOn(mockComponent.ctrlRef, 'hide').and.callThrough();
208         });
209
210         it('should test fake deletion that closes modal', <any>fakeAsync(() => {
211           // Before deletionCall
212           expect(component.submitAction).not.toHaveBeenCalled();
213           // During deletionCall
214           component.callSubmitAction();
215           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
216           expect(component.hideModal).not.toHaveBeenCalled();
217           expect(mockComponent.ctrlRef.hide).not.toHaveBeenCalled();
218           expect(component.submitAction).toHaveBeenCalled();
219           expect(mockComponent.finished).toBe(undefined);
220           // After deletionCall
221           tick(2000);
222           expect(component.hideModal).not.toHaveBeenCalled();
223           expect(mockComponent.ctrlRef.hide).toHaveBeenCalled();
224           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
225         }));
226       });
227
228       describe('Modal driven', () => {
229         beforeEach(() => {
230           mockComponent.openModalDriven();
231           spyOn(component, 'stopLoadingSpinner').and.callThrough();
232           spyOn(component, 'hideModal').and.callThrough();
233           spyOn(mockComponent, 'fakeDelete').and.callThrough();
234         });
235
236         it('should delete and close modal', <any>fakeAsync(() => {
237           // During deletionCall
238           component.callSubmitAction();
239           expect(mockComponent.finished).toBe(undefined);
240           expect(component.hideModal).not.toHaveBeenCalled();
241           // After deletionCall
242           tick(2000);
243           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
244           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
245           expect(component.hideModal).toHaveBeenCalled();
246         }));
247       });
248     });
249   });
250 });