]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component.spec.ts
import 15.2.1 Octopus source
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / shared / components / critical-confirmation-modal / critical-confirmation-modal.component.spec.ts
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', 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', 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 });