Skip to content
Snippets Groups Projects
Commit 9e7057bf authored by OZGCloud's avatar OZGCloud
Browse files

Merge branch 'master' into OZG-6676-OZG-7035-styling

parents e023cfed 5c718cb6
Branches
Tags
No related merge requests found
Showing
with 288 additions and 500 deletions
...@@ -11,31 +11,26 @@ ...@@ -11,31 +11,26 @@
> >
<ods-admin-logo-icon /> <ods-admin-logo-icon />
</a> </a>
<user-profile-button-container <user-profile-button-container data-test-id="user-profile-button"></user-profile-button-container>
data-test-id="user-profile-button"
></user-profile-button-container>
</header> </header>
<div class="flex h-screen w-full justify-center overflow-y-auto"> <div class="flex h-screen w-full justify-center overflow-y-auto">
<ods-navbar data-test-id="navigation"> <ods-navbar data-test-id="navigation">
<ng-container *ngIf="apiRoot | hasLink: ApiRootLinkRel.CONFIGURATION"> <ng-container *ngIf="apiRoot | hasLink: ApiRootLinkRel.CONFIGURATION">
<!-- <ods-nav-item caption="Organisationseinheiten" to="/organisationseinheiten">--> <ods-nav-item caption="Benutzer & Rollen" path="/benutzer_und_rollen">
<!-- <ods-orga-unit-icon icon />-->
<!-- </ods-nav-item>-->
<ods-nav-item caption="Benutzer & Rollen" to="/benutzer_und_rollen">
<ods-users-icon class="stroke-text" icon /> <ods-users-icon class="stroke-text" icon />
</ods-nav-item> </ods-nav-item>
<hr /> <hr />
<ods-nav-item caption="Postfach" to="/postfach"> <ods-nav-item caption="Postfach" path="/postfach">
<ods-mailbox-icon icon /> <ods-mailbox-icon icon />
</ods-nav-item> </ods-nav-item>
<ods-nav-item caption="Organisationseinheiten" path="/organisationseinheiten">
<ods-orga-unit-icon icon />
</ods-nav-item>
</ng-container> </ng-container>
</ods-navbar> </ods-navbar>
<main class="flex-1 overflow-y-auto bg-white px-6 py-4"> <main class="flex-1 overflow-y-auto bg-white px-6 py-4">
<router-outlet <router-outlet
*ngIf=" *ngIf="apiRoot | hasLink: ApiRootLinkRel.CONFIGURATION; else configurationResourceLinkNotAvailable"
apiRoot | hasLink: ApiRootLinkRel.CONFIGURATION;
else configurationResourceLinkNotAvailable
"
data-test-id="router-outlet" data-test-id="router-outlet"
></router-outlet> ></router-outlet>
<ng-template #configurationResourceLinkNotAvailable> <ng-template #configurationResourceLinkNotAvailable>
......
...@@ -28,7 +28,8 @@ import { OAuthModule } from 'angular-oauth2-oidc'; ...@@ -28,7 +28,8 @@ import { OAuthModule } from 'angular-oauth2-oidc';
import { HttpUnauthorizedInterceptor } from 'libs/authentication/src/lib/http-unauthorized.interceptor'; import { HttpUnauthorizedInterceptor } from 'libs/authentication/src/lib/http-unauthorized.interceptor';
import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component'; import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component';
import { environment } from '../environments/environment'; import { environment } from '../environments/environment';
import { OrganisationseinheitPageComponent } from '../pages/organisationseinheit/organisationseinheit-page/organisationseinheit-page.component'; import { OrganisationsEinheitFormPageComponent } from '../pages/organisationseinheit/organisationseinheit-form-page/organisationseinheit-form-page.component';
import { OrganisationsEinheitPageComponent } from '../pages/organisationseinheit/organisationseinheit-page/organisationseinheit-page.component';
import { PostfachPageComponent } from '../pages/postfach/postfach-page/postfach-page.component'; import { PostfachPageComponent } from '../pages/postfach/postfach-page/postfach-page.component';
import { UnavailablePageComponent } from '../pages/unavailable/unavailable-page/unavailable-page.component'; import { UnavailablePageComponent } from '../pages/unavailable/unavailable-page/unavailable-page.component';
import { UserRolesPageComponent } from '../pages/users-roles/user-roles-page/user-roles-page.component'; import { UserRolesPageComponent } from '../pages/users-roles/user-roles-page/user-roles-page.component';
...@@ -40,7 +41,8 @@ import { appRoutes } from './app.routes'; ...@@ -40,7 +41,8 @@ import { appRoutes } from './app.routes';
AppComponent, AppComponent,
PostfachPageComponent, PostfachPageComponent,
UserRolesPageComponent, UserRolesPageComponent,
OrganisationseinheitPageComponent, OrganisationsEinheitPageComponent,
OrganisationsEinheitFormPageComponent,
UserProfileButtonContainerComponent, UserProfileButtonContainerComponent,
UnavailablePageComponent, UnavailablePageComponent,
], ],
......
import { Route } from '@angular/router'; import { Route } from '@angular/router';
import { OrganisationsEinheitFormPageComponent } from '../pages/organisationseinheit/organisationseinheit-form-page/organisationseinheit-form-page.component';
import { OrganisationsEinheitPageComponent } from '../pages/organisationseinheit/organisationseinheit-page/organisationseinheit-page.component';
import { PostfachPageComponent } from '../pages/postfach/postfach-page/postfach-page.component'; import { PostfachPageComponent } from '../pages/postfach/postfach-page/postfach-page.component';
import { UserRolesPageComponent } from '../pages/users-roles/user-roles-page/user-roles-page.component'; import { UserRolesPageComponent } from '../pages/users-roles/user-roles-page/user-roles-page.component';
...@@ -18,9 +20,14 @@ export const appRoutes: Route[] = [ ...@@ -18,9 +20,14 @@ export const appRoutes: Route[] = [
component: UserRolesPageComponent, component: UserRolesPageComponent,
title: 'Admin | Benutzer & Rollen', title: 'Admin | Benutzer & Rollen',
}, },
// { {
// path: 'organisationseinheiten', path: 'organisationseinheiten',
// component: OrganisationseinheitPageComponent, component: OrganisationsEinheitPageComponent,
// title: 'Admin | Organisationseinheiten', title: 'Admin | Organisationseinheiten',
// }, },
{
path: 'organisationseinheiten/:organisationsEinheitUrl',
component: OrganisationsEinheitFormPageComponent,
title: 'Admin | Organisationseinheit',
},
]; ];
<admin-organisationseinheit-form-container/>
\ No newline at end of file
import { OrganisationsEinheitFormContainerComponent } from '@admin-client/admin-settings';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MockComponent } from 'ng-mocks';
import { OrganisationsEinheitFormPageComponent } from './organisationseinheit-form-page.component';
describe('OrganisationsEinheitFormPageComponent', () => {
let component: OrganisationsEinheitFormPageComponent;
let fixture: ComponentFixture<OrganisationsEinheitFormPageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OrganisationsEinheitFormPageComponent, MockComponent(OrganisationsEinheitFormContainerComponent)],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OrganisationsEinheitFormPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component } from '@angular/core';
@Component({
selector: 'organisationseinheit-form-page',
templateUrl: './organisationseinheit-form-page.component.html',
})
export class OrganisationsEinheitFormPageComponent {}
<admin-organisationseinheit-container></admin-organisationseinheit-container> <admin-organisationseinheit-container/>
import { OrganisationseinheitContainerComponent } from '@admin-client/admin-settings'; import { OrganisationsEinheitContainerComponent } from '@admin-client/admin-settings';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MockComponent } from 'ng-mocks'; import { MockComponent } from 'ng-mocks';
import { OrganisationseinheitPageComponent } from './organisationseinheit-page.component'; import { OrganisationsEinheitPageComponent } from './organisationseinheit-page.component';
describe('OrganisationseinheitPageComponent', () => { describe('OrganisationsEinheitPageComponent', () => {
let component: OrganisationseinheitPageComponent; let component: OrganisationsEinheitPageComponent;
let fixture: ComponentFixture<OrganisationseinheitPageComponent>; let fixture: ComponentFixture<OrganisationsEinheitPageComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ declarations: [OrganisationsEinheitPageComponent, MockComponent(OrganisationsEinheitContainerComponent)],
OrganisationseinheitPageComponent,
MockComponent(OrganisationseinheitContainerComponent),
],
}).compileComponents(); }).compileComponents();
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(OrganisationseinheitPageComponent); fixture = TestBed.createComponent(OrganisationsEinheitPageComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
......
...@@ -4,4 +4,4 @@ import { Component } from '@angular/core'; ...@@ -4,4 +4,4 @@ import { Component } from '@angular/core';
selector: 'organisationseinheit-page', selector: 'organisationseinheit-page',
templateUrl: './organisationseinheit-page.component.html', templateUrl: './organisationseinheit-page.component.html',
}) })
export class OrganisationseinheitPageComponent {} export class OrganisationsEinheitPageComponent {}
export * from './lib/admin-settings.module'; export * from './lib/admin-settings.module';
export * from './lib/organisationseinheit/organisations-einheit.model';
export * from './lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component'; export * from './lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component';
export * from './lib/organisationseinheit/organisationseinheit-form-container/organisationseinheit-form-container.component';
export * from './lib/postfach/postfach-container/postfach-container.component'; export * from './lib/postfach/postfach-container/postfach-container.component';
export * from './lib/shared/navigation-item/navigation-item.component'; export * from './lib/shared/navigation-item/navigation-item.component';
export * from './lib/users-roles/users-roles.component'; export * from './lib/users-roles/users-roles.component';
import { ApiRootService } from '@alfa-client/api-root-shared'; import { ApiRootService } from '@alfa-client/api-root-shared';
import { Environment, ENVIRONMENT_CONFIG } from '@alfa-client/environment-shared'; import { Environment, ENVIRONMENT_CONFIG } from '@alfa-client/environment-shared';
import { NavigationSharedModule } from '@alfa-client/navigation-shared';
import { ResourceRepository, TechSharedModule } from '@alfa-client/tech-shared'; import { ResourceRepository, TechSharedModule } from '@alfa-client/tech-shared';
import { UiModule } from '@alfa-client/ui';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import KcAdminClient from '@keycloak/keycloak-admin-client'; import KcAdminClient from '@keycloak/keycloak-admin-client';
import { ButtonWithSpinnerComponent, TextareaEditorComponent } from '@ods/component'; import { ButtonWithSpinnerComponent, TextareaEditorComponent } from '@ods/component';
import { MailboxIconComponent, PersonIconComponent, TextInputComponent } from '@ods/system'; import {
ExclamationIconComponent,
ListComponent,
ListItemComponent,
MailboxIconComponent,
PersonIconComponent,
TextInputComponent,
} from '@ods/system';
import { createSettingListResourceService, SettingListResourceService } from './admin-settings-resource.service'; import { createSettingListResourceService, SettingListResourceService } from './admin-settings-resource.service';
import { SettingsService } from './admin-settings.service'; import { SettingsService } from './admin-settings.service';
import { ConfigurationResourceService, createConfigurationResourceService } from './configuration/configuration-resource.service'; import { ConfigurationResourceService, createConfigurationResourceService } from './configuration/configuration-resource.service';
import { ConfigurationService } from './configuration/configuration.service'; import { ConfigurationService } from './configuration/configuration.service';
import { OrganisationseinheitContainerComponent } from './organisationseinheit/organisationseinheit-container/organisationseinheit-container.component'; import {
import { OrganisationseinheitFormComponent } from './organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component'; createOrganisationsEinheitListResourceService,
import { OrganisationseinheitListComponent } from './organisationseinheit/organisationseinheit-container/organisationseinheit-list/organisationseinheit-list.component'; OrganisationsEinheitListResourceService,
} from './organisationseinheit/organisations-einheit-list-resource.service';
import {
createOrganisationsEinheitResourceService,
OrganisationsEinheitResourceService,
} from './organisationseinheit/organisations-einheit-resource.service';
import { OrganisationsEinheitContainerComponent } from './organisationseinheit/organisationseinheit-container/organisationseinheit-container.component';
import { OrganisationsEinheitListComponent } from './organisationseinheit/organisationseinheit-container/organisationseinheit-list/organisationseinheit-list.component';
import { OrganisationsEinheitFormContainerComponent } from './organisationseinheit/organisationseinheit-form-container/organisationseinheit-form-container.component';
import { OrganisationsEinheitFormComponent } from './organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-form.component';
import { OrganisationsEinheitSignaturComponent } from './organisationseinheit/organisationseinheit-form-container/organisationseinheit-form/organisationseinheit-signatur/organisationseinheit-signatur.component';
import { PostfachContainerComponent } from './postfach/postfach-container/postfach-container.component'; import { PostfachContainerComponent } from './postfach/postfach-container/postfach-container.component';
import { PostfachFormComponent } from './postfach/postfach-container/postfach-form/postfach-form.component'; import { PostfachFormComponent } from './postfach/postfach-container/postfach-form/postfach-form.component';
import { PostfachSignaturComponent } from './postfach/postfach-container/postfach-form/postfach-signatur/postfach-signatur.component'; import { PostfachSignaturComponent } from './postfach/postfach-container/postfach-form/postfach-signatur/postfach-signatur.component';
...@@ -37,11 +56,13 @@ import { UsersRolesComponent } from './users-roles/users-roles.component'; ...@@ -37,11 +56,13 @@ import { UsersRolesComponent } from './users-roles/users-roles.component';
PostfachSignaturComponent, PostfachSignaturComponent,
NavigationItemComponent, NavigationItemComponent,
TextFieldComponent, TextFieldComponent,
OrganisationseinheitContainerComponent, OrganisationsEinheitContainerComponent,
OrganisationseinheitFormComponent, OrganisationsEinheitListComponent,
OrganisationsEinheitFormContainerComponent,
OrganisationsEinheitFormComponent,
OrganisationsEinheitSignaturComponent,
PrimaryButtonComponent, PrimaryButtonComponent,
SecondaryButtonComponent, SecondaryButtonComponent,
OrganisationseinheitListComponent,
MoreMenuComponent, MoreMenuComponent,
MoreItemButtonComponent, MoreItemButtonComponent,
SpinnerComponent, SpinnerComponent,
...@@ -58,8 +79,19 @@ import { UsersRolesComponent } from './users-roles/users-roles.component'; ...@@ -58,8 +79,19 @@ import { UsersRolesComponent } from './users-roles/users-roles.component';
MailboxIconComponent, MailboxIconComponent,
PersonIconComponent, PersonIconComponent,
ToUserNamePipe, ToUserNamePipe,
ListComponent,
ListItemComponent,
ExclamationIconComponent,
UiModule,
NavigationSharedModule,
],
exports: [
PostfachContainerComponent,
OrganisationsEinheitContainerComponent,
OrganisationsEinheitFormContainerComponent,
NavigationItemComponent,
UsersRolesComponent,
], ],
exports: [PostfachContainerComponent, OrganisationseinheitContainerComponent, NavigationItemComponent, UsersRolesComponent],
providers: [ providers: [
ConfigurationService, ConfigurationService,
SettingsService, SettingsService,
...@@ -88,6 +120,16 @@ import { UsersRolesComponent } from './users-roles/users-roles.component'; ...@@ -88,6 +120,16 @@ import { UsersRolesComponent } from './users-roles/users-roles.component';
useFactory: createSettingListResourceService, useFactory: createSettingListResourceService,
deps: [ResourceRepository, ConfigurationService], deps: [ResourceRepository, ConfigurationService],
}, },
{
provide: OrganisationsEinheitListResourceService,
useFactory: createOrganisationsEinheitListResourceService,
deps: [ResourceRepository, ApiRootService],
},
{
provide: OrganisationsEinheitResourceService,
useFactory: createOrganisationsEinheitResourceService,
deps: [ResourceRepository, OrganisationsEinheitListResourceService],
},
], ],
}) })
export class AdminSettingsModule {} export class AdminSettingsModule {}
import { AdminOrganisationsEinheitItemResource, AdminOrganisationsEinheitListResource } from '@admin-client/admin-settings';
import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared';
import { ListResourceServiceConfig, ResourceListService, ResourceRepository } from '@alfa-client/tech-shared';
import { ConfigurationResource } from 'libs/admin/settings/src/lib/configuration/configuration.model';
import { OrganisationsEinheitListLinkRel } from './organisations-einheit.linkrel';
export class OrganisationsEinheitListResourceService extends ResourceListService<
ApiRootResource,
AdminOrganisationsEinheitListResource,
AdminOrganisationsEinheitItemResource
> {}
export function createOrganisationsEinheitListResourceService(repository: ResourceRepository, apiRootService: ApiRootService) {
return new ResourceListService(buildConfig(apiRootService), repository);
}
function buildConfig(apiRootService: ApiRootService): ListResourceServiceConfig<ConfigurationResource> {
return {
baseResource: apiRootService.getApiRoot(),
createLinkRel: 'TODO',
listLinkRel: ApiRootLinkRel.ORGANISATIONS_EINHEIT,
listResourceListLinkRel: OrganisationsEinheitListLinkRel.LIST,
};
}
import { AdminOrganisationsEinheitResource } from '@admin-client/admin-settings';
import { ApiResourceService, ResourceRepository, ResourceServiceConfig } from '@alfa-client/tech-shared';
import { ConfigurationResource } from 'libs/admin/settings/src/lib/configuration/configuration.model';
import { OrganisationsEinheitListResourceService } from './organisations-einheit-list-resource.service';
import { OrganisationsEinheitLinkRel } from './organisations-einheit.linkrel';
export class OrganisationsEinheitResourceService extends ApiResourceService<
AdminOrganisationsEinheitResource,
AdminOrganisationsEinheitResource
> {}
export function createOrganisationsEinheitResourceService(
repository: ResourceRepository,
listResourceService: OrganisationsEinheitListResourceService,
) {
return new ApiResourceService(buildConfig(listResourceService), repository);
}
function buildConfig(listResourceService: OrganisationsEinheitListResourceService): ResourceServiceConfig<ConfigurationResource> {
return {
resource: listResourceService.getSelected(),
getLinkRel: OrganisationsEinheitLinkRel.SELF,
edit: { linkRel: OrganisationsEinheitLinkRel.SELF },
};
}
export enum OrganisationsEinheitListLinkRel {
LIST = 'organisationsEinheitList',
}
export enum OrganisationsEinheitLinkRel {
SELF = 'self',
}
import { ListResource } from '@alfa-client/tech-shared';
import { Resource } from '@ngxp/rest';
export interface AdminOrganisationsEinheit {
name: string;
organisationsEinheitId: string;
syncResult: AdminOrganisationsEinheitSyncResult;
settings?: AdminOrganisationsEinheitSettings;
isChild?: boolean;
}
export enum AdminOrganisationsEinheitSyncResult {
OK = 'OK',
NOT_FOUND_IN_PVOG = 'NOT_FOUND_IN_PVOG',
NAME_MISMATCH = 'NAME_MISMATCH',
ORGANISATIONSEINHEIT_ID_NOT_UNIQUE = 'ORGANISATIONSEINHEIT_ID_NOT_UNIQUE',
DELETED = 'DELETED',
}
export interface AdminOrganisationsEinheitSettings {
signatur?: string;
}
export interface AdminOrganisationsEinheitResource extends AdminOrganisationsEinheit, Resource {}
export interface AdminOrganisationsEinheitListResource extends ListResource {}
export declare type AdminOrganisationsEinheitItemResource = Resource & AdminOrganisationsEinheit;
<ng-container *ngIf="organisationseinheitItems$ | async as organisationseinheitItems">
<h1 class="heading-1 pb-4">Organisationseinheiten</h1> <h1 class="heading-1 pb-4">Organisationseinheiten</h1>
<p id="absender-desc" class="p-1">Hinterlegen Sie Name und ID der Organisationseinheiten.</p>
<admin-organisationseinheit-form
data-test-id="organisationseinheit-form"
[organisationseinheitItems]="organisationseinheitItems"
></admin-organisationseinheit-form>
<admin-secondary-button
(clickEmitter)="openDialogForNewGroup()"
data-test-id="organisationseinheit-open-dialog-button"
label="Neue Organisationseinheit anlegen"
>
</admin-secondary-button>
<admin-spinner
data-test-id="organisationseinheit-spinner"
*ngIf="deleteInProgress$ | async"
></admin-spinner>
<admin-organisationseinheit-list <admin-organisationseinheit-list
[organisationseinheitItems]="organisationseinheitItems" *ngIf="organisationsEinheitResources$ | async as organisationsEinheitResources"
(editOrganisationseinheit)="edit($event)" [organisationsEinheitResources]="organisationsEinheitResources"
(deleteOrganisationseinheit)="delete($event)" data-test-id="organisations-einheit-list"
data-test-id="organisationseinheit-list" />
></admin-organisationseinheit-list>
</ng-container>
import { AdminOrganisationsEinheitResource, OrganisationsEinheitContainerComponent } from '@admin-client/admin-settings';
import { createStateResource } from '@alfa-client/tech-shared'; import { createStateResource } from '@alfa-client/tech-shared';
import { import { Mock, existsAsHtmlElement, mock } from '@alfa-client/test-utils';
Mock,
dispatchEventFromFixture,
existsAsHtmlElement,
getElementFromFixtureByType,
mock,
notExistsAsHtmlElement,
} from '@alfa-client/test-utils';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { ButtonWithSpinnerComponent } from '@ods/component';
import { singleCold } from 'libs/tech-shared/test/marbles';
import { MockComponent } from 'ng-mocks'; import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { createOrganisationseinheit } from '../../../../test/user/user'; import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test';
import { SecondaryButtonComponent } from '../../shared/secondary-button/secondary-button.component'; import {
import { SpinnerComponent } from '../../shared/spinner/spinner.component'; createAdminOrganisationsEinheitListResource,
import { Organisationseinheit } from '../../user/user.model'; createAdminOrganisationsEinheitResource,
import { OrganisationseinheitService } from '../organisationseinheit.service'; } from '../../../../test/organisations-einheit/organisations-einheit';
import { OrganisationseinheitContainerComponent } from './organisationseinheit-container.component'; import { OrganisationsEinheitService } from '../organisationseinheit.service';
import { OrganisationseinheitFormComponent } from './organisationseinheit-form/organisationseinheit-form.component'; import { OrganisationsEinheitListComponent } from './organisationseinheit-list/organisationseinheit-list.component';
import { OrganisationseinheitListComponent } from './organisationseinheit-list/organisationseinheit-list.component';
describe('OrganisationseinheitContainerComponent', () => {
let component: OrganisationseinheitContainerComponent;
let fixture: ComponentFixture<OrganisationseinheitContainerComponent>;
const organisationseinheitService: Mock<OrganisationseinheitService> = mock(OrganisationseinheitService);
const dialogOpenButtonSelector: string = getDataTestIdOf('organisationseinheit-open-dialog-button');
const spinnerSelector: string = getDataTestIdOf('organisationseinheit-spinner');
const organisationseinheitItems: Organisationseinheit[] = [createOrganisationseinheit()]; describe('OrganisationsEinheitContainerComponent', () => {
let component: OrganisationsEinheitContainerComponent;
let fixture: ComponentFixture<OrganisationsEinheitContainerComponent>;
let formComponent: OrganisationseinheitFormComponent; const organisationsEinheitService: Mock<OrganisationsEinheitService> = mock(OrganisationsEinheitService);
let listComponent: OrganisationseinheitListComponent;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ declarations: [OrganisationsEinheitContainerComponent, MockComponent(OrganisationsEinheitListComponent)],
OrganisationseinheitContainerComponent, imports: [ButtonWithSpinnerComponent],
MockComponent(SecondaryButtonComponent), providers: [{ provide: OrganisationsEinheitService, useValue: organisationsEinheitService }],
MockComponent(OrganisationseinheitFormComponent),
MockComponent(OrganisationseinheitListComponent),
MockComponent(SpinnerComponent),
],
providers: [{ provide: OrganisationseinheitService, useValue: organisationseinheitService }],
}).compileComponents(); }).compileComponents();
fixture = TestBed.createComponent(OrganisationseinheitContainerComponent); fixture = TestBed.createComponent(OrganisationsEinheitContainerComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
organisationseinheitService.get = jest.fn().mockReturnValue(of(createStateResource(organisationseinheitItems))); organisationsEinheitService.getList = jest
fixture.detectChanges(); .fn()
.mockReturnValue(of(createStateResource(createAdminOrganisationsEinheitListResource())));
formComponent = getElementFromFixtureByType(fixture, OrganisationseinheitFormComponent); fixture.detectChanges();
listComponent = getElementFromFixtureByType(fixture, OrganisationseinheitListComponent);
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
it('should open form on new organisationseinheit button', () => { describe('component', () => {
formComponent.open = jest.fn(); describe('ngOnInit', () => {
beforeEach(() => {
dispatchEventFromFixture(fixture, dialogOpenButtonSelector, 'clickEmitter'); component.loadOrganisationsEinheitResources = jest.fn().mockReturnValue(of([createAdminOrganisationsEinheitResource()]));
expect(formComponent.open).toHaveBeenCalled();
}); });
describe('organisationseinheit list', () => { it('should call loadOrganisationsEinheitResources', () => {
it('should open form for editing on editOrganisationseinheit event', () => { component.organisationsEinheitResources$.subscribe(() => {
const organisationseinheit: Organisationseinheit = organisationseinheitItems[0]; expect(component.loadOrganisationsEinheitResources).toHaveBeenCalled();
formComponent.openEdit = jest.fn(); });
listComponent.editOrganisationseinheit.emit(organisationseinheit);
expect(formComponent.openEdit).toHaveBeenCalledWith(organisationseinheit);
}); });
it('should call deleteOrganisationseinheit form on deleteOrganisationseinheit event', () => { it('should set organisationsEinheitResources', () => {
const organisationseinheit: Organisationseinheit = organisationseinheitItems[0]; component.ngOnInit();
component.delete = jest.fn();
listComponent.deleteOrganisationseinheit.emit(organisationseinheit);
expect(component.delete).toHaveBeenCalledWith(organisationseinheit); component.organisationsEinheitResources$.subscribe(
(organisationsEinheitResources: AdminOrganisationsEinheitResource[]) => {
expect(organisationsEinheitResources.length).toBe(1);
},
);
}); });
}); });
describe('delete', () => { describe('loadOrganisationsEinheitResources', () => {
const organisationseinheit: Organisationseinheit = organisationseinheitItems[0]; it('should call organisationsEinheitService getList', () => {
component.loadOrganisationsEinheitResources();
beforeEach(() => { expect(organisationsEinheitService.getList).toHaveBeenCalled();
organisationseinheitService.delete = jest.fn().mockReturnValue(singleCold(true));
}); });
it('should call service method', () => { it('should set organisationsEinheitResources', () => {
component.delete(organisationseinheit); const organisationsEinheitResources$: Observable<AdminOrganisationsEinheitResource[]> =
component.loadOrganisationsEinheitResources();
expect(organisationseinheitService.delete).toHaveBeenCalledWith(organisationseinheit.id); organisationsEinheitResources$.subscribe((organisationsEinheitResources: AdminOrganisationsEinheitResource[]) => {
expect(organisationsEinheitResources.length).toBe(1);
}); });
it('should assign delete progress observable', () => {
component.delete(organisationseinheit);
expect(component.deleteInProgress$).toBeObservable(singleCold(true));
}); });
}); });
describe('spinner', () => {
it('should not show if delete in not progress', () => {
fixture.detectChanges();
notExistsAsHtmlElement(fixture, spinnerSelector);
}); });
it('should show if delete in progress', () => {
component.deleteInProgress$ = of(true);
fixture.detectChanges(); describe('template', () => {
describe('organisationsEinheiten list', () => {
existsAsHtmlElement(fixture, spinnerSelector); it('should show list', () => {
existsAsHtmlElement(fixture, getDataTestIdOf('organisations-einheit-list'));
});
}); });
}); });
}); });
import { StateResource } from '@alfa-client/tech-shared'; import { getEmbeddedResources, StateResource } from '@alfa-client/tech-shared';
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs'; import { map, Observable } from 'rxjs';
import { Organisationseinheit } from '../../user/user.model'; import { OrganisationsEinheitListLinkRel } from '../organisations-einheit.linkrel';
import { OrganisationseinheitService } from '../organisationseinheit.service'; import { AdminOrganisationsEinheitListResource, AdminOrganisationsEinheitResource } from '../organisations-einheit.model';
import { OrganisationseinheitFormComponent } from './organisationseinheit-form/organisationseinheit-form.component'; import { OrganisationsEinheitService } from '../organisationseinheit.service';
@Component({ @Component({
selector: 'admin-organisationseinheit-container', selector: 'admin-organisationseinheit-container',
templateUrl: './organisationseinheit-container.component.html', templateUrl: './organisationseinheit-container.component.html',
}) })
export class OrganisationseinheitContainerComponent implements OnInit { export class OrganisationsEinheitContainerComponent implements OnInit {
organisationseinheitItems$: Observable<StateResource<Organisationseinheit[]>>; organisationsEinheitResources$: Observable<AdminOrganisationsEinheitResource[]>;
deleteInProgress$: Observable<boolean> = of(false);
@ViewChild(OrganisationseinheitFormComponent) constructor(private organisationsEinheitService: OrganisationsEinheitService) {}
private form!: OrganisationseinheitFormComponent;
constructor(private organisationseinheitService: OrganisationseinheitService) {}
ngOnInit(): void { ngOnInit(): void {
this.organisationseinheitItems$ = this.organisationseinheitService.get(); this.organisationsEinheitResources$ = this.loadOrganisationsEinheitResources();
}
public openDialogForNewGroup(): void {
this.form.open();
}
public edit(organisationseinheit: Organisationseinheit): void {
this.form.openEdit(organisationseinheit);
} }
public delete(organisationseinheit: Organisationseinheit): void { loadOrganisationsEinheitResources(): Observable<AdminOrganisationsEinheitResource[]> {
this.deleteInProgress$ = this.organisationseinheitService.delete(organisationseinheit.id); return this.organisationsEinheitService
.getList()
.pipe(
map((organisationsEinheitListResource: StateResource<AdminOrganisationsEinheitListResource>) =>
getEmbeddedResources<AdminOrganisationsEinheitResource>(
organisationsEinheitListResource,
OrganisationsEinheitListLinkRel.LIST,
),
),
);
} }
} }
<ng-container *ngIf="submitInProgress$ | async"></ng-container>
<dialog #OrganisationseinheitDialog data-test-id="organisationseinheit-dialog" class="bg-gray-50">
<button
(click)="OrganisationseinheitDialog.close()"
data-test-id="organisationseinheit-close-button"
class="absolute right-3 top-1 text-2xl text-black hover:font-bold active:text-black/80"
>
&#x2715;
</button>
<form [formGroup]="formService.form" class="m-5 grid grid-cols-1 gap-5">
<h1 class="text-2xl" data-test-id="organisationseinheit-form-header">
{{ label }}
</h1>
<text-field
label="Name"
data-test-id="organisationseinheit-name"
[formControlName]="OrganisationseinheitFormService.ORGANISATIONSEINHEIT_NAME_FIELD"
></text-field>
<text-field
label="OrganisationseinheitID"
data-test-id="organisationseinheit-id"
[formControlName]="OrganisationseinheitFormService.ORGANISATIONSEINHEIT_IDS_FIELD"
></text-field>
<ods-button-with-spinner
data-test-id="organisationseinheit-save-button"
class="justify-self-end"
(clickEmitter)="submit()"
[stateResource]="organisationseinheitItems"
text="Speichern"
>
</ods-button-with-spinner>
</form>
</dialog>
import { createEmptyStateResource } from '@alfa-client/tech-shared';
import {
dispatchEventFromFixture,
getDebugElementFromFixtureByCss,
getElementFromFixture,
mock,
Mock,
} from '@alfa-client/test-utils';
import { DebugElement } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { AbstractControl, FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
import { ButtonWithSpinnerComponent } from '@ods/component';
import { getDataTestIdOf } from 'libs/tech-shared/test/data-test';
import { MockComponent, ngMocks } from 'ng-mocks';
import { of, throwError } from 'rxjs';
import { createOrganisationseinheit } from '../../../../../test/user/user';
import { TextFieldComponent } from '../../../shared/text-field/text-field.component';
import { Organisationseinheit } from '../../../user/user.model';
import { OrganisationseinheitService } from '../../organisationseinheit.service';
import { OrganisationseinheitFormComponent } from './organisationseinheit-form.component';
import { OrganisationseinheitFormService } from './organisationseinheit-form.service';
describe('OrganisationseinheitFormComponent', () => {
let component: OrganisationseinheitFormComponent;
let fixture: ComponentFixture<OrganisationseinheitFormComponent>;
let form: UntypedFormGroup;
const organisationseinheitService: Mock<OrganisationseinheitService> = mock(OrganisationseinheitService);
const saveButtonSelector: string = getDataTestIdOf('organisationseinheit-save-button');
const closeButtonSelector: string = getDataTestIdOf('organisationseinheit-close-button');
const headerSelector: string = getDataTestIdOf('organisationseinheit-form-header');
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
OrganisationseinheitFormComponent,
MockComponent(TextFieldComponent),
MockComponent(ButtonWithSpinnerComponent),
],
imports: [ReactiveFormsModule, FormsModule, ButtonWithSpinnerComponent],
providers: [{ provide: OrganisationseinheitService, useValue: organisationseinheitService }],
}).compileComponents();
fixture = TestBed.createComponent(OrganisationseinheitFormComponent);
component = fixture.componentInstance;
form = fixture.componentInstance.formService.form;
fixture.detectChanges();
component.dialog.showModal = jest.fn();
component.dialog.close = jest.fn();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('form element', () => {
const fields: string[][] = [
[OrganisationseinheitFormService.ORGANISATIONSEINHEIT_NAME_FIELD, 'Name', 'organisationseinheit-name'],
[OrganisationseinheitFormService.ORGANISATIONSEINHEIT_IDS_FIELD, 'OrganisationseinheitID', 'organisationseinheit-id'],
];
it.each(fields)('should have label for field "%s" with name "%s"', (fieldName: string, text: string, inputId: string) => {
const textFieldElement = getElementFromFixture(fixture, getDataTestIdOf(inputId));
expect(textFieldElement.getAttribute('label')).toBe(text);
});
it.each(fields)('should bind form for text-field "%s"', (fieldName, text, dataTestId) => {
const fieldValue: string = `some text-field ${text}`;
const formControl: AbstractControl = form.get(fieldName);
const textFieldComponent: DebugElement = getDebugElementFromFixtureByCss(fixture, getDataTestIdOf(dataTestId));
ngMocks.change(textFieldComponent, fieldValue);
expect(formControl.value).toBe(fieldValue);
});
});
describe('save button', () => {
let saveButtonComponent: ButtonWithSpinnerComponent;
beforeEach(() => {
saveButtonComponent = getDebugElementFromFixtureByCss(fixture, saveButtonSelector).componentInstance;
});
it('should call submit on click', () => {
component.submit = jest.fn();
dispatchEventFromFixture(fixture, saveButtonSelector, 'clickEmitter');
expect(component.submit).toHaveBeenCalled();
});
it('should be disabled while stateResource in loading', () => {
component.organisationseinheitItems = createEmptyStateResource(true);
fixture.detectChanges();
expect(saveButtonComponent.stateResource.loading).toBe(true);
});
it('should be enabled while not in progress', () => {
component.organisationseinheitItems = createEmptyStateResource(false);
fixture.detectChanges();
expect(saveButtonComponent.stateResource.loading).toBe(false);
});
});
describe('submit', () => {
beforeEach(() => {
component.handleProgressChange = jest.fn();
});
it('should not call complete on submit error', fakeAsync(() => {
component.formService.submit = () => throwError(() => new Error('some error'));
component.submit();
component.submitInProgress$.subscribe({
error: () => {},
});
tick();
expect(component.handleProgressChange).not.toHaveBeenCalled();
}));
it('should call complete on submit event', fakeAsync(() => {
component.formService.submit = () => of(false);
component.submit();
component.submitInProgress$.subscribe();
tick();
expect(component.handleProgressChange).toHaveBeenCalled();
}));
it.each([true, false])('should use submit progress "%s"', (progress) => {
component.organisationseinheitItems = createEmptyStateResource(progress);
fixture.detectChanges();
const saveButtonComponent: ButtonWithSpinnerComponent = getDebugElementFromFixtureByCss(
fixture,
saveButtonSelector,
).componentInstance;
expect(saveButtonComponent.stateResource.loading).toBe(progress);
});
});
describe('handle progress change', () => {
it('should call complete if no errors with progress false', () => {
component.completeIfNoErrors = jest.fn();
component.handleProgressChange(false);
expect(component.completeIfNoErrors).toHaveBeenCalled();
});
it('should call complete if no errors with progress true', () => {
component.completeIfNoErrors = jest.fn();
component.handleProgressChange(true);
expect(component.completeIfNoErrors).not.toHaveBeenCalled();
});
});
describe('complete if no errors', () => {
beforeEach(() => {
component.complete = jest.fn();
});
it('should call not complete with errors', () => {
component.formService.isInvalid = jest.fn().mockReturnValue(true);
component.completeIfNoErrors();
expect(component.complete).not.toHaveBeenCalled();
});
it('should call complete without errors', () => {
component.formService.isInvalid = jest.fn().mockReturnValue(false);
component.completeIfNoErrors();
expect(component.complete).toHaveBeenCalled();
});
});
describe('complete', () => {
beforeEach(() => {
component.dialog.close = jest.fn();
component.formService.reset = jest.fn();
});
it('should close dialog', () => {
component.complete();
expect(component.dialog.close).toHaveBeenCalled();
});
it('should reset form', () => {
component.complete();
expect(component.formService.reset).toHaveBeenCalled();
});
});
describe('close button', () => {
it('should call to close dialog', () => {
component.dialog.close = jest.fn();
dispatchEventFromFixture(fixture, closeButtonSelector, 'click');
expect(component.dialog.close).toHaveBeenCalled();
});
});
describe('open', () => {
beforeEach(() => {
form.markAsTouched();
});
it('should set create label', () => {
component.open();
expect(component.label).toEqual(OrganisationseinheitFormComponent.CREATE_LABEL);
});
it('should open form', () => {
component.open();
expect(component.dialog.showModal).toHaveBeenCalled();
});
it('should reset form', () => {
component.formService.reset = jest.fn();
component.open();
expect(component.formService.reset).toHaveBeenCalled();
});
});
describe('open edit', () => {
const organisationseinheit: Organisationseinheit = createOrganisationseinheit();
it('should set edit label', () => {
component.openEdit(organisationseinheit);
expect(component.label).toEqual(OrganisationseinheitFormComponent.EDIT_LABEL);
});
it('should open dialog', () => {
component.open();
expect(component.dialog.showModal).toHaveBeenCalled();
});
it('should patch form', () => {
component.formService.patch = jest.fn();
component.openEdit(organisationseinheit);
expect(component.formService.patch).toHaveBeenCalledWith(organisationseinheit);
});
});
describe('header', () => {
it('should show label text', () => {
const text: string = 'test-text';
component.label = text;
fixture.detectChanges();
const headerElement: HTMLElement = getElementFromFixture(fixture, headerSelector);
expect(headerElement.textContent.trim()).toEqual(text);
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment