1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3 import { By } from '@angular/platform-browser';
4 import { RouterTestingModule } from '@angular/router/testing';
6 import { I18n } from '@ngx-translate/i18n-polyfill';
7 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
8 import { ToastrModule } from 'ngx-toastr';
9 import { Subject, throwError as observableThrowError } from 'rxjs';
16 } from '../../../../testing/unit-test-helper';
17 import { ApiModule } from '../../../shared/api/api.module';
18 import { RbdService } from '../../../shared/api/rbd.service';
19 import { ComponentsModule } from '../../../shared/components/components.module';
20 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
21 import { DataTableModule } from '../../../shared/datatable/datatable.module';
22 import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
23 import { ExecutingTask } from '../../../shared/models/executing-task';
24 import { Permissions } from '../../../shared/models/permissions';
25 import { PipesModule } from '../../../shared/pipes/pipes.module';
26 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
27 import { NotificationService } from '../../../shared/services/notification.service';
28 import { SummaryService } from '../../../shared/services/summary.service';
29 import { TaskListService } from '../../../shared/services/task-list.service';
30 import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
31 import { RbdSnapshotListComponent } from './rbd-snapshot-list.component';
32 import { RbdSnapshotModel } from './rbd-snapshot.model';
34 describe('RbdSnapshotListComponent', () => {
35 let component: RbdSnapshotListComponent;
36 let fixture: ComponentFixture<RbdSnapshotListComponent>;
37 let summaryService: SummaryService;
39 const fakeAuthStorageService = {
43 getPermissions: () => {
44 return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] });
49 declarations: [RbdSnapshotListComponent],
53 ToastrModule.forRoot(),
55 HttpClientTestingModule,
60 { provide: AuthStorageService, useValue: fakeAuthStorageService },
67 fixture = TestBed.createComponent(RbdSnapshotListComponent);
68 component = fixture.componentInstance;
69 summaryService = TestBed.get(SummaryService);
72 it('should create', () => {
73 fixture.detectChanges();
74 expect(component).toBeTruthy();
77 describe('api delete request', () => {
79 let rbdService: RbdService;
80 let notificationService: NotificationService;
81 let authStorageService: AuthStorageService;
84 fixture.detectChanges();
85 const i18n = TestBed.get(I18n);
86 const actionLabelsI18n = TestBed.get(ActionLabelsI18n);
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(
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)
113 it('should call stopLoadingSpinner if the request fails', <any>fakeAsync(() => {
114 expect(called).toBe(false);
115 component._asyncTask('deleteSnapshot', 'rbd/snap/delete', 'someName');
117 expect(called).toBe(true);
121 describe('handling of executing tasks', () => {
122 let snapshots: RbdSnapshotModel[];
124 const addSnapshot = (name) => {
125 const model = new RbdSnapshotModel();
128 snapshots.push(model);
131 const addTask = (task_name: string, snapshot_name: string) => {
132 const task = new ExecutingTask();
133 task.name = task_name;
137 snapshot_name: snapshot_name
139 summaryService.addRunningTask(task);
142 const refresh = (data) => {
143 summaryService['summaryDataSource'].next(data);
147 fixture.detectChanges();
152 component.snapshots = snapshots;
153 component.poolName = 'rbd';
154 component.rbdName = 'foo';
155 refresh({ executing_tasks: [], finished_tasks: [] });
156 component.ngOnChanges();
157 fixture.detectChanges();
160 it('should gets all snapshots without tasks', () => {
161 expect(component.snapshots.length).toBe(3);
162 expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
165 it('should add a new image from a task', () => {
166 addTask('rbd/snap/create', 'd');
167 expect(component.snapshots.length).toBe(4);
168 expectItemTasks(component.snapshots[0], undefined);
169 expectItemTasks(component.snapshots[1], undefined);
170 expectItemTasks(component.snapshots[2], undefined);
171 expectItemTasks(component.snapshots[3], 'Creating');
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);
179 expectItemTasks(component.snapshots[0], 'Updating');
180 expectItemTasks(component.snapshots[1], 'Deleting');
181 expectItemTasks(component.snapshots[2], 'Rolling back');
185 describe('snapshot modal dialog', () => {
187 component.poolName = 'pool01';
188 component.rbdName = 'image01';
189 spyOn(TestBed.get(BsModalService), 'show').and.callFake(() => {
190 const ref = new BsModalRef();
191 ref.content = new RbdSnapshotFormComponent(
197 TestBed.get(ActionLabelsI18n)
199 ref.content.onSubmit = new Subject();
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();
212 it('should display suggested snapshot name', () => {
213 component.openCreateSnapshotModal();
214 expect(component.modalRef.content.snapName).toMatch(
215 RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+\\+[\\d:]+\$`)
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;
225 const getTableActionComponent = (): TableActionsComponent => {
226 fixture.detectChanges();
227 return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
231 permissionHelper = new PermissionHelper(component.permission, () =>
232 getTableActionComponent()
235 fn: () => tableActions.getCurrentButton().name,
241 describe('with all', () => {
243 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
246 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
247 permissionHelper.testScenarios(scenario));
249 it('shows all actions', () => {
250 expect(tableActions.tableActions.length).toBe(8);
251 expect(tableActions.tableActions).toEqual(component.tableActions);
255 describe('with read, create and update', () => {
257 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
260 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
261 permissionHelper.testScenarios(scenario));
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);
270 describe('with read, create and delete', () => {
272 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
275 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
276 scenario.single = 'Clone';
277 permissionHelper.testScenarios(scenario);
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]
291 describe('with read, edit and delete', () => {
293 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
296 it(`shows always 'Rename' as main action`, () => {
297 scenario.empty = 'Rename';
298 permissionHelper.testScenarios(scenario);
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]
313 describe('with read and create', () => {
315 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
318 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
319 scenario.single = 'Clone';
320 permissionHelper.testScenarios(scenario);
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]
333 describe('with read and edit', () => {
335 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
338 it(`shows always 'Rename' as main action`, () => {
339 scenario.empty = 'Rename';
340 permissionHelper.testScenarios(scenario);
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]
354 describe('with read and delete', () => {
356 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
359 it(`shows always 'Delete' as main action`, () => {
360 scenario.single = 'Delete';
361 scenario.empty = 'Delete';
362 permissionHelper.testScenarios(scenario);
365 it(`shows only 'Delete' action`, () => {
366 expect(tableActions.tableActions.length).toBe(1);
367 expect(tableActions.tableActions).toEqual([component.tableActions[7]]);
371 describe('with only read', () => {
373 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
376 it('shows no main action', () => {
377 permissionHelper.testScenarios({
378 fn: () => tableActions.getCurrentButton(),
384 it('shows no actions', () => {
385 expect(tableActions.tableActions.length).toBe(0);
386 expect(tableActions.tableActions).toEqual([]);
390 describe('test unprotected and protected action cases', () => {
392 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
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]
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]
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]