]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
11fdf7f2
TL
1import { Component, NgModule, NO_ERRORS_SCHEMA, TemplateRef, ViewChild } from '@angular/core';
2import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3import { NgForm, ReactiveFormsModule } from '@angular/forms';
4import { By } from '@angular/platform-browser';
5
6import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal';
7import { Observable, Subscriber, timer as observableTimer } from 'rxjs';
8
9f95a23c 9import { configureTestBed, modalServiceShow } from '../../../../testing/unit-test-helper';
11fdf7f2
TL
10import { DirectivesModule } from '../../directives/directives.module';
11import { CriticalConfirmationModalComponent } from './critical-confirmation-modal.component';
12
13@NgModule({
14 entryComponents: [CriticalConfirmationModalComponent]
15})
16export class MockModule {}
17
18@Component({
19 template: `
9f95a23c
TL
20 <button type="button" class="btn btn-secondary" (click)="openCtrlDriven()">
21 <i class="fa fa-times"></i>Deletion Ctrl-Test
11fdf7f2
TL
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
9f95a23c
TL
28 <button type="button" class="btn btn-secondary" (click)="openModalDriven()">
29 <i class="fa fa-times"></i>Deletion Modal-Test
11fdf7f2
TL
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})
36class MockComponent {
9f95a23c 37 @ViewChild('ctrlDescription', { static: true })
11fdf7f2 38 ctrlDescription: TemplateRef<any>;
9f95a23c 39 @ViewChild('modalDescription', { static: true })
11fdf7f2
TL
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
90describe('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;
11fdf7f2 106 spyOn(mockComponent.modalService, 'show').and.callFake((_modalComp, config) => {
9f95a23c
TL
107 const data = modalServiceShow(CriticalConfirmationModalComponent, config);
108 fixture = data.fixture;
109 component = data.component;
110 return data.ref;
11fdf7f2
TL
111 });
112 mockComponent.openCtrlDriven();
113 mockFixture.detectChanges();
114 });
115
116 it('should create', () => {
117 expect(component).toBeTruthy();
118 });
119
9f95a23c 120 it('should focus the checkbox form field', (done) => {
11fdf7f2
TL
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();
9f95a23c 128 done();
11fdf7f2
TL
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', () => {
9f95a23c 154 const changeValue = (value: boolean) => {
11fdf7f2
TL
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
801d1391 205 it('should test fake deletion that closes modal', fakeAsync(() => {
11fdf7f2
TL
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
801d1391 231 it('should delete and close modal', fakeAsync(() => {
11fdf7f2
TL
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});