diff --git a/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.html b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8bf3593a46c84e561faf2db2a32e15e3176a6ad1 --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.html @@ -0,0 +1,10 @@ +<ods-list-item [path]="" [attr.data-test-id]="(aggregationMapping.name | convertForDataTest)"> + <div class="flex-1 basis-1/2"> + <div class="mb-2 flex flex-wrap items-center gap-3"> + <h3 class="text-md font-semibold" data-test-class="fullname">{{ aggregationMapping.name }}</h3> + </div> + <!-- Remove null safe check operator after backend provides correct data. --> + <div>{{aggregationMapping.formIdentifier?.formEngineName}}</div> + <div>{{aggregationMapping.formIdentifier?.formId}}</div> + </div> +</ods-list-item> diff --git a/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e48ac908db1d0c99b9357fd4744db48841268ff --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AggregationMappingListItemComponent } from './aggregation-mapping-list-item.component'; + +describe('AggregationMappingListItemComponent', () => { + let component: AggregationMappingListItemComponent; + let fixture: ComponentFixture<AggregationMappingListItemComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AggregationMappingListItemComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(AggregationMappingListItemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.ts b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ca31dbc05b36f44e29eb1dec8d7f0c59bc38c546 --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list-item/aggregation-mapping-list-item.component.ts @@ -0,0 +1,14 @@ +import { AggregationMappingResource } from '@admin-client/reporting-shared'; +import { ConvertForDataTestPipe } from '@alfa-client/tech-shared'; +import { Component, Input } from '@angular/core'; +import { ListItemComponent } from '@ods/system'; + +@Component({ + selector: 'admin-aggregation-mapping-list-item', + standalone: true, + templateUrl: './aggregation-mapping-list-item.component.html', + imports: [ConvertForDataTestPipe, ListItemComponent], +}) +export class AggregationMappingListItemComponent { + @Input({ required: true }) aggregationMapping: AggregationMappingResource; +} diff --git a/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.html b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2746dbebda6ef94cca1940a4920cb20aae7323aa --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.html @@ -0,0 +1,7 @@ +<ods-spinner [stateResource]="listStateResource"> + <ods-list data-test-id="aggregation-mapping-list"> + @for (aggregationMapping of aggregationMappings; track $index) { + <admin-aggregation-mapping-list-item [aggregationMapping]="aggregationMapping" /> + } + </ods-list> +</ods-spinner> \ No newline at end of file diff --git a/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd1647f73abfe0e2858c1de123b8fc0788635612 --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.spec.ts @@ -0,0 +1,120 @@ +import { AggregationMappingListResource } from '@admin-client/reporting-shared'; +import { + createEmptyStateResource, + createLoadingStateResource, + createStateResource, + getEmbeddedResources, + StateResource, +} from '@alfa-client/tech-shared'; +import { expectComponentExistsInTemplate, getElementFromFixtureByType } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { expect } from '@jest/globals'; +import { SpinnerComponent } from '@ods/component'; +import { ListComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; +import { AggregationMappingListLinkRel } from '../../../../reporting-shared/src/lib/aggregation-mapping.linkrel'; +import { createAggregationMappingListResource } from '../../../../reporting-shared/test/aggregation-mapping'; +import { AggregationMappingListItemComponent } from './aggregation-mapping-list-item/aggregation-mapping-list-item.component'; +import { AggregationMappingListComponent } from './aggregation-mapping-list.component'; + +describe('AggregationMappingListComponent', () => { + let component: AggregationMappingListComponent; + let fixture: ComponentFixture<AggregationMappingListComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + AggregationMappingListComponent, + MockComponent(SpinnerComponent), + MockComponent(ListComponent), + MockComponent(AggregationMappingListItemComponent), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(AggregationMappingListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('component', () => { + const aggregationMappingListStateResource: StateResource<AggregationMappingListResource> = createStateResource( + createAggregationMappingListResource(), + ); + + it('should have initial state', () => { + expect(component.listStateResource).toEqual(createEmptyStateResource()); + expect(component.aggregationMappings).toEqual([]); + }); + + describe('_update', () => { + it('should set list state resource', () => { + component.aggregationMappingListStateResource = aggregationMappingListStateResource; + + expect(component.listStateResource).toEqual(aggregationMappingListStateResource); + }); + + it('should set aggregation mappings if loaded and not nil', () => { + component.aggregationMappingListStateResource = aggregationMappingListStateResource; + + expect(component.aggregationMappings).toEqual( + getEmbeddedResources(aggregationMappingListStateResource, AggregationMappingListLinkRel.LIST), + ); + }); + + it('should NOT set aggregation mappings if nil', () => { + component.aggregationMappingListStateResource = null; + + expect(component.aggregationMappings).toEqual([]); + }); + + it('should NOT set aggregation mappings if loading', () => { + component.aggregationMappingListStateResource = createLoadingStateResource(); + + expect(component.aggregationMappings).toEqual([]); + }); + }); + + describe('set aggregation mapping list state resource', () => { + it('should update component state', () => { + component._update = jest.fn(); + + component.aggregationMappingListStateResource = aggregationMappingListStateResource; + + expect(component._update).toHaveBeenCalledWith(aggregationMappingListStateResource); + }); + }); + }); + + describe('template', () => { + const aggregationMappingListStateResource: StateResource<AggregationMappingListResource> = createStateResource( + createAggregationMappingListResource(), + ); + + beforeEach(() => { + component.aggregationMappingListStateResource = aggregationMappingListStateResource; + fixture.detectChanges(); + }); + + it('should have spinner with state resource', () => { + expectComponentExistsInTemplate(fixture, SpinnerComponent); + const comp = getElementFromFixtureByType(fixture, SpinnerComponent); + expect(comp.stateResource).toEqual(aggregationMappingListStateResource); + }); + + it('should have list', () => { + expectComponentExistsInTemplate(fixture, ListComponent); + }); + + it('should have list item with mapping', () => { + expectComponentExistsInTemplate(fixture, AggregationMappingListItemComponent); + const comp = getElementFromFixtureByType(fixture, AggregationMappingListItemComponent); + expect(comp.aggregationMapping).toEqual( + getEmbeddedResources(aggregationMappingListStateResource, AggregationMappingListLinkRel.LIST)[0], + ); + }); + }); +}); diff --git a/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.ts b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..c427bc790367e93de6f2a01bc733a5bca391dd3f --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/aggregation-mapping-list/aggregation-mapping-list.component.ts @@ -0,0 +1,31 @@ +import { AggregationMappingListResource, AggregationMappingResource } from '@admin-client/reporting-shared'; +import { createEmptyStateResource, getEmbeddedResources, isLoaded, isNotNil, StateResource } from '@alfa-client/tech-shared'; +import { Component, Input } from '@angular/core'; +import { SpinnerComponent } from '@ods/component'; +import { ListComponent } from '@ods/system'; +import { AggregationMappingListLinkRel } from '../../../../reporting-shared/src/lib/aggregation-mapping.linkrel'; +import { AggregationMappingListItemComponent } from './aggregation-mapping-list-item/aggregation-mapping-list-item.component'; + +@Component({ + selector: 'admin-aggregation-mapping-list', + standalone: true, + templateUrl: './aggregation-mapping-list.component.html', + imports: [SpinnerComponent, ListComponent, AggregationMappingListItemComponent], +}) +export class AggregationMappingListComponent { + @Input({ required: true }) set aggregationMappingListStateResource( + stateResource: StateResource<AggregationMappingListResource>, + ) { + this._update(stateResource); + } + + public listStateResource: StateResource<AggregationMappingListResource> = createEmptyStateResource(); + public aggregationMappings: AggregationMappingResource[] = []; + + _update(stateResource: StateResource<AggregationMappingListResource>): void { + this.listStateResource = stateResource; + if (isNotNil(stateResource) && isLoaded(stateResource)) { + this.aggregationMappings = getEmbeddedResources(stateResource, AggregationMappingListLinkRel.LIST); + } + } +} diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html index 1fd0441a39d71372d0c65fd84d0ce5386ac52fd2..f7cb1c606f4477c316df8e6a20b2df55edf6a513 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html @@ -32,4 +32,4 @@ ></ods-routing-button> </div> -<ng-container *ngIf="listStateResource$ | async"></ng-container> +<admin-aggregation-mapping-list [aggregationMappingListStateResource]="listStateResource$ | async"></admin-aggregation-mapping-list> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts index 3abca44e4a1aa07b3bceb119a0caf010014c02ed..014109b6a80633ea7665c6d1e488aeecc1e96211 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts @@ -23,12 +23,15 @@ */ import { AggregationMappingListResource, AggregationMappingService } from '@admin-client/reporting-shared'; import { createStateResource, StateResource } from '@alfa-client/tech-shared'; -import { mock, Mock } from '@alfa-client/test-utils'; +import { expectComponentExistsInTemplate, getElementFromFixtureByType, mock, Mock } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { expect } from '@jest/globals'; import { RoutingButtonComponent } from '@ods/component'; import { singleCold } from 'libs/tech-shared/test/marbles'; import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; import { createAggregationMappingListResource } from '../../../../reporting-shared/test/aggregation-mapping'; +import { AggregationMappingListComponent } from '../aggregation-mapping-list/aggregation-mapping-list.component'; import { StatistikContainerComponent } from './statistik-container.component'; describe('StatistikContainerComponent', () => { @@ -41,8 +44,11 @@ describe('StatistikContainerComponent', () => { aggregationMappingService = mock(AggregationMappingService); await TestBed.configureTestingModule({ - imports: [StatistikContainerComponent], - declarations: [MockComponent(RoutingButtonComponent)], + imports: [ + StatistikContainerComponent, + MockComponent(RoutingButtonComponent), + MockComponent(AggregationMappingListComponent), + ], }) .overrideComponent(StatistikContainerComponent, { set: { @@ -86,4 +92,24 @@ describe('StatistikContainerComponent', () => { expect(component.listStateResource$).toBeObservable(singleCold(stateResource)); }); }); + + describe('template', () => { + describe('aggregation mapping list', () => { + it('should exists', () => { + expectComponentExistsInTemplate(fixture, AggregationMappingListComponent); + }); + + it('should have inputs', () => { + const stateResource: StateResource<AggregationMappingListResource> = createStateResource( + createAggregationMappingListResource(), + ); + component.listStateResource$ = of(stateResource); + fixture.detectChanges(); + + const comp = getElementFromFixtureByType(fixture, AggregationMappingListComponent); + + expect(comp.aggregationMappingListStateResource).toEqual(stateResource); + }); + }); + }); }); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts index 90c3515896348b1c1d6ee77ba4f247428e79c4bf..70db01b592c64a8a2afc8b59d6bb50b4e63c93d9 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts @@ -28,12 +28,13 @@ import { CommonModule } from '@angular/common'; import { Component, inject, OnInit } from '@angular/core'; import { RoutingButtonComponent } from '@ods/component'; import { Observable } from 'rxjs'; +import { AggregationMappingListComponent } from '../aggregation-mapping-list/aggregation-mapping-list.component'; @Component({ selector: 'admin-statistik-container', templateUrl: './statistik-container.component.html', standalone: true, - imports: [CommonModule, RoutingButtonComponent], + imports: [CommonModule, RoutingButtonComponent, AggregationMappingListComponent], providers: [AggregationMappingService], }) export class StatistikContainerComponent implements OnInit { diff --git a/alfa-client/libs/test-utils/src/lib/helper.ts b/alfa-client/libs/test-utils/src/lib/helper.ts index 66f33c3579e18d88743d3b23ea96703a2799bb04..df9518764f03ddd3cf6a6f24f892405ae004e056 100644 --- a/alfa-client/libs/test-utils/src/lib/helper.ts +++ b/alfa-client/libs/test-utils/src/lib/helper.ts @@ -26,8 +26,8 @@ import { ComponentFixture } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { EventData } from './model'; -export function getElementFromFixtureByType<T>(fixture: ComponentFixture<any>, component: Type<T>): T { - return getDebugElementFromFixtureByType(fixture, component).componentInstance as T; +export function getElementFromFixtureByType<T>(fixture: ComponentFixture<any>, component: Type<T>): T | null { + return getDebugElementFromFixtureByType(fixture, component)?.componentInstance as T; } function getDebugElementFromFixtureByType<T>(fixture: ComponentFixture<any>, component: Type<T>): DebugElement { diff --git a/alfa-client/libs/test-utils/src/lib/jest.helper.ts b/alfa-client/libs/test-utils/src/lib/jest.helper.ts index 60df8592afa840fd2f06b5f967e8f5cb295a9a2c..1b0f86ec5b7174219e020cac4bd096de392b4121 100644 --- a/alfa-client/libs/test-utils/src/lib/jest.helper.ts +++ b/alfa-client/libs/test-utils/src/lib/jest.helper.ts @@ -21,10 +21,11 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { Type } from '@angular/core'; import { ComponentFixture } from '@angular/core/testing'; import { expect } from '@jest/globals'; import { TooltipDirective } from '@ods/system'; -import { getDebugElementFromFixtureByCss, getElementFromFixture } from './helper'; +import { getDebugElementFromFixtureByCss, getElementFromFixture, getElementFromFixtureByType } from './helper'; export function notExistsAsHtmlElement(fixture: ComponentFixture<any>, domElement: string): void { expect(getElementFromFixture(fixture, domElement)).not.toBeInstanceOf(HTMLElement); @@ -34,6 +35,14 @@ export function existsAsHtmlElement(fixture: ComponentFixture<any>, domElement: expect(getElementFromFixture(fixture, domElement)).toBeInstanceOf(HTMLElement); } +export function expectComponentExistsInTemplate<T>(fixture: ComponentFixture<any>, component: Type<T>): void { + expect(getElementFromFixtureByType(fixture, component)).toBeInstanceOf(component); +} + +export function expectComponentMissingInTemplate<T>(fixture: ComponentFixture<any>, component: Type<T>): void { + expect(getElementFromFixtureByType(fixture, component)).toBeUndefined(); +} + export function tooltipExistsWithText(fixture: ComponentFixture<any>, domElement: string, tooltipText: string) { const tooltipInstance = getDebugElementFromFixtureByCss(fixture, domElement).injector.get(TooltipDirective); expect(tooltipInstance.componentRef.instance.text).toBe(tooltipText);