Consumer service
[portal/nonrtric-controlpanel.git] / webapp-frontend / src / app / ei-coordinator / jobs-list / jobs-list.component.spec.ts
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2021 Nordix Foundation
6  * %%
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================LICENSE_END===================================
19  */
20 import { HarnessLoader } from "@angular/cdk/testing";
21 import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
22 import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
23 import {
24   async,
25   ComponentFixture,
26   discardPeriodicTasks,
27   fakeAsync,
28   TestBed,
29   tick,
30   flushMicrotasks,
31 } from "@angular/core/testing";
32 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
33 import { MatFormFieldModule } from "@angular/material/form-field";
34 import { MatInputModule } from "@angular/material/input";
35 import { MatInputHarness } from "@angular/material/input/testing";
36 import { MatPaginatorModule } from "@angular/material/paginator";
37 import { MatSortModule } from "@angular/material/sort";
38 import { MatSortHarness } from "@angular/material/sort/testing";
39 import { MatPaginatorHarness } from "@angular/material/paginator/testing";
40 import { MatTableModule } from "@angular/material/table";
41 import { MatTableHarness } from "@angular/material/table/testing";
42 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
43 import { of } from "rxjs/observable/of";
44 import { ConsumerStatus, JobInfo } from "@interfaces/consumer.types";
45 import { UiService } from "@services/ui/ui.service";
46
47 import { Job, JobsListComponent } from "./jobs-list.component";
48 import { Subscription } from "rxjs";
49 import { ConsumerService } from "@services/ei/consumer.service";
50
51 let component: JobsListComponent;
52 let fixture: ComponentFixture<JobsListComponent>;
53
54 const jobInfo1 = {
55   info_type_id: "type1",
56   job_owner: "owner1",
57   job_result_uri: "http://one",
58   job_definition: {},
59 } as JobInfo;
60
61 const jobStatus1 = {
62   info_job_status: "ENABLED",
63   producers: ["producer1"],
64 } as ConsumerStatus;
65
66 const job1 = {
67   jobId: "job1",
68   typeId: "type1",
69   owner: "owner1",
70   targetUri: "http://one",
71   prodIds: ["producer1"],
72 } as Job;
73 const job2 = {
74   jobId: "job2",
75   typeId: "type1",
76   owner: "owner1",
77   targetUri: "http://one",
78   prodIds: ["producer1"],
79 } as Job;
80
81 describe("JobsListComponent", () => {
82   let loader: HarnessLoader;
83
84   beforeEach(async(() => {
85     const spy = jasmine.createSpyObj("EIService", [
86       "getJobIds",
87       "getJobInfo",
88       "getConsumerStatus",
89     ]);
90
91     TestBed.configureTestingModule({
92       imports: [
93         MatTableModule,
94         MatPaginatorModule,
95         FormsModule,
96         MatSortModule,
97         ReactiveFormsModule,
98         BrowserAnimationsModule,
99         MatFormFieldModule,
100         MatInputModule,
101       ],
102       schemas: [CUSTOM_ELEMENTS_SCHEMA],
103       declarations: [JobsListComponent],
104       providers: [{ provide: ConsumerService, useValue: spy }, UiService],
105     })
106       .compileComponents()
107       .then(() => {
108         fixture = TestBed.createComponent(JobsListComponent);
109         component = fixture.componentInstance;
110         loader = TestbedHarnessEnvironment.loader(fixture);
111       });
112   }));
113
114   const expectedJob1Row = {
115     jobId: "job1",
116     prodIds: "producer1",
117     typeId: "type1",
118     owner: "owner1",
119     targetUri: "http://one",
120   };
121
122   it("should create", () => {
123     expect(component).toBeTruthy();
124   });
125
126   describe("#content", () => {
127     it("should loadJobs", fakeAsync(() => {
128       setServiceSpy();
129       const newSub: Subscription = component.dataSubscription();
130       tick(0);
131
132       const actualJobs: Job[] = component.jobs();
133       expect(actualJobs.length).toEqual(2);
134       expect(actualJobs).toEqual([job1, job2]);
135       newSub.unsubscribe();
136     }));
137
138     it("should contain job table with correct columns", fakeAsync(() => {
139       setServiceSpy();
140       component.ngOnInit();
141       tick(0);
142
143       loader
144         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
145         .then((loadTable) => {
146           loadTable.getHeaderRows().then((headerRow) => {
147             headerRow[0].getCellTextByColumnName().then((header) => {
148               expect(header).toEqual({
149                 jobId: "Job ID",
150                 prodIds: "Producers",
151                 typeId: "Type ID",
152                 owner: "Owner",
153                 targetUri: "Target URI",
154               });
155             });
156           });
157         });
158
159       discardPeriodicTasks();
160     }));
161
162     it("should set correct dark mode from UIService", fakeAsync(() => {
163       setServiceSpy();
164       component.ngOnInit();
165       tick(0);
166
167       const uiService: UiService = TestBed.inject(UiService);
168       expect(component.darkMode).toBeTruthy();
169
170       uiService.darkModeState.next(false);
171       fixture.detectChanges();
172       expect(component.darkMode).toBeFalsy();
173       discardPeriodicTasks();
174     }));
175   });
176
177   describe("#jobsTable", () => {
178     it("should contain data after initialization", fakeAsync(() => {
179       setServiceSpy();
180       component.ngOnInit();
181       tick(0);
182
183       const expectedJobRows = [
184         expectedJob1Row,
185         {
186           jobId: "job2",
187           prodIds: "producer1",
188           typeId: "type1",
189           owner: "owner1",
190           targetUri: "http://one",
191         },
192       ];
193
194       loader
195         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
196         .then((loadTable) => {
197           loadTable.getRows().then((jobRows) => {
198             expect(jobRows.length).toEqual(2);
199             jobRows.forEach((row) => {
200               row.getCellTextByColumnName().then((values) => {
201                 expect(expectedJobRows).toContain(
202                   jasmine.objectContaining(values)
203                 );
204               });
205             });
206           });
207         });
208       discardPeriodicTasks();
209     }));
210
211     it("should display default values for non required properties ", fakeAsync(() => {
212       const jobMissingProperties = {
213         job_definition: {
214           jobparam2: "value2_job2",
215           jobparam3: "value3_job2",
216           jobparam1: "value1_job2",
217         },
218         job_result_uri: "http://one",
219       } as JobInfo;
220       const jobMissingPropertiesStatus = {
221         info_job_status: "ENABLED",
222         producers: ["producer1"],
223       } as ConsumerStatus;
224
225       let consumerServiceSpy = TestBed.inject(
226         ConsumerService
227       ) as jasmine.SpyObj<ConsumerService>;
228       consumerServiceSpy.getJobIds.and.returnValue(of(["job1"]));
229       consumerServiceSpy.getJobInfo.and.returnValue(of(jobMissingProperties));
230       consumerServiceSpy.getConsumerStatus.and.returnValue(
231         of(jobMissingPropertiesStatus)
232       );
233
234       component.ngOnInit();
235       tick(0);
236       const expectedJobRow = {
237         jobId: "job1",
238         prodIds: "producer1",
239         typeId: "< No type >",
240         owner: "< No owner >",
241         targetUri: "http://one",
242       };
243
244       loader
245         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
246         .then((loadTable) => {
247           loadTable.getRows().then((jobRows) => {
248             jobRows[0].getCellTextByColumnName().then((value) => {
249               expect(expectedJobRow).toEqual(jasmine.objectContaining(value));
250             });
251           });
252         });
253       discardPeriodicTasks();
254     }));
255
256     it("filtering", fakeAsync(() => {
257       setServiceSpy();
258       component.ngOnInit();
259       tick(0);
260
261       loader
262         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
263         .then((loadTable) => {
264           loader
265             .getHarness(MatInputHarness.with({ selector: "#jobIdFilter" }))
266             .then((idFilter) => {
267               tick(10);
268               idFilter.setValue("1").then((_) => {
269                 tick(10);
270                 loadTable.getRows().then((jobRows) => {
271                   expect(jobRows.length).toEqual(1);
272                   jobRows[0].getCellTextByColumnName().then((value) => {
273                     expect(expectedJob1Row).toEqual(
274                       jasmine.objectContaining(value)
275                     );
276                     idFilter.setValue("");
277                     flushMicrotasks();
278                   });
279                 });
280               });
281             });
282
283           loader
284             .getHarness(MatInputHarness.with({ selector: "#jobTypeIdFilter" }))
285             .then((typeIdFilter) => {
286               tick(10);
287               typeIdFilter.setValue("1").then((_) => {
288                 loadTable.getRows().then((jobRows) => {
289                   expect(jobRows.length).toEqual(2);
290                   jobRows[0].getCellTextByColumnName().then((value) => {
291                     expect(expectedJob1Row).toEqual(
292                       jasmine.objectContaining(value)
293                     );
294                     typeIdFilter.setValue("");
295                     flushMicrotasks();
296                   });
297                 });
298               });
299             });
300
301           loader
302             .getHarness(MatInputHarness.with({ selector: "#jobOwnerFilter" }))
303             .then((ownerFilter) => {
304               tick(10);
305               ownerFilter.setValue("1").then((_) => {
306                 loadTable.getRows().then((jobRows) => {
307                   expect(jobRows.length).toEqual(2);
308                   jobRows[0].getCellTextByColumnName().then((value) => {
309                     expect(expectedJob1Row).toEqual(
310                       jasmine.objectContaining(value)
311                     );
312                     ownerFilter.setValue("");
313                     flushMicrotasks();
314                   });
315                 });
316               });
317             });
318
319           loader
320             .getHarness(
321               MatInputHarness.with({ selector: "#jobTargetUriFilter" })
322             )
323             .then((targetUriFilter) => {
324               tick(10);
325               targetUriFilter.setValue("one").then((_) => {
326                 loadTable.getRows().then((jobRows) => {
327                   expect(jobRows.length).toEqual(2);
328                   jobRows[0].getCellTextByColumnName().then((value) => {
329                     expect(expectedJob1Row).toEqual(
330                       jasmine.objectContaining(value)
331                     );
332                     targetUriFilter.setValue("");
333                     flushMicrotasks();
334                   });
335                 });
336               });
337             });
338         });
339       discardPeriodicTasks();
340     }));
341
342     describe("#sorting", () => {
343       it("should verify sort functionality on the table", fakeAsync(() => {
344         setServiceSpy();
345         tick(0);
346
347         let sort = loader.getHarness(MatSortHarness);
348         tick(10);
349
350         sort.then((value) => {
351           value.getSortHeaders({ sortDirection: "" }).then((headers) => {
352             expect(headers.length).toBe(5);
353
354             headers[0].click();
355             tick(10);
356             headers[0].isActive().then((value) => {
357               expect(value).toBe(true);
358             });
359             headers[0].getSortDirection().then((value) => {
360               expect(value).toBe("asc");
361             });
362
363             headers[0].click();
364             tick(10);
365             headers[0].getSortDirection().then((value) => {
366               expect(value).toBe("desc");
367             });
368           });
369         });
370         discardPeriodicTasks();
371       }));
372
373       it("should sort table asc and desc by first header", fakeAsync(() => {
374         setServiceSpy();
375         tick(0);
376
377         let sort = loader.getHarness(MatSortHarness);
378         tick(10);
379
380         sort.then((value) => {
381           loader
382             .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
383             .then((loadTable) => {
384               tick(10);
385               value.getSortHeaders().then((headers) => {
386                 headers[0].click();
387                 tick(10);
388                 headers[0].getSortDirection().then((direction) => {
389                   expect(direction).toBe("asc");
390                 });
391                 loadTable.getRows().then((jobRows) => {
392                   jobRows[0].getCellTextByColumnName().then((value) => {
393                     expect(expectedJob1Row).toEqual(
394                       jasmine.objectContaining(value)
395                     );
396                   });
397                 });
398
399                 headers[0].click();
400                 tick(10);
401                 headers[0].getSortDirection().then((direction) => {
402                   expect(direction).toBe("desc");
403                 });
404                 loadTable.getRows().then((jobRows) => {
405                   jobRows[jobRows.length - 1]
406                     .getCellTextByColumnName()
407                     .then((value) => {
408                       expect(expectedJob1Row).toEqual(
409                         jasmine.objectContaining(value)
410                       );
411                     });
412                 });
413               });
414             });
415         });
416         discardPeriodicTasks();
417       }));
418
419       it("should not sort when clicking on filtering box", fakeAsync(() => {
420         const expectedJobRow = {
421           jobId: "job2",
422           prodIds: "producer1",
423           typeId: "type1",
424           owner: "owner1",
425           targetUri: "http://one",
426         };
427
428         setServiceSpy();
429         component.ngOnInit();
430         tick(0);
431
432         loader
433           .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
434           .then((loadTable) => {
435             loader
436               .getHarness(MatInputHarness.with({ selector: "#jobIdFilter" }))
437               .then((idFilter) => {
438                 tick(10);
439                 idFilter.setValue("").then((_) => {
440                   loadTable.getRows().then((jobRows) => {
441                     expect(jobRows.length).toEqual(2);
442                     jobRows[1].getCellTextByColumnName().then((value) => {
443                       expect(expectedJobRow).toEqual(
444                         jasmine.objectContaining(value)
445                       );
446                     });
447                   });
448                 });
449               });
450             loader
451               .getHarness(
452                 MatInputHarness.with({ selector: "#jobTypeIdFilter" })
453               )
454               .then((typeIdFilter) => {
455                 tick(10);
456                 typeIdFilter.setValue("").then((_) => {
457                   loadTable.getRows().then((jobRows) => {
458                     expect(jobRows.length).toEqual(2);
459                     jobRows[1].getCellTextByColumnName().then((value) => {
460                       expect(expectedJobRow).toEqual(
461                         jasmine.objectContaining(value)
462                       );
463                     });
464                   });
465                 });
466               });
467             loader
468               .getHarness(MatInputHarness.with({ selector: "#jobOwnerFilter" }))
469               .then((ownerFilter) => {
470                 tick(10);
471                 ownerFilter.setValue("").then((_) => {
472                   loadTable.getRows().then((jobRows) => {
473                     expect(jobRows.length).toEqual(2);
474                     jobRows[1].getCellTextByColumnName().then((value) => {
475                       expect(expectedJobRow).toEqual(
476                         jasmine.objectContaining(value)
477                       );
478                     });
479                   });
480                 });
481               });
482             loader
483               .getHarness(
484                 MatInputHarness.with({ selector: "#jobTargetUriFilter" })
485               )
486               .then((targetUriFilter) => {
487                 tick(10);
488                 targetUriFilter.setValue("").then((_) => {
489                   loadTable.getRows().then((jobRows) => {
490                     expect(jobRows.length).toEqual(2);
491                     jobRows[1].getCellTextByColumnName().then((value) => {
492                       expect(expectedJobRow).toEqual(
493                         jasmine.objectContaining(value)
494                       );
495                     });
496                   });
497                 });
498               });
499           });
500         discardPeriodicTasks();
501       }));
502     });
503
504     describe("#paging", () => {
505       it("should work properly on the table", fakeAsync(() => {
506         let consumerServiceSpy = TestBed.inject(
507           ConsumerService
508         ) as jasmine.SpyObj<ConsumerService>;
509         consumerServiceSpy.getJobIds.and.returnValue(
510           of(["job1", "job2", "job3", "job4", "job5", "job6", "job7"])
511         );
512         consumerServiceSpy.getJobInfo.and.returnValue(of(jobInfo1));
513         consumerServiceSpy.getConsumerStatus.and.returnValue(of(jobStatus1));
514         component.ngOnInit();
515         tick();
516
517         loader.getHarness(MatPaginatorHarness).then((paging) => {
518           paging.setPageSize(5);
519           tick(1000);
520           loader
521             .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
522             .then((loadTable) => {
523               loadTable.getRows().then((jobRows) => {
524                 expect(jobRows.length).toEqual(5);
525               });
526
527               paging.goToNextPage();
528               tick(1000);
529
530               loadTable.getRows().then((jobRows) => {
531                 expect(jobRows.length).toEqual(2);
532                 jobRows[0].getCellTextByColumnName().then((value) => {
533                   const expectedRow = {
534                     jobId: "job6",
535                     prodIds: "producer1",
536                     typeId: "type1",
537                     owner: "owner1",
538                     targetUri: "http://one",
539                   };
540                   discardPeriodicTasks();
541                   expect(expectedRow).toEqual(jasmine.objectContaining(value));
542                 });
543               });
544             });
545         });
546       }));
547     });
548   });
549 });
550
551 function setServiceSpy() {
552   let consumerServiceSpy = TestBed.inject(
553     ConsumerService
554   ) as jasmine.SpyObj<ConsumerService>;
555   consumerServiceSpy.getJobIds.and.returnValue(of(["job1", "job2"]));
556   consumerServiceSpy.getJobInfo.and.returnValue(of(jobInfo1));
557   consumerServiceSpy.getConsumerStatus.and.returnValue(of(jobStatus1));
558 }