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