]> 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
update sources to ceph Nautilus 14.2.1
[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 } 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-sm btn-primary" (click)="openCtrlDriven()">
21 <i class="fa fa-fw 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-sm btn-primary" (click)="openModalDriven()">
29 <i class="fa fa-fw 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 });