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 { ServicesModule } from '../../../shared/services/services.module';
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 ToastModule.forRoot(),
54 HttpClientTestingModule,
59 { provide: AuthStorageService, useValue: fakeAuthStorageService },
66 fixture = TestBed.createComponent(RbdSnapshotListComponent);
67 component = fixture.componentInstance;
68 summaryService = TestBed.get(SummaryService);
71 it('should create', () => {
72 fixture.detectChanges();
73 expect(component).toBeTruthy();
76 describe('api delete request', () => {
78 let rbdService: RbdService;
79 let notificationService: NotificationService;
80 let authStorageService: AuthStorageService;
83 fixture.detectChanges();
84 const i18n = TestBed.get(I18n);
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(
102 spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
103 spyOn(notificationService, 'notifyTask').and.stub();
104 component.modalRef = new BsModalRef();
105 component.modalRef.content = {
106 stopLoadingSpinner: () => (called = true)
110 it('should call stopLoadingSpinner if the request fails', <any>fakeAsync(() => {
111 expect(called).toBe(false);
112 component._asyncTask('deleteSnapshot', 'rbd/snap/delete', 'someName');
114 expect(called).toBe(true);
118 describe('handling of executing tasks', () => {
119 let snapshots: RbdSnapshotModel[];
121 const addSnapshot = (name) => {
122 const model = new RbdSnapshotModel();
125 snapshots.push(model);
128 const addTask = (task_name: string, snapshot_name: string) => {
129 const task = new ExecutingTask();
130 task.name = task_name;
134 snapshot_name: snapshot_name
136 summaryService.addRunningTask(task);
139 const expectImageTasks = (snapshot: RbdSnapshotModel, executing: string) => {
140 expect(snapshot.cdExecuting).toEqual(executing);
143 const refresh = (data) => {
144 summaryService['summaryDataSource'].next(data);
148 fixture.detectChanges();
153 component.snapshots = snapshots;
154 component.poolName = 'rbd';
155 component.rbdName = 'foo';
156 refresh({ executing_tasks: [], finished_tasks: [] });
157 component.ngOnChanges();
158 fixture.detectChanges();
161 it('should gets all snapshots without tasks', () => {
162 expect(component.snapshots.length).toBe(3);
163 expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
166 it('should add a new image from a task', () => {
167 addTask('rbd/snap/create', 'd');
168 expect(component.snapshots.length).toBe(4);
169 expectImageTasks(component.snapshots[0], undefined);
170 expectImageTasks(component.snapshots[1], undefined);
171 expectImageTasks(component.snapshots[2], undefined);
172 expectImageTasks(component.snapshots[3], 'Creating');
175 it('should show when an existing image is being modified', () => {
176 addTask('rbd/snap/edit', 'a');
177 addTask('rbd/snap/delete', 'b');
178 addTask('rbd/snap/rollback', 'c');
179 expect(component.snapshots.length).toBe(3);
180 expectImageTasks(component.snapshots[0], 'Updating');
181 expectImageTasks(component.snapshots[1], 'Deleting');
182 expectImageTasks(component.snapshots[2], 'Rolling back');
186 describe('snapshot modal dialog', () => {
188 component.poolName = 'pool01';
189 component.rbdName = 'image01';
190 spyOn(TestBed.get(BsModalService), 'show').and.callFake((content) => {
191 const ref = new BsModalRef();
192 ref.content = new content();
193 ref.content.onSubmit = new Subject();
198 it('should display old snapshot name', () => {
199 component.selection.selected = [{ name: 'oldname' }];
200 component.selection.update();
201 component.openEditSnapshotModal();
202 expect(component.modalRef.content.snapName).toBe('oldname');
203 expect(component.modalRef.content.editing).toBeTruthy();
206 it('should display suggested snapshot name', () => {
207 component.openCreateSnapshotModal();
208 expect(component.modalRef.content.snapName).toMatch(
209 RegExp(`^${component.rbdName}-\\d+T\\d+Z\$`)
214 describe('show action buttons and drop down actions depending on permissions', () => {
215 let tableActions: TableActionsComponent;
216 let scenario: { fn; empty; single };
217 let permissionHelper: PermissionHelper;
219 const getTableActionComponent = (): TableActionsComponent => {
220 fixture.detectChanges();
221 return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
225 permissionHelper = new PermissionHelper(component.permission, () =>
226 getTableActionComponent()
229 fn: () => tableActions.getCurrentButton().name,
235 describe('with all', () => {
237 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
240 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
241 permissionHelper.testScenarios(scenario));
243 it('shows all actions', () => {
244 expect(tableActions.tableActions.length).toBe(8);
245 expect(tableActions.tableActions).toEqual(component.tableActions);
249 describe('with read, create and update', () => {
251 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
254 it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
255 permissionHelper.testScenarios(scenario));
257 it(`shows all actions except for 'Delete'`, () => {
258 expect(tableActions.tableActions.length).toBe(7);
259 component.tableActions.pop();
260 expect(tableActions.tableActions).toEqual(component.tableActions);
264 describe('with read, create and delete', () => {
266 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
269 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
270 scenario.single = 'Clone';
271 permissionHelper.testScenarios(scenario);
274 it(`shows 'Create', 'Clone', 'Copy' and 'Delete' action`, () => {
275 expect(tableActions.tableActions.length).toBe(4);
276 expect(tableActions.tableActions).toEqual([
277 component.tableActions[0],
278 component.tableActions[4],
279 component.tableActions[5],
280 component.tableActions[7]
285 describe('with read, edit and delete', () => {
287 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
290 it(`shows always 'Rename' as main action`, () => {
291 scenario.empty = 'Rename';
292 permissionHelper.testScenarios(scenario);
295 it(`shows 'Rename', 'Protect', 'Unprotect', 'Rollback' and 'Delete' action`, () => {
296 expect(tableActions.tableActions.length).toBe(5);
297 expect(tableActions.tableActions).toEqual([
298 component.tableActions[1],
299 component.tableActions[2],
300 component.tableActions[3],
301 component.tableActions[6],
302 component.tableActions[7]
307 describe('with read and create', () => {
309 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
312 it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
313 scenario.single = 'Clone';
314 permissionHelper.testScenarios(scenario);
317 it(`shows 'Create', 'Clone' and 'Copy' actions`, () => {
318 expect(tableActions.tableActions.length).toBe(3);
319 expect(tableActions.tableActions).toEqual([
320 component.tableActions[0],
321 component.tableActions[4],
322 component.tableActions[5]
327 describe('with read and edit', () => {
329 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
332 it(`shows always 'Rename' as main action`, () => {
333 scenario.empty = 'Rename';
334 permissionHelper.testScenarios(scenario);
337 it(`shows 'Rename', 'Protect', 'Unprotect' and 'Rollback' actions`, () => {
338 expect(tableActions.tableActions.length).toBe(4);
339 expect(tableActions.tableActions).toEqual([
340 component.tableActions[1],
341 component.tableActions[2],
342 component.tableActions[3],
343 component.tableActions[6]
348 describe('with read and delete', () => {
350 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
353 it(`shows always 'Delete' as main action`, () => {
354 scenario.single = 'Delete';
355 scenario.empty = 'Delete';
356 permissionHelper.testScenarios(scenario);
359 it(`shows only 'Delete' action`, () => {
360 expect(tableActions.tableActions.length).toBe(1);
361 expect(tableActions.tableActions).toEqual([component.tableActions[7]]);
365 describe('with only read', () => {
367 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
370 it('shows no main action', () => {
371 permissionHelper.testScenarios({
372 fn: () => tableActions.getCurrentButton(),
378 it('shows no actions', () => {
379 expect(tableActions.tableActions.length).toBe(0);
380 expect(tableActions.tableActions).toEqual([]);
384 describe('test unprotected and protected action cases', () => {
386 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
389 it(`shows none of them if nothing is selected`, () => {
390 permissionHelper.setSelection([]);
391 fixture.detectChanges();
392 expect(tableActions.dropDownActions).toEqual([
393 component.tableActions[1],
394 component.tableActions[6]
398 it(`shows 'Protect' of them if nothing is selected`, () => {
399 permissionHelper.setSelection([{ is_protected: false }]);
400 fixture.detectChanges();
401 expect(tableActions.dropDownActions).toEqual([
402 component.tableActions[1],
403 component.tableActions[2],
404 component.tableActions[6]
408 it(`shows 'Unprotect' of them if nothing is selected`, () => {
409 permissionHelper.setSelection([{ is_protected: true }]);
410 fixture.detectChanges();
411 expect(tableActions.dropDownActions).toEqual([
412 component.tableActions[1],
413 component.tableActions[3],
414 component.tableActions[6]