1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
4 import { RouterTestingModule } from '@angular/router/testing';
6 import { NgbModalModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
7 import { MockComponent } from 'ng-mocks';
8 import { ToastrModule } from 'ngx-toastr';
9 import { Subject, throwError as observableThrowError } from 'rxjs';
10 import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
12 import { RbdService } from '~/app/shared/api/rbd.service';
13 import { ComponentsModule } from '~/app/shared/components/components.module';
14 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
15 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
16 import { DataTableModule } from '~/app/shared/datatable/datatable.module';
17 import { TableActionsComponent } from '~/app/shared/datatable/table-actions/table-actions.component';
18 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
19 import { ExecutingTask } from '~/app/shared/models/executing-task';
20 import { Permissions } from '~/app/shared/models/permissions';
21 import { PipesModule } from '~/app/shared/pipes/pipes.module';
22 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
23 import { ModalService } from '~/app/shared/services/modal.service';
24 import { NotificationService } from '~/app/shared/services/notification.service';
25 import { SummaryService } from '~/app/shared/services/summary.service';
26 import { TaskListService } from '~/app/shared/services/task-list.service';
27 import { configureTestBed, expectItemTasks, PermissionHelper } from '~/testing/unit-test-helper';
28 import { RbdSnapshotFormModalComponent } from '../rbd-snapshot-form/rbd-snapshot-form-modal.component';
29 import { RbdTabsComponent } from '../rbd-tabs/rbd-tabs.component';
30 import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
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'] });
51 RbdSnapshotListComponent,
53 MockComponent(RbdSnapshotFormModalComponent)
56 BrowserAnimationsModule,
59 HttpClientTestingModule,
63 ToastrModule.forRoot(),
67 { provide: AuthStorageService, useValue: fakeAuthStorageService },
71 [CriticalConfirmationModalComponent]
75 fixture = TestBed.createComponent(RbdSnapshotListComponent);
76 component = fixture.componentInstance;
77 component.ngOnChanges();
78 summaryService = TestBed.inject(SummaryService);
81 it('should create', () => {
82 fixture.detectChanges();
83 expect(component).toBeTruthy();
86 describe('api delete request', () => {
88 let rbdService: RbdService;
89 let rbdMirroringService: RbdMirroringService;
90 let notificationService: NotificationService;
91 let authStorageService: AuthStorageService;
94 fixture.detectChanges();
95 const modalService = TestBed.inject(ModalService);
96 const actionLabelsI18n = TestBed.inject(ActionLabelsI18n);
98 rbdMirroringService = new RbdMirroringService(null, null);
99 rbdService = new RbdService(null, null);
100 notificationService = new NotificationService(null, null, null);
101 authStorageService = new AuthStorageService();
102 authStorageService.set('user', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
103 component = new RbdSnapshotListComponent(
117 spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
118 spyOn(notificationService, 'notifyTask').and.stub();
121 it('should call stopLoadingSpinner if the request fails', fakeAsync(() => {
122 component.updateSelection(new CdTableSelection([{ name: 'someName' }]));
123 expect(called).toBe(false);
124 component.deleteSnapshotModal();
125 spyOn(component.modalRef.componentInstance, 'stopLoadingSpinner').and.callFake(() => {
128 component.modalRef.componentInstance.submitAction();
130 expect(called).toBe(true);
134 describe('handling of executing tasks', () => {
135 let snapshots: RbdSnapshotModel[];
137 const addSnapshot = (name: string) => {
138 const model = new RbdSnapshotModel();
141 snapshots.push(model);
144 const addTask = (task_name: string, snapshot_name: string) => {
145 const task = new ExecutingTask();
146 task.name = task_name;
148 image_spec: 'rbd/foo',
149 snapshot_name: snapshot_name
151 summaryService.addRunningTask(task);
154 const refresh = (data: any) => {
155 summaryService['summaryDataSource'].next(data);
159 fixture.detectChanges();
164 component.snapshots = snapshots;
165 component.poolName = 'rbd';
166 component.rbdName = 'foo';
167 refresh({ executing_tasks: [], finished_tasks: [] });
168 component.ngOnChanges();
169 fixture.detectChanges();
172 it('should gets all snapshots without tasks', () => {
173 expect(component.snapshots.length).toBe(3);
174 expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
177 it('should add a new image from a task', () => {
178 addTask('rbd/snap/create', 'd');
179 expect(component.snapshots.length).toBe(4);
180 expectItemTasks(component.snapshots[0], undefined);
181 expectItemTasks(component.snapshots[1], undefined);
182 expectItemTasks(component.snapshots[2], undefined);
183 expectItemTasks(component.snapshots[3], 'Creating');
186 it('should show when an existing image is being modified', () => {
187 addTask('rbd/snap/edit', 'a');
188 addTask('rbd/snap/delete', 'b');
189 addTask('rbd/snap/rollback', 'c');
190 expect(component.snapshots.length).toBe(3);
191 expectItemTasks(component.snapshots[0], 'Updating');
192 expectItemTasks(component.snapshots[1], 'Deleting');
193 expectItemTasks(component.snapshots[2], 'Rolling back');
197 describe('snapshot modal dialog', () => {
199 component.poolName = 'pool01';
200 component.rbdName = 'image01';
201 spyOn(TestBed.inject(ModalService), 'show').and.callFake(() => {
203 ref.componentInstance = new RbdSnapshotFormModalComponent(
208 TestBed.inject(ActionLabelsI18n)
210 ref.componentInstance.onSubmit = new Subject();
215 it('should display old snapshot name', () => {
216 component.selection.selected = [{ name: 'oldname' }];
217 component.openEditSnapshotModal();
218 expect(component.modalRef.componentInstance.snapName).toBe('oldname');
219 expect(component.modalRef.componentInstance.editing).toBeTruthy();
222 it('should display suggested snapshot name', () => {
223 component.openCreateSnapshotModal();
224 expect(component.modalRef.componentInstance.snapName).toMatch(
225 RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+[\\+-][\\d:]+$`)
230 it('should test all TableActions combinations', () => {
231 component.ngOnInit();
232 const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
233 const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
234 component.tableActions
237 expect(tableActions).toEqual({
238 'create,update,delete': {
249 primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' }
252 actions: ['Create', 'Rename', 'Protect', 'Unprotect', 'Clone', 'Copy', 'Rollback'],
253 primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' }
256 actions: ['Create', 'Clone', 'Copy', 'Delete'],
257 primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' }
260 actions: ['Create', 'Clone', 'Copy'],
261 primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' }
264 actions: ['Rename', 'Protect', 'Unprotect', 'Rollback', 'Delete'],
265 primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' }
268 actions: ['Rename', 'Protect', 'Unprotect', 'Rollback'],
269 primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' }
273 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
277 primary: { multiple: '', executing: '', single: '', no: '' }
282 describe('clone button disable state', () => {
283 let actions: RbdSnapshotActionsModel;
286 fixture.detectChanges();
287 const rbdService = TestBed.inject(RbdService);
288 const actionLabelsI18n = TestBed.inject(ActionLabelsI18n);
289 actions = new RbdSnapshotActionsModel(actionLabelsI18n, [], rbdService);
292 it('should be disabled with version 1 and protected false', () => {
293 const selection = new CdTableSelection([{ name: 'someName', is_protected: false }]);
294 const disableDesc = actions.getCloneDisableDesc(selection, ['layering']);
295 expect(disableDesc).toBe('Snapshot must be protected in order to clone.');
302 ])('should be enabled with version %d and protected %s', (version, is_protected) => {
303 actions.cloneFormatVersion = version;
304 const selection = new CdTableSelection([{ name: 'someName', is_protected: is_protected }]);
305 const disableDesc = actions.getCloneDisableDesc(selection, ['layering']);
306 expect(disableDesc).toBe(false);