]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / rbd-list / rbd-list.component.spec.ts
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { By } from '@angular/platform-browser';
4 import { RouterTestingModule } from '@angular/router/testing';
5
6 import { AlertModule } from 'ngx-bootstrap/alert';
7 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
8 import { ModalModule } from 'ngx-bootstrap/modal';
9 import { TabsModule } from 'ngx-bootstrap/tabs';
10 import { TooltipModule } from 'ngx-bootstrap/tooltip';
11 import { ToastrModule } from 'ngx-toastr';
12 import { BehaviorSubject, of } from 'rxjs';
13
14 import {
15 configureTestBed,
16 i18nProviders,
17 PermissionHelper
18 } from '../../../../testing/unit-test-helper';
19 import { RbdService } from '../../../shared/api/rbd.service';
20 import { ActionLabels } from '../../../shared/constants/app.constants';
21 import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
22 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
23 import { ExecutingTask } from '../../../shared/models/executing-task';
24 import { SummaryService } from '../../../shared/services/summary.service';
25 import { TaskListService } from '../../../shared/services/task-list.service';
26 import { SharedModule } from '../../../shared/shared.module';
27 import { RbdConfigurationListComponent } from '../rbd-configuration-list/rbd-configuration-list.component';
28 import { RbdDetailsComponent } from '../rbd-details/rbd-details.component';
29 import { RbdSnapshotListComponent } from '../rbd-snapshot-list/rbd-snapshot-list.component';
30 import { RbdListComponent } from './rbd-list.component';
31 import { RbdModel } from './rbd-model';
32
33 describe('RbdListComponent', () => {
34 let fixture: ComponentFixture<RbdListComponent>;
35 let component: RbdListComponent;
36 let summaryService: SummaryService;
37 let rbdService: RbdService;
38
39 const refresh = (data) => {
40 summaryService['summaryDataSource'].next(data);
41 };
42
43 configureTestBed({
44 imports: [
45 SharedModule,
46 BsDropdownModule.forRoot(),
47 TabsModule.forRoot(),
48 ModalModule.forRoot(),
49 TooltipModule.forRoot(),
50 ToastrModule.forRoot(),
51 AlertModule.forRoot(),
52 RouterTestingModule,
53 HttpClientTestingModule
54 ],
55 declarations: [
56 RbdListComponent,
57 RbdDetailsComponent,
58 RbdSnapshotListComponent,
59 RbdConfigurationListComponent
60 ],
61 providers: [TaskListService, i18nProviders]
62 });
63
64 beforeEach(() => {
65 fixture = TestBed.createComponent(RbdListComponent);
66 component = fixture.componentInstance;
67 summaryService = TestBed.get(SummaryService);
68 rbdService = TestBed.get(RbdService);
69
70 // this is needed because summaryService isn't being reset after each test.
71 summaryService['summaryDataSource'] = new BehaviorSubject(null);
72 summaryService['summaryData$'] = summaryService['summaryDataSource'].asObservable();
73 });
74
75 it('should create', () => {
76 expect(component).toBeTruthy();
77 });
78
79 describe('after ngOnInit', () => {
80 beforeEach(() => {
81 fixture.detectChanges();
82 spyOn(rbdService, 'list').and.callThrough();
83 });
84
85 it('should load images on init', () => {
86 refresh({});
87 expect(rbdService.list).toHaveBeenCalled();
88 });
89
90 it('should not load images on init because no data', () => {
91 refresh(undefined);
92 expect(rbdService.list).not.toHaveBeenCalled();
93 });
94
95 it('should call error function on init when summary service fails', () => {
96 spyOn(component.table, 'reset');
97 summaryService['summaryDataSource'].error(undefined);
98 expect(component.table.reset).toHaveBeenCalled();
99 expect(component.viewCacheStatusList).toEqual([{ status: ViewCacheStatus.ValueException }]);
100 });
101 });
102
103 describe('handling of executing tasks', () => {
104 let images: RbdModel[];
105
106 const addImage = (name) => {
107 const model = new RbdModel();
108 model.id = '-1';
109 model.name = name;
110 model.pool_name = 'rbd';
111 images.push(model);
112 };
113
114 const addTask = (name: string, image_name: string) => {
115 const task = new ExecutingTask();
116 task.name = name;
117 switch (task.name) {
118 case 'rbd/copy':
119 task.metadata = {
120 dest_pool_name: 'rbd',
121 dest_image_name: 'd'
122 };
123 break;
124 case 'rbd/clone':
125 task.metadata = {
126 child_pool_name: 'rbd',
127 child_image_name: 'd'
128 };
129 break;
130 default:
131 task.metadata = {
132 pool_name: 'rbd',
133 image_name: image_name
134 };
135 break;
136 }
137 summaryService.addRunningTask(task);
138 };
139
140 const expectImageTasks = (image: RbdModel, executing: string) => {
141 expect(image.cdExecuting).toEqual(executing);
142 };
143
144 beforeEach(() => {
145 images = [];
146 addImage('a');
147 addImage('b');
148 addImage('c');
149 component.images = images;
150 refresh({ executing_tasks: [], finished_tasks: [] });
151 spyOn(rbdService, 'list').and.callFake(() =>
152 of([{ poool_name: 'rbd', status: 1, value: images }])
153 );
154 fixture.detectChanges();
155 });
156
157 it('should gets all images without tasks', () => {
158 expect(component.images.length).toBe(3);
159 expect(component.images.every((image) => !image.cdExecuting)).toBeTruthy();
160 });
161
162 it('should add a new image from a task', () => {
163 addTask('rbd/create', 'd');
164 expect(component.images.length).toBe(4);
165 expectImageTasks(component.images[0], undefined);
166 expectImageTasks(component.images[1], undefined);
167 expectImageTasks(component.images[2], undefined);
168 expectImageTasks(component.images[3], 'Creating');
169 });
170
171 it('should show when a image is being cloned', () => {
172 addTask('rbd/clone', 'd');
173 expect(component.images.length).toBe(4);
174 expectImageTasks(component.images[0], undefined);
175 expectImageTasks(component.images[1], undefined);
176 expectImageTasks(component.images[2], undefined);
177 expectImageTasks(component.images[3], 'Cloning');
178 });
179
180 it('should show when a image is being copied', () => {
181 addTask('rbd/copy', 'd');
182 expect(component.images.length).toBe(4);
183 expectImageTasks(component.images[0], undefined);
184 expectImageTasks(component.images[1], undefined);
185 expectImageTasks(component.images[2], undefined);
186 expectImageTasks(component.images[3], 'Copying');
187 });
188
189 it('should show when an existing image is being modified', () => {
190 addTask('rbd/edit', 'a');
191 addTask('rbd/delete', 'b');
192 addTask('rbd/flatten', 'c');
193 expect(component.images.length).toBe(3);
194 expectImageTasks(component.images[0], 'Updating');
195 expectImageTasks(component.images[1], 'Deleting');
196 expectImageTasks(component.images[2], 'Flattening');
197 });
198 });
199
200 describe('show action buttons and drop down actions depending on permissions', () => {
201 let tableActions: TableActionsComponent;
202 let scenario: { fn; empty; single };
203 let permissionHelper: PermissionHelper;
204
205 const getTableActionComponent = (): TableActionsComponent => {
206 fixture.detectChanges();
207 return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
208 };
209
210 beforeEach(() => {
211 permissionHelper = new PermissionHelper(component.permission, () =>
212 getTableActionComponent()
213 );
214 scenario = {
215 fn: () => tableActions.getCurrentButton().name,
216 single: ActionLabels.EDIT,
217 empty: ActionLabels.CREATE
218 };
219 });
220
221 describe('with all', () => {
222 beforeEach(() => {
223 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
224 });
225
226 it(`shows 'Edit' for single selection else 'Add' as main action`, () =>
227 permissionHelper.testScenarios(scenario));
228
229 it('shows all actions', () => {
230 expect(tableActions.tableActions.length).toBe(6);
231 expect(tableActions.tableActions).toEqual(component.tableActions);
232 });
233 });
234
235 describe('with read, create and update', () => {
236 beforeEach(() => {
237 tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
238 });
239
240 it(`shows 'Edit' for single selection else 'Add' as main action`, () =>
241 permissionHelper.testScenarios(scenario));
242
243 it(`shows all actions except for 'Delete' and 'Move'`, () => {
244 expect(tableActions.tableActions.length).toBe(4);
245 component.tableActions.pop();
246 component.tableActions.pop();
247 expect(tableActions.tableActions).toEqual(component.tableActions);
248 });
249 });
250
251 describe('with read, create and delete', () => {
252 beforeEach(() => {
253 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
254 });
255
256 it(`shows 'Copy' for single selection else 'Add' as main action`, () => {
257 scenario.single = 'Copy';
258 permissionHelper.testScenarios(scenario);
259 });
260
261 it(`shows 'Add', 'Copy', 'Delete' and 'Move' action`, () => {
262 expect(tableActions.tableActions.length).toBe(4);
263 expect(tableActions.tableActions).toEqual([
264 component.tableActions[0],
265 component.tableActions[2],
266 component.tableActions[4],
267 component.tableActions[5]
268 ]);
269 });
270 });
271
272 describe('with read, edit and delete', () => {
273 beforeEach(() => {
274 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
275 });
276
277 it(`shows always 'Edit' as main action`, () => {
278 scenario.empty = 'Edit';
279 permissionHelper.testScenarios(scenario);
280 });
281
282 it(`shows 'Edit', 'Flatten', 'Delete' and 'Move' action`, () => {
283 expect(tableActions.tableActions.length).toBe(4);
284 expect(tableActions.tableActions).toEqual([
285 component.tableActions[1],
286 component.tableActions[3],
287 component.tableActions[4],
288 component.tableActions[5]
289 ]);
290 });
291 });
292
293 describe('with read and create', () => {
294 beforeEach(() => {
295 tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
296 });
297
298 it(`shows 'Copy' for single selection else 'Add' as main action`, () => {
299 scenario.single = 'Copy';
300 permissionHelper.testScenarios(scenario);
301 });
302
303 it(`shows 'Copy' and 'Add' actions`, () => {
304 expect(tableActions.tableActions.length).toBe(2);
305 expect(tableActions.tableActions).toEqual([
306 component.tableActions[0],
307 component.tableActions[2]
308 ]);
309 });
310 });
311
312 describe('with read and edit', () => {
313 beforeEach(() => {
314 tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
315 });
316
317 it(`shows always 'Edit' as main action`, () => {
318 scenario.empty = 'Edit';
319 permissionHelper.testScenarios(scenario);
320 });
321
322 it(`shows 'Edit' and 'Flatten' actions`, () => {
323 expect(tableActions.tableActions.length).toBe(2);
324 expect(tableActions.tableActions).toEqual([
325 component.tableActions[1],
326 component.tableActions[3]
327 ]);
328 });
329 });
330
331 describe('with read and delete', () => {
332 beforeEach(() => {
333 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
334 });
335
336 it(`shows always 'Delete' as main action`, () => {
337 scenario.single = 'Delete';
338 scenario.empty = 'Delete';
339 permissionHelper.testScenarios(scenario);
340 });
341
342 it(`shows 'Delete' and 'Move' actions`, () => {
343 expect(tableActions.tableActions.length).toBe(2);
344 expect(tableActions.tableActions).toEqual([
345 component.tableActions[4],
346 component.tableActions[5]
347 ]);
348 });
349 });
350
351 describe('with only read', () => {
352 beforeEach(() => {
353 tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
354 });
355
356 it('shows no main action', () => {
357 permissionHelper.testScenarios({
358 fn: () => tableActions.getCurrentButton(),
359 single: undefined,
360 empty: undefined
361 });
362 });
363
364 it('shows no actions', () => {
365 expect(tableActions.tableActions.length).toBe(0);
366 expect(tableActions.tableActions).toEqual([]);
367 });
368 });
369 });
370 });