diff --git a/Jenkinsfile b/Jenkinsfile index ef44d538b0f0f4cf970a4f323f586c31d43b6f5c..c28bf0d2fa1f10b024edee98db52ddecd73f2b6b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -44,6 +44,7 @@ pipeline { } } } + stage('Client') { environment { FORCE_COLOR = 'false' @@ -83,7 +84,29 @@ pipeline { // } // } } - stage('Server') { + + stage('Set Version') { + when { + not { + anyOf { + branch 'master' + branch 'release' + } + } + } + steps { + script { + FAILED_STAGE=env.STAGE_NAME + JAR_TAG = getRootPomVersion().replace("SNAPSHOT", "${env.BRANCH_NAME}-SNAPSHOT") + } + configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { + sh "mvn -s $MAVEN_SETTINGS versions:set -DnewVersion=${JAR_TAG} -DprocessAllModules=true" + + } + } + } + + stage('Build Server artefacts, build and push docker image') { steps { script { FAILED_STAGE=env.STAGE_NAME @@ -120,12 +143,6 @@ pipeline { } } stage('Deploy Maven Artifacts to Nexus') { - when { - anyOf { - branch 'master' - branch 'release' - } - } steps { script { FAILED_STAGE = env.STAGE_NAME @@ -133,6 +150,7 @@ pipeline { configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS -pl -alfa-client -DskipTests deploy' + sh "mvn -s $MAVEN_SETTINGS versions:revert" } } } @@ -143,6 +161,7 @@ pipeline { branch 'release' } } + steps { script { FAILED_STAGE = env.STAGE_NAME diff --git a/alfa-client/apps/admin/project.json b/alfa-client/apps/admin/project.json index e369bed13cdc4d079e36c28c7ed0d5b52b6552a7..9c6af90763bf5aec332dd62f254377b6de58f77a 100644 --- a/alfa-client/apps/admin/project.json +++ b/alfa-client/apps/admin/project.json @@ -29,6 +29,7 @@ "scripts": [], "stylePreprocessorOptions": { "includePaths": [ + "apps/alfa/src/styles/abstracts", "node_modules/@angular", "node_modules/include-media", "node_modules/typeface-roboto" diff --git a/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml b/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml index 7f7cedbca17f4c24972c206bf45d4d079cf771c7..2032db88a659c5b7fcf67aa199d6dba893bf5397 100644 --- a/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml +++ b/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml @@ -28,20 +28,26 @@ release: namespace: by-helm-test templates: - templates/network_policy.yaml -set: - networkPolicy: - dnsServerNamespace: kube-system - ssoPublicIp: 1.1.1.1/32 + tests: - it: should match apiVersion + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - isAPIVersion: of: networking.k8s.io/v1 - it: should match kind + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - isKind: of: NetworkPolicy - it: validate metadata + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - equal: path: metadata @@ -50,6 +56,9 @@ tests: namespace: by-helm-test - it: should add egress rule to administration service + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - contains: path: spec.egress @@ -84,7 +93,6 @@ tests: - it: should add additionalIngressConfig local set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-namespace-dns additionalIngressConfigLocal: - from: @@ -102,7 +110,6 @@ tests: - it: should add additionalIngressConfig global set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-namespace-dns additionalIngressConfigGlobal: - from: @@ -121,7 +128,6 @@ tests: - it: should add additionalEgressConfig local set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-dns-namespace additionalEgressConfigLocal: - to: @@ -138,7 +144,6 @@ tests: - it: should add additionalEgressConfig global set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-dns-namespace additionalEgressConfigGlobal: - to: @@ -164,9 +169,23 @@ tests: - it: test network policy unset should be disabled set: networkPolicy: - ssoPublicIp: 1.1.1.1 disabled: false dnsServerNamespace: test-dns-server-namespace asserts: - hasDocuments: count: 1 + - it: test network policy dnsServerNamespace must be set message + set: + networkPolicy: + disabled: false + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + - it: test network policy should be enabled by default + set: + networkPolicy: + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 \ No newline at end of file diff --git a/alfa-client/apps/alfa-e2e/Jenkinsfile b/alfa-client/apps/alfa-e2e/Jenkinsfile index 56172b92999fb73f5029b83c781f72e74261d364..4d9192728334431b0219afb7c8ac6372cf9bdaab 100644 --- a/alfa-client/apps/alfa-e2e/Jenkinsfile +++ b/alfa-client/apps/alfa-e2e/Jenkinsfile @@ -15,7 +15,7 @@ pipeline { } environment { - BLUE_OCEAN_URL = "https://jenkins.ozg-sh.de/job/E2E%20Tests/job/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/" + BLUE_OCEAN_URL = "https://jenkins.infra.ozg-cloud.systems/job/E2E%20Tests/job/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/" BUNDESLAND = "by" SSO_URL = "sso.dev.by.ozg-cloud.de" CLUSTER_BASE_URL = "dev.by.ozg-cloud.de" diff --git a/alfa-client/apps/alfa-e2e/docker-compose.yml b/alfa-client/apps/alfa-e2e/docker-compose.yml index 552a59c14cb9264c1e1c090cf624ca9303f2566f..e87654d697b9a340f65e9a666dff2fcca1b5ad98 100644 --- a/alfa-client/apps/alfa-e2e/docker-compose.yml +++ b/alfa-client/apps/alfa-e2e/docker-compose.yml @@ -94,8 +94,6 @@ services: - LOGGING_CONFIG=classpath:log4j2-local.xml - BPL_DEBUG_ENABLED=true - BPL_DEBUG_PORT=5000 - # bescheid-wizard - - OZGCLOUD_FEATURE_BESCHEID-WIZARD=true - OZGCLOUD_VORGANG_BESCHEID_0_FORM_ENGINE_NAME=FormSolutions - OZGCLOUD_VORGANG_BESCHEID_0_FORM_ID=KFAS_STAGE_KI_10_Haltverbot_LANDESHACKATHON ports: diff --git a/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts index 005abef8cb58c8fb185e7b7855173fae2e27f5c7..c9fa056dfbab2bc5a36c3d600cbc7c135a78db99 100644 --- a/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts @@ -38,6 +38,7 @@ export class AttachmentContainerE2EComponent { export class AttachmentListE2EComponent { private readonly locatorRoot: string = 'file-list'; + private readonly downloadAttachmentsButton: string = 'download-archive-file-button'; public getRoot() { return cy.getTestElement(this.locatorRoot); @@ -46,6 +47,14 @@ export class AttachmentListE2EComponent { public getItem(fileName: string): AttachmentE2EItem { return new AttachmentE2EItem(fileName); } + + public getDownloadAttachmentsButton(): Cypress.Chainable<JQuery<HTMLElement>> { + return this.getRoot().findTestElementWithClass(this.downloadAttachmentsButton); + } + + public downloadAttachments(): Cypress.Chainable<any> { + return this.getDownloadAttachmentsButton().click(); + } } class AttachmentE2EItem { diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts index 0f0c5cbfe7eaabfa0f4c219b936606fbbe5ccb95..5884ec6fe2f829351178b820cfc5a96e8752c343 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts @@ -22,6 +22,10 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { contains, enterWith } from '../../support/cypress.util'; +import { uploadFile } from '../../support/file-upload'; +import { getAdjustedDateGerman } from '../../support/tech.util'; + export class VorgangBescheidWizardE2EComponent { private readonly bewilligtButton: string = 'button-bewilligt'; private readonly abgelehntButton: string = 'button-abgelehnt'; @@ -44,8 +48,9 @@ export class VorgangBescheidWizardE2EComponent { 'bescheiderstellung-abbrechen-entwurf-verwerfen'; private readonly bescheidSpeichernButton: string = 'bescheiderstellung-abbrechen-entwurf-speichern'; - private readonly uploadBescheidFile: string = '-single-file-upload-button'; - private readonly uploadAttachment: string = 'Anhang_hochladen-file-upload-button'; + private readonly uploadBescheidFileButton: string = '-single-file-upload-button'; + private readonly uploadAttachmentButton: string = 'Anhang_hochladen-file-upload-button'; + private readonly uploadAutomaticBescheid: string = 'create-bescheid-document-button'; private readonly mailTextArea: string = 'Text-textarea-editor'; private readonly saveBescheid: string = 'save-button'; private readonly sendBescheid: string = 'send-button'; @@ -56,6 +61,8 @@ export class VorgangBescheidWizardE2EComponent { private readonly fileBescheidValid: string = 'Bescheid_validpdf-file-item'; private readonly fileAnhangValid: string = 'Anhang_validpdf-file-item'; + private readonly fileAutomaticBescheid: string = + 'KFAS_LIVE_KI_10_Haltverbot_befristetpdf-file-item'; private readonly bescheidDocument: string = 'bescheid-document'; private readonly attachmentDocument: string = 'bescheid-attachments'; @@ -69,6 +76,9 @@ export class VorgangBescheidWizardE2EComponent { private locatorRoot: string = 'bescheid-wizard'; + private readonly bescheidVersenden: string = 'Bescheid versenden'; + private readonly dokumenteHinzufuegen: string = 'Dokumente hinzufügen'; + public getRoot(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.locatorRoot); } @@ -144,11 +154,11 @@ export class VorgangBescheidWizardE2EComponent { } public getUploadBescheidButton(): Cypress.Chainable<JQuery<HTMLElement>> { - return cy.getTestElement(this.uploadBescheidFile); + return cy.getTestElement(this.uploadBescheidFileButton); } public getUploadAttachmentButton(): Cypress.Chainable<JQuery<HTMLElement>> { - return cy.getTestElement(this.uploadAttachment); + return cy.getTestElement(this.uploadAttachmentButton); } public getFileBescheidValidInWizard(): Cypress.Chainable<JQuery<HTMLElement>> { @@ -159,6 +169,10 @@ export class VorgangBescheidWizardE2EComponent { return cy.getTestElement(this.locatorRoot).find(`[data-test-id=${this.fileAnhangValid}]`); } + public getAutomaticBescheidFileInWizard() { + return cy.getTestElement(this.locatorRoot).find(`[data-test-id=${this.fileAutomaticBescheid}]`); + } + public getDeleteButtonOfElement(element: string): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(element).find('[title="Anhang löschen"]'); } @@ -191,8 +205,8 @@ export class VorgangBescheidWizardE2EComponent { return cy.get(this.sendenSpinner); } - public getMailTextArea(): Cypress.Chainable<JQuery<HTMLElement>> { - return cy.get(this.mailTextArea); + public getMailText(): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.getTestElement(this.nachrichtText); } public getSaveButton(): Cypress.Chainable<JQuery<HTMLElement>> { @@ -222,4 +236,61 @@ export class VorgangBescheidWizardE2EComponent { public getNachrichtText(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.nachrichtText); } + + public isBescheidDocumentsStep(): void { + contains(this.getRoot(), this.dokumenteHinzufuegen); + } + + public isBescheidVersendenStep(): void { + contains(this.getRoot(), this.bescheidVersenden); + } + + public ablehnen(): void { + this.getAbgelehntButton().should('exist').click(); + } + + public weiter(): void { + this.getWeiterButton().should('exist').click(); + } + + public close(): void { + this.getCloseButton().should('exist').click(); + } + + public isOpened(): void { + this.getRoot().should('exist'); + } + + public isClosed(): void { + this.getRoot().should('not.exist'); + } + + public getUploadAutomaticBescheidButton() { + return cy.getTestElement(this.uploadAutomaticBescheid); + } + + public uploadBescheid(fileName: string): void { + uploadFile(this.getUploadBescheidButton(), fileName); + } + + public uploadAttachment(fileName: string): void { + uploadFile(this.getUploadAttachmentButton(), fileName); + } + + public bescheidUploadSpinnerIsClosed(): void { + this.getBescheidUploadSpinner().should('not.exist'); + } + + public attachmentSpinnerIsClosed(): void { + this.getAttachmentUploadSpinner().should('not.exist'); + } + + public enterDate(difference: number): void { + this.getDateInput().should('exist'); + enterWith(this.getDateInput(), getAdjustedDateGerman(difference)); + } + + public closeDialogIsShown(): void { + this.getCloseDialog().should('exist'); + } } diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts index 7cbf56b323b60afa837fc79bae7b9b296e28448a..26821f5985776515387d535b6c6060f0773b0c35 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts @@ -30,6 +30,7 @@ import { export class VorgangFormularDatenE2EComponent { private readonly locatorRoot: string = 'formulardaten-panel'; + private readonly locatorDateien: string = 'dateien'; private readonly locatorMetadaten: string = 'metadaten'; private readonly locatorAntragdaten: string = 'antragdaten'; private readonly locatorTabset: string = 'div[role=tab]'; @@ -39,11 +40,14 @@ export class VorgangFormularDatenE2EComponent { private readonly historieContainer: VorgangFormularDatenHistorieE2EComponent = new VorgangFormularDatenHistorieE2EComponent(); + private readonly fileListHeader: string = 'file-list'; + private readonly attachmentListHeader: string = 'attachment-list'; + private readonly antragdatenTab: number = 0; - private readonly metadatenTab: number = 1; - private readonly representationTab: number = 2; - private readonly attachmentTab: number = 3; - private readonly historieTab: number = 4; + private readonly metadatenTab: number = 9; + private readonly representationTab: number = 9; + private readonly dateienTab: number = 1; + private readonly historieTab: number = 2; public getRoot() { return cy.getTestElement(this.locatorRoot); @@ -65,8 +69,8 @@ export class VorgangFormularDatenE2EComponent { return this.getTabsetElement().eq(this.representationTab); } - public getAttachmentTab() { - return this.getTabsetElement().eq(this.attachmentTab); + public getDateienTab() { + return this.getTabsetElement().eq(this.dateienTab); } public getHistorieTab() { @@ -85,6 +89,14 @@ export class VorgangFormularDatenE2EComponent { return getTestElement(this.locatorAntragdaten); } + public getDateien() { + return getTestElement(this.locatorDateien); + } + + public getTabset() { + return getTestElement(this.locatorTabset); + } + public getHistorieContainer(): VorgangFormularDatenHistorieE2EComponent { return this.historieContainer; } @@ -92,4 +104,17 @@ export class VorgangFormularDatenE2EComponent { public getHistorieItemByIndex(index: number): VorgangFormularDatenHistorieItemE2EComponent { return this.getHistorieContainer().getListItemByIndex(index); } + + public getFileListHeader() { + return getTestElement(this.fileListHeader); + } + + public getAttachmentListHeader() { + return getTestElement(this.attachmentListHeader); + } + + public getFileElementByName(fileName: string) { + fileName = fileName.replace(/\./g, '') + '-file-item'; + return getTestElement(fileName); + } } diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts index d879ee9fc1c21ef8e1318dad1ca87283118e2439..fba7e4dfd79867f2b53aeda47cd108742548cce1 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { enterWith } from '../../support/cypress.util'; import { convertToDataTestId } from '../../support/tech.util'; export class VorgangSearchE2EComponent { @@ -36,7 +37,7 @@ export class VorgangSearchE2EComponent { return cy.getTestElement(this.locatorRoot); } - public getInput() { + public getInput(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.locatorInput); } @@ -61,6 +62,10 @@ export class VorgangSearchE2EComponent { public getClearButton() { return cy.getTestElement(this.locatorClearButton); } + + public doSearch(searchString: string): void { + enterWith(this.getInput(), searchString, 300); + } } export class VorgangSearchPreviewListItemE2EComponent { diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-views.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-views.e2e.component.ts index f1240912ebb55269bfea578cd2a72a23187a61a6..446c287de76ee0c62fd746f6df9241c95ed0ce92 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-views.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-views.e2e.component.ts @@ -87,6 +87,6 @@ export class VorgangViewE2EComponent { } public getButton() { - return this.getRoot().getTestElementWithClass(this.button); + return this.getRoot().findTestElementWithClass(this.button); } } diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang.formular-daten.historie.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang.formular-daten.historie.e2e.component.ts index 2b0fe296fec1a11d59f843624c441ae14788dea9..d4b039afbb15779a709339c390a2a4f6d8c70e73 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang.formular-daten.historie.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang.formular-daten.historie.e2e.component.ts @@ -205,14 +205,14 @@ export class BescheidHistorieItemE2EComponent { } public getStatus() { - return cy.getTestElementWithClass(this.status); + return this.getRoot().findTestElementWithClass(this.status); } public getBescheidDokumentText() { - return cy.getTestElementWithClass(this.bescheidDokument); + return this.getRoot().findTestElementWithClass(this.bescheidDokument); } public getBescheidAnhaengeText() { - return cy.getTestElementWithClass(this.bescheidAnhaenge); + return this.getRoot().findTestElementWithClass(this.bescheidAnhaenge); } } diff --git a/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-loeschen-anfordern.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-loeschen-anfordern.cy.ts index 91617d3639ed07f3107ce74e274de36b9d319262..c22d4da4f7d654fdbf843bc28381da807dd31771 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-loeschen-anfordern.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-loeschen-anfordern.cy.ts @@ -32,7 +32,7 @@ import { } from '../../../model/vorgang'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { dropCollections } from '../../../support/cypress-helper'; +import { dropCollections, wait } from '../../../support/cypress-helper'; import { contains, exist, haveText, notExist } from '../../../support/cypress.util'; import { loginAsEmil } from '../../../support/user-util'; import { buildVorgang, initVorgaenge, objectIds } from '../../../support/vorgang-util'; @@ -167,6 +167,8 @@ describe('Vorgang löschen anfordern', () => { it('should show status Abgeschlossen', () => { notExist(vorgangPage.getProgressBar()); + wait(2000, 'Statusänderung flaky'); + cy.scrollTo(0, -500); haveText( vorgangPage.getVorgangDetailHeader().getStatus(), @@ -277,6 +279,10 @@ describe('Vorgang löschen anfordern', () => { it('should show status Verworfen', () => { notExist(vorgangPage.getProgressBar()); + wait(2000, 'Statusänderung im UI nicht schnell genug'); + cy.scrollTo(0, -500); + + vorgangPage.getVorgangDetailHeader().getStatus(); haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.VERWORFEN], diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/loesch-anforderung/loeschen-anfordern.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/loesch-anforderung/loeschen-anfordern.cy.ts index 6ae63d71c0d8c4d0c7477a53749f5784fcd19a96..6eb2921858a19d25aeb0423fd4c03a2a4378831e 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/loesch-anforderung/loeschen-anfordern.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/loesch-anforderung/loeschen-anfordern.cy.ts @@ -21,7 +21,7 @@ import { } from '../../../model/vorgang'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { dropCollections, visitUrl } from '../../../support/cypress-helper'; +import { dropCollections, visitUrl, wait } from '../../../support/cypress-helper'; import { contains, exist, haveText, notExist } from '../../../support/cypress.util'; import { loginAsLudwig, loginAsSabine } from '../../../support/user-util'; import { buildVorgang, initVorgaenge, initVorgang, objectIds } from '../../../support/vorgang-util'; @@ -161,6 +161,9 @@ describe('Vorgang Löschen anfordern', () => { }); it('should have status zu Löschen', () => { + wait(2000, 'Statusänderung im UI nicht schnell genug'); + cy.scrollTo(0, -500); + haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.ZU_LOESCHEN], @@ -299,6 +302,9 @@ describe('Vorgang Löschen anfordern', () => { it('should show status Abgeschlossen', () => { notExist(vorgangPage.getProgressBar()); + wait(2000, 'Statusänderung im UI nicht schnell genug'); + cy.scrollTo(0, -500); + haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.ABGESCHLOSSEN], @@ -408,6 +414,9 @@ describe('Vorgang Löschen anfordern', () => { it('should show status Verworfen', () => { notExist(vorgangPage.getProgressBar()); + wait(2000, 'Statusänderung im UI nicht schnell genug'); + cy.scrollTo(0, -500); + haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.VERWORFEN], diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/anhang-herunterladen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/vorgang-anhang-herunterladen.cy.ts similarity index 86% rename from alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/anhang-herunterladen.cy.ts rename to alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/vorgang-anhang-herunterladen.cy.ts index 69dfac76104a5ea135bb876ffc97766877219999..946a0c86da3a2c4dade2896dbaeb09ab0734084a 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/anhang-herunterladen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/vorgang-anhang-herunterladen.cy.ts @@ -24,6 +24,7 @@ import { AttachmentContainerE2EComponent } from 'apps/alfa-e2e/src/components/attachment/attachment.e2e.component'; import { VorgangFormularDatenE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component'; import { convertToDataTestId } from 'apps/alfa-e2e/src/support/tech.util'; +import { AttachmentListE2EComponent } from '../../../components/attachment/attachment.e2e.component'; import { VorgangDetailHeaderE2EComponent } from '../../../components/vorgang/vorgang-detail-header.e2e.component'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { VorgangSubnavigationE2EComponent } from '../../../components/vorgang/vorgang-subnavigation'; @@ -40,7 +41,14 @@ import { createXmlRepresentation, initGridFs, } from '../../../support/binary-file-util'; -import { dropCollections, readFileFromDownloads } from '../../../support/cypress-helper'; +import { + countDownloadFiles, + deleteDownloadFolder, + dropCollections, + getDownloadFiles, + readFileFromDownloads, + unzipDownloadFile, +} from '../../../support/cypress-helper'; import { exist, notExist } from '../../../support/cypress.util'; import { loginAsSabine } from '../../../support/user-util'; import { @@ -58,8 +66,11 @@ describe('Vorgang Anhänge', () => { const vorgangHeader: VorgangDetailHeaderE2EComponent = vorgangPage.getVorgangDetailHeader(); const subnavigation: VorgangSubnavigationE2EComponent = vorgangPage.getSubnavigation(); const attachmentContainer: AttachmentContainerE2EComponent = vorgangPage.getAttachmentContainer(); + const attachmentList: AttachmentListE2EComponent = new AttachmentListE2EComponent(); const formularDatenContainer: VorgangFormularDatenE2EComponent = vorgangPage.getFormularDatenContainer(); + const vorgangDatenFormular: VorgangFormularDatenE2EComponent = + vorgangPage.getFormularDatenContainer(); const xmlRepresentation: FileDataE2E = createXmlRepresentation(); @@ -104,7 +115,7 @@ describe('Vorgang Anhänge', () => { }); it('should show attachment on click on tab', () => { - formularDatenContainer.getAttachmentTab().click(); + formularDatenContainer.getDateienTab().click(); notExist(vorgangPage.getSpinner()); exist(attachmentContainer.getList().getRoot()); @@ -130,6 +141,22 @@ describe('Vorgang Anhänge', () => { exist(readFileFromDownloads(buildDownloadFileName(pdfAttachmentName))); }); + it('should download attachment zip file', () => { + deleteDownloadFolder().then(() => { + attachmentList.downloadAttachments(); + }); + }); + + it('should unzip attachment file', () => { + getDownloadFiles().then((files) => { + unzipDownloadFile(files[0]).then(() => { + countDownloadFiles().then((count) => { + expect(count).to.eq(3); + }); + }); + }); + }); + it('should navigate to vorganglist on back button', () => { subnavigation.getBackButton().click(); waitForSpinnerToDisappear(); @@ -155,7 +182,7 @@ describe('Vorgang Anhänge', () => { }); it('should show attachment on click on tab', () => { - formularDatenContainer.getRepresentationTab().click(); + formularDatenContainer.getDateienTab().click(); notExist(vorgangPage.getSpinner()); exist(attachmentContainer.getList().getRoot()); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts index c2943c6ef548e9872c2e2e6ad16eefff928ed4c6..81ce0e4386153c2cc241067a25805d9e5ac2d97f 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts @@ -15,7 +15,7 @@ import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-lis import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; import { dropCollections } from '../../../support/cypress-helper'; -import { contains, enterWith, exist, haveText, notExist } from '../../../support/cypress.util'; +import { contains, exist, haveText, notExist } from '../../../support/cypress.util'; import { TEST_FILE_BESCHEID_ANHANG_VALID, TEST_FILE_BESCHEID_VALID, @@ -25,12 +25,13 @@ import { buildVorgang, initVorgaenge, objectIds } from '../../../support/vorgang registerLocaleData(localeDe, 'de', localeDeExtra); -describe('Bescheid abbrechen', () => { +describe('Vorgang - Bescheid abbrechen', () => { const mainPage: MainPage = new MainPage(); const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); const bewilligtText: string = 'Bewilligt am'; const abgelehntText: string = 'Abgelehnt am'; + const stepCaption2: string = 'Dokumente hinzufügen'; const vorgangPage: VorgangPage = new VorgangPage(); const bescheidWizard: VorgangBescheidWizardE2EComponent = vorgangPage.getBescheidWizard(); @@ -57,59 +58,98 @@ describe('Bescheid abbrechen', () => { dropCollections(); }); - describe('Discard changes', () => { - it('should discard all data after clicking X and confirm dicard', () => { + describe.skip('Sollte gefixt sein nach OZG-6185 - Discard changes after click on Verwerfen', () => { + it('should click abgelehnt, enter date, and continue to step 2', () => { vorgangList.getListItem(bescheidVorgang.name).getRoot().click(); waitForSpinnerToDisappear(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); - enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-1)); - bescheidWizard.getWeiterButton().click(); - uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); - notExist(bescheidWizard.getBescheidUploadSpinner()); - uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); - notExist(bescheidWizard.getAttachmentUploadSpinner()); - bescheidWizard.getWeiterButton().click(); - bescheidWizard.getCloseButton().click(); - exist(bescheidWizard.getCloseDialog()); + bescheidWizard.ablehnen(); + bescheidWizard.enterDate(-1); + bescheidWizard.weiter(); + bescheidWizard.isBescheidDocumentsStep(); + }); + + it('should upload files and continue to step 3', () => { + bescheidWizard.uploadBescheid(TEST_FILE_BESCHEID_VALID); + bescheidWizard.bescheidUploadSpinnerIsClosed(); + bescheidWizard.uploadAttachment(TEST_FILE_BESCHEID_ANHANG_VALID); + bescheidWizard.attachmentSpinnerIsClosed(); + bescheidWizard.weiter(); + + bescheidWizard.isBescheidVersendenStep(); + }); + + it('should open dialog after click on X', () => { + bescheidWizard.close(); + + bescheidWizard.closeDialogIsShown(); + }); + + it('should close Wizard after click on Verwerfen', () => { bescheidWizard.getCloseVerwerfenButton().click(); - notExist(bescheidWizard.getRoot()); + waitForSpinnerToDisappear(); + + bescheidWizard.isClosed(); + }); + + it('should show status In Bearbeitung', () => { haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG], ); + }); + }); + describe('Keep changes after click on Entwurf speichern', () => { + it('click Vorgang', () => { + vorgangList.getListItem(bescheidVorgang.name).getRoot().click(); + waitForSpinnerToDisappear(); + }); + + it('should have default Bescheid status and continue to step 2', () => { vorgangFormularButtons.getBescheidenButton().click(); contains(bescheidWizard.getStatusText(), bewilligtText + ' ' + getAdjustedDateGerman(0)); - bescheidWizard.getWeiterButton().click(); + bescheidWizard.weiter(); + }); + + it('should not show file from previous Bescheid', () => { notExist(bescheidWizard.getFileAnhangValidInWizard()); notExist(bescheidWizard.getFileBescheidValidInWizard()); }); - it('should save all data after clicking X and confirm save', () => { - bescheidWizard.getAbgelehntButton().click(); - enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-1)); - bescheidWizard.getWeiterButton().click(); + it('should upload Bescheid and attachment', () => { uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); notExist(bescheidWizard.getBescheidUploadSpinner()); uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); notExist(bescheidWizard.getAttachmentUploadSpinner()); - bescheidWizard.getCloseButton().click(); + }); + it('should open dialog after click on X', () => { + bescheidWizard.close(); + exist(bescheidWizard.getCloseDialog()); + }); + + it('should close Wizard after click on Speichern', () => { bescheidWizard.getCloseSpeichernButton().click(); waitForSpinnerToDisappear(); notExist(bescheidWizard.getRoot()); + }); + it('should show status In Bearbeitung', () => { haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG], ); + }); + it('should keep date and status from current Bescheid in progress', () => { vorgangFormularButtons.getBescheidenButton().click(); - contains(bescheidWizard.getStatusText(), abgelehntText + ' ' + getAdjustedDateGerman(-1)); + contains(bescheidWizard.getStatusText(), bewilligtText + ' ' + getAdjustedDateGerman(0)); + }); - bescheidWizard.getWeiterButton().click(); + it('should keep files from current Bescheid in progress', () => { + bescheidWizard.weiter(); exist(bescheidWizard.getFileAnhangValidInWizard()); exist(bescheidWizard.getFileBescheidValidInWizard()); }); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-automatisch-erstellen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-automatisch-erstellen.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2a6b888ce2b2bad7055322952440965512d27b5 --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-automatisch-erstellen.cy.ts @@ -0,0 +1,121 @@ +import { registerLocaleData } from '@angular/common'; +import localeDe from '@angular/common/locales/de'; +import localeDeExtra from '@angular/common/locales/extra/de'; +import { VorgangBescheidWizardE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component'; +import { VorgangFormularButtonsE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular-buttons.e2e.components'; +import { VorgangSubnavigationE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-subnavigation'; +import { SmockerMocks } from 'apps/alfa-e2e/src/model/smocker'; +import { + EingangE2E, + EingangHeaderE2E, + VorgangE2E, + VorgangStatusE2E, +} from 'apps/alfa-e2e/src/model/vorgang'; +import 'cypress-real-events/support'; +import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; +import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; +import { VorgangPage } from '../../../page-objects/vorgang.po'; +import { addSmockerMock, dropCollections } from '../../../support/cypress-helper'; +import { exist, haveText, notExist } from '../../../support/cypress.util'; +import { initUsermanagerUsers, loginAsSabine } from '../../../support/user-util'; +import { + buildVorgang, + createVorgang, + initVorgaenge, + objectIds, +} from '../../../support/vorgang-util'; + +registerLocaleData(localeDe, 'de', localeDeExtra); + +//TODO: Jenkins konfigurieren +describe.skip('Upload automatic Bescheid', () => { + const mainPage: MainPage = new MainPage(); + const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); + + const vorgangPage: VorgangPage = new VorgangPage(); + const bescheidWizard: VorgangBescheidWizardE2EComponent = vorgangPage.getBescheidWizard(); + + const formIdP: string = 'KFAS_STAGE_KI_10_Haltverbot_LANDESHACKATHON'; + const formEngineName: string = 'FormSolutions'; + const eingangHeader: EingangHeaderE2E = { ...createVorgang().eingangs[0].header }; + + const eingangP: EingangE2E = { + ...createVorgang().eingangs[0], + header: { ...eingangHeader, formId: formIdP, formEngineName }, + }; + + const bescheidAutomatik: VorgangE2E = { + ...createVorgang(), + status: VorgangStatusE2E.IN_BEARBEITUNG, + name: 'automatischerBescheid', + eingangs: [eingangP], + }; + + const standardVorgang: VorgangE2E = { + ...buildVorgang(objectIds[1], 'Standard Vorgang'), + status: VorgangStatusE2E.IN_BEARBEITUNG, + }; + + const vorgangFormularButtons: VorgangFormularButtonsE2EComponent = + vorgangPage.getFormularButtons(); + + const vorgangSubnavigation: VorgangSubnavigationE2EComponent = vorgangPage.getSubnavigation(); + + before(() => { + initVorgaenge([bescheidAutomatik, standardVorgang]); + initUsermanagerUsers(); + + addSmockerMock(SmockerMocks.SMARTDOCUMENT_MOCK); + + loginAsSabine(); + + waitForSpinnerToDisappear(); + exist(vorgangList.getRoot()); + }); + + after(() => { + dropCollections(); + }); + + describe.skip('Upload automatic Bescheid document', () => { + it('should show automatic Bescheid button', () => { + vorgangList.getListItem(bescheidAutomatik.name).getRoot().click(); + waitForSpinnerToDisappear(); + + vorgangFormularButtons.getBescheidenButton().click(); + bescheidWizard.getWeiterButton().click(); + exist(bescheidWizard.getUploadAutomaticBescheidButton()); + }); + + it('should upload automatic Bescheid file', () => { + waitForSpinnerToDisappear(); + bescheidWizard.getUploadAutomaticBescheidButton().click(); + + exist(bescheidWizard.getBescheidUploadSpinner()); + notExist(bescheidWizard.getBescheidUploadSpinner()); + exist(bescheidWizard.getAutomaticBescheidFileInWizard()); + }); + + it('should show automatic header and text in step 3', () => { + bescheidWizard.getWeiterButton().click(); + waitForSpinnerToDisappear(); + + haveText(bescheidWizard.getMailText(), ''); + }); + }); + + describe('Do not show automatic upload button on other Vorgang', () => { + it('should not show button on Vorgang with different formID', () => { + bescheidWizard.getCloseButton().click(); + bescheidWizard.getCloseVerwerfenButton().click(); + vorgangSubnavigation.getBackButton().click(); + vorgangList.getListItem(standardVorgang.name).getRoot().click(); + waitForSpinnerToDisappear(); + + vorgangFormularButtons.getBescheidenButton().click(); + bescheidWizard.getWeiterButton().click(); + + notExist(bescheidWizard.getUploadAutomaticBescheidButton()); + }); + }); +}); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts index 648de8c2c130a59e8e59795599410515ebf92974..6e114b91917aa3a237f1eff680e867053633b652 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts @@ -47,7 +47,7 @@ describe('Bescheid Dokumente hochladen', () => { const antragstellerName: string = 'An: Max Testermann'; const betreffText: string = 'Ihr Bescheid zum Antrag'; const nachrichtText: string = - 'Sehr geehrte/r Antragsteller/in,\n\nim folgenden erhalten Sie Ihren Bescheid.\n\nMit freundlichen Grüßen\n\nIhre Verwaltung'; + 'Sehr geehrte/r Antragsteller/in,\n\nim Folgenden erhalten Sie Ihren Bescheid.\n\nMit freundlichen Grüßen\n\nIhre Verwaltung'; before(() => { initVorgaenge([bescheidVorgang]); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts index dd926439b5d110258be4ea3f702018343668ea53..8a72acae98a5c36a1fe67582333a156466723c4b 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts @@ -14,13 +14,17 @@ import { TEST_FILE_BESCHEID_ANHANG_VALID, TEST_FILE_BESCHEID_VALID, } from 'apps/alfa-e2e/src/support/data.util'; -import { uploadFile } from 'apps/alfa-e2e/src/support/file-upload'; import { getAdjustedDateGerman } from 'apps/alfa-e2e/src/support/tech.util'; import 'cypress-real-events/support'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { countDownloadFiles, dropCollections } from '../../../support/cypress-helper'; +import { + countDownloadFiles, + deleteDownloadFolder, + dropCollections, + readFileFromDownloads, +} from '../../../support/cypress-helper'; import { contains, enterWith, @@ -75,51 +79,79 @@ describe('Bescheid Info anzeigen', () => { }); describe('Show Bescheid Entwurf info on details page', () => { - it('should show Entwurf on details page', () => { + it('should not show Bescheid info in list', () => { notExist(bescheide.getBescheidListItem()); + }); + + it('should open Wizard', () => { vorgangList.getListItem(bescheidVorgang.name).getRoot().click(); waitForSpinnerToDisappear(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); - enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-1)); - bescheidWizard.getWeiterButton().click(); - bescheidWizard.getCloseButton().click(); + + bescheidWizard.isOpened(); + }); + + it('should click abgelehnt, set date and continue to step 2', () => { + bescheidWizard.ablehnen(); + bescheidWizard.enterDate(-1); + bescheidWizard.weiter(); + bescheidWizard.isBescheidDocumentsStep(); + }); + + it('should close Wizard', () => { + bescheidWizard.close(); bescheidWizard.getCloseSpeichernButton().click(); + bescheidWizard.isClosed(); + }); + + it('should show status In Bearbeitung', () => { haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG], ); + }); + it('should show Bescheid as Entwurf with abgelehnt status and date yesterday', () => { exist(bescheide.getBescheidContainer()); - contains(bescheide.getBescheidContainer(), abgelehntText + ' ' + getAdjustedDateGerman(-1)); - contains(bescheide.getBescheidContainer(), entwurfText); }); it('should show caption Bescheid fortsetzen on button', () => { contains(vorgangFormularButtons.getBescheidenButton(), fortsetzenText); }); + }); - it('should show complete info after saving Bescheid', () => { + describe('should show complete info after saving Bescheid', () => { + it('should set date to two days before and continue to step 2', () => { vorgangFormularButtons.getBescheidenButton().click(); enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-2)); bescheidWizard.getWeiterButton().click(); - uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); - notExist(bescheidWizard.getBescheidUploadSpinner()); - uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); - notExist(bescheidWizard.getAttachmentUploadSpinner()); + bescheidWizard.isBescheidDocumentsStep(); + }); - bescheidWizard.getWeiterButton().click(); + it('should upload files and continue to step 3', () => { + bescheidWizard.uploadBescheid(TEST_FILE_BESCHEID_VALID); + bescheidWizard.bescheidUploadSpinnerIsClosed(); + bescheidWizard.uploadAttachment(TEST_FILE_BESCHEID_ANHANG_VALID); + bescheidWizard.attachmentSpinnerIsClosed(); + bescheidWizard.weiter(); + + bescheidWizard.isBescheidVersendenStep(); + }); + + it('should save Bescheid and close wizard', () => { bescheidWizard.getSaveButton().click(); bescheidWizard.getConfirmAndSaveButton().click(); waitForSpinnerToDisappear(); + bescheidWizard.isClosed(); + }); + it('should show Bescheid as abgelehnt, and contain both files', () => { exist(bescheide.getBescheidContainer()); contains(bescheide.getBescheidContainer(), abgelehntText); - exist(bescheide.getFileBescheidValidInVorgang()); exist(bescheide.getFileAnhangValidInVorgang()); }); @@ -139,21 +171,35 @@ describe('Bescheid Info anzeigen', () => { }); it('should download both attachments', () => { - bescheide.getFileBescheidValidInVorgang().click(); - bescheide.getFileAnhangValidInVorgang().click(); - countDownloadFiles().then((count) => { - expect(count).to.eq(2); + deleteDownloadFolder().then(() => { + bescheide.getFileBescheidValidInVorgang().click(); + bescheide.getFileAnhangValidInVorgang().click(); + readFileFromDownloads(TEST_FILE_BESCHEID_ANHANG_VALID).then(() => { + countDownloadFiles().then((count) => { + expect(count).to.eq(2); + }); + }); }); }); + }); - it('should show second Bescheid after reopening Vorgang and creating new Bescheid', () => { + describe('should show second Bescheid after reopening Vorgang and creating new Bescheid', () => { + it('create new Bescheid and set it to abgelehnt', () => { vorgangFormularButtons.getWiedereroeffnenButton().click(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); - bescheidWizard.getWeiterButton().click(); - uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); - notExist(bescheidWizard.getBescheidUploadSpinner()); - bescheidWizard.getWeiterButton().click(); + bescheidWizard.ablehnen(); + bescheidWizard.weiter(); + bescheidWizard.isBescheidDocumentsStep(); + }); + + it('should upload Bescheid and continue to step 3', () => { + bescheidWizard.uploadBescheid(TEST_FILE_BESCHEID_VALID); + bescheidWizard.bescheidUploadSpinnerIsClosed(); + bescheidWizard.weiter(); + bescheidWizard.isBescheidVersendenStep(); + }); + + it('should show 2 Bescheid containers after saving', () => { bescheidWizard.getSaveButton().click(); bescheidWizard.getConfirmAndSaveButton().click(); exist(bescheidWizard.getBescheidSaveSpinner()); @@ -167,8 +213,10 @@ describe('Bescheid Info anzeigen', () => { exist(bescheide.getAbgelehntIconInHeader()); contains(bescheide.getBeschiedenDateInHeader(), getAdjustedDateGerman(0)); }); + }); - it('should show info on list page', () => { + describe('show Bescheid info on Vorgang list', () => { + it('should show Bescheid info on list page', () => { vorgangSubnavigationButtons.getBackButton().click(); exist(bescheide.getBescheidListItem()); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts index ee5b49ac5ea8a9551b58a159f2007fdfeaef868b..20f733647019d1d1f7bb2f70ecb7aff0206291cd 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts @@ -26,11 +26,11 @@ import { buildVorgang, initVorgaenge, objectIds } from '../../../support/vorgang registerLocaleData(localeDe, 'de', localeDeExtra); -describe.skip('Bescheid senden', () => { +describe('Bescheid senden', () => { const mainPage: MainPage = new MainPage(); const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); - const abgelehntText: string = 'Ihr Antrag wurde abgelehnt'; + const anredeText: string = 'Sehr geehrte/r Antragsteller/in,'; const mailText: string = 'Ihr Bescheid zum Antrag'; const vorgangPage: VorgangPage = new VorgangPage(); @@ -67,7 +67,6 @@ describe.skip('Bescheid senden', () => { waitForSpinnerToDisappear(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(1)); bescheidWizard.getWeiterButton().click(); uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); @@ -86,13 +85,11 @@ describe.skip('Bescheid senden', () => { it('should contain correct text and both attachments', () => { const postfachMailItem: PostfachMailListItem = postfachMailContainer.getListItem(mailText); - postfachMailItem.getRoot().click(); waitForSpinnerToDisappear(); const postfachListItem: PostfachMailListItem = postfachMailPage.getListItem(mailText); - - contains(postfachMailPage.getMailText(), abgelehntText); + contains(postfachMailPage.getMailText(), anredeText); exist( postfachListItem diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-dateien-tab.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-dateien-tab.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..85fe66298c9e1653dd2d47ddb8b6e4108c71fc2c --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-dateien-tab.cy.ts @@ -0,0 +1,133 @@ +import { registerLocaleData } from '@angular/common'; +import localeDe from '@angular/common/locales/de'; +import localeDeExtra from '@angular/common/locales/extra/de'; +import { AttachmentListE2EComponent } from 'apps/alfa-e2e/src/components/attachment/attachment.e2e.component'; +import { VorgangFormularDatenE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component'; +import { VorgangSubnavigationE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-subnavigation'; +import { FileDataE2E } from 'apps/alfa-e2e/src/model/binary-file'; +import { + createJpgAttachment, + createPdfAttachment, + createXmlRepresentation, + initGridFs, +} from 'apps/alfa-e2e/src/support/binary-file-util'; +import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; +import { EingangE2E, VorgangE2E } from '../../../model/vorgang'; +import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; +import { VorgangPage } from '../../../page-objects/vorgang.po'; +import { + createJpgGridFsData, + createPdfGridFsData, + createXmlGridFsData, +} from '../../../support/binary-file-util'; +import { dropCollections } from '../../../support/cypress-helper'; +import { contains, exist, notExist } from '../../../support/cypress.util'; +import { loginAsSabine } from '../../../support/user-util'; +import { + buildVorgang, + createVorgang, + initVorgaenge, + objectIds, +} from '../../../support/vorgang-util'; + +registerLocaleData(localeDe, 'de', localeDeExtra); + +describe('Dateien Tab', () => { + const mainPage: MainPage = new MainPage(); + const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); + + const vorgangPage: VorgangPage = new VorgangPage(); + const vorgangDatenFormular: VorgangFormularDatenE2EComponent = + vorgangPage.getFormularDatenContainer(); + const vorgangSubnavigation: VorgangSubnavigationE2EComponent = vorgangPage.getSubnavigation(); + const attachmentList: AttachmentListE2EComponent = new AttachmentListE2EComponent(); + + const xmlFileName: string = 'XML-Daten.xml'; + const jpgFileName: string = 'win.jpg'; + const pdfFileName: string = 'Anlage_Vollmacht.pdf'; + + const xmlRepresentation: FileDataE2E = createXmlRepresentation(); + + const sonstigeAttachment = { + name: 'datei_sonstiges', + files: [createJpgAttachment(), createPdfAttachment()], + }; + + const eingangWithRepresentation: EingangE2E = { + ...createVorgang().eingangs[0], + numberOfRepresentations: 1, + representations: [xmlRepresentation], + }; + + const vorgangRepresentation: VorgangE2E = { + ...buildVorgang(objectIds[0], 'VorgangWithRepresentation'), + eingangs: [eingangWithRepresentation], + }; + + const eingangWithAttachments: EingangE2E = { + ...createVorgang().eingangs[0], + numberOfRepresentations: 1, + representations: [xmlRepresentation], + numberOfAttachments: 2, + attachments: [sonstigeAttachment], + }; + + const vorgangWithAttachments: VorgangE2E = { + ...buildVorgang(objectIds[1], 'VorgangWithAttachments'), + eingangs: [eingangWithAttachments], + }; + + before(() => { + initGridFs([createXmlGridFsData(), createPdfGridFsData(), createJpgGridFsData()]); + initVorgaenge([vorgangRepresentation, vorgangWithAttachments]); + + loginAsSabine(); + + waitForSpinnerToDisappear(); + exist(vorgangList.getRoot()); + }); + + after(() => { + dropCollections(); + }); + + describe('show Dateien contents', () => { + it('should not show Metadaten or Representation tab', () => { + vorgangList.getListItem(vorgangRepresentation.name).getRoot().click(); + waitForSpinnerToDisappear(); + + notExist(vorgangDatenFormular.getMetadatenTab()); + notExist(vorgangDatenFormular.getRepresentationTab()); + }); + it('should show 1 file in header of Dateien tab', () => { + contains(vorgangDatenFormular.getDateienTab(), '(1)'); + }); + + it('should only show sub-header for Antrag', () => { + exist(vorgangDatenFormular.getFileListHeader()); + notExist(vorgangDatenFormular.getAttachmentListHeader()); + }); + + it('should show 1 XML file for Antragsdetails', () => { + exist(vorgangDatenFormular.getFileElementByName(xmlFileName)); + }); + + it('should show 3 files in header of Dateien tab', () => { + vorgangSubnavigation.getBackButton().click(); + vorgangList.getListItem(vorgangWithAttachments.name).getRoot().click(); + + contains(vorgangDatenFormular.getDateienTab(), '(3)'); + }); + + it('should show sub-headers for Antrag and Anhänge', () => { + exist(vorgangDatenFormular.getFileListHeader()); + exist(vorgangDatenFormular.getAttachmentListHeader()); + }); + + it('should show attachments and download button', () => { + exist(vorgangDatenFormular.getFileElementByName(jpgFileName)); + exist(vorgangDatenFormular.getFileElementByName(pdfFileName)); + exist(attachmentList.getDownloadAttachmentsButton(vorgangDatenFormular.getRoot())); + }); + }); +}); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts index 926d415508c42364a82a165d6f33c6fbdab5b246..768acbc0f79869d9a834fd236fcc25232232808e 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts @@ -24,6 +24,7 @@ import { registerLocaleData } from '@angular/common'; import localeDe from '@angular/common/locales/de'; import localeDeExtra from '@angular/common/locales/extra/de'; +import { SnackBarE2EComponent } from 'apps/alfa-e2e/src/components/ui/snackbar.e2e.component'; import { VorgangFormularButtonsE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular-buttons.e2e.components'; import { VorgangMoreMenuE2EComponent, @@ -46,6 +47,8 @@ registerLocaleData(localeDe, 'de', localeDeExtra); describe('Vorgang exportieren', () => { const mainPage: MainPage = new MainPage(); + const snackBar: SnackBarE2EComponent = mainPage.getSnackBar(); + const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); const vorgangPage: VorgangPage = new VorgangPage(); @@ -100,14 +103,24 @@ describe('Vorgang exportieren', () => { vorgangMoreMenu.getButton().click({ force: true }); }); - it('should show "Herunterladen" button in Abgeschlossen status', () => { + it('should handle snackbar after abschliessen', () => { vorgangFormularButtons.getAbschliessenButton().click(); + waitForSpinnerToDisappear(); + + exist(snackBar.getCloseButton()); + snackBar.getCloseButton().click(); + notExist(snackBar.getMessage()); + }); + it('should show "Herunterladen" button in Abgeschlossen status', () => { vorgangMoreMenu.getButton().click(); + exist(menuItem.getButton()); + vorgangMoreMenu.getButton().click({ force: true }); }); it('should have 1 file in download folder after download', () => { + vorgangMoreMenu.getButton().click(); deleteDownloadFolder().then(() => { menuItem.getButton().click({ force: true }); waitForSpinnerToDisappear(); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts index 2a0e6f115c47059a992399afe671ab931407920f..774264c1145b03c36e535c670df13f69554e857f 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts @@ -67,14 +67,7 @@ import { createWiedervorlageItem, } from 'apps/alfa-e2e/src/support/wiedervorlage-util'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; -import { - backspaceOn, - enterWith, - exist, - haveText, - haveValue, - notExist, -} from '../../../support/cypress.util'; +import { backspaceOn, exist, haveText, haveValue, notExist } from '../../../support/cypress.util'; import { UserRoleE2E, getUserSabine, @@ -447,6 +440,7 @@ describe('VorgangList Suche', () => { describe('select vorgang in vorgangdetail preview list', () => { it('should navigate to vorgang on selection', () => { vorgangSearch.getInput().clear().type(vorgangOtherName); + waitForSpinnerToDisappear(); exist(previewListItemVorgangOther.getRoot()); previewListItemVorgangOther.getRoot().click(); @@ -697,13 +691,13 @@ describe('VorgangList Suche', () => { describe('empty search field', () => { it('should not search with empty input', () => { - doSearchWith(''); + vorgangSearch.getInput().clear().type('{enter}'); notExist(vorgangPage.getSpinner()); }); }); function doSearchWith(searchBy: string): void { - enterWith(vorgangSearch.getInput(), searchBy); + vorgangSearch.doSearch(searchBy); } }); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts index 7ed13f59c0f6caf94b0914eff020cf57fa7630c1..c2d95b6e8e7097f83c3cb9ac6e3ec80db42e8641 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts @@ -24,7 +24,6 @@ import { dropCollections, dropSearchIndex } from 'apps/alfa-e2e/src/support/cypr import { containClass, contains, - enterWith, exist, haveLength, haveText, @@ -770,11 +769,11 @@ describe('Vorgang views and filter', () => { }); it('should unselect Alle filter', () => { - isButtonToggleNotChecked(navigation.getMeineVorgaengeFilter()); + isButtonToggleNotChecked(navigation.getAlleFilter()); }); it('should keep Meine Vorgänge unselected', () => { - isButtonToggleNotChecked(navigation.getAlleFilter()); + isButtonToggleNotChecked(navigation.getMeineVorgaengeFilter()); }); it('should keep view selection', () => { @@ -808,7 +807,7 @@ describe('Vorgang views and filter', () => { it('should keep filter selection', () => { isButtonToggleChecked(navigation.getAlleFilter()); - isButtonToggleNotChecked(navigation.getMeineVorgaengeFilter()); + isButtonToggleNotChecked(navigation.getNichtZugewiesenFilter()); }); it('should unselect Meine Vorgänge filter', () => { @@ -899,7 +898,7 @@ describe('Vorgang views and filter', () => { }); function doSearch(searchString: string): void { - enterWith(vorgangSearch.getInput(), searchString); + vorgangSearch.doSearch(searchString); } function isNotSelected(element) { diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts index 6d63193ad9a0fae0dacccbd7b9f79cab989cdb88..994c49a6065a7b85d0f294803291af520ed6b252 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts @@ -144,6 +144,8 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { waitforSpinnerToAppear(); waitForSpinnerToDisappear(); + wait(1000, 'Wait for async BE to complete'); + containClass(wiedervorlageContainer.getStatusDot(), 'erledigt'); contains( snackBar.getMessage(), @@ -185,6 +187,8 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { waitforSpinnerToAppear(); waitForSpinnerToDisappear(); + wait(1000, 'Wait for async BE to complete'); + notContainClass(wiedervorlageContainer.getStatusDot(), 'erledigt'); contains( snackBar.getMessage(), @@ -208,36 +212,66 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { }); }); - describe('tiny pentest for erledigen/wiedereroeffnen', () => { + describe('View Wiedervorlage view icon', () => { const wiedervorlage: WiedervorlageInVorgangE2EComponent = wiedervorlageContainerInVorgang.getWiedervorlage(wiedervorlageZumWiedereroeffnen.betreff); + const locatorIconIsOverdue: string = 'wiedervorlage-icon-is-overdue'; + const locatorIconDefault: string = 'wiedervorlage-icon-default'; - it('should open wiedervorlage on click', () => { - wait(500); - wiedervorlage.getLink().click(); - + it('back to vorgang list', () => { + wait(1000, 'Wait for async BE to complete'); + vorgangPage.getSubnavigation().getBackButton().click(); waitForSpinnerToDisappear(); + }); - exist(subnavigation.getRoot()); + it('should show red icon', () => { + exist(wiedervorlagenView.getRoot().findTestElementWithClass(locatorIconIsOverdue)); + }); + + it('Open Vorgang-Detail-Page', () => { + mainPage.getVorgangList().getListItem(vorgang.name).getRoot().click(); + waitForSpinnerToDisappear(); }); it('should mark as erledigt', () => { + const link = wiedervorlage.getLink(); + link.click(); + waitForSpinnerToDisappear(); subnavigation.erledigen(); + waitForSpinnerToDisappear(); - waitforSpinnerToAppear(); + wait(1000, 'Wait for async BE to complete'); + + subnavigation.navigateBack(); waitForSpinnerToDisappear(); + }); - containClass(wiedervorlageContainer.getStatusDot(), 'erledigt'); - contains( - snackBar.getMessage(), - `Die Wiedervorlage ${wiedervorlageZumWiedereroeffnen.betreff} wurde erledigt`, - ); + it('back to vorgang list', () => { + vorgangPage.getSubnavigation().getBackButton().click(); + waitForSpinnerToDisappear(); }); - it('should close snackBar on close', () => { - snackBar.getCloseButton().click(); + it('should show default icon', () => { + exist(wiedervorlagenView.getRoot().findTestElementWithClass(locatorIconDefault)); + }); + }); - notExist(snackBar.getMessage()); + describe('tiny lasttest for erledigen/wiedereroeffnen', () => { + const wiedervorlage: WiedervorlageInVorgangE2EComponent = + wiedervorlageContainerInVorgang.getWiedervorlage(wiedervorlageZumWiedereroeffnen.betreff); + + it('Open Vorgang-Detail-Page', () => { + mainPage.getVorgangList().getListItem(vorgang.name).getRoot().click(); + waitForSpinnerToDisappear(); + }); + + it('should open wiedervorlage on click', () => { + wait(500); + wiedervorlage.getLink().click(); + + waitForSpinnerToDisappear(); + + exist(subnavigation.getRoot()); }); it('should mark as open', () => { @@ -297,56 +331,32 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { notExist(snackBar.getMessage()); }); - it('should open vorgang detail on click on back', () => { - subnavigation.navigateBack(); + it('should mark as erledigt', () => { + subnavigation.erledigen(); waitforSpinnerToAppear(); waitForSpinnerToDisappear(); - exist(vorgangDetailHeader.getRoot()); - }); - }); - - //Test an die richtige Stelle verschieben und den Test vereinfachen - describe('View item icon', () => { - const wiedervorlage: WiedervorlageInVorgangE2EComponent = - wiedervorlageContainerInVorgang.getWiedervorlage(wiedervorlageZumWiedereroeffnen.betreff); - const locatorIconIsOverdue: string = 'wiedervorlage-icon-is-overdue'; - const locatorIconDefault: string = 'wiedervorlage-icon-default'; - - it('back to vorgang list', () => { - wait(500); - vorgangPage.getSubnavigation().getBackButton().click(); - waitForSpinnerToDisappear(); + containClass(wiedervorlageContainer.getStatusDot(), 'erledigt'); + contains( + snackBar.getMessage(), + `Die Wiedervorlage ${wiedervorlageZumWiedereroeffnen.betreff} wurde erledigt`, + ); }); - it('should show red icon', () => { - exist(wiedervorlagenView.getRoot().getTestElementWithClass(locatorIconIsOverdue)); - }); + it('should close snackBar on close', () => { + snackBar.getCloseButton().click(); - it('Open Vorgang-Detail-Page', () => { - mainPage.getVorgangList().getListItem(vorgang.name).getRoot().click(); - waitForSpinnerToDisappear(); + notExist(snackBar.getMessage()); }); - it('should mark as erledigt', () => { - wait(500); - wiedervorlage.getLink().click(); - waitForSpinnerToDisappear(); - subnavigation.erledigen(); - waitForSpinnerToDisappear(); + it('should open vorgang detail on click on back', () => { subnavigation.navigateBack(); - waitForSpinnerToDisappear(); - }); - it('back to vorgang list', () => { - wait(500); - vorgangPage.getSubnavigation().getBackButton().click(); + waitforSpinnerToAppear(); waitForSpinnerToDisappear(); - }); - it('should show default icon', () => { - exist(wiedervorlagenView.getRoot().getTestElementWithClass(locatorIconDefault)); + exist(vorgangDetailHeader.getRoot()); }); }); }); diff --git a/alfa-client/apps/alfa-e2e/src/fixtures/argocd/by-main-dev.yaml b/alfa-client/apps/alfa-e2e/src/fixtures/argocd/by-main-dev.yaml index 480b4e1dd5db361f8aff3f159d6302851b98a875..c73587a0b07ff1fb36e484323bd8f5ea31a19300 100644 --- a/alfa-client/apps/alfa-e2e/src/fixtures/argocd/by-main-dev.yaml +++ b/alfa-client/apps/alfa-e2e/src/fixtures/argocd/by-main-dev.yaml @@ -4,12 +4,11 @@ ozgcloud: e2eTest: true project: destinations: - - namespace: '*' + - namespace: "*" server: https://kubernetes.default.svc alfa: env: - overrideSpringProfiles: 'oc,e2e,dev' - ozgcloud_feature_bescheid-wizard: "true" + overrideSpringProfiles: "oc,e2e,dev" ingress: use_staging_cert: true ozgcloud: @@ -20,7 +19,7 @@ alfa: vorgang_manager: env: - overrideSpringProfiles: 'oc,e2e,dev' + overrideSpringProfiles: "oc,e2e,dev" ozgcloud_bescheid_smart_documents_url: http://smocker:8080/smartdocuments ozgcloud_bescheid_smart_documents_basic_auth_username: MGM ozgcloud_bescheid_smart_documents_basic_auth_password: MGM @@ -80,7 +79,7 @@ smocker: response: status: 500 headers: - body: 'Da ist etwas schiefgelaufen' + body: "Da ist etwas schiefgelaufen" # bayernid - request: diff --git a/alfa-client/apps/alfa-e2e/src/support/commands.ts b/alfa-client/apps/alfa-e2e/src/support/commands.ts index 1d1403d708893387744dec0e12e7783f116802a6..180457361a59046324cdf21cdb28989945f81b79 100644 --- a/alfa-client/apps/alfa-e2e/src/support/commands.ts +++ b/alfa-client/apps/alfa-e2e/src/support/commands.ts @@ -91,6 +91,9 @@ Cypress.Commands.add('getTestElement', (selector, ...args) => { }); Cypress.Commands.add('getTestElementWithClass', (selector, ...args) => { + console.log( + 'Achtung: Potentiell nicht eindeutiges Ergebnis, weil eine data-test-class mit cy.get() von der DOM-Root aus gesucht wird.', + ); return cy.get(`[${DATA_TEST_CLASS}="${selector}"]`, ...args); }); @@ -106,7 +109,7 @@ Cypress.Commands.add( }, ); -Cypress.Commands.add('findElement', { prevSubject: true }, (subject: any, selector) => { +Cypress.Commands.add('findElement', { prevSubject: true }, (subject: any, selector: string) => { return subject.find(selector); }); @@ -250,8 +253,8 @@ Cypress.Commands.add('addMockToSmocker', (mock) => { url: `${getSmockerUrl()}/mocks`, body: mockFile, headers: { - "Content-Type": "application/x-yaml" - } + 'Content-Type': 'application/x-yaml', + }, }); }); }); @@ -270,4 +273,4 @@ function getSmockerUrl(): string { function getSmockerEnv(): string { return Cypress.env(CypressEnv.SMOCKER); -} \ No newline at end of file +} diff --git a/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts b/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts index cc70ec8dbe3806519598d89d2482029f70699f7a..90c0c0b4058f983b0cb51861d8b632084cb2a8a3 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts @@ -40,6 +40,8 @@ enum CypressTasks { INIT_USERMANAGER_DATA = 'initUsermanagerData', COUNT_FILES = 'countFiles', DELETE_FOLDER = 'deleteFolder', + UNZIP_FILE = 'unzipDownloadFile', + GET_DOWNLOAD_FILES = 'getDownloadFiles', } enum MongoCollections { @@ -136,6 +138,14 @@ export function deleteDownloadFolder() { return cy.task(CypressTasks.DELETE_FOLDER, DOWNLOAD_FOLDER); } +export function unzipDownloadFile(file: string) { + return cy.task(CypressTasks.UNZIP_FILE, { folderName: DOWNLOAD_FOLDER, fileName: file }); +} + +export function getDownloadFiles(): Cypress.Chainable<Array<string>> { + return cy.task(CypressTasks.GET_DOWNLOAD_FILES, DOWNLOAD_FOLDER); +} + export function scrollToWindowBottom(): void { cy.window().scrollTo('bottom'); } @@ -169,8 +179,11 @@ export function urlShouldInclude(text: string) { } //TODO: anders loesen -> bad practice -export function wait(ms: number): void { +export function wait(ms: number, reason = ''): void { cy.wait(ms); + if (reason) { + console.log(`Had to wait ${ms}ms because of: ${reason}`); + } } // @@ -178,8 +191,8 @@ export function reload(): void { cy.reload(); } -export function readFileFromDownloads(fileName: string) { - return cy.readFile(`${DOWNLOAD_FOLDER}/${fileName}`); +export function readFileFromDownloads(fileName: string): Cypress.Chainable<any> { + return cy.readFile(`${DOWNLOAD_FOLDER}/${fileName}`, { timeout: 5000 }); } export function pressTab(): void { @@ -202,4 +215,4 @@ export function addSmockerMock(mock: SmockerMocks): void { export function resetSmocker(): void { cy.resetSmocker(); -} \ No newline at end of file +} diff --git a/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts b/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts index 1e965a342e415f6825afaa78d27a684a8a125bfa..a82b4f736601daf7f1b3164cbb45818a609e29de 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts @@ -1,5 +1,7 @@ import { readdir, remove } from 'fs-extra'; import { Db, Long, MongoClient, ObjectId } from 'mongodb'; +const fs = require('fs'); +const decompress = require('decompress'); const Binary = require('mongodb').Binary; @@ -54,6 +56,15 @@ module.exports = (on: any, config: any) => { deleteFolder(folderName); return 0; }, + getDownloadFiles(folderName: string) { + console.log('get files in folder %s', folderName); + return getDownloadFiles(folderName); + }, + unzipDownloadFile({ folderName, fileName }) { + console.log('unzip file %s in folder %s', fileName, folderName); + unzipFile(folderName, fileName); + return 0; + }, }); // Workaround für Angular 13 und Cypress mit Webpack 4, @@ -333,3 +344,14 @@ function deleteFolder(folderName: string): void { throw new Error(`Failed to delete folder: ${err.message}`); }); } + +function getDownloadFiles(folderName: string): Promise<Array<string>> { + return fs.readdirSync(folderName); +} + +function unzipFile(folderName: string, fileName: string): void { + console.log('File: ' + folderName + fileName); + + decompress(folderName + '/' + fileName, folderName); + return null; +} diff --git a/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts b/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts index 2e22bc2a6f6bc43f622ebac4772364105bfb9ccc..4440874cbfcc512a5b23c9394c60f50f15e07d21 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts @@ -22,6 +22,8 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { wait } from './cypress-helper'; + //TODO Naming der Methoden geradeziehen export function containClass(element: any, cssClass: string): void { @@ -101,6 +103,10 @@ export function shouldHaveAttributeBeLowerThan(element: any, attributeName: stri } // +export function shouldHaveAttribute(element: any, name: string, value: string) { + element.should('have.attr', name, value); +} + export function visible(element: any) { element.should('be.visible'); } @@ -113,8 +119,14 @@ export function enter(element: any): void { element.clear().type(CypressKeyboardActions.ENTER); } -export function enterWith(element: any, value: any): void { - element.clear().type(value + CypressKeyboardActions.ENTER); +export function enterWith( + element: Cypress.Chainable<JQuery<HTMLElement>>, + value: string, + delayBeforeEnter: number = 200, +): void { + element.clear().type(value); + wait(delayBeforeEnter); + element.type(CypressKeyboardActions.ENTER); } export function backspaceOn(element: any): void { diff --git a/alfa-client/apps/demo/src/app/app.component.html b/alfa-client/apps/demo/src/app/app.component.html index 4ecd2941d74717e3ef1791e8a0c1e4e039da9b66..81bc4dff284d5e739981af85b8d4e1cc0c4b3844 100644 --- a/alfa-client/apps/demo/src/app/app.component.html +++ b/alfa-client/apps/demo/src/app/app.component.html @@ -15,7 +15,7 @@ </div> <main class="flex-auto bg-background-50 p-6"> <div class="w-96"> - <ods-attachment-container> + <ods-attachment-wrapper> <ods-attachment caption="Mein_2Bescheid.pdf" description="234 kB" @@ -39,7 +39,7 @@ fileType="pdf" > </ods-attachment> - </ods-attachment-container> + </ods-attachment-wrapper> </div> <form id="antrag_bescheiden_form" [formGroup]="exampleForm"> diff --git a/alfa-client/apps/demo/src/app/app.component.ts b/alfa-client/apps/demo/src/app/app.component.ts index 58a09ee4d02b29261200d0f6f0db78b2bda20baa..5d64b15a623f2330d29201455ec298907bcd94fa 100644 --- a/alfa-client/apps/demo/src/app/app.component.ts +++ b/alfa-client/apps/demo/src/app/app.component.ts @@ -5,8 +5,8 @@ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { AttachmentComponent, - AttachmentContainerComponent, AttachmentIconComponent, + AttachmentWrapperComponent, BescheidGenerateIconComponent, BescheidUploadIconComponent, ButtonCardComponent, @@ -34,7 +34,7 @@ import { CustomStepperComponent } from './components/cdk-demo/custom-stepper.com imports: [ CommonModule, AttachmentComponent, - AttachmentContainerComponent, + AttachmentWrapperComponent, ButtonComponent, ButtonCardComponent, FileUploadButtonComponent, diff --git a/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts b/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts index 66db648e5e870b40619f321f04321b35b763e93d..bca9352718c8cbdb0e8f0434095d574215da1577 100644 --- a/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts +++ b/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts @@ -1,6 +1,6 @@ import { isNotNil } from '@alfa-client/tech-shared'; import { Component, Input } from '@angular/core'; -import { FormControlEditorAbstractComponent } from 'libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from '@ods/component'; @Component({ selector: 'text-field', diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html index 8c0d2203d740cd0bba3db4dc9d8642178677d0ae..09e3689508466b42cb14f601cef86e5e1e82637c 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html @@ -1,5 +1,5 @@ <div class="flex max-w-xl flex-col gap-4"> - <ods-bescheid-container + <ods-bescheid-wrapper *ngFor="let bescheid of bescheidList | toEmbeddedResources: bescheidListLinkRel.BESCHEID_LIST" data-test-id="bescheid-container-in-vorgang" > @@ -23,5 +23,5 @@ [resource]="bescheid" [linkRel]="bescheidLinkRel.ATTACHMENTS" ></alfa-binary-file-list-container> - </ods-bescheid-container> + </ods-bescheid-wrapper> </div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts index e0d873aa1642ddef9bbd08cf7ced139549f24b50..60b406d0b6ac33394f4fbf38e16e8f22bcd0aa31 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts @@ -5,7 +5,7 @@ import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-u import { DatePipe } from '@angular/common'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; -import { BescheidContainerComponent, BescheidStatusTextComponent } from '@ods/system'; +import { BescheidStatusTextComponent, BescheidWrapperComponent } from '@ods/system'; import { createBescheidListResource, createBescheidResource, @@ -37,7 +37,7 @@ describe('BescheidListInVorgangComponent', () => { MockComponent(DocumentInBescheidContainerComponent), MockComponent(BinaryFileListContainerComponent), MockComponent(BescheidStatusTextComponent), - MockComponent(BescheidContainerComponent), + MockComponent(BescheidWrapperComponent), ], }).compileComponents(); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html index aefc4c75af7cfc3bf0669e8c9fb99665f464fde4..464b7a0a4ff8783533e4160bc53d3c007eae5eed 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html @@ -1,9 +1,9 @@ <ng-container *ngIf="documentStateResource$ | async as documentStateResource"> - <ods-attachment-container> + <ods-attachment-wrapper> <alfa-binary-file-uri-container *ngIf="documentStateResource.resource | hasLink: documentLinkRel.FILE" data-test-class="binary-file-uri-container" [binaryFileUri]="documentStateResource.resource | getUrl: documentLinkRel.FILE" ></alfa-binary-file-uri-container> - </ods-attachment-container> + </ods-attachment-wrapper> </ng-container> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts index f005143e8fcd38ef21d5fe5a41753b7f98c347a6..6d4e62565ec7e6fa5e80f193cfa8c8c56fc2885d 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts @@ -16,7 +16,7 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing'; import faker from '@faker-js/faker'; import { ResourceUri, getUrl } from '@ngxp/rest'; -import { AttachmentContainerComponent } from '@ods/system'; +import { AttachmentWrapperComponent } from '@ods/system'; import { DocumentLinkRel } from 'libs/bescheid-shared/src/lib/document.linkrel'; import { DocumentResource } from 'libs/bescheid-shared/src/lib/document.model'; import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; @@ -45,7 +45,7 @@ describe('DocumentInBescheidContainerComponent', () => { HasLinkPipe, GetUrlPipe, MockComponent(BinaryFileUriContainerComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), ], providers: [ { diff --git a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts index ab43b96faec19afccbfddc606e2b0b942174c012..e6faf4a67f1e937d0aab66ae583415d8a4030bd9 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts @@ -16,8 +16,8 @@ import { CreateBescheidButtonContainerComponent } from './create-bescheid-button import { CreateBescheidButtonComponent } from './create-bescheid-button-container/create-bescheid-button/create-bescheid-button.component'; import { - BescheidContainerComponent, BescheidStatusTextComponent, + BescheidWrapperComponent, CloseIconComponent, StampIconComponent, } from '@ods/system'; @@ -31,7 +31,7 @@ import { UiModule, CommandSharedModule, BescheidStatusTextComponent, - BescheidContainerComponent, + BescheidWrapperComponent, StampIconComponent, CloseIconComponent, ], diff --git a/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts b/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts index c6fcec9a06a7d81756a40aaa2afe23b226cc12bd..4a29ed56be2585d9746e49526e4977f66dbb2239 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts @@ -74,7 +74,7 @@ export class BinaryFileEffects { loadBinaryFileList$ = createEffect(() => this.actions$.pipe( ofType(BinaryFileActions.loadBinaryFileList), - switchMap((props: LoadBinaryFileListProps) => + mergeMap((props: LoadBinaryFileListProps) => this.repository.getFiles(props.resource, props.linkRel).pipe( map((binaryFileList: BinaryFileListResource) => props.successAction(binaryFileList)), catchError((error) => of(props.failureAction(error.error))), diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts index 9ad810e73de5bb0a2015ad9b012adc2700f705d1..071f1a03138fae246311d53e97be4819b029127b 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts @@ -21,26 +21,34 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { faker } from '@faker-js/faker'; import { + BlobWithFileName, ContentType, GetRequestOptions, HttpErrorHandler, + HttpHeader, ListResource, TechSharedModule, } from '@alfa-client/tech-shared'; import { mock, mockClass, useFromMock } from '@alfa-client/test-utils'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { faker } from '@faker-js/faker'; import { Resource, ResourceFactory, ResourceUri, getUrl } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { DummyLinkRel } from 'libs/tech-shared/test/dummy'; import { createDummyListResource, createDummyResource } from 'libs/tech-shared/test/resource'; -import { of } from 'rxjs'; -import { createBinaryFileResource } from '../../test/binary-file'; +import { Observable, of } from 'rxjs'; +import { + createBinaryFileResource, + createBlob, + createGetRequestOptions, +} from '../../test/binary-file'; import { BinaryFileLinkRel } from './binary-file.linkrel'; import { BinaryFileResource } from './binary-file.model'; import { BinaryFileRepository } from './binary-file.repository'; +import * as HttpUtil from '../../../tech-shared/src/lib/http.util'; + describe('BinaryFileRepository', () => { let repository: BinaryFileRepository; @@ -181,7 +189,7 @@ describe('BinaryFileRepository', () => { }); function getExpectedRequestOptions(): HttpHeaders { - return new HttpHeaders().set('Accept', [ + return new HttpHeaders().set(HttpHeader.ACCEPT, [ ContentType.APPLICATION_PDF, ContentType.APPLICATION_JSON, ]); @@ -224,4 +232,55 @@ describe('BinaryFileRepository', () => { expect(resourceWrapper.get).toHaveBeenCalledWith(DummyLinkRel.DUMMY); }); }); + + describe('downloadArchive', () => { + const blob: Blob = createBlob(); + const uri: ResourceUri = faker.internet.url(); + + const blobWithFileName: BlobWithFileName = { blob, fileName: faker.name.firstName() }; + + beforeEach(() => { + httpClient.get.mockReturnValue(hot('a', { a: blob })); + jest.spyOn(HttpUtil, 'buildBlobWithFileName').mockReturnValue(blobWithFileName); + }); + + it('should call httpClient', () => { + const requestOptions: GetRequestOptions = createGetRequestOptions(); + repository.buildRequestOptionsForArchive = jest.fn().mockReturnValue(requestOptions); + + repository.downloadArchive(uri); + + expect(httpClient.get).toHaveBeenCalledWith(uri, requestOptions); + }); + + it('should return value', () => { + const result: Observable<BlobWithFileName> = repository.downloadArchive(uri); + + expect(result).toBeObservable(cold('b', { b: blobWithFileName })); + }); + + describe('buildRequestOptionsForArchive', () => { + it('should return httpHeaders', () => { + const result: GetRequestOptions = repository.buildRequestOptionsForArchive(); + + expect(result.responseType).toEqual('blob'); + }); + + it('should return responseType', () => { + const result: GetRequestOptions = repository.buildRequestOptionsForArchive(); + + expect(result.headers).toEqual(buildExpectedRequestOptions()); + }); + + it('should return observe', () => { + const result: GetRequestOptions = repository.buildRequestOptionsForArchive(); + + expect((<any>result).observe).toEqual('response'); + }); + }); + + function buildExpectedRequestOptions(): HttpHeaders { + return new HttpHeaders().set(HttpHeader.ACCEPT, ContentType.APPLICATION_OCTET_STREAM); + } + }); }); diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts index 9e2d3f4548b00e0fbbe64f7971b4af6c44356479..ef0c1b6762f8ac2177282f36b07d64879f8aac7c 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts @@ -21,16 +21,18 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Injectable } from '@angular/core'; import { + BlobWithFileName, ContentType, GetRequestOptions, HttpHeader, SkipInterceptor, + buildBlobWithFileName, } from '@alfa-client/tech-shared'; +import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; import { Resource, ResourceFactory, ResourceUri, getUrl } from '@ngxp/rest'; -import { Observable } from 'rxjs'; +import { Observable, map } from 'rxjs'; import { BinaryFileLinkRel } from './binary-file.linkrel'; import { BinaryFileListResource, BinaryFileResource } from './binary-file.model'; @@ -41,7 +43,11 @@ export class BinaryFileRepository { private resourceFactory: ResourceFactory, ) {} - public uploadFile(resource: Resource, linkRel: string, file: File): Observable<any> { + public uploadFile( + resource: Resource, + linkRel: string, + file: File, + ): Observable<HttpResponse<Object>> { const formData: FormData = new FormData(); formData.append('file', file, file.name); @@ -89,4 +95,16 @@ export class BinaryFileRepository { public getFiles(resource: Resource, linkRel: string): Observable<BinaryFileListResource> { return this.resourceFactory.from(resource).get(linkRel); } + + public downloadArchive(uri: ResourceUri): Observable<BlobWithFileName> { + return this.httpClient + .get<HttpResponse<Blob>>(uri, this.buildRequestOptionsForArchive()) + .pipe(map(buildBlobWithFileName)); + } + + buildRequestOptionsForArchive(): GetRequestOptions { + let headers = new HttpHeaders(); + headers = headers.set(HttpHeader.ACCEPT, ContentType.APPLICATION_OCTET_STREAM); + return <GetRequestOptions>{ headers, responseType: 'blob' as 'json', observe: 'response' }; + } } diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts index ff3225272c34a37e6ccb820eaed7b82c39e4ef6a..9d69ae78a151079401a9d94caceff99e8c980675 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts @@ -21,30 +21,32 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { faker } from '@faker-js/faker'; import { + BlobWithFileName, + StateResource, createEmptyStateResource, createStateResource, - StateResource, } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { SnackBarService } from '@alfa-client/ui'; -import { Resource } from '@ngxp/rest'; +import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { fakeAsync, tick } from '@angular/core/testing'; +import { faker } from '@faker-js/faker'; +import { Resource, ResourceUri } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; -import { createBinaryFileResource } from 'libs/binary-file-shared/test/binary-file'; +import { createBinaryFileResource, createBlob } from 'libs/binary-file-shared/test/binary-file'; import { VALIDATION_MESSAGES, ValidationMessageCode, } from 'libs/tech-shared/src/lib/validation/tech.validation.messages'; import { DummyLinkRel } from 'libs/tech-shared/test/dummy'; import { createDummyResource } from 'libs/tech-shared/test/resource'; -import { of } from 'rxjs'; +import { Observable, of } from 'rxjs'; +import { createHttpErrorResponse } from '../../../tech-shared/test/http'; +import { singleHot } from '../../../tech-shared/test/marbles'; import { BinaryFileResource } from './binary-file.model'; import { BinaryFileRepository } from './binary-file.repository'; import { BinaryFileService } from './binary-file.service'; -import { fakeAsync, tick } from '@angular/core/testing'; -import { createHttpErrorResponse } from '../../../tech-shared/test/http'; describe('BinaryFileService', () => { let service: BinaryFileService; @@ -80,7 +82,23 @@ describe('BinaryFileService', () => { expect(repository.download).toHaveBeenCalledWith(binaryFileResource); }); - describe('save file', () => { + it('should return value', () => { + repository.download.mockReturnValue(singleHot(blob, '-a')); + + const returnValue: Observable<StateResource<Blob>> = service.downloadFile( + binaryFileResource, + downloadNamePrefix, + ); + + expect(returnValue).toBeObservable( + cold('ab', { + a: createEmptyStateResource(true), + b: createStateResource(blob), + }), + ); + }); + + describe('save data', () => { describe('on existing response', () => { it('should save file without prefix', () => { service.saveBinaryFile(blob, binaryFileResource, undefined); @@ -122,12 +140,72 @@ describe('BinaryFileService', () => { }); }); + describe('download archive', () => { + const blob: Blob = createBlob(); + const resourceUri: ResourceUri = faker.internet.url(); + + beforeEach(() => { + service.save = jest.fn(); + repository.downloadArchive.mockReturnValue(hot('a', { a: blob })); + }); + + it('should call repository', () => { + service.downloadArchive(resourceUri); + + expect(repository.downloadArchive).toHaveBeenCalledWith(resourceUri); + }); + + it('should return value', () => { + repository.downloadArchive.mockReturnValue(singleHot(blob, '-a')); + service.saveData = jest.fn().mockReturnValue(createStateResource(blob)); + + const returnValue: Observable<StateResource<Blob>> = service.downloadArchive(resourceUri); + + expect(returnValue).toBeObservable( + cold('ab', { + a: createEmptyStateResource(true), + b: createStateResource(blob), + }), + ); + }); + + describe('save data', () => { + const fileName: string = faker.random.word(); + const blobWithFileName: BlobWithFileName = { blob, fileName }; + + describe('on existing response', () => { + it('should save file', () => { + service.saveData(blobWithFileName); + + expect(service.save).toHaveBeenCalledWith(blob, fileName); + }); + + it('should return loaded stateResource', () => { + const result: StateResource<Blob> = service.saveData(blobWithFileName); + + expect(result).toEqual(createStateResource(blob)); + }); + }); + + describe('on non existing response', () => { + it('should return loading stateResource', () => { + const result: StateResource<Blob> = service.saveData({ + ...blobWithFileName, + blob: undefined, + }); + + expect(result).toEqual(createEmptyStateResource(true)); + }); + }); + }); + }); + describe('upload file', () => { const dummyResource: Resource = createDummyResource(); const dummyLinkRel: string = DummyLinkRel.DUMMY; const fileLocation: string = 'fileLocation'; - const uploadFileResponse: HttpResponse<void> = <any>{ + const uploadFileResponse: HttpResponse<Object> = <any>{ response: { headers: { ['Location']: fileLocation } }, }; diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts index 8e051164991b0bb42bcf1b7c268f23d2307e0516..bb6592cb76931ff30edca949cc28a864686ecfce 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts @@ -22,7 +22,9 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { + BlobWithFileName, EMPTY_STRING, + HttpHeader, StateResource, createEmptyStateResource, createErrorStateResource, @@ -58,7 +60,9 @@ export class BinaryFileService { showValidationErrorSnackBar: boolean = true, ): Observable<StateResource<BinaryFileResource>> { return this.repository.uploadFile(resource, linkRel, file).pipe( - mergeMap((response: HttpResponse<void>) => this.getFile(response.headers.get('Location'))), + mergeMap((response: HttpResponse<Object>) => + this.getFile(response.headers.get(HttpHeader.LOCATION)), + ), catchError((errorResponse) => this.handleError(errorResponse.error, showValidationErrorSnackBar), ), @@ -115,13 +119,29 @@ export class BinaryFileService { return file.name; } + public downloadArchive(uri: ResourceUri): Observable<StateResource<Blob>> { + return this.repository.downloadArchive(uri).pipe( + map((data: BlobWithFileName) => this.saveData(data)), + startWith(createEmptyStateResource<Blob>(true)), + ); + } + + saveData(dataWithFileName: BlobWithFileName): StateResource<Blob> { + const data: Blob = dataWithFileName.blob; + if (isNil(data)) { + return createEmptyStateResource(true); + } + this.save(data, dataWithFileName.fileName); + return createStateResource(data); + } + save(data: any, fileName: string): void { saveAs(data, fileName); } public getFile(uri: ResourceUri): Observable<StateResource<BinaryFileResource>> { return this.repository.getFile(uri).pipe( - map((fileList) => createStateResource(fileList)), + map((fileList: BinaryFileResource) => createStateResource(fileList)), startWith(createEmptyStateResource<BinaryFileResource>(true)), ); } @@ -131,7 +151,7 @@ export class BinaryFileService { linkRel: string, ): Observable<StateResource<BinaryFileListResource>> { return this.repository.getFiles(resource, linkRel).pipe( - map((fileList) => createStateResource(fileList)), + map((fileList: BinaryFileListResource) => createStateResource(fileList)), startWith(createEmptyStateResource<BinaryFileListResource>(true)), ); } diff --git a/alfa-client/libs/binary-file-shared/test/binary-file.ts b/alfa-client/libs/binary-file-shared/test/binary-file.ts index 11ec8db5f9e7d0fea09215377ba9cb3911d93ea9..c776a49793353a3674e60ed40431df7392091509 100644 --- a/alfa-client/libs/binary-file-shared/test/binary-file.ts +++ b/alfa-client/libs/binary-file-shared/test/binary-file.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { GetRequestOptions, StateResource, createStateResource } from '@alfa-client/tech-shared'; import { faker } from '@faker-js/faker'; import { BinaryFileListLinkRel } from 'libs/binary-file-shared/src/lib/binary-file.linkrel'; import { @@ -64,3 +64,11 @@ export function createLoadingBinaryFileStateResource(): StateResource<BinaryFile export function createLoadedBinaryFileResource(): StateResource<BinaryFileResource> { return createStateResource(createBinaryFileResource()); } + +export function createBlob(): Blob { + return <Blob>{}; +} + +export function createGetRequestOptions(): GetRequestOptions { + return <GetRequestOptions>{}; +} diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html index ccb6ff68ca977040b551bbccdae63a27b5122617..bdd7a27733a69d622edeb8e8abff53f5eb8c8d2b 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html @@ -1,5 +1,5 @@ -<ods-attachment-container> +<ods-attachment-wrapper> <alfa-binary-file-list [binaryFileListStateResource]="binaryFileListStateResource$ | async" ></alfa-binary-file-list> -</ods-attachment-container> +</ods-attachment-wrapper> diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts index 861fc464fac3928e8c5a25ff71fcd93031abfa13..1541adfb3f823192df4745d1534ab7ca2478df01 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts @@ -11,7 +11,7 @@ import { of } from 'rxjs'; import { BinaryFileListContainerComponent } from './binary-file-list-container.component'; import { BinaryFileListComponent } from './binary-file-list/binary-file-list.component'; -import { AttachmentContainerComponent } from '@ods/system'; +import { AttachmentWrapperComponent } from '@ods/system'; describe('BinaryFileListContainerComponent', () => { let component: BinaryFileListContainerComponent; @@ -30,7 +30,7 @@ describe('BinaryFileListContainerComponent', () => { declarations: [ BinaryFileListContainerComponent, MockComponent(BinaryFileListComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), ], providers: [ { diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html index bd452e3e647ee15ed78a06b3e75374873c3f7b8d..156e5a66a1684b330e0a364a7e66a476244bf1cd 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html @@ -1,11 +1,10 @@ <ng-container *ngIf="binaryFileStateResource$ | async as binaryFileStateResource"> - <ods-attachment-container> + <ods-attachment-wrapper> <alfa-binary-file2-container *ngIf="binaryFileStateResource.resource as binaryFile" data-test-class="binary-file-container" [file]="binaryFile" - [isLoading]="binaryFileStateResource.loading" [deletable]="false" ></alfa-binary-file2-container> - </ods-attachment-container> + </ods-attachment-wrapper> </ng-container> diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts index 34d0358a03a8f651acba5a8335d860df4e8578c8..0080d68eb8b8c6ae8a425340e1422b5055e21fa6 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts @@ -21,7 +21,7 @@ import { of } from 'rxjs'; import { BinaryFile2ContainerComponent } from '../binary-file2-container/binary-file2-container.component'; import { BinaryFileUriContainerComponent } from './binary-file-uri-container.component'; -import { AttachmentContainerComponent } from '@ods/system'; +import { AttachmentWrapperComponent } from '@ods/system'; describe('BinaryFileUriContainerComponent', () => { let component: BinaryFileUriContainerComponent; @@ -37,7 +37,7 @@ describe('BinaryFileUriContainerComponent', () => { declarations: [ BinaryFileUriContainerComponent, MockComponent(BinaryFile2ContainerComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), ], providers: [ { @@ -97,13 +97,6 @@ describe('BinaryFileUriContainerComponent', () => { expect(binaryFileContainerComponent.file).toEqual(binaryFileStateResource.resource); }); - it('should be called with isLoading', () => { - const binaryFileContainerComponent: BinaryFile2ContainerComponent = - getMockComponent<BinaryFile2ContainerComponent>(fixture, BinaryFile2ContainerComponent); - - expect(binaryFileContainerComponent.isLoading).toBe(binaryFileStateResource.loading); - }); - it('should be called with deleteable', () => { const binaryFileContainerComponent: BinaryFile2ContainerComponent = getMockComponent<BinaryFile2ContainerComponent>(fixture, BinaryFile2ContainerComponent); diff --git a/alfa-client/libs/binary-file/src/lib/binary-file.module.ts b/alfa-client/libs/binary-file/src/lib/binary-file.module.ts index b1f7c8e578217084c9419a892fa0edb503846c31..89f78362805623629dbb65e5e9963789da21a337 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file.module.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file.module.ts @@ -25,9 +25,11 @@ import { TechSharedModule } from '@alfa-client/tech-shared'; import { UiModule } from '@alfa-client/ui'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { DownloadButtonComponent } from '@ods/component'; import { AttachmentComponent, - AttachmentContainerComponent, + AttachmentHeaderComponent, + AttachmentWrapperComponent, CloseIconComponent, SpinnerIconComponent, } from '@ods/system'; @@ -39,6 +41,7 @@ import { BinaryFileListComponent } from './binary-file-list-container/binary-fil import { BinaryFileUriContainerComponent } from './binary-file-uri-container/binary-file-uri-container.component'; import { BinaryFile2ContainerComponent } from './binary-file2-container/binary-file2-container.component'; import { BinaryFile2Component } from './binary-file2-container/binary-file2/binary-file2.component'; +import { DownloadArchiveFileButtonContainerComponent } from './download-archive-file-button-container/download-archive-file-button-container.component'; import { HorizontalBinaryFileListComponent } from './horizontal-binary-file-list/horizontal-binary-file-list.component'; import { VerticalBinaryFileListComponent } from './vertical-binary-file-list/vertical-binary-file-list.component'; @@ -48,9 +51,11 @@ import { VerticalBinaryFileListComponent } from './vertical-binary-file-list/ver UiModule, TechSharedModule, AttachmentComponent, - AttachmentContainerComponent, + AttachmentHeaderComponent, + AttachmentWrapperComponent, SpinnerIconComponent, CloseIconComponent, + DownloadButtonComponent, ], declarations: [ BinaryFileAttachmentContainerComponent, @@ -63,6 +68,7 @@ import { VerticalBinaryFileListComponent } from './vertical-binary-file-list/ver BinaryFileUriContainerComponent, BinaryFileListContainerComponent, BinaryFileListComponent, + DownloadArchiveFileButtonContainerComponent, ], exports: [ BinaryFileAttachmentContainerComponent, diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html index f901ea0826e96e938dab2632af3d9d5c754531bb..41fe9ab51a28ec7cc893e0dfd418cbffb86075c5 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html @@ -3,7 +3,6 @@ [file]="file" [stateResource]="fileStateResource$ | async" [deletable]="deletable" - [isLoading]="isLoading" [downloadToken]="downloadToken$ | async" (startDownload)="startDownload($event)" (startDelete)="startDelete.emit($event)" diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts index 6461380c6ebe06fa80e8a242440f9acee33051fa..ad052ff94920bcd3eb6f1b974aeeba26b276f675 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts @@ -12,7 +12,6 @@ export class BinaryFile2ContainerComponent { @Input() file: BinaryFileResource; @Input() downloadFileNamePrefix: string; @Input() deletable: boolean = false; - @Input() isLoading: boolean = false; @Output() startDelete: EventEmitter<BinaryFileResource> = new EventEmitter(); diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html index cd64683430db122e9d8411e606f9f80be75d2028..48f6c28798060e009239a18af52f8c24c4944492 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html @@ -1,10 +1,12 @@ <ods-attachment [caption]="file.name" + [loadingCaption]="file.name + ' wird geladen...'" [description]="file.size | fileSizePlain" [fileType]="getIconType(file.contentType)" (click)="downloadFile()" [attr.aria-label]="'Anhang: Dateiname: ' + file.name" [isLoading]="isLoading" + data-test-class="download-file-button" > <div close class="flex-shrink self-center"> <button diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts index 6118b4d86b30fae7b721cf1f34c44ca190bdc421..c22c2d1c4bc05dd546c8e8e95f990fa359b7a01f 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts @@ -19,7 +19,6 @@ export class BinaryFile2Component { @Input() stateResource: StateResource<Resource>; @Input() deletable: boolean = false; @Input() downloadToken: ApiDownloadToken = <ApiDownloadToken>{}; - @Input() isLoading: boolean = false; readonly fileLinkRel = BinaryFileLinkRel; @@ -33,6 +32,10 @@ export class BinaryFile2Component { return this.getStateResource().loading; } + get isLoading(): boolean { + return this.getStateResource().loading; + } + getStateResource(): StateResource<Resource> { return isNil(this.stateResource) ? createEmptyStateResource<Resource>() : this.stateResource; } diff --git a/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.html b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..a649ed00b6baf0a460ce19e409ad2fc536ebeb24 --- /dev/null +++ b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.html @@ -0,0 +1,5 @@ +<ods-download-button + [stateResource]="downloadArchiveInProgress$ | async" + data-test-class="download-archive" + (click)="downloadArchive()" +/> diff --git a/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.spec.ts b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b711f3a245de614563b6d1914ea0dbd09a0a73b --- /dev/null +++ b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.spec.ts @@ -0,0 +1,49 @@ +import { BinaryFileService } from '@alfa-client/binary-file-shared'; +import { Mock, dispatchEventFromFixture, mock } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import faker from '@faker-js/faker'; +import { DownloadButtonComponent } from '@ods/component'; +import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; +import { MockComponent } from 'ng-mocks'; +import { DownloadArchiveFileButtonContainerComponent } from './download-archive-file-button-container.component'; + +describe('DownloadArchiveFileButtonContainerComponent', () => { + let component: DownloadArchiveFileButtonContainerComponent; + let fixture: ComponentFixture<DownloadArchiveFileButtonContainerComponent>; + + const downloadArchiveButton: string = getDataTestClassOf('download-archive'); + + const binaryFileService: Mock<BinaryFileService> = mock(BinaryFileService); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DownloadArchiveFileButtonContainerComponent], + imports: [MockComponent(DownloadButtonComponent)], + providers: [ + { + provide: BinaryFileService, + useValue: binaryFileService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DownloadArchiveFileButtonContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('download attachments', () => { + const downloadUri: string = faker.internet.url(); + + it('should call service to download archive', () => { + component.downloadUri = downloadUri; + dispatchEventFromFixture(fixture, downloadArchiveButton, 'click'); + + expect(binaryFileService.downloadArchive).toHaveBeenCalledWith(downloadUri); + }); + }); +}); diff --git a/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.ts b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c584eeb78164e120a18be156e24703c1c9f87ad --- /dev/null +++ b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.ts @@ -0,0 +1,22 @@ +import { BinaryFileService } from '@alfa-client/binary-file-shared'; +import { StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { Component, Input } from '@angular/core'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'alfa-download-archive-file-button-container', + templateUrl: './download-archive-file-button-container.component.html', +}) +export class DownloadArchiveFileButtonContainerComponent { + @Input() downloadUri: string; + + public downloadArchiveInProgress$: Observable<StateResource<unknown>> = of( + createEmptyStateResource(), + ); + + constructor(private binaryFileService: BinaryFileService) {} + + public downloadArchive(): void { + this.downloadArchiveInProgress$ = this.binaryFileService.downloadArchive(this.downloadUri); + } +} diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html index fbea31c02eff3f5d30abd9cf1c1ff7a73447f716..187ab55187039b6ca7ea2f9d6b1ab8e0cc456a4a 100644 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html +++ b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html @@ -23,17 +23,31 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<ozgcloud-spinner [stateResource]="fileListResource"> - <div class="vertical" data-test-id="file-list"> - <alfa-binary-file-container +<ozgcloud-spinner + *ngIf="binaryFileListStateResource.resource" + [stateResource]="binaryFileListStateResource" +> + <ods-attachment-wrapper [title]="title" data-test-id="file-list"> + <ods-attachment-header [title]="title"> + <alfa-download-archive-file-button-container + *ngIf="archiveDownloadUri" + data-test-class="download-archive-file-button" + [downloadUri]="archiveDownloadUri" + action-buttons + ></alfa-download-archive-file-button-container + ></ods-attachment-header> + <ng-container *ngFor=" - let binaryFileResource of fileListResource.resource + let binaryFileResource of binaryFileListStateResource.resource | toEmbeddedResources: fileListRel.FILE_LIST " - [file]="binaryFileResource" - [deletable]="deletable" - [downloadFileNamePrefix]="downloadFileNamePrefix" > - </alfa-binary-file-container> - </div> + <alfa-binary-file2-container + [file]="binaryFileResource" + [deletable]="deletable" + [downloadFileNamePrefix]="downloadFileNamePrefix" + > + </alfa-binary-file2-container> + </ng-container> + </ods-attachment-wrapper> </ozgcloud-spinner> diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.scss b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.scss deleted file mode 100644 index 1212c1617eca6dd57a7d70ae7069d282a3611ea7..0000000000000000000000000000000000000000 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.scss +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (C) 2022 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. - */ -.vertical { - display: flex; - flex-direction: column; - max-width: 100%; - align-items: flex-start; - margin: 0 -4px; - margin-top: 1rem; -} diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts index 07c0a783352ebfa2a91827c1216b71ca1736baae..a42980975bd6362343a865a3dae296bbd3191728 100644 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts +++ b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts @@ -21,24 +21,46 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { createEmptyStateResource, ToEmbeddedResourcesPipe } from '@alfa-client/tech-shared'; +import { BinaryFileResource } from '@alfa-client/binary-file-shared'; +import { ToEmbeddedResourcesPipe, createStateResource } from '@alfa-client/tech-shared'; +import { + existsAsHtmlElement, + getMockComponent, + notExistsAsHtmlElement, +} from '@alfa-client/test-utils'; import { SpinnerComponent } from '@alfa-client/ui'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import faker from '@faker-js/faker'; +import { ResourceUri } from '@ngxp/rest'; +import { AttachmentHeaderComponent, AttachmentWrapperComponent } from '@ods/system'; +import { + createBinaryFileListResource, + createBinaryFileResource, +} from 'libs/binary-file-shared/test/binary-file'; +import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; -import { BinaryFileContainerComponent } from './../binary-file-container/binary-file-container.component'; +import { BinaryFile2ContainerComponent } from '../binary-file2-container/binary-file2-container.component'; +import { DownloadArchiveFileButtonContainerComponent } from '../download-archive-file-button-container/download-archive-file-button-container.component'; import { VerticalBinaryFileListComponent } from './vertical-binary-file-list.component'; describe('VerticalBinaryFileListComponent', () => { let component: VerticalBinaryFileListComponent; let fixture: ComponentFixture<VerticalBinaryFileListComponent>; + const downloadArchiveFileButton: string = getDataTestClassOf('download-archive-file-button'); + + const binaryFile: BinaryFileResource = createBinaryFileResource(); + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ VerticalBinaryFileListComponent, ToEmbeddedResourcesPipe, - MockComponent(BinaryFileContainerComponent), + MockComponent(AttachmentWrapperComponent), + MockComponent(AttachmentHeaderComponent), + MockComponent(BinaryFile2ContainerComponent), MockComponent(SpinnerComponent), + MockComponent(DownloadArchiveFileButtonContainerComponent), ], }).compileComponents(); }); @@ -46,11 +68,80 @@ describe('VerticalBinaryFileListComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(VerticalBinaryFileListComponent); component = fixture.componentInstance; - component.fileListResource = createEmptyStateResource(); + component.binaryFileListStateResource = createStateResource( + createBinaryFileListResource([binaryFile]), + ); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + describe('binary file component', () => { + describe('should be called with', () => { + it('file', () => { + const comp: BinaryFile2ContainerComponent = getMockComponent( + fixture, + BinaryFile2ContainerComponent, + ); + + expect(comp.file).toBe(binaryFile); + }); + + it('deletable', () => { + const comp: BinaryFile2ContainerComponent = getMockComponent( + fixture, + BinaryFile2ContainerComponent, + ); + + expect(comp.deletable).toBe(component.deletable); + }); + + it('downloadFileNamePrefix', () => { + const comp: BinaryFile2ContainerComponent = getMockComponent( + fixture, + BinaryFile2ContainerComponent, + ); + + expect(comp.downloadFileNamePrefix).toBe(component.downloadFileNamePrefix); + }); + }); + }); + + describe('download archive button', () => { + const downloadUri: ResourceUri = faker.internet.url(); + + it('should be visible if uri exists', () => { + component.archiveDownloadUri = downloadUri; + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, downloadArchiveFileButton); + }); + + it('should be hidden if uri is undefined', () => { + component.archiveDownloadUri = undefined; + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, downloadArchiveFileButton); + }); + + describe('component should be called with', () => { + beforeEach(() => { + component.archiveDownloadUri = downloadUri; + fixture.detectChanges(); + }); + + it('downloadUri', () => { + const comp: DownloadArchiveFileButtonContainerComponent = getMockComponent( + fixture, + DownloadArchiveFileButtonContainerComponent, + ); + + expect(comp.downloadUri).toBe(downloadUri); + }); + }); + }); }); diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts index 3eb9096547a466caf4ac3b11ee935f728bee1745..363dc54b3bfb8ff2dc7f7bd5e00570afe9fe5565 100644 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts +++ b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts @@ -21,20 +21,22 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Input } from '@angular/core'; import { BinaryFileListLinkRel, BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { Component, Input } from '@angular/core'; +import { ResourceUri } from '@ngxp/rest'; @Component({ selector: 'alfa-vertical-binary-file-list', templateUrl: './vertical-binary-file-list.component.html', - styleUrls: ['./vertical-binary-file-list.component.scss'], }) export class VerticalBinaryFileListComponent { - @Input() fileListResource: StateResource<BinaryFileListResource> = + @Input() binaryFileListStateResource: StateResource<BinaryFileListResource> = createEmptyStateResource<BinaryFileListResource>(); @Input() downloadFileNamePrefix: string; + @Input() title: string = ''; @Input() deletable: boolean = false; + @Input() archiveDownloadUri: ResourceUri; readonly fileListRel = BinaryFileListLinkRel; } diff --git a/alfa-client/libs/design-component/src/index.ts b/alfa-client/libs/design-component/src/index.ts index 8670320bc61acad5f807315ea595229b26da068d..0ed0ab64368e0b3716f3ed6b1ab8d7da0a6fc2d7 100644 --- a/alfa-client/libs/design-component/src/index.ts +++ b/alfa-client/libs/design-component/src/index.ts @@ -1,5 +1,7 @@ export * from './lib/button-with-spinner/button-with-spinner.component'; +export * from './lib/download-button/download-button.component'; export * from './lib/form/file-upload-editor/file-upload-editor.component'; +export * from './lib/form/formcontrol-editor.abstract.component'; export * from './lib/form/single-file-upload-editor/single-file-upload-editor.component'; export * from './lib/form/text-editor/text-editor.component'; export * from './lib/form/textarea-editor/textarea-editor.component'; diff --git a/alfa-client/libs/design-component/src/lib/download-button/download-button.component.spec.ts b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b5b17755eea019979b1ec405107272c3f43b692 --- /dev/null +++ b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.spec.ts @@ -0,0 +1,48 @@ +import { createEmptyStateResource } from '@alfa-client/tech-shared'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DownloadButtonComponent } from './download-button.component'; + +describe('DownloadButtonComponent', () => { + let component: DownloadButtonComponent; + let fixture: ComponentFixture<DownloadButtonComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DownloadButtonComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(DownloadButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('isLoading', () => { + it('should return false', () => { + component.stateResource = createEmptyStateResource(); + + const isLoading: boolean = component.isLoading; + + expect(isLoading).toBeFalsy(); + }); + + it('should return true if stateResource is loading', () => { + component.stateResource = createEmptyStateResource(true); + + const isLoading: boolean = component.isLoading; + + expect(isLoading).toBeTruthy(); + }); + + it('should return true if stateResource is reloading', () => { + component.stateResource = { ...createEmptyStateResource(), reload: true }; + + const isLoading: boolean = component.isLoading; + + expect(isLoading).toBeTruthy(); + }); + }); +}); diff --git a/alfa-client/libs/design-component/src/lib/download-button/download-button.component.ts b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd69b20129739ad87ed4cd40744a858355b3fefc --- /dev/null +++ b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.ts @@ -0,0 +1,35 @@ +import { CommandResource } from '@alfa-client/command-shared'; +import { StateResource } from '@alfa-client/tech-shared'; +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ButtonComponent, SaveIconComponent, iconVariants } from '@ods/system'; +import { VariantProps } from 'class-variance-authority'; + +type IconVariants = VariantProps<typeof iconVariants>; + +@Component({ + selector: 'ods-download-button', + standalone: true, + imports: [CommonModule, ButtonComponent, SaveIconComponent], + template: `<ods-button + [dataTestId]="dataTestId" + variant="icon" + size="fit" + spinnerSize="small" + [isLoading]="isLoading" + (click)="clickEmitter.emit()" + > + <ods-save-icon icon [size]="size" class="fill-text"></ods-save-icon> + </ods-button>`, +}) +export class DownloadButtonComponent { + @Input() dataTestId: string = ''; + @Input() size: IconVariants['size'] = 'small'; + @Input() set stateResource(resource: StateResource<CommandResource>) { + this.isLoading = resource.loading || resource.reload; + } + + @Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); + + isLoading: boolean = false; +} diff --git a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts index 64341eff5a4cbcfc8a9b5730559f23e1acfe440b..bcd618c337bd126c861a20360a9ac56633bfa0ec 100644 --- a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts +++ b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts @@ -32,7 +32,6 @@ import { } from '@angular/forms'; import { FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { ValidationErrorComponent } from 'libs/ui/src/lib/ui/validation-error/validation-error.component'; import { MockComponent } from 'ng-mocks'; import { FileUploadEditorComponent } from './file-upload-editor.component'; @@ -51,7 +50,6 @@ describe('FileUploadEditorComponent', () => { await TestBed.configureTestingModule({ declarations: [ FileUploadEditorComponent, - MockComponent(ValidationErrorComponent), MockComponent(SpinnerIconComponent), MockComponent(FileUploadButtonComponent), ], diff --git a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts index d6e927851b53ccb0b3048b0f7562d10af3a75275..3c7992f6f52d3525ad32f74fd4282442d44dd1a8 100644 --- a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts @@ -1,5 +1,4 @@ import { StateResource, TechSharedModule } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { NgForOf } from '@angular/common'; import { Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core'; import { @@ -16,6 +15,7 @@ import { SpinnerIconComponent, } from '@ods/system'; import { uniqueId } from 'lodash-es'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ods-file-upload-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component.ts b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts similarity index 91% rename from alfa-client/libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component.ts rename to alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts index f0270e9c2fea92811bc6b8a1f3c0b2d115c3c240..a023a009d1780be33395159d25234da39285f745 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts @@ -21,16 +21,18 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Optional, Self } from '@angular/core'; -import { ControlValueAccessor, UntypedFormControl, NgControl } from '@angular/forms'; import { Issue } from '@alfa-client/tech-shared'; +import { Component, OnDestroy, OnInit, Optional, Self } from '@angular/core'; +import { ControlValueAccessor, NgControl, UntypedFormControl } from '@angular/forms'; import { isEmpty } from 'lodash-es'; import { Subscription } from 'rxjs'; @Component({ template: 'NO UI', }) -export abstract class FormControlEditorAbstractComponent implements ControlValueAccessor { +export abstract class FormControlEditorAbstractComponent + implements ControlValueAccessor, OnInit, OnDestroy +{ readonly fieldControl: UntypedFormControl = new UntypedFormControl(); public onChange = (text: string | Date) => undefined; public onTouched = () => undefined; diff --git a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts index 2a7ee6d4fc46926081e1aedbb5bf086f9fc63883..4d44ce0dcbb1165a9ddb6bad46a1537fee601d6d 100644 --- a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts +++ b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts @@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { ValidationErrorComponent } from 'libs/ui/src/lib/ui/validation-error/validation-error.component'; import { MockComponent } from 'ng-mocks'; import { SingleFileUploadEditorComponent } from './single-file-upload-editor.component'; @@ -18,7 +17,6 @@ describe('SingleFileUploadEditorComponent', () => { declarations: [ SingleFileUploadEditorComponent, FileUploadButtonComponent, - MockComponent(ValidationErrorComponent), MockComponent(SpinnerIconComponent), ], imports: [ReactiveFormsModule], diff --git a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts index 36db826a36c473a9012eafc2d3470ce05af011e8..8da41fe6bb90f102b7bad62c44fd230f0eb094c7 100644 --- a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts @@ -1,20 +1,15 @@ import { TechSharedModule, isNotNil } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { Component, EventEmitter, HostListener, Input, Output } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; import { uniqueId } from 'lodash-es'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ods-single-file-upload-editor', templateUrl: './single-file-upload-editor.component.html', standalone: true, - imports: [ - FileUploadButtonComponent, - SpinnerIconComponent, - ReactiveFormsModule, - TechSharedModule, - ], + imports: [FileUploadButtonComponent, SpinnerIconComponent, ReactiveFormsModule, TechSharedModule], }) export class SingleFileUploadEditorComponent extends FormControlEditorAbstractComponent { @Input() label: string = ''; 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 bd8034d8366f83cf4ddbee1e54c271fb932f2c9a..544b9312b03adba3e0e097e8eba8a20751e5b013 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 @@ -5,7 +5,7 @@ [autocomplete]="autocomplete" [variant]="variant" [attr.data-test-id]="(label | convertForDataTest) + '-text-editor'" - [required]="required" + [required]="isRequired" [focus]="focus" > <ods-validation-error 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 a1881546fe7309af8c1992f7c17645fe3e692ad4..c78a7554800ed6f61eb9d189617811e7f9d0490a 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 @@ -1,9 +1,9 @@ import { TechSharedModule } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { CommonModule } from '@angular/common'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { TextInputComponent } from '@ods/system'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; import { ValidationErrorComponent } from '../validation-error/validation-error.component'; @Component({ @@ -18,11 +18,11 @@ import { ValidationErrorComponent } from '../validation-error/validation-error.c ], templateUrl: './text-editor.component.html', }) -export class TextEditorComponent extends FormControlEditorAbstractComponent implements OnInit { +export class TextEditorComponent extends FormControlEditorAbstractComponent { @Input({ required: true }) label: string; @Input() autocomplete: 'off' | 'email' = 'off'; @Input() placeholder: string = ''; - @Input() required: boolean = false; + @Input() isRequired: boolean = false; @Input() focus: boolean = false; get variant(): string { diff --git a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html index 73ea744d07206658aafb6af0e95533b8d106960b..5f3959bd34260ac44c11db6a108f24583986b9b0 100644 --- a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html +++ b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html @@ -5,7 +5,7 @@ [rows]="rows" [variant]="variant" [attr.data-test-id]="(label | convertForDataTest) + '-textarea-editor'" - [required]="required" + [required]="isRequired" [focus]="focus" > <ods-validation-error diff --git a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts index 1514871a5a9e0d6032e96fd0b85cd3064726ff13..f78ee2c938fdb89b91a17060ba862ecadcff04aa 100644 --- a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts @@ -1,9 +1,9 @@ import { TechSharedModule } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { TextareaComponent } from '@ods/system'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; import { ValidationErrorComponent } from '../validation-error/validation-error.component'; @Component({ @@ -20,9 +20,9 @@ import { ValidationErrorComponent } from '../validation-error/validation-error.c }) export class TextareaEditorComponent extends FormControlEditorAbstractComponent { @Input({ required: true }) label: string; - @Input() placeholder: string; + @Input() placeholder: string = ''; @Input() rows: number = 10; - @Input() required: boolean = false; + @Input() isRequired: boolean = false; @Input() focus: boolean = false; get variant(): string { diff --git a/alfa-client/libs/design-system/src/index.ts b/alfa-client/libs/design-system/src/index.ts index 9f7b60a883c3ad40fc3bb29b3bd56d8a058d9197..57c220dc85cf06f66d1e23edd66bc7d9bff947ea 100644 --- a/alfa-client/libs/design-system/src/index.ts +++ b/alfa-client/libs/design-system/src/index.ts @@ -1,7 +1,8 @@ -export * from './lib/attachment-container/attachment-container.component'; +export * from './lib/attachment-header/attachment-header.component'; +export * from './lib/attachment-wrapper/attachment-wrapper.component'; export * from './lib/attachment/attachment.component'; -export * from './lib/bescheid-container/bescheid-container.component'; export * from './lib/bescheid-status-text/bescheid-status-text.component'; +export * from './lib/bescheid-wrapper/bescheid-wrapper.component'; export * from './lib/button-card/button-card.component'; export * from './lib/button/button.component'; export * from './lib/form/error-message/error-message.component'; @@ -15,6 +16,7 @@ export * from './lib/icons/bescheid-upload-icon/bescheid-upload-icon.component'; export * from './lib/icons/close-icon/close-icon.component'; export * from './lib/icons/exclamation-icon/exclamation-icon.component'; export * from './lib/icons/file-icon/file-icon.component'; +export * from './lib/icons/iconVariants'; export * from './lib/icons/save-icon/save-icon.component'; export * from './lib/icons/send-icon/send-icon.component'; export * from './lib/icons/spinner-icon/spinner-icon.component'; diff --git a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.spec.ts b/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.spec.ts deleted file mode 100644 index 5c799383d81b51dd6df2350ed794ed09c999df1f..0000000000000000000000000000000000000000 --- a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AttachmentContainerComponent } from './attachment-container.component'; - -describe('AttachmentContainerComponent', () => { - let component: AttachmentContainerComponent; - let fixture: ComponentFixture<AttachmentContainerComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AttachmentContainerComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(AttachmentContainerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.ts b/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.ts deleted file mode 100644 index 35de705fee041ebb34196879393dffcaecb76f26..0000000000000000000000000000000000000000 --- a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; - -@Component({ - selector: 'ods-attachment-container', - standalone: true, - imports: [CommonModule], - template: `<div - class="border-grayborder block overflow-hidden rounded-md border shadow empty:hidden" - > - <ng-content></ng-content> - </div>`, -}) -export class AttachmentContainerComponent {} diff --git a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.stories.ts b/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.stories.ts deleted file mode 100644 index dbb89342b65d8059a7eb55cce494d2ec53dac8f6..0000000000000000000000000000000000000000 --- a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.stories.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; - -import { AttachmentComponent } from '../attachment/attachment.component'; -import { AttachmentContainerComponent } from './attachment-container.component'; - -const meta: Meta<AttachmentContainerComponent> = { - title: 'Containers/Attachment container', - component: AttachmentContainerComponent, - parameters: { - docs: { - description: { - component: 'Container for multiple attachments', - }, - }, - }, - decorators: [ - moduleMetadata({ - imports: [AttachmentContainerComponent, AttachmentComponent], - }), - ], - excludeStories: /.*Data$/, - tags: ['autodocs'], -}; - -export default meta; -type Story = StoryObj<AttachmentContainerComponent>; - -export const Default: Story = { - render: () => ({ - template: `<ods-attachment-container> - <ods-attachment caption="Attachment" description="200 kB" fileType="pdf"></ods-attachment> - <ods-attachment caption="Second attachment" description="432 kB" fileType="doc"></ods-attachment> - </ods-attachment-container>`, - }), -}; diff --git a/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.spec.ts b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1484d6418804db087a5418a8eb26a8e41114398 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AttachmentHeaderComponent } from './attachment-header.component'; + +describe('AttachmentHeaderComponent', () => { + let component: AttachmentHeaderComponent; + let fixture: ComponentFixture<AttachmentHeaderComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AttachmentHeaderComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(AttachmentHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.ts b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dadeccb218137f87612c2d67a12c7087a6286927 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.ts @@ -0,0 +1,15 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ods-attachment-header', + standalone: true, + imports: [CommonModule], + template: `<div class="flex h-11 items-center justify-between px-3"> + <h4 class="text-sm font-medium text-text">{{ title }}</h4> + <ng-content select="[action-buttons]"></ng-content> + </div>`, +}) +export class AttachmentHeaderComponent { + @Input() title: string = ''; +} diff --git a/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.stories.ts b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..33a3d93237c69b82a28b08efaf36cc92f5970c5e --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.stories.ts @@ -0,0 +1,45 @@ +import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; + +import { DownloadButtonComponent } from '../../../../design-component/src/lib/download-button/download-button.component'; + +import { AttachmentComponent } from '../attachment/attachment.component'; +import { AttachmentHeaderComponent } from './attachment-header.component'; + +const meta: Meta<AttachmentHeaderComponent> = { + title: 'Attachment header', + component: AttachmentHeaderComponent, + parameters: { + docs: { + description: { + component: 'Header may contain title and/or buttons', + }, + }, + }, + decorators: [ + moduleMetadata({ + imports: [AttachmentHeaderComponent, AttachmentComponent, DownloadButtonComponent], + }), + ], + excludeStories: /.*Data$/, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj<AttachmentHeaderComponent>; + +export const Default: Story = { + args: { + title: 'Anhänge', + }, + argTypes: { + title: { + description: 'Title for group of files', + }, + }, + render: (args) => ({ + props: args, + template: `<ods-attachment-header ${argsToTemplate(args)}> + <ods-download-button action-buttons /> + </ods-attachment-header>`, + }), +}; diff --git a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.spec.ts b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.spec.ts similarity index 50% rename from alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.spec.ts rename to alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.spec.ts index 32b8fc6eadf7d114d8730894bd5d666e33b7bc81..bc771a072c51f229dd9c2d68262a32a2f3db915b 100644 --- a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.spec.ts +++ b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.spec.ts @@ -1,16 +1,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { BescheidContainerComponent } from './bescheid-container.component'; +import { AttachmentWrapperComponent } from './attachment-wrapper.component'; -describe('BescheidContainerComponent', () => { - let component: BescheidContainerComponent; - let fixture: ComponentFixture<BescheidContainerComponent>; +describe('AttachmentWrapperComponent', () => { + let component: AttachmentWrapperComponent; + let fixture: ComponentFixture<AttachmentWrapperComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [BescheidContainerComponent], + imports: [AttachmentWrapperComponent], }).compileComponents(); - fixture = TestBed.createComponent(BescheidContainerComponent); + fixture = TestBed.createComponent(AttachmentWrapperComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.ts b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..863cc92dffee0d1f7a1e2fd58636f40a6a39f1b5 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.ts @@ -0,0 +1,13 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'ods-attachment-wrapper', + standalone: true, + imports: [CommonModule], + styles: [ + ':host {@apply flex flex-col overflow-hidden rounded-md shadow shadow-grayborder border border-grayborder empty:hidden}', + ], + template: `<ng-content></ng-content>`, +}) +export class AttachmentWrapperComponent {} diff --git a/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.stories.ts b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..485acda2600b59166e331fe38bffba7392c1e783 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.stories.ts @@ -0,0 +1,47 @@ +import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; + +import { DownloadButtonComponent } from '../../../../design-component/src/lib/download-button/download-button.component'; + +import { AttachmentComponent } from '../attachment/attachment.component'; +import { AttachmentWrapperComponent } from './attachment-wrapper.component'; + +const meta: Meta<AttachmentWrapperComponent> = { + title: 'Wrappers/Attachment wrapper', + component: AttachmentWrapperComponent, + parameters: { + docs: { + description: { + component: 'Wrapper for multiple attachments', + }, + }, + }, + decorators: [ + moduleMetadata({ + imports: [AttachmentWrapperComponent, AttachmentComponent, DownloadButtonComponent], + }), + ], + excludeStories: /.*Data$/, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj<AttachmentWrapperComponent>; + +export const Default: Story = { + args: { + title: 'Anhänge', + }, + argTypes: { + title: { + description: 'Title for group of files', + }, + }, + render: (args) => ({ + props: args, + template: `<ods-attachment-wrapper ${argsToTemplate(args)}> + <ods-download-button action-buttons /> + <ods-attachment caption="Attachment" description="200 kB" fileType="pdf"></ods-attachment> + <ods-attachment caption="Second attachment" description="432 kB" fileType="doc"></ods-attachment> + </ods-attachment-wrapper>`, + }), +}; diff --git a/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.spec.ts b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..741c0bb800a3c711eb0771d36c00fa55feb38dee --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidWrapperComponent } from './bescheid-wrapper.component'; + +describe('BescheidWrapperComponent', () => { + let component: BescheidWrapperComponent; + let fixture: ComponentFixture<BescheidWrapperComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BescheidWrapperComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWrapperComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.ts b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.ts similarity index 54% rename from alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.ts rename to alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.ts index 16f18a8cc4c562567f48be52c279bba2aa139ac3..62f2564416b0855318c87c4fa3b0c2b2b9b47912 100644 --- a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.ts +++ b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.ts @@ -2,11 +2,11 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; @Component({ - selector: 'ods-bescheid-container', + selector: 'ods-bescheid-wrapper', standalone: true, imports: [CommonModule], - template: ` <article class="bg-background-150 flex flex-col gap-4 rounded-lg p-4"> + template: ` <article class="flex flex-col gap-4 rounded-lg bg-background-150 p-4"> <ng-content></ng-content> </article>`, }) -export class BescheidContainerComponent {} +export class BescheidWrapperComponent {} diff --git a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-comtainer.stories.ts b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.stories.ts similarity index 60% rename from alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-comtainer.stories.ts rename to alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.stories.ts index b0fc0da094793d9e46009e818b7f365a0f4f72ff..2d3a4ee07f081623052bb6b16e76cbb42dcd8c40 100644 --- a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-comtainer.stories.ts +++ b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.stories.ts @@ -1,17 +1,17 @@ import { moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; -import { AttachmentContainerComponent } from '../attachment-container/attachment-container.component'; +import { AttachmentWrapperComponent } from '../attachment-wrapper/attachment-wrapper.component'; import { AttachmentComponent } from '../attachment/attachment.component'; import { BescheidStatusTextComponent } from '../bescheid-status-text/bescheid-status-text.component'; -import { BescheidContainerComponent } from './bescheid-container.component'; +import { BescheidWrapperComponent } from './bescheid-wrapper.component'; -const meta: Meta<BescheidContainerComponent> = { - title: 'Containers/Bescheid container', - component: BescheidContainerComponent, +const meta: Meta<BescheidWrapperComponent> = { + title: 'Wrappers/Bescheid wrapper', + component: BescheidWrapperComponent, parameters: { docs: { description: { - component: 'Container for bescheid information', + component: 'Wrapper for bescheid information', }, }, }, @@ -19,8 +19,8 @@ const meta: Meta<BescheidContainerComponent> = { moduleMetadata({ imports: [ AttachmentComponent, - AttachmentContainerComponent, - BescheidContainerComponent, + AttachmentWrapperComponent, + BescheidWrapperComponent, BescheidStatusTextComponent, ], }), @@ -30,20 +30,20 @@ const meta: Meta<BescheidContainerComponent> = { }; export default meta; -type Story = StoryObj<BescheidContainerComponent>; +type Story = StoryObj<BescheidWrapperComponent>; export const Default: Story = { render: () => ({ - template: `<ods-bescheid-container> + template: `<ods-bescheid-wrapper> <ods-bescheid-status-text bewilligt="true" dateText="13.11.2024" [hasBescheidDraft]="false" ></ods-bescheid-status-text> - <ods-attachment-container> + <ods-attachment-wrapper> <ods-attachment caption="Attachment" description="200 kB" fileType="pdf"></ods-attachment> <ods-attachment caption="Second attachment" description="432 kB" fileType="doc"></ods-attachment> - </ods-attachment-container> - </ods-bescheid-container>`, + </ods-attachment-wrapper> + </ods-bescheid-wrapper>`, }), }; 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 19d22368fd3dd405970df6c7a77790783ee19d77..58ba7ec0d96c8c0eb0516c9fbc367b885d686837 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 @@ -2,19 +2,22 @@ import { CommonModule } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { VariantProps, cva } from 'class-variance-authority'; +import { IconVariants } from '../icons/iconVariants'; import { SpinnerIconComponent } from '../icons/spinner-icon/spinner-icon.component'; export const buttonVariants = cva( - 'flex cursor-pointer items-center gap-4 rounded-md font-medium disabled:cursor-wait text-sm min-w-32 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ozgblue-800', + 'flex cursor-pointer items-center gap-4 rounded-md font-medium disabled:cursor-wait text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary', { variants: { variant: { primary: 'hover:enabled:bg-primary-hover bg-primary text-white shadow-sm', outline: 'border border-primary bg-background-50 text-primary hover:enabled:bg-background-100', + icon: 'border border-transparent hover:border-primary', }, size: { - medium: 'h-9 py-2 px-4', + medium: 'h-9 py-2 px-4 min-w-32', + fit: 'h-fit p-2', }, }, defaultVariants: { @@ -39,8 +42,8 @@ type ButtonVariants = VariantProps<typeof buttonVariants>; (click)="clickEmitter.emit()" > <ng-content *ngIf="!isLoading" select="[icon]"></ng-content> - <ods-spinner-icon *ngIf="isLoading" size="medium"></ods-spinner-icon> - <div class="flex-grow">{{ text }}</div> + <ods-spinner-icon *ngIf="isLoading" [size]="spinnerSize"></ods-spinner-icon> + <div *ngIf="text" class="flex-grow">{{ text }}</div> </button>`, }) export class ButtonComponent { @@ -49,6 +52,7 @@ export class ButtonComponent { @Input() isLoading: boolean = false; @Input() variant: ButtonVariants['variant']; @Input() size: ButtonVariants['size']; + @Input() spinnerSize: IconVariants['size'] = 'medium'; @Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); diff --git a/alfa-client/libs/design-system/src/lib/button/button.stories.ts b/alfa-client/libs/design-system/src/lib/button/button.stories.ts index 5db5e2e20ad61881bf767c822890d900898544ea..c5ac6ce8b3312deac5a00ce17af2a73f073565e6 100644 --- a/alfa-client/libs/design-system/src/lib/button/button.stories.ts +++ b/alfa-client/libs/design-system/src/lib/button/button.stories.ts @@ -1,6 +1,6 @@ import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; -import { FileIconComponent } from '../icons/file-icon/file-icon.component'; +import { SaveIconComponent } from '../icons/save-icon/save-icon.component'; import { ButtonComponent } from './button.component'; const meta: Meta<ButtonComponent> = { @@ -8,7 +8,7 @@ const meta: Meta<ButtonComponent> = { component: ButtonComponent, decorators: [ moduleMetadata({ - imports: [ButtonComponent, FileIconComponent], + imports: [ButtonComponent, SaveIconComponent], }), ], excludeStories: /.*Data$/, @@ -23,6 +23,8 @@ export const Default: Story = { text: 'Hello world!', isLoading: false, variant: 'primary', + size: 'medium', + spinnerSize: 'medium', }, argTypes: { variant: { @@ -37,11 +39,12 @@ export const WithIcon: Story = { args: { text: 'I have an icon', variant: 'outline', + size: 'medium', }, render: (args) => ({ props: args, template: `<ods-button ${argsToTemplate(args)}> - <ods-file-icon icon fileType='pdf' size='small'></ods-file-icon> + <ods-save-icon icon size='small'></ods-save-icon> </ods-button>`, }), }; @@ -50,5 +53,19 @@ export const IsLoading: Story = { args: { text: 'Loading...', isLoading: true, + size: 'medium', }, }; + +export const OnlyIcon: Story = { + args: { + variant: 'icon', + size: 'fit', + }, + render: (args) => ({ + props: args, + template: `<ods-button ${argsToTemplate(args)}> + <ods-save-icon icon size='medium'></ods-save-icon> + </ods-button>`, + }), +}; 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 87770f419954a3c32d59424b0aab2a6a6c05a96b..e136a50bb991af6df9384d7ceca287879b3cdcc8 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 @@ -6,12 +6,14 @@ import { cva, VariantProps } from 'class-variance-authority'; import { ErrorMessageComponent } from '../error-message/error-message.component'; const textInputVariants = cva( - 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base leading-5 text-text focus:border-primary focus:ring-primary outline-none', + 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base leading-5 text-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2', { variants: { variant: { - default: 'border-primary-600/50', - error: 'border-error', + default: + 'border-primary-600/50 focus-visible:outline-focus focus-visible:border-background-200 hover:border-primary-hover', + error: + 'border-error/50 hover:border-error focus-visible:outline-error focus-visible:border-background-200', }, }, defaultVariants: { @@ -27,9 +29,8 @@ type TextInputVariants = VariantProps<typeof textInputVariants>; imports: [CommonModule, ErrorMessageComponent, ReactiveFormsModule, TechSharedModule], template: ` <div> - <label [for]="id" class="text-md mb-2 block font-medium text-text" - >{{ label }} - <ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> + <label [for]="id" class="text-md mb-2 block font-medium text-text"> + {{ inputLabel }}<ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> </label> <div class="mt-2"> <input @@ -39,9 +40,9 @@ type TextInputVariants = VariantProps<typeof textInputVariants>; [ngClass]="textInputVariants({ variant })" [placeholder]="placeholder" [autocomplete]="autocomplete" - [required]="required" + [attr.aria-required]="required" [attr.aria-invalid]="variant === 'error'" - [attr.data-test-id]="(label | convertForDataTest) + '-text-input'" + [attr.data-test-id]="(inputLabel | convertForDataTest) + '-text-input'" #inputElement /> </div> @@ -52,7 +53,10 @@ type TextInputVariants = VariantProps<typeof textInputVariants>; export class TextInputComponent { @ViewChild('inputElement') inputElement: ElementRef; - @Input({ required: true }) label: string; + @Input({ required: true }) set label(label: string) { + this.inputLabel = label; + this.id = convertForDataTest(label); + } @Input() placeholder: string = ''; @Input() autocomplete: string = 'off'; @Input() variant: TextInputVariants['variant']; @@ -65,9 +69,7 @@ export class TextInputComponent { } } + inputLabel: string; + id: string; textInputVariants = textInputVariants; - - get id(): string { - return convertForDataTest(this.label); - } } diff --git a/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts b/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts index 21e6c863afcc41c876cdfd0fa45704770ce39d56..90a4fa85f7dcb0b5a55f6a926a0d08ab63e332a9 100644 --- a/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts +++ b/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts @@ -1,16 +1,18 @@ -import { convertForDataTest, TechSharedModule } from '@alfa-client/tech-shared'; +import { TechSharedModule, convertForDataTest } from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { cva, VariantProps } from 'class-variance-authority'; +import { VariantProps, cva } from 'class-variance-authority'; const textareaVariants = cva( - 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base text-text leading-5 focus:border-primary focus:ring-primary outline-none', + 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base text-text leading-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2', { variants: { variant: { - default: 'border-primary-600/50', - error: 'border-error', + default: + 'border-primary-600/50 focus-visible:outline-focus focus-visible:border-background-200 hover:border-primary-hover', + error: + 'border-error/50 hover:border-error focus-visible:outline-error focus-visible:border-background-200', }, }, defaultVariants: { @@ -26,9 +28,8 @@ type TextareaVariants = VariantProps<typeof textareaVariants>; imports: [CommonModule, ReactiveFormsModule, TechSharedModule], template: ` <div class="mt-2"> - <label [for]="id" class="text-md mb-2 block font-medium text-text" - >{{ label }} - <ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> + <label [for]="id" class="text-md mb-2 block font-medium text-text"> + {{ inputLabel }}<ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> </label> <textarea [id]="id" @@ -37,9 +38,9 @@ type TextareaVariants = VariantProps<typeof textareaVariants>; [ngClass]="textareaVariants({ variant })" [placeholder]="placeholder" [autocomplete]="autocomplete" - [required]="required" + [attr.aria-required]="required" [attr.aria-invalid]="variant === 'error'" - [attr.data-test-id]="(label | convertForDataTest) + '-textarea'" + [attr.data-test-id]="(inputLabel | convertForDataTest) + '-textarea'" #textAreaElement ></textarea> <ng-content select="[error]"></ng-content> @@ -49,7 +50,10 @@ type TextareaVariants = VariantProps<typeof textareaVariants>; export class TextareaComponent { @ViewChild('textAreaElement') textAreaElement: ElementRef; - @Input({ required: true }) label!: string; + @Input({ required: true }) set label(label: string) { + this.inputLabel = label; + this.id = convertForDataTest(label); + } @Input({ required: true }) placeholder!: string; @Input() error: string; @Input() rows: number = 3; @@ -64,9 +68,7 @@ export class TextareaComponent { } } + inputLabel: string; + id: string; textareaVariants = textareaVariants; - - get id(): string { - return convertForDataTest(this.label); - } } diff --git a/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts index dea522781c4adc45ddfa582fd86fa7f52f5774f5..30a34056cee8da2a6e01385790c7c8e48cee4431 100644 --- a/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-attachment-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts index 3a7f86c81501c7dc5722b5f868bf1c12fa05c615..d9b829b2269a676de6cf15fed9e50e7cdd9081fa 100644 --- a/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-bescheid-generate-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts index 3947b891b5f3d07cd29fd9a6c0f3a2bb9c2f6089..cc265fdbb039365e1eeae63fc69b339578493eba 100644 --- a/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-bescheid-upload-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts index 81c6ce8e91c4ab7fab1ef6e4e9f0bcf6dfc8f7c2..5de688d73e98d67b1296d1519332fd7e3b7c9660 100644 --- a/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-close-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts index 9dda95ea08ca28bee431be67e9400e6ccaa6f0ec..c425c77aaa40b0efa0d0e2dd5b14decbad216f5e 100644 --- a/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-exclamation-icon', 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 544495d4fab1c914f1126ffa182911cdb23d7a10..e387e2d121ea54836748f7ee9d04ba3923ad1f56 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 @@ -4,7 +4,7 @@ import { VariantProps, cva } from 'class-variance-authority'; import { SpinnerIconComponent } from '../spinner-icon/spinner-icon.component'; -const fileiconVariants = cva('', { +const fileiconVariants = cva('fill-ozggray-300', { variants: { fileType: { doc: 'fill-doc', diff --git a/alfa-client/libs/design-system/src/lib/icons/IconClasses.ts b/alfa-client/libs/design-system/src/lib/icons/iconVariants.ts similarity index 100% rename from alfa-client/libs/design-system/src/lib/icons/IconClasses.ts rename to alfa-client/libs/design-system/src/lib/icons/iconVariants.ts diff --git a/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts index 405d1199cd3837498a19164e319107b1b5a706d7..b2d22480f7100aefc09ed77c3d2d499eebef01bf 100644 --- a/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-save-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts index 60205dd2942b8c0a47fd75f9ffe699a731c33061..6a34bb7ebb5b3b019a8254bf2f723eb93ac2b58e 100644 --- a/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-send-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts index 05aaf8d9aab8a37f8caa3f078f47b9dab1e96117..321829613a72fd018fbadd3b9117c3f16e119cc8 100644 --- a/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts @@ -1,7 +1,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-spinner-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts index 7b71c7b5d4dc15511d67441582c2539c7abea06b..26bb89c266ab88212c87bf6b0beba4874843fb2c 100644 --- a/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-stamp-icon', diff --git a/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css b/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css index ef516cdcbe6c4023552a81647ff66826ae0c9ab9..7e997ffe4f9bfb19811dc99c99a708dd8d92e741 100644 --- a/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css +++ b/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css @@ -7,6 +7,7 @@ :root { --warning: 38 92% 50%; --color-error: 0 71% 49%; + --color-focus: 212 64% 47%; --color-background-secondary: 0 0% 98%; --color-mainbg: 0 0% 100%; @@ -20,7 +21,7 @@ --color-background-150: 0 0% 95%; --color-background-200: 0 0% 90%; - --color-grayborder: 0 0% 75%; + --color-grayborder: 0 0% 92%; --color-bewilligt-100: 122 100% 92%; --color-bewilligt-700: 122 100% 29%; @@ -36,6 +37,7 @@ .dark { --color-error: 0 99% 72%; + --color-focus: 43 100% 48%; --color-background-secondary: 0 0% 16%; --color-mainbg: 0 0% 14%; diff --git a/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js b/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js index a207c9b06a2b19caa92f6abda9a783ae20ea61cd..1c7b298d5afa17aae60ab57b36b9152cec514121 100644 --- a/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js +++ b/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js @@ -98,6 +98,7 @@ module.exports = { whitetext: 'hsl(var(--color-text-white) / <alpha-value>)', warning: 'hsl(var(--warning))', error: 'hsl(var(--color-error))', + focus: 'hsl(var(--color-focus))', }, }, }, diff --git a/alfa-client/libs/design-system/test/helm/network_policy_test.yaml b/alfa-client/libs/design-system/test/helm/network_policy_test.yaml index 7e828b00c019b1f771e7c6adc373b09a431e0a76..7820c1acdcdfafa48427c049c6292757ba2ae6f5 100644 --- a/alfa-client/libs/design-system/test/helm/network_policy_test.yaml +++ b/alfa-client/libs/design-system/test/helm/network_policy_test.yaml @@ -28,20 +28,26 @@ release: namespace: by-helm-test templates: - templates/network_policy.yaml -set: - networkPolicy: - dnsServerNamespace: kube-system - ssoPublicIp: 1.1.1.1/32 + tests: - it: should match apiVersion + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - isAPIVersion: of: networking.k8s.io/v1 - it: should match kind + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - isKind: of: NetworkPolicy - it: validate metadata + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - equal: path: metadata @@ -75,7 +81,6 @@ tests: - it: should add additionalIngressConfig local set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-namespace-dns additionalIngressConfigLocal: - from: @@ -93,7 +98,6 @@ tests: - it: should add additionalIngressConfig global set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-namespace-dns additionalIngressConfigGlobal: - from: @@ -112,7 +116,6 @@ tests: - it: should add additionalEgressConfig local set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-dns-namespace additionalEgressConfigLocal: - to: @@ -129,7 +132,6 @@ tests: - it: should add additionalEgressConfig global set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-dns-namespace additionalEgressConfigGlobal: - to: @@ -155,9 +157,23 @@ tests: - it: test network policy unset should be disabled set: networkPolicy: - ssoPublicIp: 1.1.1.1 disabled: false dnsServerNamespace: test-dns-server-namespace asserts: - hasDocuments: count: 1 + - it: test network policy dnsServerNamespace must be set message + set: + networkPolicy: + disabled: false + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + - it: test network policy should be enabled by default + set: + networkPolicy: + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 \ No newline at end of file diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html index 05831b3b1608abfbd8d66d39c61a4e4e929e8775..a06ec149adf4dcaf0ed23c913dba68d720bbdfbb 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html @@ -23,5 +23,5 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<alfa-vertical-binary-file-list [fileListResource]="attachments$ | async"> +<alfa-vertical-binary-file-list [binaryFileListStateResource]="attachments$ | async"> </alfa-vertical-binary-file-list> diff --git a/alfa-client/libs/tech-shared/src/lib/error/error.util.spec.ts b/alfa-client/libs/tech-shared/src/lib/error/error.util.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..118b8869a0f9a95cba5d73c84b0eb6be5f94bfb8 --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/error/error.util.spec.ts @@ -0,0 +1,132 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { createApiError, createHttpErrorResponse, createIssue } from 'libs/tech-shared/test/error'; +import { MessageCode } from '../message-code'; +import { ApiError, Issue } from '../tech.model'; +import { + getApiErrorFromHttpErrorResponse, + isApiError, + isNotApiError, + isResourceNotFoundError, + isServiceUnavailableMessageCode, +} from './error.util'; + +describe('error.util', () => { + describe('isApiError', () => { + it('should return true if parameter is ApiError', () => { + const apiError: ApiError = createApiError(); + + const result: boolean = isApiError(apiError); + + expect(result).toBeTruthy(); + }); + + it('should return false if empty ApiError.Issues property', () => { + const apiError: ApiError = createApiError([]); + + const result: boolean = isApiError(apiError); + + expect(result).toBeFalsy(); + }); + + it('should return false if parameter is no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = createHttpErrorResponse(); + + const result: boolean = isApiError(httpErrorResponse); + + expect(result).toBeFalsy(); + }); + }); + + describe('isNotApiError', () => { + it('should return true if parameter is no ApiError', () => { + const result: boolean = isNotApiError(createHttpErrorResponse()); + + expect(result).toBeTruthy(); + }); + + it('should return false if parameter is ApiError', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isNotApiError(apiError); + + expect(result).toBeFalsy(); + }); + }); + + describe('isServiceUnavailableMessageCode', () => { + it('should return true if SERVICE_UNAVAILABLE error code', () => { + const issue: Issue = { ...createIssue(), messageCode: MessageCode.SERVICE_UNAVAILABLE }; + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isServiceUnavailableMessageCode(apiError); + + expect(result).toBeTruthy(); + }); + + it('should return false if not SERVICE_UNAVAILABLE error code', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isServiceUnavailableMessageCode(apiError); + + expect(result).toBeFalsy(); + }); + + it('should return false if input parameter is no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = new HttpErrorResponse({}); + + const result: boolean = isServiceUnavailableMessageCode(httpErrorResponse); + + expect(result).toBeFalsy(); + }); + }); + + describe('isResourceNotFoundError', () => { + it('should return true if RESOURCE_NOT_FOUND error code', () => { + const issue: Issue = { ...createIssue(), messageCode: MessageCode.RESOURCE_NOT_FOUND }; + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isResourceNotFoundError(apiError); + + expect(result).toBeTruthy(); + }); + + it('should return false if not RESOURCE_NOT_FOUND error code', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isResourceNotFoundError(apiError); + + expect(result).toBeFalsy(); + }); + + it('should return false if input parameter is no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = new HttpErrorResponse({}); + + const result: boolean = isResourceNotFoundError(httpErrorResponse); + + expect(result).toBeFalsy(); + }); + }); + + describe('getApiErrorFromHttpErrorResponse', () => { + it('should return ApiError', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + const httpErrorResponse: HttpErrorResponse = createHttpErrorResponse(apiError); + + const result: ApiError = getApiErrorFromHttpErrorResponse(httpErrorResponse); + + expect(result).toBe(apiError); + }); + + it('should return undefined if no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = new HttpErrorResponse({}); + + const result: ApiError = getApiErrorFromHttpErrorResponse(httpErrorResponse); + + expect(result).toBeUndefined(); + }); + }); +}); diff --git a/alfa-client/libs/tech-shared/src/lib/error/error.util.ts b/alfa-client/libs/tech-shared/src/lib/error/error.util.ts index 81c2a8d62e817d5e4907c194bf9dd62bfa2fd3a1..00271229b4f5fe102b8ebb7ebe92d43a55465d3d 100644 --- a/alfa-client/libs/tech-shared/src/lib/error/error.util.ts +++ b/alfa-client/libs/tech-shared/src/lib/error/error.util.ts @@ -22,16 +22,32 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { HttpErrorResponse } from '@angular/common/http'; -import { isNil } from 'lodash-es'; +import { has } from 'lodash-es'; import { MessageCode } from '../message-code'; -import { ApiError, HttpError } from '../tech.model'; +import { ApiError } from '../tech.model'; -export function isApiError(value: any): boolean { - return !isNil(value?.issues) && !isNil(value.issues[0]); +export function isApiError(error: unknown): boolean { + return has(error, 'issues[0]'); } -export function isServiceUnavailableMessageCode(error: ApiError): boolean { - return error.issues[0].messageCode == MessageCode.SERVICE_UNAVAILABLE; +export function isNotApiError(error: unknown): boolean { + return !isApiError(error); +} + +export function isServiceUnavailableMessageCode(error: unknown): boolean { + if (isNotApiError(error)) { + return false; + } + + return (error as ApiError).issues[0].messageCode === MessageCode.SERVICE_UNAVAILABLE; +} + +export function isResourceNotFoundError(error: unknown): boolean { + if (isNotApiError(error)) { + return false; + } + + return (error as ApiError).issues[0].messageCode === MessageCode.RESOURCE_NOT_FOUND; } export function getApiErrorFromHttpErrorResponse(httpErrorResponse: HttpErrorResponse): ApiError { diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts index e7ff1676a507c878018db9eb3cc4e1de38b2d972..10709bf89427a487644c718947970ac429a1a6ba 100644 --- a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts @@ -2,32 +2,31 @@ import { Resource } from '@ngxp/rest'; import { createDummyResource } from 'libs/tech-shared/test/resource'; import { HasAnyLinkPipe } from './has-any-link.pipe'; +import * as TechUtil from '../tech.util'; + describe('HasAnyLinkPipe', () => { const oneLink: string = 'one'; const anotherLink: string = 'another'; const resource: Resource = createDummyResource([oneLink, anotherLink]); const pipe: HasAnyLinkPipe = new HasAnyLinkPipe(); - it('should return true if resource has at least on link', () => { - const result: boolean = pipe.transform(resource, oneLink, 'notExists'); + describe('transform', () => { + let hasAnyLinkSpy: jest.SpyInstance<boolean>; - expect(result).toBe(true); - }); - it('resource return true if resoure has multiple links', () => { - const result: boolean = pipe.transform(resource, oneLink, anotherLink); + beforeEach(() => { + hasAnyLinkSpy = jest.spyOn(TechUtil, 'hasAnyLink').mockReturnValue(true); + }); - expect(result).toBe(true); - }); + it('should call has any link', () => { + pipe.transform(resource, oneLink); - it('should return false if resource has no of given links', () => { - const result: boolean = pipe.transform(resource, 'notExists', 'notExistsToo'); - - expect(result).toBe(false); - }); + expect(hasAnyLinkSpy).toHaveBeenCalledWith(resource, oneLink); + }); - it('should return false if resource is null', () => { - const result: boolean = pipe.transform(null, 'notExists', 'notExistsToo'); + it('should return value', () => { + const result: boolean = pipe.transform(resource, oneLink, anotherLink); - expect(result).toBe(false); + expect(result).toBe(true); + }); }); }); diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts index a6627c5fd1072763525025f304082f42749be211..b2827a5cbad9d558ed5666ed506cb9cfa38411ea 100644 --- a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts +++ b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts @@ -1,12 +1,11 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { hasLink, Resource } from '@ngxp/rest'; -import { isEmpty } from 'lodash-es'; +import { Resource } from '@ngxp/rest'; +import { LinkRelationName } from '../resource/resource.model'; +import { hasAnyLink } from '../tech.util'; @Pipe({ name: 'hasAnyLink' }) export class HasAnyLinkPipe implements PipeTransform { - transform(resource: Resource, ...links: string[]) { - return !isEmpty( - links.map((link) => hasLink(resource, link)).filter((hasLink) => hasLink === true), - ); + transform(resource: Resource, ...links: LinkRelationName[]): boolean { + return hasAnyLink(resource, ...links); } } diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.spec.ts b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3353c6cdb3b8957c4c6ae71f849c4a504b700cd --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.spec.ts @@ -0,0 +1,32 @@ +import { Resource } from '@ngxp/rest'; +import { createDummyResource } from 'libs/tech-shared/test/resource'; +import { NotHasAnyLinkPipe } from './not-has-any-link.pipe'; + +import * as TechUtil from '../tech.util'; + +describe('NotHasAnyLinkPipe', () => { + const oneLink: string = 'one'; + const anotherLink: string = 'another'; + const resource: Resource = createDummyResource([oneLink, anotherLink]); + const pipe: NotHasAnyLinkPipe = new NotHasAnyLinkPipe(); + + describe('transform', () => { + let notHasAnyLinkSpy: jest.SpyInstance<boolean>; + + beforeEach(() => { + notHasAnyLinkSpy = jest.spyOn(TechUtil, 'notHasAnyLink').mockReturnValue(true); + }); + + it('should call not has any link', () => { + pipe.transform(resource, oneLink); + + expect(notHasAnyLinkSpy).toHaveBeenCalledWith(resource, oneLink); + }); + + it('should return value', () => { + const result: boolean = pipe.transform(resource, oneLink, anotherLink); + + expect(result).toBe(true); + }); + }); +}); diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.ts b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..f207bca8edccd5f6229207b9cfb0c8d9204e3b92 --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Resource } from '@ngxp/rest'; +import { LinkRelationName } from '../resource/resource.model'; +import { notHasAnyLink } from '../tech.util'; + +@Pipe({ name: 'notHasAnyLink' }) +export class NotHasAnyLinkPipe implements PipeTransform { + transform(resource: Resource, ...links: LinkRelationName[]): boolean { + return notHasAnyLink(resource, ...links); + } +} diff --git a/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts b/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts index ecb138d23695a7111038967cf2d80147cbac5206..331031b298487fcfa77df7c10041a33cfedec7bb 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts @@ -40,6 +40,7 @@ import { FormatToPrettyDatePipe } from './pipe/format-to-pretty-date.pipe'; import { GetUrlPipe } from './pipe/get-url.pipe'; import { HasAnyLinkPipe } from './pipe/has-any-link.pipe'; import { HasLinkPipe } from './pipe/has-link.pipe'; +import { NotHasAnyLinkPipe } from './pipe/not-has-any-link.pipe'; import { NotHasLinkPipe } from './pipe/not-has-link.pipe'; import { ToEmbeddedResourcesPipe } from './pipe/to-embedded-resource.pipe'; import { ToResourceUriPipe } from './pipe/to-resource-uri.pipe'; @@ -57,6 +58,7 @@ import { ToTrafficLightPipe } from './pipe/to-traffic-light.pipe'; HasLinkPipe, HasAnyLinkPipe, NotHasLinkPipe, + NotHasAnyLinkPipe, ToResourceUriPipe, ToTrafficLightPipe, ToTrafficLightTooltipPipe, @@ -77,6 +79,7 @@ import { ToTrafficLightPipe } from './pipe/to-traffic-light.pipe'; HasLinkPipe, HasAnyLinkPipe, NotHasLinkPipe, + NotHasAnyLinkPipe, ToResourceUriPipe, ToTrafficLightPipe, ToTrafficLightTooltipPipe, diff --git a/alfa-client/libs/tech-shared/src/lib/tech.model.ts b/alfa-client/libs/tech-shared/src/lib/tech.model.ts index 3d9ca01c2225d3550fd9b8832a8feac5a3b4e50e..c85852676e5184152aa17856cca07bd33b3a3223 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech.model.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech.model.ts @@ -68,6 +68,7 @@ export enum HttpMethod { export enum HttpHeader { ACCEPT = 'Accept', CONTENT_DISPOSITION = 'content-disposition', + LOCATION = 'Location', } export interface BlobWithFileName { diff --git a/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts b/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts index 446e4961c800953f06a005d7a36c2eb2e643ef91..6184389586142f4acbdc09bb20ffe793ac3424ff 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts @@ -23,6 +23,9 @@ */ import { faker } from '@faker-js/faker'; +import { Resource } from '@ngxp/rest'; +import { createDummyResource } from 'libs/tech-shared/test/resource'; +import { LinkRelationName } from './resource/resource.model'; import { EMPTY_STRING, allEmpty, @@ -32,11 +35,13 @@ import { encodeUrlForEmbedding, getFirstLetter, getStringValue, + hasAnyLink, hasContent, hasMinLength, isNotEmpty, isNotNil, isNotNull, + notHasAnyLink, replaceAllWhitespaces, replacePlaceholder, replacePlaceholders, @@ -363,4 +368,50 @@ describe('TechUtil', () => { expect(value).toBeUndefined(); }); }); + + describe('has any link', () => { + const oneLink: string = 'one'; + const anotherLink: string = 'another'; + const resource: Resource = createDummyResource([oneLink, anotherLink]); + + it('should return true if resource has at least on link', () => { + const result: boolean = hasAnyLink(resource, oneLink, 'notExists'); + + expect(result).toBe(true); + }); + + it('resource return true if resoure has multiple links', () => { + const result: boolean = hasAnyLink(resource, oneLink, anotherLink); + + expect(result).toBe(true); + }); + + it('should return false if resource has no of given links', () => { + const result: boolean = hasAnyLink(resource, 'notExists', 'notExistsToo'); + + expect(result).toBe(false); + }); + + it('should return false if resource is null', () => { + const result: boolean = hasAnyLink(null, 'notExists', 'notExistsToo'); + + expect(result).toBe(false); + }); + }); + + describe('not has any link', () => { + const link: LinkRelationName = 'one'; + const otherLink: LinkRelationName = 'two'; + + it('should return false if at least one link exists', () => { + const result: boolean = notHasAnyLink(createDummyResource([link]), link, otherLink); + + expect(result).toBe(false); + }); + it('should return true if no link exists', () => { + const result: boolean = notHasAnyLink(createDummyResource(), link, otherLink); + + expect(result).toBe(true); + }); + }); }); diff --git a/alfa-client/libs/tech-shared/src/lib/tech.util.ts b/alfa-client/libs/tech-shared/src/lib/tech.util.ts index 0d227ab5dd449507c3b3b9da22b753c8d82f3799..55e881a99ac582741b7bea58ddde81b00c5c11b0 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech.util.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech.util.ts @@ -21,9 +21,11 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { Resource, hasLink } from '@ngxp/rest'; import { Base64 } from 'js-base64'; import { isEmpty, isNil, isNull, isUndefined } from 'lodash-es'; import { sanitize } from 'sanitize-filename-ts'; +import { LinkRelationName } from './resource/resource.model'; import { ApiError } from './tech.model'; export const EMPTY_STRING: string = ''; @@ -143,3 +145,15 @@ export function convertToBoolean(booleanStr: string): boolean { export function joinAndTrim(inputStrings: string[], delimiter: string = ' ') { return inputStrings.join(delimiter).trim(); } + +export function notHasAnyLink(resource: Resource, ...links: LinkRelationName[]): boolean { + return !hasAnyLink(resource, ...links); +} + +export function hasAnyLink(resource: Resource, ...links: LinkRelationName[]): boolean { + return !isEmpty( + links + .map((link: LinkRelationName) => hasLink(resource, link)) + .filter((hasLink: boolean) => hasLink === true), + ); +} diff --git a/alfa-client/libs/ui/src/index.ts b/alfa-client/libs/ui/src/index.ts index 233b0596ed17ce89cf3bc4dd6f0f3a49e94ce87c..ed84f3db5d20804637786f2d47d4e2ad8fed0f65 100644 --- a/alfa-client/libs/ui/src/index.ts +++ b/alfa-client/libs/ui/src/index.ts @@ -37,7 +37,6 @@ export * from './lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.model'; export * from './lib/ui/editor/date-editor/date-editor.component'; export * from './lib/ui/editor/enum-editor/enum-editor.component'; export * from './lib/ui/editor/enum-editor/enum-editor.model'; -export * from './lib/ui/editor/formcontrol-editor.abstract.component'; export * from './lib/ui/editor/text-editor/text-editor.component'; export * from './lib/ui/editor/textarea-editor/textarea-editor.component'; export * from './lib/ui/expansion-panel/expansion-panel.component'; diff --git a/alfa-client/libs/ui/src/lib/assets/update.svg b/alfa-client/libs/ui/src/lib/assets/update.svg new file mode 100644 index 0000000000000000000000000000000000000000..1eb6a0751c7673b2de4a52c06aba58f191f32570 --- /dev/null +++ b/alfa-client/libs/ui/src/lib/assets/update.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M480-120q-75 0-140.5-28.5t-114-77q-48.5-48.5-77-114T120-480q0-75 28.5-140.5t77-114q48.5-48.5 114-77T480-840q82 0 155.5 35T760-706v-94h80v240H600v-80h110q-41-56-101-88t-129-32q-117 0-198.5 81.5T200-480q0 117 81.5 198.5T480-200q105 0 183.5-68T756-440h82q-15 137-117.5 228.5T480-120Zm112-192L440-464v-216h80v184l128 128-56 56Z"/></svg> \ No newline at end of file diff --git a/alfa-client/libs/ui/src/lib/icon/icon.model.ts b/alfa-client/libs/ui/src/lib/icon/icon.model.ts index a7b7d26ba85860a97d0977b9a1d2cdfa2bca0a43..a7fa9937a697357073f444068efd1e352271a745 100644 --- a/alfa-client/libs/ui/src/lib/icon/icon.model.ts +++ b/alfa-client/libs/ui/src/lib/icon/icon.model.ts @@ -43,4 +43,5 @@ export enum Icons { VORGANG_VORPRUEFEN = 'vorgang_vorpruefen', XDOMEA = 'xdomea_file', PASTE_FROM_CLIPBOARD = 'paste_from_clipboard', + UPDATE = 'update', } diff --git a/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts index 814dc618b5a9b4cd8d9d0e9fd1f7690110e7d495..99a9eb92b61246dbcc5ded540b949153ffea8a2f 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts @@ -31,8 +31,8 @@ import { ViewChild, } from '@angular/core'; import { ResourceUri } from '@ngxp/rest'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { isEmpty, isEqual, isNil } from 'lodash-es'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-autocomplete-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts index d896c82a1626ffd500382b34f58481fd1a794124..0d76de506f2b3e1a7114b0984a780bed9f505fd8 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; -import { CheckboxEnumEditorItem } from './checkbox-enum-editor.model'; import { MatCheckboxChange } from '@angular/material/checkbox'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; +import { CheckboxEnumEditorItem } from './checkbox-enum-editor.model'; /** * Ein Checkbox, dessen Wert beim FormControl statt boolean als string aus dem enum editor item diff --git a/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts index 27619ab5f03eb534053f871ae817dcacd07f5fe3..ea6c64bf12eff1271c4c2ac016bd73333e9e6308 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts @@ -25,7 +25,7 @@ import { add2000Years } from '@alfa-client/tech-shared'; import { Component, Input } from '@angular/core'; import { MatDatepickerInputEvent } from '@angular/material/datepicker'; import { isDate } from 'date-fns'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-date-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts index 58a4a6f3413d95da58da14ec6b0cf0126eecfa76..4e4451f0621feb2c6c1472eedd2d18f27a2da348 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts @@ -22,7 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { Component, Input, OnInit } from '@angular/core'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { EnumEditorItem } from './enum-editor.model'; @Component({ diff --git a/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts index 172a8deb368e10e86648c95cd7ddb40d18d51d86..5e9fde7e1eb22fb4ec2929181c3bd122693f7558 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { StateResource } from '@alfa-client/tech-shared'; import { Component, ElementRef, @@ -33,13 +34,12 @@ import { } from '@angular/core'; import { ControlContainer, + FormGroupDirective, UntypedFormArray, UntypedFormControl, - FormGroupDirective, } from '@angular/forms'; -import { StateResource } from '@alfa-client/tech-shared'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { uniqueId } from 'lodash-es'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-file-upload-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts index 017dab82a8ba476c585b0af20b9f7c3080e0ff7b..5bb6b3a96e9f06d8770c527bd80dd202b9c3e06c 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts @@ -24,8 +24,8 @@ import { hasContent } from '@alfa-client/tech-shared'; import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { SubscriptSizing } from '@angular/material/form-field'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { Observable, map, startWith } from 'rxjs'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-text-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts index ff008c8ba924f7ff54291ecf8f0130bda01aeea5..12d1e2d4906205137a55508042d63b95c9a7e857 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts @@ -23,7 +23,7 @@ */ import { CdkTextareaAutosize } from '@angular/cdk/text-field'; import { AfterViewInit, Component, Input, ViewChild } from '@angular/core'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-textarea-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/messages.ts b/alfa-client/libs/ui/src/lib/ui/messages.ts index 35f77d1e3c0b4cb1134b96853eaead712319de2b..49891db3e6afb98e20be5f33e612a2054bb2f41b 100644 --- a/alfa-client/libs/ui/src/lib/ui/messages.ts +++ b/alfa-client/libs/ui/src/lib/ui/messages.ts @@ -22,6 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ export enum Messages { - HTTP_STATUS_FORBIDDEN = 'Die Aktion konnte wegen fehlender Berechtigungen nicht durchgeführt werden', + HTTP_STATUS_FORBIDDEN = 'Die Aktion konnte wegen fehlender Berechtigungen nicht durchgeführt werden.', HTTP_USER_MANAGER_SERVICE_UNAVAILABLE = 'Der UserManager ist zurzeit leider nicht verfügbar.', + HTTP_STATUS_VORGANG_NOT_FOUND = 'Der aufgerufene Vorgang wurde nicht gefunden.', } diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts index 6e6bc3d6944283627afe1d9044ec8111e9575742..27769a03b7ebd03c1db3a8ad50ee02d5b5ce9a69 100644 --- a/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts @@ -10,6 +10,7 @@ export class OzgcloudDialogService { readonly WIZARD_DIALOG_CONFIG: DialogConfig = { width: '1000px', restoreFocus: false, + disableClose: true, }; constructor(private dialog: Dialog) {} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.html deleted file mode 100644 index 0c6b5172652511b4c53e0ace77438a6a55b0728a..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- - - Copyright (C) 2022 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. - ---> -<alfa-vertical-binary-file-list - [fileListResource]="fileListResource$ | async" - [downloadFileNamePrefix]="vorgangWithEingang.nummer" -> -</alfa-vertical-binary-file-list> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.scss b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.scss deleted file mode 100644 index 9a08a5aabce6cc4cdbb268c4190a8d67f82f19e5..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2022 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. - */ diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.spec.ts deleted file mode 100644 index a9ec25ceade2c4416584d658de61791a3db9c645..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2022 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 { VerticalBinaryFileListComponent } from '@alfa-client/binary-file'; -import { mock } from '@alfa-client/test-utils'; -import { - VorgangService, - VorgangWithEingangLinkRel, - VorgangWithEingangResource, -} from '@alfa-client/vorgang-shared'; -import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; -import { VorgangDetailAttachmentListComponent } from './vorgang-detail-attachment-list.component'; - -describe('VorgangDetailAttachmentListComponent', () => { - let component: VorgangDetailAttachmentListComponent; - let fixture: ComponentFixture<VorgangDetailAttachmentListComponent>; - - const vorgangService = mock(VorgangService); - const vorgangWithAttachments: VorgangWithEingangResource = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.ATTACHMENTS, - ]); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - VorgangDetailAttachmentListComponent, - MockComponent(VerticalBinaryFileListComponent), - ], - providers: [ - { - provide: VorgangService, - useValue: vorgangService, - }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(VorgangDetailAttachmentListComponent); - component = fixture.componentInstance; - component.vorgangWithEingang = createVorgangWithEingangResource(); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('on vorgang with "attachment" link', () => { - it('should call vorgangService methods', () => { - component.vorgangWithEingang = vorgangWithAttachments; - - component.ngOnInit(); - - expect(vorgangService.getAttachments).toHaveBeenCalled(); - }); - }); -}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.ts deleted file mode 100644 index f1d39c242772553f9b49f790df9b3220d3afea05..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2022 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, OnInit } from '@angular/core'; -import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; -import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { Observable, of } from 'rxjs'; - -@Component({ - selector: 'alfa-vorgang-detail-attachment-list', - templateUrl: './vorgang-detail-attachment-list.component.html', - styleUrls: ['./vorgang-detail-attachment-list.component.scss'], -}) -export class VorgangDetailAttachmentListComponent implements OnInit { - @Input() vorgangWithEingang: VorgangWithEingangResource; - - fileListResource$: Observable<StateResource<BinaryFileListResource>> = of( - createEmptyStateResource<BinaryFileListResource>(), - ); - - constructor(private vorgangService: VorgangService) {} - - ngOnInit(): void { - this.fileListResource$ = this.vorgangService.getAttachments(); - } -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..085d1d818ee3dbe414ea74dde833aa1ce01366c7 --- /dev/null +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.html @@ -0,0 +1,29 @@ +<div class="flex w-full flex-col gap-6 py-4 xl:flex-row"> + <div class="flex-1"> + <ng-container + *ngIf="representationListStateResource$ | async as representationListStateResource" + > + <alfa-vertical-binary-file-list + *ngIf="representationListStateResource.resource" + title="Antrag" + data-test-id="representation-list" + [binaryFileListStateResource]="representationListStateResource" + [downloadFileNamePrefix]="downloadPrefix" + > + </alfa-vertical-binary-file-list> + </ng-container> + </div> + <div class="flex-1"> + <ng-container *ngIf="attachmentListStateResource$ | async as attachmentListStateResource"> + <alfa-vertical-binary-file-list + *ngIf="attachmentListStateResource.resource" + title="Anhänge" + data-test-id="attachment-list" + [binaryFileListStateResource]="attachmentListStateResource" + [downloadFileNamePrefix]="downloadPrefix" + [archiveDownloadUri]="attachmentsDownloadUri" + > + </alfa-vertical-binary-file-list> + </ng-container> + </div> +</div> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..141729a2b4ec449a4746a6b97968e44bbf4f8499 --- /dev/null +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.spec.ts @@ -0,0 +1,223 @@ +import { VerticalBinaryFileListComponent } from '@alfa-client/binary-file'; +import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; +import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { Mock, getDebugElementFromFixtureByCss, mock } from '@alfa-client/test-utils'; +import { + VorgangService, + VorgangWithEingangLinkRel, + VorgangWithEingangResource, +} from '@alfa-client/vorgang-shared'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import faker from '@faker-js/faker'; +import { getUrl } from '@ngxp/rest'; +import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { VorgangDetailDateienContainerComponent } from './vorgang-detail-dateien-container.component'; + +describe('VorgangDetailDateienContainerComponent', () => { + let component: VorgangDetailDateienContainerComponent; + let fixture: ComponentFixture<VorgangDetailDateienContainerComponent>; + + const attachmentList: string = getDataTestIdOf('attachment-list'); + const representationList: string = getDataTestIdOf('representation-list'); + + const vorgang: VorgangWithEingangResource = createVorgangWithEingangResource(); + + const vorgangService: Mock<VorgangService> = mock(VorgangService); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [VorgangDetailDateienContainerComponent], + declarations: [ + VorgangDetailDateienContainerComponent, + MockComponent(VerticalBinaryFileListComponent), + ], + providers: [ + { + provide: VorgangService, + useValue: vorgangService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(VorgangDetailDateienContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call handleVorgangChange on set new vorgang', () => { + component.handleVorgangChange = jest.fn(); + + component.vorgangWithEingang = createVorgangWithEingangResource(); + + expect(component.handleVorgangChange).toHaveBeenCalled(); + }); + + describe('handle vorgang change', () => { + it('should set downloadPrefix', () => { + component.handleVorgangChange(vorgang); + + expect(component.downloadPrefix).toEqual(vorgang.nummer); + }); + + it('should call handle attachements', () => { + component.handleAttachments = jest.fn(); + + component.handleVorgangChange(vorgang); + + expect(component.handleAttachments).toHaveBeenCalledWith(vorgang); + }); + + it('should call get representations if exists', () => { + component.getRepresentationsIfExists = jest.fn(); + + component.handleVorgangChange(vorgang); + + expect(component.getRepresentationsIfExists).toHaveBeenCalledWith(vorgang); + }); + }); + + describe('get representations if exists', () => { + it('should call vorgangService if link exists', () => { + component.getRepresentationsIfExists( + createVorgangWithEingangResource([VorgangWithEingangLinkRel.REPRESENTATIONS]), + ); + + expect(vorgangService.getRepresentations).toHaveBeenCalled(); + }); + }); + + describe('handle attachments', () => { + it('should call set download uri', () => { + component.setAttachmentsDownloadUri = jest.fn(); + + component.handleAttachments(vorgang); + + expect(component.setAttachmentsDownloadUri).toHaveBeenCalledWith(vorgang); + }); + + it('should call vorgangService methods', () => { + component.handleVorgangChange( + createVorgangWithEingangResource([VorgangWithEingangLinkRel.ATTACHMENTS]), + ); + + expect(vorgangService.getAttachments).toHaveBeenCalled(); + }); + + it('should call get attachments if exists', () => { + component.getAttachmentsIfExists = jest.fn(); + + component.handleVorgangChange(vorgang); + + expect(component.getAttachmentsIfExists).toHaveBeenCalledWith(vorgang); + }); + }); + + describe('get attachments if exists', () => { + it('should call vorgangService if link exists', () => { + component.getAttachmentsIfExists( + createVorgangWithEingangResource([VorgangWithEingangLinkRel.ATTACHMENTS]), + ); + + expect(vorgangService.getAttachments).toHaveBeenCalled(); + }); + }); + + describe('download attachments link', () => { + it('should be set if exists', () => { + const vorganWithLink: VorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS, + ]); + + component.handleVorgangChange(vorganWithLink); + + expect(component.attachmentsDownloadUri).toBe( + getUrl(vorganWithLink, VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS), + ); + }); + + it('should NOT be set if missing', () => { + const vorganWithLink: VorgangWithEingangResource = createVorgangWithEingangResource(); + + component.handleVorgangChange(vorganWithLink); + + expect(component.attachmentsDownloadUri).toBeUndefined(); + }); + }); + + describe('on existing attachments', () => { + describe('binary file list component should be called with', () => { + const binaryFileListResource: BinaryFileListResource = createBinaryFileListResource(); + const binaryFileListStateResource: StateResource<BinaryFileListResource> = + createStateResource(binaryFileListResource); + + beforeEach(() => { + component.attachmentListStateResource$ = of(binaryFileListStateResource); + fixture.detectChanges(); + }); + + it('binaryFileListStateResource', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(attachmentList); + expect(binaryFileListComp.binaryFileListStateResource).toEqual(binaryFileListStateResource); + }); + + it('downloadFileNamePrefix', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(attachmentList); + expect(binaryFileListComp.downloadFileNamePrefix).toEqual(component.downloadPrefix); + }); + + it('archiveDownloadUri', () => { + component.attachmentsDownloadUri = faker.internet.url(); + + fixture.detectChanges(); + + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(attachmentList); + expect(binaryFileListComp.archiveDownloadUri).toEqual(component.attachmentsDownloadUri); + }); + }); + }); + + describe('On existing representations', () => { + describe('binary file list component should be called with', () => { + const binaryFileListResource: BinaryFileListResource = createBinaryFileListResource(); + const binaryFileListStateResource: StateResource<BinaryFileListResource> = + createStateResource(binaryFileListResource); + + beforeEach(() => { + component.representationListStateResource$ = of(binaryFileListStateResource); + fixture.detectChanges(); + }); + + it('binaryFileListStateResource', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(representationList); + expect(binaryFileListComp.binaryFileListStateResource).toEqual(binaryFileListStateResource); + }); + + it('downloadFileNamePrefix', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(representationList); + expect(binaryFileListComp.downloadFileNamePrefix).toEqual(component.downloadPrefix); + }); + }); + }); + + function getVerticalBinaryFileListComponent( + dataTestIdElement: string, + ): VerticalBinaryFileListComponent { + return getDebugElementFromFixtureByCss( + fixture, + 'alfa-vertical-binary-file-list' + dataTestIdElement, + ).componentInstance; + } +}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..55c4a9368bb1a8a89b0b1f8cecf7185682c2ccaf --- /dev/null +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.ts @@ -0,0 +1,67 @@ +import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; +import { StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { + VorgangService, + VorgangWithEingangLinkRel, + VorgangWithEingangResource, +} from '@alfa-client/vorgang-shared'; +import { Component, Input } from '@angular/core'; +import { getUrl, hasLink } from '@ngxp/rest'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'alfa-vorgang-detail-dateien-container', + templateUrl: './vorgang-detail-dateien-container.component.html', +}) +export class VorgangDetailDateienContainerComponent { + @Input() set vorgangWithEingang(vorgangWithEingang: VorgangWithEingangResource) { + this.handleVorgangChange(vorgangWithEingang); + } + + public attachmentListStateResource$: Observable<StateResource<BinaryFileListResource>> = of( + createEmptyStateResource<BinaryFileListResource>(), + ); + public representationListStateResource$: Observable<StateResource<BinaryFileListResource>> = of( + createEmptyStateResource<BinaryFileListResource>(), + ); + + public downloadPrefix: string; + public attachmentsDownloadUri: string; + + public readonly vorgangWithEingangLinkRel = VorgangWithEingangLinkRel; + + constructor(private vorgangService: VorgangService) {} + + handleVorgangChange(vorgangWithEingang: VorgangWithEingangResource): void { + this.downloadPrefix = vorgangWithEingang.nummer; + + this.handleAttachments(vorgangWithEingang); + this.getRepresentationsIfExists(vorgangWithEingang); + } + + handleAttachments(vorgangWithEingang: VorgangWithEingangResource): void { + this.setAttachmentsDownloadUri(vorgangWithEingang); + this.getAttachmentsIfExists(vorgangWithEingang); + } + + setAttachmentsDownloadUri(vorgangWithEingang: VorgangWithEingangResource): void { + if (hasLink(vorgangWithEingang, VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS)) { + this.attachmentsDownloadUri = getUrl( + vorgangWithEingang, + VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS, + ); + } + } + + getRepresentationsIfExists(vorgangWithEingang: VorgangWithEingangResource): void { + if (hasLink(vorgangWithEingang, VorgangWithEingangLinkRel.REPRESENTATIONS)) { + this.representationListStateResource$ = this.vorgangService.getRepresentations(); + } + } + + getAttachmentsIfExists(vorgangWithEingang: VorgangWithEingangResource): void { + if (hasLink(vorgangWithEingang, VorgangWithEingangLinkRel.ATTACHMENTS)) { + this.attachmentListStateResource$ = this.vorgangService.getAttachments(); + } + } +} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html index fdb75c0c568b35de0627f2b6295459812f1d2b8f..1d4d2f566f9ea57e41318a3f59f7b40f0c19b308 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html @@ -40,32 +40,20 @@ [antragsData]="antragsData" ></alfa-vorgang-detail-antrag-data> </mat-tab> - <mat-tab label="Metadaten" [disabled]="!metaData"> - <alfa-vorgang-detail-meta-data - data-test-id="tab-meta-data" - [metaData]="metaData" - ></alfa-vorgang-detail-meta-data> - </mat-tab> - <mat-tab - label="Datenrepräsentation ({{ vorgangWithEingang.eingang.numberOfRepresentations }})" - [disabled]="vorgangWithEingang | notHasLink: vorgangWithEingangLinkRel.REPRESENTATIONS" - > - <ng-template matTabContent> - <alfa-vorgang-detail-representation-list - data-test-id="tab-representations-list" - [vorgangWithEingang]="vorgangWithEingang" - ></alfa-vorgang-detail-representation-list> - </ng-template> - </mat-tab> <mat-tab - label="Anhänge ({{ vorgangWithEingang.eingang.numberOfAttachments }})" - [disabled]="vorgangWithEingang | notHasLink: vorgangWithEingangLinkRel.ATTACHMENTS" + label="Dateien ({{ filesNumber }})" + [disabled]=" + vorgangWithEingang + | notHasAnyLink + : vorgangWithEingangLinkRel.REPRESENTATIONS + : vorgangWithEingangLinkRel.ATTACHMENTS + " > <ng-template matTabContent> - <alfa-vorgang-detail-attachment-list - data-test-id="tab-attachments-list" + <alfa-vorgang-detail-dateien-container + *ngIf="vorgangWithEingang" [vorgangWithEingang]="vorgangWithEingang" - ></alfa-vorgang-detail-attachment-list> + ></alfa-vorgang-detail-dateien-container> </ng-template> </mat-tab> <mat-tab diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts index dfe0151ba8bf70bb061b862471ec6b678e1b1952..83a309ba164e11fc7debead039783ae81e8aa925 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts @@ -21,30 +21,27 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatTabsModule } from '@angular/material/tabs'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { HistorieContainerComponent } from '@alfa-client/historie'; -import { HasLinkPipe, NotHasLinkPipe } from '@alfa-client/tech-shared'; +import { HasLinkPipe } from '@alfa-client/tech-shared'; import { getElementFromFixture } from '@alfa-client/test-utils'; import { ExpansionPanelComponent } from '@alfa-client/ui'; import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatTabsModule } from '@angular/material/tabs'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { NotHasAnyLinkPipe } from 'libs/tech-shared/src/lib/pipe/not-has-any-link.pipe'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; import { VorgangDetailAntragDataComponent } from './vorgang-detail-antrag-data/vorgang-detail-antrag-data.component'; -import { VorgangDetailAttachmentListComponent } from './vorgang-detail-attachment-list/vorgang-detail-attachment-list.component'; +import { VorgangDetailDateienContainerComponent } from './vorgang-detail-dateien-container/vorgang-detail-dateien-container.component'; import { VorgangDetailFormularDatenComponent } from './vorgang-detail-formular-daten.component'; -import { VorgangDetailMetaDataComponent } from './vorgang-detail-meta-data/vorgang-detail-meta-data.component'; -import { VorgangDetailRepresentationListComponent } from './vorgang-detail-representation-list/vorgang-detail-representation-list.component'; describe('VorgangDetailFormularDatenComponent', () => { let component: VorgangDetailFormularDatenComponent; let fixture: ComponentFixture<VorgangDetailFormularDatenComponent>; - const tabMetaData: string = getTabByIndex(2); - const tabRepresentations: string = getTabByIndex(3); - const tabAttachments: string = getTabByIndex(4); - const tabHistorie: string = getTabByIndex(5); + const tabDateien: string = getTabByIndex(2); + const tabHistorie: string = getTabByIndex(3); function getTabByIndex(index: number): string { return `div[role=tab]:nth-child(${index})`; @@ -62,12 +59,10 @@ describe('VorgangDetailFormularDatenComponent', () => { imports: [NoopAnimationsModule, MatTabsModule], declarations: [ HasLinkPipe, - NotHasLinkPipe, + NotHasAnyLinkPipe, VorgangDetailFormularDatenComponent, MockComponent(VorgangDetailAntragDataComponent), - MockComponent(VorgangDetailMetaDataComponent), - MockComponent(VorgangDetailAttachmentListComponent), - MockComponent(VorgangDetailRepresentationListComponent), + MockComponent(VorgangDetailDateienContainerComponent), MockComponent(ExpansionPanelComponent), MockComponent(HistorieContainerComponent), ], @@ -85,98 +80,82 @@ describe('VorgangDetailFormularDatenComponent', () => { expect(component).toBeTruthy(); }); - describe('Metadaten', () => { - it('should show tab enabled if Vorgang contains metadata', () => { - component.metaData = { test: 'testValue' }; + describe('Tag Dateien', () => { + describe('Datenrepräsentation', () => { + const vorgangWithRepresentations: VorgangWithEingangResource = + createVorgangWithEingangResource([VorgangWithEingangLinkRel.REPRESENTATIONS]); - fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabMetaData); + it('should be enabled and selected if representations links is present', () => { + component.vorgangWithEingang = vorgangWithRepresentations; - expect(metaDataTab).not.toHaveClass(disabledTabClass); - }); + fixture.detectChanges(); - it('should disable tab if Vorgang contains no metadata', () => { - fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabMetaData); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).not.toHaveClass(disabledTabClass); + expect(tab).toHaveAttribute('aria-selected', 'true'); + }); - expect(metaDataTab).toHaveClass(disabledTabClass); - }); - - it('should show MetaData label', () => { - fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabMetaData); + it('should be disable and not selected if representations link is NOT present', () => { + component.vorgangWithEingang = vorgangWithEingang; - expect(metaDataTab).toHaveTextContent('Metadaten'); - }); - }); + fixture.detectChanges(); - describe('Tab Datenrepräsentation', () => { - const vorgangWithRepresentations: VorgangWithEingangResource = createVorgangWithEingangResource( - [VorgangWithEingangLinkRel.REPRESENTATIONS], - ); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveClass(disabledTabClass); + expect(tab).toHaveAttribute('aria-selected', 'false'); + }); - it('should be enabled and selected if representations links is present', () => { - component.vorgangWithEingang = vorgangWithRepresentations; + it('should show number of Representations in the label', () => { + component.vorgangWithEingang = { + ...vorgangWithRepresentations, + eingang: { ...vorgangWithRepresentations.eingang, numberOfAttachments: 0 }, + }; - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabRepresentations); + fixture.detectChanges(); - expect(tab).not.toHaveClass(disabledTabClass); - expect(tab).toHaveAttribute('aria-selected', 'true'); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveTextContent( + `Dateien (${vorgangWithRepresentations.eingang.numberOfRepresentations})`, + ); + }); }); - it('should be disable and not selected if representations link is NOT present', () => { - component.vorgangWithEingang = vorgangWithEingang; + describe('Anhänge', () => { + const vorgangWithAttachments: VorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.ATTACHMENTS, + ]); - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabRepresentations); + it('should be enabled if attachments link is present', () => { + component.vorgangWithEingang = vorgangWithAttachments; - expect(tab).toHaveClass(disabledTabClass); - expect(tab).toHaveAttribute('aria-selected', 'false'); - }); - - it('should show number of Representations in the label', () => { - component.vorgangWithEingang = vorgangWithRepresentations; - - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabRepresentations); - const tabLabel = `Datenrepräsentation (${vorgangWithRepresentations.eingang.numberOfRepresentations})`; - - expect(tab).toHaveTextContent(tabLabel); - }); - }); - - describe('Tab Anhänge', () => { - const vorgangWithAttachments: VorgangWithEingangResource = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.ATTACHMENTS, - ]); + fixture.detectChanges(); - it('should be enabled if attachments link is present', () => { - component.vorgangWithEingang = vorgangWithAttachments; + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).not.toHaveClass(disabledTabClass); + }); - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabAttachments); - - expect(tab).not.toHaveClass(disabledTabClass); - }); - - it('should be disable if attachments link is NOT present', () => { - component.vorgangWithEingang = vorgangWithEingang; + it('should be disable if attachments link is NOT present', () => { + component.vorgangWithEingang = vorgangWithEingang; - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabAttachments); + fixture.detectChanges(); - expect(tab).toHaveClass(disabledTabClass); - }); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveClass(disabledTabClass); + }); - it('should show number of Attachments in the label', () => { - component.vorgangWithEingang = vorgangWithAttachments; + it('should show number of Attachments in the label', () => { + component.vorgangWithEingang = { + ...vorgangWithAttachments, + eingang: { ...vorgangWithAttachments.eingang, numberOfRepresentations: 0 }, + }; - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabAttachments); - const tabLabel = `Anhänge (${vorgangWithAttachments.eingang.numberOfAttachments})`; + fixture.detectChanges(); - expect(tab).toHaveTextContent(tabLabel); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveTextContent( + `Dateien (${vorgangWithAttachments.eingang.numberOfAttachments})`, + ); + }); }); }); @@ -185,8 +164,8 @@ describe('VorgangDetailFormularDatenComponent', () => { component.vorgangWithEingang = vorgangWithHistorie; fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabHistorie); + const tab: HTMLElement = getElementFromFixture(fixture, tabHistorie); expect(tab).toBeInTheDocument(); }); @@ -194,8 +173,8 @@ describe('VorgangDetailFormularDatenComponent', () => { component.vorgangWithEingang = vorgangWithEingang; fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabHistorie); + const tab: HTMLElement = getElementFromFixture(fixture, tabHistorie); expect(tab).not.toBeInTheDocument(); }); @@ -203,8 +182,8 @@ describe('VorgangDetailFormularDatenComponent', () => { component.vorgangWithEingang = vorgangWithHistorie; fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabHistorie); + const metaDataTab: HTMLElement = getElementFromFixture(fixture, tabHistorie); expect(metaDataTab).toHaveTextContent('Historie'); }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts index 89c9894be1cd94b8e61ce7981abf3f4688c33caf..69a869b9ccdb4a363b3f9e0d75388f018cc1d22e 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts @@ -21,8 +21,8 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Input, OnInit } from '@angular/core'; import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { Component, Input, OnInit } from '@angular/core'; import { hasLink } from '@ngxp/rest'; @Component({ @@ -33,7 +33,6 @@ import { hasLink } from '@ngxp/rest'; export class VorgangDetailFormularDatenComponent implements OnInit { @Input() vorgangWithEingang: VorgangWithEingangResource; - metaData: object; antragsData: object; readonly vorgangWithEingangLinkRel = VorgangWithEingangLinkRel; @@ -43,6 +42,13 @@ export class VorgangDetailFormularDatenComponent implements OnInit { } get defaultSelection(): number { - return hasLink(this.vorgangWithEingang, this.vorgangWithEingangLinkRel.REPRESENTATIONS) ? 2 : 0; + return hasLink(this.vorgangWithEingang, this.vorgangWithEingangLinkRel.REPRESENTATIONS) ? 1 : 0; + } + + get filesNumber(): number { + return ( + this.vorgangWithEingang.eingang.numberOfAttachments + + this.vorgangWithEingang.eingang.numberOfRepresentations + ); } } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.html deleted file mode 100644 index 993f0b128ebd152b9b054e1f2c76acd5c54fd198..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.html +++ /dev/null @@ -1,29 +0,0 @@ -<!-- - - Copyright (C) 2022 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. - ---> -<alfa-vorgang-detail-form-data-table - data-test-id="metadaten" - [formData]="metaData" -></alfa-vorgang-detail-form-data-table> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.scss b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.scss deleted file mode 100644 index b571b3c71b2625b19658760f31f072dc3134468d..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.scss +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (C) 2022 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. - */ -:host { - display: inline-block; - margin-top: 0.5rem; - overflow: hidden; -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.spec.ts deleted file mode 100644 index 9a253e89f86b3dd11bfe9811f02f0839b141e315..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2022 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 { MockComponent } from 'ng-mocks'; -import { VorgangDetailFormDataTableComponent } from '../vorgang-detail-form-data-table/vorgang-detail-form-data-table.component'; -import { VorgangDetailMetaDataComponent } from './vorgang-detail-meta-data.component'; - -describe('VorgangDetailMetaDataComponent', () => { - let component: VorgangDetailMetaDataComponent; - let fixture: ComponentFixture<VorgangDetailMetaDataComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - VorgangDetailMetaDataComponent, - MockComponent(VorgangDetailFormDataTableComponent), - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(VorgangDetailMetaDataComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.ts deleted file mode 100644 index 1ab47d3436611b4542506e51920f2e3dda936b6a..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2022 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-vorgang-detail-meta-data', - templateUrl: './vorgang-detail-meta-data.component.html', - styleUrls: ['./vorgang-detail-meta-data.component.scss'], -}) -export class VorgangDetailMetaDataComponent { - @Input() metaData: object; -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.html deleted file mode 100644 index 0c6b5172652511b4c53e0ace77438a6a55b0728a..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- - - Copyright (C) 2022 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. - ---> -<alfa-vertical-binary-file-list - [fileListResource]="fileListResource$ | async" - [downloadFileNamePrefix]="vorgangWithEingang.nummer" -> -</alfa-vertical-binary-file-list> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.scss b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.scss deleted file mode 100644 index 9a08a5aabce6cc4cdbb268c4190a8d67f82f19e5..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2022 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. - */ diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.spec.ts deleted file mode 100644 index 04a6e0a8f98a6568cade6dadaa49deeab04ebeab..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2022 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 { VerticalBinaryFileListComponent } from '@alfa-client/binary-file'; -import { mock } from '@alfa-client/test-utils'; -import { - VorgangService, - VorgangWithEingangLinkRel, - VorgangWithEingangResource, -} from '@alfa-client/vorgang-shared'; -import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; -import { VorgangDetailRepresentationListComponent } from './vorgang-detail-representation-list.component'; - -describe('VorgangDetailRepresentationListComponent', () => { - let component: VorgangDetailRepresentationListComponent; - let fixture: ComponentFixture<VorgangDetailRepresentationListComponent>; - - const vorgangService = mock(VorgangService); - const vorgangWithRepresentations: VorgangWithEingangResource = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.REPRESENTATIONS, - ]); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - VorgangDetailRepresentationListComponent, - MockComponent(VerticalBinaryFileListComponent), - ], - providers: [ - { - provide: VorgangService, - useValue: vorgangService, - }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(VorgangDetailRepresentationListComponent); - component = fixture.componentInstance; - component.vorgangWithEingang = createVorgangWithEingangResource(); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('on vorgang with "representation" link', () => { - it('should call vorgangService methods', () => { - component.vorgangWithEingang = vorgangWithRepresentations; - - component.ngOnInit(); - - expect(vorgangService.getRepresentations).toHaveBeenCalled(); - }); - }); -}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.ts deleted file mode 100644 index 2007d106199389a23bd6370bee91fd36f5cfca2f..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2022 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, OnInit } from '@angular/core'; -import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; -import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { Observable, of } from 'rxjs'; - -@Component({ - selector: 'alfa-vorgang-detail-representation-list', - templateUrl: './vorgang-detail-representation-list.component.html', - styleUrls: ['./vorgang-detail-representation-list.component.scss'], -}) -export class VorgangDetailRepresentationListComponent implements OnInit { - @Input() vorgangWithEingang: VorgangWithEingangResource; - - fileListResource$: Observable<StateResource<BinaryFileListResource>> = of( - createEmptyStateResource<BinaryFileListResource>(), - ); - - constructor(private vorgangService: VorgangService) {} - - ngOnInit(): void { - this.fileListResource$ = this.vorgangService.getRepresentations(); - } -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts index 9e29f9d8172e162e37e5187cb5729efe2dd45eaf..5c631ffeb6e6c4007028f95655df19047482b1b9 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts @@ -9,10 +9,10 @@ import { import { BinaryFileResource } from '@alfa-client/binary-file-shared'; import { CommandLinkRel, CommandResource } from '@alfa-client/command-shared'; import { - createStateResource, EMPTY_STRING, - formatForDatabase, StateResource, + createStateResource, + formatForDatabase, } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; @@ -114,6 +114,22 @@ describe('BescheidenFormService', () => { expect(service.sendByManual.value).toBeFalsy(); }); + it('should not emit if value not changed', () => { + service.sendByManual.next(true); + service.sendByManual.next = jest.fn(); + + service.updateSendByManual(BescheidSendBy.MANUAL); + + expect(service.sendByManual.next).not.toHaveBeenCalled(); + }); + it('should emit on value change', () => { + service.sendByManual.next(false); + service.sendByManual.next = jest.fn(); + + service.updateSendByManual(BescheidSendBy.MANUAL); + + expect(service.sendByManual.next).toHaveBeenCalled(); + }); }); describe('initializeFormChanges', () => { diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts index d9e05716a5c15264f7f5b671ecec6a8f61cc1ca1..b079362062868d5fd121503655948db80538f466 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts @@ -10,14 +10,14 @@ import { BinaryFileResource } from '@alfa-client/binary-file-shared'; import { CommandResource, tapOnCommandSuccessfullyDone } from '@alfa-client/command-shared'; import { AbstractFormService, - convertToBoolean, EMPTY_STRING, - formatForDatabase, HttpError, + StateResource, + convertToBoolean, + formatForDatabase, isLoaded, isNotEmpty, isNotNil, - StateResource, } from '@alfa-client/tech-shared'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { Injectable, OnDestroy } from '@angular/core'; @@ -28,18 +28,18 @@ import { UntypedFormControl, UntypedFormGroup, } from '@angular/forms'; -import { getUrl, hasLink, Resource, ResourceUri } from '@ngxp/rest'; +import { Resource, ResourceUri, getUrl, hasLink } from '@ngxp/rest'; import { isEmpty, isNil, isUndefined } from 'lodash-es'; import { BehaviorSubject, + Observable, + Subject, + Subscription, combineLatest, filter, first, map, - Observable, startWith, - Subject, - Subscription, } from 'rxjs'; @Injectable() @@ -92,7 +92,8 @@ export class BescheidenFormService extends AbstractFormService implements OnDest } updateSendByManual(sendBy: BescheidSendBy): void { - this.sendByManual.next(sendBy === BescheidSendBy.MANUAL); + const isSendByManual = sendBy === BescheidSendBy.MANUAL; + if (isSendByManual !== this.sendByManual.value) this.sendByManual.next(isSendByManual); } initializeFormChanges(): void { diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html index 95e424d51d4542bce8bc97612cd8f632bca51b48..ff4e542e0d9181e9ee7bc98b468e62b2a1d23482 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html @@ -1,4 +1,4 @@ -<ods-attachment-container> +<ods-attachment-wrapper> <alfa-binary-file2-container *ngFor="let attachment of existingAttachments" [file]="attachment" @@ -27,4 +27,4 @@ [isLoading]="uploadFileInProgress.loading" ></ods-attachment> </ng-container> -</ods-attachment-container> +</ods-attachment-wrapper> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts index 38a32fe726c44d6f7c0152e0caec38bfb8c90791..63758c537759f45fbe750b6c286b6b2e5778864d 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts @@ -14,11 +14,7 @@ import { existsAsHtmlElement, Mock, mock, notExistsAsHtmlElement } from '@alfa-c import { OzgcloudSvgIconComponent, SpinnerComponent } from '@alfa-client/ui'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; -import { - AttachmentComponent, - AttachmentContainerComponent, - SpinnerIconComponent, -} from '@ods/system'; +import { AttachmentComponent, AttachmentWrapperComponent, SpinnerIconComponent } from '@ods/system'; import { MockComponent, MockPipe } from 'ng-mocks'; import { BehaviorSubject, EMPTY, Observable, of, Subscription } from 'rxjs'; import { createUploadFileInProgress } from '../../../../../../../bescheid-shared/src/test/bescheid'; @@ -58,7 +54,7 @@ describe('VorgangDetailBescheidenResultAttachmentsComponent', () => { MockPipe(ConvertApiErrorToErrorMessagesPipe), MockComponent(OzgcloudSvgIconComponent), MockComponent(SpinnerComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), MockComponent(BinaryFile2ContainerComponent), MockComponent(SpinnerIconComponent), MockComponent(AttachmentComponent), diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html index 2080ba5b2a7b977a0837a437c97ae3d1579ba396..bb762af88d7b449d222804be6eccd2be60690f5e 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html @@ -5,7 +5,7 @@ > Bitte fügen Sie ein Bescheiddokument hinzu. </p> -<ods-attachment-container> +<ods-attachment-wrapper> <ng-container *ngIf="bescheidDocumentFile.resource"> <alfa-binary-file2-container *ngIf=" @@ -39,4 +39,4 @@ [errorMessages]="createDocumentErrorMessages" data-test-id="create-bescheid-document-attachment" ></ods-attachment> -</ods-attachment-container> +</ods-attachment-wrapper> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts index 15a99f1bea6f85d4287f1ca9d0a85fffc4144dbc..c61b90e368bb525ef644dfd2e95d156c06338d22 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts @@ -3,14 +3,14 @@ import { BinaryFile2ContainerComponent } from '@alfa-client/binary-file'; import { CommandResource } from '@alfa-client/command-shared'; import { ConvertApiErrorToErrorMessagesPipe, + StateResource, createEmptyStateResource, createStateResource, - StateResource, } from '@alfa-client/tech-shared'; -import { existsAsHtmlElement, Mock, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; +import { Mock, existsAsHtmlElement, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { getUrl } from '@ngxp/rest'; -import { AttachmentComponent, AttachmentContainerComponent } from '@ods/system'; +import { AttachmentComponent, AttachmentWrapperComponent } from '@ods/system'; import { createBescheidResource } from 'libs/bescheid-shared/src/test/bescheid'; import { createBinaryFileResource } from 'libs/binary-file-shared/test/binary-file'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; @@ -47,7 +47,7 @@ describe('VorgangDetailBescheidenResultDokumentComponent', () => { VorgangDetailBescheidenResultDokumentComponent, MockComponent(BinaryFile2ContainerComponent), MockComponent(AttachmentComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), MockPipe(ConvertApiErrorToErrorMessagesPipe), ], providers: [ diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html index 0b4b30cf95db4e50dbaec098d73c7b5fe1d0399b..5f5be409ae38611e4d72d5712e7beaae28677299 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html @@ -12,6 +12,7 @@ label="Betreff" placeholder="Betreff hier eingeben" [focus]="focusBetreff" + [isRequired]="true" > </ods-text-editor> @@ -20,6 +21,7 @@ label="Text" placeholder="Nachrichtentext hier eingeben" [focus]="focusNachricht" + [isRequired]="true" > </ods-textarea-editor> </div> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts index b0b363829a8bbc5d1fc404163e0283f38115b4e4..22332fc5187c9ce1bc318ceb77b4b7ad0e30e833 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts @@ -1,15 +1,15 @@ import { BescheidLinkRel, BescheidResource, BescheidService } from '@alfa-client/bescheid-shared'; import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; import { - StateResource, createEmptyStateResource, createStateResource, + StateResource, } from '@alfa-client/tech-shared'; import { - Mock, dispatchEventFromFixture, existsAsHtmlElement, getElementFromFixture, + Mock, mock, notExistsAsHtmlElement, } from '@alfa-client/test-utils'; @@ -59,6 +59,7 @@ describe('VorgangDetailBescheidenResultComponent', () => { new BehaviorSubject({ beschiedenAm: new Date(), bewilligt: false }), ); formService.getActiveStep.mockReturnValue(EMPTY); + formService.isSendByManual.mockReturnValue(EMPTY); await TestBed.configureTestingModule({ declarations: [ @@ -219,10 +220,12 @@ describe('VorgangDetailBescheidenResultComponent', () => { expect(formService.getBescheidChanges).toHaveBeenCalled(); }); - it('should call formservice to get sendByManual', () => { + it('should get is send by manual', () => { + component.getIsSendManually = jest.fn(); + component.ngOnInit(); - expect(formService.isSendByManual).toHaveBeenCalled(); + expect(component.getIsSendManually).toHaveBeenCalled(); }); it('should call service to get upload bescheid document in progress', () => { @@ -668,4 +671,27 @@ describe('VorgangDetailBescheidenResultComponent', () => { expect(component.sendWithNachricht$.value).toEqual(createEmptyStateResource()); }); }); + + describe('getIsSendManually', () => { + beforeEach(() => { + component.resetSend = jest.fn(); + }); + + it('should get is send by manual from from service', () => { + component.getIsSendManually(); + + expect(formService.isSendByManual).toHaveBeenCalled(); + }); + + it.each([true, false])( + 'should reset send if send by manual is %s', + (isSendByManual: boolean) => { + formService.isSendByManual.mockReturnValue(of(isSendByManual)); + + component.getIsSendManually().subscribe(); + + expect(component.resetSend).toHaveBeenCalled(); + }, + ); + }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts index c46f6f7dc96be59c98c92990dec4f064045994cc..5b423f9fdb6be53a986356f5bab275490a479dc7 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts @@ -13,10 +13,10 @@ import { switchMapCommandSuccessfullyDone, tapOnCommandSuccessfullyDone, } from '@alfa-client/command-shared'; -import { StateResource, createEmptyStateResource, isLoaded } from '@alfa-client/tech-shared'; +import { createEmptyStateResource, isLoaded, StateResource } from '@alfa-client/tech-shared'; import { Component, EventEmitter, OnInit, Output } from '@angular/core'; -import { Resource, hasLink } from '@ngxp/rest'; -import { BehaviorSubject, Observable, OperatorFunction, filter, map, of, tap } from 'rxjs'; +import { hasLink, Resource } from '@ngxp/rest'; +import { BehaviorSubject, filter, map, Observable, of, OperatorFunction, tap } from 'rxjs'; import { BescheidenFormService } from '../bescheiden.formservice'; type sendBescheid = ( @@ -84,7 +84,7 @@ export class VorgangDetailBescheidenResultComponent implements OnInit { this.activeStep$ = this.getActiveStep(); this.bescheid$ = this.formService.getBescheidChanges(); - this.sendByManual$ = this.formService.isSendByManual(); + this.sendByManual$ = this.getIsSendManually(); this.canSave$ = this.bescheidDraftStateResource$.pipe( filter(isLoaded), @@ -107,6 +107,10 @@ export class VorgangDetailBescheidenResultComponent implements OnInit { .pipe(tap((step: number) => this.resetStateOnStepChange(step))); } + getIsSendManually(): Observable<boolean> { + return this.formService.isSendByManual().pipe(tap(() => this.resetSend())); + } + private resetStateOnStepChange(step: number): void { if (this.isNotLast(step)) this.resetSend(); if (this.shouldClearAttachmentInProgress(step)) this.bescheidService.clearAttachmentUpload(); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.html index 5ebc616b3fa6376d2cb5437ccf98b74c90f2888b..3868f94249128b79fe4e08b15d228fac0498165d 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.html @@ -1,3 +1,7 @@ -<button (click)="onClick()" class="absolute right-3 top-3 text-text" data-test-id="close-bescheid"> +<button + (click)="clickEmitter.emit()" + class="absolute right-3 top-3 text-text" + data-test-id="close-bescheid" +> <mat-icon>close</mat-icon> </button> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.spec.ts index 88edfcc33994a41c77e6ef8e46d7a53e6637ea0b..d143d1618f0b45bcf78d65061f489811034d219e 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.spec.ts @@ -1,28 +1,15 @@ -import { Mock, mock } from '@alfa-client/test-utils'; -import { OzgcloudDialogService } from '@alfa-client/ui'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; -import { VorgangDetailBescheidenAbbrechenDialogComponent } from '../vorgang-detail-bescheiden-abbrechen-dialog/vorgang-detail-bescheiden-abbrechen-dialog.component'; import { VorgangDetailBescheidenAbbrechenButtonComponent } from './vorgang-detail-bescheiden-abbrechen-button.component'; describe('VorgangDetailBescheidenAbbrechenButtonComponent', () => { let component: VorgangDetailBescheidenAbbrechenButtonComponent; let fixture: ComponentFixture<VorgangDetailBescheidenAbbrechenButtonComponent>; - let ozgcloudDialogService: Mock<OzgcloudDialogService>; - beforeEach(async () => { - ozgcloudDialogService = mock(OzgcloudDialogService); - await TestBed.configureTestingModule({ declarations: [VorgangDetailBescheidenAbbrechenButtonComponent], imports: [MatIcon], - providers: [ - { - provide: OzgcloudDialogService, - useValue: ozgcloudDialogService, - }, - ], }).compileComponents(); fixture = TestBed.createComponent(VorgangDetailBescheidenAbbrechenButtonComponent); @@ -33,15 +20,4 @@ describe('VorgangDetailBescheidenAbbrechenButtonComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); - - describe('onClick', () => { - it('should call ozgcloudDialogService.open', () => { - component.onClick(); - - expect(ozgcloudDialogService.openInCallingComponentContext).toHaveBeenCalledWith( - VorgangDetailBescheidenAbbrechenDialogComponent, - component.viewContainerRef, - ); - }); - }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.ts index 41f0834080232437ba18c70f809071c2f9c3dc8a..4f1051d4c4c98c148725c077185d35463b916a6c 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component.ts @@ -1,6 +1,4 @@ -import { OzgcloudDialogService } from '@alfa-client/ui'; -import { Component, ViewContainerRef } from '@angular/core'; -import { VorgangDetailBescheidenAbbrechenDialogComponent } from '../vorgang-detail-bescheiden-abbrechen-dialog/vorgang-detail-bescheiden-abbrechen-dialog.component'; +import { Component, EventEmitter, Output } from '@angular/core'; @Component({ selector: 'alfa-vorgang-detail-bescheiden-abbrechen-button', @@ -8,15 +6,5 @@ import { VorgangDetailBescheidenAbbrechenDialogComponent } from '../vorgang-deta styles: [], }) export class VorgangDetailBescheidenAbbrechenButtonComponent { - constructor( - private readonly ozgcloudDialogService: OzgcloudDialogService, - readonly viewContainerRef: ViewContainerRef, - ) {} - - public onClick(): void { - this.ozgcloudDialogService.openInCallingComponentContext<VorgangDetailBescheidenAbbrechenDialogComponent>( - VorgangDetailBescheidenAbbrechenDialogComponent, - this.viewContainerRef, - ); - } + @Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.html index e2427e885a079271ae20cac54e8a0ea1f168013f..f1e668502936106b848030aa3635d0fd7323b7f6 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.html @@ -6,11 +6,13 @@ data-test-id="bescheid-wizard" > <div class="fixed inset-0 z-10 w-screen overflow-y-auto"> - <div class="flex h-full items-end items-center justify-center p-8"> + <div class="flex h-full items-center justify-center p-8"> <div class="relative h-full w-full max-w-7xl transform overflow-hidden rounded-lg bg-background-200 px-6 py-10 text-left shadow-xl transition-all" > - <alfa-vorgang-detail-bescheiden-abbrechen-button></alfa-vorgang-detail-bescheiden-abbrechen-button> + <alfa-vorgang-detail-bescheiden-abbrechen-button + (clickEmitter)="cancelWizard()" + ></alfa-vorgang-detail-bescheiden-abbrechen-button> <form [formGroup]="formService.form" class="flex h-full flex-row gap-11"> <alfa-vorgang-detail-bescheiden-steps class="flex w-1/2" diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.spec.ts index c82b596f3669350ebd42f569cae44749fa9c92f8..b201b90bf4bf872be0f5c3461dd719624462f58b 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.spec.ts @@ -1,6 +1,7 @@ import { BescheidResource, BescheidService } from '@alfa-client/bescheid-shared'; import { PostfachService } from '@alfa-client/postfach-shared'; import { Mock, dispatchEventFromFixture, mock, useFromMock } from '@alfa-client/test-utils'; +import { OzgcloudDialogService } from '@alfa-client/ui'; import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; import { ComponentFixture, TestBed } from '@angular/core/testing'; @@ -8,11 +9,13 @@ import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms'; import { MatIcon } from '@angular/material/icon'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; +import { Subscription, of } from 'rxjs'; import { createBescheidResource } from '../../../../../bescheid-shared/src/test/bescheid'; import { createVorgangWithEingangResource } from '../../../../../vorgang-shared/test/vorgang'; import { BescheidenFormService } from './bescheiden.formservice'; import { VorgangDetailBescheidenResultComponent } from './vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component'; import { VorgangDetailBescheidenAbbrechenButtonComponent } from './vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-button/vorgang-detail-bescheiden-abbrechen-button.component'; +import { VorgangDetailBescheidenAbbrechenDialogComponent } from './vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-dialog/vorgang-detail-bescheiden-abbrechen-dialog.component'; import { VorgangDetailBescheidenStepsComponent } from './vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps.component'; import { VorgangDetailBescheidenComponent } from './vorgang-detail-bescheiden.component'; @@ -23,8 +26,8 @@ describe('VorgangDetailBescheidenComponent', () => { let bescheidService: Mock<BescheidService>; let vorgangService: Mock<VorgangService>; let formService: BescheidenFormService; - let dialogRef: Mock<DialogRef>; let postfachService: Mock<PostfachService>; + let ozgcloudDialogService: Mock<OzgcloudDialogService>; let vorgangWithEingangResource: VorgangWithEingangResource; let bescheidDraftResource: BescheidResource; @@ -35,8 +38,8 @@ describe('VorgangDetailBescheidenComponent', () => { bescheidService = mock(BescheidService); vorgangService = mock(VorgangService); formService = new BescheidenFormService(new UntypedFormBuilder(), useFromMock(bescheidService)); - dialogRef = mock(DialogRef); postfachService = mock(PostfachService); + ozgcloudDialogService = mock(OzgcloudDialogService); vorgangWithEingangResource = createVorgangWithEingangResource(); bescheidDraftResource = createBescheidResource(); @@ -70,7 +73,7 @@ describe('VorgangDetailBescheidenComponent', () => { }, { provide: DialogRef, - useValue: dialogRef, + useValue: { keydownEvents: of(KeyboardEvent), close: jest.fn(), disableClose: false }, }, { provide: DIALOG_DATA, @@ -80,12 +83,17 @@ describe('VorgangDetailBescheidenComponent', () => { provide: PostfachService, useValue: postfachService, }, + { + provide: OzgcloudDialogService, + useValue: ozgcloudDialogService, + }, ], imports: [ReactiveFormsModule], }).compileComponents(); fixture = TestBed.createComponent(VorgangDetailBescheidenComponent); component = fixture.componentInstance; + fixture.detectChanges(); }); @@ -109,13 +117,40 @@ describe('VorgangDetailBescheidenComponent', () => { expect(patchValues).toBeCalledWith(bescheidDraftResource); }); + + it('should call handleEscapeKey', () => { + component.handleEscapeKey = jest.fn(); + + component.ngOnInit(); + + expect(component.handleEscapeKey).toHaveBeenCalled(); + }); + }); + + describe('handleEscapeKey', () => { + it('should subscribe to dialogRef.keydownEvents', () => { + component.handleEscapeKey(); + + expect(component.keydownEventsSubscription).toBeInstanceOf(Subscription); + }); + }); + + describe('ngOnDestroy', () => { + it('should unsubscribe keydownEventsSubscription', () => { + component.keydownEventsSubscription = new Subscription(); + jest.spyOn(component.keydownEventsSubscription, 'unsubscribe'); + + component.ngOnDestroy(); + + expect(component.keydownEventsSubscription.unsubscribe).toHaveBeenCalled(); + }); }); describe('onClose', () => { it('should call dialogRef.close', () => { component.onClose(); - expect(dialogRef.close).toHaveBeenCalled(); + expect(component.dialogRef.close).toHaveBeenCalled(); }); it('should call vorgang service to reload current vorgang', () => { @@ -135,7 +170,18 @@ describe('VorgangDetailBescheidenComponent', () => { it('should close dialog ref', () => { dispatchEventFromFixture(fixture, bescheidenResult, 'closeDialog'); - expect(dialogRef.close).toHaveBeenCalled(); + expect(component.dialogRef.close).toHaveBeenCalled(); + }); + }); + + describe('cancelWizard', () => { + it('should call ozgcloudDialogService.open', () => { + component.cancelWizard(); + + expect(ozgcloudDialogService.openInCallingComponentContext).toHaveBeenCalledWith( + VorgangDetailBescheidenAbbrechenDialogComponent, + component.viewContainerRef, + ); }); }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts index c3a880423edbb95b2b2153db66b3202ae9dd7bad..a88075f699841c8e2b5d9c085023f6f087efc854 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts @@ -1,19 +1,24 @@ import { BescheidResource } from '@alfa-client/bescheid-shared'; import { PostfachService } from '@alfa-client/postfach-shared'; +import { OzgcloudDialogService } from '@alfa-client/ui'; import { VorgangService } from '@alfa-client/vorgang-shared'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; +import { Subscription, filter } from 'rxjs'; import { BescheidenFormService } from './bescheiden.formservice'; import { BescheidenDialogData } from './bescheiden.model'; +import { VorgangDetailBescheidenAbbrechenDialogComponent } from './vorgang-detail-bescheiden-steps/vorgang-detail-bescheiden-steps-content/vorgang-detail-bescheiden-abbrechen-dialog/vorgang-detail-bescheiden-abbrechen-dialog.component'; @Component({ selector: 'alfa-vorgang-detail-bescheiden', templateUrl: './vorgang-detail-bescheiden.component.html', providers: [BescheidenFormService], }) -export class VorgangDetailBescheidenComponent implements OnInit { +export class VorgangDetailBescheidenComponent implements OnDestroy, OnInit { private readonly bescheidDraftResource: BescheidResource; + keydownEventsSubscription: Subscription; + public activeStep: number = 1; constructor( @@ -22,6 +27,8 @@ export class VorgangDetailBescheidenComponent implements OnInit { private readonly vorgangService: VorgangService, private readonly postfachService: PostfachService, @Inject(DIALOG_DATA) private readonly dialogData: BescheidenDialogData, + private readonly ozgcloudDialogService: OzgcloudDialogService, + readonly viewContainerRef: ViewContainerRef, ) { this.bescheidDraftResource = dialogData.bescheidDraftResource; } @@ -29,6 +36,17 @@ export class VorgangDetailBescheidenComponent implements OnInit { ngOnInit(): void { this.formService.setVorgangWithEingangResource(this.dialogData.vorgangWithEingangResource); this.formService.patchValues(this.bescheidDraftResource); + this.handleEscapeKey(); + } + + handleEscapeKey(): void { + this.keydownEventsSubscription = this.dialogRef.keydownEvents + .pipe(filter((event) => event.key === 'Escape')) + .subscribe(() => this.cancelWizard()); + } + + ngOnDestroy(): void { + this.keydownEventsSubscription.unsubscribe(); } public onClose(): void { @@ -36,4 +54,11 @@ export class VorgangDetailBescheidenComponent implements OnInit { this.vorgangService.reloadCurrentVorgang(); this.postfachService.setPostfachMailOnReload(); } + + public cancelWizard(): void { + this.ozgcloudDialogService.openInCallingComponentContext<VorgangDetailBescheidenAbbrechenDialogComponent>( + VorgangDetailBescheidenAbbrechenDialogComponent, + this.viewContainerRef, + ); + } } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts index 7466616259f69afe3069e61596a3af74acb2b891..a3554bb8bab782f85f2d0924eeb5dd51c9a906d5 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts @@ -22,9 +22,20 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { LoeschAnforderungService } from '@alfa-client/loesch-anforderung-shared'; -import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { + Issue, + MessageCode, + StateResource, + createErrorStateResource, + createStateResource, +} from '@alfa-client/tech-shared'; import { Mock, existsAsHtmlElement, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; -import { SpinnerComponent, SpinnerTransparencyComponent } from '@alfa-client/ui'; +import { + Messages, + SnackBarService, + SpinnerComponent, + SpinnerTransparencyComponent, +} from '@alfa-client/ui'; import { VorgangCommandService, VorgangService, @@ -34,11 +45,12 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { createApiError, createIssue } from 'libs/tech-shared/test/error'; import { ProgressBarComponent } from 'libs/ui/src/lib/ui/progress-bar/progress-bar.component'; import { SubnavigationComponent } from 'libs/ui/src/lib/ui/subnavigation/subnavigation.component'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; -import { Subject } from 'rxjs'; +import { EMPTY, Subject, of } from 'rxjs'; import { VorgangDetailActionButtonsComponent } from './vorgang-detail-area/vorgang-detail-action-buttons/vorgang-detail-action-buttons.component'; import { VorgangDetailAreaComponent } from './vorgang-detail-area/vorgang-detail-area.component'; import { VorgangDetailFormularButtonsComponent } from './vorgang-detail-area/vorgang-detail-formular-buttons/vorgang-detail-formular-buttons.component'; @@ -46,6 +58,9 @@ import { VorgangDetailBackButtonContainerComponent } from './vorgang-detail-back import { VorgangDetailMoreMenuComponent } from './vorgang-detail-more-menu/vorgang-detail-more-menu.component'; import { VorgangDetailPageComponent } from './vorgang-detail-page.component'; +import { NavigationService } from '@alfa-client/navigation-shared'; +import * as ErrorUtil from 'libs/tech-shared/src/lib/error/error.util'; + type withGetVorgang = { getVorgangWithEingang: () => Subject<StateResource<VorgangWithEingang>>; }; @@ -58,6 +73,8 @@ describe('VorgangDetailPageComponent', () => { let vorgangService: Mock<VorgangService> | withGetVorgang; const vorgangCommandService = mock(VorgangCommandService); const loeschAnforderungService = mock(LoeschAnforderungService); + const navigationService: Mock<NavigationService> = mock(NavigationService); + const snackbarService: Mock<SnackBarService> = mock(SnackBarService); const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); @@ -100,6 +117,14 @@ describe('VorgangDetailPageComponent', () => { provide: LoeschAnforderungService, useValue: loeschAnforderungService, }, + { + provide: NavigationService, + useValue: navigationService, + }, + { + provide: SnackBarService, + useValue: snackbarService, + }, ], }); @@ -114,7 +139,7 @@ describe('VorgangDetailPageComponent', () => { describe('ngOnInit', () => { it('should get vorgangWithEingang', () => { - vorgangService.getVorgangWithEingang = jest.fn(); + vorgangService.getVorgangWithEingang = jest.fn().mockReturnValue(of(EMPTY)); component.ngOnInit(); @@ -197,4 +222,62 @@ describe('VorgangDetailPageComponent', () => { existsAsHtmlElement(fixture, detailArea); }); }); + + describe('handleApiError', () => { + afterEach(() => { + snackbarService.showInfo.mockClear(); + navigationService.navigateToVorgangList.mockClear(); + }); + + describe('with ApiError RESOURCE_NOT_FOUND', () => { + const issue: Issue = { ...createIssue(), messageCode: MessageCode.RESOURCE_NOT_FOUND }; + const resource: StateResource<any> = createErrorStateResource(createApiError([issue])); + + it('should call isResourceNotFoundError', () => { + const isResourceNotFoundError: jest.SpyInstance<boolean> = jest + .spyOn(ErrorUtil, 'isResourceNotFoundError') + .mockClear(); + + component.handleApiError(resource); + + expect(isResourceNotFoundError).toHaveBeenCalled(); + }); + + it('should call showSnackBarInfo', () => { + component.handleApiError(resource); + + expect(snackbarService.showInfo).toHaveBeenCalledWith( + Messages.HTTP_STATUS_VORGANG_NOT_FOUND, + ); + }); + + it('should call navigateToVorgangList', () => { + component.handleApiError(resource); + + expect(navigationService.navigateToVorgangList).toHaveBeenCalled(); + }); + }); + + describe('without ApiError', () => { + it('should not call snackbarService.showInfo', () => { + const resource: StateResource<any> = createStateResource( + createVorgangWithEingangResource(), + ); + + component.handleApiError(resource); + + expect(snackbarService.showInfo).not.toHaveBeenCalled(); + }); + + it('should not call navigationService.navigateToVorgangList', () => { + const resource: StateResource<any> = createStateResource( + createVorgangWithEingangResource(), + ); + + component.handleApiError(resource); + + expect(navigationService.navigateToVorgangList).not.toHaveBeenCalled(); + }); + }); + }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts index 8d51e40d64b5f8a1cc56566ff9a5bdcf733cfebb..97e2bae42d5480038b639184224643bf1b440318 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts @@ -23,14 +23,16 @@ */ import { CommandResource } from '@alfa-client/command-shared'; import { LoeschAnforderungService } from '@alfa-client/loesch-anforderung-shared'; -import { StateResource } from '@alfa-client/tech-shared'; +import { NavigationService } from '@alfa-client/navigation-shared'; +import { StateResource, isResourceNotFoundError } from '@alfa-client/tech-shared'; +import { Messages, SnackBarService } from '@alfa-client/ui'; import { VorgangCommandService, VorgangService, VorgangWithEingangResource, } from '@alfa-client/vorgang-shared'; import { Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, tap } from 'rxjs'; import { BescheidenFormService } from './vorgang-detail-bescheiden/bescheiden.formservice'; @Component({ @@ -48,12 +50,25 @@ export class VorgangDetailPageComponent implements OnInit { private vorgangService: VorgangService, private vorgangCommandService: VorgangCommandService, private loeschAnforderungService: LoeschAnforderungService, + public snackbarService: SnackBarService, + public navigationService: NavigationService, ) {} ngOnInit(): void { - this.vorgangStateResource$ = this.vorgangService.getVorgangWithEingang(); + this.vorgangStateResource$ = this.vorgangService + .getVorgangWithEingang() + .pipe( + tap((resource: StateResource<VorgangWithEingangResource>) => this.handleApiError(resource)), + ); this.revokeCommandStateResource$ = this.vorgangCommandService.getRevokeCommand(); this.vorgangLoeschenCommandStateResource$ = this.loeschAnforderungService.getEndgueltigLoeschenCommand(); } + + handleApiError(resource: StateResource<VorgangWithEingangResource>) { + if (isResourceNotFoundError(resource.error)) { + this.snackbarService.showInfo(Messages.HTTP_STATUS_VORGANG_NOT_FOUND); + this.navigationService.navigateToVorgangList(); + } + } } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts index 910757039e1fdfe3e1b5ff885a2491687d92cfa2..5a6d84231e9b41aa3bc51655315edd6ff5e4e531 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts @@ -49,7 +49,7 @@ import { } from '@ods/component'; import { AttachmentComponent, - AttachmentContainerComponent, + AttachmentWrapperComponent, BescheidGenerateIconComponent, BescheidStatusTextComponent, BescheidUploadIconComponent, @@ -80,11 +80,9 @@ import { VorgangDetailAreaComponent } from './vorgang-detail-page/vorgang-detail import { VorgangDetailFormularButtonsComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-buttons/vorgang-detail-formular-buttons.component'; import { VorgangDetailAntragDataComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-antrag-data/vorgang-detail-antrag-data.component'; import { VorgangDetailEingangHeaderComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-antrag-data/vorgang-detail-eingang-header/vorgang-detail-eingang-header.component'; -import { VorgangDetailAttachmentListComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component'; +import { VorgangDetailDateienContainerComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component'; import { VorgangDetailFormDataTableComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-form-data-table/vorgang-detail-form-data-table.component'; import { VorgangDetailFormularDatenComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component'; -import { VorgangDetailMetaDataComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component'; -import { VorgangDetailRepresentationListComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component'; import { VorgangDetailHeaderComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-header/vorgang-detail-header.component'; import { VorgangDetailBackButtonContainerComponent } from './vorgang-detail-page/vorgang-detail-back-button-container/vorgang-detail-back-button-container.component'; import { VorgangDetailBackButtonComponent } from './vorgang-detail-page/vorgang-detail-back-button-container/vorgang-detail-back-button/vorgang-detail-back-button.component'; @@ -155,7 +153,7 @@ const routes: Routes = [ BescheidUploadIconComponent, BescheidGenerateIconComponent, AttachmentComponent, - AttachmentContainerComponent, + AttachmentWrapperComponent, ButtonWithSpinnerComponent, FileUploadEditorComponent, SingleFileUploadEditorComponent, @@ -185,9 +183,6 @@ const routes: Routes = [ VorgangDetailBackButtonContainerComponent, VorgangDetailFormularDatenComponent, VorgangDetailAntragDataComponent, - VorgangDetailMetaDataComponent, - VorgangDetailAttachmentListComponent, - VorgangDetailRepresentationListComponent, VorgangDetailMoreMenuComponent, VorgangExportContainerComponent, ProcessVorgangContainerComponent, @@ -216,13 +211,11 @@ const routes: Routes = [ VorgangDetailBescheidenResultAttachmentsComponent, VorgangDetailBescheidenBescheidVersendenComponent, VorgangDetailBescheidenResultNachrichtComponent, + VorgangDetailDateienContainerComponent, ], exports: [ VorgangDetailAntragstellerComponent, VorgangDetailAntragDataComponent, - VorgangDetailMetaDataComponent, - VorgangDetailAttachmentListComponent, - VorgangDetailRepresentationListComponent, VorgangDetailBescheidenComponent, ], }) diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html index f1ac25eb71af847bdcb522fe3f231474f1341c78..dc08624feba50617540824639b130daa51881517 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html @@ -30,6 +30,7 @@ class="search-field" data-test-id="search-form" > + <span aria-live="polite" class="sr-only" role="status">{{ searchResultPreviewLabel }}</span> <button #searchSubmitButton type="submit" @@ -61,14 +62,14 @@ (optionSelected)="formService.submitByPreviewList($event.option.value, searchString)" > <ozgcloud-spinner - [stateResource]="vorgangSearchPreviewList" - [class.autocomplete-spinner]="vorgangSearchPreviewList.loading" + [stateResource]="vorgangListPreview" + [class.autocomplete-spinner]="vorgangListPreview.loading" ></ozgcloud-spinner> - <ng-container *ngIf="vorgangSearchPreviewList.resource" data-test-id="search-preview-list"> + <ng-container *ngIf="vorgangListPreview.resource" data-test-id="search-preview-list"> <mat-option *ngFor=" - let vorgang of vorgangSearchPreviewList.resource + let vorgang of vorgangListPreview.resource | toEmbeddedResources: vorgangListLinkRel.VORGANG_HEADER_LIST " [value]="vorgang" diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts index 9fe1622edc1c551c7bff4d9ed5cb9ed910bceb40..ca73c782d282523f6ae32f0ef50fa9ec872d217f 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts @@ -54,7 +54,11 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NavigationEnd, Router, RouterEvent } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { getDataTestClassOf, getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { createVorgangListResource } from 'libs/vorgang-shared/test/vorgang'; +import { + createVorgangListResource, + createVorgangListResourceWithResource, + createVorgangResource, +} from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; import { BehaviorSubject, ReplaySubject, Subject, of } from 'rxjs'; import { VorgangSearchAutocompleteOptionsContentComponent } from './vorgang-search-autocomplete-options-content/vorgang-search-autocomplete-options-content.component'; @@ -186,6 +190,66 @@ describe('VorgangSearchComponent', () => { }); }); + describe('set vorgangSearchPreviewList', () => { + it('should set vorgangPreview', () => { + const vorgangList = createStateResource(createVorgangListResource()); + + component.vorgangSearchPreviewList = vorgangList; + + expect(component.vorgangListPreview).toEqual(vorgangList); + }); + + it('should call buildSearchResultPreviewLabel', () => { + component.buildSearchResultPreviewLabel = jest.fn(); + const vorgangList = createStateResource(createVorgangListResource()); + + component.vorgangSearchPreviewList = vorgangList; + + expect(component.buildSearchResultPreviewLabel).toBeCalledWith(vorgangList); + }); + + it('should set previewLabel', () => { + component.buildSearchResultPreviewLabel = jest.fn(() => 'test'); + const vorgangList = createStateResource(createVorgangListResource()); + + component.vorgangSearchPreviewList = vorgangList; + + expect(component.searchResultPreviewLabel).toBe('test'); + }); + }); + + describe('buildSearchResultPreviewLabel', () => { + it('should return label for many search results', () => { + const vorgangList = createStateResource(createVorgangListResource()); + + const result = component.buildSearchResultPreviewLabel(vorgangList); + + expect(result).toBe( + '10 Vorschläge werden angezeigt, nutze Pfeiltaste nach unten, um diese zu erreichen', + ); + }); + + it('should return label for one search result', () => { + const vorgangList = createStateResource( + createVorgangListResourceWithResource([createVorgangResource()]), + ); + + const result = component.buildSearchResultPreviewLabel(vorgangList); + + expect(result).toBe( + 'Ein Vorschlag wird angezeigt, nutze Pfeiltaste nach unten, um den zu erreichen', + ); + }); + + it('should return empty string', () => { + const vorgangList = createStateResource(null); + + const result = component.buildSearchResultPreviewLabel(vorgangList); + + expect(result).toBe(EMPTY_STRING); + }); + }); + describe('search clear button', () => { beforeEach(() => { searchStringSubj.next('test'); diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts index 5375e849b6359a8ba54dd883b83ed8da5daae844..7857c502c95a7b38bed14c9a85ef50d8a748a68a 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts @@ -21,7 +21,12 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { StateResource, isNotNil } from '@alfa-client/tech-shared'; +import { + EMPTY_STRING, + StateResource, + getEmbeddedResources, + isNotNil, +} from '@alfa-client/tech-shared'; import { VorgangHeaderLinkRel, VorgangListLinkRel, @@ -51,7 +56,12 @@ import { VorgangSearchFormService } from './vorgang-search.formservice'; providers: [VorgangSearchFormService], }) export class VorgangSearchComponent implements OnInit, OnDestroy { - @Input() vorgangSearchPreviewList: StateResource<VorgangListResource>; + @Input() set vorgangSearchPreviewList( + vorgangListStateResource: StateResource<VorgangListResource>, + ) { + this.vorgangListPreview = vorgangListStateResource; + this.searchResultPreviewLabel = this.buildSearchResultPreviewLabel(vorgangListStateResource); + } @Input() searchString: string; @Output() public clearVorgangSearchPreviewList: EventEmitter<void> = new EventEmitter<void>(); @@ -60,6 +70,8 @@ export class VorgangSearchComponent implements OnInit, OnDestroy { @ViewChild('searchSubmitButton') searchSubmitButton: MatButton; @ViewChild('searchAutoComplete') searchAutoComplete: MatAutocomplete; + vorgangListPreview: StateResource<VorgangListResource>; + searchResultPreviewLabel: string = ''; previouslyEnteredSearchValue: string; private subscription: Subscription; @@ -101,6 +113,20 @@ export class VorgangSearchComponent implements OnInit, OnDestroy { } } + buildSearchResultPreviewLabel( + vorgangListStateResource: StateResource<VorgangListResource>, + ): string { + const previewListLength = getEmbeddedResources( + vorgangListStateResource, + this.vorgangListLinkRel.VORGANG_HEADER_LIST, + )?.length; + if (previewListLength === 1) + return 'Ein Vorschlag wird angezeigt, nutze Pfeiltaste nach unten, um den zu erreichen'; + if (previewListLength > 1) + return `${previewListLength} Vorschläge werden angezeigt, nutze Pfeiltaste nach unten, um diese zu erreichen`; + return EMPTY_STRING; + } + private isRelatedTargetSearchButton(event: FocusEvent): boolean { return event.relatedTarget === this.searchSubmitButton._elementRef.nativeElement; } diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts index f84d501a719fb4fe3a75a86a22a2ed92757f2850..e9d054d5197c95f9912ce6b5a787df923694d446 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts @@ -21,9 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { FormControl, UntypedFormBuilder } from '@angular/forms'; -import { Params } from '@angular/router'; -import { faker } from '@faker-js/faker'; import { NavigationService } from '@alfa-client/navigation-shared'; import { EMPTY_STRING, encodeUrlForEmbedding } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; @@ -32,6 +29,9 @@ import { VorgangListService, VorgangResource, } from '@alfa-client/vorgang-shared'; +import { FormControl, UntypedFormBuilder } from '@angular/forms'; +import { Params } from '@angular/router'; +import { faker } from '@faker-js/faker'; import { ResourceUri, getUrl } from '@ngxp/rest'; import { createVorgangResource } from 'libs/vorgang-shared/test/vorgang'; import { Observable, of } from 'rxjs'; @@ -131,15 +131,6 @@ describe('VorgangSearchFormService', () => { expect(shouldSearch).toBeFalsy(); }); - - it('on searchString is equals lastsearchString', () => { - formService.lastSearchString = VALID_SEARCH_STRING; - formService.isSearchInputNotPristine = jest.fn().mockReturnValue(true); - - const shouldSearch: boolean = formService.shouldSearchForPreview(VALID_SEARCH_STRING); - - expect(shouldSearch).toBeFalsy(); - }); }); describe('should return true', () => { @@ -148,7 +139,7 @@ describe('VorgangSearchFormService', () => { formService.isSearchInputNotPristine = jest.fn().mockReturnValue(true); }); - it('on searchInput is NOT pristine, has minLength and is not lastSearchString', () => { + it('on searchInput is NOT pristine and has minLength', () => { const shouldSearch: boolean = formService.shouldSearchForPreview(VALID_SEARCH_STRING); expect(shouldSearch).toBeTruthy(); diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts index 3a65e9250ae11831cf30e208f7898e93a78ffeaf..bf88a1381c9a827c61a71dc204f5cbaf820ca63a 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts @@ -21,9 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Injectable, OnDestroy } from '@angular/core'; -import { FormGroup, UntypedFormBuilder, UntypedFormControl } from '@angular/forms'; -import { Params } from '@angular/router'; import { NavigationService } from '@alfa-client/navigation-shared'; import { EMPTY_STRING, @@ -37,7 +34,10 @@ import { VorgangListService, VorgangResource, } from '@alfa-client/vorgang-shared'; -import { isEmpty, isEqual } from 'lodash-es'; +import { Injectable, OnDestroy } from '@angular/core'; +import { FormGroup, UntypedFormBuilder, UntypedFormControl } from '@angular/forms'; +import { Params } from '@angular/router'; +import { isEmpty } from 'lodash-es'; import { Observable, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, first, tap } from 'rxjs/operators'; @@ -100,9 +100,7 @@ export class VorgangSearchFormService implements OnDestroy { shouldSearchForPreview(value: string): boolean { return ( - this.isSearchInputNotPristine() && - hasMinLength(value, this.PREVIEW_SEARCH_STRING_MIN_LENGTH) && - !isEqual(this.lastSearchString, value) + this.isSearchInputNotPristine() && hasMinLength(value, this.PREVIEW_SEARCH_STRING_MIN_LENGTH) ); } diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/wiedervorlage-icon/wiedervorlage-icon.component.html b/alfa-client/libs/vorgang-shared-ui/src/lib/wiedervorlage-icon/wiedervorlage-icon.component.html index 5e95367c03a46c38103869483604b2442dc0fa7b..9bf2265a277651fe8d40eff1a80ad736136077ea 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/wiedervorlage-icon/wiedervorlage-icon.component.html +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/wiedervorlage-icon/wiedervorlage-icon.component.html @@ -6,6 +6,9 @@ ></ozgcloud-svgicon> </ng-container> <ng-template #defaultFrist> - <ozgcloud-icon icon="update" data-test-class="wiedervorlage-icon-default"></ozgcloud-icon> + <ozgcloud-svgicon + svgIcon="update" + data-test-class="wiedervorlage-icon-default" + ></ozgcloud-svgicon> </ng-template> </div> diff --git a/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts b/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts index 508ab793ded8f30eb496de410dea11af6e79b417..57cfeeab38cb013afb230703102924d436b2acd0 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts @@ -64,6 +64,7 @@ export enum VorgangWithEingangLinkRel { BESCHEID_DRAFT = 'bescheidDraft', BESCHEIDE = 'bescheide', UEBERSPRINGEN_UND_ABSCHLIESSEN = 'ueberspringen_und_abschliessen', + DOWNLOAD_ATTACHMENTS = 'downloadAttachments', } export enum LoeschAnforderungLinkRel { diff --git a/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html b/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html index f0c81c71c4fc151f86c17011eabc9193f48f2cef..4ae91cb40e746eef978cc1682f431ec3ab17e9f1 100644 --- a/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html +++ b/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html @@ -52,7 +52,7 @@ </div> </div> <div class="flex min-w-0 flex-1 flex-col gap-2"> - <div class="text-base font-medium">{{ vorgang.name }}</div> + <div data-test-id="name" class="text-base font-medium">{{ vorgang.name }}</div> <div> <alfa-vorgang-nummer class="vorgang-nummer" [vorgang]="vorgang"></alfa-vorgang-nummer> </div> diff --git a/alfa-client/libs/vorgang/src/lib/vorgang-list-page-container/vorgang-list-page/vorgang-views-menu/vorgang-views-menu.component.spec.ts b/alfa-client/libs/vorgang/src/lib/vorgang-list-page-container/vorgang-list-page/vorgang-views-menu/vorgang-views-menu.component.spec.ts index 2d312f19021b016c3985e0b17b487fc982210f79..68e9ff8f95bb4ffb237ddc3b5863f15a84ffee44 100644 --- a/alfa-client/libs/vorgang/src/lib/vorgang-list-page-container/vorgang-list-page/vorgang-views-menu/vorgang-views-menu.component.spec.ts +++ b/alfa-client/libs/vorgang/src/lib/vorgang-list-page-container/vorgang-list-page/vorgang-views-menu/vorgang-views-menu.component.spec.ts @@ -21,21 +21,25 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ApiRootLinkRel } from '@alfa-client/api-root-shared'; import { HasLinkPipe, createStateResource } from '@alfa-client/tech-shared'; import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-utils'; -import { OzgcloudIconComponent, PostfachIconComponent } from '@alfa-client/ui'; +import { + OzgcloudIconComponent, + OzgcloudSvgIconComponent, + PostfachIconComponent, +} from '@alfa-client/ui'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { HasAnyLinkPipe } from 'libs/tech-shared/src/lib/pipe/has-any-link.pipe'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { createVorgangStatistic } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; +import { WiedervorlageIconComponent } from '../../../../../../vorgang-shared-ui/src/lib/wiedervorlage-icon/wiedervorlage-icon.component'; import { VorgangSearchViewItemContainerComponent } from './vorgang-search-view-item-container/vorgang-search-view-item-container.component'; import { VorgangViewItemContainerComponent } from './vorgang-view-item-container/vorgang-view-item-container.component'; import { VorgangViewItemComponent } from './vorgang-view-item-container/vorgang-view-item/vorgang-view-item.component'; import { VorgangViewsMenuComponent } from './vorgang-views-menu.component'; -import { WiedervorlageIconComponent } from '../../../../../../vorgang-shared-ui/src/lib/wiedervorlage-icon/wiedervorlage-icon.component'; describe('VorgangViewsMenuComponent', () => { let component: VorgangViewsMenuComponent; @@ -63,6 +67,7 @@ describe('VorgangViewsMenuComponent', () => { MockComponent(VorgangViewItemComponent), MockComponent(VorgangSearchViewItemContainerComponent), MockComponent(OzgcloudIconComponent), + MockComponent(OzgcloudSvgIconComponent), MockComponent(PostfachIconComponent), ], }).compileComponents(); diff --git a/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html b/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html index 62717bd101780a3e2c58031f1a1404640613ffcb..c472eaa9ff02c349f6db5a95cef29902f0a5a76a 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html +++ b/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html @@ -24,7 +24,7 @@ --> <alfa-vertical-binary-file-list - [fileListResource]="attachments$ | async" + [binaryFileListStateResource]="attachments$ | async" [deletable]="false" data-test-id="wiedervorlage-attachment-list" > diff --git a/alfa-client/package-lock.json b/alfa-client/package-lock.json index d98128ff37fbc9a75656282a0e5652ee59966c91..a7e9f6cfe77a4da3de7f151bdcbd299486c59c20 100644 --- a/alfa-client/package-lock.json +++ b/alfa-client/package-lock.json @@ -1,12 +1,12 @@ { "name": "alfa", - "version": "0.3.0-SNAPSHOT", + "version": "0.7.0-SNAPSHOT", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "alfa", - "version": "0.3.0-SNAPSHOT", + "version": "0.7.0-SNAPSHOT", "license": "MIT", "dependencies": { "@angular/animations": "17.3.10", @@ -32,6 +32,7 @@ "angular-oauth2-oidc-jwks": "17.0.2", "class-variance-authority": "^0.7.0", "date-fns": "^2.30.0", + "decompress": "^4.2.1", "file-saver": "2.0.5", "include-media": "^1.4.10", "js-base64": "^3.7.7", @@ -88,7 +89,7 @@ "autoprefixer": "^10.4.19", "cypress": "13.6.2", "cypress-file-upload": "5.0.8", - "cypress-mochawesome-reporter": "3.6.0", + "cypress-mochawesome-reporter": "3.8", "cypress-real-events": "^1.12.0", "cypress-timestamps": "^1.2.3", "eslint": "8.57.0", @@ -6191,11 +6192,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6219,17 +6215,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6480,6 +6465,14 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/find-up/-/find-up-4.1.0.tgz", @@ -6492,6 +6485,18 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/locate-path/-/locate-path-5.0.0.tgz", @@ -16124,6 +16129,26 @@ "node": ">=14.15.0" } }, + "node_modules/@yarnpkg/parsers/node_modules/argparse": { + "version": "1.0.10", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@zkochan/js-yaml": { "version": "0.0.6", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", @@ -16135,11 +16160,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@zkochan/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/abab": { "version": "2.0.6", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/abab/-/abab-2.0.6.tgz", @@ -16498,12 +16518,9 @@ "devOptional": true }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-hidden": { "version": "1.2.4", @@ -17731,15 +17748,36 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, "engines": { "node": "*" } }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/buffer-from/-/buffer-from-1.1.2.tgz", @@ -19562,10 +19600,11 @@ } }, "node_modules/cypress-mochawesome-reporter": { - "version": "3.6.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.6.0.tgz", - "integrity": "sha512-NeYpeZVB5YCU10I3a1yA2qHt+YREo0jZw4Gj83JTJ7YX/ZLFfd8MYKl2O19d/yYC8np/fpMufp5gt3ympd9DWQ==", + "version": "3.8.2", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.8.2.tgz", + "integrity": "sha512-oJZkNzhNmN9ZD+LmZyFuPb8aWaIijyHyqYh52YOBvR6B6ckfJNCHP3A98a+/nG0H4t46CKTNwo+wNpMa4d2kjA==", "dev": true, + "license": "MIT", "dependencies": { "commander": "^10.0.1", "fs-extra": "^10.0.1", @@ -19871,6 +19910,221 @@ "node": ">=0.10" } }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "license": "MIT", + "dependencies": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "license": "MIT", + "dependencies": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tar/node_modules/bl": { + "version": "1.2.3", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/decompress-tar/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-tar/node_modules/isarray": { + "version": "1.0.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/decompress-tar/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/decompress-tar/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/decompress-tar/node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "license": "MIT", + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "license": "MIT", + "dependencies": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "license": "MIT", + "dependencies": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "license": "MIT", + "dependencies": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-unzip/node_modules/get-stream": { + "version": "2.3.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "license": "MIT", + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress/node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/deep-equal/-/deep-equal-2.2.3.tgz", @@ -20907,11 +21161,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -20995,17 +21244,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -21631,7 +21869,6 @@ "version": "1.1.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, "dependencies": { "pend": "~1.2.0" } @@ -21703,6 +21940,15 @@ "node": ">=14.14" } }, + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -24049,6 +24295,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/is-number/-/is-number-7.0.0.tgz", @@ -26076,12 +26328,11 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -27588,13 +27839,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "peer": true - }, "node_modules/mocha/node_modules/chokidar": { "version": "3.5.3", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/chokidar/-/chokidar-3.5.3.tgz", @@ -27689,19 +27933,6 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/mocha/node_modules/minimatch": { "version": "5.0.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/minimatch/-/minimatch-5.0.1.tgz", @@ -29491,11 +29722,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/nx/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/nx/node_modules/chalk": { "version": "4.1.2", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/chalk/-/chalk-4.1.2.tgz", @@ -29530,17 +29756,6 @@ "node": ">=8" } }, - "node_modules/nx/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/nx/node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/jsonc-parser/-/jsonc-parser-3.2.0.tgz", @@ -29593,7 +29808,6 @@ "version": "4.1.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -30383,8 +30597,7 @@ "node_modules/pend": { "version": "1.2.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/performance-now": { "version": "2.1.0", @@ -30416,6 +30629,27 @@ "node": ">=0.10.0" } }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/pirates/-/pirates-4.0.6.tgz", @@ -31062,11 +31296,6 @@ } } }, - "node_modules/postcss-loader/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/postcss-loader/node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -31092,17 +31321,6 @@ } } }, - "node_modules/postcss-loader/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/postcss-logical": { "version": "5.0.4", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/postcss-logical/-/postcss-logical-5.0.4.tgz", @@ -33380,6 +33598,25 @@ "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/secure-compare/-/secure-compare-3.0.1.tgz", "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==" }, + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "license": "MIT", + "dependencies": { + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "2.20.3", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/select-hose/-/select-hose-2.0.0.tgz", @@ -34642,6 +34879,15 @@ "node": ">=8" } }, + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "license": "MIT", + "dependencies": { + "is-natural-number": "^4.0.1" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -35442,6 +35688,12 @@ "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", "dev": true }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "license": "MIT" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -36098,6 +36350,16 @@ "node": ">=0.8.0" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/undici": { "version": "6.11.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/undici/-/undici-6.11.1.tgz", @@ -37784,7 +38046,6 @@ "version": "4.0.2", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "engines": { "node": ">=0.4" } @@ -37887,7 +38148,6 @@ "version": "2.10.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/alfa-client/package.json b/alfa-client/package.json index dc0151bac5d9d6567a481c5009ca1a0bebdccd54..8dc2364f58bb67444d25979f3fd75136efdac63e 100644 --- a/alfa-client/package.json +++ b/alfa-client/package.json @@ -1,6 +1,6 @@ { "name": "alfa", - "version": "0.3.0-SNAPSHOT", + "version": "0.7.0-SNAPSHOT", "license": "MIT", "scripts": { "start": "nx run alfa:serve --port 4300 --disable-host-check", @@ -71,6 +71,7 @@ "angular-oauth2-oidc-jwks": "17.0.2", "class-variance-authority": "^0.7.0", "date-fns": "^2.30.0", + "decompress": "^4.2.1", "file-saver": "2.0.5", "include-media": "^1.4.10", "js-base64": "^3.7.7", @@ -127,7 +128,7 @@ "autoprefixer": "^10.4.19", "cypress": "13.6.2", "cypress-file-upload": "5.0.8", - "cypress-mochawesome-reporter": "3.6.0", + "cypress-mochawesome-reporter": "3.8", "cypress-real-events": "^1.12.0", "cypress-timestamps": "^1.2.3", "eslint": "8.57.0", @@ -160,4 +161,4 @@ "ts-node": "10.9.1", "typescript": "5.4.5" } -} \ No newline at end of file +} diff --git a/alfa-client/pom.xml b/alfa-client/pom.xml index 57beed7ab6d0d58e675a3475c8bf14a5603b8923..31c038a64e63e52771d87ecb1085e907e9af2cb8 100644 --- a/alfa-client/pom.xml +++ b/alfa-client/pom.xml @@ -29,7 +29,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> diff --git a/alfa-server/pom.xml b/alfa-server/pom.xml index 3b98d77c4c8185451f105def421e6d428750e002..5124be2c496ebd8623a1445b16fdcb0a2074c4f0 100644 --- a/alfa-server/pom.xml +++ b/alfa-server/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <artifactId>alfa-server</artifactId> diff --git a/alfa-server/src/main/resources/application-dev.yml b/alfa-server/src/main/resources/application-dev.yml index 70ae53c6c499ef254dd591b6ef9a668022e741e9..52ca56a22d8fa5ded4dcecfae01b3e22553c2da1 100644 --- a/alfa-server/src/main/resources/application-dev.yml +++ b/alfa-server/src/main/resources/application-dev.yml @@ -10,7 +10,6 @@ server: ozgcloud: feature: reply-always-allowed: true - bescheid-wizard: true production: false stage: production: false diff --git a/alfa-server/src/main/resources/application-e2e.yml b/alfa-server/src/main/resources/application-e2e.yml index d91ee9e7f3874a0799b8c92bcdb28dab13dcb9ad..7aa16cc6e02921c1fa3157a05b185fcbf08c623d 100644 --- a/alfa-server/src/main/resources/application-e2e.yml +++ b/alfa-server/src/main/resources/application-e2e.yml @@ -10,7 +10,6 @@ ozgcloud: feature: createBescheid: true reply-always-allowed: true - bescheid-wizard: true forwarding: lninfo: url: classpath:files/LandesnetzInfo.html diff --git a/alfa-server/src/main/resources/application-local.yml b/alfa-server/src/main/resources/application-local.yml index 55480e664c9e5159bc787f902bd7c1d8e77eda1a..b113a7052d27746ebf67fdd3600b381e7e658dd5 100644 --- a/alfa-server/src/main/resources/application-local.yml +++ b/alfa-server/src/main/resources/application-local.yml @@ -18,7 +18,6 @@ grpc: ozgcloud: feature: reply-always-allowed: true - bescheid-wizard: true production: false user-assistance: documentation: diff --git a/alfa-service/javac.20240524_095230.args b/alfa-service/javac.20240524_095230.args deleted file mode 100644 index afb64b5f04472a1efb08d555a627210a5f51a84e..0000000000000000000000000000000000000000 --- a/alfa-service/javac.20240524_095230.args +++ /dev/null @@ -1 +0,0 @@ -@/tmp/org.codehaus.plexus.compiler.javac.JavacCompiler16216354973819705899arguments diff --git a/alfa-service/pom.xml b/alfa-service/pom.xml index f57d5f690a4cabf8e13bb94d2c7fd98a24a2949e..f2e9a4141f8dc1c75e3769a1671b1ca511714d8c 100644 --- a/alfa-service/pom.xml +++ b/alfa-service/pom.xml @@ -31,7 +31,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <artifactId>alfa-service</artifactId> diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java index e708645828f0bd692f4763859586a3725db45f12..1211543468abb21bc9b39b98408cd8075adc1710 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java @@ -23,33 +23,70 @@ */ package de.ozgcloud.alfa.attachment; -import org.springframework.beans.factory.annotation.Autowired; +import java.io.OutputStream; + import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; +import de.ozgcloud.alfa.common.zipdownload.ZipDownloadService; +import de.ozgcloud.alfa.vorgang.VorgangController; +import lombok.RequiredArgsConstructor; @RestController -@RequestMapping(AttachmentController.ATTACHMENT_PATH) +@RequestMapping(AttachmentController.PATH) +@RequiredArgsConstructor public class AttachmentController { - static final String ATTACHMENT_PATH = "/api/attachments"; // NOSONAR + static final String PATH = "/api/attachments"; // NOSONAR static final String PARAM_EINGANG_ID = "eingangId"; - @Autowired - private OzgFileService fileService; - @Autowired - private BinaryFileModelAssembler modelAssembler; + private final OzgFileService fileService; + private final BinaryFileModelAssembler modelAssembler; @GetMapping(params = PARAM_EINGANG_ID) public CollectionModel<EntityModel<OzgFile>> getAllByEingang(@RequestParam String eingangId) { return modelAssembler.toCollectionModel(fileService.getAttachmentsByEingang(eingangId)); } + + @RestController + @RequestMapping(AttachmentsByVorgangController.PATH) + @RequiredArgsConstructor + public static class AttachmentsByVorgangController { + static final String PATH = "/api/vorgang/{vorgangId}/attachments"; // NOSONAR + + private final OzgFileService fileService; + private final ZipDownloadService zipDownloadService; + private final VorgangController vorgangController; + + @GetMapping(params = PARAM_EINGANG_ID, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public ResponseEntity<StreamingResponseBody> download(@PathVariable String vorgangId, @RequestParam String eingangId) { + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", buildZipName(vorgangId))) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(out -> createZipFile(out, eingangId)); + } + + String buildZipName(String vorgangId) { + return vorgangController.getVorgang(vorgangId).getNummer() + "_Anhaenge.zip"; + } + + void createZipFile(OutputStream out, String eingangId) { + var ozgFiles = fileService.getAttachmentsByEingang(eingangId); + zipDownloadService.write(ozgFiles.toList(), out); + } + } + } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java index 3a134ac79f67c11e6168fe0116bcebbbfe94b24f..836adfdc95b6ca002dc00e842398b34b93279560 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java @@ -39,6 +39,9 @@ class BescheidHistorieProcessor implements HistorieProcessor { private static final Predicate<Command> IS_NOT_UPDATE_ATTACHED_ITEM = Predicate .not(command -> command.getCommandOrder() == CommandOrder.UPDATE_ATTACHED_ITEM && IS_BESCHEID_ATTACHED_ITEM.test(command)); + private static final Predicate<Command> IS_NOT_PATCH_ATTACHED_ITEM = Predicate + .not(command -> command.getCommandOrder() == CommandOrder.PATCH_ATTACHED_ITEM + && IS_BESCHEID_ATTACHED_ITEM.test(command)); @Override public List<Command> process(List<Command> commands) { @@ -52,6 +55,7 @@ class BescheidHistorieProcessor implements HistorieProcessor { .filter(IS_NOT_DELETE_BESCHEID_COMMAND) .filter(IS_NOT_CREATE_ATTACHED_ITEM) .filter(IS_NOT_UPDATE_ATTACHED_ITEM) + .filter(IS_NOT_PATCH_ATTACHED_ITEM) .toList(); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java index 1977b5ffb63dbf495578eeac0ebce27d6d7a4af5..1846ef681a80e682b12b20e191be1412107212f2 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessor.java @@ -12,7 +12,6 @@ import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.stereotype.Component; import de.ozgcloud.alfa.bescheid.BescheidController.BescheidByVorgangController; -import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.ModelBuilder; import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController; import de.ozgcloud.alfa.common.user.CurrentUserService; @@ -30,7 +29,6 @@ class BescheidVorgangProcessor implements RepresentationModelProcessor<EntityMod static final LinkRelation REL_BESCHEIDE = LinkRelation.of("bescheide"); private final BescheidService bescheidService; - private final FeatureToggleProperties featureToggleProperties; private final CurrentUserService currentUserService; @Override @@ -66,11 +64,11 @@ class BescheidVorgangProcessor implements RepresentationModelProcessor<EntityMod } BooleanSupplier isRetrievingDraftAllowed(Vorgang vorgang) { - return () -> featureToggleProperties.isBescheidWizard() && isVorgangInBearbeitung(vorgang) && draftExists(vorgang); + return () -> isVorgangInBearbeitung(vorgang) && draftExists(vorgang); } BooleanSupplier isCreatingDraftAllowed(Vorgang vorgang) { - return () -> featureToggleProperties.isBescheidWizard() && isVorgangInBearbeitung(vorgang) && !draftExists(vorgang); + return () -> isVorgangInBearbeitung(vorgang) && !draftExists(vorgang); } boolean isVorgangInBearbeitung(Vorgang vorgang) { diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentRemoteService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentRemoteService.java index 0cef6022ccd7c3ec41b83c090e732d467d7492a3..628f7a930eb4fd1df57ee334722661b43a980728 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentRemoteService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/DocumentRemoteService.java @@ -1,13 +1,9 @@ package de.ozgcloud.alfa.bescheid; -import java.util.UUID; - -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import de.ozgcloud.alfa.common.GrpcUtil; -import de.ozgcloud.alfa.common.binaryfile.FileId; import de.ozgcloud.document.DocumentServiceGrpc.DocumentServiceBlockingStub; import de.ozgcloud.document.GrpcGetDocumentRequest; import net.devh.boot.grpc.client.inject.GrpcClient; @@ -21,14 +17,6 @@ class DocumentRemoteService { private DocumentMapper documentMapper; public Document getDocument(String documentId) { - // DUMMY - if (StringUtils.equals(documentId, "DOCUMENT_URI")) { - return Document.builder() - .id(UUID.randomUUID().toString()) - .fileId(FileId.from("DOCUMENT_FILE_URI")) - .build(); - } - // var response = documentServiceStub.getDocument(GrpcGetDocumentRequest.newBuilder().setId(documentId).build()); return documentMapper.fromGrpc(response.getDocument()); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java index 2e8d93a5e51a15f05418aac4952d926c64717300..b06e637da196c9b9d5dfb11a5d51a72fb505da7d 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java @@ -21,9 +21,4 @@ public class FeatureToggleProperties { * Enable mail reply option regardless of other configuration. */ private boolean replyAlwaysAllowed = false; - - /** - * Enable/Disable bescheid wizard feature. - */ - private boolean bescheidWizard = false; } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadService.java new file mode 100644 index 0000000000000000000000000000000000000000..46ddd81e4099efc42772a94ce7e3fd33f1198d3d --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadService.java @@ -0,0 +1,58 @@ +package de.ozgcloud.alfa.common.zipdownload; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.springframework.stereotype.Service; + +import de.ozgcloud.alfa.common.binaryfile.BinaryFileService; +import de.ozgcloud.alfa.common.file.OzgFile; +import de.ozgcloud.common.binaryfile.TempFileUtils; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ZipDownloadService { + + private final BinaryFileService binaryFileService; + + public void write(List<OzgFile> ozgFiles, OutputStream out) { + writeZipFileContent(createZipFile(ozgFiles), out); + } + + File createZipFile(List<OzgFile> ozgFiles) { + var file = TempFileUtils.createTmpFile().toFile(); + try (var zipOutputStream = new ZipOutputStream(new FileOutputStream(file))) { + ozgFiles.forEach(ozgFile -> addOzgFileToZip(ozgFile, zipOutputStream)); + return file; + } catch (Exception e) { + throw new TechnicalException("Error creating zip file", e); + } + } + + void addOzgFileToZip(OzgFile ozgFile, ZipOutputStream zipOutputStream) { + try { + zipOutputStream.putNextEntry(new ZipEntry(ozgFile.getName())); + binaryFileService.writeFileContent(ozgFile.getId(), zipOutputStream); + zipOutputStream.closeEntry(); + } catch (IOException e) { + throw new TechnicalException("Cannot add file to ZIP.", e); + } + } + + void writeZipFileContent(File zipFile, OutputStream outputStream) { + try (var fileInputStream = new FileInputStream(zipFile)) { + fileInputStream.transferTo(outputStream); + } catch (Exception e) { + throw new TechnicalException("Error writing zip file to output stream", e); + } + } + +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java index ec181a8640f7f38d35ab43d06714ab8afd48411d..28913930a067740c12c8bf214423b59426f22c54 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java @@ -23,7 +23,6 @@ */ package de.ozgcloud.alfa.representation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.web.bind.annotation.GetMapping; @@ -34,22 +33,23 @@ import org.springframework.web.bind.annotation.RestController; import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; +import lombok.RequiredArgsConstructor; @RestController -@RequestMapping(RepresentationController.REPRESENTATIONS_PATH) +@RequestMapping(RepresentationController.PATH) +@RequiredArgsConstructor public class RepresentationController { - static final String REPRESENTATIONS_PATH = "/api/representations"; // NOSONAR + static final String PATH = "/api/representations"; // NOSONAR static final String PARAM_EINGANG_ID = "eingangId"; - @Autowired - private OzgFileService fileService; - @Autowired - private BinaryFileModelAssembler modelAssembler; + private final OzgFileService fileService; + private final BinaryFileModelAssembler modelAssembler; @GetMapping(params = PARAM_EINGANG_ID) public CollectionModel<EntityModel<OzgFile>> getAllByEingang(@RequestParam String eingangId) { return modelAssembler.toCollectionModel(fileService.getRepresentationsByEingang(eingangId)); } + } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java index 78b09e77437d01710618ee0351819412a198a274..94edbda19108796dfe1b0518e3cea960fa3aa1d5 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java @@ -31,7 +31,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; @@ -39,6 +38,7 @@ import org.springframework.hateoas.server.RepresentationModelProcessor; import org.springframework.stereotype.Component; import de.ozgcloud.alfa.attachment.AttachmentController; +import de.ozgcloud.alfa.attachment.AttachmentController.AttachmentsByVorgangController; import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.ModelBuilder; import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController; @@ -51,12 +51,15 @@ import de.ozgcloud.alfa.postfach.PostfachMailController; import de.ozgcloud.alfa.representation.RepresentationController; import de.ozgcloud.alfa.vorgang.VorgangProperties.VorgangProperty; import de.ozgcloud.alfa.vorgang.forwarding.ForwardingController; +import lombok.RequiredArgsConstructor; @Component +@RequiredArgsConstructor class VorgangWithEingangProcessor implements RepresentationModelProcessor<EntityModel<VorgangWithEingang>> { static final LinkRelation REL_KOMMENTARE = LinkRelation.of("kommentare"); static final LinkRelation REL_ATTACHMENTS = LinkRelation.of("attachments"); + static final LinkRelation REL_DOWNLOAD_ATTACHMENTS = LinkRelation.of("downloadAttachments"); static final LinkRelation REL_REPRESENTATIONS = LinkRelation.of("representations"); static final LinkRelation REL_POSTFACH_MAILS = LinkRelation.of("postfachMails"); static final LinkRelation REL_VORGANG_FORWARDING = LinkRelation.of("forwarding"); @@ -68,19 +71,13 @@ class VorgangWithEingangProcessor implements RepresentationModelProcessor<Entity static final String USER_PROFILE_SEARCH_DELETED_PARAM = "deleted"; static final String USER_PROFILE_SEARCH_ORGANISATIONS_EINHEIT_ID_PARAM = "organisationsEinheitId"; - @Autowired - private PostfachMailController postfachMailController; - @Autowired - private CurrentUserService userService; - @Autowired - private UserManagerUrlProvider userManagerUrlProvider; + private final PostfachMailController postfachMailController; + private final CurrentUserService userService; + private final UserManagerUrlProvider userManagerUrlProvider; - @Autowired - private FeatureToggleProperties featureToggleProperties; - @Autowired - private VorgangProperties vorgangProperties; - @Autowired - private VorgangProcessorProperties vorgangProcessorProperties; + private final FeatureToggleProperties featureToggleProperties; + private final VorgangProperties vorgangProperties; + private final VorgangProcessorProperties vorgangProcessorProperties; private static final Predicate<VorgangWithEingang> HAS_ATTACHMENTS = vorgangWithEingang -> vorgangWithEingang.getEingang() .getNumberOfAttachments() > 0; @@ -99,20 +96,22 @@ class VorgangWithEingangProcessor implements RepresentationModelProcessor<Entity return ModelBuilder.fromModel(model) .addLink(linkTo(KommentarByVorgangController.class).slash(vorgang.getId()).slash("kommentars").withRel(REL_KOMMENTARE)) .ifMatch(HAS_ATTACHMENTS) - .addLink(vorgangWithEingang -> linkTo(methodOn(AttachmentController.class).getAllByEingang(vorgangWithEingang.getEingang().getId())) - .withRel(REL_ATTACHMENTS)) + .addLinks(linkTo(methodOn(AttachmentController.class).getAllByEingang(vorgang.getEingang().getId())) + .withRel(REL_ATTACHMENTS), + linkTo(methodOn(AttachmentsByVorgangController.class).download(vorgang.getId(), vorgang.getEingang().getId())) + .withRel(REL_DOWNLOAD_ATTACHMENTS)) .ifMatch(HAS_REPRESENTATIONS) .addLink(vorgangWithEingang -> linkTo( methodOn(RepresentationController.class).getAllByEingang(vorgangWithEingang.getEingang().getId())) - .withRel(REL_REPRESENTATIONS)) + .withRel(REL_REPRESENTATIONS)) .ifMatch(this::isPostfachConfigured) .addLink(linkTo(methodOn(PostfachMailController.class).getAll(vorgang.getId())).withRel(REL_POSTFACH_MAILS)) .ifMatch(this::isEinheitlicherAnsprechpartner) .addLink(linkTo(methodOn(ForwardingController.class).findByVorgangId(vorgang.getId())).withRel(REL_VORGANG_FORWARDING)) .addLink(linkTo(methodOn(HistorieController.class).getAll(vorgang.getId())).withRel(REL_HISTORIE)) .ifMatch(() -> userManagerUrlProvider.isConfiguredForSearchUserProfile() && hasOrganisationsEinheitId(vorgang)) - .addLink(() -> buildSearchUserProfilesLink(vorgang)) - .ifMatch(() -> isCreateBescheidEnabled(vorgang)) + .addLink(this::buildSearchUserProfilesLink) + .ifMatch(this::isCreateBescheidEnabled) .addLink(linkTo(methodOn(CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(), null)).withRel(REL_BESCHEID)) .ifMatch(this::isProcessable) diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java index 801e18a6bde57ae9c7d1a6c12a9190a438cee89b..7e7b3a6e20a4aa2ca117acb79ece432c7d5cf57c 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java @@ -36,24 +36,29 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import de.ozgcloud.alfa.common.zipdownload.ZipDownloadService; +import lombok.SneakyThrows; class AttachmentControllerTest { - private final String PATH = AttachmentController.ATTACHMENT_PATH; - + @Spy @InjectMocks private AttachmentController controller; @Mock private BinaryFileModelAssembler modelAssembler; @Mock private OzgFileService fileService; + @Mock + private ZipDownloadService zipDownloadService; private MockMvc mockMvc; @@ -70,27 +75,33 @@ class AttachmentControllerTest { when(fileService.getAttachmentsByEingang(anyString())).thenReturn(Stream.of(OzgFileTestFactory.create())); } + @SneakyThrows @Test - void shouldCallEndpoint() throws Exception { - callEndpoint(); + void shouldResponseWithStatusOk() { + var result = callEndpoint(); + + result.andExpect(status().isOk()); } + @SneakyThrows @Test - void shoudlCallFileService() throws Exception { + void shoudlCallFileService() { callEndpoint(); verify(fileService).getAttachmentsByEingang(any()); } + @SneakyThrows @Test - void shouldCallModelAssembler() throws Exception { + void shouldCallModelAssembler() { callEndpoint(); verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<OzgFile>>any()); } - private void callEndpoint() throws Exception { - mockMvc.perform(get(PATH).param(AttachmentController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); + @SneakyThrows + private ResultActions callEndpoint() { + return mockMvc.perform(get(AttachmentController.PATH).param(AttachmentController.PARAM_EINGANG_ID, "1")); } } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentsByVorgangControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentsByVorgangControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..54638cb8a8390236ab8fc1c155d8ff3e33759779 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentsByVorgangControllerTest.java @@ -0,0 +1,179 @@ +package de.ozgcloud.alfa.attachment; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.io.OutputStream; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.attachment.AttachmentController.AttachmentsByVorgangController; +import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; +import de.ozgcloud.alfa.common.file.OzgFileService; +import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import de.ozgcloud.alfa.common.zipdownload.ZipDownloadService; +import de.ozgcloud.alfa.vorgang.EingangTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangController; +import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; +import lombok.SneakyThrows; + +class AttachmentsByVorgangControllerTest { + + @Spy + @InjectMocks + private AttachmentsByVorgangController controller; + @Mock + private BinaryFileModelAssembler modelAssembler; + @Mock + private OzgFileService fileService; + @Mock + private ZipDownloadService zipDownloadService; + @Mock + private VorgangController vorgangController; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @DisplayName("Download") + @Nested + class TestDownload { + private final String filename = LoremIpsum.getInstance().getWords(1); + + @BeforeEach + void setUpMock() { + doReturn(filename).when(controller).buildZipName(VorgangHeaderTestFactory.ID); + } + + @SneakyThrows + @Test + void shouldRespondWithStatusOk() { + var response = doRequest(); + + response.andExpect(status().isOk()); + } + + @SneakyThrows + @Test + void shouldBuildZipName() { + doRequest(); + + verify(controller).buildZipName(VorgangHeaderTestFactory.ID); + } + + @SneakyThrows + @Test + void shouldHaveContentDispositonHeader() { + var response = doRequest(); + + response.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)); + } + + @SneakyThrows + @Test + void shouldHaveContentType() { + var response = doRequest(); + + response.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)); + } + + @SneakyThrows + @Test + void shouldWriteZipFile() { + doRequest(); + + verify(controller).createZipFile(any(), eq(EingangTestFactory.ID)); + } + + @SneakyThrows + private ResultActions doRequest() { + return mockMvc.perform(asyncDispatch( + mockMvc.perform(get(AttachmentsByVorgangController.PATH.replace("{vorgangId}", VorgangHeaderTestFactory.ID) + "?eingangId=" + + EingangTestFactory.ID) + .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_OCTET_STREAM_VALUE)) + .andReturn())) + .andExpect(status().isOk()); + } + } + + @Nested + class TestBuildZipName { + private final String vorgangId = UUID.randomUUID().toString(); + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + @BeforeEach + void mockVorgangController() { + when(vorgangController.getVorgang(vorgangId)).thenReturn(vorgang); + } + + @Test + void shouldCallVorgangController() { + callController(); + + verify(vorgangController).getVorgang(vorgangId); + } + + @Test + void shouldReturnFileName() { + var resultFileName = callController(); + + assertThat(resultFileName).isEqualTo(vorgang.getNummer() + "_Anhaenge.zip"); + } + + private String callController() { + return controller.buildZipName(vorgangId); + } + } + + @Nested + class TestCreateZipFile { + @Mock + private OutputStream outputStream; + + @Test + void shouldGetAttachments() { + callController(); + + verify(fileService).getAttachmentsByEingang(EingangTestFactory.ID); + } + + @Test + void shouldCallZipDownloadService() { + var ozgFile = OzgFileTestFactory.create(); + when(fileService.getAttachmentsByEingang(EingangTestFactory.ID)).thenReturn(Stream.of(ozgFile)); + + callController(); + + verify(zipDownloadService).write(List.of(ozgFile), outputStream); + } + + private void callController() { + controller.createZipFile(outputStream, EingangTestFactory.ID); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java index ac60d058d4b2b3a315ac2312cae6375a160275d6..b3a68ed0ad5e3d441a2705cf6181e802882d0e55 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java @@ -79,7 +79,7 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "CREATE_BESCHEID", "DELETE_BESCHEID" }) void shouldFilterCommandWithOrder(CommandOrder order) { - var command = CommandTestFactory.createBuilder().order(order.toString()).build(); + var command = CommandTestFactory.createBuilder().order(order.name()).build(); var commands = processor.filterByOrder(Collections.singletonList(command)); @@ -89,30 +89,19 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.EXCLUDE, names = { "CREATE_BESCHEID", "DELETE_BESCHEID" }) void shouldKeepCommandWithOrder(CommandOrder order) { - var command = CommandTestFactory.createBuilder().order(order.toString()).build(); + var command = CommandTestFactory.createBuilder().order(order.name()).build(); var commands = processor.filterByOrder(Collections.singletonList(command)); assertThat(commands).containsExactly(command); } - @DisplayName("should filter bescheid related create attached item command") - @Test - void shouldFilterCreateAttachedItemCommand() { - var command = CommandTestFactory.createBuilder().order(CommandOrder.CREATE_ATTACHED_ITEM.toString()) - .body(Map.of(VorgangAttachedItem.FIELD_ITEM_NAME, BescheidHistorieProcessor.BESCHEID_ITEM_NAME)) - .build(); - - var commands = processor.filterByOrder(Collections.singletonList(command)); - - assertThat(commands).isEmpty(); - } - - @DisplayName("should filter bescheid related update attached item command") - @Test - void shouldFilterUpdateAttachedItemCommand() { - var command = CommandTestFactory.createBuilder().order(CommandOrder.UPDATE_ATTACHED_ITEM.toString()) - .body(Map.of(VorgangAttachedItem.FIELD_ITEM_NAME, BescheidHistorieProcessor.BESCHEID_ITEM_NAME)) + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "CREATE_ATTACHED_ITEM", "UPDATE_ATTACHED_ITEM", "PATCH_ATTACHED_ITEM" }) + @DisplayName("should filter bescheid related command") + void shouldFilterAttachedItemCommand(CommandOrder order) { + var command = CommandTestFactory.createBuilder().order(order.name()) + .body(Collections.singletonMap(VorgangAttachedItem.FIELD_ITEM_NAME, BescheidHistorieProcessor.BESCHEID_ITEM_NAME)) .build(); var commands = processor.filterByOrder(Collections.singletonList(command)); @@ -156,17 +145,17 @@ class BescheidHistorieProcessorTest { private final Command sendBescheidCommand = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(BescheidTestFactory.ID) - .order(CommandOrder.SEND_BESCHEID.toString()) + .order(CommandOrder.SEND_BESCHEID.name()) .build(); private final Command updateBescheidCommand = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(BescheidTestFactory.ID) - .order(CommandOrder.UPDATE_BESCHEID.toString()) + .order(CommandOrder.UPDATE_BESCHEID.name()) .build(); private final Command updateBescheidCommandOfBescheidNotSent = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(UUID.randomUUID().toString()) - .order(CommandOrder.UPDATE_BESCHEID.toString()) + .order(CommandOrder.UPDATE_BESCHEID.name()) .build(); private final List<Command> commands = List.of(sendBescheidCommand, updateBescheidCommand, updateBescheidCommandOfBescheidNotSent); @@ -197,7 +186,7 @@ class BescheidHistorieProcessorTest { private final Command sendBescheidCommand = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(BescheidTestFactory.ID) - .order(CommandOrder.SEND_BESCHEID.toString()).build(); + .order(CommandOrder.SEND_BESCHEID.name()).build(); private final Command relatedUpdateBescheidCommand = CommandTestFactory.createBuilder().id(UUID.randomUUID().toString()) .createdAt(ZonedDateTime.now()).build(); @@ -293,7 +282,6 @@ class BescheidHistorieProcessorTest { private Map<String, Object> createBescheidMap(SendBy sendBy) { var item = BescheidTestFactory.createAsMap(); - item.remove(Bescheid.SEND_BY_FIELD); item.put(Bescheid.SEND_BY_FIELD, sendBy); return item; } @@ -317,7 +305,7 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "UPDATE_BESCHEID" }) void shouldKeepUpdateBescheidCommandsOnly(CommandOrder order) { - var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.toString()).build(); + var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.name()).build(); var relatedCommands = processor.getRelatedUpdateBescheidCommands(command, Collections.singletonList(commandWithNonMatchingOrder)); @@ -327,7 +315,7 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.EXCLUDE, names = { "UPDATE_BESCHEID" }) void shouldFilterNonUpdateBescheidCommands(CommandOrder order) { - var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.toString()).build(); + var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.name()).build(); var relatedCommands = processor.getRelatedUpdateBescheidCommands(command, Collections.singletonList(commandWithNonMatchingOrder)); @@ -381,7 +369,8 @@ class BescheidHistorieProcessorTest { @Test void shouldThrowOnMissingSendBy() { - assertThatThrownBy(() -> processor.isManualSent(Map.of())).isInstanceOf(NullPointerException.class); + var bescheidItemMap = Collections.<String, Object>emptyMap(); + assertThatThrownBy(() -> processor.isManualSent(bescheidItemMap)).isInstanceOf(NullPointerException.class); } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java index ec24740d58cd87f75c86a04801232b5076fe03f3..fc714d41d1e45acb791372084e94c4c0b0bddab8 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidVorgangProcessorTest.java @@ -20,7 +20,6 @@ import org.mockito.Spy; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; -import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.UserProfileUrlProvider; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserRole; @@ -43,9 +42,6 @@ class BescheidVorgangProcessorTest { @Mock private CurrentUserService currentUserService; - @Mock - private FeatureToggleProperties featureToggleProperties; - @Nested class TestProcess { @@ -215,7 +211,6 @@ class BescheidVorgangProcessorTest { @Test void shouldReturnTrue() { - givenFeatureToggleEnabled(true); givenVorgangInBearbeitung(true); givenDraftExists(true); @@ -224,18 +219,8 @@ class BescheidVorgangProcessorTest { assertThat(booleanSupplier.getAsBoolean()).isTrue(); } - @Test - void shouldReturnFalseIfFeatureToggleIsDisabled() { - givenFeatureToggleEnabled(false); - - var booleanSupplier = callMethod(); - - assertThat(booleanSupplier.getAsBoolean()).isFalse(); - } - @Test void shouldReturnFalseIfVorgangNotInBearbeitung() { - givenFeatureToggleEnabled(true); givenVorgangInBearbeitung(false); var booleanSupplier = callMethod(); @@ -245,7 +230,6 @@ class BescheidVorgangProcessorTest { @Test void shouldReturnFalseIfDraftDoesNotExist() { - givenFeatureToggleEnabled(true); givenVorgangInBearbeitung(true); givenDraftExists(false); @@ -264,7 +248,6 @@ class BescheidVorgangProcessorTest { @Test void shouldReturnTrue() { - givenFeatureToggleEnabled(true); givenVorgangInBearbeitung(true); givenDraftExists(false); @@ -273,18 +256,8 @@ class BescheidVorgangProcessorTest { assertThat(booleanSupplier.getAsBoolean()).isTrue(); } - @Test - void shouldReturnFalseIfFeatureToggleIsDisabled() { - givenFeatureToggleEnabled(false); - - var booleanSupplier = callMethod(); - - assertThat(booleanSupplier.getAsBoolean()).isFalse(); - } - @Test void shouldReturnFalseIfVorgangNotInBearbeitung() { - givenFeatureToggleEnabled(true); givenVorgangInBearbeitung(false); var booleanSupplier = callMethod(); @@ -294,7 +267,6 @@ class BescheidVorgangProcessorTest { @Test void shouldReturnFalseIfDraftExists() { - givenFeatureToggleEnabled(true); givenVorgangInBearbeitung(true); givenDraftExists(true); @@ -360,10 +332,6 @@ class BescheidVorgangProcessorTest { } } - private void givenFeatureToggleEnabled(boolean enabled) { - when(featureToggleProperties.isBescheidWizard()).thenReturn(enabled); - } - private void givenVorgangInBearbeitung(boolean inBearbeitung) { doReturn(inBearbeitung).when(processor).isVorgangInBearbeitung(vorgang); } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..943488ca0faf1f231ab479314884ed9c522d4de4 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadServiceTest.java @@ -0,0 +1,262 @@ +package de.ozgcloud.alfa.common.zipdownload; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.Spy; + +import de.ozgcloud.alfa.common.binaryfile.BinaryFileService; +import de.ozgcloud.alfa.common.file.OzgFile; +import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.SneakyThrows; + +public class ZipDownloadServiceTest { + + @Spy + @InjectMocks + private ZipDownloadService service; + + @Mock + private BinaryFileService binaryFileService; + + @Nested + class TestWrite { + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + private final List<OzgFile> ozgFiles = List.of(OzgFileTestFactory.create()); + @Mock + private File zipFile; + + @Test + void shouldCreateZipFile() { + callService(); + + verify(service).createZipFile(ozgFiles); + } + + @Test + void shouldWriteZipFileContent() { + doReturn(zipFile).when(service).createZipFile(ozgFiles); + doNothing().when(service).writeZipFileContent(zipFile, outputStream); + + callService(); + + verify(service).writeZipFileContent(zipFile, outputStream); + } + + private void callService() { + service.write(ozgFiles, outputStream); + } + } + + @Nested + class TestCreateZipFile { + private final OzgFile file1 = OzgFileTestFactory.createWithUniqueId(); + private final OzgFile file2 = OzgFileTestFactory.createWithUniqueId(); + private final List<OzgFile> ozgFiles = List.of(file1, file2); + + private MockedConstruction<FileOutputStream> mockFileOutputStream; + private MockedConstruction<ZipOutputStream> mockZipOutputStream; + private File file; + private FileOutputStream fileOutputStream; + + @BeforeEach + void setUpMocks() { + mockFileOutputStream = mockConstruction(FileOutputStream.class, + (mock, context) -> { + file = (File) context.arguments().get(0); + }); + mockZipOutputStream = mockConstruction(ZipOutputStream.class, + (mock, context) -> { + fileOutputStream = (FileOutputStream) context.arguments().get(0); + }); + } + + @AfterEach + void cleanUp() { + mockFileOutputStream.close(); + mockZipOutputStream.close(); + } + + @Test + void shouldCreateZipOutputStream() { + callService(); + + assertThat(mockZipOutputStream.constructed()).hasSize(1); + assertThat(fileOutputStream).isEqualTo(mockFileOutputStream.constructed().get(0)); + } + + @Test + void shouldAddOzgFilesToZip() { + callService(); + + verify(service).addOzgFileToZip(file1, mockZipOutputStream.constructed().get(0)); + verify(service).addOzgFileToZip(file2, mockZipOutputStream.constructed().get(0)); + } + + @Test + void shouldReturnFile() { + var resultFile = callService(); + + assertThat(resultFile).isEqualTo(file); + } + + @Test + void shouldThrowTechnicalException() { + doThrow(TechnicalException.class).when(service).addOzgFileToZip(any(), any()); + + assertThrows(TechnicalException.class, this::callService); + } + + private File callService() { + return service.createZipFile(ozgFiles); + } + } + + @Nested + class TestAddOzgFileToZip { + private final OzgFile ozgFile = OzgFileTestFactory.create(); + + @Mock + private ZipOutputStream zipOutputStream; + + private MockedConstruction<ZipEntry> mockZipEntry; + private String entryName; + + @BeforeEach + void setUpMocks() { + mockZipEntry = mockConstruction(ZipEntry.class, + (mock, context) -> { + entryName = (String) context.arguments().get(0); + }); + } + + @AfterEach + void cleanUp() { + mockZipEntry.close(); + } + + @Test + void shouldCreateZipEntry() { + callService(); + + assertThat(mockZipEntry.constructed()).hasSize(1); + } + + @Test + void shouldSetZipEntryName() { + callService(); + + assertThat(entryName).isEqualTo(ozgFile.getName()); + } + + @Test + @SneakyThrows + void shouldPutNextEntry() { + callService(); + + verify(zipOutputStream).putNextEntry(mockZipEntry.constructed().get(0)); + } + + @Test + @SneakyThrows + void shouldWriteContent() { + callService(); + + verify(binaryFileService).writeFileContent(ozgFile.getId(), zipOutputStream); + } + + @Test + @SneakyThrows + void shouldCloseEntry() { + callService(); + + verify(zipOutputStream).closeEntry(); + } + + @Test + @SneakyThrows + void shouldThrowTechnicalException() { + doThrow(IOException.class).when(zipOutputStream).putNextEntry(any()); + + assertThrows(TechnicalException.class, this::callService); + } + + private void callService() { + service.addOzgFileToZip(ozgFile, zipOutputStream); + } + } + + @Nested + class TestWriteZipFileContent { + private MockedConstruction<FileInputStream> mockFileInputStream; + + private File argumentFile; + @Mock + private File file; + + @Mock + private FileOutputStream fileOutputStream; + + @AfterEach + void cleanUp() { + mockFileInputStream.close(); + } + + @Test + void shouldCreateFileInputStreamWithFileArgument() { + mockFileInputStream = mockConstruction(FileInputStream.class, + (mock, context) -> { + argumentFile = (File) context.arguments().get(0); + }); + + callService(); + + assertThat(mockFileInputStream.constructed()).hasSize(1); + assertThat(argumentFile).isEqualTo(file); + } + + @Test + @SneakyThrows + void shouldTransferToOutputStream() { + mockFileInputStream = mockConstruction(FileInputStream.class); + + callService(); + + verify(mockFileInputStream.constructed().get(0)).transferTo(fileOutputStream); + } + + @Test + void shouldThrowTechnicalException() { + mockFileInputStream = mockConstruction(FileInputStream.class, + (mock, context) -> { + when(mock.transferTo(fileOutputStream)).thenThrow(IOException.class); + }); + + assertThrows(TechnicalException.class, this::callService); + } + + private void callService() { + service.writeZipFileContent(file, fileOutputStream); + } + + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java index 689c5630291732fc004ab4529f3a97ca83d8b3b1..baa2ecbbff4cb2b1ddf4e43ac192d10109088069 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java @@ -36,6 +36,7 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -43,11 +44,11 @@ import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import lombok.SneakyThrows; class RepresentationControllerTest { - private final String PATH = RepresentationController.REPRESENTATIONS_PATH; - + @Spy @InjectMocks private RepresentationController controller; @Mock @@ -70,27 +71,32 @@ class RepresentationControllerTest { when(fileService.getRepresentationsByEingang(anyString())).thenReturn(Stream.of(OzgFileTestFactory.create())); } + @SneakyThrows @Test - void shouldCallEndpoint() throws Exception { + void shouldCallEndpoint() { callEndpoint(); } + @SneakyThrows @Test - void shoudlCallFileService() throws Exception { + void shouldCallFileService() { callEndpoint(); verify(fileService).getRepresentationsByEingang(any()); } + @SneakyThrows @Test - void shouldCallModelAssembler() throws Exception { + void shouldCallModelAssembler() { callEndpoint(); verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<OzgFile>>any()); } - private void callEndpoint() throws Exception { - mockMvc.perform(get(PATH).param(RepresentationController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); + @SneakyThrows + private void callEndpoint() { + mockMvc.perform(get(RepresentationController.PATH).param(RepresentationController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); } } + } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java index 1022eb3a27b1e8dfc262aafc25ab99b4d80883c0..05306f1e29f313ec547dee7e7a8fc5ec1d652614 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java @@ -77,30 +77,59 @@ class VorgangWithEingangProcessorTest { private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + @DisplayName("Attachments") @Nested - class TestAttachmentsLink { - - private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_ATTACHMENTS; - private final String PATH = "/api/attachments?eingangId=" + EingangTestFactory.ID; + class TestAttachments { @BeforeEach void init() { initUserProfileUrlProvider(urlProvider); } - @DisplayName("should be present on numberOfAttachments > 0") - @Test - void shouldBePresentByAttachments() { - var link = processor.process(buildModelWithAttachments(1)).getLink(linkRel); + @DisplayName("link") + @Nested + class TestLink { - assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + private static final String PATH = "/api/attachments?eingangId=" + EingangTestFactory.ID; + private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_ATTACHMENTS; + + @DisplayName("should be present if attachments exists") + @Test + void shouldBePresent() { + var link = processor.process(buildModelWithAttachments(1)).getLink(linkRel); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + } + + @Test + void shouldNotBePresent() { + var link = processor.process(buildModelWithAttachments(0)).getLink(linkRel); + + assertThat(link).isNotPresent(); + } } - @Test - void shouldNotBePresent() { - var link = processor.process(buildModelWithAttachments(0)).getLink(linkRel); + @DisplayName("download link") + @Nested + class TestDownloadLink { - assertThat(link).isNotPresent(); + private static final String PATH = "/api/vorgang/" + VorgangHeaderTestFactory.ID + "/attachments?eingangId=" + EingangTestFactory.ID; + private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_DOWNLOAD_ATTACHMENTS; + + @DisplayName("should be present if attachments exists") + @Test + void shouldBePresent() { + var link = processor.process(buildModelWithAttachments(1)).getLink(linkRel); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + } + + @Test + void shouldNotBePresent() { + var link = processor.process(buildModelWithAttachments(0)).getLink(linkRel); + + assertThat(link).isNotPresent(); + } } private EntityModel<VorgangWithEingang> buildModelWithAttachments(int numberOfAttachments) { @@ -114,15 +143,22 @@ class VorgangWithEingangProcessorTest { } } + @DisplayName("Representations") @Nested class TestRepresentationsLink { + @BeforeEach + void init() { + initUserProfileUrlProvider(urlProvider); + } + + private static final String PATH = "/api/representations?eingangId=" + EingangTestFactory.ID; + private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_REPRESENTATIONS; - private final String PATH = "/api/representations?eingangId=" + EingangTestFactory.ID; - @DisplayName("should be present on numberOfRepresentations > 0") + @DisplayName("should be present if representations exist") @Test - void shouldBePresentByRepresentations() { + void shouldBePresent() { var link = processor.process(buildModelWithRepresentation(1)).getLink(linkRel); assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); @@ -389,7 +425,6 @@ class VorgangWithEingangProcessorTest { private VorgangWithEingang createVorgang(Eingang eingang) { return VorgangWithEingangTestFactory.createBuilder().eingang(eingang).build(); } - } @Nested diff --git a/alfa-xdomea/pom.xml b/alfa-xdomea/pom.xml index ee34595691b9d2da7c53e3e963a12708ace55e4e..6f92cd2bc38e598b1dc2ab97a546ab4c331c7139 100644 --- a/alfa-xdomea/pom.xml +++ b/alfa-xdomea/pom.xml @@ -31,7 +31,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <artifactId>alfa-xdomea</artifactId> diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java index 827cfa4b00d8a52024ae35e562f539e54cb64f61..4f3e62cffd7e5a5e07f7dccc55a2f8ca59e092f7 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java @@ -42,17 +42,22 @@ public class ExportHistorieService { } String createValueBeforeChange(VorgangChange vorgangChange) { - if (vorgangChange.getOrder().equals(CommandOrder.ASSIGN_USER.name())) { - return appendOrganisationseinheitenID(vorgangChange.getValueBeforeChange(), vorgangChange.getOrganisationseinheitenID()); - } - return vorgangChange.getValueBeforeChange(); + return createValueChange(vorgangChange, vorgangChange.getValueBeforeChange()); } String createValueAfterChange(VorgangChange vorgangChange) { - if (vorgangChange.getOrder().equals(CommandOrder.ASSIGN_USER.name())) { - return appendOrganisationseinheitenID(vorgangChange.getValueAfterChange(), vorgangChange.getOrganisationseinheitenID()); + return createValueChange(vorgangChange, vorgangChange.getValueAfterChange()); + } + + private String createValueChange(VorgangChange vorgangChange, String valueChange) { + if (isAssignUserOrder(vorgangChange)) { + return appendOrganisationseinheitenID(valueChange, vorgangChange.getOrganisationseinheitenID()); } - return vorgangChange.getValueAfterChange(); + return valueChange; + } + + private boolean isAssignUserOrder(VorgangChange vorgangChange) { + return vorgangChange.getOrder().equals(CommandOrder.ASSIGN_USER.name()); } String appendOrganisationseinheitenID(String text, String organisationseinheitenID) { diff --git a/pom.xml b/pom.xml index 814bc554e80a557f0b17d1e15e5f5c12df36f530..9f392394780ae740b80a1a4c48cc613ae8061467 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> <name>Alfa Parent</name> <packaging>pom</packaging> diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml index cad88f7ba2e55da7b791497853c8ff633af6aaac..b7221ad6bd6407099a01862c2ffb85bb5199d49d 100644 --- a/src/main/helm/templates/deployment.yaml +++ b/src/main/helm/templates/deployment.yaml @@ -98,7 +98,7 @@ spec: {{- if ((.Values.ozgcloud).vorgang).bescheid}} {{- range $index, $bescheid := ((.Values.ozgcloud).vorgang).bescheid }} - name: ozgcloud_vorgang_bescheid_{{ $index }}_formId - value: {{ $bescheid.formId }} + value: {{ $bescheid.formId | quote }} - name: ozgcloud_vorgang_bescheid_{{ $index }}_formEngineName value: {{ $bescheid.formEngineName }} {{- end }} diff --git a/src/test/helm/deployment_defaults_env_test.yaml b/src/test/helm/deployment_defaults_env_test.yaml index 99d108c95f63fc674e22cf2cc369af4db57c0ae5..0bf3a7adbf32e293791d0af1a2804bf7c8c4d6df 100644 --- a/src/test/helm/deployment_defaults_env_test.yaml +++ b/src/test/helm/deployment_defaults_env_test.yaml @@ -92,7 +92,7 @@ tests: - formEngineName: AFM formId: form_id_1 - formEngineName: FormSolutions - formId: form_id_2 + formId: 2290 asserts: - contains: path: spec.template.spec.containers[0].env @@ -108,7 +108,7 @@ tests: path: spec.template.spec.containers[0].env content: name: ozgcloud_vorgang_bescheid_1_formId - value: form_id_2 + value: "2290" - contains: path: spec.template.spec.containers[0].env content: diff --git a/src/test/helm/network_policy_test.yaml b/src/test/helm/network_policy_test.yaml index c74aa40bad0792d6da348b499eae6a22315ea6fa..af65c804e62b7fb9aebb26f65f2322b536f71550 100644 --- a/src/test/helm/network_policy_test.yaml +++ b/src/test/helm/network_policy_test.yaml @@ -28,21 +28,30 @@ release: namespace: by-helm-test templates: - templates/network_policy.yaml -set: - networkPolicy: - dnsServerNamespace: kube-system - ssoPublicIp: 1.1.1.1/32 + tests: - it: should match apiVersion + set: + networkPolicy: + dnsServerNamespace: kube-system + ssoPublicIp: 1.1.1.1/32 asserts: - isAPIVersion: of: networking.k8s.io/v1 - it: should match kind + set: + networkPolicy: + dnsServerNamespace: kube-system + ssoPublicIp: 1.1.1.1/32 asserts: - isKind: of: NetworkPolicy - it: validate metadata + set: + networkPolicy: + dnsServerNamespace: kube-system + ssoPublicIp: 1.1.1.1/32 asserts: - equal: path: metadata @@ -187,3 +196,31 @@ tests: asserts: - hasDocuments: count: 1 + + - it: test network policy dnsServerNamespace must be set message + set: + networkPolicy: + disabled: false + ssoPublicIp: 1.1.1.1/32 + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + + - it: test network policy ssoPublicIp must be set message + set: + networkPolicy: + disabled: false + dnsServerNamespace: test-dns-server-namespace + asserts: + - failedTemplate: + errorMessage: networkPolicy.ssoPublicIp must be set + + - it: test network policy should be enabled by default + set: + networkPolicy: + ssoPublicIp: 1.1.1.1 + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 \ No newline at end of file