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