]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / rbd-snapshot-list / rbd-snapshot-list.component.spec.ts
CommitLineData
11fdf7f2
TL
1import { HttpClientTestingModule } from '@angular/common/http/testing';
2import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3import { By } from '@angular/platform-browser';
4import { RouterTestingModule } from '@angular/router/testing';
5
6import { I18n } from '@ngx-translate/i18n-polyfill';
11fdf7f2 7import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
494da23a 8import { ToastrModule } from 'ngx-toastr';
11fdf7f2
TL
9import { Subject, throwError as observableThrowError } from 'rxjs';
10
11import {
12 configureTestBed,
eafe8130 13 expectItemTasks,
11fdf7f2
TL
14 i18nProviders,
15 PermissionHelper
16} from '../../../../testing/unit-test-helper';
17import { ApiModule } from '../../../shared/api/api.module';
18import { RbdService } from '../../../shared/api/rbd.service';
19import { ComponentsModule } from '../../../shared/components/components.module';
eafe8130 20import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
11fdf7f2
TL
21import { DataTableModule } from '../../../shared/datatable/datatable.module';
22import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
23import { ExecutingTask } from '../../../shared/models/executing-task';
24import { Permissions } from '../../../shared/models/permissions';
25import { PipesModule } from '../../../shared/pipes/pipes.module';
26import { AuthStorageService } from '../../../shared/services/auth-storage.service';
27import { NotificationService } from '../../../shared/services/notification.service';
11fdf7f2
TL
28import { SummaryService } from '../../../shared/services/summary.service';
29import { TaskListService } from '../../../shared/services/task-list.service';
92f5a8d4 30import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
11fdf7f2
TL
31import { RbdSnapshotListComponent } from './rbd-snapshot-list.component';
32import { RbdSnapshotModel } from './rbd-snapshot.model';
33
34describe('RbdSnapshotListComponent', () => {
35 let component: RbdSnapshotListComponent;
36 let fixture: ComponentFixture<RbdSnapshotListComponent>;
37 let summaryService: SummaryService;
38
39 const fakeAuthStorageService = {
40 isLoggedIn: () => {
41 return true;
42 },
43 getPermissions: () => {
44 return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] });
45 }
46 };
47
48 configureTestBed({
49 declarations: [RbdSnapshotListComponent],
50 imports: [
51 DataTableModule,
52 ComponentsModule,
494da23a 53 ToastrModule.forRoot(),
11fdf7f2
TL
54 ApiModule,
55 HttpClientTestingModule,
56 RouterTestingModule,
57 PipesModule
58 ],
59 providers: [
60 { provide: AuthStorageService, useValue: fakeAuthStorageService },
61 TaskListService,
62 i18nProviders
63 ]
64 });
65
66 beforeEach(() => {
67 fixture = TestBed.createComponent(RbdSnapshotListComponent);
68 component = fixture.componentInstance;
69 summaryService = TestBed.get(SummaryService);
70 });
71
72 it('should create', () => {
73 fixture.detectChanges();
74 expect(component).toBeTruthy();
75 });
76
77 describe('api delete request', () => {
78 let called;
79 let rbdService: RbdService;
80 let notificationService: NotificationService;
81 let authStorageService: AuthStorageService;
82
83 beforeEach(() => {
84 fixture.detectChanges();
85 const i18n = TestBed.get(I18n);
eafe8130 86 const actionLabelsI18n = TestBed.get(ActionLabelsI18n);
11fdf7f2
TL
87 called = false;
88 rbdService = new RbdService(null, null);
89 notificationService = new NotificationService(null, null, null);
90 authStorageService = new AuthStorageService();
91 authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
92 component = new RbdSnapshotListComponent(
93 authStorageService,
94 null,
95 null,
96 null,
97 rbdService,
98 null,
99 notificationService,
100 null,
101 null,
eafe8130
TL
102 i18n,
103 actionLabelsI18n
11fdf7f2
TL
104 );
105 spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
106 spyOn(notificationService, 'notifyTask').and.stub();
107 component.modalRef = new BsModalRef();
108 component.modalRef.content = {
109 stopLoadingSpinner: () => (called = true)
110 };
111 });
112
113 it('should call stopLoadingSpinner if the request fails', <any>fakeAsync(() => {
114 expect(called).toBe(false);
115 component._asyncTask('deleteSnapshot', 'rbd/snap/delete', 'someName');
116 tick(500);
117 expect(called).toBe(true);
118 }));
119 });
120
121 describe('handling of executing tasks', () => {
122 let snapshots: RbdSnapshotModel[];
123
124 const addSnapshot = (name) => {
125 const model = new RbdSnapshotModel();
126 model.id = 1;
127 model.name = name;
128 snapshots.push(model);
129 };
130
131 const addTask = (task_name: string, snapshot_name: string) => {
132 const task = new ExecutingTask();
133 task.name = task_name;
134 task.metadata = {
135 pool_name: 'rbd',
136 image_name: 'foo',
137 snapshot_name: snapshot_name
138 };
139 summaryService.addRunningTask(task);
140 };
141
11fdf7f2
TL
142 const refresh = (data) => {
143 summaryService['summaryDataSource'].next(data);
144 };
145
146 beforeEach(() => {
147 fixture.detectChanges();
148 snapshots = [];
149 addSnapshot('a');
150 addSnapshot('b');
151 addSnapshot('c');
152 component.snapshots = snapshots;
153 component.poolName = 'rbd';
154 component.rbdName = 'foo';
155 refresh({ executing_tasks: [], finished_tasks: [] });
156 component.ngOnChanges();
157 fixture.detectChanges();
158 });
159
160 it('should gets all snapshots without tasks', () => {
161 expect(component.snapshots.length).toBe(3);
162 expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
163 });
164
165 it('should add a new image from a task', () => {
166 addTask('rbd/snap/create', 'd');
167 expect(component.snapshots.length).toBe(4);
eafe8130
TL
168 expectItemTasks(component.snapshots[0], undefined);
169 expectItemTasks(component.snapshots[1], undefined);
170 expectItemTasks(component.snapshots[2], undefined);
171 expectItemTasks(component.snapshots[3], 'Creating');
11fdf7f2
TL
172 });
173
174 it('should show when an existing image is being modified', () => {
175 addTask('rbd/snap/edit', 'a');
176 addTask('rbd/snap/delete', 'b');
177 addTask('rbd/snap/rollback', 'c');
178 expect(component.snapshots.length).toBe(3);
eafe8130
TL
179 expectItemTasks(component.snapshots[0], 'Updating');
180 expectItemTasks(component.snapshots[1], 'Deleting');
181 expectItemTasks(component.snapshots[2], 'Rolling back');
11fdf7f2
TL
182 });
183 });
184
185 describe('snapshot modal dialog', () => {
186 beforeEach(() => {
187 component.poolName = 'pool01';
188 component.rbdName = 'image01';
92f5a8d4 189 spyOn(TestBed.get(BsModalService), 'show').and.callFake(() => {
11fdf7f2 190 const ref = new BsModalRef();
92f5a8d4
TL
191 ref.content = new RbdSnapshotFormComponent(
192 null,
193 null,
194 null,
195 null,
196 TestBed.get(I18n),
197 TestBed.get(ActionLabelsI18n)
198 );
11fdf7f2
TL
199 ref.content.onSubmit = new Subject();
200 return ref;
201 });
202 });
203
204 it('should display old snapshot name', () => {
205 component.selection.selected = [{ name: 'oldname' }];
206 component.selection.update();
207 component.openEditSnapshotModal();
208 expect(component.modalRef.content.snapName).toBe('oldname');
209 expect(component.modalRef.content.editing).toBeTruthy();
210 });
211
212 it('should display suggested snapshot name', () => {
213 component.openCreateSnapshotModal();
214 expect(component.modalRef.content.snapName).toMatch(
81eedcae 215 RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+\\+[\\d:]+\$`)
11fdf7f2
TL
216 );
217 });
218 });
219
220 describe('show action buttons and drop down actions depending on permissions', () => {
221 let tableActions: TableActionsComponent;
222 let scenario: { fn; empty; single };
223 let permissionHelper: PermissionHelper;
224
225 const getTableActionComponent = (): TableActionsComponent => {
226 fixture.detectChanges();
227 return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
228 };
229
230 beforeEach(() => {
231 permissionHelper = new PermissionHelper(component.permission, () =>
232 getTableActionComponent()
233 );
234 scenario = {
235 fn: () => tableActions.getCurrentButton().name,
236 single: 'Rename',
237 empty: 'Create'
238 };
239 });
240
241 describe('with all', () => {
242 beforeEach(() => {
243 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
244 });
245
246 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
247 permissionHelper.testScenarios(scenario));
248
249 it('shows all actions', () => {
250 expect(tableActions.tableActions.length).toBe(8);
251 expect(tableActions.tableActions).toEqual(component.tableActions);
252 });
253 });
254
255 describe('with read, create and update', () => {
256 beforeEach(() => {
257 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
258 });
259
260 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
261 permissionHelper.testScenarios(scenario));
262
263 it(`shows all actions except for 'Delete'`, () => {
264 expect(tableActions.tableActions.length).toBe(7);
265 component.tableActions.pop();
266 expect(tableActions.tableActions).toEqual(component.tableActions);
267 });
268 });
269
270 describe('with read, create and delete', () => {
271 beforeEach(() => {
272 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
273 });
274
275 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
276 scenario.single = 'Clone';
277 permissionHelper.testScenarios(scenario);
278 });
279
280 it(`shows 'Create', 'Clone', 'Copy' and 'Delete' action`, () => {
281 expect(tableActions.tableActions.length).toBe(4);
282 expect(tableActions.tableActions).toEqual([
283 component.tableActions[0],
284 component.tableActions[4],
285 component.tableActions[5],
286 component.tableActions[7]
287 ]);
288 });
289 });
290
291 describe('with read, edit and delete', () => {
292 beforeEach(() => {
293 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
294 });
295
296 it(`shows always 'Rename' as main action`, () => {
297 scenario.empty = 'Rename';
298 permissionHelper.testScenarios(scenario);
299 });
300
301 it(`shows 'Rename', 'Protect', 'Unprotect', 'Rollback' and 'Delete' action`, () => {
302 expect(tableActions.tableActions.length).toBe(5);
303 expect(tableActions.tableActions).toEqual([
304 component.tableActions[1],
305 component.tableActions[2],
306 component.tableActions[3],
307 component.tableActions[6],
308 component.tableActions[7]
309 ]);
310 });
311 });
312
313 describe('with read and create', () => {
314 beforeEach(() => {
315 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
316 });
317
318 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
319 scenario.single = 'Clone';
320 permissionHelper.testScenarios(scenario);
321 });
322
323 it(`shows 'Create', 'Clone' and 'Copy' actions`, () => {
324 expect(tableActions.tableActions.length).toBe(3);
325 expect(tableActions.tableActions).toEqual([
326 component.tableActions[0],
327 component.tableActions[4],
328 component.tableActions[5]
329 ]);
330 });
331 });
332
333 describe('with read and edit', () => {
334 beforeEach(() => {
335 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
336 });
337
338 it(`shows always 'Rename' as main action`, () => {
339 scenario.empty = 'Rename';
340 permissionHelper.testScenarios(scenario);
341 });
342
343 it(`shows 'Rename', 'Protect', 'Unprotect' and 'Rollback' actions`, () => {
344 expect(tableActions.tableActions.length).toBe(4);
345 expect(tableActions.tableActions).toEqual([
346 component.tableActions[1],
347 component.tableActions[2],
348 component.tableActions[3],
349 component.tableActions[6]
350 ]);
351 });
352 });
353
354 describe('with read and delete', () => {
355 beforeEach(() => {
356 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
357 });
358
359 it(`shows always 'Delete' as main action`, () => {
360 scenario.single = 'Delete';
361 scenario.empty = 'Delete';
362 permissionHelper.testScenarios(scenario);
363 });
364
365 it(`shows only 'Delete' action`, () => {
366 expect(tableActions.tableActions.length).toBe(1);
367 expect(tableActions.tableActions).toEqual([component.tableActions[7]]);
368 });
369 });
370
371 describe('with only read', () => {
372 beforeEach(() => {
373 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
374 });
375
376 it('shows no main action', () => {
377 permissionHelper.testScenarios({
378 fn: () => tableActions.getCurrentButton(),
379 single: undefined,
380 empty: undefined
381 });
382 });
383
384 it('shows no actions', () => {
385 expect(tableActions.tableActions.length).toBe(0);
386 expect(tableActions.tableActions).toEqual([]);
387 });
388 });
389
390 describe('test unprotected and protected action cases', () => {
391 beforeEach(() => {
392 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
393 });
394
395 it(`shows none of them if nothing is selected`, () => {
396 permissionHelper.setSelection([]);
397 fixture.detectChanges();
398 expect(tableActions.dropDownActions).toEqual([
399 component.tableActions[1],
400 component.tableActions[6]
401 ]);
402 });
403
404 it(`shows 'Protect' of them if nothing is selected`, () => {
405 permissionHelper.setSelection([{ is_protected: false }]);
406 fixture.detectChanges();
407 expect(tableActions.dropDownActions).toEqual([
408 component.tableActions[1],
409 component.tableActions[2],
410 component.tableActions[6]
411 ]);
412 });
413
414 it(`shows 'Unprotect' of them if nothing is selected`, () => {
415 permissionHelper.setSelection([{ is_protected: true }]);
416 fixture.detectChanges();
417 expect(tableActions.dropDownActions).toEqual([
418 component.tableActions[1],
419 component.tableActions[3],
420 component.tableActions[6]
421 ]);
422 });
423 });
424 });
425});