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 { ToastModule } from 'ng2-toastr';
8 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
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 { DataTableModule } from '../../../shared/datatable/datatable.module';
20 import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
21 import { ExecutingTask } from '../../../shared/models/executing-task';
22 import { Permissions } from '../../../shared/models/permissions';
23 import { PipesModule } from '../../../shared/pipes/pipes.module';
24 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
25 import { NotificationService } from '../../../shared/services/notification.service';
26 import { SummaryService } from '../../../shared/services/summary.service';
27 import { TaskListService } from '../../../shared/services/task-list.service';
28 import { RbdSnapshotListComponent } from './rbd-snapshot-list.component';
29 import { RbdSnapshotModel } from './rbd-snapshot.model';
31 describe('RbdSnapshotListComponent', () => {
32 let component: RbdSnapshotListComponent;
33 let fixture: ComponentFixture<RbdSnapshotListComponent>;
34 let summaryService: SummaryService;
36 const fakeAuthStorageService = {
40 getPermissions: () => {
41 return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] });
46 declarations: [RbdSnapshotListComponent],
50 ToastModule.forRoot(),
52 HttpClientTestingModule,
57 { provide: AuthStorageService, useValue: fakeAuthStorageService },
64 fixture = TestBed.createComponent(RbdSnapshotListComponent);
65 component = fixture.componentInstance;
66 summaryService = TestBed.get(SummaryService);
69 it('should create', () => {
70 fixture.detectChanges();
71 expect(component).toBeTruthy();
74 describe('api delete request', () => {
76 let rbdService: RbdService;
77 let notificationService: NotificationService;
78 let authStorageService: AuthStorageService;
81 fixture.detectChanges();
82 const i18n = TestBed.get(I18n);
84 rbdService = new RbdService(null, null);
85 notificationService = new NotificationService(null, null, null);
86 authStorageService = new AuthStorageService();
87 authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
88 component = new RbdSnapshotListComponent(
100 spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
101 spyOn(notificationService, 'notifyTask').and.stub();
102 component.modalRef = new BsModalRef();
103 component.modalRef.content = {
104 stopLoadingSpinner: () => (called = true)
108 it('should call stopLoadingSpinner if the request fails', <any>fakeAsync(() => {
109 expect(called).toBe(false);
110 component._asyncTask('deleteSnapshot', 'rbd/snap/delete', 'someName');
112 expect(called).toBe(true);
116 describe('handling of executing tasks', () => {
117 let snapshots: RbdSnapshotModel[];
119 const addSnapshot = (name) => {
120 const model = new RbdSnapshotModel();
123 snapshots.push(model);
126 const addTask = (task_name: string, snapshot_name: string) => {
127 const task = new ExecutingTask();
128 task.name = task_name;
132 snapshot_name: snapshot_name
134 summaryService.addRunningTask(task);
137 const expectImageTasks = (snapshot: RbdSnapshotModel, executing: string) => {
138 expect(snapshot.cdExecuting).toEqual(executing);
141 const refresh = (data) => {
142 summaryService['summaryDataSource'].next(data);
146 fixture.detectChanges();
151 component.snapshots = snapshots;
152 component.poolName = 'rbd';
153 component.rbdName = 'foo';
154 refresh({ executing_tasks: [], finished_tasks: [] });
155 component.ngOnChanges();
156 fixture.detectChanges();
159 it('should gets all snapshots without tasks', () => {
160 expect(component.snapshots.length).toBe(3);
161 expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
164 it('should add a new image from a task', () => {
165 addTask('rbd/snap/create', 'd');
166 expect(component.snapshots.length).toBe(4);
167 expectImageTasks(component.snapshots[0], undefined);
168 expectImageTasks(component.snapshots[1], undefined);
169 expectImageTasks(component.snapshots[2], undefined);
170 expectImageTasks(component.snapshots[3], 'Creating');
173 it('should show when an existing image is being modified', () => {
174 addTask('rbd/snap/edit', 'a');
175 addTask('rbd/snap/delete', 'b');
176 addTask('rbd/snap/rollback', 'c');
177 expect(component.snapshots.length).toBe(3);
178 expectImageTasks(component.snapshots[0], 'Updating');
179 expectImageTasks(component.snapshots[1], 'Deleting');
180 expectImageTasks(component.snapshots[2], 'Rolling back');
184 describe('snapshot modal dialog', () => {
186 component.poolName = 'pool01';
187 component.rbdName = 'image01';
188 spyOn(TestBed.get(BsModalService), 'show').and.callFake((content) => {
189 const ref = new BsModalRef();
190 ref.content = new content();
191 ref.content.onSubmit = new Subject();
196 it('should display old snapshot name', () => {
197 component.selection.selected = [{ name: 'oldname' }];
198 component.selection.update();
199 component.openEditSnapshotModal();
200 expect(component.modalRef.content.snapName).toBe('oldname');
201 expect(component.modalRef.content.editing).toBeTruthy();
204 it('should display suggested snapshot name', () => {
205 component.openCreateSnapshotModal();
206 expect(component.modalRef.content.snapName).toMatch(
207 RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+\\+[\\d:]+\$`)
212 describe('show action buttons and drop down actions depending on permissions', () => {
213 let tableActions: TableActionsComponent;
214 let scenario: { fn; empty; single };
215 let permissionHelper: PermissionHelper;
217 const getTableActionComponent = (): TableActionsComponent => {
218 fixture.detectChanges();
219 return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
223 permissionHelper = new PermissionHelper(component.permission, () =>
224 getTableActionComponent()
227 fn: () => tableActions.getCurrentButton().name,
233 describe('with all', () => {
235 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
238 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
239 permissionHelper.testScenarios(scenario));
241 it('shows all actions', () => {
242 expect(tableActions.tableActions.length).toBe(8);
243 expect(tableActions.tableActions).toEqual(component.tableActions);
247 describe('with read, create and update', () => {
249 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
252 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
253 permissionHelper.testScenarios(scenario));
255 it(`shows all actions except for 'Delete'`, () => {
256 expect(tableActions.tableActions.length).toBe(7);
257 component.tableActions.pop();
258 expect(tableActions.tableActions).toEqual(component.tableActions);
262 describe('with read, create and delete', () => {
264 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
267 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
268 scenario.single = 'Clone';
269 permissionHelper.testScenarios(scenario);
272 it(`shows 'Create', 'Clone', 'Copy' and 'Delete' action`, () => {
273 expect(tableActions.tableActions.length).toBe(4);
274 expect(tableActions.tableActions).toEqual([
275 component.tableActions[0],
276 component.tableActions[4],
277 component.tableActions[5],
278 component.tableActions[7]
283 describe('with read, edit and delete', () => {
285 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
288 it(`shows always 'Rename' as main action`, () => {
289 scenario.empty = 'Rename';
290 permissionHelper.testScenarios(scenario);
293 it(`shows 'Rename', 'Protect', 'Unprotect', 'Rollback' and 'Delete' action`, () => {
294 expect(tableActions.tableActions.length).toBe(5);
295 expect(tableActions.tableActions).toEqual([
296 component.tableActions[1],
297 component.tableActions[2],
298 component.tableActions[3],
299 component.tableActions[6],
300 component.tableActions[7]
305 describe('with read and create', () => {
307 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
310 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
311 scenario.single = 'Clone';
312 permissionHelper.testScenarios(scenario);
315 it(`shows 'Create', 'Clone' and 'Copy' actions`, () => {
316 expect(tableActions.tableActions.length).toBe(3);
317 expect(tableActions.tableActions).toEqual([
318 component.tableActions[0],
319 component.tableActions[4],
320 component.tableActions[5]
325 describe('with read and edit', () => {
327 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
330 it(`shows always 'Rename' as main action`, () => {
331 scenario.empty = 'Rename';
332 permissionHelper.testScenarios(scenario);
335 it(`shows 'Rename', 'Protect', 'Unprotect' and 'Rollback' actions`, () => {
336 expect(tableActions.tableActions.length).toBe(4);
337 expect(tableActions.tableActions).toEqual([
338 component.tableActions[1],
339 component.tableActions[2],
340 component.tableActions[3],
341 component.tableActions[6]
346 describe('with read and delete', () => {
348 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
351 it(`shows always 'Delete' as main action`, () => {
352 scenario.single = 'Delete';
353 scenario.empty = 'Delete';
354 permissionHelper.testScenarios(scenario);
357 it(`shows only 'Delete' action`, () => {
358 expect(tableActions.tableActions.length).toBe(1);
359 expect(tableActions.tableActions).toEqual([component.tableActions[7]]);
363 describe('with only read', () => {
365 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
368 it('shows no main action', () => {
369 permissionHelper.testScenarios({
370 fn: () => tableActions.getCurrentButton(),
376 it('shows no actions', () => {
377 expect(tableActions.tableActions.length).toBe(0);
378 expect(tableActions.tableActions).toEqual([]);
382 describe('test unprotected and protected action cases', () => {
384 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
387 it(`shows none of them if nothing is selected`, () => {
388 permissionHelper.setSelection([]);
389 fixture.detectChanges();
390 expect(tableActions.dropDownActions).toEqual([
391 component.tableActions[1],
392 component.tableActions[6]
396 it(`shows 'Protect' of them if nothing is selected`, () => {
397 permissionHelper.setSelection([{ is_protected: false }]);
398 fixture.detectChanges();
399 expect(tableActions.dropDownActions).toEqual([
400 component.tableActions[1],
401 component.tableActions[2],
402 component.tableActions[6]
406 it(`shows 'Unprotect' of them if nothing is selected`, () => {
407 permissionHelper.setSelection([{ is_protected: true }]);
408 fixture.detectChanges();
409 expect(tableActions.dropDownActions).toEqual([
410 component.tableActions[1],
411 component.tableActions[3],
412 component.tableActions[6]