Fixing various details in the control-panel
[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   status: "ENABLED"
73 } as Job;
74 const job2 = {
75   jobId: "job2",
76   typeId: "type1",
77   owner: "owner1",
78   targetUri: "http://one",
79   prodIds: ["producer1"],
80   status: "ENABLED"
81 } as Job;
82
83 describe("JobsListComponent", () => {
84   let loader: HarnessLoader;
85
86   beforeEach(async(() => {
87     const spy = jasmine.createSpyObj("EIService", [
88       "getJobIds",
89       "getJobInfo",
90       "getConsumerStatus",
91     ]);
92
93     TestBed.configureTestingModule({
94       imports: [
95         MatTableModule,
96         MatPaginatorModule,
97         FormsModule,
98         MatSortModule,
99         ReactiveFormsModule,
100         BrowserAnimationsModule,
101         MatFormFieldModule,
102         MatInputModule,
103       ],
104       schemas: [CUSTOM_ELEMENTS_SCHEMA],
105       declarations: [JobsListComponent],
106       providers: [{ provide: ConsumerService, useValue: spy }, UiService],
107     })
108       .compileComponents()
109       .then(() => {
110         fixture = TestBed.createComponent(JobsListComponent);
111         component = fixture.componentInstance;
112         loader = TestbedHarnessEnvironment.loader(fixture);
113       });
114   }));
115
116   const expectedJob1Row = {
117     jobId: "job1",
118     prodIds: "producer1",
119     typeId: "type1",
120     owner: "owner1",
121     targetUri: "http://one",
122     status: "ENABLED"
123   };
124
125   it("should create", () => {
126     expect(component).toBeTruthy();
127   });
128
129   describe("#content", () => {
130     it("should loadJobs", fakeAsync(() => {
131       setServiceSpy();
132       const newSub: Subscription = component.dataSubscription();
133       tick(0);
134
135       const actualJobs: Job[] = component.jobs();
136       expect(actualJobs.length).toEqual(2);
137       expect(actualJobs).toEqual([job1, job2]);
138       newSub.unsubscribe();
139     }));
140
141     it("should contain job table with correct columns", fakeAsync(() => {
142       setServiceSpy();
143       component.ngOnInit();
144       tick(0);
145
146       loader
147         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
148         .then((loadTable) => {
149           loadTable.getHeaderRows().then((headerRow) => {
150             headerRow[0].getCellTextByColumnName().then((header) => {
151               expect(header).toEqual({
152                 jobId: "Job ID",
153                 prodIds: "Producers",
154                 typeId: "Type ID",
155                 owner: "Owner",
156                 targetUri: "Target URI",
157                 status: "Status"
158               });
159             });
160           });
161         });
162
163       discardPeriodicTasks();
164     }));
165
166     it("should set correct dark mode from UIService", fakeAsync(() => {
167       setServiceSpy();
168       component.ngOnInit();
169       tick(0);
170
171       const uiService: UiService = TestBed.inject(UiService);
172       expect(component.darkMode).toBeTruthy();
173
174       uiService.darkModeState.next(false);
175       fixture.detectChanges();
176       expect(component.darkMode).toBeFalsy();
177       discardPeriodicTasks();
178     }));
179   });
180
181   describe("#jobsTable", () => {
182     it("should contain data after initialization", fakeAsync(() => {
183       setServiceSpy();
184       component.ngOnInit();
185       tick(0);
186
187       const expectedJobRows = [
188         expectedJob1Row,
189         {
190           jobId: "job2",
191           prodIds: "producer1",
192           typeId: "type1",
193           owner: "owner1",
194           targetUri: "http://one",
195           status: "ENABLED"
196         },
197       ];
198
199       loader
200         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
201         .then((loadTable) => {
202           loadTable.getRows().then((jobRows) => {
203             expect(jobRows.length).toEqual(2);
204             jobRows.forEach((row) => {
205               row.getCellTextByColumnName().then((values) => {
206                 expect(expectedJobRows).toContain(
207                   jasmine.objectContaining(values)
208                 );
209               });
210             });
211           });
212         });
213       discardPeriodicTasks();
214     }));
215
216     it("should display default values for non required properties ", fakeAsync(() => {
217       const jobMissingProperties = {
218         job_definition: {
219           jobparam2: "value2_job2",
220           jobparam3: "value3_job2",
221           jobparam1: "value1_job2",
222         },
223         job_result_uri: "http://one",
224       } as JobInfo;
225       const jobMissingPropertiesStatus = {
226         info_job_status: "ENABLED",
227         producers: ["producer1"],
228       } as ConsumerStatus;
229
230       let consumerServiceSpy = TestBed.inject(
231         ConsumerService
232       ) as jasmine.SpyObj<ConsumerService>;
233       consumerServiceSpy.getJobIds.and.returnValue(of(["job1"]));
234       consumerServiceSpy.getJobInfo.and.returnValue(of(jobMissingProperties));
235       consumerServiceSpy.getConsumerStatus.and.returnValue(
236         of(jobMissingPropertiesStatus)
237       );
238
239       component.ngOnInit();
240       tick(0);
241       const expectedJobRow = {
242         jobId: "job1",
243         prodIds: "producer1",
244         typeId: "< No type >",
245         owner: "< No owner >",
246         targetUri: "http://one",
247         status: "ENABLED",
248       };
249
250       loader
251         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
252         .then((loadTable) => {
253           loadTable.getRows().then((jobRows) => {
254             jobRows[0].getCellTextByColumnName().then((value) => {
255               expect(expectedJobRow).toEqual(jasmine.objectContaining(value));
256             });
257           });
258         });
259       discardPeriodicTasks();
260     }));
261
262     it("filtering", fakeAsync(() => {
263       setServiceSpy();
264       component.ngOnInit();
265       tick(0);
266
267       loader
268         .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
269         .then((loadTable) => {
270           loader
271             .getHarness(MatInputHarness.with({ selector: "#jobIdFilter" }))
272             .then((idFilter) => {
273               tick(10);
274               idFilter.setValue("1").then((_) => {
275                 tick(10);
276                 loadTable.getRows().then((jobRows) => {
277                   expect(jobRows.length).toEqual(1);
278                   jobRows[0].getCellTextByColumnName().then((value) => {
279                     expect(expectedJob1Row).toEqual(
280                       jasmine.objectContaining(value)
281                     );
282                     idFilter.setValue("");
283                     flushMicrotasks();
284                   });
285                 });
286               });
287             });
288
289           loader
290             .getHarness(MatInputHarness.with({ selector: "#jobTypeIdFilter" }))
291             .then((typeIdFilter) => {
292               tick(10);
293               typeIdFilter.setValue("1").then((_) => {
294                 loadTable.getRows().then((jobRows) => {
295                   expect(jobRows.length).toEqual(2);
296                   jobRows[0].getCellTextByColumnName().then((value) => {
297                     expect(expectedJob1Row).toEqual(
298                       jasmine.objectContaining(value)
299                     );
300                     typeIdFilter.setValue("");
301                     flushMicrotasks();
302                   });
303                 });
304               });
305             });
306
307           loader
308             .getHarness(MatInputHarness.with({ selector: "#jobOwnerFilter" }))
309             .then((ownerFilter) => {
310               tick(10);
311               ownerFilter.setValue("1").then((_) => {
312                 loadTable.getRows().then((jobRows) => {
313                   expect(jobRows.length).toEqual(2);
314                   jobRows[0].getCellTextByColumnName().then((value) => {
315                     expect(expectedJob1Row).toEqual(
316                       jasmine.objectContaining(value)
317                     );
318                     ownerFilter.setValue("");
319                     flushMicrotasks();
320                   });
321                 });
322               });
323             });
324
325           loader
326             .getHarness(
327               MatInputHarness.with({ selector: "#jobTargetUriFilter" })
328             )
329             .then((targetUriFilter) => {
330               tick(10);
331               targetUriFilter.setValue("one").then((_) => {
332                 loadTable.getRows().then((jobRows) => {
333                   expect(jobRows.length).toEqual(2);
334                   jobRows[0].getCellTextByColumnName().then((value) => {
335                     expect(expectedJob1Row).toEqual(
336                       jasmine.objectContaining(value)
337                     );
338                     targetUriFilter.setValue("");
339                     flushMicrotasks();
340                   });
341                 });
342               });
343             });
344
345             loader
346             .getHarness(
347               MatInputHarness.with({ selector: "#jobStatusFilter" })
348             )
349             .then((statusFilter) => {
350               tick(10);
351               statusFilter.setValue("ENA").then((_) => {
352                 loadTable.getRows().then((jobRows) => {
353                   expect(jobRows.length).toEqual(2);
354                   jobRows[0].getCellTextByColumnName().then((value) => {
355                     expect(expectedJob1Row).toEqual(
356                       jasmine.objectContaining(value)
357                     );
358                     statusFilter.setValue("");
359                     flushMicrotasks();
360                   });
361                 });
362               });
363             });
364         });
365       discardPeriodicTasks();
366     }));
367
368     describe("#sorting", () => {
369       it("should verify sort functionality on the table", fakeAsync(() => {
370         setServiceSpy();
371         tick(0);
372
373         let sort = loader.getHarness(MatSortHarness);
374         tick(10);
375
376         sort.then((value) => {
377           value.getSortHeaders({ sortDirection: "" }).then((headers) => {
378             expect(headers.length).toBe(6);
379
380             headers[0].click();
381             tick(10);
382             headers[0].isActive().then((value) => {
383               expect(value).toBe(true);
384             });
385             headers[0].getSortDirection().then((value) => {
386               expect(value).toBe("asc");
387             });
388
389             headers[0].click();
390             tick(10);
391             headers[0].getSortDirection().then((value) => {
392               expect(value).toBe("desc");
393             });
394           });
395         });
396         discardPeriodicTasks();
397       }));
398
399       it("should sort table asc and desc by first header", fakeAsync(() => {
400         setServiceSpy();
401         tick(0);
402
403         let sort = loader.getHarness(MatSortHarness);
404         tick(10);
405
406         sort.then((value) => {
407           loader
408             .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
409             .then((loadTable) => {
410               tick(10);
411               value.getSortHeaders().then((headers) => {
412                 headers[0].click();
413                 tick(10);
414                 headers[0].getSortDirection().then((direction) => {
415                   expect(direction).toBe("asc");
416                 });
417                 loadTable.getRows().then((jobRows) => {
418                   jobRows[0].getCellTextByColumnName().then((value) => {
419                     expect(expectedJob1Row).toEqual(
420                       jasmine.objectContaining(value)
421                     );
422                   });
423                 });
424
425                 headers[0].click();
426                 tick(10);
427                 headers[0].getSortDirection().then((direction) => {
428                   expect(direction).toBe("desc");
429                 });
430                 loadTable.getRows().then((jobRows) => {
431                   jobRows[jobRows.length - 1]
432                     .getCellTextByColumnName()
433                     .then((value) => {
434                       expect(expectedJob1Row).toEqual(
435                         jasmine.objectContaining(value)
436                       );
437                     });
438                 });
439               });
440             });
441         });
442         discardPeriodicTasks();
443       }));
444
445       it("should not sort when clicking on filtering box", fakeAsync(() => {
446         const expectedJobRow = {
447           jobId: "job2",
448           prodIds: "producer1",
449           typeId: "type1",
450           owner: "owner1",
451           targetUri: "http://one",
452           status: "ENABLED"
453         };
454
455         setServiceSpy();
456         component.ngOnInit();
457         tick(0);
458
459         loader
460           .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
461           .then((loadTable) => {
462             loader
463               .getHarness(MatInputHarness.with({ selector: "#jobIdFilter" }))
464               .then((idFilter) => {
465                 tick(10);
466                 idFilter.setValue("").then((_) => {
467                   loadTable.getRows().then((jobRows) => {
468                     expect(jobRows.length).toEqual(2);
469                     jobRows[1].getCellTextByColumnName().then((value) => {
470                       expect(expectedJobRow).toEqual(
471                         jasmine.objectContaining(value)
472                       );
473                     });
474                   });
475                 });
476               });
477             loader
478               .getHarness(
479                 MatInputHarness.with({ selector: "#jobTypeIdFilter" })
480               )
481               .then((typeIdFilter) => {
482                 tick(10);
483                 typeIdFilter.setValue("").then((_) => {
484                   loadTable.getRows().then((jobRows) => {
485                     expect(jobRows.length).toEqual(2);
486                     jobRows[1].getCellTextByColumnName().then((value) => {
487                       expect(expectedJobRow).toEqual(
488                         jasmine.objectContaining(value)
489                       );
490                     });
491                   });
492                 });
493               });
494             loader
495               .getHarness(MatInputHarness.with({ selector: "#jobOwnerFilter" }))
496               .then((ownerFilter) => {
497                 tick(10);
498                 ownerFilter.setValue("").then((_) => {
499                   loadTable.getRows().then((jobRows) => {
500                     expect(jobRows.length).toEqual(2);
501                     jobRows[1].getCellTextByColumnName().then((value) => {
502                       expect(expectedJobRow).toEqual(
503                         jasmine.objectContaining(value)
504                       );
505                     });
506                   });
507                 });
508               });
509             loader
510               .getHarness(
511                 MatInputHarness.with({ selector: "#jobTargetUriFilter" })
512               )
513               .then((targetUriFilter) => {
514                 tick(10);
515                 targetUriFilter.setValue("").then((_) => {
516                   loadTable.getRows().then((jobRows) => {
517                     expect(jobRows.length).toEqual(2);
518                     jobRows[1].getCellTextByColumnName().then((value) => {
519                       expect(expectedJobRow).toEqual(
520                         jasmine.objectContaining(value)
521                       );
522                     });
523                   });
524                 });
525               });
526               loader
527               .getHarness(
528                 MatInputHarness.with({ selector: "#jobStatusFilter" })
529               )
530               .then((statusFilter) => {
531                 tick(10);
532                 statusFilter.setValue("").then((_) => {
533                   loadTable.getRows().then((jobRows) => {
534                     expect(jobRows.length).toEqual(2);
535                     jobRows[1].getCellTextByColumnName().then((value) => {
536                       expect(expectedJobRow).toEqual(
537                         jasmine.objectContaining(value)
538                       );
539                     });
540                   });
541                 });
542               });
543           });
544         discardPeriodicTasks();
545       }));
546     });
547
548     describe("#paging", () => {
549       it("should work properly on the table", fakeAsync(() => {
550         let consumerServiceSpy = TestBed.inject(
551           ConsumerService
552         ) as jasmine.SpyObj<ConsumerService>;
553         consumerServiceSpy.getJobIds.and.returnValue(
554           of(["job1", "job2", "job3", "job4", "job5", "job6", "job7"])
555         );
556         consumerServiceSpy.getJobInfo.and.returnValue(of(jobInfo1));
557         consumerServiceSpy.getConsumerStatus.and.returnValue(of(jobStatus1));
558         component.ngOnInit();
559         tick();
560
561         loader.getHarness(MatPaginatorHarness).then((paging) => {
562           paging.setPageSize(5);
563           tick(1000);
564           loader
565             .getHarness(MatTableHarness.with({ selector: "#jobsTable" }))
566             .then((loadTable) => {
567               loadTable.getRows().then((jobRows) => {
568                 expect(jobRows.length).toEqual(5);
569               });
570
571               paging.goToNextPage();
572               tick(1000);
573
574               loadTable.getRows().then((jobRows) => {
575                 expect(jobRows.length).toEqual(2);
576                 jobRows[0].getCellTextByColumnName().then((value) => {
577                   const expectedRow = {
578                     jobId: "job6",
579                     prodIds: "producer1",
580                     typeId: "type1",
581                     owner: "owner1",
582                     targetUri: "http://one",
583                     status: "ENABLED",
584                   };
585                   discardPeriodicTasks();
586                   expect(expectedRow).toEqual(jasmine.objectContaining(value));
587                 });
588               });
589             });
590         });
591       }));
592     });
593   });
594 });
595
596 function setServiceSpy() {
597   let consumerServiceSpy = TestBed.inject(
598     ConsumerService
599   ) as jasmine.SpyObj<ConsumerService>;
600   consumerServiceSpy.getJobIds.and.returnValue(of(["job1", "job2"]));
601   consumerServiceSpy.getJobInfo.and.returnValue(of(jobInfo1));
602   consumerServiceSpy.getConsumerStatus.and.returnValue(of(jobStatus1));
603 }