diff --git a/alfa-client/apps/admin-e2e/docker-compose.yml b/alfa-client/apps/admin-e2e/docker-compose.yml index 11682ebeb086ca5740d4da1b5c3c2feaa34aaee1..edc5dc8b2f98963025b2521c960a87a29efc6efb 100644 --- a/alfa-client/apps/admin-e2e/docker-compose.yml +++ b/alfa-client/apps/admin-e2e/docker-compose.yml @@ -79,7 +79,7 @@ services: depends_on: - user-manager extra_hosts: - - "host.docker.internal:host-gateway" + - 'host.docker.internal:host-gateway' alfa-cors-proxy: image: alfa-cors-proxy @@ -91,7 +91,6 @@ services: alfa: condition: service_started - user-manager: image: docker.ozg-sh.de/user-manager:${USER_MANAGER_DOCKER_IMAGE:-snapshot-latest} platform: linux/amd64 @@ -145,4 +144,4 @@ services: user-manager: condition: service_started extra_hosts: - - "host.docker.internal:host-gateway" + - 'host.docker.internal:host-gateway' diff --git a/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts index 0af0f39b14cfd07cdd0ad868b8c780c1bbbea19f..12140f8351c958ba5388638076727ef41bee5053 100644 --- a/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts +++ b/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts @@ -22,154 +22,157 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import 'cypress-real-events'; -import { AdminUserE2E } from '../../model/util'; -import { exist } from '../../support/cypress.util'; +import { convertToDataTestId } from '../../support/tech-util'; -export class BenutzerE2EComponent { +//TODO BenutzerListPage erstellen welche den Button und die Liste enthaelt. +export class BenutzerListE2EComponent { + private readonly headline: string = 'user-list-headline'; + private readonly list: string = 'user-list'; private readonly benutzerHinzufuegenButton: string = 'add-user-button'; - private readonly userEntry: string = 'user-entry-'; - private readonly userVorname: string = 'Vorname-text-input'; - private readonly userNachname: string = 'Nachname-text-input'; - private readonly userBenutzername: string = 'Benutzername-text-input'; - private readonly userMail: string = 'E-Mail-text-input'; - private readonly adminCheckbox: string = 'Admin-checkbox-editor'; - private readonly loeschenCheckbox: string = 'Loschen-checkbox-editor'; - private readonly userCheckbox: string = 'User-checkbox-editor'; - private readonly postCheckbox: string = 'Poststelle-checkbox-editor'; - private readonly saveButton: string = 'save-button'; - private readonly organisationsEinheitCheckbox: string = '-checkbox-editor'; + + public getHeadline(): Cypress.Chainable<Element> { + return cy.getTestElement(this.headline); + } + + public getList(): Cypress.Chainable<Element> { + return cy.getTestElement(this.list); + } public getHinzufuegenButton(): Cypress.Chainable<Element> { return cy.getTestElement(this.benutzerHinzufuegenButton); } - public hinzufuegenButtonIsVisible(): void { - exist(this.getHinzufuegenButton()); + public getItem(userName: string): BenutzerListItemE2EComponent { + return new BenutzerListItemE2EComponent(userName); } +} + +export class BenutzerListItemE2EComponent { + private root: string; - public clickAddUser(): void { - this.getHinzufuegenButton().click(); + private fullName: string = 'fullname'; + private email: string = 'email'; + private userName: string = 'username'; + + private roles: string = 'roles'; + + private organisationsEinheiten: string = 'organisations-einheiten'; + private noOrganisationsEinheitenText: string = 'no-organisations-einheit-text'; + + constructor(userName: string) { + this.root = convertToDataTestId(userName) + '-user-entry'; } - public addUser(user: AdminUserE2E): void { - this.enterVorname(user.vorname); - this.enterNachname(user.nachname); - this.enterBenutzername(user.benutzername); - this.enterMail(user.email); + public getRoot(): Cypress.Chainable<Element> { + return cy.getTestElement(this.root); + } - if (user.isAdmin) { - this.clickAdminCheckbox(); - } - if (user.isUser) { - this.clickUserCheckbox(); - } - if (user.isLoeschen) { - this.clickLoeschenCheckbox(); - } - if (user.isPoststelle) { - this.clickPostCheckbox(); - } + public getRoles(): Cypress.Chainable<Element> { + return this.getRoot().getTestElementWithClass(this.roles); + } - if (user.organisationseinheiten) { - for (const einheit of user.organisationseinheiten) { - this.clickOrganisationsEinheitCheckbox(einheit); - } - } - this.saveUser(); + public getOrganisationsEinheiten(): Cypress.Chainable<Element> { + return this.getRoot().getTestElementWithClass(this.organisationsEinheiten); } - public getUserEntry(user: string): Cypress.Chainable<Element> { - user = this.userEntry + user; - return cy.getTestElement(user); + public getNoOrganisationsEinheitText(): Cypress.Chainable<Element> { + return this.getRoot().getTestElementWithClass(this.noOrganisationsEinheitenText); } - public clickUserEntry(user: string): void { - this.getUserEntry(user).click(); + public getFullName(): Cypress.Chainable<Element> { + return this.getRoot().getTestElementWithClass(this.fullName); } - public stringExistsInUserEntry(phrase: string, user: string): void { - this.getUserEntry(user).within(() => { - exist(cy.contains(phrase)); - }); + public getEMail(): Cypress.Chainable<Element> { + return this.getRoot().getTestElementWithClass(this.email); } - public getVornameInput(): Cypress.Chainable<Element> { - return cy.getTestElement(this.userVorname); + public getUserName(): Cypress.Chainable<Element> { + return this.getRoot().getTestElementWithClass(this.userName); } +} + +export class BenutzerE2EComponent { + private readonly headline: string = 'benutzer-form-headline'; + + private readonly userVorname: string = 'Vorname-text-input'; + private readonly userNachname: string = 'Nachname-text-input'; + private readonly userBenutzername: string = 'Benutzername-text-input'; + private readonly userMail: string = 'E-Mail-text-input'; - public enterVorname(vorname: string): void { - this.getVornameInput().type(vorname); + private readonly adminCheckbox: string = 'Admin-checkbox-editor'; + private readonly loeschenCheckbox: string = 'Loschen-checkbox-editor'; + private readonly userCheckbox: string = 'User-checkbox-editor'; + private readonly postCheckbox: string = 'Poststelle-checkbox-editor'; + private readonly datenbeauftragungCheckbox: string = 'Datenbeauftragung-checkbox-editor'; + + private readonly organisationsEinheitCheckboxSuffix: string = '-checkbox-editor'; + + private readonly saveButton: string = 'save-button'; + private readonly deleteButton: string = 'delete-button'; + + public getHeadline(): Cypress.Chainable<Element> { + return cy.getTestElement(this.headline); } - public getNachnameInput(): Cypress.Chainable<Element> { - return cy.getTestElement(this.userNachname); + public getVornameInput(): Cypress.Chainable<Element> { + return cy.getTestElement(this.userVorname); } - public enterNachname(nachname: string): void { - this.getNachnameInput().type(nachname); + public getNachnameInput(): Cypress.Chainable<Element> { + return cy.getTestElement(this.userNachname); } public getBenutzernameInput(): Cypress.Chainable<Element> { return cy.getTestElement(this.userBenutzername); } - public enterBenutzername(benutzername: string): void { - this.getBenutzernameInput().type(benutzername); - } - public getMailInput(): Cypress.Chainable<Element> { return cy.getTestElement(this.userMail); } - public enterMail(mail: string): void { - this.getMailInput().type(mail); - } - public getAdminCheckbox(): Cypress.Chainable<Element> { return cy.getTestElement(this.adminCheckbox); } - public clickAdminCheckbox(): void { - this.getAdminCheckbox().click(); - } - - public getLoeschenCheckbox() { + public getLoeschenCheckbox(): Cypress.Chainable<Element> { return cy.getTestElement(this.loeschenCheckbox); } - public clickLoeschenCheckbox(): void { - this.getLoeschenCheckbox().click(); - } - public getUserCheckbox(): Cypress.Chainable<Element> { return cy.getTestElement(this.userCheckbox); } - public clickUserCheckbox(): void { - this.getUserCheckbox().click(); - } - public getPostCheckbox(): Cypress.Chainable<Element> { return cy.getTestElement(this.postCheckbox); } - public clickPostCheckbox(): void { - this.getPostCheckbox().click(); + public getDatenbeauftragungCheckbox(): Cypress.Chainable<Element> { + return cy.getTestElement(this.datenbeauftragungCheckbox); + } + + public getOrganisationsEinheitCheckbox(einheit: string): Cypress.Chainable<Element> { + return cy.getTestElement(einheit + this.organisationsEinheitCheckboxSuffix); } public getSaveButton(): Cypress.Chainable<Element> { return cy.getTestElement(this.saveButton); } - public saveUser(): void { - this.getSaveButton().click(); + public getDeleteButton(): Cypress.Chainable<Element> { + return cy.getTestElement(this.deleteButton); } +} - public getOrganisationsEinheitCheckbox(einheit: string): Cypress.Chainable<Element> { - return cy.getTestElement(einheit + this.organisationsEinheitCheckbox); +export class BenutzerDeleteDialogE2EComponent { + private readonly deleteButton: string = 'dialog-delete'; + private readonly cancelButton: string = 'cancel-dialog'; + + public getCancelButton(): Cypress.Chainable<Element> { + return cy.getTestElement(this.cancelButton); } - public clickOrganisationsEinheitCheckbox(einheit: string): void { - this.getOrganisationsEinheitCheckbox(einheit).click(); + public getDeleteButton(): Cypress.Chainable<Element> { + return cy.getTestElement(this.deleteButton); } } diff --git a/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten-signatur.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten-signatur.e2e.component.ts deleted file mode 100644 index e1f91229ad68813bbab4625854ddc2ed6f89b79d..0000000000000000000000000000000000000000 --- a/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten-signatur.e2e.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { clearText, haveValue, typeText } from '../../support/cypress.util'; - -export class OrganisationseinheitenSignaturE2EComponent { - private readonly organisationsEinheitName: string = 'organisations-form-container-headline'; - private readonly signatureText: string = 'signature-textarea'; - private readonly saveSignatureButton: string = 'save-button'; - - public getOrganisationsEinheitName(): Cypress.Chainable<Element> { - return cy.getTestElement(this.organisationsEinheitName); - } - - public getSignatureText(): Cypress.Chainable<Element> { - return cy.getTestElement(this.signatureText); - } - - public setSignature(signatur: string): void { - this.clearSignature(); - typeText(this.getSignatureText(), signatur); - } - - public clearSignature(): void { - clearText(this.getSignatureText()); - } - - public getSaveButton(): Cypress.Chainable<Element> { - return cy.getTestElement(this.saveSignatureButton); - } - - public saveSignature(): void { - this.getSaveButton().click(); - } - - public hasSignature(compare: string): void { - haveValue(this.getSignatureText(), compare); - } - - public hasScrollbar(): void { - this.getSignatureText().then((textarea) => { - const scrollHeight = textarea[0].scrollHeight; - const clientHeight = textarea[0].clientHeight; - - expect(scrollHeight).to.be.greaterThan(clientHeight); - }); - } -} diff --git a/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts index 4d38f761c34b586f7da999a6a2e46a1d74056371..ac3917c2dd21ca7859c009a824267033c8df5349 100644 --- a/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts +++ b/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts @@ -21,66 +21,52 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { containClass, exist, haveText, notContainClass } from '../../support/cypress.util'; +export class OrganisationsEinheitListE2EComponent { + private readonly root: string = 'organisations-einheit-list'; -export class OrganisationsEinheitenE2EComponent { - private readonly organisationsEinheitenList: string = 'organisations-einheit-list'; - private readonly organisationsEinheitenName: string = 'organisations-einheit-name'; - private readonly organisationsEinheitenID: string = 'organisations-einheit-id'; - private readonly organisationsEinheitHinzufuegen: string = 'add-organisationseinheit-button'; - private readonly errorColor: string = 'text-red-500'; - private readonly errorIcon: string = 'organisations-einheit-sync-error'; - - private readonly errorText: string = 'Organisationseinheit wurde nicht in den PVOG-Daten gefunden.'; - - public getOrganisationsEinheitList(): Cypress.Chainable<Element> { - return cy.getTestElement(this.organisationsEinheitenList); + public getRoot(): Cypress.Chainable<Element> { + return cy.getTestElement(this.root); } - public getListItemByName(name: string): Cypress.Chainable<JQuery<HTMLElement>> { - return cy.getTestElement(this.organisationsEinheitenName).contains(name); + public getListItem(name: string): OrganisationsEinheitListItemE2EComponent { + return new OrganisationsEinheitListItemE2EComponent(name); } +} - public openOrganisationsEinheit(name: string): void { - this.getListItemByName(name).click(); - } +export class OrganisationsEinheitListItemE2EComponent { + private root: string; - public getOrganisationsEinheitHinzufuegenButton(): Cypress.Chainable<Element> { - return cy.getTestElement(this.organisationsEinheitHinzufuegen); - } + private readonly organisationsEinheitItemSuffix: string = '-organisation-item'; + private readonly deleteButton: string = 'delete-button'; - public clickHinzufuegen(): void { - this.getOrganisationsEinheitHinzufuegenButton().click(); + constructor(name: string) { + this.root = name; } - public organisationsEinheitContainsID(name: string, id: string): void { - this.getListItemByName(name) - .parents('a') - .within(() => { - haveText(cy.getTestElement(this.organisationsEinheitenID), id); - }); + public getRoot(): Cypress.Chainable<Element> { + return cy.getTestElement(this.root + this.organisationsEinheitItemSuffix); } - public elementIsShownAsError(name: string): void { - containClass(this.getListItemByName(name).closest('ods-list-item'), this.errorColor); + public getDeleteButton(): Cypress.Chainable<Element> { + return this.getRoot().findTestElementWithClass(this.deleteButton); } +} + +export class OrganisationsEinheitDeleteDialogE2EComponent { + private readonly root: string = 'organisations-einheit-delete-dialog'; + + private readonly deleteButton: string = 'dialog-delete-button'; + private readonly cancelButton: string = 'dialog-cancel-button'; - public getErrorIconInElement(name: string): Cypress.Chainable<JQuery<HTMLAnchorElement>> { - return this.getListItemByName(name) - .parents('a') - .within(() => { - exist(cy.getTestElement(this.errorIcon)); - }); + public getRoot(): Cypress.Chainable<Element> { + return cy.getTestElement(this.root); } - public getErrorTooltip(name: string): Cypress.Chainable<JQuery<HTMLElement>> { - return this.getErrorIconInElement(name) - .find('ods-exclamation-icon[mattooltip]') - .should('have.attr', 'mattooltip') - .and('include', this.errorText); + public getDeleteButton(): Cypress.Chainable<Element> { + return cy.getTestElement(this.deleteButton); } - public elementShowsNoError(name: string): void { - notContainClass(this.getListItemByName(name).closest('ods-list-item'), this.errorColor); + public getCancelButton(): Cypress.Chainable<Element> { + return cy.getTestElement(this.cancelButton); } } diff --git a/alfa-client/apps/admin-e2e/src/components/statistik/statistik-fields-form.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/statistik/statistik-fields-form.e2e.component.ts index f3c565869006d9935a5c54de0a9fb388d73cbc3c..8936e02edbe69c765364304bc497d2be61a970da 100644 --- a/alfa-client/apps/admin-e2e/src/components/statistik/statistik-fields-form.e2e.component.ts +++ b/alfa-client/apps/admin-e2e/src/components/statistik/statistik-fields-form.e2e.component.ts @@ -1,15 +1,16 @@ import { enterWith } from '../../support/cypress.util'; export class StatistikFieldsFormE2EComponent { - private readonly locatorFormEngineInput: string = 'form-engine-input'; - private readonly locatorFormIdInput: string = 'form-id-input'; - private readonly locatorFormDataFieldInput: string = 'data-statistik-field-'; - private readonly locatorAddFieldButton: string = 'add-data-field-button'; - private readonly locatorSaveButton: string = 'save-statistik-fields-button'; - private readonly locatorCancelButton: string = 'cancel-statistik-fields-button'; + private readonly formEngineInput: string = 'form-engine-name-text-input'; + private readonly formIdInput: string = 'form-id-text-input'; + private readonly formDataFieldInput: string = 'mapping-field-'; + private readonly addDataFieldButton: string = 'add-mapping-button'; + private readonly deleteDataFieldButtonPrefix: string = 'remove-mapping-button-'; + private readonly saveButton: string = 'save-button'; + private readonly cancelButton: string = 'cancel-button'; public getFormEngineInput(): Cypress.Chainable<Element> { - return cy.getTestElement(this.locatorFormEngineInput); + return cy.getTestElement(this.formEngineInput); } public enterFormEngine(text: string): void { @@ -17,7 +18,7 @@ export class StatistikFieldsFormE2EComponent { } public getFormIdInput(): Cypress.Chainable<Element> { - return cy.getTestElement(this.locatorFormIdInput); + return cy.getTestElement(this.formIdInput); } public enterFormId(text: string): void { @@ -25,7 +26,7 @@ export class StatistikFieldsFormE2EComponent { } public getAddFieldButton(): Cypress.Chainable<Element> { - return cy.getTestElement(this.locatorAddFieldButton); + return cy.getTestElement(this.addDataFieldButton); } public addField(): void { @@ -33,15 +34,23 @@ export class StatistikFieldsFormE2EComponent { } public getDataFieldInput(index: number): Cypress.Chainable<Element> { - return cy.getTestElement(this.locatorFormDataFieldInput + index); + return cy.getTestElement(this.formDataFieldInput + index + '-text-input'); } public enterDataFieldPath(text: string, index: number): void { enterWith(this.getDataFieldInput(index), text); } + public getDataFieldDeleteButton(index: number): Cypress.Chainable<Element> { + return cy.getTestElement(this.deleteDataFieldButtonPrefix + index); + } + + public deleteDataField(index: number): void { + this.getDataFieldDeleteButton(index).click(); + } + public getSaveButton(): Cypress.Chainable<Element> { - return cy.getTestElement(this.locatorSaveButton); + return cy.getTestElement(this.saveButton); } public save(): void { @@ -49,7 +58,7 @@ export class StatistikFieldsFormE2EComponent { } public getCancelButton(): Cypress.Chainable<Element> { - return cy.getTestElement(this.locatorCancelButton); + return cy.getTestElement(this.cancelButton); } public cancel(): void { diff --git a/alfa-client/apps/admin-e2e/src/components/statistik/statistik.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/statistik/statistik.e2e.component.ts index 8748a5bad339ca0bc381d88d51c3fbe3747d0d16..b7467232f5d596a2984bc109e84180810c5e8dc3 100644 --- a/alfa-client/apps/admin-e2e/src/components/statistik/statistik.e2e.component.ts +++ b/alfa-client/apps/admin-e2e/src/components/statistik/statistik.e2e.component.ts @@ -1,14 +1,12 @@ -import { exist } from '../../support/cypress.util'; - export class StatistikE2EComponent { private readonly locatorHeaderText: string = 'statistik-header-text'; private readonly locatorWeitereFelderAuswertenButton = 'weitere-felder-auswerten-button'; - public isHeaderTextVisible(): void { - exist(cy.getTestElement(this.locatorHeaderText)); + public getHeaderText(): Cypress.Chainable<Element> { + return cy.getTestElement(this.locatorHeaderText); } - public getWeiterFelderAuswertenButton(): Cypress.Chainable<Element> { + public getWeitereFelderAuswertenButton(): Cypress.Chainable<Element> { return cy.getTestElement(this.locatorWeitereFelderAuswertenButton); } } diff --git a/alfa-client/apps/admin-e2e/src/components/user-profile/current-user-profile.component.e2e.ts b/alfa-client/apps/admin-e2e/src/components/user-profile/current-user-profile.component.e2e.ts index b96370d68b65cf7848e13b6eb5807473aadb40a3..effa3edb707cf24d98306dc972fefae6770b154e 100644 --- a/alfa-client/apps/admin-e2e/src/components/user-profile/current-user-profile.component.e2e.ts +++ b/alfa-client/apps/admin-e2e/src/components/user-profile/current-user-profile.component.e2e.ts @@ -26,6 +26,7 @@ import { UserProfileE2EComponent } from './user-profile.component.e2e'; export class CurrentUserProfileE2EComponent { private readonly locatorUserIconButton: string = 'popup-button-content'; private readonly locatorLogoutButton: string = 'popup-logout-button'; + private readonly locatorDocumentation: string = 'admin-documentation'; private readonly locatorRoot: string = 'current-user'; @@ -46,7 +47,11 @@ export class CurrentUserProfileE2EComponent { return cy.getTestElement(this.locatorUserIconButton); } - private getLogoutButton() { + public getLogoutButton() { return cy.getTestElement(this.locatorLogoutButton); } + + public getDocumentation() { + return cy.getTestElement(this.locatorDocumentation); + } } diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/app/user-profile-menu.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/app/user-profile-menu.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..19852a52775d1f104314441a3a85ce466a7df7f0 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/app/user-profile-menu.cy.ts @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { getUrl } from '@ngxp/rest'; +import { HttpMethodE2E } from '../../../model/util'; +import { HeaderE2EComponent } from '../../../page-objects/header.po'; +import { MainPage } from '../../../page-objects/main.po'; +import { getBaseUrl, intercept, waitOfInterceptor } from '../../../support/cypress-helper'; +import { exist, shouldHaveAttribute } from '../../../support/cypress.util'; +import { ApiRootLinkRelE2E } from '../../../support/linkrels'; +import { loginAsAriane } from '../../../support/user-util'; + +describe('User Profile Menu', () => { + const mainPage: MainPage = new MainPage(); + const header: HeaderE2EComponent = mainPage.getHeader(); + + let documentationLink: string = ''; + + before(() => { + const interceptor: string = 'getApiRoot'; + intercept(HttpMethodE2E.GET, `${getBaseUrl()}/api`).as(interceptor); + loginAsAriane(); + waitOfInterceptor(interceptor).then( + (interception) => (documentationLink = getUrl(interception?.response?.body, ApiRootLinkRelE2E.DOCUMENTATIONS)), + ); + }); + + describe('open user profile menu', () => { + before(() => { + header.getCurrentUserProfile().getUserIconButton().click(); + }); + + it('should show logout button', () => { + exist(header.getCurrentUserProfile().getLogoutButton()); + }); + + it('should show documentation', () => { + exist(header.getCurrentUserProfile().getDocumentation()); + }); + + it('should find documentation link', () => { + shouldHaveAttribute(header.getCurrentUserProfile().getDocumentation().find('a'), 'href', documentationLink); + }); + }); +}); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-anlegen.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-anlegen.cy.ts index 0b9aad6ab869bad69f47e7c59cbf0b85e39abe61..0d4a3c538f90b3621783a7f8ccc750429823434f 100644 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-anlegen.cy.ts +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-anlegen.cy.ts @@ -1,69 +1,60 @@ - -import { MainPage } from 'apps/admin-e2e/src/page-objects/main.po'; -import { BenutzerE2EComponent } from '../../../components/benutzer/benutzer.e2e.component'; -import { beChecked, beEnabled, contains, exist, notBeChecked, notBeEnabled } from '../../../support/cypress.util'; -import { AlfaRollen, AlfaUsers, loginAsAriane } from '../../../support/user-util'; +import { E2EBenutzerHelper } from 'apps/admin-e2e/src/helper/benutzer/benutzer.helper'; +import { E2EBenutzerVerifier } from 'apps/admin-e2e/src/helper/benutzer/benutzer.verifier'; +import { getCypressEnv, interceptWithResponse, waitOfInterceptor } from 'apps/admin-e2e/src/support/cypress-helper'; import { SnackBarE2EComponent } from '../../../components/ui/snackbar.e2e.component'; -import { SnackbarMessagesE2E } from '../../../model/util'; -import { getCypressEnv, interceptWithResponse, wait, waitOfInterceptor } from 'apps/admin-e2e/src/support/cypress-helper'; -import { AdminUserE2E, HttpMethodE2E } from '../../../model/util'; +import { AdminUserE2E, HttpMethodE2E, SnackbarMessagesE2E } from '../../../model/util'; +import { contains, notExist } from '../../../support/cypress.util'; +import { loginAsAriane } from '../../../support/user-util'; -const mainPage: MainPage = new MainPage(); -const benutzerPage: BenutzerE2EComponent = new BenutzerE2EComponent(); -const snackBar: SnackBarE2EComponent = new SnackBarE2EComponent(); +describe('Benutzer anlegen', () => { + const benutzerHelper: E2EBenutzerHelper = new E2EBenutzerHelper(); + const benutzerVerifier: E2EBenutzerVerifier = new E2EBenutzerVerifier(); -const vorname: string = 'Theo'; -const nachname: string = 'Testuser'; -const benutzername: string = 'testtheo'; -const emailAddress: string = 'theo.test@ozg-sh.de'; + const snackBar: SnackBarE2EComponent = new SnackBarE2EComponent(); -const newUser: AdminUserE2E = { - vorname: 'Theo', - nachname: 'Testuser', - benutzername: 'testtheo', - email: 'theo.test@ozg-sh.de', - isAdmin: true, - isUser: true, -} + const newUser: AdminUserE2E = { + vorname: 'Theo', + nachname: 'Testuser', + username: 'testtheo', + email: 'theo.test@ozg-sh.de', + isAdmin: true, + isUser: true, + organisationseinheiten: [], + }; -describe('Benutzer anlegen', () => { before(() => { loginAsAriane(); }); - it('should open Benutzer tab and show Hinzufuegen button', () => { - mainPage.clickBenutzerNavigationItem(); - - exist(benutzerPage.getHinzufuegenButton()); - }); - - it('should show snackbar message on error', () => { + it('should show error snackbar after save and receiving error response', () => { const interceptor: string = 'postUser'; - const url: string = getCypressEnv('keycloakUrl') + '/admin/realms/' + getCypressEnv('keycloakRealm') + '/users' + const url: string = getCypressEnv('keycloakUrl') + '/admin/realms/' + getCypressEnv('keycloakRealm') + '/users'; const errorCode: number = 500; const errorBody: string = 'Internal Server Error'; - interceptWithResponse(HttpMethodE2E.POST, url, { errorCode, errorBody }).as(interceptor); + benutzerHelper.openNewBenutzerPage(); - benutzerPage.clickAddUser(); - benutzerPage.addUser(newUser); + benutzerHelper.addBenutzer(newUser); + benutzerHelper.saveBenutzer(); waitOfInterceptor(interceptor).then(() => { contains(snackBar.getMessage(), SnackbarMessagesE2E.NUTZER_FEHLGESCHLAGEN); + snackBar.getCloseButton().click(); + notExist(snackBar.getMessage()); }); }); - it('should show snackbar message on saving user', () => { - snackBar.getCloseButton().click(); + it('should show snackbar after save', () => { + benutzerHelper.openNewBenutzerPage(); + + benutzerHelper.addBenutzer(newUser); + benutzerHelper.saveBenutzer(); - benutzerPage.saveUser(); contains(snackBar.getMessage(), SnackbarMessagesE2E.NUTZER_ANGELEGT); + snackBar.getCloseButton().click(); }); - it('should display new user in users table', () => { - benutzerPage.stringExistsInUserEntry(AlfaRollen.USER, benutzername); - benutzerPage.stringExistsInUserEntry(vorname, benutzername); - benutzerPage.stringExistsInUserEntry(nachname, benutzername); - // FEHLT NOCH: benutzerPage.stringExistsInUserEntry(AlfaRollen.ADMIN, benutzername); + it('should show created user in list', () => { + benutzerVerifier.verifyUserInList(newUser); }); }); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-loesche.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-loesche.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd494a41834116ef96c2ed992e026fba95bb53ef --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-loesche.cy.ts @@ -0,0 +1,33 @@ +import { faker } from '@faker-js/faker'; +import { E2EBenutzerHelper } from 'apps/admin-e2e/src/helper/benutzer/benutzer.helper'; +import { E2EBenutzerVerifier } from 'apps/admin-e2e/src/helper/benutzer/benutzer.verifier'; +import { AdminUserE2E } from 'apps/admin-e2e/src/model/util'; +import { loginAsAriane } from 'apps/admin-e2e/src/support/user-util'; + +describe('Benutzer Löschen', () => { + const benutzerVerifier: E2EBenutzerVerifier = new E2EBenutzerVerifier(); + const benutzerHelper: E2EBenutzerHelper = new E2EBenutzerHelper(); + + const userName: string = 'testtheo' + faker.string.uuid(); + const user: AdminUserE2E = { + vorname: 'Theo', + nachname: 'Testuser', + username: userName, + email: 'theo' + faker.string.uuid() + '@ozg-sh.de', + isUser: true, + organisationseinheiten: [], + }; + + before(() => { + loginAsAriane(); + }); + + it('should delete user', () => { + benutzerHelper.openNewBenutzerPage(); + benutzerHelper.addBenutzerAndSave(user); + + benutzerHelper.deleteBenutzer(userName); + + benutzerVerifier.verifyUserNotInList(userName); + }); +}); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-zu-oe-hinzufuegen.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-zu-oe-hinzufuegen.cy.ts index 9095c63279a17ffe2cbada1f4de9f956593c0638..2a6fc233600a14024c3d62d7056afb41effa8750 100644 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-zu-oe-hinzufuegen.cy.ts +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer-zu-oe-hinzufuegen.cy.ts @@ -1,110 +1,84 @@ -import { OrganisationsEinheitenE2EComponent } from 'apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component'; -import { ZustaendigeStelleDialogE2EComponent } from 'apps/admin-e2e/src/components/zustaendige-stelle/zustaendige-stelle-dialog.e2e.component'; +import { E2EBenutzerHelper } from 'apps/admin-e2e/src/helper/benutzer/benutzer.helper'; +import { E2EBenutzerVerifier } from 'apps/admin-e2e/src/helper/benutzer/benutzer.verifier'; +import { OrganisationsEinheitE2E } from 'apps/admin-e2e/src/model/organisations-einheit'; import { AdminUserE2E } from 'apps/admin-e2e/src/model/util'; -import { MainPage } from 'apps/admin-e2e/src/page-objects/main.po'; -import { BenutzerE2EComponent } from '../../../components/benutzer/benutzer.e2e.component'; -import { beChecked, exist, notBeChecked } from '../../../support/cypress.util'; import { loginAsAriane } from '../../../support/user-util'; describe('Organisationseinheit zu Benutzer hinzufügen', () => { - const mainPage: MainPage = new MainPage(); - const benutzerPage: BenutzerE2EComponent = new BenutzerE2EComponent(); - const organisationsEinheitenComponent: OrganisationsEinheitenE2EComponent = new OrganisationsEinheitenE2EComponent(); - const zustaendigeStelleSearchComponent: ZustaendigeStelleDialogE2EComponent = new ZustaendigeStelleDialogE2EComponent(); - const organistationsEinheitOrdnungsamt: string = 'Ordnungsamt'; - const organisationsEinheitDenkmalpflege: string = 'Denkmalpflege'; - const organisationsEinheitLiegenschaften: string = 'Liegenschaften'; - const organisationsEinheitNone: string = 'keine zuständige Stelle zugewiesen'; + const benutzerVerifier: E2EBenutzerVerifier = new E2EBenutzerVerifier(); + const benutzerHelper: E2EBenutzerHelper = new E2EBenutzerHelper(); + const vorname: string = 'Theo'; const nachname: string = 'Testuser'; + const now1 = new Date(); - const benutzername1: string = 'testtheo' + now1.getSeconds().toString() + now1.getMilliseconds().toString(); + const userName1: string = 'testtheo' + now1.getSeconds().toString() + now1.getMilliseconds().toString(); const emailAddress1: string = 'theo' + now1.getSeconds().toString() + now1.getMilliseconds().toString() + '@ozg-sh.de'; + const now2 = new Date(now1.getTime() + 1000); - const benutzername2: string = 'testtheo' + now2.getSeconds().toString() + now2.getMilliseconds().toString(); + const userName2: string = 'testtheo' + now2.getSeconds().toString() + now2.getMilliseconds().toString(); const emailAddress2: string = 'theo' + now2.getSeconds().toString() + now2.getMilliseconds().toString() + '@ozg-sh.de'; - const newUser1: AdminUserE2E = { + const userWithoutOrganisationsEinheiten: AdminUserE2E = { vorname: vorname, nachname: nachname, - benutzername: benutzername1, + username: userName1, email: emailAddress1, isUser: true, + organisationseinheiten: [], }; - const newUser2: AdminUserE2E = { + const userWithOrganisationsEinheiten: AdminUserE2E = { vorname: vorname, nachname: nachname, - benutzername: benutzername2, + username: userName2, email: emailAddress2, isUser: true, - organisationseinheiten: ['Denkmalpflege'], + organisationseinheiten: [OrganisationsEinheitE2E.DENKMALPFLEGE], }; before(() => { loginAsAriane(); }); - it('should click Hinzufügen button and show Organisationseinheiten in Benutzer page', () => { - mainPage.benutzerNavigationItemIsVisible(); - mainPage.clickBenutzerNavigationItem(); - benutzerPage.hinzufuegenButtonIsVisible(); + it('should show organisationseinheiten in formular', () => { + benutzerHelper.openNewBenutzerPage(); - benutzerPage.clickAddUser(); - exist(benutzerPage.getOrganisationsEinheitCheckbox(organisationsEinheitDenkmalpflege)); - exist(benutzerPage.getOrganisationsEinheitCheckbox(organistationsEinheitOrdnungsamt)); + benutzerVerifier.verifyOrganisationsEinheitenInFormular([ + OrganisationsEinheitE2E.DENKMALPFLEGE, + OrganisationsEinheitE2E.ORDNUNGSAMT, + ]); }); - it('should add no Organisationseinheit to new user without selection and saving', () => { - benutzerPage.addUser(newUser1); + it('should add new user without organisationseinheiten', () => { + benutzerHelper.openNewBenutzerPage(); - benutzerPage.stringExistsInUserEntry(organisationsEinheitNone, benutzername1); - }); + benutzerHelper.addBenutzerAndSave(userWithoutOrganisationsEinheiten); - it('should add Organisationseinheit to new user after selection and saving', () => { - benutzerPage.clickAddUser(); - benutzerPage.addUser(newUser2); - - benutzerPage.stringExistsInUserEntry(organisationsEinheitDenkmalpflege, benutzername2); + benutzerVerifier.verifyNoOrganisationsEinheitInBenutzerList(userName1); }); - it('should remove Organisationseinheit from existing user on deselection and save', () => { - benutzerPage.clickUserEntry(benutzername2); - beChecked(benutzerPage.getOrganisationsEinheitCheckbox(organisationsEinheitDenkmalpflege)); + it('should add new user with organisationseinheiten', () => { + benutzerHelper.openNewBenutzerPage(); - benutzerPage.clickOrganisationsEinheitCheckbox(organisationsEinheitDenkmalpflege); - benutzerPage.saveUser(); + benutzerHelper.addBenutzerAndSave(userWithOrganisationsEinheiten); - benutzerPage.stringExistsInUserEntry(organisationsEinheitNone, benutzername2); + benutzerVerifier.verfiyOrganisationsEinheitInBenutzerList(OrganisationsEinheitE2E.DENKMALPFLEGE, userName2); }); - it('should add Organisationseinheit to existing user on selection and save', () => { - benutzerPage.clickUserEntry(benutzername2); - notBeChecked(benutzerPage.getOrganisationsEinheitCheckbox(organistationsEinheitOrdnungsamt)); + it('should remove organisationseinheit from existing user', () => { + benutzerHelper.openBenutzerPage(userName2); - benutzerPage.clickOrganisationsEinheitCheckbox(organistationsEinheitOrdnungsamt); - benutzerPage.saveUser(); + benutzerHelper.editOrganisationsEinheitenAndSave([OrganisationsEinheitE2E.DENKMALPFLEGE]); - benutzerPage.stringExistsInUserEntry(organistationsEinheitOrdnungsamt, benutzername2); + benutzerVerifier.verifyNoOrganisationsEinheitInBenutzerList(userName2); }); - it('should enable new Organisationseinheit for users after adding it', () => { - mainPage.clickOrganisationsEinheitenNavigationItem(); - organisationsEinheitenComponent.clickHinzufuegen(); - zustaendigeStelleSearchComponent.enterSearchTerm(organisationsEinheitLiegenschaften); - zustaendigeStelleSearchComponent.getZustaendigeStelleTitle(0).then((title: string) => { - zustaendigeStelleSearchComponent.clickFoundItem(0); - }); - - mainPage.clickBenutzerNavigationItem(); - benutzerPage.clickUserEntry(benutzername2); + it('should add organisationseinheit to existing user', () => { + benutzerHelper.openBenutzerPage(userName2); - exist(benutzerPage.getOrganisationsEinheitCheckbox(organisationsEinheitLiegenschaften)); - }); + benutzerHelper.editOrganisationsEinheitenAndSave([OrganisationsEinheitE2E.ORDNUNGSAMT]); - it('should add new Organisationseinheit to existing user', () => { - benutzerPage.clickOrganisationsEinheitCheckbox(organisationsEinheitLiegenschaften); - benutzerPage.saveUser(); - benutzerPage.stringExistsInUserEntry(organisationsEinheitLiegenschaften, benutzername2); + benutzerVerifier.verfiyOrganisationsEinheitInBenutzerList(OrganisationsEinheitE2E.ORDNUNGSAMT, userName2); }); }); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer_rollen.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer_rollen.cy.ts index 9514df893c043a400aa61ec1e517a6007846eb68..6f2aeed20740461f3dc978afdc2b974fec471f00 100644 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer_rollen.cy.ts +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/benutzer_rollen/benutzer_rollen.cy.ts @@ -21,45 +21,63 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { MainPage } from 'apps/admin-e2e/src/page-objects/main.po'; -import { BenutzerE2EComponent } from '../../../components/benutzer/benutzer.e2e.component'; -import { beChecked, beEnabled, exist, notBeChecked, notBeEnabled } from '../../../support/cypress.util'; +import { E2EBenutzerHelper } from 'apps/admin-e2e/src/helper/benutzer/benutzer.helper'; +import { OrganisationsEinheitE2E } from 'apps/admin-e2e/src/model/organisations-einheit'; +import { + BenutzerE2EComponent, + BenutzerListE2EComponent, + BenutzerListItemE2EComponent, +} from '../../../components/benutzer/benutzer.e2e.component'; +import { beChecked, beEnabled, contains, exist, notBeChecked, notBeEnabled } from '../../../support/cypress.util'; import { AlfaRollen, AlfaUsers, loginAsAriane } from '../../../support/user-util'; -const mainPage: MainPage = new MainPage(); -const benutzerPage: BenutzerE2EComponent = new BenutzerE2EComponent(); -const organisationsEinheitOrdnungsamt: string = 'Ordnungsamt'; -const organisationsEinheitDenkmalpflege: string = 'Denkmalpflege'; -const organisationsEinheitWirtschaftsfoerderung: string = 'Wirtschaftsförderung'; -const organisationsEinheitNone: string = 'keine zuständige Stelle zugewiesen'; -const emailAddress: string = 'peter.von.der.post@ozg-sh.de'; - describe('Benutzer und Rollen', () => { + const benutzerPage: BenutzerE2EComponent = new BenutzerE2EComponent(); + const benutzerListPage: BenutzerListE2EComponent = new BenutzerListE2EComponent(); + + //TODO In der entsprechenden json hinterlegen und darauf zugreifen + const emailAddress: string = 'peter.von.der.post@ozg-sh.de'; + + const helper: E2EBenutzerHelper = new E2EBenutzerHelper(); + before(() => { loginAsAriane(); }); - it('should open Benutzer tab and show Hinzufuegen button', () => { - mainPage.clickBenutzerNavigationItem(); + it('should show users and attributes in list', () => { + helper.openBenutzerListPage(); - exist(benutzerPage.getHinzufuegenButton()); - }); + const ariane: BenutzerListItemE2EComponent = benutzerListPage.getItem(AlfaUsers.ARAINE); + exist(ariane.getRoot()); + contains(ariane.getRoles(), AlfaRollen.USER); + + const dorothea: BenutzerListItemE2EComponent = benutzerListPage.getItem(AlfaUsers.DOROTHEA); + exist(dorothea.getRoot()); + contains(dorothea.getRoles(), AlfaRollen.USER); + + const ludwig: BenutzerListItemE2EComponent = benutzerListPage.getItem(AlfaUsers.LUDWIG); + exist(ludwig.getRoot()); + contains(ludwig.getRoles(), AlfaRollen.LOESCHEN); + contains(ludwig.getOrganisationsEinheiten(), OrganisationsEinheitE2E.ORDNUNGSAMT); + + const zelda: BenutzerListItemE2EComponent = benutzerListPage.getItem(AlfaUsers.ZELDA); + exist(zelda.getRoot()); + contains(zelda.getRoles(), AlfaRollen.USER); + contains(zelda.getOrganisationsEinheiten(), OrganisationsEinheitE2E.DENKMALPFLEGE); + contains(zelda.getOrganisationsEinheiten(), OrganisationsEinheitE2E.WIRTSCHAFTSFOERDERUNG); + + const peter: BenutzerListItemE2EComponent = benutzerListPage.getItem(AlfaUsers.PETER); + exist(peter.getRoot()); + contains(peter.getRoles(), AlfaRollen.POSTSTELLE); + contains(peter.getEMail(), emailAddress); - it('should show users and attributes in table', () => { - exist(benutzerPage.getUserEntry('ariane')); - benutzerPage.stringExistsInUserEntry(AlfaRollen.USER, AlfaUsers.DOROTHEA); - benutzerPage.stringExistsInUserEntry(organisationsEinheitOrdnungsamt, AlfaUsers.LUDWIG); - benutzerPage.stringExistsInUserEntry(AlfaRollen.USER, AlfaUsers.ZELDA); - benutzerPage.stringExistsInUserEntry(AlfaRollen.LOESCHEN, AlfaUsers.LUDWIG); - benutzerPage.stringExistsInUserEntry(organisationsEinheitDenkmalpflege, AlfaUsers.ZELDA); - benutzerPage.stringExistsInUserEntry(organisationsEinheitWirtschaftsfoerderung, AlfaUsers.ZELDA); - benutzerPage.stringExistsInUserEntry(organisationsEinheitNone, AlfaUsers.RICHARD); - benutzerPage.stringExistsInUserEntry(emailAddress, AlfaUsers.PETER); - benutzerPage.stringExistsInUserEntry(AlfaRollen.POSTSTELLE, AlfaUsers.PETER); + const richard: BenutzerListItemE2EComponent = benutzerListPage.getItem(AlfaUsers.RICHARD); + exist(richard.getRoot()); + exist(richard.getNoOrganisationsEinheitText()); }); it('should show single user screen on click', () => { - benutzerPage.clickAddUser(); + helper.openNewBenutzerPage(); exist(benutzerPage.getVornameInput()); exist(benutzerPage.getNachnameInput()); @@ -73,47 +91,47 @@ describe('Benutzer und Rollen', () => { }); it('should activate loeschen checkbox and deactivate the other two checkboxes', () => { - benutzerPage.clickLoeschenCheckbox(); + benutzerPage.getLoeschenCheckbox().click(); beChecked(benutzerPage.getLoeschenCheckbox()); notBeEnabled(benutzerPage.getUserCheckbox()); notBeEnabled(benutzerPage.getPostCheckbox()); - benutzerPage.clickLoeschenCheckbox(); + benutzerPage.getLoeschenCheckbox().click(); notBeChecked(benutzerPage.getLoeschenCheckbox()); beEnabled(benutzerPage.getUserCheckbox()); beEnabled(benutzerPage.getPostCheckbox()); }); it('should additionally activate and deactivate admin checkbox', () => { - benutzerPage.clickLoeschenCheckbox(); - benutzerPage.clickAdminCheckbox(); + benutzerPage.getLoeschenCheckbox().click(); + benutzerPage.getAdminCheckbox().click(); beChecked(benutzerPage.getLoeschenCheckbox()); beChecked(benutzerPage.getAdminCheckbox()); - benutzerPage.clickAdminCheckbox(); + benutzerPage.getAdminCheckbox().click(); notBeChecked(benutzerPage.getAdminCheckbox()); }); it('should activate user checkbox and deactivate the other two checkboxes', () => { - benutzerPage.clickLoeschenCheckbox(); - benutzerPage.clickUserCheckbox(); + benutzerPage.getLoeschenCheckbox().click(); + benutzerPage.getUserCheckbox().click(); beChecked(benutzerPage.getUserCheckbox()); notBeEnabled(benutzerPage.getLoeschenCheckbox()); notBeEnabled(benutzerPage.getPostCheckbox()); - benutzerPage.clickUserCheckbox(); + benutzerPage.getUserCheckbox().click(); notBeChecked(benutzerPage.getUserCheckbox()); beEnabled(benutzerPage.getLoeschenCheckbox()); beEnabled(benutzerPage.getPostCheckbox()); }); it('should activate post checkbox and deactivate the other two checkboxes', () => { - benutzerPage.clickPostCheckbox(); + benutzerPage.getPostCheckbox().click(); beChecked(benutzerPage.getPostCheckbox()); notBeEnabled(benutzerPage.getLoeschenCheckbox()); notBeEnabled(benutzerPage.getUserCheckbox()); - benutzerPage.clickPostCheckbox(); + benutzerPage.getPostCheckbox().click(); notBeChecked(benutzerPage.getPostCheckbox()); beEnabled(benutzerPage.getLoeschenCheckbox()); beEnabled(benutzerPage.getUserCheckbox()); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/daria.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/daria.cy.ts index 57867568976e5deb0675a0ccd3fc52f9e5e19d79..33a06a73b83c3a839b0490f96aa17ddb42106994 100644 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/daria.cy.ts +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/daria.cy.ts @@ -1,5 +1,5 @@ import { MainPage, waitForSpinnerToDisappear } from 'apps/admin-e2e/src/page-objects/main.po'; -import { exist, notExist } from 'apps/admin-e2e/src/support/cypress.util'; +import { exist, notExist, visible } from 'apps/admin-e2e/src/support/cypress.util'; import { loginAsDaria } from 'apps/admin-e2e/src/support/user-util'; import { StatistikE2EComponent } from '../../../components/statistik/statistik.e2e.component'; @@ -37,7 +37,7 @@ describe('Navigation', () => { }); it('should show header text', () => { - statistikPage.isHeaderTextVisible(); + visible(statistikPage.getHeaderText()); }); }); }); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/safira.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/safira.cy.ts index 6f76dafdfc3161646383b34465056c2e751fa33c..005da2e5a576948787fdeb1f26165bd250b22138 100644 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/safira.cy.ts +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/navigation/safira.cy.ts @@ -1,5 +1,5 @@ import { MainPage, waitForSpinnerToDisappear } from 'apps/admin-e2e/src/page-objects/main.po'; -import { exist } from 'apps/admin-e2e/src/support/cypress.util'; +import { exist, visible } from 'apps/admin-e2e/src/support/cypress.util'; import { loginAsSafira } from 'apps/admin-e2e/src/support/user-util'; import { StatistikE2EComponent } from '../../../components/statistik/statistik.e2e.component'; @@ -38,7 +38,7 @@ describe('Navigation', () => { }); it('should show page on selection', () => { - statistikPage.isHeaderTextVisible(); + visible(statistikPage.getHeaderText()); }); it('should mark navigation item as selected', () => { diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisations-einheit.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisations-einheit.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f52b488067ab890200384cfe2d0eba448c87078 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisations-einheit.cy.ts @@ -0,0 +1,53 @@ +import { E2EOrganisationsEinheitHelper } from 'apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.helper'; +import { E2EOrganisationsEinheitVerifier } from 'apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.verifier'; +import { OrganisationsEinheitPage } from 'apps/admin-e2e/src/page-objects/organisations-einheit.po'; +import { ZustaendigeStelleDialogE2EComponent } from '../../../components/zustaendige-stelle/zustaendige-stelle-dialog.e2e.component'; +import { exist } from '../../../support/cypress.util'; +import { loginAsAriane } from '../../../support/user-util'; + +describe('Organisationseinheit', () => { + const organisationsEinheitPage: OrganisationsEinheitPage = new OrganisationsEinheitPage(); + + const organisationsEinheitHelper: E2EOrganisationsEinheitHelper = new E2EOrganisationsEinheitHelper(); + const organisationsEinheitVerifier: E2EOrganisationsEinheitVerifier = new E2EOrganisationsEinheitVerifier(); + + const zustaendigeStelleSearchComponent: ZustaendigeStelleDialogE2EComponent = new ZustaendigeStelleDialogE2EComponent(); + + const organisationsEinheit: string = 'Wasserwerk - Hamburg Wasser - Hamburger Stadtentwässerung'; + + before(() => { + loginAsAriane(); + }); + + describe('hinzufügen', () => { + it('should show search dialog on add button click', () => { + organisationsEinheitHelper.openOrganisationsEinheitPage(); + + organisationsEinheitPage.getAddButton().click(); + + exist(zustaendigeStelleSearchComponent.getZustaendigeStelleForm()); + }); + + it('should find at least one organisationseinheit on search', () => { + zustaendigeStelleSearchComponent.enterSearchTerm(organisationsEinheit); + + zustaendigeStelleSearchComponent.expectNumberOfEntriesToBeGreaterThan(1); + }); + + it('should show organisationseinheit in list', () => { + organisationsEinheitHelper.addOrganisationsEinheit(organisationsEinheit); + + organisationsEinheitVerifier.verifyOrganisationsEinheitInList(organisationsEinheit); + }); + }); + + describe('löschen', () => { + it('should not show entry in list', () => { + organisationsEinheitHelper.openOrganisationsEinheitPage(); + + organisationsEinheitHelper.deleteOrganisationsEinheit(organisationsEinheit); + + organisationsEinheitVerifier.verifyOrganisationsEinheitNotInList(organisationsEinheit); + }); + }); +}); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisations-einheiten-page.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisations-einheiten-page.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..c17724c86bb9ed02eacc5f5f88bbcf35e865b79a --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisations-einheiten-page.cy.ts @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { OrganisationsEinheitListE2EComponent } from 'apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component'; +import { E2EOrganisationsEinheitHelper } from 'apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.helper'; +import { OrganisationsEinheitE2E } from 'apps/admin-e2e/src/model/organisations-einheit'; +import { OrganisationsEinheitPage } from 'apps/admin-e2e/src/page-objects/organisations-einheit.po'; +import { exist } from '../../../support/cypress.util'; +import { loginAsAriane } from '../../../support/user-util'; + +describe('Organisationsheiten page', () => { + const organisationsEinheitPage: OrganisationsEinheitPage = new OrganisationsEinheitPage(); + + const organisationsEinheitHelper: E2EOrganisationsEinheitHelper = new E2EOrganisationsEinheitHelper(); + + const organisationsEinheitList: OrganisationsEinheitListE2EComponent = new OrganisationsEinheitListE2EComponent(); + + before(() => { + loginAsAriane(); + }); + + it('should show list', () => { + organisationsEinheitHelper.openOrganisationsEinheitPage(); + + exist(organisationsEinheitPage.getList().getRoot()); + }); + + it('should show add button', () => { + organisationsEinheitHelper.openOrganisationsEinheitPage(); + + exist(organisationsEinheitPage.getAddButton()); + }); + + it('should show default (Bauamt, Fundstelle, Denkmalpflege) entries', () => { + organisationsEinheitHelper.openOrganisationsEinheitPage(); + + exist(organisationsEinheitList.getListItem(OrganisationsEinheitE2E.BAUAMT).getRoot()); + exist(organisationsEinheitList.getListItem(OrganisationsEinheitE2E.FUNDSTELLE).getRoot()); + exist(organisationsEinheitList.getListItem(OrganisationsEinheitE2E.DENKMALPFLEGE).getRoot()); + }); +}); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-hinzufuegen.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-hinzufuegen.cy.ts deleted file mode 100644 index ecdc072a69ee0a81f41aa786ccb0a23eacc4f450..0000000000000000000000000000000000000000 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-hinzufuegen.cy.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { OrganisationsEinheitenE2EComponent } from '../../../components/organisationseinheiten/organisationseinheiten.e2e.component'; -import { ZustaendigeStelleDialogE2EComponent } from '../../../components/zustaendige-stelle/zustaendige-stelle-dialog.e2e.component'; -import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; -import { exist } from '../../../support/cypress.util'; -import { loginAsAriane } from '../../../support/user-util'; - -describe('Organisationseinheiten', () => { - const mainPage: MainPage = new MainPage(); - const organisationsEinheitenComponent: OrganisationsEinheitenE2EComponent = new OrganisationsEinheitenE2EComponent(); - const zustaendigeStelleSearchComponent: ZustaendigeStelleDialogE2EComponent = new ZustaendigeStelleDialogE2EComponent(); - - const searchTerm: string = 'Hamburg'; - - before(() => { - loginAsAriane(); - }); - - it('should show table with Organisationseinheiten', () => { - waitForSpinnerToDisappear(); - mainPage.clickOrganisationsEinheitenNavigationItem(); - - exist(organisationsEinheitenComponent.getOrganisationsEinheitHinzufuegenButton()); - }); - - it('should show button to add Organisationseinheit', () => { - exist(organisationsEinheitenComponent.getOrganisationsEinheitHinzufuegenButton()); - }); - - it('should show search Organisationseinheit dialog', () => { - organisationsEinheitenComponent.clickHinzufuegen(); - - exist(zustaendigeStelleSearchComponent.getZustaendigeStelleForm()); - }); - - it('should find at least one Organisationseinheit', () => { - zustaendigeStelleSearchComponent.enterSearchTerm(searchTerm); - - zustaendigeStelleSearchComponent.expectNumberOfEntriesToBeGreaterThan(1); - }); - - it('should add first Organisationseinheit', () => { - zustaendigeStelleSearchComponent.getZustaendigeStelleTitle(0).then((title: string) => { - zustaendigeStelleSearchComponent.clickFoundItem(0); - exist(organisationsEinheitenComponent.getListItemByName(title)); - }); - }); -}); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-laden.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-laden.cy.ts deleted file mode 100644 index 91a1673e96e41b8dbdae4e4338e864a0527f816e..0000000000000000000000000000000000000000 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-laden.cy.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { OrganisationsEinheitenE2EComponent } from '../../../components/organisationseinheiten/organisationseinheiten.e2e.component'; -import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; -import { exist } from '../../../support/cypress.util'; -import { OrganisationsEinheitSyncResultE2E } from '../../../support/organisationseinheit'; -import { - createBauamtOrganisationsEinheit, - createDenkmalpflegeOrganisationsEinheit, - createFundstelleOrganisationsEinheit, - initOrganisationsEinheiten, -} from '../../../support/organisationseinheit-util'; -import { loginAsAriane } from '../../../support/user-util'; - -describe.skip('TODO: activate after fix for OZG-7391: show Organisationsheiten', () => { - const mainPage: MainPage = new MainPage(); - const organisationsEinheitenTab: OrganisationsEinheitenE2EComponent = new OrganisationsEinheitenE2EComponent(); - - before(() => { - loginAsAriane(); - initOrganisationsEinheiten([ - createBauamtOrganisationsEinheit(), - { ...createDenkmalpflegeOrganisationsEinheit(), syncResult: OrganisationsEinheitSyncResultE2E.NAME_MISMATCH }, - { ...createFundstelleOrganisationsEinheit(), syncResult: OrganisationsEinheitSyncResultE2E.NOT_FOUND_IN_PVOG }, - ]); - }); - - it('should show table of Organisationseinheiten', () => { - waitForSpinnerToDisappear(); - mainPage.clickOrganisationsEinheitenNavigationItem(); - - exist(organisationsEinheitenTab.getOrganisationsEinheitList()); - }); - - it('should show identical data in Keycloak and PVOG without error', () => { - organisationsEinheitenTab.getListItemByName('Bauamt'); - organisationsEinheitenTab.organisationsEinheitContainsID('Bauamt', '248240886'); - organisationsEinheitenTab.elementShowsNoError('Bauamt'); - }); - - it('should show data not found in PVOG in red color and with error icon and tooltip message', () => { - const organisationsEinheitName: string = 'Fundstelle'; - - organisationsEinheitenTab.organisationsEinheitContainsID(organisationsEinheitName, '10363455'); - organisationsEinheitenTab.elementIsShownAsError(organisationsEinheitName); - organisationsEinheitenTab.getErrorIconInElement(organisationsEinheitName); - organisationsEinheitenTab.getErrorTooltip(organisationsEinheitName); - }); - - it('should get name for OE from PVOG, if mismatch with Keycloak, but without error', () => { - organisationsEinheitenTab.organisationsEinheitContainsID('Landesamt für Denkmalpflege', '9093371'); - organisationsEinheitenTab.elementShowsNoError('Landesamt für Denkmalpflege'); - }); -}); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-signaturen.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-signaturen.cy.ts deleted file mode 100644 index 1e5a19499a0ad62ca94e6cbf43aff7c18856f6d8..0000000000000000000000000000000000000000 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/organisationseinheiten/organisationseinheiten-signaturen.cy.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { OrganisationseinheitenSignaturE2EComponent } from '../../../components/organisationseinheiten/organisationseinheiten-signatur.e2e.component'; -import { OrganisationsEinheitenE2EComponent } from '../../../components/organisationseinheiten/organisationseinheiten.e2e.component'; -import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; -import { haveText } from '../../../support/cypress.util'; -import { loginAsAriane } from '../../../support/user-util'; - -describe.skip('TODO: activate after fix for OZG-7391: Signatur', () => { - const mainPage: MainPage = new MainPage(); - const signaturePage: OrganisationseinheitenSignaturE2EComponent = new OrganisationseinheitenSignaturE2EComponent(); - const organisationsEinheitenTab: OrganisationsEinheitenE2EComponent = new OrganisationsEinheitenE2EComponent(); - - const signatureText: string = 'Signatur\nmit\n\n\n\nZeilenumbruch\n\n'; - - before(() => { - loginAsAriane(); - }); - - it('should open signature page for Bauamt on click', () => { - waitForSpinnerToDisappear(); - mainPage.openOrganisationsEinheiten(); - organisationsEinheitenTab.openOrganisationsEinheit('Bauamt'); - - haveText(signaturePage.getOrganisationsEinheitName(), 'Bauamt'); - }); - - it('should show signature input with scrollbar', () => { - signaturePage.setSignature(signatureText); - signaturePage.saveSignature(); - - signaturePage.hasSignature(signatureText); - signaturePage.hasScrollbar(); - }); - - it.skip('should enter and save empty signature', () => { - signaturePage.clearSignature(); - signaturePage.saveSignature(); - - signaturePage.hasSignature(''); - }); -}); diff --git a/alfa-client/apps/admin-e2e/src/e2e/main-tests/statistik/statistik-fields.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/main-tests/statistik/statistik-fields.cy.ts index dae58525a8d2522f3c0a241f5f24423cba928f4b..d877ae4b900417af37328322176ce8070c6d0844 100644 --- a/alfa-client/apps/admin-e2e/src/e2e/main-tests/statistik/statistik-fields.cy.ts +++ b/alfa-client/apps/admin-e2e/src/e2e/main-tests/statistik/statistik-fields.cy.ts @@ -1,40 +1,30 @@ -import { ROUTES } from '@admin-client/shared'; +import { StatistikE2EComponent } from 'apps/admin-e2e/src/components/statistik/statistik.e2e.component'; import { StatistikFieldsFormE2EComponent } from '../../../components/statistik/statistik-fields-form.e2e.component'; -import { StatistikE2EComponent } from '../../../components/statistik/statistik.e2e.component'; -import { urlShouldEndsWith } from '../../../support/cypress-helper'; -import { exist } from '../../../support/cypress.util'; +import { exist, haveText, haveValue } from '../../../support/cypress.util'; import { loginAsDaria } from '../../../support/user-util'; describe('Felder in Statistik hinzufügen', () => { const component: StatistikE2EComponent = new StatistikE2EComponent(); const fieldsFormComponent: StatistikFieldsFormE2EComponent = new StatistikFieldsFormE2EComponent(); + const dataText1: string = 'Eingabe A'; + const dataText2: string = 'Eingabe B'; + before(() => { loginAsDaria(); }); - it('should be on statistik page', () => { - urlShouldEndsWith(ROUTES.STATISTIK); - }); - - it('should show statistik header', () => { - component.isHeaderTextVisible(); + it('should show "Weitere Felder auswerten" button', () => { + exist(component.getWeitereFelderAuswertenButton()); }); - it('should show "Weiter Felder auswerten" button', () => { - exist(component.getWeiterFelderAuswertenButton()); - }); - - it('should navigate to route', () => { - component.getWeiterFelderAuswertenButton().click(); - - urlShouldEndsWith(ROUTES.STATISTIK_NEU); - }); + it('should have all form elements after button click', () => { + component.getWeitereFelderAuswertenButton().click(); - it('should have all form elements', () => { exist(fieldsFormComponent.getFormEngineInput()); exist(fieldsFormComponent.getFormIdInput()); exist(fieldsFormComponent.getDataFieldInput(0)); + exist(fieldsFormComponent.getDataFieldDeleteButton(0)); exist(fieldsFormComponent.getAddFieldButton()); exist(fieldsFormComponent.getSaveButton()); exist(fieldsFormComponent.getCancelButton()); @@ -46,9 +36,22 @@ describe('Felder in Statistik hinzufügen', () => { exist(fieldsFormComponent.getDataFieldInput(1)); }); + it('should enter text in both data fields', () => { + fieldsFormComponent.enterDataFieldPath(dataText1, 0); + fieldsFormComponent.enterDataFieldPath(dataText2, 1); + + haveValue(fieldsFormComponent.getDataFieldInput(0), dataText1); + haveValue(fieldsFormComponent.getDataFieldInput(1), dataText2); + }); + + it('should delete data fields', () => { + fieldsFormComponent.deleteDataField(0); + haveValue(fieldsFormComponent.getDataFieldInput(0), dataText2); + }); + it('should navigate to statistik on cancel', () => { fieldsFormComponent.cancel(); - urlShouldEndsWith(ROUTES.STATISTIK); + exist(component.getWeitereFelderAuswertenButton()); }); }); diff --git a/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.executor.ts b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.executor.ts new file mode 100644 index 0000000000000000000000000000000000000000..320db78e13e0fee0cae8dad0cd94969ad7b0e2de --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.executor.ts @@ -0,0 +1,61 @@ +import { + BenutzerDeleteDialogE2EComponent, + BenutzerE2EComponent, + BenutzerListE2EComponent, +} from '../../components/benutzer/benutzer.e2e.component'; +import { SnackBarE2EComponent } from '../../components/ui/snackbar.e2e.component'; +import { OrganisationsEinheitE2E } from '../../model/organisations-einheit'; +import { AdminUserE2E } from '../../model/util'; +import { exist, notExist } from '../../support/cypress.util'; + +export class E2EBenutzerExecutor { + private benutzerPage: BenutzerE2EComponent = new BenutzerE2EComponent(); + private snackBar: SnackBarE2EComponent = new SnackBarE2EComponent(); + private benutzerDeleteDialog: BenutzerDeleteDialogE2EComponent = new BenutzerDeleteDialogE2EComponent(); + private benutzerListPage: BenutzerListE2EComponent = new BenutzerListE2EComponent(); + + public modifyBenutzer(user: AdminUserE2E): void { + this.benutzerPage.getVornameInput().type(user.vorname); + this.benutzerPage.getNachnameInput().type(user.nachname); + this.benutzerPage.getBenutzernameInput().type(user.username); + this.benutzerPage.getMailInput().type(user.email); + + if (user.isAdmin) { + this.benutzerPage.getAdminCheckbox().click(); + } + if (user.isUser) { + this.benutzerPage.getUserCheckbox().click(); + } + if (user.isLoeschen) { + this.benutzerPage.getLoeschenCheckbox().click(); + } + if (user.isPoststelle) { + this.benutzerPage.getPostCheckbox().click(); + } + + this.modifyOrganisationsEinheiten(user.organisationseinheiten); + } + + public modifyOrganisationsEinheiten(organisationseinheiten: OrganisationsEinheitE2E[]): void { + organisationseinheiten.forEach((name: string) => this.benutzerPage.getOrganisationsEinheitCheckbox(name).click()); + } + + public saveAndCloseSnackbar(): void { + this.saveBenutzer(); + exist(this.snackBar.getMessage()); + this.snackBar.getCloseButton().click(); + notExist(this.snackBar.getMessage()); + exist(this.benutzerListPage.getList()); + } + + public saveBenutzer(): void { + this.benutzerPage.getSaveButton().click(); + } + + public deleteBenutzer(): void { + this.benutzerPage.getDeleteButton().click(); + exist(this.benutzerDeleteDialog.getDeleteButton()); + this.benutzerDeleteDialog.getDeleteButton().click(); + exist(this.benutzerListPage.getList()); + } +} diff --git a/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.helper.ts b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.helper.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b06766322f460cdb12f6ec065e19b9e79e756f0 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.helper.ts @@ -0,0 +1,61 @@ +import { OrganisationsEinheitE2E } from '../../model/organisations-einheit'; +import { AdminUserE2E } from '../../model/util'; +import { E2EBenutzerExecutor } from './benutzer.executor'; +import { E2EBenutzerNavigator } from './benutzer.navigator'; + +export class E2EBenutzerHelper { + private navigator: E2EBenutzerNavigator = new E2EBenutzerNavigator(); + private executer: E2EBenutzerExecutor = new E2EBenutzerExecutor(); + + public openBenutzerListPage(): void { + this.navigator.openBenutzerListPage(); + } + + public openNewBenutzerPage(): void { + this.navigator.openNewBenutzerPage(); + } + + public addBenutzerAndSave(user: AdminUserE2E): void { + this.addBenutzer(user); + this.saveAndCloseSnackbar(); + } + + public addBenutzer(user: AdminUserE2E): void { + this.modifyBenutzer(user); + } + + public editBenutzerAndSave(user: AdminUserE2E): void { + this.editBenutzer(user); + this.saveAndCloseSnackbar(); + } + + public editBenutzer(user: AdminUserE2E): void { + this.modifyBenutzer(user); + } + + private modifyBenutzer(user: AdminUserE2E): void { + this.executer.modifyBenutzer(user); + } + + public editOrganisationsEinheitenAndSave(organisationsEinheiten: OrganisationsEinheitE2E[]): void { + this.executer.modifyOrganisationsEinheiten(organisationsEinheiten); + this.saveAndCloseSnackbar(); + } + + private saveAndCloseSnackbar(): void { + this.executer.saveAndCloseSnackbar(); + } + + public saveBenutzer(): void { + this.executer.saveBenutzer(); + } + + public deleteBenutzer(userName: string): void { + this.openBenutzerPage(userName); + this.executer.deleteBenutzer(); + } + + public openBenutzerPage(userName: string): void { + this.navigator.openBenutzerPage(userName); + } +} diff --git a/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.navigator.ts b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.navigator.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc5eee020b1931cd7dfa074c9ae070e7c6fe5121 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.navigator.ts @@ -0,0 +1,32 @@ +import { BenutzerE2EComponent, BenutzerListE2EComponent } from '../../components/benutzer/benutzer.e2e.component'; +import { MainPage } from '../../page-objects/main.po'; +import { exist } from '../../support/cypress.util'; + +export class E2EBenutzerNavigator { + private mainPage: MainPage = new MainPage(); + private benutzerListPage: BenutzerListE2EComponent = new BenutzerListE2EComponent(); + private benutzerPage: BenutzerE2EComponent = new BenutzerE2EComponent(); + + public openNewBenutzerPage(): void { + this.openBenutzerListPage(); + + this.benutzerListPage.getHinzufuegenButton().click(); + exist(this.benutzerPage.getHeadline()); + } + + public openBenutzerPage(userName: string) { + this.openBenutzerListPage(); + this.benutzerListPage.getItem(userName).getRoot().click(); + exist(this.benutzerPage.getHeadline()); + } + + public openBenutzerListPage(): void { + this.navigateToDomain(); + this.mainPage.getBenutzerNavigationItem().click(); + exist(this.benutzerListPage.getHeadline()); + } + + private navigateToDomain(): void { + this.mainPage.getHeader().getLogo().click(); + } +} diff --git a/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.verifier.ts b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.verifier.ts new file mode 100644 index 0000000000000000000000000000000000000000..be750e3dfbfe90c3821451932ecb475bd31cbbe5 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/benutzer/benutzer.verifier.ts @@ -0,0 +1,51 @@ +import { + BenutzerE2EComponent, + BenutzerListE2EComponent, + BenutzerListItemE2EComponent, +} from '../../components/benutzer/benutzer.e2e.component'; +import { AdminUserE2E } from '../../model/util'; +import { contains, exist, notExist } from '../../support/cypress.util'; +import { AlfaRollen } from '../../support/user-util'; + +export class E2EBenutzerVerifier { + private noOrganisationsEinheitAssignedText: string = 'keine zuständige Stelle zugewiesen'; + + private benutzerListPage: BenutzerListE2EComponent = new BenutzerListE2EComponent(); + private benutzerPage: BenutzerE2EComponent = new BenutzerE2EComponent(); + + public verfiyOrganisationsEinheitInBenutzerList(organisationsEinheit: string, userName: string): void { + const benutzerItem: BenutzerListItemE2EComponent = this.getBenutzerItem(userName); + contains(benutzerItem.getOrganisationsEinheiten(), organisationsEinheit); + } + + public verifyNoOrganisationsEinheitInBenutzerList(userName: string): void { + const benutzerItem: BenutzerListItemE2EComponent = this.getBenutzerItem(userName); + contains(benutzerItem.getNoOrganisationsEinheitText(), this.noOrganisationsEinheitAssignedText); + } + + public verifyOrganisationsEinheitenInFormular(organisationsEinheiten: string[]): void { + organisationsEinheiten.forEach((organisationsEinheit) => { + exist(this.benutzerPage.getOrganisationsEinheitCheckbox(organisationsEinheit)); + }); + } + + public verifyUserInList(user: AdminUserE2E): void { + const benutzer: BenutzerListItemE2EComponent = this.getBenutzerItem(user.username); + contains(benutzer.getUserName(), user.username); + contains(benutzer.getFullName(), user.vorname); + contains(benutzer.getFullName(), user.nachname); + + if (user.isUser) contains(benutzer.getRoles(), AlfaRollen.USER); + if (user.isLoeschen) contains(benutzer.getRoles(), AlfaRollen.LOESCHEN); + if (user.isPoststelle) contains(benutzer.getRoles(), AlfaRollen.POSTSTELLE); + if (user.isAdmin) contains(benutzer.getRoles(), AlfaRollen.ADMIN); + } + + public verifyUserNotInList(userName: string): void { + notExist(this.getBenutzerItem(userName).getRoot()); + } + + private getBenutzerItem(userName: string): BenutzerListItemE2EComponent { + return this.benutzerListPage.getItem(userName); + } +} diff --git a/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.executor.ts b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.executor.ts new file mode 100644 index 0000000000000000000000000000000000000000..a89ad1f958ca17a73511980431455f6e8ae479bc --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.executor.ts @@ -0,0 +1,34 @@ +import { + OrganisationsEinheitDeleteDialogE2EComponent, + OrganisationsEinheitListE2EComponent, +} from '../../components/organisationseinheiten/organisationseinheiten.e2e.component'; +import { ZustaendigeStelleDialogE2EComponent } from '../../components/zustaendige-stelle/zustaendige-stelle-dialog.e2e.component'; +import { OrganisationsEinheitPage } from '../../page-objects/organisations-einheit.po'; +import { exist } from '../../support/cypress.util'; +import { convertToDataTestId } from '../../support/tech-util'; + +export class E2EOrganisationsEinheitExecutor { + private readonly organisationsEinheitPage: OrganisationsEinheitPage = new OrganisationsEinheitPage(); + private readonly organisationsEinheitList: OrganisationsEinheitListE2EComponent = this.organisationsEinheitPage.getList(); + + private readonly deleteDialog: OrganisationsEinheitDeleteDialogE2EComponent = + new OrganisationsEinheitDeleteDialogE2EComponent(); + + private readonly zustaendigeStelleSearchComponent: ZustaendigeStelleDialogE2EComponent = + new ZustaendigeStelleDialogE2EComponent(); + + public addOrganisationsEinheit(name: string): void { + //TODO von index auf Name umstellen + this.zustaendigeStelleSearchComponent + .getZustaendigeStelleTitle(0) + .then((name: string) => this.zustaendigeStelleSearchComponent.clickFoundItem(0)); + exist(this.organisationsEinheitList.getRoot()); + } + + public deleteOrganisationsEinheit(name: string): void { + this.organisationsEinheitList.getListItem(convertToDataTestId(name)).getDeleteButton().click(); + exist(this.deleteDialog.getRoot()); + this.deleteDialog.getDeleteButton().click(); + exist(this.organisationsEinheitList.getRoot()); + } +} diff --git a/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.helper.ts b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.helper.ts new file mode 100644 index 0000000000000000000000000000000000000000..339793cde888c3cedcbd1dc6e4dd3b7a7f28e313 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.helper.ts @@ -0,0 +1,19 @@ +import { E2EOrganisationsEinheitExecutor } from './organisations-einheit.executor'; +import { E2EOrganisationsEinheitNavigator } from './organisations-einheit.navigator'; + +export class E2EOrganisationsEinheitHelper { + private readonly navigator: E2EOrganisationsEinheitNavigator = new E2EOrganisationsEinheitNavigator(); + private readonly executor: E2EOrganisationsEinheitExecutor = new E2EOrganisationsEinheitExecutor(); + + public openOrganisationsEinheitPage(): void { + this.navigator.openOrganisationsEinheitListPage(); + } + + public addOrganisationsEinheit(name: string): void { + this.executor.addOrganisationsEinheit(name); + } + + public deleteOrganisationsEinheit(name: string): void { + this.executor.deleteOrganisationsEinheit(name); + } +} diff --git a/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.navigator.ts b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.navigator.ts new file mode 100644 index 0000000000000000000000000000000000000000..6274ee80d4bbbf2d2326f685d83d84da6e1ea07e --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.navigator.ts @@ -0,0 +1,21 @@ +import { OrganisationsEinheitListE2EComponent } from '../../components/organisationseinheiten/organisationseinheiten.e2e.component'; +import { MainPage } from '../../page-objects/main.po'; +import { OrganisationsEinheitPage } from '../../page-objects/organisations-einheit.po'; +import { exist } from '../../support/cypress.util'; + +export class E2EOrganisationsEinheitNavigator { + private readonly mainPage: MainPage = new MainPage(); + + private readonly organisationsEinheitPage: OrganisationsEinheitPage = new OrganisationsEinheitPage(); + private readonly organisationsEinheitList: OrganisationsEinheitListE2EComponent = this.organisationsEinheitPage.getList(); + + public openOrganisationsEinheitListPage(): void { + this.navigateToDomain(); + this.mainPage.getOrganisationEinheitNavigationItem().click(); + exist(this.organisationsEinheitList.getRoot()); + } + + private navigateToDomain(): void { + this.mainPage.getHeader().getLogo().click(); + } +} diff --git a/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.verifier.ts b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.verifier.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb1b43021649ffea97c252237556966d3dea38ac --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/helper/organisations-einheit/organisations-einheit.verifier.ts @@ -0,0 +1,17 @@ +import { OrganisationsEinheitListE2EComponent } from '../../components/organisationseinheiten/organisationseinheiten.e2e.component'; +import { OrganisationsEinheitPage } from '../../page-objects/organisations-einheit.po'; +import { exist, notExist } from '../../support/cypress.util'; +import { convertToDataTestId } from '../../support/tech-util'; + +export class E2EOrganisationsEinheitVerifier { + private readonly organisationsEinheitPage: OrganisationsEinheitPage = new OrganisationsEinheitPage(); + private readonly organisationsEinheitList: OrganisationsEinheitListE2EComponent = this.organisationsEinheitPage.getList(); + + public verifyOrganisationsEinheitInList(name: string): void { + exist(this.organisationsEinheitList.getListItem(convertToDataTestId(name)).getRoot()); + } + + public verifyOrganisationsEinheitNotInList(name: string): void { + notExist(this.organisationsEinheitList.getListItem(convertToDataTestId(name)).getRoot()); + } +} diff --git a/alfa-client/apps/admin-e2e/src/model/organisations-einheit.ts b/alfa-client/apps/admin-e2e/src/model/organisations-einheit.ts new file mode 100644 index 0000000000000000000000000000000000000000..311c1bfd5c4f39e55d5ddf17a4f91fdf34f19067 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/model/organisations-einheit.ts @@ -0,0 +1,7 @@ +export enum OrganisationsEinheitE2E { + BAUAMT = 'Bauamt', + DENKMALPFLEGE = 'Denkmalpflege', + FUNDSTELLE = 'Fundstelle', + ORDNUNGSAMT = 'Ordnungsamt', + WIRTSCHAFTSFOERDERUNG = 'Wirtschaftsförderung', +} diff --git a/alfa-client/apps/admin-e2e/src/model/util.ts b/alfa-client/apps/admin-e2e/src/model/util.ts index a6e185610f221306000e326517a8906ecb1c1f6f..03041ea28be5dae394e7df97d6bf38ae72d2a182 100644 --- a/alfa-client/apps/admin-e2e/src/model/util.ts +++ b/alfa-client/apps/admin-e2e/src/model/util.ts @@ -1,3 +1,5 @@ +import { OrganisationsEinheitE2E } from './organisations-einheit'; + /* * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den * Ministerpräsidenten des Landes Schleswig-Holstein @@ -41,11 +43,11 @@ export enum HttpMethodE2E { export interface AdminUserE2E { vorname: string; nachname: string; - benutzername: string; + username: string; email: string; isAdmin?: boolean; isUser?: boolean; isLoeschen?: boolean; isPoststelle?: boolean; - organisationseinheiten?: string[]; + organisationseinheiten: OrganisationsEinheitE2E[]; } diff --git a/alfa-client/apps/admin-e2e/src/page-objects/header.po.ts b/alfa-client/apps/admin-e2e/src/page-objects/header.po.ts index fb87e566cab2f09fd01b593fa883841fc6075243..60fc24b0d4bf09419e0f0e0b0e15f3b439d2f500 100644 --- a/alfa-client/apps/admin-e2e/src/page-objects/header.po.ts +++ b/alfa-client/apps/admin-e2e/src/page-objects/header.po.ts @@ -24,13 +24,13 @@ import { CurrentUserProfileE2EComponent } from '../components/user-profile/current-user-profile.component.e2e'; import { UserSettingsE2EComponent } from '../components/user-settings/user-settings.component.e2e'; +//TODO Zu den Componenten packen, nicht zu den page-objects export class HeaderE2EComponent { private readonly locatorLogo: string = 'logo-link'; private readonly locatorRoot: string = 'header'; private readonly userSettings: UserSettingsE2EComponent = new UserSettingsE2EComponent(); - private readonly currentUserProfile: CurrentUserProfileE2EComponent = - new CurrentUserProfileE2EComponent(); + private readonly currentUserProfile: CurrentUserProfileE2EComponent = new CurrentUserProfileE2EComponent(); public getRoot() { return cy.getTestElement(this.locatorRoot); diff --git a/alfa-client/apps/admin-e2e/src/page-objects/login.po.ts b/alfa-client/apps/admin-e2e/src/page-objects/login.po.ts index 73005caaf69cb0be2990541fdabe003326a20153..48a03c246ceeeea3b8b7e58c3f211e091c86e955 100644 --- a/alfa-client/apps/admin-e2e/src/page-objects/login.po.ts +++ b/alfa-client/apps/admin-e2e/src/page-objects/login.po.ts @@ -1,3 +1,4 @@ +//TODO Das sollte eher eine Component als eine Page sein export class LoginPage { private readonly locatorLogin: string = '#kc-login'; private readonly locatorBarrierefreiheitLink: string = '#kc-barrierefreiheit'; diff --git a/alfa-client/apps/admin-e2e/src/page-objects/organisations-einheit.po.ts b/alfa-client/apps/admin-e2e/src/page-objects/organisations-einheit.po.ts new file mode 100644 index 0000000000000000000000000000000000000000..b37d9a9d3f0cd1cafc518f2ae995b0988807c7e9 --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/page-objects/organisations-einheit.po.ts @@ -0,0 +1,14 @@ +import { OrganisationsEinheitListE2EComponent } from '../components/organisationseinheiten/organisationseinheiten.e2e.component'; + +export class OrganisationsEinheitPage { + private readonly addButton: string = 'add-organisationseinheit-button'; + private readonly list: OrganisationsEinheitListE2EComponent = new OrganisationsEinheitListE2EComponent(); + + public getAddButton(): Cypress.Chainable<Element> { + return cy.getTestElement(this.addButton); + } + + public getList(): OrganisationsEinheitListE2EComponent { + return this.list; + } +} diff --git a/alfa-client/apps/admin-e2e/src/support/cypress.util.ts b/alfa-client/apps/admin-e2e/src/support/cypress.util.ts index 505762fe8f5193921ee7b5bffb76234f5b21393b..637963609081aa8cddbd24bbf5f56369b80cf0ef 100644 --- a/alfa-client/apps/admin-e2e/src/support/cypress.util.ts +++ b/alfa-client/apps/admin-e2e/src/support/cypress.util.ts @@ -122,13 +122,13 @@ export function enter(element: Cypress.Chainable<Element>): void { element.clear().type(CypressKeyboardActions.ENTER); } -export function enterWith(element: Cypress.Chainable<JQuery<HTMLElement>>, value: string, delayBeforeEnter: number = 200): void { +export function enterWith(element: Cypress.Chainable<Element>, value: string, delayBeforeEnter: number = 200): void { element.clear().type(value); wait(delayBeforeEnter); element.type(CypressKeyboardActions.ENTER); } -export function typeText(element: Cypress.Chainable<JQuery<HTMLElement>>, value: string): void { +export function typeText(element: Cypress.Chainable<Element>, value: string): void { element.type(value); } diff --git a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.scss b/alfa-client/apps/admin-e2e/src/support/linkrels.ts similarity index 92% rename from alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.scss rename to alfa-client/apps/admin-e2e/src/support/linkrels.ts index 54c4f3eb8c92af93694c03cdf577fed23cf9f86b..4891aa61e1bdf2e6bcc77f167e7aa6a7fd7aedb1 100644 --- a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.scss +++ b/alfa-client/apps/admin-e2e/src/support/linkrels.ts @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den * Ministerpräsidenten des Landes Schleswig-Holstein * Staatskanzlei @@ -21,3 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +export enum ApiRootLinkRelE2E { + DOCUMENTATIONS = 'documentations', +} diff --git a/alfa-client/apps/admin-e2e/src/support/tech-util.ts b/alfa-client/apps/admin-e2e/src/support/tech-util.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1bbcb74ae2b53c9fc3fc05bbcab81896c4e183e --- /dev/null +++ b/alfa-client/apps/admin-e2e/src/support/tech-util.ts @@ -0,0 +1,15 @@ +export function convertToDataTestId(value: string): string { + if (value == null || value == undefined) { + return ''; + } + value = replaceAllWhitespaces(value, '_'); + return simpleTransliteration(value); +} + +export function replaceAllWhitespaces(value: string, replaceWith: string): string { + return value.replace(/ /g, replaceWith); +} + +export function simpleTransliteration(value: string) { + return value.normalize('NFKD').replace(/[^-A-Za-z0-9_]/g, ''); +} diff --git a/alfa-client/apps/admin-e2e/src/support/user-util.ts b/alfa-client/apps/admin-e2e/src/support/user-util.ts index 46f07823c27eee9c7872cc8f72a39a33a5326548..d5e7462638087a82b1e4d365e0a59e11932962e5 100644 --- a/alfa-client/apps/admin-e2e/src/support/user-util.ts +++ b/alfa-client/apps/admin-e2e/src/support/user-util.ts @@ -23,7 +23,8 @@ */ import { UserE2E } from '../model/user'; import { HeaderE2EComponent } from '../page-objects/header.po'; -import { MainPage } from '../page-objects/main.po'; +import { MainPage, waitForSpinnerToDisappear } from '../page-objects/main.po'; +import { wait } from './cypress-helper'; export function loginAsAriane(): void { login(UserJsonPath.ARIANE); @@ -43,6 +44,8 @@ export function loginAsSafira(): void { function login(userJson: string): void { cy.fixture(userJson).then((user) => loginByUi(user)); + waitForSpinnerToDisappear(); + wait(3000, 'Wait for initial navigation is done'); } // Hinweis: cacheAcrossSpecs: true lässt Tests umfallen @@ -85,10 +88,11 @@ export enum AlfaRollen { USER = 'VERWALTUNG_USER', LOESCHEN = 'VERWALTUNG_LOESCHEN', POSTSTELLE = 'VERWALTUNG_POSTSTELLE', - ADMIN = 'ADMIN_ADMIN' + ADMIN = 'ADMIN_ADMIN', } export enum AlfaUsers { + ARAINE = 'ariane', DOROTHEA = 'dorothea', LUDWIG = 'ludwig', PETER = 'peter', diff --git a/alfa-client/apps/admin/src/app/app.component.html b/alfa-client/apps/admin/src/app/app.component.html index 8302d53018783e91ffb01539a627b9e5ee7730ff..55e1c8824521c5c846932ee5d648172d86466705 100644 --- a/alfa-client/apps/admin/src/app/app.component.html +++ b/alfa-client/apps/admin/src/app/app.component.html @@ -36,7 +36,10 @@ > <ods-admin-logo-icon /> </a> - <user-profile-button-container data-test-id="user-profile-button"></user-profile-button-container> + <user-profile-button-container + [apiRootStateResource]="apiRootStateResource$ | async" + data-test-id="user-profile-button" + ></user-profile-button-container> </header> <div class="flex h-screen w-full justify-center overflow-y-auto"> <ods-navbar data-test-id="navigation"> diff --git a/alfa-client/apps/admin/src/app/app.component.spec.ts b/alfa-client/apps/admin/src/app/app.component.spec.ts index 34bd937efea886d40f3dc6f77df5eb1b3d68bce5..b0a354caa1b666ad9ad15a4527e77f8a632496c6 100644 --- a/alfa-client/apps/admin/src/app/app.component.spec.ts +++ b/alfa-client/apps/admin/src/app/app.component.spec.ts @@ -27,31 +27,18 @@ import { KeycloakTokenService } from '@admin/keycloak-shared'; import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared'; import { BuildInfoComponent } from '@alfa-client/common'; import { createEmptyStateResource, createStateResource, HasLinkPipe } from '@alfa-client/tech-shared'; -import { - existsAsHtmlElement, - getElementComponentFromFixtureByCss, - Mock, - mock, - notExistsAsHtmlElement, -} from '@alfa-client/test-utils'; +import { existsAsHtmlElement, getElementComponentFromFixtureByCss, Mock, mock, notExistsAsHtmlElement, } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { AuthenticationService } from '@authentication'; -import { - AdminLogoIconComponent, - MailboxIconComponent, - NavbarComponent, - NavItemComponent, - OrgaUnitIconComponent, - UsersIconComponent, -} from '@ods/system'; +import { AdminLogoIconComponent, MailboxIconComponent, NavbarComponent, NavItemComponent, OrgaUnitIconComponent, UsersIconComponent, } from '@ods/system'; import { createConfigurationResource } from 'libs/admin/configuration-shared/test/configuration'; import { MenuContainerComponent } from 'libs/admin/configuration/src/lib/menu-container/menu-container.component'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent, MockDirective } from 'ng-mocks'; import { of, Subscription } from 'rxjs'; -import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component'; +import { UserProfileButtonContainerComponent } from '../../../../libs/admin/user-profile/src/lib/user-menu/user-profile.button-container.component'; import { UnavailablePageComponent } from '../pages/unavailable/unavailable-page/unavailable-page.component'; import { AppComponent } from './app.component'; diff --git a/alfa-client/apps/admin/src/app/app.component.ts b/alfa-client/apps/admin/src/app/app.component.ts index 6de5c0ab4da5fed7ffc763b86cdb18e253c44528..bb5f2013fde365c95ae01525f1b8cb170decddd6 100644 --- a/alfa-client/apps/admin/src/app/app.component.ts +++ b/alfa-client/apps/admin/src/app/app.component.ts @@ -41,7 +41,7 @@ import { UsersIconComponent, } from '@ods/system'; import { filter, Observable, Subscription } from 'rxjs'; -import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component'; +import { UserProfileButtonContainerComponent } from '../../../../libs/admin/user-profile/src/lib/user-menu/user-profile.button-container.component'; import { UnavailablePageComponent } from '../pages/unavailable/unavailable-page/unavailable-page.component'; @Component({ diff --git a/alfa-client/apps/admin/src/common/user-profile-button-container/user-profile-button-container.component.spec.ts b/alfa-client/apps/admin/src/common/user-profile-button-container/user-profile-button-container.component.spec.ts deleted file mode 100644 index b40a661442d6728f6c7819a0548e556d615197a8..0000000000000000000000000000000000000000 --- a/alfa-client/apps/admin/src/common/user-profile-button-container/user-profile-button-container.component.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { dispatchEventFromFixture, getElementFromFixture, mock, Mock } from '@alfa-client/test-utils'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { DropdownMenuButtonItemComponent, DropdownMenuComponent, LogoutIconComponent } from '@ods/system'; -import { AuthenticationService } from '@authentication'; -import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { MockComponent } from 'ng-mocks'; -import { UserProfileButtonContainerComponent } from './user-profile.button-container.component'; - -describe('UserProfileButtonContainerComponent', () => { - let component: UserProfileButtonContainerComponent; - let fixture: ComponentFixture<UserProfileButtonContainerComponent>; - - const authenticationService: Mock<AuthenticationService> = mock(AuthenticationService); - - const popupButtonContent: string = getDataTestIdOf('popup-button-content'); - const popupLogoutButton: string = getDataTestIdOf('popup-logout-button'); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [UserProfileButtonContainerComponent], - imports: [ - RouterTestingModule, - MockComponent(DropdownMenuComponent), - MockComponent(DropdownMenuButtonItemComponent), - MockComponent(LogoutIconComponent), - ], - providers: [ - { - provide: AuthenticationService, - useValue: authenticationService, - }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(UserProfileButtonContainerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('ngOnInit', () => { - it('should call authService to get current user initials', () => { - component.ngOnInit(); - - expect(authenticationService.getCurrentUserInitials).toHaveBeenCalled(); - }); - }); - - describe('popup button', () => { - it('should show initials', () => { - component.currentUserInitials = 'AV'; - fixture.detectChanges(); - - const popupButtonContentElement: HTMLElement = getElementFromFixture(fixture, popupButtonContent); - - expect(popupButtonContentElement.textContent.trim()).toEqual('AV'); - }); - }); - - describe('logout', () => { - it('should call authService logout', () => { - dispatchEventFromFixture(fixture, popupLogoutButton, 'itemClicked'); - - expect(authenticationService.logout).toHaveBeenCalled(); - }); - }); -}); diff --git a/alfa-client/apps/alfa-e2e/src/components/user-assistance/help-menu.component.e2e.ts b/alfa-client/apps/alfa-e2e/src/components/user-assistance/help-menu.component.e2e.ts index 6900b4f81bba5213341add0864bec162856037fb..9bdd8978dbaafbf31aec2301c0a66348be778a6d 100644 --- a/alfa-client/apps/alfa-e2e/src/components/user-assistance/help-menu.component.e2e.ts +++ b/alfa-client/apps/alfa-e2e/src/components/user-assistance/help-menu.component.e2e.ts @@ -1,4 +1,4 @@ -import { shouldHaveAttribute } from "../../support/cypress.util"; +import { shouldHaveAttribute } from '../../support/cypress.util'; /* * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den @@ -26,8 +26,8 @@ import { shouldHaveAttribute } from "../../support/cypress.util"; export class HelpMenuE2EComponent { private readonly root: string = 'help-menu'; private readonly button: string = 'help-menu-button'; - private readonly dropdownButton: string ='dropdown-button'; - private readonly openDocumentationButton: string = 'open-documentation-button'; + private readonly dropdownButton: string = 'dropdown-button'; + private readonly documentation: string = 'documentation'; private readonly openImpressumButton: string = 'impressum'; public getRoot() { @@ -42,15 +42,15 @@ export class HelpMenuE2EComponent { return this.getRoot().getTestElement(this.dropdownButton); } - public getOpenDocumentationButton() { - return this.getRoot().getTestElementWithOid(this.openDocumentationButton); + public getDocumentation() { + return this.getRoot().getTestElementWithOid(this.documentation); } public getImpressumButton(): Cypress.Chainable<Element> { - return cy.getTestElement(this.openImpressumButton) + return cy.getTestElement(this.openImpressumButton); } public impressumLinkIs(link: string): void { - shouldHaveAttribute(this.getImpressumButton().find('a'),'href', link) + shouldHaveAttribute(this.getImpressumButton().find('a'), 'href', link); } } diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/user-assistance/help-menu.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/user-assistance/help-menu.cy.ts index 4cc001a7f49f79190e8e1f53ef1242c36378d238..f2b5373adae2011b3bd3221041f783d5c8dd9bac 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/user-assistance/help-menu.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/user-assistance/help-menu.cy.ts @@ -25,7 +25,7 @@ import { HelpMenuE2EComponent } from 'apps/alfa-e2e/src/components/user-assistan import { HeaderE2EComponent } from 'apps/alfa-e2e/src/page-objects/header.po'; import { MainPage, waitForSpinnerToDisappear } from 'apps/alfa-e2e/src/page-objects/main.po'; import { dropCollections } from 'apps/alfa-e2e/src/support/cypress-helper'; -import { contains, exist, shouldHaveAttribute } from 'apps/alfa-e2e/src/support/cypress.util'; +import { exist } from 'apps/alfa-e2e/src/support/cypress.util'; import { loginAsSabine } from 'apps/alfa-e2e/src/support/user-util'; describe('Help Menu', () => { @@ -33,7 +33,7 @@ describe('Help Menu', () => { const header: HeaderE2EComponent = mainPage.getHeader(); const helpMenu: HelpMenuE2EComponent = header.getHelpMenu(); - const impressumLink: string = 'https://static.dev.sh.ozg-cloud.de/impressum' + const impressumLink: string = 'https://static.dev.sh.ozg-cloud.de/impressum'; before(() => { loginAsSabine(); @@ -50,7 +50,7 @@ describe('Help Menu', () => { it('should show "open documentation"', () => { helpMenu.getRoot().click(); - exist(helpMenu.getOpenDocumentationButton()); + exist(helpMenu.getDocumentation()); }); it('should show Impressum button and find link', () => { @@ -59,13 +59,7 @@ describe('Help Menu', () => { }); it('should open documentation', () => { - helpMenu - .getOpenDocumentationButton() - .find('a') - .invoke('removeAttr', 'target') - .click() - .url() - .should('include', 'benutzerleitfaden'); + helpMenu.getDocumentation().find('a').invoke('removeAttr', 'target').click().url().should('include', 'benutzerleitfaden'); }); }); }); diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.spec.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.spec.ts index ea3ca2d74ffe1eb2b6fb903a300d5b1b7b36e455..5a91a0813ffbe8dec5a89c9f526382168481bdcd 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.spec.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.spec.ts @@ -49,15 +49,16 @@ describe('KeycloakResourceService', () => { }); describe('getAll', () => { + const stateResource: StateResource<unknown> = createStateResource([]); + beforeEach(() => { - service.handleChanges = jest.fn(); + service.handleChanges = jest.fn().mockReturnValue(singleCold(stateResource)); }); - it('should return stateResource as observable', (done) => { - service.getAll().subscribe((stateResource) => { - expect(stateResource).toBe(service.stateResource.value); - done(); - }); + it('should return stateResource', () => { + const stateResource$: Observable<StateResource<unknown[]>> = service.getAll(); + + expect(stateResource$).toBeObservable(singleCold(stateResource)); }); it('should call handleChanges ', fakeAsync(() => { @@ -68,12 +69,25 @@ describe('KeycloakResourceService', () => { }); describe('handleChanges', () => { - it('should call doIfLoadingRequired', () => { - const doIfLoadingRequired: jest.SpyInstance<boolean> = jest.spyOn(ResourceUtil, 'doIfLoadingRequired'); + let doIfLoadingRequiredSpy: jest.SpyInstance<boolean>; + + beforeEach(() => { + doIfLoadingRequiredSpy = jest.spyOn(ResourceUtil, 'doIfLoadingRequired').mockImplementation(); + }); + it('should call doIfLoadingRequired', () => { service.handleChanges(emptyStateResource); - expect(doIfLoadingRequired).toHaveBeenCalled(); + expect(doIfLoadingRequiredSpy).toHaveBeenCalled(); + }); + + it('should return stateResource', (done) => { + service.stateResource.next(createStateResource([])); + + service.handleChanges(emptyStateResource).subscribe((stateResource: StateResource<[]>) => { + expect(stateResource).toEqual(createStateResource([])); + done(); + }); }); }); diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.ts index 572761923b93f3d3a21719659416b268fb17076f..ae74c73ee2c59c989a0db77dbaa1b881c4061448 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak.resource.service.ts @@ -22,22 +22,20 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { createEmptyStateResource, createStateResource, doIfLoadingRequired, StateResource } from '@alfa-client/tech-shared'; -import { BehaviorSubject, first, map, Observable, startWith, tap } from 'rxjs'; +import { BehaviorSubject, first, map, Observable, startWith, switchMap, tap } from 'rxjs'; export abstract class KeycloakResourceService<T> { - readonly stateResource: BehaviorSubject<StateResource<T[]>> = new BehaviorSubject({ - ...createStateResource<T[]>([]), - loaded: false, - }); + readonly stateResource: BehaviorSubject<StateResource<T[]>> = new BehaviorSubject(createEmptyStateResource()); public getAll(): Observable<StateResource<T[]>> { return this.stateResource .asObservable() - .pipe(tap((stateResource: StateResource<T[]>): void => this.handleChanges(stateResource))); + .pipe(switchMap((stateResource: StateResource<T[]>) => this.handleChanges(stateResource))); } - handleChanges(stateResource: StateResource<T[]>): void { + handleChanges(stateResource: StateResource<T[]>): Observable<StateResource<T[]>> { doIfLoadingRequired(stateResource, (): void => this.loadResource()); + return this.stateResource.asObservable(); } loadResource(): void { @@ -65,7 +63,7 @@ export abstract class KeycloakResourceService<T> { protected abstract _saveInKeycloak(item: T): Observable<T>; - public delete(id: string): Observable<StateResource<unknown>> { + public delete(id: string): Observable<StateResource<void>> { return this.handleLoading(this._deleteInKeycloak(id)); } diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts index 43bc3d38565e870585b2ba846b8c2d681d977014..e86fbad2ab7cc0d9716dc13409990320dde904d7 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts @@ -1,6 +1,7 @@ import { AdminOrganisationsEinheit } from '@admin-client/organisations-einheit-shared'; import { mock, Mock } from '@alfa-client/test-utils'; import { TestBed } from '@angular/core/testing'; +import { faker } from '@faker-js/faker'; import KcAdminClient from '@keycloak/keycloak-admin-client'; import GroupRepresentation from '@keycloak/keycloak-admin-client/lib/defs/groupRepresentation'; import { createGroupRep } from '../../../organisations-einheit-shared/src/test/organisations-einheit'; @@ -85,4 +86,27 @@ describe('AdminOrganisationsEinheitRepository', () => { }); }); }); + + describe('delete', () => { + const organisationsEinheitId: string = faker.string.uuid(); + + beforeEach(() => { + kcAdminClient.groups = <any>{ + del: jest.fn().mockReturnValue(Promise.resolve()), + }; + }); + + it('should call kcAdminClient groups del', () => { + repository.delete(organisationsEinheitId); + + expect(kcAdminClient.groups['del']).toHaveBeenCalledWith({ id: organisationsEinheitId }); + }); + + it('should return void', (done) => { + repository.delete(organisationsEinheitId).subscribe((result: void) => { + expect(result).toBeUndefined(); + done(); + }); + }); + }); }); diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts index c2d454434e07562492f0c929bc710d7793a70403..abc3737d301f3f82b603a6bac0e5b85cd4e725ca 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts @@ -43,4 +43,8 @@ export class AdminOrganisationsEinheitRepository { attributes: group.attributes, }; } + + public delete(organisationseinheitId: string): Observable<void> { + return from(this.kcAdminClient.groups.del({ id: organisationseinheitId })); + } } diff --git a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts index 3a93c42e6b0042f02dd9f509969444431ce6c374..5fac205bca0b5ebb2b5b025ad8450da89cfd8793 100644 --- a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts +++ b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts @@ -26,3 +26,8 @@ export interface AdminOrganisationsEinheit { name: string; attributes: { [key: string]: string[] }; } + +export interface OrganisationsEinheitDeleteDialogData { + organisationsEinheitName: string; + organisationsEinheitId: string; +} diff --git a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts index db8b44f6aad3242e172a407fa6731ce992a60fcc..12b297f16e2cebbc8e774df56fe5d878fdcd6adb 100644 --- a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts +++ b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.spec.ts @@ -23,8 +23,12 @@ */ import { AdminOrganisationsEinheit, AdminOrganisationsEinheitService } from '@admin-client/organisations-einheit-shared'; import { AdminOrganisationsEinheitRepository } from '@admin/keycloak-shared'; +import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { TestBed } from '@angular/core/testing'; +import { faker } from '@faker-js/faker'; +import { of } from 'rxjs'; +import { singleColdCompleted } from '../../../../tech-shared/test/marbles'; import { createAdminOrganisationsEinheit } from '../test/organisations-einheit'; describe('AdminOrganisationsEinheitService', () => { @@ -55,4 +59,23 @@ describe('AdminOrganisationsEinheitService', () => { expect(repository.create).toHaveBeenCalledWith(organisationsEinheit); }); }); + + describe('deleteInKeycloak', () => { + const organisationsEinheitId: string = faker.string.uuid(); + + it('should call repository delete', () => { + service._deleteInKeycloak(organisationsEinheitId); + + expect(repository.delete).toHaveBeenCalledWith(organisationsEinheitId); + }); + + it('should return result', () => { + const state: StateResource<unknown> = createEmptyStateResource(); + repository.delete.mockReturnValue(of(state)); + + const result = service._deleteInKeycloak(organisationsEinheitId); + + expect(result).toBeObservable(singleColdCompleted(state)); + }); + }); }); diff --git a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.ts b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.ts index 5c12d0613ef05cc74acb0ca4ee61794c61c5e95c..35f7580e9c46cd6262c7a68973d324e32022dcdc 100644 --- a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.ts +++ b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.service.ts @@ -45,6 +45,6 @@ export class AdminOrganisationsEinheitService extends KeycloakResourceService<Ad } _deleteInKeycloak(id: string): Observable<void> { - throw new Error('Method not implemented.'); + return this.repository.delete(id); } } diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.html b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..21de75aa26f03bde92ecc76d8a80a6e901565f7a --- /dev/null +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.html @@ -0,0 +1,7 @@ +<admin-organisations-einheit-delete-dialog + [organisationsEinheitName]="organisationsEinheitName" + [deleteStateResource]="deleteStateResource$ | async" + (cancel)="closeDialog()" + (delete)="delete()" + data-test-id="organisations-einheit-delete-dialog" +/> diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.spec.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..fad1b24d54d5e650b57182de2ce9f7751dddecb6 --- /dev/null +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.spec.ts @@ -0,0 +1,145 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdminOrganisationsEinheitService } from '@admin-client/organisations-einheit-shared'; +import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixture, getMockComponent, Mock, mock } from '@alfa-client/test-utils'; +import { OzgcloudDialogService } from '@alfa-client/ui'; +import { DIALOG_DATA } from '@angular/cdk/dialog'; +import { faker } from '@faker-js/faker'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { singleColdCompleted } from 'libs/tech-shared/test/marbles'; +import { Observable, of } from 'rxjs'; +import { OrganisationsEinheitDeleteDialogContainerComponent } from './organisations-einheit-delete-dialog-container.component'; +import { OrganisationsEinheitDeleteDialogComponent } from './organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component'; + +describe('OrganisationsEinheitDeleteDialogContainerComponent', () => { + let component: OrganisationsEinheitDeleteDialogContainerComponent; + let fixture: ComponentFixture<OrganisationsEinheitDeleteDialogContainerComponent>; + + let dialogService: Mock<OzgcloudDialogService>; + let organisationsEinheitService: Mock<AdminOrganisationsEinheitService>; + + const dialogData = { organisationsEinheitName: faker.word.sample(), organisationsEinheitId: faker.string.uuid() }; + + const organisationsEinheitDeleteDialog: string = getDataTestIdOf('organisations-einheit-delete-dialog'); + + beforeEach(() => { + dialogService = mock(OzgcloudDialogService); + organisationsEinheitService = { ...mock(AdminOrganisationsEinheitService), delete: jest.fn() }; + + TestBed.configureTestingModule({ + imports: [OrganisationsEinheitDeleteDialogContainerComponent], + providers: [ + { provide: OzgcloudDialogService, useValue: dialogService }, + { provide: AdminOrganisationsEinheitService, useValue: organisationsEinheitService }, + { + provide: DIALOG_DATA, + useValue: dialogData, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(OrganisationsEinheitDeleteDialogContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('organisations einheit delete dialog', () => { + it('should exist', () => { + const stateResource: StateResource<void> = createEmptyStateResource(); + component.deleteStateResource$ = of(stateResource); + component.organisationsEinheitName = dialogData.organisationsEinheitName; + + const deleteDialogComponent: OrganisationsEinheitDeleteDialogComponent = getMockComponent( + fixture, + OrganisationsEinheitDeleteDialogComponent, + ); + + expect(deleteDialogComponent.organisationsEinheitName).toBe(component.organisationsEinheitName); + expect(deleteDialogComponent.deleteStateResource).toEqual(stateResource); + }); + + it('should call closeDialog on cancel emit', () => { + component.closeDialog = jest.fn(); + + dispatchEventFromFixture(fixture, organisationsEinheitDeleteDialog, 'cancel'); + + expect(component.closeDialog).toHaveBeenCalled(); + }); + + it('should call delete on delete emit', () => { + component.delete = jest.fn(); + + dispatchEventFromFixture(fixture, organisationsEinheitDeleteDialog, 'delete'); + + expect(component.delete).toHaveBeenCalled(); + }); + }); + }); + + describe('component', () => { + describe('constructor', () => { + it('should set organisationsEinheitName', () => { + expect(component.organisationsEinheitName).toBe(dialogData.organisationsEinheitName); + }); + + it('should set organisationsEinheitId', () => { + expect(component.organisationsEinheitId).toBe(dialogData.organisationsEinheitId); + }); + }); + + describe('closeDialog', () => { + it('should call dialogService closeAll', () => { + dialogService.closeAll = jest.fn(); + + component.closeDialog(); + + expect(dialogService.closeAll).toHaveBeenCalled(); + }); + }); + + describe('delete', () => { + const stateResource: StateResource<void> = createEmptyStateResource(); + const stateResource$: Observable<StateResource<void>> = of(stateResource); + const loadingStateResource: StateResource<void> = createEmptyStateResource(true); + const loadingStateResource$: Observable<StateResource<void>> = of(loadingStateResource); + + beforeEach(() => { + organisationsEinheitService.delete.mockReturnValue(stateResource$); + }); + + it('should call organisationsEinheitService delete', () => { + component.delete(); + + expect(organisationsEinheitService.delete).toHaveBeenCalledWith(dialogData.organisationsEinheitId); + }); + + it('should set deleteStateResource$', () => { + component.delete(); + + expect(component.deleteStateResource$).toBeObservable(singleColdCompleted(stateResource)); + }); + + it('should close dialog on delete done', () => { + component.delete(); + component.deleteStateResource$.subscribe(); + + expect(dialogService.closeAll).toHaveBeenCalled(); + }); + + it('should NOT close dialog on delete loading', () => { + organisationsEinheitService.delete.mockReturnValue(loadingStateResource$); + + component.delete(); + component.deleteStateResource$.subscribe(); + + expect(dialogService.closeAll).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..01ce6b8eac5000f08ba18da2a7d6480eccafb109 --- /dev/null +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component.ts @@ -0,0 +1,49 @@ +import { + AdminOrganisationsEinheitService, + OrganisationsEinheitDeleteDialogData, +} from '@admin-client/organisations-einheit-shared'; +import { createEmptyStateResource, isNotLoading, StateResource } from '@alfa-client/tech-shared'; +import { OzgcloudDialogService } from '@alfa-client/ui'; +import { DIALOG_DATA } from '@angular/cdk/dialog'; +import { AsyncPipe } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { Observable, of, tap } from 'rxjs'; +import { OrganisationsEinheitDeleteDialogComponent } from './organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component'; + +@Component({ + selector: 'admin-organisations-einheit-delete-dialog-container', + standalone: true, + imports: [AsyncPipe, OrganisationsEinheitDeleteDialogComponent], + templateUrl: './organisations-einheit-delete-dialog-container.component.html', +}) +export class OrganisationsEinheitDeleteDialogContainerComponent { + private readonly dialogService = inject(OzgcloudDialogService); + private readonly dialogData: OrganisationsEinheitDeleteDialogData = inject(DIALOG_DATA); + private readonly organisationsEinheitService = inject(AdminOrganisationsEinheitService); + + public organisationsEinheitName: string; + public organisationsEinheitId: string; + + constructor() { + this.organisationsEinheitName = this.dialogData.organisationsEinheitName; + this.organisationsEinheitId = this.dialogData.organisationsEinheitId; + } + + public deleteStateResource$: Observable<StateResource<void>> = of(createEmptyStateResource<void>()); + + public closeDialog(): void { + this.dialogService.closeAll(); + } + + public delete(): void { + this.deleteStateResource$ = this.organisationsEinheitService + .delete(this.organisationsEinheitId) + .pipe(tap((stateResource: StateResource<void>) => this.closeDialogOnDeleteDone(stateResource))); + } + + private closeDialogOnDeleteDone(stateResource: StateResource<void>): void { + if (isNotLoading(stateResource)) { + this.dialogService.closeAll(); + } + } +} diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.html b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..1c75a946f561155e6bd95526ffeb58eb4b6cc4c1 --- /dev/null +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.html @@ -0,0 +1,28 @@ +<div class="block flex max-w-xl flex-col gap-4 bg-background-100 p-8"> + <h1 class="text-xl">Organisationseinheit</h1> + + <h2 class="text-lg">{{ organisationsEinheitName }}</h2> + + <p> + <span class="font-bold">Achtung:</span> Durch das Entfernen der Organisationseinheit aus dieser Liste wird die + Organisationseinheit auch für alle Benutzer entfernt. + </p> + + <div class="flex justify-between"> + <ods-button + (clickEmitter)="cancel.emit()" + variant="outline" + text="Abbrechen" + dataTestId="dialog-cancel-button" + data-test-id="dialog-cancel-ods-button" + /> + + <ods-button-with-spinner + [stateResource]="deleteStateResource" + (clickEmitter)="delete.emit()" + text="Löschen" + dataTestId="dialog-delete-button" + data-test-id="dialog-delete-ods-button" + /> + </div> +</div> diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.spec.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..5099b902cc490b643dd94764f282a66e9b45aead --- /dev/null +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.spec.ts @@ -0,0 +1,79 @@ +import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { + dispatchEventFromFixture, + existsAsHtmlElement, + getElementComponentFromFixtureByCss, + MockEvent, +} from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { ButtonComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; +import { getDataTestIdOf } from '../../../../../../../../tech-shared/test/data-test'; +import { OrganisationsEinheitDeleteDialogComponent } from './organisations-einheit-delete-dialog.component'; + +describe('OrganisationsEinheitDeleteDialogComponent', () => { + let component: OrganisationsEinheitDeleteDialogComponent; + let fixture: ComponentFixture<OrganisationsEinheitDeleteDialogComponent>; + + const deleteButton: string = getDataTestIdOf('dialog-delete-ods-button'); + const cancelButton: string = getDataTestIdOf('dialog-cancel-ods-button'); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + OrganisationsEinheitDeleteDialogComponent, + MockComponent(ButtonWithSpinnerComponent), + MockComponent(ButtonComponent), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(OrganisationsEinheitDeleteDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('cancel button', () => { + it('should exist', () => { + existsAsHtmlElement(fixture, cancelButton); + }); + + it('should emit cancel on click', () => { + component.cancel.emit = jest.fn(); + + dispatchEventFromFixture(fixture, cancelButton, MockEvent.CLICK); + + expect(component.cancel.emit).toHaveBeenCalled(); + }); + }); + + describe('delete button', () => { + it('should exist', () => { + existsAsHtmlElement(fixture, deleteButton); + }); + + it('should have inputs', () => { + const stateResource: StateResource<void> = createEmptyStateResource(); + component.deleteStateResource = stateResource; + + fixture.detectChanges(); + + const button: ButtonWithSpinnerComponent = getElementComponentFromFixtureByCss(fixture, deleteButton); + expect(button.stateResource).toBe(stateResource); + }); + + it('should emit delete on click', () => { + component.delete.emit = jest.fn(); + + dispatchEventFromFixture(fixture, deleteButton, MockEvent.CLICK); + + expect(component.delete.emit).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..bf433fea87b9bd41af8e4e3e3133b46540b40f30 --- /dev/null +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog/organisations-einheit-delete-dialog.component.ts @@ -0,0 +1,18 @@ +import { StateResource } from '@alfa-client/tech-shared'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { ButtonComponent } from '@ods/system'; + +@Component({ + selector: 'admin-organisations-einheit-delete-dialog', + standalone: true, + templateUrl: './organisations-einheit-delete-dialog.component.html', + imports: [ButtonWithSpinnerComponent, ButtonComponent], +}) +export class OrganisationsEinheitDeleteDialogComponent { + @Input() organisationsEinheitName: string; + @Input() deleteStateResource: StateResource<void>; + + @Output() cancel: EventEmitter<void> = new EventEmitter<void>(); + @Output() delete: EventEmitter<void> = new EventEmitter<void>(); +} diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.html b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.html index fc5ff5f37637c6c1cf2b9aa687ef25b0678b41f3..a64e70976ffe314813d4a5cea38c9915905dc7ca 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.html +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.html @@ -26,10 +26,22 @@ <ods-list data-test-id="organisations-einheit-list"> @for (organisationsEinheit of organisationsEinheitList; track $index) { <ods-list-item [attr.data-test-id]="(organisationsEinheit.name | convertForDataTest) + '-organisation-item'"> - <dl class="flex-1 basis-3/4 font-semibold"> - <dt class="sr-only">Name</dt> - <dd data-test-id="organisations-einheit-name">{{ organisationsEinheit.name }}</dd> - </dl> + <div class="space-between flex w-full items-center"> + <dl class="flex-1 basis-3/4 font-semibold"> + <dt class="sr-only">Name</dt> + <dd data-test-id="organisations-einheit-name">{{ organisationsEinheit.name }}</dd> + </dl> + <ods-open-dialog-button + [tooltip]="'Organisationseinheit löschen'" + variant="ghost" + size="fit" + dataTestClass="delete-button" + data-test-id="delete-organisations-einheit-dialog-button" + [dialogData]="{ organisationsEinheitName: organisationsEinheit.name, organisationsEinheitId: organisationsEinheit.id }" + > + <ods-delete-icon icon /> + </ods-open-dialog-button> + </div> </ods-list-item> } </ods-list> diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.spec.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.spec.ts index dd2fba6125d358ef26de554cdf79d2afdc40cf9b..c016c3c9c89a5e1767b684775068803afaf5280d 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.spec.ts +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.spec.ts @@ -23,10 +23,11 @@ */ import { AdminOrganisationsEinheit } from '@admin-client/organisations-einheit-shared'; import { ConvertForDataTestPipe } from '@alfa-client/tech-shared'; -import { existsAsHtmlElement, getElementFromFixture, mock } from '@alfa-client/test-utils'; +import { existsAsHtmlElement, getElementFromFixture, getElementFromFixtureByType, mock } from '@alfa-client/test-utils'; import { CommonModule } from '@angular/common'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; +import { OpenDialogButtonComponent } from '@ods/component'; import { ExclamationIconComponent, ListComponent, ListItemComponent } from '@ods/system'; import { getConvertedDataTestIdOf, getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; @@ -39,6 +40,7 @@ describe('OrganisationsEinheitListComponent', () => { const listSelector: string = getDataTestIdOf('organisations-einheit-list'); const listItemSuffux: string = '-organisation-item'; + const deleteButtonTestId: string = getDataTestIdOf('delete-organisations-einheit-dialog-button'); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -48,7 +50,7 @@ describe('OrganisationsEinheitListComponent', () => { useValue: mock(ActivatedRoute), }, ], - imports: [CommonModule, ConvertForDataTestPipe], + imports: [CommonModule, ConvertForDataTestPipe, MockComponent(OpenDialogButtonComponent)], declarations: [ OrganisationsEinheitListComponent, MockComponent(ListComponent), @@ -92,5 +94,27 @@ describe('OrganisationsEinheitListComponent', () => { }); }); }); + + describe('open delete dialog button', () => { + const organisationsEinheit: AdminOrganisationsEinheit = createAdminOrganisationsEinheit(); + + it('should exist', () => { + component.organisationsEinheitList = [organisationsEinheit]; + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, deleteButtonTestId); + }); + + it('should have inputs', () => { + component.organisationsEinheitList = [organisationsEinheit]; + + fixture.detectChanges(); + const dialog: OpenDialogButtonComponent = getElementFromFixtureByType(fixture, OpenDialogButtonComponent); + + expect(dialog.dialogData.organisationsEinheitName).toBe(organisationsEinheit.name); + expect(dialog.dialogData.organisationsEinheitId).toBe(organisationsEinheit.id); + }); + }); }); }); diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.ts index 7efc0bbbe6727d1d558c63bad8ffe6a2d08b9d35..9fabaed44ebd2111a45a4812193ca6fa774202c9 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.ts +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-list/organisations-einheit-list.component.ts @@ -22,23 +22,27 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { AdminOrganisationsEinheit } from '@admin-client/organisations-einheit-shared'; -import { ConvertForDataTestPipe, ToResourceUriPipe } from '@alfa-client/tech-shared'; +import { ConvertForDataTestPipe } from '@alfa-client/tech-shared'; +import { DIALOG_COMPONENT } from '@alfa-client/ui'; import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { ExclamationIconComponent, ListComponent, ListItemComponent, TooltipDirective } from '@ods/system'; +import { OpenDialogButtonComponent } from '@ods/component'; +import { DeleteIconComponent, ListComponent, ListItemComponent, TooltipDirective } from '@ods/system'; +import { OrganisationsEinheitDeleteDialogContainerComponent } from './organisations-einheit-delete-dialog-container/organisations-einheit-delete-dialog-container.component'; @Component({ selector: 'admin-organisations-einheit-list', templateUrl: './organisations-einheit-list.component.html', standalone: true, + providers: [{ provide: DIALOG_COMPONENT, useValue: OrganisationsEinheitDeleteDialogContainerComponent }], imports: [ CommonModule, ListComponent, ListItemComponent, - ExclamationIconComponent, - TooltipDirective, - ToResourceUriPipe, ConvertForDataTestPipe, + OpenDialogButtonComponent, + TooltipDirective, + DeleteIconComponent, ], }) export class OrganisationsEinheitListComponent { diff --git a/alfa-client/libs/admin/shared/src/lib/admin-delete-open-dialog-button/admin-delete-open-dialog-button.component.html b/alfa-client/libs/admin/shared/src/lib/admin-delete-open-dialog-button/admin-delete-open-dialog-button.component.html index 3577b2352dcfb4450a36e0726ae4d47b34d80ed4..c006d95c841f7f9686e98a762901b99511a7e101 100644 --- a/alfa-client/libs/admin/shared/src/lib/admin-delete-open-dialog-button/admin-delete-open-dialog-button.component.html +++ b/alfa-client/libs/admin/shared/src/lib/admin-delete-open-dialog-button/admin-delete-open-dialog-button.component.html @@ -1,3 +1,3 @@ -<ods-open-dialog-button variant='outline_error' label="Löschen" dataTestId="delete-button" > +<ods-open-dialog-button variant="outline_error" label="Löschen" dataTestId="delete-button"> <ods-delete-icon icon /> -</ods-open-dialog-button> \ No newline at end of file +</ods-open-dialog-button> diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html index 8bd6a5f92932e22006b3b8e2cb353a9c3fa93eb1..ccd9cc7ea4c19f667ba950072f96795375c8cbd9 100644 --- a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html @@ -2,5 +2,6 @@ (clickEmitter)="submit()" text="Speichern" dataTestId="save-button" + data-test-id="save" [stateResource]="stateResource$ | async" /> diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts index 52a303f378e824677dd6518126ae838ebd416e3a..baee873bfd7c0db07a9295de75844d638edaead5 100644 --- a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts @@ -4,7 +4,7 @@ import { dispatchEventFromFixture, getMockComponent, Mock, MockEvent } from '@al import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Resource } from '@ngxp/rest'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { singleCold } from 'libs/tech-shared/test/marbles'; import { createDummyResource } from 'libs/tech-shared/test/resource'; import { MockComponent } from 'ng-mocks'; @@ -17,7 +17,7 @@ describe('AdminSaveButtonComponent', () => { let formService: Mock<AbstractFormService<Resource>>; - const saveButton: string = getDataTestIdAttributeOf('save-button'); + const saveButton: string = getDataTestIdOf('save'); const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); 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 a2bc7e4bfe5efdec1af21b9ad1da7deaccf1a21f..3abca44e4a1aa07b3bceb119a0caf010014c02ed 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,11 @@ */ import { AggregationMappingListResource, AggregationMappingService } from '@admin-client/reporting-shared'; import { createStateResource, StateResource } from '@alfa-client/tech-shared'; -import { existsAsHtmlElement, mock, Mock } from '@alfa-client/test-utils'; +import { mock, Mock } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RoutingButtonComponent } from '@ods/component'; import { singleCold } from 'libs/tech-shared/test/marbles'; import { MockComponent } from 'ng-mocks'; -import { getDataTestIdAttributeOf } from '../../../../../tech-shared/test/data-test'; import { createAggregationMappingListResource } from '../../../../reporting-shared/test/aggregation-mapping'; import { StatistikContainerComponent } from './statistik-container.component'; @@ -36,8 +35,6 @@ describe('StatistikContainerComponent', () => { let component: StatistikContainerComponent; let fixture: ComponentFixture<StatistikContainerComponent>; - const evaluateAdditionalFieldsTestId: string = getDataTestIdAttributeOf('weitere-felder-auswerten-button'); - let aggregationMappingService: Mock<AggregationMappingService>; beforeEach(async () => { @@ -68,16 +65,6 @@ describe('StatistikContainerComponent', () => { expect(component).toBeTruthy(); }); - describe('template', () => { - describe('weiter felder auswerten button', () => { - it('should exists', () => { - fixture.detectChanges(); - - existsAsHtmlElement(fixture, evaluateAdditionalFieldsTestId); - }); - }); - }); - describe('on init', () => { const stateResource: StateResource<AggregationMappingListResource> = createStateResource( createAggregationMappingListResource(), diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html index 27805c87fe074bae41311083bc1aed20d5aaeaef..ce24e7184c9f832dec08b205237303ce3cd25af7 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html @@ -7,18 +7,25 @@ [formControlName]="StatistikFieldsFormService.FIELD_FORM_ENGINE_NAME" label="Formengine" placeholder="Tragen Sie hier die Formengine des Formulars ein." - data-test-id="form-engine-name-input" + data-test-id="form-engine-name" + dataTestId="form-engine-name" ></ods-text-editor> <ods-text-editor [formControlName]="StatistikFieldsFormService.FIELD_FORM_ID" label="FormID" placeholder="Tragen Sie hier die FormID des Formulars ein." - data-test-id="form-id-input" + data-test-id="form-id" + dataTestId="form-id" ></ods-text-editor> </div> <statistik-fields-form-mapping /> </form> - <ods-button text="Datenfeld hinzufügen" dataTestId="add-mapping-button" (clickEmitter)="formService.addMapping()"> + <ods-button + text="Datenfeld hinzufügen" + dataTestId="add-mapping-button" + data-test-id="add-mapping" + (clickEmitter)="formService.addMapping()" + > <ods-plus-icon icon class="fill-whitetext" /> </ods-button> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts index a0154fd3137315cfbfd12187d70f0ffb43635df5..e124d07cf71b81b1c0ee692d87cd25ed8e8b59cd 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts @@ -6,7 +6,7 @@ import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angul import { TextEditorComponent } from '@ods/component'; import { ButtonComponent, PlusIconComponent } from '@ods/system'; import { MockComponent } from 'ng-mocks'; -import { getDataTestIdAttributeOf, getDataTestIdOf } from '../../../../../tech-shared/test/data-test'; +import { getDataTestIdOf } from '../../../../../tech-shared/test/data-test'; import { AdminCancelButtonComponent } from '../../../../shared/src/lib/admin-cancel-button/admin-cancel-button.component'; import { AdminSaveButtonComponent } from '../../../../shared/src/lib/admin-save-button/admin-save-button.component'; import { AdminStatistikFieldsFormComponent } from './admin-statistik-fields-form.component'; @@ -17,9 +17,9 @@ describe('AdminStatistikFieldsFormComponent', () => { let component: AdminStatistikFieldsFormComponent; let fixture: ComponentFixture<AdminStatistikFieldsFormComponent>; - const formEngineNameInputTestId: string = getDataTestIdOf('form-engine-name-input'); - const formIdInputTestId: string = getDataTestIdOf('form-id-input'); - const addMappingButton: string = getDataTestIdAttributeOf('add-mapping-button'); + const formEngineNameInputTestId: string = getDataTestIdOf('form-engine-name'); + const formIdInputTestId: string = getDataTestIdOf('form-id'); + const addMappingButton: string = getDataTestIdOf('add-mapping'); const formBuilder: FormBuilder = new FormBuilder(); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html index 2ce86b545b1039b795911cf172f154b9e2b97871..569fc993f66170c76ad2cad6c28679b36e86e210 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html @@ -10,6 +10,7 @@ formControlName="sourcePath" label="Pfad des Datenfeldes" placeholder="Tragen Sie hier den gesamten Pfad des Datenfeldes ein, das Sie auswerten möchten." + [dataTestId]="'mapping-field-' + i" [attr.data-test-id]="'mapping-field-' + i" ></ods-text-editor> <ods-button @@ -19,6 +20,7 @@ destructive="true" (clickEmitter)="formService.removeMapping(i)" [dataTestId]="'remove-mapping-button-' + i" + [attr.data-test-id]="'remove-mapping-' + i" > <ods-delete-icon icon /> </ods-button> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts index 61cd006e429951cde3657be7688b30063cc2e6eb..d06746e48f9660298e3c4e2d305a1404cd0e8605 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts @@ -5,7 +5,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { TextEditorComponent } from '@ods/component'; import { ButtonComponent, DeleteIconComponent } from '@ods/system'; -import { getDataTestIdOf, getDynamicDataTestIdAttributOf } from 'libs/tech-shared/test/data-test'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; import { StatistikFieldsFormService } from '../statistik-fields.formservice'; import { AdminStatistikFieldsFormMappingComponent } from './statistik-fields-form-mapping.component'; @@ -15,7 +15,7 @@ describe('AdminStatistikFieldsFormMappingComponent', () => { let fixture: ComponentFixture<AdminStatistikFieldsFormMappingComponent>; const mappingField: string = getDataTestIdOf('mapping-field-0'); - const removeMappingButton: string = getDynamicDataTestIdAttributOf('remove-mapping-button-0'); + const removeMappingButton: string = getDataTestIdOf('remove-mapping-0'); const formBuilder: FormBuilder = new FormBuilder(); diff --git a/alfa-client/libs/admin/user-profile/.eslintrc.json b/alfa-client/libs/admin/user-profile/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..b10f9813a8f5c59432cf245301dc4d01a8031fd1 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "lib", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "lib", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/alfa-client/libs/admin/user-profile/README.md b/alfa-client/libs/admin/user-profile/README.md new file mode 100644 index 0000000000000000000000000000000000000000..274c5f872b16cc6d084af4624ac985d6def1ab1d --- /dev/null +++ b/alfa-client/libs/admin/user-profile/README.md @@ -0,0 +1,7 @@ +# admin-user-profile + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test admin-user-profile` to execute the unit tests. diff --git a/alfa-client/libs/admin/user-profile/jest.config.ts b/alfa-client/libs/admin/user-profile/jest.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..8670098e4b2ced9d4dc48f74066897f0cde39ff4 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/jest.config.ts @@ -0,0 +1,21 @@ +export default { + displayName: 'admin-user-profile', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], + coverageDirectory: '../../../coverage/libs/admin/user-profile', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '<rootDir>/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/alfa-client/libs/admin/user-profile/project.json b/alfa-client/libs/admin/user-profile/project.json new file mode 100644 index 0000000000000000000000000000000000000000..fc4e68c48443f646a90034421e184a837300707c --- /dev/null +++ b/alfa-client/libs/admin/user-profile/project.json @@ -0,0 +1,22 @@ +{ + "name": "admin-user-profile", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/admin/user-profile/src", + "prefix": "lib", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}" + ], + "options": { + "jestConfig": "libs/admin/user-profile/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + } +} diff --git a/alfa-client/libs/admin/user-profile/src/index.ts b/alfa-client/libs/admin/user-profile/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.html b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.html new file mode 100644 index 0000000000000000000000000000000000000000..c3fe2d2837418f91d393a6de8cb98b20b271a0a5 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.html @@ -0,0 +1,3 @@ +<ods-dropdown-menu-link-item caption="Leitfaden für die Administration" text="PDF öffnen" [url]="url"> + <ods-file-icon icon fileType="pdf" size="medium" /> +</ods-dropdown-menu-link-item> diff --git a/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.scss b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..bcbd245ce7ef1ee60e79e99fce4f19f468a8c145 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.scss @@ -0,0 +1,3 @@ +:host { + white-space: nowrap; +} diff --git a/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.spec.ts b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..174d7d1fddc546ec91902bd0ee6d7a2f4509f561 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DropdownMenuLinkItemComponent, FileIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; +import { DocumentationComponent } from './documentation.component'; + +describe('DocumentationComponent', () => { + let component: DocumentationComponent; + let fixture: ComponentFixture<DocumentationComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DocumentationComponent, MockComponent(DropdownMenuLinkItemComponent), MockComponent(FileIconComponent)], + }).compileComponents(); + + fixture = TestBed.createComponent(DocumentationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.ts b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3efae263c5f161d036cecce326dd636e292ce25 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/documentation/documentation.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { DropdownMenuLinkItemComponent, FileIconComponent } from '@ods/system'; + +@Component({ + selector: 'admin-documentation', + templateUrl: './documentation.component.html', + styleUrls: ['./documentation.component.scss'], + standalone: true, + imports: [DropdownMenuLinkItemComponent, FileIconComponent], +}) +export class DocumentationComponent { + @Input() url: string; +} diff --git a/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.html b/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9c5b0aa958eaadea5a44ecb92f5cbe45c05b06e6 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.html @@ -0,0 +1,3 @@ +<ods-dropdown-menu-button-item caption="Abmelden" (clickEmitter)="logout.emit()" data-test-id="popup-logout-button"> + <ods-logout-icon icon class="fill-primary" /> +</ods-dropdown-menu-button-item> diff --git a/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.spec.ts b/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..092b7217e8fe78d548dad54ed264c61c4cb0fdcd --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.spec.ts @@ -0,0 +1,42 @@ +import { dispatchEventFromFixture, MockEvent } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { expect } from '@jest/globals'; +import { getDataTestIdOf } from '../../../../../tech-shared/test/data-test'; +import { AdminUserLogoutButtonComponent } from './admin-user-logout-button.component'; + +describe('AdminUserLogoutButtonComponent', () => { + let component: AdminUserLogoutButtonComponent; + let fixture: ComponentFixture<AdminUserLogoutButtonComponent>; + + const logoutButtonTestId: string = getDataTestIdOf('popup-logout-button'); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AdminUserLogoutButtonComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminUserLogoutButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('menu button item', () => { + describe('output', () => { + describe('clickEmitter', () => { + it('should emit', () => { + component.logout.emit = jest.fn(); + + dispatchEventFromFixture(fixture, logoutButtonTestId, MockEvent.CLICK); + + expect(component.logout.emit).toHaveBeenCalled(); + }); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.ts b/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6e3f4cd7f873a43d3012767e30b5f58738cbee69 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/user-logout-button/admin-user-logout-button.component.ts @@ -0,0 +1,12 @@ +import { Component, EventEmitter, Output } from '@angular/core'; +import { DropdownMenuButtonItemComponent, LogoutIconComponent } from '@ods/system'; + +@Component({ + selector: 'admin-user-logout-button', + standalone: true, + templateUrl: './admin-user-logout-button.component.html', + imports: [DropdownMenuButtonItemComponent, LogoutIconComponent], +}) +export class AdminUserLogoutButtonComponent { + @Output() logout = new EventEmitter<void>(); +} diff --git a/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.html b/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..78c636770990371bb05b7e6eef2bcca474ca67f0 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.html @@ -0,0 +1,8 @@ +<div + role="img" + class="flex size-9 items-center justify-center rounded-full border-2 border-transparent bg-ozggray-900 hover:border-primary" +> + <p class="font-semibold text-whitetext" data-test-id="popup-button-content"> + {{ currentUserInitials }} + </p> +</div> diff --git a/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.spec.ts b/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d14d9482666d406fa59e7d97856497e2f3ba930b --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AdminUserMenuButtonComponent } from './admin-user-menu-button.component'; + +describe('AdminUserMenuButtonComponent', () => { + let component: AdminUserMenuButtonComponent; + let fixture: ComponentFixture<AdminUserMenuButtonComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AdminUserMenuButtonComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminUserMenuButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.ts b/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..02e3f12cef1b770fd4ee46a561d5949a84bc3e92 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/user-menu-button/admin-user-menu-button.component.ts @@ -0,0 +1,10 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'admin-user-menu-button', + standalone: true, + templateUrl: './admin-user-menu-button.component.html', +}) +export class AdminUserMenuButtonComponent { + @Input() currentUserInitials: string; +} diff --git a/alfa-client/apps/admin/src/common/user-profile-button-container/user-profile-button-container.component.html b/alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile-button-container.component.html similarity index 66% rename from alfa-client/apps/admin/src/common/user-profile-button-container/user-profile-button-container.component.html rename to alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile-button-container.component.html index 29fc9153463819b76fb30d8f2deef1dd77e7a9c4..7f5574fb4b4a3e18aa19c5a44094644825a59433 100644 --- a/alfa-client/apps/admin/src/common/user-profile-button-container/user-profile-button-container.component.html +++ b/alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile-button-container.component.html @@ -24,20 +24,15 @@ --> <ods-dropdown-menu buttonClass="rounded-full"> - <div - button-content - role="img" - class="flex size-9 items-center justify-center rounded-full border-2 border-transparent bg-ozggray-900 hover:border-primary" - > - <p class="font-semibold text-whitetext" data-test-id="popup-button-content"> - {{ currentUserInitials }} - </p> - </div> - <ods-dropdown-menu-button-item - caption="Abmelden" - (itemClicked)="authenticationService.logout()" - data-test-id="popup-logout-button" - > - <ods-logout-icon icon /> - </ods-dropdown-menu-button-item> + <admin-user-menu-button button-content [currentUserInitials]="currentUserInitials"></admin-user-menu-button> + + <admin-user-logout-button (logout)="authenticationService.logout()"></admin-user-logout-button> + + @if (apiRootStateResource.resource | hasLink: ApiRootLinkRel.DOCUMENTATIONS) { + <div class="h-2"></div> + <admin-documentation + [url]="apiRootStateResource.resource | getUrl: ApiRootLinkRel.DOCUMENTATIONS" + data-test-id="admin-documentation" + /> + } </ods-dropdown-menu> diff --git a/alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile-button-container.component.spec.ts b/alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile-button-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb23876d9563a75acb6816a5cc5cca5285b2480a --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile-button-container.component.spec.ts @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { ApiRootLinkRel, ApiRootResource } from '@alfa-client/api-root-shared'; +import { createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixtureByType, existsAsHtmlElement, getElementComponentFromFixtureByCss, getElementFromFixture, mock, Mock, notExistsAsHtmlElement, } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { AuthenticationService } from '@authentication'; +import { expect } from '@jest/globals'; +import { getUrl } from '@ngxp/rest'; +import { DropdownMenuButtonItemComponent, DropdownMenuComponent, LogoutIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; +import { createApiRootResource } from '../../../../../api-root-shared/test/api-root'; +import { getDataTestIdOf } from '../../../../../tech-shared/test/data-test'; +import { DocumentationComponent } from '../documentation/documentation.component'; +import { AdminUserLogoutButtonComponent } from '../user-logout-button/admin-user-logout-button.component'; +import { UserProfileButtonContainerComponent } from './user-profile.button-container.component'; + +describe('UserProfileButtonContainerComponent', () => { + let component: UserProfileButtonContainerComponent; + let fixture: ComponentFixture<UserProfileButtonContainerComponent>; + + let authenticationService: Mock<AuthenticationService>; + + const popupButtonContent: string = getDataTestIdOf('popup-button-content'); + const popupLogoutButton: string = getDataTestIdOf('popup-logout-button'); + const documentationTestId: string = getDataTestIdOf('admin-documentation'); + + const apiRootStateResource: StateResource<ApiRootResource> = createStateResource(createApiRootResource()); + + beforeEach(() => { + authenticationService = mock(AuthenticationService); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [UserProfileButtonContainerComponent], + imports: [ + RouterTestingModule, + MockComponent(DropdownMenuComponent), + MockComponent(DropdownMenuButtonItemComponent), + MockComponent(LogoutIconComponent), + ], + providers: [ + { + provide: AuthenticationService, + useValue: authenticationService, + }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UserProfileButtonContainerComponent); + component = fixture.componentInstance; + component.apiRootStateResource = apiRootStateResource; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + it('should call authService to get current user initials', () => { + component.ngOnInit(); + + expect(authenticationService.getCurrentUserInitials).toHaveBeenCalled(); + }); + }); + + describe('template', () => { + describe('popup button', () => { + it('should show initials', () => { + component.currentUserInitials = 'AV'; + fixture.detectChanges(); + + const popupButtonContentElement: HTMLElement = getElementFromFixture(fixture, popupButtonContent); + + expect(popupButtonContentElement.textContent.trim()).toEqual('AV'); + }); + }); + + describe('logout', () => { + it('should call authService logout', () => { + dispatchEventFromFixtureByType(fixture, AdminUserLogoutButtonComponent, 'logout'); + + expect(authenticationService.logout).toHaveBeenCalled(); + }); + }); + + describe('documentation', () => { + it('should exists', () => { + component.apiRootStateResource = createStateResource(createApiRootResource([ApiRootLinkRel.DOCUMENTATIONS])); + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, documentationTestId); + }); + + it('should NOT exists', () => { + component.apiRootStateResource = createStateResource(createApiRootResource([])); + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, documentationTestId); + }); + + it('should have inputs', () => { + component.apiRootStateResource = createStateResource(createApiRootResource([ApiRootLinkRel.DOCUMENTATIONS])); + + fixture.detectChanges(); + const documentationComponent: DocumentationComponent = getElementComponentFromFixtureByCss(fixture, documentationTestId); + + expect(documentationComponent.url).toEqual( + getUrl(component.apiRootStateResource.resource, ApiRootLinkRel.DOCUMENTATIONS), + ); + }); + }); + }); +}); diff --git a/alfa-client/apps/admin/src/common/user-profile-button-container/user-profile.button-container.component.ts b/alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile.button-container.component.ts similarity index 59% rename from alfa-client/apps/admin/src/common/user-profile-button-container/user-profile.button-container.component.ts rename to alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile.button-container.component.ts index f2f4bd8351b8eb6be525ce81a68c8aec3c081a6e..6a7d7299beab272fc36cd93602f449dd0bff1af7 100644 --- a/alfa-client/apps/admin/src/common/user-profile-button-container/user-profile.button-container.component.ts +++ b/alfa-client/libs/admin/user-profile/src/lib/user-menu/user-profile.button-container.component.ts @@ -21,20 +21,36 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, OnInit } from '@angular/core'; -import { DropdownMenuButtonItemComponent, DropdownMenuComponent, LogoutIconComponent } from '@ods/system'; +import { ApiRootLinkRel, ApiRootResource } from '@alfa-client/api-root-shared'; +import { GetUrlPipe, HasLinkPipe, StateResource } from '@alfa-client/tech-shared'; +import { Component, inject, Input, OnInit } from '@angular/core'; import { AuthenticationService } from '@authentication'; +import { DropdownMenuComponent } from '@ods/system'; +import { DocumentationComponent } from '../documentation/documentation.component'; +import { AdminUserLogoutButtonComponent } from '../user-logout-button/admin-user-logout-button.component'; +import { AdminUserMenuButtonComponent } from '../user-menu-button/admin-user-menu-button.component'; @Component({ selector: 'user-profile-button-container', templateUrl: './user-profile-button-container.component.html', standalone: true, - imports: [DropdownMenuComponent, DropdownMenuButtonItemComponent, LogoutIconComponent], + imports: [ + DropdownMenuComponent, + HasLinkPipe, + GetUrlPipe, + DocumentationComponent, + AdminUserLogoutButtonComponent, + AdminUserMenuButtonComponent, + ], }) export class UserProfileButtonContainerComponent implements OnInit { + @Input() apiRootStateResource: StateResource<ApiRootResource>; + public currentUserInitials: string; - constructor(public authenticationService: AuthenticationService) {} + public readonly authenticationService: AuthenticationService = inject(AuthenticationService); + + public readonly ApiRootLinkRel = ApiRootLinkRel; ngOnInit(): void { this.currentUserInitials = this.authenticationService.getCurrentUserInitials(); diff --git a/alfa-client/libs/admin/user-profile/src/test-setup.ts b/alfa-client/libs/admin/user-profile/src/test-setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..c408668266d2fec3a9803c0ec044bc163fb987fe --- /dev/null +++ b/alfa-client/libs/admin/user-profile/src/test-setup.ts @@ -0,0 +1,12 @@ +import '@testing-library/jest-dom'; +import 'jest-preset-angular/setup-jest'; + +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +getTestBed().resetTestEnvironment(); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false }, + errorOnUnknownProperties: true, + errorOnUnknownElements: true, +}); diff --git a/alfa-client/libs/admin/user-profile/tsconfig.json b/alfa-client/libs/admin/user-profile/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..8ca9ad312c2bd4dc364383853ddd91a2ed8f86fd --- /dev/null +++ b/alfa-client/libs/admin/user-profile/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "target": "es2022" + } +} diff --git a/alfa-client/libs/admin/user-profile/tsconfig.lib.json b/alfa-client/libs/admin/user-profile/tsconfig.lib.json new file mode 100644 index 0000000000000000000000000000000000000000..8441346f6e5858b2ef4235cb3c3160eda256f94a --- /dev/null +++ b/alfa-client/libs/admin/user-profile/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/alfa-client/libs/admin/user-profile/tsconfig.spec.json b/alfa-client/libs/admin/user-profile/tsconfig.spec.json new file mode 100644 index 0000000000000000000000000000000000000000..e637bf83b59243f9ebc9b37842cfbf3f159bba47 --- /dev/null +++ b/alfa-client/libs/admin/user-profile/tsconfig.spec.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "target": "es2016", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user-form-headline/user-form-headline.component.html b/alfa-client/libs/admin/user/src/lib/user-form/user-form-headline/user-form-headline.component.html index 56c74fd10047e5dece25229b0888dbbe70f9ed12..c352d3863a3f95b25eabe0b448501702592f2db1 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user-form-headline/user-form-headline.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-form/user-form-headline/user-form-headline.component.html @@ -1,7 +1,7 @@ -<h1 class="heading-1 mb-4"> - @if(isPatch){ +<h1 class="heading-1 mb-4" data-test-id="benutzer-form-headline"> + @if (isPatch) { Benutzer bearbeiten } @else { Benutzer anlegen } -</h1> \ No newline at end of file +</h1> diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html index 792807ba833b4a69bec08bf2f740315fdfc7bbd9..129d8476634e65400913eb770ac3f6b9c9ff017a 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html @@ -5,46 +5,38 @@ <h3 class="text-md block font-medium text-text">Administration</h3> <div class="flex items-center gap-2"> <ods-checkbox-editor [formControlName]="UserFormService.ADMIN" label="Admin" inputId="admin" /> - <div - role="tooltip" - tabindex="0" + <button tooltip='Wird nur in Kombination mit "User" verwendet. Diese Rolle kann Funktionen in Keycloak und der Administration konfigurieren, z.B. Benutzer anlegen, Gruppen erstellen bzw. Organisationseinheiten hinzufügen und Rollen zuweisen.' > <ods-info-icon /> - </div> + </button> </div> </div> <div [formGroupName]="UserFormService.ALFA_GROUP" class="flex flex-col gap-2"> <h3 class="text-md block font-medium text-text">Alfa</h3> <div class="flex items-center gap-2"> <ods-checkbox-editor [formControlName]="UserFormService.LOESCHEN" label="Löschen" inputId="delete" /> - <div - role="tooltip" - tabindex="0" + <button tooltip='Diese Rolle hat dieselben Rechte wie die Rolle "User". Zusätzlich kann "Löschen" ausgewählte Vorgänge aus Alfa löschen. Diese Rolle sollten zwei Benutzer haben, da das Löschen einem Vieraugen-Prinzip folgt.' > <ods-info-icon /> - </div> + </button> </div> <div class="flex items-center gap-2"> <ods-checkbox-editor [formControlName]="UserFormService.USER" label="User" inputId="user" /> - <div - role="tooltip" - tabindex="0" + <button tooltip='Diese Rolle kann alle Vorgänge sehen und bearbeiten, wenn diese seiner Organisationseinheit zugewiesen sind. Ist kompatibel mit "Löschen" und "Admin".' > <ods-info-icon /> - </div> + </button> </div> <div class="flex items-center gap-2"> <ods-checkbox-editor [formControlName]="UserFormService.POSTSTELLE" label="Poststelle" inputId="post_office" /> - <div - role="tooltip" - tabindex="0" + <button tooltip='Diese Rolle kann ausschließlich alle neu eingegangenen Vorgänge sehen und anderen Benutzern zuweisen. Sie sollte nur einem Benutzer zugewiesen sein. Dieser sollte keine weiteren Rollen besitzen. (Sie ist aber kompatibel mit der "Admin")' > <ods-info-icon /> - </div> + </button> </div> </div> </div> diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user-form-save-button/user-form-save-button.component.html b/alfa-client/libs/admin/user/src/lib/user-form/user-form-save-button/user-form-save-button.component.html index 0277da75dbb4169dd33e4927d89702b1105ce9d9..064289aa47b5e6ff3e9e37656407d30cd0d52c8d 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user-form-save-button/user-form-save-button.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-form/user-form-save-button/user-form-save-button.component.html @@ -3,4 +3,4 @@ (clickEmitter)="submit()" text="Speichern" dataTestId="save-button" -/> \ No newline at end of file +/> diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts index 3be33cbcdfd5b941af8ce9ef4a51c63160419bb5..724564f208c9f26cb6265da1bbad81b52ad92b13 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts @@ -26,8 +26,13 @@ import { ROUTES } from '@admin-client/shared'; import { User, UserService } from '@admin-client/user-shared'; import { PatchConfig } from '@admin/keycloak-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; -import { createEmptyStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared'; -import { Mock, mock } from '@alfa-client/test-utils'; +import { + createEmptyStateResource, + createLoadingStateResource, + createStateResource, + StateResource, +} from '@alfa-client/tech-shared'; +import { Mock, mock, mockWindowError } from '@alfa-client/test-utils'; import { SnackBarService } from '@alfa-client/ui'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { AbstractControl, FormControl, FormGroup, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; @@ -159,7 +164,13 @@ describe('UserFormService', () => { }); describe('initOrganisationsEinheiten', () => { + beforeEach(() => { + formService._addOrganisationsEinheitenToForm = jest.fn(); + }); + it('should call adminOrganisationsEinheitService getAll', () => { + formService._initOrganisationsEinheiten(); + expect(adminOrganisationsEinheitService.getAll).toHaveBeenCalled(); }); @@ -170,8 +181,6 @@ describe('UserFormService', () => { }); it('should call addOrganisationsEinheitenToForm ', fakeAsync(() => { - formService._addOrganisationsEinheitenToForm = jest.fn(); - formService._initOrganisationsEinheiten().subscribe(); tick(); @@ -189,6 +198,15 @@ describe('UserFormService', () => { it('should set initOrganisationsEinheiten$', () => { expect(formService['_initOrganisationsEinheiten$']).toBeDefined(); }); + + it('should not throw any exception on loading state resource', () => { + adminOrganisationsEinheitService.getAll.mockReturnValue(of(createLoadingStateResource())); + const errorMock: any = mockWindowError(); + + formService._initOrganisationsEinheiten(); + + expect(errorMock).not.toHaveBeenCalled(); + }); }); describe('addOrganisationsEinheitenToForm', () => { diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts index a456872659a6b1ebd15a0bb570080b5834069fdd..3d0960b1d363fe87688c1c9fd07f7b61ddefc67c 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts @@ -26,7 +26,7 @@ import { ROUTES } from '@admin-client/shared'; import { User, UserService } from '@admin-client/user-shared'; import { KeycloakFormService, PatchConfig } from '@admin/keycloak-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; -import { createEmptyStateResource, EMPTY_STRING, mapToResource, StateResource } from '@alfa-client/tech-shared'; +import { createEmptyStateResource, EMPTY_STRING, isLoaded, mapToResource, StateResource } from '@alfa-client/tech-shared'; import { SnackBarService } from '@alfa-client/ui'; import { Injectable, OnDestroy } from '@angular/core'; import { @@ -39,7 +39,7 @@ import { Validators, } from '@angular/forms'; import { UrlSegment } from '@angular/router'; -import { catchError, Observable, of, Subscription, tap } from 'rxjs'; +import { catchError, filter, Observable, of, Subscription, tap } from 'rxjs'; @Injectable() export class UserFormService extends KeycloakFormService<User> implements OnDestroy { @@ -137,6 +137,7 @@ export class UserFormService extends KeycloakFormService<User> implements OnDest _initOrganisationsEinheiten(): Observable<AdminOrganisationsEinheit[]> { const organisationsEinheitenGroup: UntypedFormGroup = this.getOrganisationsEinheitenGroup(); return this.adminOrganisationsEinheitService.getAll().pipe( + filter(isLoaded), mapToResource<AdminOrganisationsEinheit[]>(), tap((organisationsEinheiten: AdminOrganisationsEinheit[]): void => { this.setOrganisationsEinheitenIdsInMap(organisationsEinheiten); diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.spec.ts b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.spec.ts index e3b7bcedafc499a5bf7187d096e9bcb1eb9d6105..e03fe86039ad5c895d6be78eb173a56e78aa445e 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.spec.ts @@ -20,6 +20,7 @@ describe('UserListContainerComponent', () => { userService = { ...mock(UserService), getAll: jest.fn().mockReturnValue(usersStateResource$), + refresh: jest.fn(), }; await TestBed.configureTestingModule({ @@ -55,4 +56,12 @@ describe('UserListContainerComponent', () => { expect(userList.usersStateResource).toBe(usersStateResource); }); }); + + describe('on destroy', () => { + it('should call service to refresh list', () => { + component.ngOnDestroy(); + + expect(userService.refresh).toHaveBeenCalled(); + }); + }); }); diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.ts b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.ts index 371df8b2664808b39e524439d4ae45d63016f177..ac2e37357acff04c7849031dec3d2cefe30f6a44 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.ts +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list-container.component.ts @@ -1,8 +1,8 @@ import { User, UserService } from '@admin-client/user-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { StateResource } from '@alfa-client/tech-shared'; import { AsyncPipe } from '@angular/common'; -import { Component, inject, OnInit } from '@angular/core'; -import { Observable, of } from 'rxjs'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; import { UserListComponent } from './user-list/user-list.component'; @Component({ @@ -11,12 +11,16 @@ import { UserListComponent } from './user-list/user-list.component'; imports: [UserListComponent, AsyncPipe], templateUrl: './user-list-container.component.html', }) -export class UserListContainerComponent implements OnInit { +export class UserListContainerComponent implements OnInit, OnDestroy { private userService = inject(UserService); - public usersStateResource$: Observable<StateResource<User[]>> = of(createEmptyStateResource<User[]>()); + public usersStateResource$: Observable<StateResource<User[]>>; ngOnInit(): void { this.usersStateResource$ = this.userService.getAll(); } + + ngOnDestroy(): void { + this.userService.refresh(); + } } diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html index 153e3e19deecd0653b83ae9fc7e516f85b6a8716..e2db54f1168e688391e37bd141c058e60dd45022 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html @@ -23,10 +23,12 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<h1 class="heading-1 mb-4">Benutzer & Rollen</h1> +<h1 class="heading-1 mb-4" data-test-id="user-list-headline">Benutzer & Rollen</h1> <ods-routing-button [linkPath]="ROUTES.BENUTZER_NEU" text="Benutzer hinzufügen" class="mb-4 w-fit" dataTestId="add-user-button" /> -<ods-list> - @for (user of usersStateResource.resource; track $index) { - <admin-user [user]="user" class="block w-full" /> - } -</ods-list> +<ods-spinner [stateResource]="usersStateResource"> + <ods-list data-test-id="user-list"> + @for (user of usersStateResource.resource; track $index) { + <admin-user [user]="user" class="block w-full" /> + } + </ods-list> +</ods-spinner> diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.spec.ts b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.spec.ts index 23bc4e3efe65add8ea04eaff6d4324b907190dc8..fe161b5c1fc09a5a9b2def8a5a500b261358a9fe 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.spec.ts @@ -26,7 +26,7 @@ import { User } from '@admin-client/user-shared'; import { createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared'; import { getMockComponent } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RoutingButtonComponent } from '@ods/component'; +import { RoutingButtonComponent, SpinnerComponent } from '@ods/component'; import { MockComponent } from 'ng-mocks'; import { createUser } from '../../../../../user-shared/test/user'; import { UserListComponent } from './user-list.component'; @@ -38,8 +38,10 @@ describe('UsersListComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [UserListComponent], - declarations: [MockComponent(RoutingButtonComponent), MockComponent(UserComponent)], + imports: [ + UserListComponent, + [MockComponent(RoutingButtonComponent), MockComponent(UserComponent), MockComponent(SpinnerComponent)], + ], }).compileComponents(); fixture = TestBed.createComponent(UserListComponent); diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.ts b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.ts index 984d6540945e2739b3b89e66eb9fc03ebc7264d6..61df6b59bfa24732237a7837e9a0d67e7f2f90cc 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.ts +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.ts @@ -26,7 +26,7 @@ import { User } from '@admin-client/user-shared'; import { StateResource } from '@alfa-client/tech-shared'; import { AsyncPipe } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { ButtonWithSpinnerComponent, RoutingButtonComponent } from '@ods/component'; +import { ButtonWithSpinnerComponent, RoutingButtonComponent, SpinnerComponent } from '@ods/component'; import { ListComponent, ListItemComponent } from '@ods/system'; import { UserComponent } from './user/user.component'; @@ -34,7 +34,15 @@ import { UserComponent } from './user/user.component'; selector: 'admin-user-list', templateUrl: './user-list.component.html', standalone: true, - imports: [ButtonWithSpinnerComponent, ListComponent, UserComponent, AsyncPipe, ListItemComponent, RoutingButtonComponent], + imports: [ + ButtonWithSpinnerComponent, + ListComponent, + UserComponent, + AsyncPipe, + ListItemComponent, + RoutingButtonComponent, + SpinnerComponent, + ], }) export class UserListComponent { @Input() usersStateResource: StateResource<User[]>; diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-data/user-data.component.html b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-data/user-data.component.html index 7d455acd981b3767f809810878baea7ebedd5cf1..a09af6b607a48ed6e0bf6bddec4dedae9a1ebd46 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-data/user-data.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-data/user-data.component.html @@ -4,13 +4,13 @@ <span class="sr-only">E-Mail:</span> <ods-mailbox-icon size="small" class="stroke-gray-600" /> </dt> - <dd>{{ user.email }}</dd> + <dd data-test-class="email">{{ user.email }}</dd> </div> <div class="flex items-center gap-2"> <dt> <span class="sr-only">Benutzername:</span> <ods-person-icon /> </dt> - <dd>{{ user.username }}</dd> + <dd data-test-class="username">{{ user.username }}</dd> </div> -</dl> \ No newline at end of file +</dl> diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-organisations-einheiten/user-organisations-einheiten.component.html b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-organisations-einheiten/user-organisations-einheiten.component.html index 550037d391c229139ce5248398ff7715a6e730d4..2836ffd0b61913f495192bfa344d376fb79b6584 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-organisations-einheiten/user-organisations-einheiten.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-organisations-einheiten/user-organisations-einheiten.component.html @@ -1,18 +1,18 @@ <h4 class="sr-only">Zuständige Stellen</h4> @if (organisationsEinheiten.length > 0) { - <ul class="list-outside list-disc pl-4"> - @for(group of organisationsEinheiten | slice: 0 : MAX_GROUPS_TO_DISPLAY; track $index) { + <ul class="list-outside list-disc pl-4" data-test-class="organisations-einheiten"> + @for (group of organisationsEinheiten | slice: 0 : MAX_GROUPS_TO_DISPLAY; track $index) { <li>{{ group }}</li> } </ul> @if (organisationsEinheiten.length > MAX_GROUPS_TO_DISPLAY) { - @if(organisationsEinheiten.length - MAX_GROUPS_TO_DISPLAY === 1 ){ + @if (organisationsEinheiten.length - MAX_GROUPS_TO_DISPLAY === 1) { <p class="pl-4 text-gray-500">und 1 weiterer</p> } @else { <p class="pl-4 text-gray-500">und {{ organisationsEinheiten.length - MAX_GROUPS_TO_DISPLAY }} weitere</p> } } } @else { - <p> keine zuständige Stelle zugewiesen </p> -} \ No newline at end of file + <p data-test-class="no-organisations-einheit-text">keine zuständige Stelle zugewiesen</p> +} diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-roles/user-roles.component.html b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-roles/user-roles.component.html index 26b29dd6d1082879467997191335242a734aac76..f9f7e74372668a5ecf0a74c01808bc675c20327c 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-roles/user-roles.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user-roles/user-roles.component.html @@ -1,4 +1,4 @@ -<dl class="flex flex-wrap gap-2"> +<dl class="flex flex-wrap gap-2" data-test-class="roles"> <dt class="sr-only">Rollen:</dt> @for (role of roles; track $index) { <dd diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.html b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.html index b19df22eb0537d270a9485c8a03b39bf04108939..cda8fb80181aea98d263a1928f683852722a18d7 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.html @@ -1,7 +1,7 @@ -<ods-list-item [path]="user.id" [attr.data-test-id]="'user-entry-' + user.username"> +<ods-list-item [path]="user.id" [attr.data-test-id]="(user.username | convertForDataTest) + '-user-entry'"> <div class="flex-1 basis-1/2"> <div class="mb-2 flex flex-wrap items-center gap-3"> - <h3 class="text-md font-semibold">{{ user | toUserName }}</h3> + <h3 class="text-md font-semibold" data-test-class="fullname">{{ user | toUserName }}</h3> <admin-user-roles [roles]="user | toUserRoles" /> </div> diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.spec.ts b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.spec.ts index afd371e8c6812b03a5b6b671545b4e4344c18ac3..42da5233cedfd9c1dd176fb8973f1b7ea388dc48 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.spec.ts @@ -1,3 +1,4 @@ +import { ConvertForDataTestPipe } from '@alfa-client/tech-shared'; import { getMockComponent } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ListItemComponent } from '@ods/system'; @@ -17,6 +18,7 @@ describe('UserComponent', () => { await TestBed.configureTestingModule({ imports: [ UserComponent, + ConvertForDataTestPipe, MockComponent(ListItemComponent), MockComponent(UserRolesComponent), MockComponent(UserDataComponent), diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.ts b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.ts index f46a2dc2950c2f651dc2e313a4831cbf10ca6ba8..49a54feaafe1f7b2444813699b7eeb2a15fc9487 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.ts +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user/user.component.ts @@ -1,4 +1,5 @@ import { ToUserNamePipe, ToUserRolesPipe, User } from '@admin-client/user-shared'; +import { ConvertForDataTestPipe } from '@alfa-client/tech-shared'; import { NgForOf } from '@angular/common'; import { Component, Input } from '@angular/core'; import { ListItemComponent } from '@ods/system'; @@ -17,6 +18,7 @@ import { UserRolesComponent } from './user-roles/user-roles.component'; UserDataComponent, UserRolesComponent, ToUserRolesPipe, + ConvertForDataTestPipe, ], templateUrl: './user.component.html', }) diff --git a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html index c4901be8608a9e1162963472472090a19fad3a5a..c4c009107436b4b7a8837c0fa9c31c4add1008d6 100644 --- a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html +++ b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html @@ -29,7 +29,8 @@ [placeholder]="placeholder" [autocomplete]="autocomplete" [variant]="variant" - [attr.data-test-id]="(label | convertForDataTest) + '-text-editor'" + [attr.data-test-id]="dataTestId + '-text-editor'" + [dataTestId]="dataTestId" [required]="isRequired" [focus]="focus" [showLabel]="showLabel" @@ -38,6 +39,6 @@ error [invalidParams]="invalidParams" [label]="label" - [attr.data-test-id]="(label | convertForDataTest) + '-text-editor-error'" + [attr.data-test-id]="dataTestId + '-text-editor-error'" ></ods-validation-error> </ods-text-input> diff --git a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.spec.ts b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.spec.ts index 7774ab8ddcebff345d1d62853e8ced4e0773adae..e6dce44b7e467a5d99d3e45667f05c716e29fc68 100644 --- a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.spec.ts +++ b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.spec.ts @@ -35,8 +35,9 @@ describe('TextEditorComponent', () => { let fixture: ComponentFixture<TextEditorComponent>; const labelText: string = faker.word.noun(); - const inputTestId: string = getDataTestIdOf(labelText + '-text-editor'); - const errorId: string = getDataTestIdOf(labelText + '-text-editor-error'); + const dataTestId: string = faker.string.alphanumeric(10); + const inputTestId: string = getDataTestIdOf(dataTestId + '-text-editor'); + const errorId: string = getDataTestIdOf(dataTestId + '-text-editor-error'); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -47,6 +48,7 @@ describe('TextEditorComponent', () => { fixture = TestBed.createComponent(TextEditorComponent); component = fixture.componentInstance; component.label = labelText; + component.dataTestId = dataTestId; fixture.detectChanges(); }); diff --git a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts index cfdd66512de073b74f0dbfd2bbed633573f9651f..45f7c42c8f3f609567e89d3075411ac652eb8882 100644 --- a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts @@ -42,6 +42,7 @@ export class TextEditorComponent extends FormControlEditorAbstractComponent { @Input() isRequired: boolean = false; @Input() focus: boolean = false; @Input() showLabel: boolean = true; + @Input() dataTestId: string; get variant(): string { return this.invalidParams.length > 0 ? 'error' : 'default'; diff --git a/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.spec.ts b/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.spec.ts index 8cbe8c1ae06eabaea31340ddd8eb31f710cab503..edbd1a28e05784e5de0448629fa12c6beaa41669 100644 --- a/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.spec.ts +++ b/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.spec.ts @@ -1,9 +1,11 @@ import { OzgCloudComponentFactory } from '@alfa-client/tech-shared'; import { dispatchEventFromFixture, Mock, mock, MockEvent, mockGetValue } from '@alfa-client/test-utils'; import { DIALOG_COMPONENT, OzgcloudDialogService } from '@alfa-client/ui'; +import { DIALOG_DATA } from '@angular/cdk/dialog'; import { ComponentType } from '@angular/cdk/portal'; import { ComponentRef, Injector, ViewContainerRef } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { expect } from '@jest/globals'; import { ButtonComponent } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; @@ -24,6 +26,7 @@ describe('OpenDialogButtonComponent', () => { const componentRef: ComponentRef<any> = <any>{ instance: { constructor: null } }; const dialogResponse: any = {}; + const dummyDialogData: any = { someField: 'someValue' }; beforeEach(async () => { dialogComponent = {}; @@ -74,31 +77,27 @@ describe('OpenDialogButtonComponent', () => { }); it('should call dialog service to open dialog', () => { - dispatchEventFromFixture(fixture, openDialog, MockEvent.CLICK); - - expect(dialogService.openInContext).toHaveBeenCalledWith(componentRef.instance.constructor, viewContainerRef); - }); - }); + component.dialogData = dummyDialogData; - describe('open', () => { - beforeEach(() => { - component._createComponent = jest.fn().mockReturnValue(componentRef); - }); - - it('should emit close emitter on dialog close', () => { - component.close.emit = jest.fn(); - - component.open(); + dispatchEventFromFixture(fixture, openDialog, MockEvent.CLICK); - expect(component.close.emit).toHaveBeenCalled(); + expect(dialogService.openInContext).toHaveBeenCalledWith( + componentRef.instance.constructor, + viewContainerRef, + dummyDialogData, + ); }); }); describe('create component', () => { it('should call component factory to create component', () => { + component.dialogData = dummyDialogData; + component._createComponent(); - expect(componentFactory.createComponent).toHaveBeenCalledWith(dialogComponent, injector); + expect(componentFactory.createComponent).toHaveBeenCalledWith(dialogComponent, injector, [ + { provide: DIALOG_DATA, useValue: dummyDialogData }, + ]); }); }); }); diff --git a/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.ts b/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.ts index 2815aab6fd3ed8601930bfe061fecc23fc0e4704..974cffb092526ebbc9bf0839b057ea0dde1297ec 100644 --- a/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.ts +++ b/alfa-client/libs/design-component/src/lib/open-dialog-button/open-dialog-button.component.ts @@ -1,19 +1,21 @@ import { OzgCloudComponentFactory } from '@alfa-client/tech-shared'; import { DIALOG_COMPONENT, OzgcloudDialogService } from '@alfa-client/ui'; +import { DIALOG_DATA } from '@angular/cdk/dialog'; import { ComponentType } from '@angular/cdk/portal'; -import { Component, ComponentRef, EventEmitter, inject, Injector, Input, Output, ViewContainerRef } from '@angular/core'; +import { Component, ComponentRef, inject, Injector, Input, ViewContainerRef } from '@angular/core'; import { ButtonComponent, ButtonVariants } from '@ods/system'; -import { first } from 'rxjs'; @Component({ selector: 'ods-open-dialog-button', standalone: true, imports: [ButtonComponent], - template: `<ods-button + template: ` <ods-button (clickEmitter)="open()" [variant]="variant" [text]="label" + [size]="size" [dataTestId]="dataTestId" + [dataTestClass]="dataTestClass" data-test-id="open-dialog" > <ng-container icon> @@ -32,18 +34,18 @@ export class OpenDialogButtonComponent { @Input() label: string; @Input() dataTestId: string; + @Input() dataTestClass: string; @Input() variant: ButtonVariants['variant'] = 'primary'; - - @Output() close: EventEmitter<void> = new EventEmitter(); + @Input() dialogData: any; + @Input() size: ButtonVariants['size']; public open(): void { - this.dialogService - .openInContext(this._createComponent().instance.constructor, this.viewContainerRef) - .closed.pipe(first()) - .subscribe(this.close.emit); + this.dialogService.openInContext(this._createComponent().instance.constructor, this.viewContainerRef, this.dialogData); } _createComponent(): ComponentRef<any> { - return this.componentFactory.createComponent<any>(this.component, this.injector); + return this.componentFactory.createComponent<any>(this.component, this.injector, [ + { provide: DIALOG_DATA, useValue: this.dialogData }, + ]); } } diff --git a/alfa-client/libs/design-system/src/lib/button/button.component.ts b/alfa-client/libs/design-system/src/lib/button/button.component.ts index c0f7892c3c2c5184571b175dbff2326591415495..47e76172adb5e6f9600be54291ee79a841defb78 100644 --- a/alfa-client/libs/design-system/src/lib/button/button.component.ts +++ b/alfa-client/libs/design-system/src/lib/button/button.component.ts @@ -96,6 +96,7 @@ export type ButtonVariants = VariantProps<typeof buttonVariants>; [attr.aria-disabled]="isDisabled" [attr.aria-label]="text" [attr.data-test-id]="dataTestId" + [attr.data-test-class]="dataTestClass" (click)="clickEmitter.emit()" > <ng-content *ngIf="!isLoading" select="[icon]"></ng-content> @@ -106,6 +107,7 @@ export type ButtonVariants = VariantProps<typeof buttonVariants>; export class ButtonComponent { @Input() text: string = ''; @Input() dataTestId: string = ''; + @Input() dataTestClass: string = ''; @Input() disabled: boolean = false; @Input() isLoading: boolean = false; @Input({ transform: booleanAttribute }) destructive: boolean = false; diff --git a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.spec.ts b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.spec.ts index 0b747f1e0507e5375a8dd0baba6a7ce2733ba89c..1af5235f276bf8afd8787fd155e104f3a8b64040 100644 --- a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.spec.ts +++ b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.spec.ts @@ -43,13 +43,13 @@ describe('DropdownMenuButtonItemComponent', () => { expect(component).toBeTruthy(); }); - describe('itemClicked emitter', () => { - it('should emit itemClicked', () => { - component.itemClicked.emit = jest.fn(); + describe('clickEmitter', () => { + it('should emit', () => { + component.clickEmitter.emit = jest.fn(); dispatchEventFromFixture(fixture, 'button', 'click'); - expect(component.itemClicked.emit).toHaveBeenCalled(); + expect(component.clickEmitter.emit).toHaveBeenCalled(); }); }); }); diff --git a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.ts b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.ts index 2c1b5427927141d6b7ca40515ee64432d9d50e2e..85f7e1f0f6895e6798d6697f3e7e63391be8ca14 100644 --- a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.ts +++ b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-button-item/dropdown-menu-button-item.component.ts @@ -28,19 +28,21 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; selector: 'ods-dropdown-menu-button-item', standalone: true, imports: [CommonModule], - template: `<button - class="flex min-h-12 w-full items-center gap-4 border-2 border-transparent px-4 py-3 text-start outline-none hover:border-primary focus-visible:border-focus" - role="menuitem" - (click)="itemClicked.emit()" - [attr.data-test-id]="dataTestId" - > - <ng-content select="[icon]" /> - <p class="text-text">{{ caption }}</p> - </button>`, + template: ` <div class="w-full bg-whitetext p-1.5"> + <button + class="flex w-full items-center gap-2 rounded-md border border-transparent px-4 py-2 text-start font-medium outline-none hover:bg-background-150 focus-visible:border-primary dark:hover:bg-neutral-700" + role="menuitem" + (click)="clickEmitter.emit()" + [attr.data-test-id]="dataTestId" + > + <ng-content select="[icon]" /> + <p class="text-sm text-primary">{{ caption }}</p> + </button> + </div>`, }) export class DropdownMenuButtonItemComponent { @Input({ required: true }) caption!: string; @Input() dataTestId: string; - @Output() itemClicked: EventEmitter<MouseEvent> = new EventEmitter(); + @Output() clickEmitter: EventEmitter<MouseEvent> = new EventEmitter(); } diff --git a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-item/dropdown-menu-item.component.ts b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-item/dropdown-menu-item.component.ts index 301af6719e17a63de18c5141a3bec564265976cd..2314cab202a68e6d276c7d0a3a731ae15df5c239 100644 --- a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-item/dropdown-menu-item.component.ts +++ b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-item/dropdown-menu-item.component.ts @@ -7,6 +7,6 @@ import { Component } from '@angular/core'; imports: [CommonModule], styles: [':host {@apply block min-h-12 px-4 py-3 first:mt-2 last:mb-2}'], - template: ` <ng-content /> `, + template: `<ng-content />`, }) export class DropdownMenuItemComponent {} diff --git a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-link-item/dropdown-menu-link-item.component.ts b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-link-item/dropdown-menu-link-item.component.ts index 045191b56a76618a85600a82757607f267b4c8e4..24427bb02e4ed24c83d01a0c4c942b474ef7385a 100644 --- a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-link-item/dropdown-menu-link-item.component.ts +++ b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu-link-item/dropdown-menu-link-item.component.ts @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; import { OpenLinkIconComponent } from '../../icons/open-link-icon/open-link-icon.component'; import { LinkComponent } from '../../link/link.component'; @@ -5,17 +6,27 @@ import { LinkComponent } from '../../link/link.component'; @Component({ selector: 'ods-dropdown-menu-link-item', standalone: true, - imports: [OpenLinkIconComponent, LinkComponent], - styles: [':host {@apply first:mt-2}'], - template: ` <ods-link [url]="url" class="bg-whitetext" [openInNewTab]="true"> - <div class="flex items-center gap-2 px-4 py-3"> - <p class="font-medium text-primary">{{ text }}</p> - <ods-open-link-icon class="size-5" /> - <span class="sr-only">Öffnet in einem neuen Tab</span> - </div> - </ods-link>`, + imports: [LinkComponent, OpenLinkIconComponent, CommonModule], + template: `<div class="w-full bg-whitetext p-1.5"> + <ods-link [url]="url" [openInNewTab]="true"> + <div class="flex min-w-80 gap-3 px-3 py-1.5"> + <ng-content select="[icon]" /> + <div class="flex flex-col gap-1"> + @if (caption) { + <p class="text-sm font-medium text-text">{{ caption }}</p> + } + <div class="flex items-center gap-2"> + <p class="text-sm font-normal text-primary">{{ text }}</p> + <ods-open-link-icon size="small" /> + <span class="sr-only">Öffnet in einem neuen Tab</span> + </div> + </div> + </div> + </ods-link> + </div>`, }) export class DropdownMenuLinkItemComponent { @Input() url: string; + @Input() caption: string; @Input() text: string; } diff --git a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts index 04a15c7eec0780ac9b8709224ab54aecaf77ab6f..24c78fceb054182a65572cef755fa379dddca255 100644 --- a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts +++ b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts @@ -46,9 +46,10 @@ import { twMerge } from 'tailwind-merge'; > <ng-content select="[button-content]" /> </button> + <div *ngIf="isPopupOpen" - class="absolute z-50 max-h-120 min-w-44 max-w-96 animate-fadeIn overflow-y-auto rounded bg-dropdownBg shadow-md ring-1 ring-grayborder focus:outline-none" + class="absolute z-50 mt-2 min-w-44 max-w-96 animate-fadeIn rounded-lg bg-background-100 ring-1 ring-grayborder drop-shadow-lg focus:outline-none" [ngClass]="alignTo === 'left' ? 'right-0' : 'left-0'" role="menu" aria-modal="true" diff --git a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts index ca6a007c19e49ebb481ff0655b407cc3076a9c08..1b8bf5fdf47948bbc9374b01086238c3e41c0ac3 100644 --- a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts +++ b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { convertForDataTest, ConvertForDataTestPipe, EMPTY_STRING } from '@alfa-client/tech-shared'; +import { convertForDataTest, ConvertForDataTestPipe, EMPTY_STRING, isNotUndefined } from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; @@ -70,7 +70,7 @@ type TextInputVariants = VariantProps<typeof textInputVariants>; [autocomplete]="autocomplete" [attr.aria-required]="required" [attr.aria-invalid]="variant === 'error'" - [attr.data-test-id]="(inputLabel | convertForDataTest) + '-text-input'" + [attr.data-test-id]="_dataTestId + '-text-input'" (click)="clickEmitter.emit()" #inputElement /> @@ -88,6 +88,7 @@ export class TextInputComponent { @Input({ required: true }) set label(label: string) { this.inputLabel = label; this.id = `${convertForDataTest(label)}-${uniqueId()}`; + this._dataTestId = convertForDataTest(this.inputLabel); } @Input() placeholder: string = ''; @Input() autocomplete: string = 'off'; @@ -97,6 +98,9 @@ export class TextInputComponent { @Input() withPrefix: boolean = false; @Input() withSuffix: boolean = false; @Input() showLabel: boolean = true; + @Input() set dataTestId(value: string) { + if (isNotUndefined(value)) this._dataTestId = value; + } @Input() set focus(value: boolean) { if (value && this.inputElement) { @@ -108,5 +112,6 @@ export class TextInputComponent { inputLabel: string; id: string; + _dataTestId: string; textInputVariants = textInputVariants; } diff --git a/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts index 2c084618d209a629d826c35f1397e8ff6c1de6d0..1e3a7ec67b3136e428a177080bd99f7410cb6ae1 100644 --- a/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts @@ -38,6 +38,7 @@ const fileiconVariants = cva('fill-ozggray-300', { }, size: { small: 'w-4 h-5', + medium: 'w-5 h-6', large: 'w-8 h-10', }, }, diff --git a/alfa-client/libs/design-system/src/lib/link/link.component.ts b/alfa-client/libs/design-system/src/lib/link/link.component.ts index 64830deee7cf6873156013281ec33d7e2a56199e..a7f529ad5ca57b02e72dcb6033b128a9c2e77416 100644 --- a/alfa-client/libs/design-system/src/lib/link/link.component.ts +++ b/alfa-client/libs/design-system/src/lib/link/link.component.ts @@ -11,7 +11,7 @@ import { twMerge } from 'tailwind-merge'; [href]="url" [class]=" twMerge( - 'block rounded-lg border-2 border-transparent text-text hover:bg-ghost-hover focus-visible:border-focus focus-visible:bg-ghost-hover focus-visible:outline-none dark:hover:bg-neutral-700', + 'block rounded-md border border-transparent text-text hover:bg-neutral-100 focus-visible:border-primary focus-visible:outline-none dark:hover:bg-neutral-700', class ) " diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts index 98a918c03eb9d525d9bc2977f1885838dd873da3..1ceecd9e1f16e3309e234235887150f8e5c6ee3a 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts @@ -24,16 +24,19 @@ import { BinaryFileService } from '@alfa-client/binary-file-shared'; import { CommandOrder, CommandResource, CommandService, CreateCommand } from '@alfa-client/command-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; -import { createEmptyStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { createEmptyStateResource, createStateResource, EMPTY_STRING, StateResource } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { faker } from '@faker-js/faker'; import { expect } from '@jest/globals'; +import { ResourceUri } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; import { createCommandResource } from 'libs/command-shared/test/command'; import { createKommentar, createKommentarListResource, createKommentarResource } from 'libs/kommentar-shared/test/kommentar'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { of } from 'rxjs'; +import { singleCold } from '../../../tech-shared/test/marbles'; import { KommentarLinkRel, KommentarListLinkRel } from './kommentar.linkrel'; import { Kommentar, KOMMENTAR_UPLOADED_ATTACHMENTS, KommentarListResource, KommentarResource } from './kommentar.model'; import { KommentarRepository } from './kommentar.repository'; @@ -327,4 +330,78 @@ describe('KommentarService', () => { expect(binaryFileService.clearUploadedFiles).toHaveBeenCalledWith(KOMMENTAR_UPLOADED_ATTACHMENTS); }); }); + + describe('is new kommentar formular visible', () => { + it('should emit true', () => { + service._currentlyEdited$.next(EMPTY_STRING); + service.formularVisibility$.next(true); + + expect(service.isFormularVisible()).toBeObservable(singleCold(true)); + }); + + it('should emit false if any kommentar is being edited', () => { + service._currentlyEdited$.next(faker.internet.url()); + service.formularVisibility$.next(true); + + expect(service.isFormularVisible()).toBeObservable(singleCold(false)); + }); + + it('should emit false if not visible', () => { + service._currentlyEdited$.next(EMPTY_STRING); + service.formularVisibility$.next(false); + + expect(service.isFormularVisible()).toBeObservable(singleCold(false)); + }); + }); + + describe('show new kommentar formular', () => { + beforeEach(() => { + service.setCurrentlyEdited = jest.fn(); + }); + + it('should set currently edited to empty string', () => { + service.showFormular(); + + expect(service.setCurrentlyEdited).toHaveBeenCalledWith(EMPTY_STRING); + }); + + it('should set formular visibility', () => { + service.showFormular(); + + expect(service.formularVisibility$).toBeObservable(singleCold(true)); + }); + }); + + describe('set currently edited kommentar uri', () => { + beforeEach(() => { + service.clearUploadedFiles = jest.fn(); + service.hideFormular = jest.fn(); + }); + + it('should clear uploaded files', () => { + service.setCurrentlyEdited(faker.internet.url()); + + expect(service.clearUploadedFiles).toHaveBeenCalled(); + }); + + it('should hide kommentar creation formular', () => { + service.setCurrentlyEdited(faker.internet.url()); + + expect(service.hideFormular).toHaveBeenCalled(); + }); + + it('should NOT hide kommentar creation formular', () => { + service.setCurrentlyEdited(EMPTY_STRING); + + expect(service.hideFormular).not.toHaveBeenCalled(); + }); + + it('should emit currently edited resource uri', () => { + const resourceUri: ResourceUri = faker.internet.url(); + + service.setCurrentlyEdited(resourceUri); + + expect(service._currentlyEdited$).toBeObservable(singleCold(resourceUri)); + }); + }); }); diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts index 27efa8ae9da9e852906f3917b070e27e9faeca2d..6749d94ff0079091abf5f3a42d6c67786b171d08 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts @@ -24,13 +24,13 @@ import { BinaryFileListResource, BinaryFileService } from '@alfa-client/binary-file-shared'; import { CommandOrder, CommandResource, CommandService, CreateCommand, isDone } from '@alfa-client/command-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; -import { createEmptyStateResource, createStateResource, doIfLoadingRequired, StateResource } from '@alfa-client/tech-shared'; +import { createEmptyStateResource, createStateResource, doIfLoadingRequired, EMPTY_STRING, isNotEmpty, StateResource, } from '@alfa-client/tech-shared'; import { VorgangResource, VorgangService } from '@alfa-client/vorgang-shared'; import { Injectable } from '@angular/core'; import { Params } from '@angular/router'; -import { hasLink, Resource } from '@ngxp/rest'; +import { hasLink, Resource, ResourceUri } from '@ngxp/rest'; import { isNil } from 'lodash-es'; -import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'; +import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; import { map, startWith, tap } from 'rxjs/operators'; import { KommentarLinkRel, KommentarListLinkRel } from './kommentar.linkrel'; import { Kommentar, KOMMENTAR_UPLOADED_ATTACHMENTS, KommentarListResource, KommentarResource } from './kommentar.model'; @@ -42,6 +42,7 @@ export class KommentarService { createEmptyStateResource<KommentarListResource>(), ); readonly formularVisibility$: BehaviorSubject<boolean> = new BehaviorSubject(false); + readonly _currentlyEdited$: BehaviorSubject<ResourceUri> = new BehaviorSubject(''); private navigationSub: Subscription; @@ -104,7 +105,9 @@ export class KommentarService { } public isFormularVisible(): Observable<boolean> { - return this.formularVisibility$.asObservable(); + return combineLatest([this.formularVisibility$, this._currentlyEdited$]).pipe( + map(([isVisible, currentlyEdited]) => isVisible && currentlyEdited === EMPTY_STRING), + ); } public canCreateNewKommentar(kommentareListResource: KommentarListResource): Observable<boolean> { @@ -114,6 +117,7 @@ export class KommentarService { } public showFormular(): void { + this.setCurrentlyEdited(EMPTY_STRING); this.formularVisibility$.next(true); } @@ -170,4 +174,16 @@ export class KommentarService { public clearUploadedFiles(): void { this.binaryFileService.clearUploadedFiles(KOMMENTAR_UPLOADED_ATTACHMENTS); } + + public getCurrentlyEdited(): Observable<ResourceUri> { + return this._currentlyEdited$.asObservable(); + } + + public setCurrentlyEdited(resourceUri: ResourceUri): void { + this.clearUploadedFiles(); + if (isNotEmpty(resourceUri)) { + this.hideFormular(); + } + this._currentlyEdited$.next(resourceUri); + } } diff --git a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.html b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.html index 8b432f5eee5589efc42249c5bfdf3f64a631b818..30f142acca7a7374f7e11d554c238f72a235b1ae 100644 --- a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.html +++ b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.html @@ -27,6 +27,7 @@ <ozgcloud-expansion-panel headline="Kommentare"> <alfa-kommentar-list-in-vorgang [kommentarListStateResource]="kommentarListStateResource" + [currentlyEdited]="kommentarService.getCurrentlyEdited() | async" data-test-id="kommentar-list-in-vorgang" > </alfa-kommentar-list-in-vorgang> diff --git a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.spec.ts b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.spec.ts index 44794b0072f9b661cd22120938e0a045e9b5d330..1fa11168673776f20130a7be5616abb0ac84380a 100644 --- a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.spec.ts +++ b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.spec.ts @@ -70,6 +70,14 @@ describe('KommentarListInVorgangContainerComponent', () => { expect(component).toBeTruthy(); }); + describe('ng on init', () => { + it('should call kommentar service isFormularVisible', () => { + component.ngOnInit(); + + expect(kommentarService.isFormularVisible).toHaveBeenCalled(); + }); + }); + describe('ng on changes', () => { beforeEach(() => { kommentarService.isFormularVisible.mockReturnValue(new Observable((o) => o.next(false))); @@ -78,12 +86,6 @@ describe('KommentarListInVorgangContainerComponent', () => { ); }); - it('should call kommentar service isFormularVisible', () => { - component.ngOnChanges(); - - expect(kommentarService.isFormularVisible).toHaveBeenCalled(); - }); - it('should call kommentar service getKommentareByVorgang', () => { component.ngOnChanges(); diff --git a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.ts b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.ts index 8c601d9240a060ca4ceb9cd18a9b9964198eae83..21d3159a29496a6927f7076de4734ff03c663f44 100644 --- a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.ts +++ b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang-container.component.ts @@ -21,10 +21,10 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Input, OnChanges } from '@angular/core'; import { KommentarListResource, KommentarService } from '@alfa-client/kommentar-shared'; import { StateResource } from '@alfa-client/tech-shared'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { Component, inject, Input, OnChanges, OnInit } from '@angular/core'; import { mergeMap, Observable } from 'rxjs'; @Component({ @@ -32,25 +32,24 @@ import { mergeMap, Observable } from 'rxjs'; templateUrl: './kommentar-list-in-vorgang-container.component.html', styleUrls: ['./kommentar-list-in-vorgang-container.component.scss'], }) -export class KommentarListInVorgangContainerComponent implements OnChanges { +export class KommentarListInVorgangContainerComponent implements OnChanges, OnInit { @Input() vorgangStateResource: StateResource<VorgangWithEingangResource>; - showFormular$: Observable<boolean>; + public readonly kommentarService = inject(KommentarService); + + public showFormular$: Observable<boolean>; kommentarListStateResource$: Observable<StateResource<KommentarListResource>>; canCreateNewKommentar$: Observable<boolean>; - constructor(private kommentarService: KommentarService) {} + ngOnInit(): void { + this.showFormular$ = this.kommentarService.isFormularVisible(); + } ngOnChanges(): void { this.reloadKommentarListOnVorgangReload(); - this.showFormular$ = this.kommentarService.isFormularVisible(); - this.kommentarListStateResource$ = this.kommentarService.getKommentareByVorgang( - this.vorgangStateResource.resource, - ); + this.kommentarListStateResource$ = this.kommentarService.getKommentareByVorgang(this.vorgangStateResource.resource); this.canCreateNewKommentar$ = this.kommentarListStateResource$.pipe( - mergeMap((stateResource) => - this.kommentarService.canCreateNewKommentar(stateResource.resource), - ), + mergeMap((stateResource) => this.kommentarService.canCreateNewKommentar(stateResource.resource)), ); } diff --git a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.html b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.html index be3ecc3720ffb2e967b888e54243eb72a5234c11..75ccfb30068aad3b5d8e2847618a3267cb0fb400 100644 --- a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.html +++ b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.html @@ -27,5 +27,6 @@ *ngFor="let kommentar of kommentare" [kommentar]="kommentar" [kommentarListStateResource]="kommentarListStateResource" + [currentlyEdited]="currentlyEdited" > </alfa-kommentar-list-item-in-vorgang> diff --git a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.ts b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.ts index 4519d517cefe76995824afd7b45b8651ac354b8a..1d7212702a1b398d0da13637dc24e598d1f5bf8d 100644 --- a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.ts +++ b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-in-vorgang.component.ts @@ -21,10 +21,11 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Input, OnChanges } from '@angular/core'; import { KommentarListResource, KommentarResource } from '@alfa-client/kommentar-shared'; -import { KommentarListLinkRel } from 'libs/kommentar-shared/src/lib/kommentar.linkrel'; import { getEmbeddedResources, StateResource } from '@alfa-client/tech-shared'; +import { Component, Input, OnChanges } from '@angular/core'; +import { ResourceUri } from '@ngxp/rest'; +import { KommentarListLinkRel } from 'libs/kommentar-shared/src/lib/kommentar.linkrel'; @Component({ selector: 'alfa-kommentar-list-in-vorgang', @@ -33,17 +34,15 @@ import { getEmbeddedResources, StateResource } from '@alfa-client/tech-shared'; }) export class KommentarListInVorgangComponent implements OnChanges { @Input() kommentarListStateResource: StateResource<KommentarListResource>; + @Input() currentlyEdited: ResourceUri; - kommentare: KommentarResource[]; + public kommentare: KommentarResource[]; ngOnChanges(): void { this.kommentare = this.getKommentare(); } getKommentare(): KommentarResource[] { - return getEmbeddedResources( - this.kommentarListStateResource, - KommentarListLinkRel.KOMMENTAR_LIST, - ); + return getEmbeddedResources(this.kommentarListStateResource, KommentarListLinkRel.KOMMENTAR_LIST); } } diff --git a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.spec.ts b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.spec.ts index 127b7fda5dd6f18ac336393aa425dd3903e94ee7..c5a1d190d2f95214dd39f60e95119fbda5d9e9ba 100644 --- a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.spec.ts +++ b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.spec.ts @@ -21,26 +21,17 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { KommentarLinkRel, KommentarResource, KommentarService } from '@alfa-client/kommentar-shared'; +import { ConvertForDataTestPipe, FormatDateWithTimePipe, HasLinkPipe } from '@alfa-client/tech-shared'; +import { getElementFromFixture, mock } from '@alfa-client/test-utils'; +import { UserProfileInKommentarContainerComponent } from '@alfa-client/user-profile'; import { registerLocaleData } from '@angular/common'; import localeDe from '@angular/common/locales/de'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - KommentarLinkRel, - KommentarListLinkRel, - KommentarService, -} from '@alfa-client/kommentar-shared'; -import { - ConvertForDataTestPipe, - createStateResource, - FormatDateWithTimePipe, - HasLinkPipe, -} from '@alfa-client/tech-shared'; -import { getElementFromFixture, mock } from '@alfa-client/test-utils'; -import { UserProfileInKommentarContainerComponent } from '@alfa-client/user-profile'; -import { - createKommentarListResource, - createKommentarResource, -} from 'libs/kommentar-shared/test/kommentar'; +import { faker } from '@faker-js/faker/.'; +import { expect } from '@jest/globals'; +import { getUrl } from '@ngxp/rest'; +import { createKommentarResource } from 'libs/kommentar-shared/test/kommentar'; import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; import { KommentarFormComponent } from '../../kommentar-form/kommentar-form.component'; @@ -85,6 +76,37 @@ describe('KommentarListItemInVorgangComponent', () => { expect(component).toBeTruthy(); }); + describe('set currently edited', () => { + const kommentar: KommentarResource = createKommentarResource(); + + it('should set edit mode to false on same currently edited resource uri', () => { + component.editMode = false; + component.kommentar = kommentar; + + component.currentlyEdited = getUrl(kommentar); + + expect(component.editMode).toBe(false); + }); + + it('should set edit mode to false on different currently edited resource uri', () => { + component.editMode = true; + component.kommentar = kommentar; + + component.currentlyEdited = faker.internet.url(); + + expect(component.editMode).toBe(false); + }); + + it('should set edit mode to true', () => { + component.editMode = true; + component.kommentar = kommentar; + + component.currentlyEdited = getUrl(kommentar); + + expect(component.editMode).toBe(true); + }); + }); + describe('user profile', () => { it('should be visible on existing link', () => { component.kommentar = createKommentarResource([KommentarLinkRel.CREATED_BY]); @@ -105,18 +127,9 @@ describe('KommentarListItemInVorgangComponent', () => { }); }); - describe('kommentare attachments', () => { - it('should be loaded', () => { - const kommentarResource = createKommentarResource(); - component.kommentar = kommentarResource; - - component.ngOnInit(); - - expect(kommentarService.getAttachments).toHaveBeenCalledWith(kommentarResource); - }); - }); - describe('edit', () => { + const kommentar: KommentarResource = createKommentarResource(); + it('should change editMode', () => { component.kommentar = createKommentarResource([KommentarLinkRel.EDIT]); @@ -125,12 +138,20 @@ describe('KommentarListItemInVorgangComponent', () => { expect(component.editMode).toBeTruthy(); }); - it('should not change editMode', () => { - component.kommentar = createKommentarResource(); + it('should NOT change editMode', () => { + component.kommentar = kommentar; component.edit(); expect(component.editMode).toBeFalsy(); }); + + it('should set currently edited kommetar uri', () => { + component.kommentar = kommentar; + + component.edit(); + + expect(kommentarService.setCurrentlyEdited).toHaveBeenCalledWith(getUrl(kommentar)); + }); }); }); diff --git a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts index cf6a951f4bd32c241281ce7c3207092387f92ce1..7da6b91741e10ca74fdb3a4572d43190481826d5 100644 --- a/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts +++ b/alfa-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts @@ -21,35 +21,32 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; import { KommentarLinkRel, KommentarListResource, KommentarResource, KommentarService } from '@alfa-client/kommentar-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; -import { Component, Input, OnInit } from '@angular/core'; -import { hasLink } from '@ngxp/rest'; -import { Observable, of } from 'rxjs'; +import { StateResource } from '@alfa-client/tech-shared'; +import { Component, inject, Input } from '@angular/core'; +import { getUrl, hasLink, ResourceUri } from '@ngxp/rest'; @Component({ selector: 'alfa-kommentar-list-item-in-vorgang', templateUrl: './kommentar-list-item-in-vorgang.component.html', styleUrls: ['./kommentar-list-item-in-vorgang.component.scss'], }) -export class KommentarListItemInVorgangComponent implements OnInit { +export class KommentarListItemInVorgangComponent { @Input() kommentar: KommentarResource; - @Input() kommentarListStateResource: StateResource<KommentarListResource>; - attachments$: Observable<StateResource<BinaryFileListResource>> = of(createEmptyStateResource<BinaryFileListResource>()); - editMode: boolean = false; + @Input() set currentlyEdited(value: ResourceUri) { + this.editMode &&= value === getUrl(this.kommentar); + } - readonly kommentarLinkRel = KommentarLinkRel; + public kommentarService = inject(KommentarService); - constructor(public kommentarService: KommentarService) {} + public editMode: boolean = false; - ngOnInit(): void { - this.attachments$ = this.kommentarService.getAttachments(this.kommentar); - } + public readonly kommentarLinkRel = KommentarLinkRel; - edit(): void { + public edit(): void { this.editMode = hasLink(this.kommentar, KommentarLinkRel.EDIT); + this.kommentarService.setCurrentlyEdited(getUrl(this.kommentar)); } } diff --git a/alfa-client/libs/tech-shared/src/lib/service/component.factory.spec.ts b/alfa-client/libs/tech-shared/src/lib/service/component.factory.spec.ts index 32fb2f6d84f6be4f3699b21359aa61395a0d50f8..d8a94aec3ede53c078234f2b4e75cde96aeedbdb 100644 --- a/alfa-client/libs/tech-shared/src/lib/service/component.factory.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/service/component.factory.spec.ts @@ -1,8 +1,9 @@ import { Mock, mock, mockGetValue } from '@alfa-client/test-utils'; -import { ApplicationRef, ComponentRef, EnvironmentInjector, Injector, ViewRef } from '@angular/core'; +import { ApplicationRef, ComponentRef, EnvironmentInjector, Injector, Provider, ViewRef } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { OzgCloudComponentFactory } from './component.factory'; +import { DIALOG_DATA } from '@angular/cdk/dialog'; import { ComponentType } from '@angular/cdk/portal'; jest.mock('@angular/core', () => ({ @@ -16,6 +17,8 @@ describe('OzgCloudComponentFactory', () => { let appRef: Mock<ApplicationRef>; let envInjector: Mock<EnvironmentInjector>; + const dummyProvider: Provider = { provide: DIALOG_DATA, useValue: { someField: 'value' } } as Provider; + beforeEach(() => { appRef = mock(ApplicationRef); envInjector = mock(EnvironmentInjector as any); @@ -45,7 +48,7 @@ describe('OzgCloudComponentFactory', () => { }); it('should call createComponent', () => { - factory.createComponent(componentType, injector); + factory.createComponent(componentType, injector, [dummyProvider]); expect(createComponentSpy).toHaveBeenCalledWith(componentType, { environmentInjector: envInjector, @@ -76,7 +79,7 @@ describe('OzgCloudComponentFactory', () => { }); it('should create an injector by given parent selector', () => { - factory._createElementInjector(injectorMock); + factory._createElementInjector(injectorMock, [dummyProvider]); expect(createSpy).toHaveBeenCalled(); }); diff --git a/alfa-client/libs/tech-shared/src/lib/service/component.factory.ts b/alfa-client/libs/tech-shared/src/lib/service/component.factory.ts index e54497e9fc92bbb198891d487ddcf7565ffab139..b3d13c11b4897ff1204a3d565edfb18f7bee5b9a 100644 --- a/alfa-client/libs/tech-shared/src/lib/service/component.factory.ts +++ b/alfa-client/libs/tech-shared/src/lib/service/component.factory.ts @@ -1,22 +1,35 @@ import { ComponentType } from '@angular/cdk/portal'; -import { ApplicationRef, ComponentRef, createComponent, EnvironmentInjector, inject, Injectable, Injector } from '@angular/core'; +import { + ApplicationRef, + ComponentRef, + createComponent, + EnvironmentInjector, + inject, + Injectable, + Injector, + Provider, +} from '@angular/core'; @Injectable({ providedIn: 'root' }) export class OzgCloudComponentFactory { private readonly appRef = inject(ApplicationRef); private readonly envInjector = inject(EnvironmentInjector); - public createComponent<T>(componentType: ComponentType<any>, parentInjector: Injector): ComponentRef<T> { + public createComponent<T>( + componentType: ComponentType<any>, + parentInjector: Injector, + providers: Provider[] = [], + ): ComponentRef<T> { const component: ComponentRef<any> = <ComponentRef<any>>createComponent(componentType, { environmentInjector: this.envInjector, - elementInjector: this._createElementInjector(parentInjector), + elementInjector: this._createElementInjector(parentInjector, providers), }); this._registerComponentToChangeDetection(component); return component; } - _createElementInjector(parentInjector: Injector): Injector { - return Injector.create({ providers: [], parent: parentInjector }); + _createElementInjector(parentInjector: Injector, providers: Provider[] = []): Injector { + return Injector.create({ providers, parent: parentInjector }); } _registerComponentToChangeDetection(component: ComponentRef<any>): void { diff --git a/alfa-client/libs/tech-shared/test/data-test.ts b/alfa-client/libs/tech-shared/test/data-test.ts index 795b2dee30c083f71da5842c2cc64738c3dd9da4..c4fbe01458d666825688364aee1f75b9fac3ea4e 100644 --- a/alfa-client/libs/tech-shared/test/data-test.ts +++ b/alfa-client/libs/tech-shared/test/data-test.ts @@ -35,10 +35,9 @@ export function getDataTestIdOf(value: string): string { return `[data-test-id="${value}"]`; } +/** + * @deprecated use getDataTestIfOf instead and a direct data-test-id at the component + */ export function getDataTestIdAttributeOf(value: string): string { return `[dataTestId="${value}"]`; } - -export function getDynamicDataTestIdAttributOf(value: string): string { - return `[ng-reflect-data-test-id="${value}"]`; -} diff --git a/alfa-client/libs/test-utils/src/lib/dialog.ts b/alfa-client/libs/test-utils/src/lib/dialog.ts index 7e09b7bea501d2f2d52bd1b696a7f27d975efb4f..5a8c5fe4737efc92501377610d333e50af98f5d8 100644 --- a/alfa-client/libs/test-utils/src/lib/dialog.ts +++ b/alfa-client/libs/test-utils/src/lib/dialog.ts @@ -22,7 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { jest } from '@jest/globals'; -import { EMPTY, Observable } from 'rxjs'; +import { EMPTY, Observable, of } from 'rxjs'; export class DialogRefMock<R = unknown> { public keydownEvents: Observable<KeyboardEvent> = EMPTY; @@ -34,3 +34,9 @@ export class DialogRefMock<R = unknown> { export function createDialogRefMock<R = unknown>(): DialogRefMock<R> { return new DialogRefMock(); } + +export function createdClosedDialogRefMock<R = unknown>(result?: R): DialogRefMock<R> { + const dialogRefMock = createDialogRefMock<R>(); + dialogRefMock.closed = of(result); + return dialogRefMock; +} diff --git a/alfa-client/libs/test-utils/src/lib/helper.ts b/alfa-client/libs/test-utils/src/lib/helper.ts index e847268dea4139a32915ce071ddd79fb20da4e39..66f33c3579e18d88743d3b23ea96703a2799bb04 100644 --- a/alfa-client/libs/test-utils/src/lib/helper.ts +++ b/alfa-client/libs/test-utils/src/lib/helper.ts @@ -55,6 +55,11 @@ export function dispatchEventFromFixture<T>(fixture: ComponentFixture<T>, elemen element.nativeElement.dispatchEvent(new Event(event)); } +export function dispatchEventFromFixtureByType<T, C>(fixture: ComponentFixture<T>, component: Type<C>, event: string): void { + const element: DebugElement = getDebugElementFromFixtureByType(fixture, component); + element.nativeElement.dispatchEvent(new Event(event)); +} + export function triggerEvent<T>(eventData: EventData<T>) { const element: DebugElement = getDebugElementFromFixtureByCss(eventData.fixture, eventData.elementSelector); element.triggerEventHandler(eventData.name, eventData.data); diff --git a/alfa-client/libs/test-utils/src/lib/mocking.ts b/alfa-client/libs/test-utils/src/lib/mocking.ts index 0fb616db60420994880e9acfddec1d0d37e10486..20ac6152a4230849892a5f6e5599670b24e05165 100644 --- a/alfa-client/libs/test-utils/src/lib/mocking.ts +++ b/alfa-client/libs/test-utils/src/lib/mocking.ts @@ -53,3 +53,9 @@ export function mockGetValue(object: any, name: string, returnValue: any): void get: jest.fn(() => returnValue), }); } + +export function mockWindowError(): any { + const errorHandler = jest.fn(); + window.onerror = errorHandler; + return errorHandler; +} diff --git a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.html b/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.html index 9ec99cebdafed843b5ded6af4b56c34fb2b04638..f0b1defde6fba19d8ef6144a37945c05dcbc3735 100644 --- a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.html +++ b/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.html @@ -23,11 +23,6 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<ods-dropdown-menu-text-item - class="border-b border-b-grayborder border-t-grayborder bg-whitetext" - title="Benutzerleitfaden" - description="Alle Funktionen der Allgemeinen Fachanwendung (Alfa) erklärt." -> - <ods-file-icon icon fileType="pdf" size="large"></ods-file-icon> - <alfa-open-documentation-button additionalContent [url]="url" data-test-id="documentations-component" /> -</ods-dropdown-menu-text-item> +<ods-dropdown-menu-link-item caption="Benutzerleitfaden Alfa" text="PDF öffnen" [url]="url"> + <ods-file-icon icon fileType="pdf" size="medium" /> +</ods-dropdown-menu-link-item> diff --git a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.spec.ts b/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.spec.ts index b7307c4a9f800117718ab72571c87289e011907c..fb232797a308c689f244460f103deafbabf9c5f3 100644 --- a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.spec.ts +++ b/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/documentation.component.spec.ts @@ -22,10 +22,9 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { DropdownMenuTextItemComponent, FileIconComponent } from '@ods/system'; +import { DropdownMenuLinkItemComponent, FileIconComponent } from '@ods/system'; import { MockComponent } from 'ng-mocks'; import { DocumentationComponent } from './documentation.component'; -import { OpenDocumentationButtonComponent } from './open-documentation-button/open-documentation-button.component'; describe('DocumentationComponent', () => { let component: DocumentationComponent; @@ -33,12 +32,7 @@ describe('DocumentationComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - DocumentationComponent, - MockComponent(OpenDocumentationButtonComponent), - MockComponent(DropdownMenuTextItemComponent), - MockComponent(FileIconComponent), - ], + declarations: [DocumentationComponent, MockComponent(DropdownMenuLinkItemComponent), MockComponent(FileIconComponent)], }).compileComponents(); fixture = TestBed.createComponent(DocumentationComponent); diff --git a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.html b/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.html deleted file mode 100644 index d3f460b90b5c7c7232d01e2b4e0674ddee2a8a8c..0000000000000000000000000000000000000000 --- a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.html +++ /dev/null @@ -1,32 +0,0 @@ -<!-- - - Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - Ministerpräsidenten des Landes Schleswig-Holstein - Staatskanzlei - Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - - Lizenziert unter der EUPL, Version 1.2 oder - sobald - diese von der Europäischen Kommission genehmigt wurden - - Folgeversionen der EUPL ("Lizenz"); - Sie dürfen dieses Werk ausschließlich gemäß - dieser Lizenz nutzen. - Eine Kopie der Lizenz finden Sie hier: - - https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - - Sofern nicht durch anwendbare Rechtsvorschriften - gefordert oder in schriftlicher Form vereinbart, wird - die unter der Lizenz verbreitete Software "so wie sie - ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - ausdrücklich oder stillschweigend - verbreitet. - Die sprachspezifischen Genehmigungen und Beschränkungen - unter der Lizenz sind dem Lizenztext zu entnehmen. - ---> -<ozgcloud-open-url-button - text="Öffnen" - [url]="url" - [targetName]="'_blank'" - [tooltip]="'Öffnet in einem neuen Tab'" - data-test-id="open-documentation-button" -/> diff --git a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.spec.ts b/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.spec.ts deleted file mode 100644 index a57801c4f80be45225f6d78c3789d1cdec31b271..0000000000000000000000000000000000000000 --- a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { OpenDocumentationButtonComponent } from './open-documentation-button.component'; -import { MockComponent } from 'ng-mocks'; -import { OpenUrlButtonComponent } from '@alfa-client/ui'; - -describe('OpenDocumentationButtonComponent', () => { - let component: OpenDocumentationButtonComponent; - let fixture: ComponentFixture<OpenDocumentationButtonComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [OpenDocumentationButtonComponent, MockComponent(OpenUrlButtonComponent)], - }).compileComponents(); - - fixture = TestBed.createComponent(OpenDocumentationButtonComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.ts b/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.ts deleted file mode 100644 index c0719256cfb74979b4cbe95a7c3e3c451cd08af4..0000000000000000000000000000000000000000 --- a/alfa-client/libs/user-assistance/src/lib/help-menu/documentation/open-documentation-button/open-documentation-button.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { Component, Input } from '@angular/core'; - -@Component({ - selector: 'alfa-open-documentation-button', - templateUrl: './open-documentation-button.component.html', - styleUrls: ['./open-documentation-button.component.scss'], -}) -export class OpenDocumentationButtonComponent { - @Input() url: string; -} diff --git a/alfa-client/libs/user-assistance/src/lib/help-menu/help-menu.component.html b/alfa-client/libs/user-assistance/src/lib/help-menu/help-menu.component.html index 24ee1e914732e11ea00e3dad00a7cffea735b000..24583fe97f504dcb3d35279e5a1ab0d932a97ca9 100644 --- a/alfa-client/libs/user-assistance/src/lib/help-menu/help-menu.component.html +++ b/alfa-client/libs/user-assistance/src/lib/help-menu/help-menu.component.html @@ -38,6 +38,7 @@ } @if (apiRootStateResource?.resource?.impressumUrl) { + <div class="h-2"></div> <ods-dropdown-menu-link-item [url]="apiRootStateResource.resource.impressumUrl" text="Impressum" data-test-id="impressum" /> } </ods-dropdown-menu> diff --git a/alfa-client/libs/user-assistance/src/lib/user-assistance.module.ts b/alfa-client/libs/user-assistance/src/lib/user-assistance.module.ts index 63fc01ce61325ce4935a8292df7314a550e83940..f448be3a653b757112bf18d262ff617fcf3600ab 100644 --- a/alfa-client/libs/user-assistance/src/lib/user-assistance.module.ts +++ b/alfa-client/libs/user-assistance/src/lib/user-assistance.module.ts @@ -34,7 +34,6 @@ import { import { MatFabButton } from '@angular/material/button'; import { MatMenuTrigger } from '@angular/material/menu'; import { DocumentationComponent } from './help-menu/documentation/documentation.component'; -import { OpenDocumentationButtonComponent } from './help-menu/documentation/open-documentation-button/open-documentation-button.component'; import { HelpButtonComponent } from './help-menu/help-button/help-button.component'; import { HelpMenuComponent } from './help-menu/help-menu.component'; import { OpenUrlButtonComponent } from '@alfa-client/ui'; @@ -53,7 +52,7 @@ import { OpenUrlButtonComponent } from '@alfa-client/ui'; DropdownMenuLinkItemComponent, OpenUrlButtonComponent, ], - declarations: [HelpMenuComponent, DocumentationComponent, OpenDocumentationButtonComponent, HelpButtonComponent], + declarations: [HelpMenuComponent, DocumentationComponent, HelpButtonComponent], exports: [HelpMenuComponent], }) export class UserAssistanceModule {} diff --git a/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.html b/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.html index a2aac078aaf9d5fc6783864da5b363cf460d9bee..797bf6c78e5323f1914b56376d75b5409c19b3c5 100644 --- a/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.html +++ b/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.html @@ -36,7 +36,7 @@ class="user-profile-icon" > </alfa-user-icon> - <ods-dropdown-menu-button-item caption="Abmelden" (itemClicked)="logoutEmitter.emit()" dataTestId="logout-button"> - <ods-logout-icon icon /> + <ods-dropdown-menu-button-item caption="Abmelden" (clickEmitter)="logoutEmitter.emit()" dataTestId="logout-button"> + <ods-logout-icon icon class="fill-primary" /> </ods-dropdown-menu-button-item> </ods-dropdown-menu> diff --git a/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.spec.ts b/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.spec.ts index ab53c008163ee44d5f785b065da91de6f203363c..2bf68369cdab87155caf1bb0d47489feb1552fc7 100644 --- a/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.spec.ts +++ b/alfa-client/libs/user-profile/src/lib/user-profile-in-header-container/user-profile-in-header/user-profile-in-header.component.spec.ts @@ -22,7 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { createStateResource } from '@alfa-client/tech-shared'; -import { dispatchEventFromFixture, getElementComponentFromFixtureByCss, mock, useFromMock } from '@alfa-client/test-utils'; +import { dispatchEventFromFixture, getElementComponentFromFixtureByCss, mock, MockEvent, useFromMock, } from '@alfa-client/test-utils'; import { UserIconComponent } from '@alfa-client/user-profile'; import { getUserName, UserProfileResource } from '@alfa-client/user-profile-shared'; import { EventEmitter } from '@angular/core'; @@ -90,7 +90,7 @@ describe('UserProfileInHeaderComponent', () => { describe('template', () => { describe('click on logout button', () => { it('should emit logout event', () => { - dispatchEventFromFixture(fixture, logoutButton, 'itemClicked'); + dispatchEventFromFixture(fixture, logoutButton, MockEvent.CLICK); expect(component.logoutEmitter.emit).toHaveBeenCalled(); }); diff --git a/alfa-client/tsconfig.base.json b/alfa-client/tsconfig.base.json index f6bb4c8b2657d8acef026b997d32794971be5656..615131502b18ad4b0d693d9d2baeed734f62c4ce 100644 --- a/alfa-client/tsconfig.base.json +++ b/alfa-client/tsconfig.base.json @@ -78,7 +78,8 @@ "@alfa-client/zustaendige-stelle-shared": ["libs/zustaendige-stelle-shared/src/index.ts"], "@authentication": ["libs/authentication/src/index.ts"], "@ods/component": ["libs/design-component/src/index.ts"], - "@ods/system": ["libs/design-system/src/index.ts"] + "@ods/system": ["libs/design-system/src/index.ts"], + "admin-user-profile": ["libs/admin/admin-user-profile/src/index.ts"] } }, "exclude": ["node_modules", "tmp"]