]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.spec.ts
import 15.2.0 Octopus source
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / rbd-form / rbd-form.component.spec.ts
CommitLineData
11fdf7f2 1import { HttpClientTestingModule } from '@angular/common/http/testing';
eafe8130 2import { ComponentFixture, discardPeriodicTasks, fakeAsync, TestBed } from '@angular/core/testing';
11fdf7f2 3import { ReactiveFormsModule } from '@angular/forms';
494da23a 4import { ActivatedRoute, Router } from '@angular/router';
11fdf7f2
TL
5import { RouterTestingModule } from '@angular/router/testing';
6import { TooltipModule } from 'ngx-bootstrap/tooltip';
7
494da23a 8import { ToastrModule } from 'ngx-toastr';
11fdf7f2
TL
9
10import { By } from '@angular/platform-browser';
494da23a 11import { of } from 'rxjs';
eafe8130
TL
12import { delay } from 'rxjs/operators';
13
11fdf7f2
TL
14import { ActivatedRouteStub } from '../../../../testing/activated-route-stub';
15import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
16import { RbdService } from '../../../shared/api/rbd.service';
9f95a23c 17import { ImageSpec } from '../../../shared/models/image-spec';
11fdf7f2
TL
18import { SharedModule } from '../../../shared/shared.module';
19import { RbdConfigurationFormComponent } from '../rbd-configuration-form/rbd-configuration-form.component';
9f95a23c 20import { RbdImageFeature } from './rbd-feature.interface';
11fdf7f2
TL
21import { RbdFormMode } from './rbd-form-mode.enum';
22import { RbdFormComponent } from './rbd-form.component';
23
24describe('RbdFormComponent', () => {
25 let component: RbdFormComponent;
26 let fixture: ComponentFixture<RbdFormComponent>;
27 let activatedRoute: ActivatedRouteStub;
28
9f95a23c 29 const queryNativeElement = (cssSelector: string) =>
494da23a
TL
30 fixture.debugElement.query(By.css(cssSelector)).nativeElement;
31
11fdf7f2
TL
32 configureTestBed({
33 imports: [
34 HttpClientTestingModule,
35 ReactiveFormsModule,
36 RouterTestingModule,
494da23a 37 ToastrModule.forRoot(),
11fdf7f2
TL
38 SharedModule,
39 TooltipModule
40 ],
41 declarations: [RbdFormComponent, RbdConfigurationFormComponent],
42 providers: [
43 {
44 provide: ActivatedRoute,
45 useValue: new ActivatedRouteStub({ pool: 'foo', name: 'bar', snap: undefined })
46 },
494da23a
TL
47 i18nProviders,
48 RbdService
11fdf7f2
TL
49 ]
50 });
51
52 beforeEach(() => {
53 fixture = TestBed.createComponent(RbdFormComponent);
54 component = fixture.componentInstance;
55 activatedRoute = TestBed.get(ActivatedRoute);
56 });
57
58 it('should create', () => {
59 expect(component).toBeTruthy();
60 });
61
eafe8130 62 describe('create/edit/clone/copy image', () => {
9f95a23c
TL
63 let createAction: jasmine.Spy;
64 let editAction: jasmine.Spy;
65 let cloneAction: jasmine.Spy;
66 let copyAction: jasmine.Spy;
67 let rbdServiceGetSpy: jasmine.Spy;
eafe8130
TL
68
69 beforeEach(() => {
70 createAction = spyOn(component, 'createAction').and.stub();
71 editAction = spyOn(component, 'editAction').and.stub();
72 cloneAction = spyOn(component, 'cloneAction').and.stub();
73 copyAction = spyOn(component, 'copyAction').and.stub();
74 spyOn(component, 'setResponse').and.stub();
75 spyOn(TestBed.get(Router), 'navigate').and.stub();
76 rbdServiceGetSpy = spyOn(TestBed.get(RbdService), 'get');
77 rbdServiceGetSpy.and.returnValue(of({ pool_name: 'foo', pool_image: 'bar' }));
78 component.mode = undefined;
79 });
80
81 it('should create image', () => {
82 component.ngOnInit();
83 component.submit();
84
85 expect(createAction).toHaveBeenCalledTimes(1);
86 expect(editAction).toHaveBeenCalledTimes(0);
87 expect(cloneAction).toHaveBeenCalledTimes(0);
88 expect(copyAction).toHaveBeenCalledTimes(0);
89 });
90
91 it('should not edit image if no image data is received', fakeAsync(() => {
92 component.mode = RbdFormMode.editing;
93 rbdServiceGetSpy.and.returnValue(
94 of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(100))
95 );
96 component.ngOnInit();
97 component.submit();
98
99 expect(createAction).toHaveBeenCalledTimes(0);
100 expect(editAction).toHaveBeenCalledTimes(0);
101 expect(cloneAction).toHaveBeenCalledTimes(0);
102 expect(copyAction).toHaveBeenCalledTimes(0);
103
104 discardPeriodicTasks();
105 }));
106
107 it('should edit image after image data is received', () => {
108 component.mode = RbdFormMode.editing;
109 component.ngOnInit();
110 component.submit();
111
112 expect(createAction).toHaveBeenCalledTimes(0);
113 expect(editAction).toHaveBeenCalledTimes(1);
114 expect(cloneAction).toHaveBeenCalledTimes(0);
115 expect(copyAction).toHaveBeenCalledTimes(0);
116 });
117
118 it('should not clone image if no image data is received', fakeAsync(() => {
119 component.mode = RbdFormMode.cloning;
120 rbdServiceGetSpy.and.returnValue(
121 of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(100))
122 );
123 component.ngOnInit();
124 component.submit();
125
126 expect(createAction).toHaveBeenCalledTimes(0);
127 expect(editAction).toHaveBeenCalledTimes(0);
128 expect(cloneAction).toHaveBeenCalledTimes(0);
129 expect(copyAction).toHaveBeenCalledTimes(0);
130
131 discardPeriodicTasks();
132 }));
133
134 it('should clone image after image data is received', () => {
135 component.mode = RbdFormMode.cloning;
136 component.ngOnInit();
137 component.submit();
138
139 expect(createAction).toHaveBeenCalledTimes(0);
140 expect(editAction).toHaveBeenCalledTimes(0);
141 expect(cloneAction).toHaveBeenCalledTimes(1);
142 expect(copyAction).toHaveBeenCalledTimes(0);
143 });
144
145 it('should not copy image if no image data is received', fakeAsync(() => {
146 component.mode = RbdFormMode.copying;
147 rbdServiceGetSpy.and.returnValue(
148 of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(100))
149 );
150 component.ngOnInit();
151 component.submit();
152
153 expect(createAction).toHaveBeenCalledTimes(0);
154 expect(editAction).toHaveBeenCalledTimes(0);
155 expect(cloneAction).toHaveBeenCalledTimes(0);
156 expect(copyAction).toHaveBeenCalledTimes(0);
157
158 discardPeriodicTasks();
159 }));
160
161 it('should copy image after image data is received', () => {
162 component.mode = RbdFormMode.copying;
163 component.ngOnInit();
164 component.submit();
165
166 expect(createAction).toHaveBeenCalledTimes(0);
167 expect(editAction).toHaveBeenCalledTimes(0);
168 expect(cloneAction).toHaveBeenCalledTimes(0);
169 expect(copyAction).toHaveBeenCalledTimes(1);
170 });
171 });
172
11fdf7f2
TL
173 describe('should test decodeURIComponent of params', () => {
174 let rbdService: RbdService;
175
176 beforeEach(() => {
177 rbdService = TestBed.get(RbdService);
178 component.mode = RbdFormMode.editing;
179 fixture.detectChanges();
180 spyOn(rbdService, 'get').and.callThrough();
181 });
182
9f95a23c
TL
183 it('with namespace', () => {
184 activatedRoute.setParams({ image_spec: 'foo%2Fbar%2Fbaz' });
185
186 expect(rbdService.get).toHaveBeenCalledWith(new ImageSpec('foo', 'bar', 'baz'));
187 });
188
11fdf7f2 189 it('without snapName', () => {
9f95a23c 190 activatedRoute.setParams({ image_spec: 'foo%2Fbar', snap: undefined });
11fdf7f2 191
9f95a23c 192 expect(rbdService.get).toHaveBeenCalledWith(new ImageSpec('foo', null, 'bar'));
11fdf7f2
TL
193 expect(component.snapName).toBeUndefined();
194 });
195
196 it('with snapName', () => {
9f95a23c 197 activatedRoute.setParams({ image_spec: 'foo%2Fbar', snap: 'baz%2Fbaz' });
11fdf7f2 198
9f95a23c 199 expect(rbdService.get).toHaveBeenCalledWith(new ImageSpec('foo', null, 'bar'));
11fdf7f2
TL
200 expect(component.snapName).toBe('baz/baz');
201 });
202 });
203
204 describe('test image configuration component', () => {
205 it('is visible', () => {
206 fixture.detectChanges();
9f95a23c
TL
207 expect(
208 fixture.debugElement.query(By.css('cd-rbd-configuration-form')).nativeElement.parentElement
209 .hidden
210 ).toBe(true);
494da23a
TL
211 });
212 });
213
214 describe('tests for feature flags', () => {
9f95a23c
TL
215 let deepFlatten: any,
216 layering: any,
217 exclusiveLock: any,
218 objectMap: any,
219 journaling: any,
220 fastDiff: any;
494da23a
TL
221 const defaultFeatures = [
222 // Supposed to be enabled by default
223 'deep-flatten',
224 'exclusive-lock',
225 'fast-diff',
226 'layering',
227 'object-map'
228 ];
229 const allFeatureNames = [
230 'deep-flatten',
231 'layering',
232 'exclusive-lock',
233 'object-map',
234 'journaling',
235 'fast-diff'
236 ];
9f95a23c 237 const setFeatures = (features: Record<string, RbdImageFeature>) => {
494da23a
TL
238 component.features = features;
239 component.featuresList = component.objToArray(features);
240 component.createForm();
241 };
242 const getFeatureNativeElements = () => allFeatureNames.map((f) => queryNativeElement(`#${f}`));
243
244 it('should convert feature flags correctly in the constructor', () => {
245 setFeatures({
246 one: { desc: 'one', allowEnable: true, allowDisable: true },
247 two: { desc: 'two', allowEnable: true, allowDisable: true },
248 three: { desc: 'three', allowEnable: true, allowDisable: true }
249 });
250 expect(component.featuresList).toEqual([
251 { desc: 'one', key: 'one', allowDisable: true, allowEnable: true },
252 { desc: 'two', key: 'two', allowDisable: true, allowEnable: true },
253 { desc: 'three', key: 'three', allowDisable: true, allowEnable: true }
254 ]);
255 });
256
257 describe('test edit form flags', () => {
258 const prepare = (pool: string, image: string, enabledFeatures: string[]): void => {
259 const rbdService = TestBed.get(RbdService);
260 spyOn(rbdService, 'get').and.returnValue(
261 of({
262 name: image,
263 pool_name: pool,
264 features_name: enabledFeatures
265 })
266 );
267 spyOn(rbdService, 'defaultFeatures').and.returnValue(of(defaultFeatures));
268 component.router = { url: `/block/rbd/edit/${pool}/${image}` } as Router;
269 fixture.detectChanges();
270 [
271 deepFlatten,
272 layering,
273 exclusiveLock,
274 objectMap,
275 journaling,
276 fastDiff
277 ] = getFeatureNativeElements();
278 };
279
280 it('should have the interlock feature for flags disabled, if one feature is not set', () => {
281 prepare('rbd', 'foobar', ['deep-flatten', 'exclusive-lock', 'layering', 'object-map']);
282
283 expect(objectMap.disabled).toBe(false);
284 expect(fastDiff.disabled).toBe(false);
285
286 expect(objectMap.checked).toBe(true);
287 expect(fastDiff.checked).toBe(false);
288
289 fastDiff.click();
290 fastDiff.click();
291
292 expect(objectMap.checked).toBe(true); // Shall not be disabled by `fast-diff`!
293 });
294
295 it('should not disable object-map when fast-diff is unchecked', () => {
296 prepare('rbd', 'foobar', ['deep-flatten', 'exclusive-lock', 'layering', 'object-map']);
297
298 fastDiff.click();
299 fastDiff.click();
300
301 expect(objectMap.checked).toBe(true); // Shall not be disabled by `fast-diff`!
302 });
303
304 it('should not enable fast-diff when object-map is checked', () => {
305 prepare('rbd', 'foobar', ['deep-flatten', 'exclusive-lock', 'layering', 'object-map']);
306
307 objectMap.click();
308 objectMap.click();
309
310 expect(fastDiff.checked).toBe(false); // Shall not be disabled by `fast-diff`!
311 });
312 });
313
314 describe('test create form flags', () => {
315 beforeEach(() => {
316 const rbdService = TestBed.get(RbdService);
317 spyOn(rbdService, 'defaultFeatures').and.returnValue(of(defaultFeatures));
318 component.router = { url: '/block/rbd/create' } as Router;
319 fixture.detectChanges();
320 [
321 deepFlatten,
322 layering,
323 exclusiveLock,
324 objectMap,
325 journaling,
326 fastDiff
327 ] = getFeatureNativeElements();
328 });
329
330 it('should initialize the checkboxes correctly', () => {
331 expect(deepFlatten.disabled).toBe(false);
332 expect(layering.disabled).toBe(false);
333 expect(exclusiveLock.disabled).toBe(false);
334 expect(objectMap.disabled).toBe(false);
335 expect(journaling.disabled).toBe(false);
336 expect(fastDiff.disabled).toBe(false);
337
338 expect(deepFlatten.checked).toBe(true);
339 expect(layering.checked).toBe(true);
340 expect(exclusiveLock.checked).toBe(true);
341 expect(objectMap.checked).toBe(true);
342 expect(journaling.checked).toBe(false);
343 expect(fastDiff.checked).toBe(true);
344 });
345
346 it('should disable features if their requirements are not met (exclusive-lock)', () => {
347 exclusiveLock.click(); // unchecks exclusive-lock
348 expect(objectMap.disabled).toBe(true);
349 expect(journaling.disabled).toBe(true);
350 expect(fastDiff.disabled).toBe(true);
351 });
352
353 it('should disable features if their requirements are not met (object-map)', () => {
354 objectMap.click(); // unchecks object-map
355 expect(fastDiff.disabled).toBe(true);
356 });
11fdf7f2
TL
357 });
358 });
359});