]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { HttpClientTestingModule } from '@angular/common/http/testing'; |
2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; | |
e306af50 | 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; |
11fdf7f2 TL |
4 | import { RouterTestingModule } from '@angular/router/testing'; |
5 | ||
11fdf7f2 TL |
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'; | |
494da23a | 11 | import { ToastrModule } from 'ngx-toastr'; |
11fdf7f2 TL |
12 | import { BehaviorSubject, of } from 'rxjs'; |
13 | ||
14 | import { | |
15 | configureTestBed, | |
eafe8130 | 16 | expectItemTasks, |
11fdf7f2 TL |
17 | i18nProviders, |
18 | PermissionHelper | |
19 | } from '../../../../testing/unit-test-helper'; | |
20 | import { RbdService } from '../../../shared/api/rbd.service'; | |
11fdf7f2 TL |
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'; | |
9f95a23c | 30 | import { RbdTabsComponent } from '../rbd-tabs/rbd-tabs.component'; |
11fdf7f2 TL |
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 | ||
9f95a23c | 40 | const refresh = (data: any) => { |
11fdf7f2 TL |
41 | summaryService['summaryDataSource'].next(data); |
42 | }; | |
43 | ||
44 | configureTestBed({ | |
45 | imports: [ | |
e306af50 | 46 | BrowserAnimationsModule, |
11fdf7f2 TL |
47 | SharedModule, |
48 | BsDropdownModule.forRoot(), | |
49 | TabsModule.forRoot(), | |
50 | ModalModule.forRoot(), | |
51 | TooltipModule.forRoot(), | |
494da23a | 52 | ToastrModule.forRoot(), |
11fdf7f2 TL |
53 | AlertModule.forRoot(), |
54 | RouterTestingModule, | |
55 | HttpClientTestingModule | |
56 | ], | |
57 | declarations: [ | |
58 | RbdListComponent, | |
59 | RbdDetailsComponent, | |
60 | RbdSnapshotListComponent, | |
9f95a23c TL |
61 | RbdConfigurationListComponent, |
62 | RbdTabsComponent | |
11fdf7f2 TL |
63 | ], |
64 | providers: [TaskListService, i18nProviders] | |
65 | }); | |
66 | ||
67 | beforeEach(() => { | |
68 | fixture = TestBed.createComponent(RbdListComponent); | |
69 | component = fixture.componentInstance; | |
70 | summaryService = TestBed.get(SummaryService); | |
71 | rbdService = TestBed.get(RbdService); | |
72 | ||
73 | // this is needed because summaryService isn't being reset after each test. | |
74 | summaryService['summaryDataSource'] = new BehaviorSubject(null); | |
75 | summaryService['summaryData$'] = summaryService['summaryDataSource'].asObservable(); | |
76 | }); | |
77 | ||
78 | it('should create', () => { | |
79 | expect(component).toBeTruthy(); | |
80 | }); | |
81 | ||
82 | describe('after ngOnInit', () => { | |
83 | beforeEach(() => { | |
84 | fixture.detectChanges(); | |
85 | spyOn(rbdService, 'list').and.callThrough(); | |
86 | }); | |
87 | ||
88 | it('should load images on init', () => { | |
89 | refresh({}); | |
90 | expect(rbdService.list).toHaveBeenCalled(); | |
91 | }); | |
92 | ||
93 | it('should not load images on init because no data', () => { | |
94 | refresh(undefined); | |
95 | expect(rbdService.list).not.toHaveBeenCalled(); | |
96 | }); | |
97 | ||
98 | it('should call error function on init when summary service fails', () => { | |
99 | spyOn(component.table, 'reset'); | |
100 | summaryService['summaryDataSource'].error(undefined); | |
101 | expect(component.table.reset).toHaveBeenCalled(); | |
102 | expect(component.viewCacheStatusList).toEqual([{ status: ViewCacheStatus.ValueException }]); | |
103 | }); | |
104 | }); | |
105 | ||
9f95a23c TL |
106 | describe('handling of deletion', () => { |
107 | beforeEach(() => { | |
108 | fixture.detectChanges(); | |
109 | }); | |
110 | ||
111 | it('should check if there are no snapshots', () => { | |
112 | component.selection.add({ | |
113 | id: '-1', | |
114 | name: 'rbd1', | |
115 | pool_name: 'rbd' | |
116 | }); | |
117 | expect(component.hasSnapshots()).toBeFalsy(); | |
118 | }); | |
119 | ||
120 | it('should check if there are snapshots', () => { | |
121 | component.selection.add({ | |
122 | id: '-1', | |
123 | name: 'rbd1', | |
124 | pool_name: 'rbd', | |
125 | snapshots: [{}, {}] | |
126 | }); | |
127 | expect(component.hasSnapshots()).toBeTruthy(); | |
128 | }); | |
129 | ||
130 | it('should get delete disable description', () => { | |
131 | component.selection.add({ | |
132 | id: '-1', | |
133 | name: 'rbd1', | |
134 | pool_name: 'rbd', | |
135 | snapshots: [ | |
136 | { | |
137 | children: [{}] | |
138 | } | |
139 | ] | |
140 | }); | |
f91f0fd5 | 141 | expect(component.getDeleteDisableDesc(component.selection)).toBe( |
9f95a23c TL |
142 | 'This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD.' |
143 | ); | |
144 | }); | |
145 | ||
146 | it('should list all protected snapshots', () => { | |
147 | component.selection.add({ | |
148 | id: '-1', | |
149 | name: 'rbd1', | |
150 | pool_name: 'rbd', | |
151 | snapshots: [ | |
152 | { | |
153 | name: 'snap1', | |
154 | is_protected: false | |
155 | }, | |
156 | { | |
157 | name: 'snap2', | |
158 | is_protected: true | |
159 | } | |
160 | ] | |
161 | }); | |
162 | ||
163 | expect(component.listProtectedSnapshots()).toEqual(['snap2']); | |
164 | }); | |
165 | }); | |
166 | ||
11fdf7f2 TL |
167 | describe('handling of executing tasks', () => { |
168 | let images: RbdModel[]; | |
169 | ||
9f95a23c | 170 | const addImage = (name: string) => { |
11fdf7f2 TL |
171 | const model = new RbdModel(); |
172 | model.id = '-1'; | |
173 | model.name = name; | |
174 | model.pool_name = 'rbd'; | |
175 | images.push(model); | |
176 | }; | |
177 | ||
178 | const addTask = (name: string, image_name: string) => { | |
179 | const task = new ExecutingTask(); | |
180 | task.name = name; | |
181 | switch (task.name) { | |
182 | case 'rbd/copy': | |
183 | task.metadata = { | |
184 | dest_pool_name: 'rbd', | |
9f95a23c | 185 | dest_namespace: null, |
11fdf7f2 TL |
186 | dest_image_name: 'd' |
187 | }; | |
188 | break; | |
189 | case 'rbd/clone': | |
190 | task.metadata = { | |
191 | child_pool_name: 'rbd', | |
9f95a23c | 192 | child_namespace: null, |
11fdf7f2 TL |
193 | child_image_name: 'd' |
194 | }; | |
195 | break; | |
9f95a23c | 196 | case 'rbd/create': |
11fdf7f2 TL |
197 | task.metadata = { |
198 | pool_name: 'rbd', | |
9f95a23c | 199 | namespace: null, |
11fdf7f2 TL |
200 | image_name: image_name |
201 | }; | |
202 | break; | |
9f95a23c TL |
203 | default: |
204 | task.metadata = { | |
205 | image_spec: `rbd/${image_name}` | |
206 | }; | |
207 | break; | |
11fdf7f2 TL |
208 | } |
209 | summaryService.addRunningTask(task); | |
210 | }; | |
211 | ||
11fdf7f2 TL |
212 | beforeEach(() => { |
213 | images = []; | |
214 | addImage('a'); | |
215 | addImage('b'); | |
216 | addImage('c'); | |
217 | component.images = images; | |
218 | refresh({ executing_tasks: [], finished_tasks: [] }); | |
219 | spyOn(rbdService, 'list').and.callFake(() => | |
9f95a23c | 220 | of([{ pool_name: 'rbd', status: 1, value: images }]) |
11fdf7f2 TL |
221 | ); |
222 | fixture.detectChanges(); | |
223 | }); | |
224 | ||
225 | it('should gets all images without tasks', () => { | |
226 | expect(component.images.length).toBe(3); | |
9f95a23c | 227 | expect(component.images.every((image: any) => !image.cdExecuting)).toBeTruthy(); |
11fdf7f2 TL |
228 | }); |
229 | ||
230 | it('should add a new image from a task', () => { | |
231 | addTask('rbd/create', 'd'); | |
232 | expect(component.images.length).toBe(4); | |
eafe8130 TL |
233 | expectItemTasks(component.images[0], undefined); |
234 | expectItemTasks(component.images[1], undefined); | |
235 | expectItemTasks(component.images[2], undefined); | |
236 | expectItemTasks(component.images[3], 'Creating'); | |
11fdf7f2 TL |
237 | }); |
238 | ||
239 | it('should show when a image is being cloned', () => { | |
240 | addTask('rbd/clone', 'd'); | |
241 | expect(component.images.length).toBe(4); | |
eafe8130 TL |
242 | expectItemTasks(component.images[0], undefined); |
243 | expectItemTasks(component.images[1], undefined); | |
244 | expectItemTasks(component.images[2], undefined); | |
245 | expectItemTasks(component.images[3], 'Cloning'); | |
11fdf7f2 TL |
246 | }); |
247 | ||
248 | it('should show when a image is being copied', () => { | |
249 | addTask('rbd/copy', 'd'); | |
250 | expect(component.images.length).toBe(4); | |
eafe8130 TL |
251 | expectItemTasks(component.images[0], undefined); |
252 | expectItemTasks(component.images[1], undefined); | |
253 | expectItemTasks(component.images[2], undefined); | |
254 | expectItemTasks(component.images[3], 'Copying'); | |
11fdf7f2 TL |
255 | }); |
256 | ||
257 | it('should show when an existing image is being modified', () => { | |
258 | addTask('rbd/edit', 'a'); | |
259 | addTask('rbd/delete', 'b'); | |
260 | addTask('rbd/flatten', 'c'); | |
261 | expect(component.images.length).toBe(3); | |
eafe8130 TL |
262 | expectItemTasks(component.images[0], 'Updating'); |
263 | expectItemTasks(component.images[1], 'Deleting'); | |
264 | expectItemTasks(component.images[2], 'Flattening'); | |
11fdf7f2 TL |
265 | }); |
266 | }); | |
267 | ||
9f95a23c TL |
268 | it('should test all TableActions combinations', () => { |
269 | const permissionHelper: PermissionHelper = new PermissionHelper(component.permission); | |
270 | const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions( | |
271 | component.tableActions | |
272 | ); | |
273 | ||
274 | expect(tableActions).toEqual({ | |
275 | 'create,update,delete': { | |
276 | actions: ['Create', 'Edit', 'Copy', 'Flatten', 'Delete', 'Move to Trash'], | |
277 | primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' } | |
278 | }, | |
279 | 'create,update': { | |
280 | actions: ['Create', 'Edit', 'Copy', 'Flatten'], | |
281 | primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' } | |
282 | }, | |
283 | 'create,delete': { | |
284 | actions: ['Create', 'Copy', 'Delete', 'Move to Trash'], | |
285 | primary: { multiple: 'Create', executing: 'Copy', single: 'Copy', no: 'Create' } | |
286 | }, | |
287 | create: { | |
288 | actions: ['Create', 'Copy'], | |
289 | primary: { multiple: 'Create', executing: 'Copy', single: 'Copy', no: 'Create' } | |
290 | }, | |
291 | 'update,delete': { | |
292 | actions: ['Edit', 'Flatten', 'Delete', 'Move to Trash'], | |
293 | primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' } | |
294 | }, | |
295 | update: { | |
296 | actions: ['Edit', 'Flatten'], | |
297 | primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' } | |
298 | }, | |
299 | delete: { | |
300 | actions: ['Delete', 'Move to Trash'], | |
301 | primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' } | |
302 | }, | |
303 | 'no-permissions': { | |
304 | actions: [], | |
305 | primary: { multiple: '', executing: '', single: '', no: '' } | |
306 | } | |
11fdf7f2 TL |
307 | }); |
308 | }); | |
ec96510d FG |
309 | |
310 | const getActionDisable = (name: string) => | |
311 | component.tableActions.find((o) => o.name === name).disable; | |
312 | ||
313 | const testActions = (selection: any, expected: { [action: string]: string | boolean }) => { | |
314 | expect(getActionDisable('Edit')(selection)).toBe(expected.edit || false); | |
315 | expect(getActionDisable('Delete')(selection)).toBe(expected.delete || false); | |
316 | expect(getActionDisable('Copy')(selection)).toBe(expected.copy || false); | |
317 | expect(getActionDisable('Flatten')(selection)).toBeTruthy(); | |
318 | expect(getActionDisable('Move to Trash')(selection)).toBe(expected.moveTrash || false); | |
319 | }; | |
320 | ||
321 | it('should disable edit, copy, flatten and move action if RBD is in status `Removing`', () => { | |
322 | component.selection.selected = [ | |
323 | { | |
324 | name: 'foobar', | |
325 | pool_name: 'rbd', | |
326 | snapshots: [], | |
327 | source: 'REMOVING' | |
328 | } | |
329 | ]; | |
330 | ||
331 | const message = `Action not possible for an RBD in status 'Removing'`; | |
332 | const expected = { | |
333 | edit: message, | |
334 | copy: message, | |
335 | moveTrash: message | |
336 | }; | |
337 | testActions(component.selection, expected); | |
338 | }); | |
11fdf7f2 | 339 | }); |