]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
814f18a5dd254624cee7ad82f86c3ef290ffdf82
[ceph-ci.git] /
1 import { Component, NgModule, 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 { Observable, Subscriber, timer as observableTimer } from 'rxjs';
6
7 import { DirectivesModule } from '~/app/shared/directives/directives.module';
8 import { configureTestBed, modalServiceShow } from '~/testing/unit-test-helper';
9 import { AlertPanelComponent } from '../alert-panel/alert-panel.component';
10 import { LoadingPanelComponent } from '../loading-panel/loading-panel.component';
11 import { CriticalConfirmationModalComponent } from './critical-confirmation-modal.component';
12 import { CheckboxModule, ModalService, PlaceholderService } from 'carbon-components-angular';
13 import { ModalCdsService } from '../../services/modal-cds.service';
14 import { DeletionImpact } from '../../enum/critical-confirmation-modal-impact.enum';
15
16 @NgModule({})
17 export class MockModule {}
18
19 @Component({
20   template: `
21     <button type="button" class="btn btn-danger" (click)="openCtrlDriven()">
22       <i class="fa fa-times"></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" class="btn btn-danger" (click)="openModalDriven()">
30       <i class="fa fa-times"></i>Deletion Modal-Test
31       <ng-template #modalDescription>
32         The spinner is handled by the modal if your given deletion function returns a Observable.
33       </ng-template>
34     </button>
35   `
36 })
37 class MockComponent {
38   @ViewChild('ctrlDescription', { static: true })
39   ctrlDescription: TemplateRef<any>;
40   @ViewChild('modalDescription', { static: true })
41   modalDescription: TemplateRef<any>;
42   someData = [1, 2, 3, 4, 5];
43   finished: number[];
44   ctrlRef: any;
45   modalRef: any;
46
47   // Normally private - public was needed for the tests
48   constructor(public modalService: ModalCdsService) {}
49
50   openCtrlDriven() {
51     this.ctrlRef = this.modalService.show(CriticalConfirmationModalComponent, {
52       submitAction: this.fakeDeleteController.bind(this),
53       bodyTemplate: this.ctrlDescription
54     });
55   }
56
57   openModalDriven() {
58     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
59       submitActionObservable: this.fakeDelete(),
60       bodyTemplate: this.modalDescription
61     });
62   }
63
64   finish() {
65     this.finished = [6, 7, 8, 9];
66   }
67
68   fakeDelete() {
69     return (): Observable<any> => {
70       return new Observable((observer: Subscriber<any>) => {
71         observableTimer(100).subscribe(() => {
72           observer.next(this.finish());
73           observer.complete();
74         });
75       });
76     };
77   }
78
79   fakeDeleteController() {
80     observableTimer(100).subscribe(() => {
81       this.finish();
82       this.ctrlRef.close();
83     });
84   }
85 }
86
87 describe('CriticalConfirmationModalComponent', () => {
88   let mockComponent: MockComponent;
89   let component: CriticalConfirmationModalComponent;
90   let mockFixture: ComponentFixture<MockComponent>;
91
92   configureTestBed({
93     declarations: [
94       MockComponent,
95       CriticalConfirmationModalComponent,
96       LoadingPanelComponent,
97       AlertPanelComponent
98     ],
99     imports: [ReactiveFormsModule, MockModule, DirectivesModule, CheckboxModule],
100     providers: [
101       ModalService,
102       PlaceholderService,
103       { provide: 'itemNames', useValue: [] },
104       { provide: 'itemDescription', useValue: 'entry' },
105       { provide: 'actionDescription', useValue: 'delete' }
106     ]
107   });
108
109   beforeEach(() => {
110     mockFixture = TestBed.createComponent(MockComponent);
111     mockComponent = mockFixture.componentInstance;
112     spyOn(mockComponent.modalService, 'show').and.callFake((_modalComp, config) => {
113       const data = modalServiceShow(CriticalConfirmationModalComponent, config);
114       component = data.componentInstance;
115       return data;
116     });
117     mockComponent.openCtrlDriven();
118     mockFixture.detectChanges();
119   });
120
121   it('should create', () => {
122     expect(component).toBeTruthy();
123   });
124
125   it('should throw an error if no action is defined', () => {
126     component = Object.assign(component, {
127       submitAction: null,
128       submitActionObservable: null
129     });
130     expect(() => component.ngOnInit()).toThrowError('No submit action defined');
131   });
132
133   it('should test if the ctrl driven mock is set correctly through mock component', () => {
134     expect(component.bodyTemplate).toBeTruthy();
135     expect(component.submitAction).toBeTruthy();
136     expect(component.submitActionObservable).not.toBeTruthy();
137   });
138
139   it('should test if the modal driven mock is set correctly through mock component', () => {
140     mockComponent.openModalDriven();
141     expect(component.bodyTemplate).toBeTruthy();
142     expect(component.submitActionObservable).toBeTruthy();
143     expect(component.submitAction).not.toBeTruthy();
144   });
145
146   describe('component functions', () => {
147     const changeValue = (formControl: string, value: any) => {
148       const ctrl = component.deletionForm.get(formControl);
149       ctrl.setValue(value);
150       ctrl.markAsDirty();
151       ctrl.updateValueAndValidity();
152     };
153
154     it('should test hideModal', () => {
155       expect(component.hideModal).toBeTruthy();
156       spyOn(component, 'closeModal').and.callThrough();
157       expect(component.closeModal).not.toHaveBeenCalled();
158       component.hideModal();
159       expect(component.closeModal).toHaveBeenCalled();
160     });
161
162     describe('validate confirmation', () => {
163       const testValidation = (submitted: boolean, error: string, expected: boolean) => {
164         expect(
165           component.deletionForm.showError('confirmation', <NgForm>{ submitted: submitted }, error)
166         ).toBe(expected);
167       };
168
169       beforeEach(() => {
170         component.deletionForm.reset();
171         const ctrl = component.deletionForm.get('impact');
172         ctrl.setValue(DeletionImpact.normal);
173         ctrl.markAsDirty();
174         ctrl.updateValueAndValidity();
175         component.deletionForm.get('confirmation').updateValueAndValidity();
176       });
177
178       it('should test empty values', () => {
179         testValidation(true, 'required', true);
180         changeValue('confirmation', true);
181         changeValue('confirmation', false);
182         testValidation(true, 'required', true);
183       });
184     });
185
186     describe('validate confirmInput', () => {
187       const testValidation = (submitted: boolean, error: string, expected: boolean) => {
188         expect(
189           component.deletionForm.showError('confirmInput', <NgForm>{ submitted: submitted }, error)
190         ).toBe(expected);
191       };
192
193       beforeEach(() => {
194         component.deletionForm.reset();
195         const ctrl = component.deletionForm.get('impact');
196         ctrl.setValue(DeletionImpact.high);
197         ctrl.markAsDirty();
198         ctrl.updateValueAndValidity();
199         component.deletionForm.get('confirmInput').updateValueAndValidity();
200       });
201
202       it('should test empty values', () => {
203         testValidation(true, 'required', true);
204         changeValue('confirmInput', 'dummytext');
205         changeValue('confirmInput', '');
206         testValidation(true, 'required', true);
207       });
208
209       it('should give error, if entered resource name is wrong', () => {
210         component.itemNames = ['resource1'];
211         changeValue('confirmInput', 'dummytext');
212         testValidation(true, 'matchResource', true);
213       });
214
215       it('should give error, if entered resource name is correct', () => {
216         component.itemNames = ['resource1'];
217         changeValue('confirmInput', 'resource1');
218         testValidation(true, 'matchResource', false);
219       });
220     });
221
222     describe('deletion call', () => {
223       beforeEach(() => {
224         spyOn(component, 'stopLoadingSpinner').and.callThrough();
225         spyOn(component, 'hideModal').and.callThrough();
226       });
227
228       describe('Controller driven', () => {
229         beforeEach(() => {
230           spyOn(component, 'submitAction').and.callThrough();
231           spyOn(mockComponent.ctrlRef, 'close').and.callThrough();
232         });
233
234         it('should test fake deletion that closes modal', fakeAsync(() => {
235           // Before deletionCall
236           expect(component.submitAction).not.toHaveBeenCalled();
237           // During deletionCall
238           component.callSubmitAction();
239           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
240           expect(component.hideModal).not.toHaveBeenCalled();
241           expect(mockComponent.ctrlRef.close).not.toHaveBeenCalled();
242           expect(component.submitAction).toHaveBeenCalled();
243           expect(mockComponent.finished).toBe(undefined);
244           // After deletionCall
245           tick(2000);
246           expect(component.hideModal).not.toHaveBeenCalled();
247           expect(mockComponent.ctrlRef.close).toHaveBeenCalled();
248           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
249         }));
250       });
251
252       describe('Modal driven', () => {
253         beforeEach(() => {
254           mockComponent.openModalDriven();
255           spyOn(component, 'stopLoadingSpinner').and.callThrough();
256           spyOn(component, 'hideModal').and.callThrough();
257           spyOn(mockComponent, 'fakeDelete').and.callThrough();
258         });
259
260         it('should delete and close modal', fakeAsync(() => {
261           // During deletionCall
262           component.callSubmitAction();
263           expect(mockComponent.finished).toBe(undefined);
264           expect(component.hideModal).not.toHaveBeenCalled();
265           // After deletionCall
266           tick(2000);
267           expect(mockComponent.finished).toEqual([6, 7, 8, 9]);
268           expect(component.stopLoadingSpinner).not.toHaveBeenCalled();
269           expect(component.hideModal).toHaveBeenCalled();
270         }));
271       });
272     });
273   });
274 });