]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { HttpClientTestingModule } from '@angular/common/http/testing'; |
2 | import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; | |
3 | import { ReactiveFormsModule } from '@angular/forms'; | |
4 | import { By } from '@angular/platform-browser'; | |
e306af50 | 5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; |
11fdf7f2 TL |
6 | import { RouterTestingModule } from '@angular/router/testing'; |
7 | ||
f67539c2 TL |
8 | import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; |
9 | import _ from 'lodash'; | |
9f95a23c | 10 | import { ToastrModule } from 'ngx-toastr'; |
11fdf7f2 TL |
11 | import { EMPTY, of } from 'rxjs'; |
12 | ||
f67539c2 TL |
13 | import { CephModule } from '~/app/ceph/ceph.module'; |
14 | import { PerformanceCounterModule } from '~/app/ceph/performance-counter/performance-counter.module'; | |
15 | import { CoreModule } from '~/app/core/core.module'; | |
16 | import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; | |
17 | import { OsdService } from '~/app/shared/api/osd.service'; | |
18 | import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component'; | |
19 | import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; | |
20 | import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component'; | |
21 | import { TableActionsComponent } from '~/app/shared/datatable/table-actions/table-actions.component'; | |
22 | import { CdTableAction } from '~/app/shared/models/cd-table-action'; | |
23 | import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; | |
24 | import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum'; | |
25 | import { Permissions } from '~/app/shared/models/permissions'; | |
26 | import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; | |
27 | import { ModalService } from '~/app/shared/services/modal.service'; | |
11fdf7f2 TL |
28 | import { |
29 | configureTestBed, | |
f67539c2 TL |
30 | OrchestratorHelper, |
31 | PermissionHelper, | |
32 | TableActionHelper | |
33 | } from '~/testing/unit-test-helper'; | |
11fdf7f2 TL |
34 | import { OsdReweightModalComponent } from '../osd-reweight-modal/osd-reweight-modal.component'; |
35 | import { OsdListComponent } from './osd-list.component'; | |
f38dd50b | 36 | import { ResizeObserver as ResizeObserverPolyfill } from '@juggle/resize-observer'; |
11fdf7f2 TL |
37 | |
38 | describe('OsdListComponent', () => { | |
39 | let component: OsdListComponent; | |
40 | let fixture: ComponentFixture<OsdListComponent>; | |
41 | let modalServiceShowSpy: jasmine.Spy; | |
81eedcae | 42 | let osdService: OsdService; |
f67539c2 | 43 | let orchService: OrchestratorService; |
11fdf7f2 TL |
44 | |
45 | const fakeAuthStorageService = { | |
46 | getPermissions: () => { | |
9f95a23c TL |
47 | return new Permissions({ |
48 | 'config-opt': ['read', 'update', 'create', 'delete'], | |
49 | osd: ['read', 'update', 'create', 'delete'] | |
50 | }); | |
11fdf7f2 TL |
51 | } |
52 | }; | |
53 | ||
9f95a23c TL |
54 | const getTableAction = (name: string) => |
55 | component.tableActions.find((action) => action.name === name); | |
11fdf7f2 TL |
56 | |
57 | const setFakeSelection = () => { | |
58 | // Default data and selection | |
9f95a23c TL |
59 | const selection = [{ id: 1, tree: { device_class: 'ssd' } }]; |
60 | const data = [{ id: 1, tree: { device_class: 'ssd' } }]; | |
11fdf7f2 TL |
61 | |
62 | // Table data and selection | |
63 | component.selection = new CdTableSelection(); | |
64 | component.selection.selected = selection; | |
11fdf7f2 TL |
65 | component.osds = data; |
66 | component.permissions = fakeAuthStorageService.getPermissions(); | |
67 | }; | |
68 | ||
9f95a23c | 69 | const openActionModal = (actionName: string) => { |
11fdf7f2 TL |
70 | setFakeSelection(); |
71 | getTableAction(actionName).click(); | |
72 | }; | |
73 | ||
74 | /** | |
75 | * The following modals are called after the information about their | |
76 | * safety to destroy/remove/mark them lost has been retrieved, hence | |
77 | * we will have to fake its request to be able to open those modals. | |
78 | */ | |
79 | const mockSafeToDestroy = () => { | |
f67539c2 | 80 | spyOn(TestBed.inject(OsdService), 'safeToDestroy').and.callFake(() => |
9f95a23c TL |
81 | of({ is_safe_to_destroy: true }) |
82 | ); | |
83 | }; | |
84 | ||
85 | const mockSafeToDelete = () => { | |
f67539c2 | 86 | spyOn(TestBed.inject(OsdService), 'safeToDelete').and.callFake(() => |
9f95a23c | 87 | of({ is_safe_to_delete: true }) |
11fdf7f2 TL |
88 | ); |
89 | }; | |
90 | ||
f67539c2 TL |
91 | const mockOrch = () => { |
92 | const features = [OrchestratorFeature.OSD_CREATE, OrchestratorFeature.OSD_DELETE]; | |
93 | OrchestratorHelper.mockStatus(true, features); | |
9f95a23c TL |
94 | }; |
95 | ||
11fdf7f2 TL |
96 | configureTestBed({ |
97 | imports: [ | |
e306af50 | 98 | BrowserAnimationsModule, |
11fdf7f2 TL |
99 | HttpClientTestingModule, |
100 | PerformanceCounterModule, | |
9f95a23c TL |
101 | ToastrModule.forRoot(), |
102 | CephModule, | |
11fdf7f2 | 103 | ReactiveFormsModule, |
f67539c2 | 104 | NgbDropdownModule, |
9f95a23c TL |
105 | RouterTestingModule, |
106 | CoreModule, | |
11fdf7f2 TL |
107 | RouterTestingModule |
108 | ], | |
11fdf7f2 TL |
109 | providers: [ |
110 | { provide: AuthStorageService, useValue: fakeAuthStorageService }, | |
111 | TableActionsComponent, | |
f67539c2 | 112 | ModalService |
11fdf7f2 TL |
113 | ] |
114 | }); | |
115 | ||
116 | beforeEach(() => { | |
117 | fixture = TestBed.createComponent(OsdListComponent); | |
11fdf7f2 | 118 | component = fixture.componentInstance; |
f67539c2 TL |
119 | osdService = TestBed.inject(OsdService); |
120 | modalServiceShowSpy = spyOn(TestBed.inject(ModalService), 'show').and.returnValue({ | |
121 | // mock the close function, it might be called if there are async tests. | |
122 | close: jest.fn() | |
123 | }); | |
124 | orchService = TestBed.inject(OrchestratorService); | |
f38dd50b TL |
125 | if (typeof window !== 'undefined') { |
126 | window.ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill; | |
127 | } | |
11fdf7f2 TL |
128 | }); |
129 | ||
130 | it('should create', () => { | |
131 | fixture.detectChanges(); | |
132 | expect(component).toBeTruthy(); | |
133 | }); | |
134 | ||
81eedcae | 135 | it('should have columns that are sortable', () => { |
9f95a23c TL |
136 | fixture.detectChanges(); |
137 | expect( | |
138 | component.columns | |
e306af50 | 139 | .filter((column) => !(column.prop === undefined)) |
9f95a23c TL |
140 | .every((column) => Boolean(column.prop)) |
141 | ).toBeTruthy(); | |
81eedcae TL |
142 | }); |
143 | ||
144 | describe('getOsdList', () => { | |
9f95a23c | 145 | let osds: any[]; |
adb31ebb | 146 | let flagsSpy: jasmine.Spy; |
9f95a23c TL |
147 | |
148 | const createOsd = (n: number) => | |
149 | <Record<string, any>>{ | |
150 | in: 'in', | |
151 | up: 'up', | |
152 | tree: { | |
153 | device_class: 'ssd' | |
154 | }, | |
155 | stats_history: { | |
801d1391 TL |
156 | op_out_bytes: [ |
157 | [n, n], | |
158 | [n * 2, n * 2] | |
159 | ], | |
160 | op_in_bytes: [ | |
161 | [n * 3, n * 3], | |
162 | [n * 4, n * 4] | |
163 | ] | |
9f95a23c TL |
164 | }, |
165 | stats: { | |
166 | stat_bytes_used: n * n, | |
167 | stat_bytes: n * n * n | |
168 | }, | |
169 | state: [] | |
170 | }; | |
81eedcae TL |
171 | |
172 | const expectAttributeOnEveryOsd = (attr: string) => | |
173 | expect(component.osds.every((osd) => Boolean(_.get(osd, attr)))).toBeTruthy(); | |
174 | ||
175 | beforeEach(() => { | |
176 | spyOn(osdService, 'getList').and.callFake(() => of(osds)); | |
adb31ebb | 177 | flagsSpy = spyOn(osdService, 'getFlags').and.callFake(() => of([])); |
81eedcae TL |
178 | osds = [createOsd(1), createOsd(2), createOsd(3)]; |
179 | component.getOsdList(); | |
180 | }); | |
181 | ||
182 | it('should replace "this.osds" with new data', () => { | |
183 | expect(component.osds.length).toBe(3); | |
184 | expect(osdService.getList).toHaveBeenCalledTimes(1); | |
185 | ||
186 | osds = [createOsd(4)]; | |
187 | component.getOsdList(); | |
188 | expect(component.osds.length).toBe(1); | |
189 | expect(osdService.getList).toHaveBeenCalledTimes(2); | |
190 | }); | |
191 | ||
192 | it('should have custom attribute "collectedStates"', () => { | |
193 | expectAttributeOnEveryOsd('collectedStates'); | |
194 | expect(component.osds[0].collectedStates).toEqual(['in', 'up']); | |
195 | }); | |
196 | ||
9f95a23c TL |
197 | it('should have "destroyed" state in "collectedStates"', () => { |
198 | osds[0].state.push('destroyed'); | |
199 | osds[0].up = 0; | |
200 | component.getOsdList(); | |
201 | ||
202 | expectAttributeOnEveryOsd('collectedStates'); | |
203 | expect(component.osds[0].collectedStates).toEqual(['in', 'destroyed']); | |
204 | }); | |
205 | ||
81eedcae TL |
206 | it('should have custom attribute "stats_history.out_bytes"', () => { |
207 | expectAttributeOnEveryOsd('stats_history.out_bytes'); | |
208 | expect(component.osds[0].stats_history.out_bytes).toEqual([1, 2]); | |
209 | }); | |
210 | ||
211 | it('should have custom attribute "stats_history.in_bytes"', () => { | |
212 | expectAttributeOnEveryOsd('stats_history.in_bytes'); | |
213 | expect(component.osds[0].stats_history.in_bytes).toEqual([3, 4]); | |
214 | }); | |
215 | ||
216 | it('should have custom attribute "stats.usage"', () => { | |
217 | expectAttributeOnEveryOsd('stats.usage'); | |
218 | expect(component.osds[0].stats.usage).toBe(1); | |
219 | expect(component.osds[1].stats.usage).toBe(0.5); | |
220 | expect(component.osds[2].stats.usage).toBe(3 / 9); | |
221 | }); | |
222 | ||
223 | it('should have custom attribute "cdIsBinary" to be true', () => { | |
224 | expectAttributeOnEveryOsd('cdIsBinary'); | |
225 | expect(component.osds[0].cdIsBinary).toBe(true); | |
226 | }); | |
adb31ebb TL |
227 | |
228 | it('should return valid individual flags only', () => { | |
229 | const osd1 = createOsd(1); | |
230 | const osd2 = createOsd(2); | |
231 | osd1.state = ['noup', 'exists', 'up']; | |
232 | osd2.state = ['noup', 'exists', 'up', 'noin']; | |
233 | osds = [osd1, osd2]; | |
234 | component.getOsdList(); | |
235 | ||
236 | expect(component.osds[0].cdIndivFlags).toStrictEqual(['noup']); | |
237 | expect(component.osds[1].cdIndivFlags).toStrictEqual(['noup', 'noin']); | |
238 | }); | |
239 | ||
240 | it('should not fail on empty individual flags list', () => { | |
241 | expect(component.osds[0].cdIndivFlags).toStrictEqual([]); | |
242 | }); | |
243 | ||
244 | it('should not return disabled cluster-wide flags', () => { | |
245 | flagsSpy.and.callFake(() => of(['noout', 'nodown', 'sortbitwise'])); | |
246 | component.getOsdList(); | |
247 | expect(component.osds[0].cdClusterFlags).toStrictEqual(['noout', 'nodown']); | |
248 | ||
249 | flagsSpy.and.callFake(() => of(['noout', 'purged_snapdirs', 'nodown'])); | |
250 | component.getOsdList(); | |
251 | expect(component.osds[0].cdClusterFlags).toStrictEqual(['noout', 'nodown']); | |
252 | ||
253 | flagsSpy.and.callFake(() => of(['recovery_deletes', 'noout', 'pglog_hardlimit', 'nodown'])); | |
254 | component.getOsdList(); | |
255 | expect(component.osds[0].cdClusterFlags).toStrictEqual(['noout', 'nodown']); | |
256 | }); | |
257 | ||
258 | it('should not fail on empty cluster-wide flags list', () => { | |
259 | flagsSpy.and.callFake(() => of([])); | |
260 | component.getOsdList(); | |
261 | expect(component.osds[0].cdClusterFlags).toStrictEqual([]); | |
262 | }); | |
f67539c2 TL |
263 | |
264 | it('should have custom attribute "cdExecuting"', () => { | |
265 | osds[1].operational_status = 'unmanaged'; | |
266 | osds[2].operational_status = 'deleting'; | |
267 | component.getOsdList(); | |
268 | expect(component.osds[0].cdExecuting).toBeUndefined(); | |
269 | expect(component.osds[1].cdExecuting).toBeUndefined(); | |
270 | expect(component.osds[2].cdExecuting).toBe('deleting'); | |
271 | }); | |
81eedcae TL |
272 | }); |
273 | ||
9f95a23c TL |
274 | describe('show osd actions as defined', () => { |
275 | const getOsdActions = () => { | |
11fdf7f2 | 276 | fixture.detectChanges(); |
9f95a23c TL |
277 | return fixture.debugElement.query(By.css('#cluster-wide-actions')).componentInstance |
278 | .dropDownActions; | |
11fdf7f2 TL |
279 | }; |
280 | ||
9f95a23c TL |
281 | it('shows osd actions after osd-actions', () => { |
282 | fixture.detectChanges(); | |
283 | expect(fixture.debugElement.query(By.css('#cluster-wide-actions'))).toBe( | |
284 | fixture.debugElement.queryAll(By.directive(TableActionsComponent))[1] | |
11fdf7f2 | 285 | ); |
11fdf7f2 TL |
286 | }); |
287 | ||
9f95a23c TL |
288 | it('shows both osd actions', () => { |
289 | const osdActions = getOsdActions(); | |
290 | expect(osdActions).toEqual(component.clusterWideActions); | |
291 | expect(osdActions.length).toBe(3); | |
292 | }); | |
11fdf7f2 | 293 | |
9f95a23c TL |
294 | it('shows only "Flags" action', () => { |
295 | component.permissions.configOpt.read = false; | |
296 | const osdActions = getOsdActions(); | |
297 | expect(osdActions[0].name).toBe('Flags'); | |
298 | expect(osdActions.length).toBe(1); | |
299 | }); | |
300 | ||
301 | it('shows only "Recovery Priority" action', () => { | |
302 | component.permissions.osd.read = false; | |
303 | const osdActions = getOsdActions(); | |
304 | expect(osdActions[0].name).toBe('Recovery Priority'); | |
305 | expect(osdActions[1].name).toBe('PG scrub'); | |
306 | expect(osdActions.length).toBe(2); | |
307 | }); | |
308 | ||
309 | it('shows no osd actions', () => { | |
310 | component.permissions.configOpt.read = false; | |
311 | component.permissions.osd.read = false; | |
312 | const osdActions = getOsdActions(); | |
313 | expect(osdActions).toEqual([]); | |
314 | }); | |
315 | }); | |
316 | ||
317 | it('should test all TableActions combinations', () => { | |
318 | const permissionHelper: PermissionHelper = new PermissionHelper(component.permissions.osd); | |
319 | const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions( | |
320 | component.tableActions | |
321 | ); | |
322 | ||
323 | expect(tableActions).toEqual({ | |
324 | 'create,update,delete': { | |
325 | actions: [ | |
326 | 'Create', | |
327 | 'Edit', | |
adb31ebb | 328 | 'Flags', |
9f95a23c TL |
329 | 'Scrub', |
330 | 'Deep Scrub', | |
331 | 'Reweight', | |
332 | 'Mark Out', | |
333 | 'Mark In', | |
334 | 'Mark Down', | |
335 | 'Mark Lost', | |
336 | 'Purge', | |
337 | 'Destroy', | |
338 | 'Delete' | |
339 | ], | |
340 | primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Create' } | |
341 | }, | |
342 | 'create,update': { | |
343 | actions: [ | |
344 | 'Create', | |
345 | 'Edit', | |
adb31ebb | 346 | 'Flags', |
9f95a23c TL |
347 | 'Scrub', |
348 | 'Deep Scrub', | |
349 | 'Reweight', | |
350 | 'Mark Out', | |
351 | 'Mark In', | |
352 | 'Mark Down' | |
353 | ], | |
354 | primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Create' } | |
355 | }, | |
356 | 'create,delete': { | |
357 | actions: ['Create', 'Mark Lost', 'Purge', 'Destroy', 'Delete'], | |
358 | primary: { | |
359 | multiple: 'Create', | |
360 | executing: 'Mark Lost', | |
361 | single: 'Mark Lost', | |
362 | no: 'Create' | |
363 | } | |
364 | }, | |
365 | create: { | |
366 | actions: ['Create'], | |
367 | primary: { multiple: 'Create', executing: 'Create', single: 'Create', no: 'Create' } | |
368 | }, | |
369 | 'update,delete': { | |
370 | actions: [ | |
371 | 'Edit', | |
adb31ebb | 372 | 'Flags', |
9f95a23c TL |
373 | 'Scrub', |
374 | 'Deep Scrub', | |
375 | 'Reweight', | |
376 | 'Mark Out', | |
377 | 'Mark In', | |
378 | 'Mark Down', | |
379 | 'Mark Lost', | |
380 | 'Purge', | |
381 | 'Destroy', | |
382 | 'Delete' | |
383 | ], | |
384 | primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Edit' } | |
385 | }, | |
386 | update: { | |
adb31ebb TL |
387 | actions: [ |
388 | 'Edit', | |
389 | 'Flags', | |
390 | 'Scrub', | |
391 | 'Deep Scrub', | |
392 | 'Reweight', | |
393 | 'Mark Out', | |
394 | 'Mark In', | |
395 | 'Mark Down' | |
396 | ], | |
9f95a23c TL |
397 | primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Edit' } |
398 | }, | |
399 | delete: { | |
400 | actions: ['Mark Lost', 'Purge', 'Destroy', 'Delete'], | |
401 | primary: { | |
402 | multiple: 'Mark Lost', | |
403 | executing: 'Mark Lost', | |
404 | single: 'Mark Lost', | |
405 | no: 'Mark Lost' | |
406 | } | |
407 | }, | |
408 | 'no-permissions': { | |
409 | actions: [], | |
410 | primary: { multiple: '', executing: '', single: '', no: '' } | |
411 | } | |
11fdf7f2 TL |
412 | }); |
413 | }); | |
414 | ||
415 | describe('test table actions in submenu', () => { | |
9f95a23c TL |
416 | beforeEach(() => { |
417 | fixture.detectChanges(); | |
418 | }); | |
419 | ||
11fdf7f2 TL |
420 | beforeEach(fakeAsync(() => { |
421 | // The menu needs a click to render the dropdown! | |
422 | const dropDownToggle = fixture.debugElement.query(By.css('.dropdown-toggle')); | |
423 | dropDownToggle.triggerEventHandler('click', null); | |
424 | tick(); | |
425 | fixture.detectChanges(); | |
426 | })); | |
427 | ||
9f95a23c | 428 | it('has all menu entries disabled except create', () => { |
11fdf7f2 | 429 | const tableActionElement = fixture.debugElement.query(By.directive(TableActionsComponent)); |
f67539c2 | 430 | const toClassName = TestBed.inject(TableActionsComponent).toClassName; |
11fdf7f2 | 431 | const getActionClasses = (action: CdTableAction) => |
f67539c2 | 432 | tableActionElement.query(By.css(`[ngbDropdownItem].${toClassName(action)}`)).classes; |
11fdf7f2 TL |
433 | |
434 | component.tableActions.forEach((action) => { | |
9f95a23c TL |
435 | if (action.name === 'Create') { |
436 | return; | |
437 | } | |
11fdf7f2 TL |
438 | expect(getActionClasses(action).disabled).toBe(true); |
439 | }); | |
440 | }); | |
441 | }); | |
442 | ||
443 | describe('tests if all modals are opened correctly', () => { | |
444 | /** | |
445 | * Helper function to check if a function opens a modal | |
446 | * | |
447 | * @param modalClass - The expected class of the modal | |
448 | */ | |
9f95a23c | 449 | const expectOpensModal = (actionName: string, modalClass: any): void => { |
11fdf7f2 TL |
450 | openActionModal(actionName); |
451 | ||
452 | // @TODO: check why tsc is complaining when passing 'expectationFailOutput' param. | |
453 | expect(modalServiceShowSpy.calls.any()).toBeTruthy(); | |
454 | expect(modalServiceShowSpy.calls.first().args[0]).toBe(modalClass); | |
455 | ||
456 | modalServiceShowSpy.calls.reset(); | |
457 | }; | |
458 | ||
459 | it('opens the reweight modal', () => { | |
460 | expectOpensModal('Reweight', OsdReweightModalComponent); | |
461 | }); | |
462 | ||
9f95a23c TL |
463 | it('opens the form modal', () => { |
464 | expectOpensModal('Edit', FormModalComponent); | |
465 | }); | |
466 | ||
11fdf7f2 TL |
467 | it('opens all confirmation modals', () => { |
468 | const modalClass = ConfirmationModalComponent; | |
469 | expectOpensModal('Mark Out', modalClass); | |
470 | expectOpensModal('Mark In', modalClass); | |
471 | expectOpensModal('Mark Down', modalClass); | |
472 | }); | |
473 | ||
474 | it('opens all critical confirmation modals', () => { | |
475 | const modalClass = CriticalConfirmationModalComponent; | |
476 | mockSafeToDestroy(); | |
477 | expectOpensModal('Mark Lost', modalClass); | |
478 | expectOpensModal('Purge', modalClass); | |
479 | expectOpensModal('Destroy', modalClass); | |
f67539c2 | 480 | mockOrch(); |
9f95a23c TL |
481 | mockSafeToDelete(); |
482 | expectOpensModal('Delete', modalClass); | |
11fdf7f2 TL |
483 | }); |
484 | }); | |
485 | ||
486 | describe('tests if the correct methods are called on confirmation', () => { | |
487 | const expectOsdServiceMethodCalled = ( | |
488 | actionName: string, | |
9f95a23c TL |
489 | osdServiceMethodName: |
490 | | 'markOut' | |
491 | | 'markIn' | |
492 | | 'markDown' | |
493 | | 'markLost' | |
494 | | 'purge' | |
495 | | 'destroy' | |
496 | | 'delete' | |
11fdf7f2 | 497 | ): void => { |
81eedcae | 498 | const osdServiceSpy = spyOn(osdService, osdServiceMethodName).and.callFake(() => EMPTY); |
11fdf7f2 | 499 | openActionModal(actionName); |
f67539c2 | 500 | const initialState = modalServiceShowSpy.calls.first().args[1]; |
11fdf7f2 TL |
501 | const submit = initialState.onSubmit || initialState.submitAction; |
502 | submit.call(component); | |
503 | ||
504 | expect(osdServiceSpy.calls.count()).toBe(1); | |
505 | expect(osdServiceSpy.calls.first().args[0]).toBe(1); | |
506 | ||
507 | // Reset spies to be able to recreate them | |
508 | osdServiceSpy.calls.reset(); | |
509 | modalServiceShowSpy.calls.reset(); | |
510 | }; | |
511 | ||
512 | it('calls the corresponding service methods in confirmation modals', () => { | |
513 | expectOsdServiceMethodCalled('Mark Out', 'markOut'); | |
514 | expectOsdServiceMethodCalled('Mark In', 'markIn'); | |
515 | expectOsdServiceMethodCalled('Mark Down', 'markDown'); | |
516 | }); | |
517 | ||
518 | it('calls the corresponding service methods in critical confirmation modals', () => { | |
519 | mockSafeToDestroy(); | |
520 | expectOsdServiceMethodCalled('Mark Lost', 'markLost'); | |
521 | expectOsdServiceMethodCalled('Purge', 'purge'); | |
522 | expectOsdServiceMethodCalled('Destroy', 'destroy'); | |
f67539c2 | 523 | mockOrch(); |
9f95a23c TL |
524 | mockSafeToDelete(); |
525 | expectOsdServiceMethodCalled('Delete', 'delete'); | |
11fdf7f2 TL |
526 | }); |
527 | }); | |
f67539c2 TL |
528 | |
529 | describe('table actions', () => { | |
530 | const fakeOsds = require('./fixtures/osd_list_response.json'); | |
531 | ||
532 | beforeEach(() => { | |
533 | component.permissions = fakeAuthStorageService.getPermissions(); | |
534 | spyOn(osdService, 'getList').and.callFake(() => of(fakeOsds)); | |
535 | spyOn(osdService, 'getFlags').and.callFake(() => of([])); | |
536 | }); | |
537 | ||
538 | const testTableActions = async ( | |
539 | orch: boolean, | |
540 | features: OrchestratorFeature[], | |
541 | tests: { selectRow?: number; expectResults: any }[] | |
542 | ) => { | |
543 | OrchestratorHelper.mockStatus(orch, features); | |
544 | fixture.detectChanges(); | |
545 | await fixture.whenStable(); | |
546 | ||
547 | for (const test of tests) { | |
548 | if (test.selectRow) { | |
549 | component.selection = new CdTableSelection(); | |
550 | component.selection.selected = [test.selectRow]; | |
551 | } | |
552 | await TableActionHelper.verifyTableActions( | |
553 | fixture, | |
554 | component.tableActions, | |
555 | test.expectResults | |
556 | ); | |
557 | } | |
558 | }; | |
559 | ||
560 | it('should have correct states when Orchestrator is enabled', async () => { | |
561 | const tests = [ | |
562 | { | |
563 | expectResults: { | |
564 | Create: { disabled: false, disableDesc: '' }, | |
565 | Delete: { disabled: true, disableDesc: '' } | |
566 | } | |
567 | }, | |
568 | { | |
569 | selectRow: fakeOsds[0], | |
570 | expectResults: { | |
571 | Create: { disabled: false, disableDesc: '' }, | |
572 | Delete: { disabled: false, disableDesc: '' } | |
573 | } | |
574 | }, | |
575 | { | |
576 | selectRow: fakeOsds[1], // Select a row that is not managed. | |
577 | expectResults: { | |
578 | Create: { disabled: false, disableDesc: '' }, | |
579 | Delete: { disabled: true, disableDesc: '' } | |
580 | } | |
581 | }, | |
582 | { | |
583 | selectRow: fakeOsds[2], // Select a row that is being deleted. | |
584 | expectResults: { | |
585 | Create: { disabled: false, disableDesc: '' }, | |
586 | Delete: { disabled: true, disableDesc: '' } | |
587 | } | |
588 | } | |
589 | ]; | |
590 | ||
591 | const features = [ | |
592 | OrchestratorFeature.OSD_CREATE, | |
593 | OrchestratorFeature.OSD_DELETE, | |
594 | OrchestratorFeature.OSD_GET_REMOVE_STATUS | |
595 | ]; | |
596 | await testTableActions(true, features, tests); | |
597 | }); | |
598 | ||
599 | it('should have correct states when Orchestrator is disabled', async () => { | |
600 | const resultNoOrchestrator = { | |
601 | disabled: true, | |
602 | disableDesc: orchService.disableMessages.noOrchestrator | |
603 | }; | |
604 | const tests = [ | |
605 | { | |
606 | expectResults: { | |
607 | Create: resultNoOrchestrator, | |
608 | Delete: { disabled: true, disableDesc: '' } | |
609 | } | |
610 | }, | |
611 | { | |
612 | selectRow: fakeOsds[0], | |
613 | expectResults: { | |
614 | Create: resultNoOrchestrator, | |
615 | Delete: resultNoOrchestrator | |
616 | } | |
617 | } | |
618 | ]; | |
619 | await testTableActions(false, [], tests); | |
620 | }); | |
621 | ||
622 | it('should have correct states when Orchestrator features are missing', async () => { | |
623 | const resultMissingFeatures = { | |
624 | disabled: true, | |
625 | disableDesc: orchService.disableMessages.missingFeature | |
626 | }; | |
627 | const tests = [ | |
628 | { | |
629 | expectResults: { | |
630 | Create: resultMissingFeatures, | |
631 | Delete: { disabled: true, disableDesc: '' } | |
632 | } | |
633 | }, | |
634 | { | |
635 | selectRow: fakeOsds[0], | |
636 | expectResults: { | |
637 | Create: resultMissingFeatures, | |
638 | Delete: resultMissingFeatures | |
639 | } | |
640 | } | |
641 | ]; | |
642 | await testTableActions(true, [], tests); | |
643 | }); | |
644 | }); | |
11fdf7f2 | 645 | }); |