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';
15 } from '../../../../testing/unit-test-helper';
16 import { ApiModule } from '../../../shared/api/api.module';
17 import { RbdService } from '../../../shared/api/rbd.service';
18 import { ComponentsModule } from '../../../shared/components/components.module';
19 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
20 import { DataTableModule } from '../../../shared/datatable/datatable.module';
21 import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
22 import { ExecutingTask } from '../../../shared/models/executing-task';
23 import { Permissions } from '../../../shared/models/permissions';
24 import { PipesModule } from '../../../shared/pipes/pipes.module';
25 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
26 import { NotificationService } from '../../../shared/services/notification.service';
27 import { SummaryService } from '../../../shared/services/summary.service';
28 import { TaskListService } from '../../../shared/services/task-list.service';
29 import { RbdSnapshotListComponent } from './rbd-snapshot-list.component';
30 import { RbdSnapshotModel } from './rbd-snapshot.model';
32 describe('RbdSnapshotListComponent', () => {
33 let component: RbdSnapshotListComponent;
34 let fixture: ComponentFixture<RbdSnapshotListComponent>;
35 let summaryService: SummaryService;
37 const fakeAuthStorageService = {
41 getPermissions: () => {
42 return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] });
47 declarations: [RbdSnapshotListComponent],
51 ToastrModule.forRoot(),
53 HttpClientTestingModule,
58 { provide: AuthStorageService, useValue: fakeAuthStorageService },
65 fixture = TestBed.createComponent(RbdSnapshotListComponent);
66 component = fixture.componentInstance;
67 summaryService = TestBed.get(SummaryService);
70 it('should create', () => {
71 fixture.detectChanges();
72 expect(component).toBeTruthy();
75 describe('api delete request', () => {
77 let rbdService: RbdService;
78 let notificationService: NotificationService;
79 let authStorageService: AuthStorageService;
82 fixture.detectChanges();
83 const i18n = TestBed.get(I18n);
84 const actionLabelsI18n = TestBed.get(ActionLabelsI18n);
86 rbdService = new RbdService(null, null);
87 notificationService = new NotificationService(null, null, null);
88 authStorageService = new AuthStorageService();
89 authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
90 component = new RbdSnapshotListComponent(
103 spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
104 spyOn(notificationService, 'notifyTask').and.stub();
105 component.modalRef = new BsModalRef();
106 component.modalRef.content = {
107 stopLoadingSpinner: () => (called = true)
111 it('should call stopLoadingSpinner if the request fails', <any>fakeAsync(() => {
112 expect(called).toBe(false);
113 component._asyncTask('deleteSnapshot', 'rbd/snap/delete', 'someName');
115 expect(called).toBe(true);
119 describe('handling of executing tasks', () => {
120 let snapshots: RbdSnapshotModel[];
122 const addSnapshot = (name) => {
123 const model = new RbdSnapshotModel();
126 snapshots.push(model);
129 const addTask = (task_name: string, snapshot_name: string) => {
130 const task = new ExecutingTask();
131 task.name = task_name;
135 snapshot_name: snapshot_name
137 summaryService.addRunningTask(task);
140 const expectImageTasks = (snapshot: RbdSnapshotModel, executing: string) => {
141 expect(snapshot.cdExecuting).toEqual(executing);
144 const refresh = (data) => {
145 summaryService['summaryDataSource'].next(data);
149 fixture.detectChanges();
154 component.snapshots = snapshots;
155 component.poolName = 'rbd';
156 component.rbdName = 'foo';
157 refresh({ executing_tasks: [], finished_tasks: [] });
158 component.ngOnChanges();
159 fixture.detectChanges();
162 it('should gets all snapshots without tasks', () => {
163 expect(component.snapshots.length).toBe(3);
164 expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
167 it('should add a new image from a task', () => {
168 addTask('rbd/snap/create', 'd');
169 expect(component.snapshots.length).toBe(4);
170 expectImageTasks(component.snapshots[0], undefined);
171 expectImageTasks(component.snapshots[1], undefined);
172 expectImageTasks(component.snapshots[2], undefined);
173 expectImageTasks(component.snapshots[3], 'Creating');
176 it('should show when an existing image is being modified', () => {
177 addTask('rbd/snap/edit', 'a');
178 addTask('rbd/snap/delete', 'b');
179 addTask('rbd/snap/rollback', 'c');
180 expect(component.snapshots.length).toBe(3);
181 expectImageTasks(component.snapshots[0], 'Updating');
182 expectImageTasks(component.snapshots[1], 'Deleting');
183 expectImageTasks(component.snapshots[2], 'Rolling back');
187 describe('snapshot modal dialog', () => {
189 component.poolName = 'pool01';
190 component.rbdName = 'image01';
191 spyOn(TestBed.get(BsModalService), 'show').and.callFake((content) => {
192 const ref = new BsModalRef();
193 ref.content = new content();
194 ref.content.onSubmit = new Subject();
199 it('should display old snapshot name', () => {
200 component.selection.selected = [{ name: 'oldname' }];
201 component.selection.update();
202 component.openEditSnapshotModal();
203 expect(component.modalRef.content.snapName).toBe('oldname');
204 expect(component.modalRef.content.editing).toBeTruthy();
207 it('should display suggested snapshot name', () => {
208 component.openCreateSnapshotModal();
209 expect(component.modalRef.content.snapName).toMatch(
210 RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+\\+[\\d:]+\$`)
215 describe('show action buttons and drop down actions depending on permissions', () => {
216 let tableActions: TableActionsComponent;
217 let scenario: { fn; empty; single };
218 let permissionHelper: PermissionHelper;
220 const getTableActionComponent = (): TableActionsComponent => {
221 fixture.detectChanges();
222 return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
226 permissionHelper = new PermissionHelper(component.permission, () =>
227 getTableActionComponent()
230 fn: () => tableActions.getCurrentButton().name,
236 describe('with all', () => {
238 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
241 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
242 permissionHelper.testScenarios(scenario));
244 it('shows all actions', () => {
245 expect(tableActions.tableActions.length).toBe(8);
246 expect(tableActions.tableActions).toEqual(component.tableActions);
250 describe('with read, create and update', () => {
252 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
255 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
256 permissionHelper.testScenarios(scenario));
258 it(`shows all actions except for 'Delete'`, () => {
259 expect(tableActions.tableActions.length).toBe(7);
260 component.tableActions.pop();
261 expect(tableActions.tableActions).toEqual(component.tableActions);
265 describe('with read, create and delete', () => {
267 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
270 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
271 scenario.single = 'Clone';
272 permissionHelper.testScenarios(scenario);
275 it(`shows 'Create', 'Clone', 'Copy' and 'Delete' action`, () => {
276 expect(tableActions.tableActions.length).toBe(4);
277 expect(tableActions.tableActions).toEqual([
278 component.tableActions[0],
279 component.tableActions[4],
280 component.tableActions[5],
281 component.tableActions[7]
286 describe('with read, edit and delete', () => {
288 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
291 it(`shows always 'Rename' as main action`, () => {
292 scenario.empty = 'Rename';
293 permissionHelper.testScenarios(scenario);
296 it(`shows 'Rename', 'Protect', 'Unprotect', 'Rollback' and 'Delete' action`, () => {
297 expect(tableActions.tableActions.length).toBe(5);
298 expect(tableActions.tableActions).toEqual([
299 component.tableActions[1],
300 component.tableActions[2],
301 component.tableActions[3],
302 component.tableActions[6],
303 component.tableActions[7]
308 describe('with read and create', () => {
310 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
313 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
314 scenario.single = 'Clone';
315 permissionHelper.testScenarios(scenario);
318 it(`shows 'Create', 'Clone' and 'Copy' actions`, () => {
319 expect(tableActions.tableActions.length).toBe(3);
320 expect(tableActions.tableActions).toEqual([
321 component.tableActions[0],
322 component.tableActions[4],
323 component.tableActions[5]
328 describe('with read and edit', () => {
330 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
333 it(`shows always 'Rename' as main action`, () => {
334 scenario.empty = 'Rename';
335 permissionHelper.testScenarios(scenario);
338 it(`shows 'Rename', 'Protect', 'Unprotect' and 'Rollback' actions`, () => {
339 expect(tableActions.tableActions.length).toBe(4);
340 expect(tableActions.tableActions).toEqual([
341 component.tableActions[1],
342 component.tableActions[2],
343 component.tableActions[3],
344 component.tableActions[6]
349 describe('with read and delete', () => {
351 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
354 it(`shows always 'Delete' as main action`, () => {
355 scenario.single = 'Delete';
356 scenario.empty = 'Delete';
357 permissionHelper.testScenarios(scenario);
360 it(`shows only 'Delete' action`, () => {
361 expect(tableActions.tableActions.length).toBe(1);
362 expect(tableActions.tableActions).toEqual([component.tableActions[7]]);
366 describe('with only read', () => {
368 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
371 it('shows no main action', () => {
372 permissionHelper.testScenarios({
373 fn: () => tableActions.getCurrentButton(),
379 it('shows no actions', () => {
380 expect(tableActions.tableActions.length).toBe(0);
381 expect(tableActions.tableActions).toEqual([]);
385 describe('test unprotected and protected action cases', () => {
387 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
390 it(`shows none of them if nothing is selected`, () => {
391 permissionHelper.setSelection([]);
392 fixture.detectChanges();
393 expect(tableActions.dropDownActions).toEqual([
394 component.tableActions[1],
395 component.tableActions[6]
399 it(`shows 'Protect' of them if nothing is selected`, () => {
400 permissionHelper.setSelection([{ is_protected: false }]);
401 fixture.detectChanges();
402 expect(tableActions.dropDownActions).toEqual([
403 component.tableActions[1],
404 component.tableActions[2],
405 component.tableActions[6]
409 it(`shows 'Unprotect' of them if nothing is selected`, () => {
410 permissionHelper.setSelection([{ is_protected: true }]);
411 fixture.detectChanges();
412 expect(tableActions.dropDownActions).toEqual([
413 component.tableActions[1],
414 component.tableActions[3],
415 component.tableActions[6]