From 1f5664f310dc6a18ecc10dbd2a0705500b2e15c9 Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Thu, 25 Feb 2021 14:32:33 +0100 Subject: [PATCH] Create component for EI Producers list Change-Id: I9e39a731193f1253b2b2a1fc3b5e41fd524c0a3b Signed-off-by: elinuxhenrik Issue-ID: NONRTRIC-455 --- webapp-frontend/src/app/app.module.ts | 3 + .../ei-coordinator/ei-coordinator.component.html | 52 +------ .../ei-coordinator.component.spec.ts | 167 ++++++--------------- .../app/ei-coordinator/ei-coordinator.component.ts | 65 +------- .../producers-list/producers-list.component.html | 50 ++++++ .../producers-list/producers-list.component.scss | 7 + .../producers-list.component.spec.ts | 140 +++++++++++++++++ .../producers-list/producers-list.component.ts | 96 ++++++++++++ 8 files changed, 349 insertions(+), 231 deletions(-) create mode 100644 webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.html create mode 100644 webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.scss create mode 100644 webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.spec.ts create mode 100644 webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.ts diff --git a/webapp-frontend/src/app/app.module.ts b/webapp-frontend/src/app/app.module.ts index be4e434..20fe471 100644 --- a/webapp-frontend/src/app/app.module.ts +++ b/webapp-frontend/src/app/app.module.ts @@ -68,6 +68,7 @@ import { EICoordinatorComponent } from './ei-coordinator/ei-coordinator.componen import { HttpMockRequestInterceptor } from './interceptor.mock'; import { environment } from 'src/environments/environment'; import { HttpRequestInterceptor } from './interceptor'; +import { ProducersListComponent } from './ei-coordinator/producers-list/producers-list.component'; export const isMock = environment.mock; @@ -82,6 +83,7 @@ export const isMock = environment.mock; MainComponent, NodeModulesComponent, SidenavListComponent, + ProducersListComponent, ], imports: [ AppRoutingModule, @@ -144,6 +146,7 @@ export const isMock = environment.mock; providers: [ CookieService, ErrorDialogService, + ProducersListComponent, UiService, { provide: HTTP_INTERCEPTORS, diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html index fc560dd..57a87f9 100644 --- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html @@ -29,57 +29,7 @@ limitations under the License.

Producers

- -
- - - -
-
- - - Producer ID - -
-
-
- {{eiProducer.ei_producer_id}} -
- - - -
-
- - - Producer types - -
-
-
- {{this.getProducerTypes(eiProducer)}} -
- - - -
-
- - - Producer status - -
-
-
- {{this.getProducerStatus(eiProducer)}} -
- - - -
-
+

Jobs

diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts index d516b14..5cda8e5 100644 --- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts @@ -22,37 +22,28 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { HarnessLoader } from '@angular/cdk/testing'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonHarness } from '@angular/material/button/testing'; import { MatIconModule } from '@angular/material/icon'; import { MatInputHarness } from '@angular/material/input/testing' import { MatTableModule } from '@angular/material/table'; import { MatTableHarness } from '@angular/material/table/testing'; -import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { of } from 'rxjs'; import { EICoordinatorComponent } from './ei-coordinator.component'; import { EIJobDataSource } from './ei-job.datasource'; -import { EIProducerDataSource } from './ei-producer.datasource'; import { UiService } from '../services/ui/ui.service'; -import { EIJob, EIProducer } from '../interfaces/ei.types'; +import { EIJob } from '../interfaces/ei.types'; +import { ProducersListComponent } from './producers-list/producers-list.component'; describe('EICoordinatorComponent', () => { let component: EICoordinatorComponent; let fixture: ComponentFixture; let loader: HarnessLoader; - let producerDataSourceSpy: jasmine.SpyObj; + let producersListSpy: jasmine.SpyObj; let jobDataSourceSpy: jasmine.SpyObj; - const producer1 = { - ei_producer_id: 'producer1', - ei_producer_types: [ 'type1', 'type2' ], - status: 'ENABLED' - } as EIProducer; - const producer2 = { - ei_producer_id: 'producer2', - ei_producer_types: [ 'type2', 'type3' ], - status: 'DISABLED' - } as EIProducer; - const job1 = { ei_job_identity: 'job1', ei_type_identity: 'type1', @@ -67,14 +58,14 @@ describe('EICoordinatorComponent', () => { } as EIJob; beforeEach(async () => { - producerDataSourceSpy = jasmine.createSpyObj('EIProducerDataSource', [ 'loadProducers', 'eiProducers', 'eiProducersSubject' ]); + producersListSpy = jasmine.createSpyObj('producersListSpy', ['refresh']); jobDataSourceSpy = jasmine.createSpyObj('EIJobDataSource', [ 'loadJobs', 'eiJobs', 'eiJobsSubject' ]); - - producerDataSourceSpy.eiProducersSubject.and.returnValue(of({ producers: [producer1, producer2] })); - jobDataSourceSpy.eiJobsSubject.and.returnValue(of({ jobs: [job1, job2] })); + + jobDataSourceSpy.eiJobsSubject.and.returnValue(of([ job1, job2 ])); await TestBed.configureTestingModule({ imports: [ + MatButtonModule, MatIconModule, MatTableModule, BrowserAnimationsModule, @@ -87,13 +78,13 @@ describe('EICoordinatorComponent', () => { EICoordinatorComponent ], providers: [ + { provide: ProducersListComponent, useValue: producersListSpy }, { provide: EIJobDataSource, useValue: jobDataSourceSpy }, - { provide: EIProducerDataSource, useValue: producerDataSourceSpy }, UiService, FormBuilder, ] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(EICoordinatorComponent); component = fixture.componentInstance; @@ -106,26 +97,23 @@ describe('EICoordinatorComponent', () => { }); describe('#content', () => { - it('should contain refresh button with correct icon', () => { - const button = fixture.debugElement.nativeElement.querySelector('#refreshButton'); - expect(button).toBeTruthy(); - expect(button.innerHTML).toContain('refresh'); + it('should contain refresh button with coorect icon', async () => { + let refreshButton = await loader.getHarness(MatButtonHarness.with({ selector: '#refreshButton' })); + expect(refreshButton).toBeTruthy(); + expect(await refreshButton.getText()).toEqual('refresh'); }); - it('should contain producers table with correct columns', async () => { - let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'})); - let headerRow = (await producersTable.getHeaderRows())[0]; - let headers = await headerRow.getCellTextByColumnName(); - - expect(headers).toEqual({id: 'Producer ID', types: 'Producer types', status: 'Producer status'}); + it('should contain producers table', async () => { + const producersTableComponent = fixture.debugElement.nativeElement.querySelector('nrcp-producers-list'); + expect(producersTableComponent).toBeTruthy(); }); it('should contain jobs table with correct columns', async () => { - let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'})); + let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' })); let headerRow = (await producersTable.getHeaderRows())[0]; let headers = await headerRow.getCellTextByColumnName(); - expect(headers).toEqual({id: 'Job ID', typeId: 'Type ID', owner: 'Owner', targetUri: 'Target URI'}); + expect(headers).toEqual({ id: 'Job ID', typeId: 'Type ID', owner: 'Owner', targetUri: 'Target URI' }); }); it('should set correct dark mode from UIService', () => { @@ -137,95 +125,19 @@ describe('EICoordinatorComponent', () => { expect(component.darkMode).toBeFalsy(); }); - }); - - describe('#producersTable', () => { - const expectedProducer1Row = { id: 'producer1', types: 'type1,type2', status: 'ENABLED' }; - beforeEach(() => { - const producers: EIProducer[] =[ producer1, producer2 ]; - producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers)); - }); - - it('should contain data after initialization', async () => { - component.ngOnInit(); - const expectedProducerRows = [ - expectedProducer1Row, - {id: 'producer2', types: 'type2,type3', status: 'DISABLED'} - ]; - let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'})); - let producerRows = await producersTable.getRows(); - expect(producerRows.length).toEqual(2); - producerRows.forEach(row => { - row.getCellTextByColumnName().then(values => { - expect(expectedProducerRows).toContain(jasmine.objectContaining(values)); - }); - }); - }); - - describe('should display default values for non required properties', () => { - it('producer defaults', async () => { - const producerMissingProperties = { - ei_producer_id: 'producer1' - } as EIProducer; - const producers: EIProducer[] =[ producerMissingProperties ]; - producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers)); - component.ngOnInit(); - - const expectedProducerRow = { id: 'producer1', types: '< No types >', status: '< No status >' }; - let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'})); - let producerRows = await producersTable.getRows(); - expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducerRow); - }); - - it('job defaults', async () => { - const jobMissingProperties = { - ei_job_identity: 'job1', - target_uri: 'http://one' - } as EIJob; - const jobs: EIJob[] =[ jobMissingProperties ]; - jobDataSourceSpy.eiJobsSubject.and.returnValue(of(jobs)); - component.ngOnInit(); - - const expectedJobRow = { id: 'job1', typeId: '< No type >', owner: '< No owner >', targetUri: 'http://one' }; - let jobsTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'})); - let jobRows = await jobsTable.getRows(); - expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJobRow); - }); - }); - - it('filtering', async () => { - component.ngOnInit(); - let producersTable = await loader.getHarness(MatTableHarness.with({selector: '#producersTable'})); - let idFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#producerIdFilter'})); - await idFilterInput.setValue("1"); - let producerRows = await producersTable.getRows(); - expect(producerRows.length).toEqual(1); - expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row); - - idFilterInput.setValue(''); - let typesFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#producerTypesFilter'})); - await typesFilterInput.setValue("1"); - producerRows = await producersTable.getRows(); - expect(producerRows.length).toEqual(1); - expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row); - await typesFilterInput.setValue("2"); - producerRows = await producersTable.getRows(); - expect(producerRows.length).toEqual(2); + it('should refresh tables', async () => { + let refreshButton = await loader.getHarness(MatButtonHarness.with({ selector: '#refreshButton' })); + await refreshButton.click(); - typesFilterInput.setValue(''); - let statusFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#producerStatusFilter'})); - await statusFilterInput.setValue("enabled"); - producerRows = await producersTable.getRows(); - expect(producerRows.length).toEqual(1); - expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row); + expect(producersListSpy.refresh).toHaveBeenCalled(); }); }); describe('#jobsTable', () => { const expectedJob1Row = { id: 'job1', typeId: 'type1', owner: 'owner1', targetUri: 'http://one' }; beforeEach(() => { - const jobs: EIJob[] =[ job1, job2 ]; + const jobs: EIJob[] = [ job1, job2 ]; jobDataSourceSpy.eiJobsSubject.and.returnValue(of(jobs)); }); @@ -235,7 +147,7 @@ describe('EICoordinatorComponent', () => { expectedJob1Row, { id: 'job2', typeId: 'type2', owner: 'owner2', targetUri: 'http://two' } ]; - let jobsTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'})); + let jobsTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' })); let jobRows = await jobsTable.getRows(); expect(jobRows.length).toEqual(2); jobRows.forEach(row => { @@ -245,31 +157,46 @@ describe('EICoordinatorComponent', () => { }); }); + it('job defaults', async () => { + const jobMissingProperties = { + ei_job_identity: 'job1', + target_uri: 'http://one' + } as EIJob; + const jobs: EIJob[] = [jobMissingProperties]; + jobDataSourceSpy.eiJobsSubject.and.returnValue(of(jobs)); + component.ngOnInit(); + + const expectedJobRow = { id: 'job1', typeId: '< No type >', owner: '< No owner >', targetUri: 'http://one' }; + let jobsTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' })); + let jobRows = await jobsTable.getRows(); + expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJobRow); + }); + it('filtering', async () => { component.ngOnInit(); - let jobsTable = await loader.getHarness(MatTableHarness.with({selector: '#jobsTable'})); + let jobsTable = await loader.getHarness(MatTableHarness.with({ selector: '#jobsTable' })); - let idFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobIdFilter'})); + let idFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobIdFilter' })); await idFilterInput.setValue("1"); let jobRows = await jobsTable.getRows(); expect(jobRows.length).toEqual(1); expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJob1Row); idFilterInput.setValue(''); - let typeIdFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobTypeIdFilter'})); + let typeIdFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobTypeIdFilter' })); await typeIdFilterInput.setValue("1"); jobRows = await jobsTable.getRows(); expect(jobRows.length).toEqual(1); typeIdFilterInput.setValue(''); - let ownerFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobOwnerFilter'})); + let ownerFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobOwnerFilter' })); await ownerFilterInput.setValue("1"); jobRows = await jobsTable.getRows(); expect(jobRows.length).toEqual(1); expect(await jobRows[0].getCellTextByColumnName()).toEqual(expectedJob1Row); ownerFilterInput.setValue(''); - let targetUriFilterInput = await loader.getHarness(MatInputHarness.with({selector: '#jobTargetUriFilter'})); + let targetUriFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#jobTargetUriFilter' })); await targetUriFilterInput.setValue("one"); jobRows = await jobsTable.getRows(); expect(jobRows.length).toEqual(1); diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts index 9698f55..79b7129 100644 --- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts @@ -22,11 +22,10 @@ import { Sort } from '@angular/material/sort'; import { FormBuilder, FormGroup, AbstractControl } from '@angular/forms'; import { MatTableDataSource } from '@angular/material/table'; -import { EIJob, EIProducer } from '../interfaces/ei.types'; +import { EIJob } from '../interfaces/ei.types'; import { EIJobDataSource } from './ei-job.datasource'; -import { EIProducerDataSource } from './ei-producer.datasource'; import { UiService } from '../services/ui/ui.service'; -import { Observable } from 'rxjs/Observable'; +import { ProducersListComponent } from './producers-list/producers-list.component'; @Component({ selector: 'nrcp-ei-coordinator', @@ -36,17 +35,14 @@ import { Observable } from 'rxjs/Observable'; export class EICoordinatorComponent implements OnInit { darkMode: boolean; - searchString: string; formGroup: FormGroup; jobsDataSource: MatTableDataSource = new MatTableDataSource(); - producersDataSource: MatTableDataSource= new MatTableDataSource(); readonly jobsFormControl: AbstractControl; - readonly producersFormControl: AbstractControl; constructor( + private producersList: ProducersListComponent, private eiJobsDataSource: EIJobDataSource, - private eiProducersDataSource: EIProducerDataSource, private ui: UiService, private formBuilder: FormBuilder) { this.formGroup = formBuilder.group({ filter: [""] }); @@ -57,32 +53,19 @@ export class EICoordinatorComponent implements OnInit { owner: '', targetUri:'' }); - this.producersFormControl = formBuilder.group({ - ei_producer_id: '', - ei_producer_types: '', - status: '' - }); } ngOnInit() { this.eiJobsDataSource.loadJobs(); - this.eiProducersDataSource.loadProducers(); this.eiJobsDataSource.eiJobsSubject().subscribe((data) => { this.jobsDataSource.data = data; }); - this.eiProducersDataSource.eiProducersSubject().subscribe((data) => { - this.producersDataSource.data = data; - }); this.jobsFormControl.valueChanges.subscribe(value => { const filter = {...value, id: value.id.trim().toLowerCase()} as string; this.jobsDataSource.filter = filter; }); - this.producersFormControl.valueChanges.subscribe(value => { - const filter = {...value, ei_producer_id: value.ei_producer_id.trim().toLowerCase()} as string; - this.producersDataSource.filter = filter; - }); this.jobsDataSource.filterPredicate = ((data: EIJob, filter) => { return this.isDataIncluding(data.ei_job_identity, filter.id) @@ -91,12 +74,6 @@ export class EICoordinatorComponent implements OnInit { && this.isDataIncluding(data.ei_type_identity, filter.typeId); }) as (data: EIJob, filter: any) => boolean; - this.producersDataSource.filterPredicate = ((data, filter) => { - return this.isDataIncluding(data.ei_producer_id, filter.ei_producer_id) - && this.isDataIncluding(data.ei_producer_types.join(','), filter.ei_producer_types) - && this.isDataIncluding(data.status, filter.status); - }) as (data: EIProducer, filter: any) => boolean; - this.ui.darkModeState.subscribe((isDark) => { this.darkMode = isDark; }); @@ -117,20 +94,6 @@ export class EICoordinatorComponent implements OnInit { this.jobsDataSource.data = data; } - sortProducers(sort: Sort){ - const data = this.producersDataSource.data - data.sort((a: EIProducer, b: EIProducer) => { - const isAsc = sort.direction === 'asc'; - switch (sort.active) { - case 'id': return this.compare(a.ei_producer_id, b.ei_producer_id, isAsc); - case 'types': return this.compare(a.ei_producer_types, b.ei_producer_types, isAsc); - case 'status': return this.compare(a.status, b.status, isAsc); - default: return 0; - } - }); - this.producersDataSource.data = data; - } - compare(a: any, b: any, isAsc: boolean) { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); } @@ -156,30 +119,12 @@ export class EICoordinatorComponent implements OnInit { } return '< No owner >'; } - - getProducerTypes(eiProducer: EIProducer): string[] { - if (eiProducer.ei_producer_types) { - return eiProducer.ei_producer_types; - } - return ['< No types >']; - } - - getProducerStatus(eiProducer: EIProducer): string { - if (eiProducer.status) { - return eiProducer.status; - } - return '< No status >'; - } - refreshTables() { this.eiJobsDataSource.loadJobs(); - this.eiProducersDataSource.loadProducers(); - this.eiJobsDataSource.eiJobsSubject().subscribe((data) => { this.jobsDataSource.data = data; }); - this.eiProducersDataSource.eiProducersSubject().subscribe((data) => { - this.producersDataSource.data = data; - }); + + this.producersList.refresh(); } } diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.html b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.html new file mode 100644 index 0000000..57e61c5 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.html @@ -0,0 +1,50 @@ +
+ + + +
+
+ + + Producer ID + +
+
+
+ {{eiProducer.ei_producer_id}} +
+ + + +
+
+ + + Producer types + +
+
+
+ {{this.getProducerTypes(eiProducer)}} +
+ + + +
+
+ + + Producer status + +
+
+
+ {{this.getProducerStatus(eiProducer)}} +
+ + + +
+
diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.scss b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.scss new file mode 100644 index 0000000..b1963bb --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.scss @@ -0,0 +1,7 @@ +.ei-coordinator-table { + width: 100%; + min-height: 150px; + margin-top: 10px; + margin-bottom: 10px; + background-color: transparent; + } diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.spec.ts b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.spec.ts new file mode 100644 index 0000000..2f537f4 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.spec.ts @@ -0,0 +1,140 @@ +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { MatInputHarness } from '@angular/material/input/testing'; +import { MatTableModule } from '@angular/material/table'; +import { MatTableHarness } from '@angular/material/table/testing'; +import { of } from 'rxjs/observable/of'; +import { EIProducer } from 'src/app/interfaces/ei.types'; +import { UiService } from 'src/app/services/ui/ui.service'; +import { EIProducerDataSource } from '../ei-producer.datasource'; + +import { ProducersListComponent } from './producers-list.component'; + +describe('ProducersListComponent', () => { + let component: ProducersListComponent; + let fixture: ComponentFixture; + let loader: HarnessLoader; + let producerDataSourceSpy: jasmine.SpyObj; + + const producer1 = { + ei_producer_id: 'producer1', + ei_producer_types: ['type1', 'type2'], + status: 'ENABLED' + } as EIProducer; + const producer2 = { + ei_producer_id: 'producer2', + ei_producer_types: ['type2', 'type3'], + status: 'DISABLED' + } as EIProducer; + + beforeEach(async(() => { + producerDataSourceSpy = jasmine.createSpyObj('EIProducerDataSource', ['loadProducers', 'eiProducers', 'eiProducersSubject']); + + const producers: EIProducer[] = [producer1, producer2]; + producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers)); + + TestBed.configureTestingModule({ + imports: [ + MatTableModule, + ReactiveFormsModule + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ], + declarations: [ + ProducersListComponent + ], + providers: [ + { provide: EIProducerDataSource, useValue: producerDataSourceSpy }, + UiService, + FormBuilder, + ] + }) + .compileComponents(); + })); + + const expectedProducer1Row = { id: 'producer1', types: 'type1,type2', status: 'ENABLED' }; + beforeEach(() => { + fixture = TestBed.createComponent(ProducersListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should contain producers table with correct columns', async () => { + let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' })); + let headerRow = (await producersTable.getHeaderRows())[0]; + let headers = await headerRow.getCellTextByColumnName(); + + expect(headers).toEqual({ id: 'Producer ID', types: 'Producer types', status: 'Producer status' }); + }); + + + it('should contain data after initialization', async () => { + component.ngOnInit(); + const expectedProducerRows = [ + expectedProducer1Row, + { id: 'producer2', types: 'type2,type3', status: 'DISABLED' } + ]; + let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' })); + let producerRows = await producersTable.getRows(); + expect(producerRows.length).toEqual(2); + producerRows.forEach(row => { + row.getCellTextByColumnName().then(values => { + expect(expectedProducerRows).toContain(jasmine.objectContaining(values)); + }); + }); + }); + + describe('should display default values for non required properties', () => { + it('producer defaults', async () => { + const producerMissingProperties = { + ei_producer_id: 'producer1' + } as EIProducer; + const producers: EIProducer[] = [producerMissingProperties]; + producerDataSourceSpy.eiProducersSubject.and.returnValue(of(producers)); + component.ngOnInit(); + + const expectedProducerRow = { id: 'producer1', types: '< No types >', status: '< No status >' }; + let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' })); + let producerRows = await producersTable.getRows(); + expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducerRow); + }); + }); + + it('filtering', async () => { + const expectedProducer1Row = { id: 'producer1', types: 'type1,type2', status: 'ENABLED' }; + component.ngOnInit(); + let producersTable = await loader.getHarness(MatTableHarness.with({ selector: '#producersTable' })); + + let idFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#producerIdFilter' })); + await idFilterInput.setValue("1"); + let producerRows = await producersTable.getRows(); + expect(producerRows.length).toEqual(1); + expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row); + + idFilterInput.setValue(''); + let typesFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#producerTypesFilter' })); + await typesFilterInput.setValue("1"); + producerRows = await producersTable.getRows(); + expect(producerRows.length).toEqual(1); + expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row); + await typesFilterInput.setValue("2"); + producerRows = await producersTable.getRows(); + expect(producerRows.length).toEqual(2); + + typesFilterInput.setValue(''); + let statusFilterInput = await loader.getHarness(MatInputHarness.with({ selector: '#producerStatusFilter' })); + await statusFilterInput.setValue("enabled"); + producerRows = await producersTable.getRows(); + expect(producerRows.length).toEqual(1); + expect(await producerRows[0].getCellTextByColumnName()).toEqual(expectedProducer1Row); + }); +}); diff --git a/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.ts b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.ts new file mode 100644 index 0000000..8fc57b0 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/producers-list/producers-list.component.ts @@ -0,0 +1,96 @@ +import { Component, OnInit } from '@angular/core'; +import { AbstractControl, FormBuilder } from '@angular/forms'; +import { Sort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { EIProducer } from 'src/app/interfaces/ei.types'; +import { UiService } from 'src/app/services/ui/ui.service'; +import { EIProducerDataSource } from '../ei-producer.datasource'; + +@Component({ + selector: 'nrcp-producers-list', + templateUrl: './producers-list.component.html', + styleUrls: ['./producers-list.component.scss'] +}) +export class ProducersListComponent implements OnInit { + darkMode: boolean; + producersDataSource: MatTableDataSource = new MatTableDataSource(); + + readonly producersFormControl: AbstractControl; + + constructor( + private eiProducersDataSource: EIProducerDataSource, + private ui: UiService, + private formBuilder: FormBuilder) { + this.producersFormControl = formBuilder.group({ + ei_producer_id: '', + ei_producer_types: '', + status: '' + }); + } + + ngOnInit(): void { + this.refresh(); + + this.producersFormControl.valueChanges.subscribe(value => { + const filter = { ...value, ei_producer_id: value.ei_producer_id.trim().toLowerCase() } as string; + this.producersDataSource.filter = filter; + }); + + this.producersDataSource.filterPredicate = ((data, filter) => { + return this.isDataIncluding(data.ei_producer_id, filter.ei_producer_id) + && this.isDataIncluding(data.ei_producer_types.join(','), filter.ei_producer_types) + && this.isDataIncluding(data.status, filter.status); + }) as (data: EIProducer, filter: any) => boolean; + + this.ui.darkModeState.subscribe((isDark) => { + this.darkMode = isDark; + }); + } + + isDataIncluding(data: string, filter: string): boolean { + return !filter || data.toLowerCase().includes(filter); + } + + sortProducers(sort: Sort) { + const data = this.producersDataSource.data + data.sort((a: EIProducer, b: EIProducer) => { + const isAsc = sort.direction === 'asc'; + switch (sort.active) { + case 'id': return this.compare(a.ei_producer_id, b.ei_producer_id, isAsc); + case 'types': return this.compare(a.ei_producer_types, b.ei_producer_types, isAsc); + case 'status': return this.compare(a.status, b.status, isAsc); + default: return 0; + } + }); + this.producersDataSource.data = data; + } + + compare(a: any, b: any, isAsc: boolean) { + return (a < b ? -1 : 1) * (isAsc ? 1 : -1); + } + + stopSort(event: any) { + event.stopPropagation(); + } + + getProducerTypes(eiProducer: EIProducer): string[] { + if (eiProducer.ei_producer_types) { + return eiProducer.ei_producer_types; + } + return ['< No types >']; + } + + getProducerStatus(eiProducer: EIProducer): string { + if (eiProducer.status) { + return eiProducer.status; + } + return '< No status >'; + } + + refresh() { + this.eiProducersDataSource.loadProducers(); + this.eiProducersDataSource.eiProducersSubject().subscribe((data) => { + this.producersDataSource.data = data; + }); + } +} -- 2.16.6