From b9f8ba6b905cdf8d9d847bd231dba30e9d6ac29d Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Wed, 26 Mar 2025 18:45:56 +0100 Subject: [PATCH 1/8] OZG-7872 use state vorgang directly on change listener (avoiding useless reloading) --- .../libs/kommentar-shared/src/lib/kommentar.service.spec.ts | 2 +- alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts | 2 +- .../libs/postfach-shared/src/lib/postfach.service.spec.ts | 2 +- alfa-client/libs/postfach-shared/src/lib/postfach.service.ts | 2 +- .../wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts | 2 +- .../libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts index 5860d36428..bd09cc77d8 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts @@ -461,7 +461,7 @@ describe('KommentarService', () => { it('should call vorgang service to get vorgang with eingang', () => { service._listenToVorgangChange(); - expect(vorgangService.getVorgangWithEingang).toHaveBeenCalled(); + expect(vorgangService.selectVorgangWithEingang).toHaveBeenCalled(); }); it('should call handle vorgang change', () => { diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts index 395872897b..876f32e5af 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts @@ -107,7 +107,7 @@ export class KommentarService { _listenToVorgangChange(): void { this.vorgangSubscription = this.vorgangService - .getVorgangWithEingang() + .selectVorgangWithEingang() .subscribe((vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource>) => this._handleVorgangChange(vorgangWithEingangStateResource), ); diff --git a/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts b/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts index 9149471c8d..876e08a03d 100644 --- a/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts +++ b/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts @@ -582,7 +582,7 @@ describe('PostfachService', () => { it('should call vorgang service to get vorgang with eingang', () => { service._listenToVorgangChange(); - expect(vorgangService.getVorgangWithEingang).toHaveBeenCalled(); + expect(vorgangService.selectVorgangWithEingang).toHaveBeenCalled(); }); it('should call handle vorgang change', () => { diff --git a/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts b/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts index f650edd3f4..b7fbfbe62d 100644 --- a/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts +++ b/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts @@ -292,7 +292,7 @@ export class PostfachService { _listenToVorgangChange(): void { this.vorgangChangeSubscription = this.vorgangService - .getVorgangWithEingang() + .selectVorgangWithEingang() .subscribe((vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource>) => this._handleVorgangChange(vorgangWithEingangStateResource), ); diff --git a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts index 13e5fcd714..f2513adc3b 100644 --- a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts +++ b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts @@ -583,7 +583,7 @@ describe('WiedervorlageService', () => { it('should call vorgang service to get vorgang with eingang', () => { service._listenToVorgangChange(); - expect(vorgangService.getVorgangWithEingang).toHaveBeenCalled(); + expect(vorgangService.selectVorgangWithEingang).toHaveBeenCalled(); }); it('should call handle vorgang change', () => { diff --git a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts index 6590ff6738..ad5e3f9ea1 100644 --- a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts +++ b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts @@ -122,7 +122,7 @@ export class WiedervorlageService implements OnDestroy { _listenToVorgangChange(): void { this.vorgangSubscription = this.vorgangService - .getVorgangWithEingang() + .selectVorgangWithEingang() .subscribe((vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource>) => this._handleVorgangChange(vorgangWithEingangStateResource), ); -- GitLab From 80320525d5d8a10901e28939e9c970db8dcd0056 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Mon, 31 Mar 2025 23:51:41 +0200 Subject: [PATCH 2/8] OZG-7872 OZG-8041 add e2e test for pending send postfach command --- .../postfach/postfach-mail.e2e.component.ts | 107 +++++++++--------- .../vorgang-detail/vorgang-forwarding.cy.ts | 2 +- .../postfach-mail/postfach-mail-error.cy.ts | 77 ++++++------- .../postfach-mail/postfach-mail.cy.ts | 23 ++-- ...ail.filtered-by-organisationseinheit.cy.ts | 11 +- ...postfach-nachricht-authorize-by-role.cy.ts | 25 +--- .../postfach-nachricht-pending-send.cy.ts | 67 +++++++++++ .../postfach-nachricht-reply-button.cy.ts | 9 +- .../postfach-mail/postfach-nachrichten.cy.ts | 11 +- .../vorgang-forwarding.cy.ts | 2 +- .../src/fixtures/command/command.json | 7 +- .../postfach/send-postfach-nachricht.json | 23 ++++ .../usermanager/usermanager_user_ludwig.json | 18 +++ .../usermanager/usermanager_user_zonk.json | 18 +++ .../helper/forwarding/forwarding.navigator.ts | 2 +- .../postfach-nachricht.navigator.ts | 9 ++ .../postfach-nachricht.verifier.ts | 27 +++++ .../src/helper/vorgang/vorgang.navigator.ts | 10 +- .../apps/alfa-e2e/src/model/command.ts | 11 +- .../apps/alfa-e2e/src/page-objects/main.po.ts | 18 ++- .../postfach-mail.component.po.ts | 6 +- .../alfa-e2e/src/support/cypress-helper.ts | 4 + .../alfa-e2e/src/support/cypress-tasks.ts | 25 ++-- alfa-client/apps/alfa-e2e/src/support/e2e.ts | 7 ++ .../src/support/postfach-nachricht.util.ts | 6 + .../postfach-mail-button.component.html | 1 - .../incomming-mail.component.html | 8 +- .../incomming-mail.component.spec.ts | 4 +- ...tgoing-mail-error-container.component.html | 2 +- ...ing-mail-error-container.component.spec.ts | 57 ++++++---- ...outgoing-mail-error-container.component.ts | 25 ++-- .../outgoing-mail-error.component.html | 14 +-- .../outgoing-mail.component.html | 10 +- .../outgoing-mail.component.spec.ts | 8 +- .../postfach-mail-date.component.html | 4 +- .../postfach-mail-date.component.spec.ts | 5 +- ...stroked-button-with-spinner.component.html | 1 + ...d-stroked-button-with-spinner.component.ts | 1 + 38 files changed, 410 insertions(+), 255 deletions(-) create mode 100644 alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts create mode 100644 alfa-client/apps/alfa-e2e/src/fixtures/postfach/send-postfach-nachricht.json create mode 100644 alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_ludwig.json create mode 100644 alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_zonk.json create mode 100644 alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.navigator.ts create mode 100644 alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.verifier.ts diff --git a/alfa-client/apps/alfa-e2e/src/components/postfach/postfach-mail.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/postfach/postfach-mail.e2e.component.ts index 1bb7839d10..30a485fc0e 100644 --- a/alfa-client/apps/alfa-e2e/src/components/postfach/postfach-mail.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/postfach/postfach-mail.e2e.component.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { getTestElement } from '../../support/cypress-helper'; import { convertToDataTestId } from '../../support/tech.util'; import { AttachmentContainerE2EComponent } from '../attachment/attachment.e2e.component'; import { UserProfileE2EComponent } from '../user-profile/user-profile.component.e2e'; @@ -36,103 +37,105 @@ export class PostfachMailE2EComponent { private readonly locatorRoot: string = 'postfach-nachrichten-container-in-vorgang'; - public getRoot() { - return cy.getTestElement(this.locatorRoot); + public getRoot(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.locatorRoot); } - public getCreateButtonWithText() { - return cy.getTestElement(this.createMailButtonWithText); + public getCreateButtonWithText(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.createMailButtonWithText); } - public getCreateButtonWithoutText() { - return cy.getTestElement(this.createMailButtonWithoutText); + public getCreateButtonWithoutText(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.createMailButtonWithoutText); } - public getList() { - return cy.getTestElement(this.list); + public getList(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.list); } - public getNoPostfachText() { - return cy.getTestElement(this.noPostfachText); + public getNoPostfachText(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.noPostfachText); } - public getListItem(subject: string): PostfachMailListItem { - return new PostfachMailListItem(subject); + public getListItem(subject: string): PostfachMailListItemE2EComponent { + return new PostfachMailListItemE2EComponent(subject); } - public getAttachments() { - return cy.getTestElement(this.attachments); + public getAttachments(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.attachments); } - public getDownloadButtonWithIcon() { - return cy.getTestElement(this.downloadButtonWithIcon); + public getDownloadButtonWithIcon(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.downloadButtonWithIcon); } - public getDownloadButtonWithLabel() { - return cy.getTestElement(this.downloadButtonWithLabel); + public getDownloadButtonWithLabel(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.downloadButtonWithLabel); } } -export class PostfachMailListItem { - //TODO: Rename -> PostfachMailListItemE2EComponent +export class PostfachMailListItemE2EComponent { + private readonly createdAt: string = 'mail-created-at'; + private readonly subject: string = 'mail-subject'; + private readonly text: string = 'mail-text'; + private readonly replyIcon: string = 'reply-icon'; + private readonly sentAt: string = 'mail-sent-at'; + private readonly editButton: string = 'postfach-nachricht-edit-button-container'; - private readonly locatorCreatedAt: string = 'mail-created-at'; - private readonly locatorSubject: string = 'mail-subject'; - private readonly locatorText: string = 'mail-text'; - private readonly locatorReplyIcon: string = 'reply-icon'; - private readonly locatorSentAt: string = 'mail-sent-at'; + private readonly sendErrorText: string = 'mail-send-error-text'; + private readonly sendErrorIcon: string = 'mail-send-error-icon'; + private readonly resendButton: string = 'resend-nachricht-button'; - private readonly locatorMailSendErrorText: string = 'mail-send-error-text'; - private readonly locatorMailSendErrorIcon: string = 'mail-send-error-icon'; - private readonly locatorMailResendButton: string = 'mail-resend-button'; + private readonly attachmentContainer: AttachmentContainerE2EComponent = new AttachmentContainerE2EComponent(); - private readonly attachmentContainer: AttachmentContainerE2EComponent = - new AttachmentContainerE2EComponent(); - - private locatorRoot: string; + private root: string; constructor(subject: string) { - this.locatorRoot = convertToDataTestId(subject) + '-item'; + this.root = convertToDataTestId(subject) + '-item'; + } + + public getRoot(): Cypress.Chainable<JQuery<Element>> { + return getTestElement(this.root); } - public getRoot() { - return cy.getTestElement(this.locatorRoot); + public getEditButton(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.editButton); } - public getSubject() { - return this.getRoot().getTestElement(this.locatorSubject); + public getSubject(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.subject); } - public getText() { - return this.getRoot().getTestElement(this.locatorText); + public getText(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.text); } - public getCreatedAt() { - return this.getRoot().getTestElement(this.locatorCreatedAt); + public getCreatedAt(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.createdAt); } public getUserProfile(): UserProfileE2EComponent { - return new UserProfileE2EComponent(this.locatorRoot); + return new UserProfileE2EComponent(this.root); } - public getReplyIcon() { - return this.getRoot().getTestElement(this.locatorReplyIcon); + public getReplyIcon(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.replyIcon); } - public getSentAt() { - return this.getRoot().getTestElement(this.locatorSentAt); + public getSentAt(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.sentAt); } - public getMailSendErrorText() { - return this.getRoot().getTestElement(this.locatorMailSendErrorText); + public getSendErrorText(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.sendErrorText); } - public getMailSendErrorIcon() { - return this.getRoot().getTestElement(this.locatorMailSendErrorIcon); + public getSendErrorIcon(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.sendErrorIcon); } - public getResendButton() { - return this.getRoot().getTestElement(this.locatorMailResendButton); + public getResendButton(): Cypress.Chainable<JQuery<Element>> { + return this.getRoot().findTestElementWithClass(this.resendButton); } public getAttachmentContainer(): AttachmentContainerE2EComponent { diff --git a/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-forwarding.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-forwarding.cy.ts index 82dd57a8ae..0a80fc4dc8 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-forwarding.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/einheitlicher-ansprechpartner/vorgang-detail/vorgang-forwarding.cy.ts @@ -31,7 +31,7 @@ describe('Vorgang weiterleiten innerhalb der OzgCloud', () => { }); it('should open vorgang', () => { - vorgangNavigator.openVorgangDetailByName(vorgangWeiterleiten.name); + vorgangNavigator.openVorgang(vorgangWeiterleiten.name); }); it('should display Weiterleiten button in Status Neu', () => { diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts index 14814f7d70..7248a1727b 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts @@ -21,35 +21,40 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { E2EAppHelper } from 'apps/alfa-e2e/src/helper/app.helper'; +import { E2EPostfachNachrichtVerifier } from 'apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.verifier'; +import { E2EVorgangNavigator } from 'apps/alfa-e2e/src/helper/vorgang/vorgang.navigator'; import { createPostfachNachrichtAttachedItem, createPostfachNachrichtReplyItem, } from 'apps/alfa-e2e/src/support/postfach-nachricht.util'; -import { PostfachMailE2EComponent, PostfachMailListItem } from '../../../components/postfach/postfach-mail.e2e.component'; +import { + PostfachMailE2EComponent, + PostfachMailListItemE2EComponent, +} from '../../../components/postfach/postfach-mail.e2e.component'; import { SnackBarE2EComponent } from '../../../components/ui/snackbar.e2e.component'; -import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { VorgangSubnavigationE2EComponent } from '../../../components/vorgang/vorgang-subnavigation'; import { VorgangE2E } from '../../../model/vorgang'; import { DirectionE2E, PostfachMailItemE2E, PostfachNachrichtMessageCodeE2E, - PostfachNachrichtMessageCodeMessagesE2E, PostfachNachrichtSnackbarMessageE2E, VorgangAttachedItemE2E, } from '../../../model/vorgang-attached-item'; -import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; +import { containsSpinner, MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { PostfachMailPage } from '../../../page-objects/postfach-mail.component.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { dropCollections } from '../../../support/cypress-helper'; import { contains, exist, notExist } from '../../../support/cypress.util'; -import { getUserSabineId, loginAsSabine } from '../../../support/user-util'; +import { getUserSabineId } from '../../../support/user-util'; import { initVorgangAttachedItem } from '../../../support/vorgang-attached-item-util'; import { createVorgang, initVorgang, objectIds } from '../../../support/vorgang-util'; describe('PostfachMail error', () => { + const appHelper: E2EAppHelper = new E2EAppHelper(); + const vorgangNavigator: E2EVorgangNavigator = new E2EVorgangNavigator(); + const mainPage: MainPage = new MainPage(); - const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); const snackbar: SnackBarE2EComponent = mainPage.getSnackBar(); const vorgangPage: VorgangPage = new VorgangPage(); @@ -58,9 +63,12 @@ describe('PostfachMail error', () => { const postfachMailPage: PostfachMailPage = new PostfachMailPage(); + const verifier: E2EPostfachNachrichtVerifier = new E2EPostfachNachrichtVerifier(); + const vorgang: VorgangE2E = createVorgang(); const postfachMailItem: PostfachMailItemE2E = { ...createPostfachNachrichtReplyItem(), + subject: 'Failed Outgoing Postfach Nachricht 1', createdBy: getUserSabineId(), direction: DirectionE2E.OUT, sentAt: '2022-12-02T15:00:00.790Z[UTC]', @@ -73,26 +81,26 @@ describe('PostfachMail error', () => { item: postfachMailItem, }; + const postfachNachricht2: PostfachMailItemE2E = { + ...postfachMailItem, + subject: 'Failed Outgoing Postfach Nachricht 2', + }; + const vorgangAttachedItem2: VorgangAttachedItemE2E = { + ...createPostfachNachrichtAttachedItem(objectIds[1], vorgang._id.$oid), + item: postfachNachricht2, + }; + before(() => { initVorgang(vorgang); - initVorgangAttachedItem([vorgangAttachedItem]); + initVorgangAttachedItem([vorgangAttachedItem, vorgangAttachedItem2]); - loginAsSabine(); - - waitForSpinnerToDisappear(); - exist(vorgangList.getRoot()); - }); - - after(() => { - dropCollections(); + appHelper.loginAsSabine(); }); describe('navigate to vorgang detail', () => { it('should open vorgang detail', () => { - vorgangList.getListItem(vorgang.name).getRoot().click(); + vorgangNavigator.openVorgang(vorgang.name); waitForSpinnerToDisappear(); - - exist(vorgangPage.getVorgangDetailHeader().getRoot()); }); it('should show postfach mail button', () => { @@ -109,44 +117,23 @@ describe('PostfachMail error', () => { }); describe('postfach mail list', () => { - let listItem: PostfachMailListItem; + let listItem: PostfachMailListItemE2EComponent; beforeEach(() => { listItem = postfachMailPage.getListItem(postfachMailItem.subject); }); - describe('contains failed sent mail item', () => { - it('should show mail subject', () => { - contains(listItem.getSubject(), postfachMailItem.subject); - }); - - it('should show user profile', () => { - exist(listItem.getUserProfile().getRoot()); - }); - - it('should show sent date', () => { - exist(listItem.getSentAt()); - }); - - it('should show text', () => { - contains(listItem.getMailSendErrorText(), PostfachNachrichtMessageCodeMessagesE2E.PROCESSING_FAILED); - }); - - it('should show error message', () => { - exist(listItem.getMailSendErrorIcon()); - }); - - it('should show button', () => { - exist(listItem.getResendButton()); - }); + it('should contains failed postfach nachricht item', () => { + verifier.verifyFailedItemInList(postfachMailItem.subject); }); describe('resend mail', () => { it('click on resend button should hide error', () => { listItem.getResendButton().click(); + containsSpinner(listItem.getResendButton()); waitForSpinnerToDisappear(); - notExist(listItem.getMailSendErrorIcon()); + notExist(listItem.getSendErrorIcon()); }); describe('resend mail item', () => { diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.cy.ts index f818bb1783..6a33ba8948 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.cy.ts @@ -34,7 +34,10 @@ import { createPostfachNachrichtReplyItem, } from 'apps/alfa-e2e/src/support/postfach-nachricht.util'; import { PostfachMailFormularE2EComponent } from '../../../components/postfach/postfach-mail-formular.e2e.component'; -import { PostfachMailE2EComponent, PostfachMailListItem } from '../../../components/postfach/postfach-mail.e2e.component'; +import { + PostfachMailE2EComponent, + PostfachMailListItemE2EComponent, +} from '../../../components/postfach/postfach-mail.e2e.component'; import { FixedDialogE2EComponent } from '../../../components/ui/fixed-dialog.e2e.component'; import { SnackBarE2EComponent } from '../../../components/ui/snackbar.e2e.component'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; @@ -50,7 +53,7 @@ import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main. import { PostfachMailPage } from '../../../page-objects/postfach-mail.component.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; import { expectIconWithBadge, expectIconWithoutBadge } from '../../../support/angular.util'; -import { dropCollections, readFileFromDownloads } from '../../../support/cypress-helper'; +import { readFileFromDownloads } from '../../../support/cypress-helper'; import { beChecked, contains, exist, notBeChecked, notBeVisible, notExist, visible } from '../../../support/cypress.util'; import { TEST_FILE_WITH_CONTENT, TEST_FILE_WITH_CONTENT_4_MB, TEST_FILE_WITHOUT_CONTENT } from '../../../support/data.util'; import { initUsermanagerUsers, loginAsSabine } from '../../../support/user-util'; @@ -122,10 +125,6 @@ describe('PostfachMail', () => { exist(vorgangList.getRoot()); }); - after(() => { - dropCollections(); - }); - describe('mail icon', () => { it('should not be visible', () => { notExist(vorgangList.getListItem(vorgang.name).getPostfachIcon()); @@ -328,7 +327,7 @@ describe('PostfachMail', () => { }); it('should show postfach mail in list', () => { - const postfachMailItem: PostfachMailListItem = postfachMailContainer.getListItem(postfachMailToSend.subject); + const postfachMailItem: PostfachMailListItemE2EComponent = postfachMailContainer.getListItem(postfachMailToSend.subject); exist(postfachMailItem.getRoot()); exist(postfachMailItem.getUserProfile().getRoot()); @@ -340,7 +339,7 @@ describe('PostfachMail', () => { describe('click on postfach mail item with attachment', () => { it('should show postfach item list', () => { - const postfachMailItem: PostfachMailListItem = postfachMailContainer.getListItem(postfachMailToSend.subject); + const postfachMailItem: PostfachMailListItemE2EComponent = postfachMailContainer.getListItem(postfachMailToSend.subject); postfachMailItem.getRoot().click(); waitForSpinnerToDisappear(); @@ -348,13 +347,13 @@ describe('PostfachMail', () => { }); it('should contain mail item with attachment', () => { - const postfachListItem: PostfachMailListItem = postfachMailPage.getListItem(postfachMailToSend.subject); + const postfachListItem: PostfachMailListItemE2EComponent = postfachMailPage.getListItem(postfachMailToSend.subject); exist(postfachListItem.getAttachmentContainer().getList().getItem(TEST_FILE_WITH_CONTENT).getRoot()); }); it('should download attachment after click', () => { - const postfachListItem: PostfachMailListItem = postfachMailPage.getListItem(postfachMailToSend.subject); + const postfachListItem: PostfachMailListItemE2EComponent = postfachMailPage.getListItem(postfachMailToSend.subject); postfachListItem.getAttachmentContainer().getList().getItem(TEST_FILE_WITH_CONTENT).getDownloadButton().click(); waitForSpinnerToDisappear(); @@ -363,7 +362,7 @@ describe('PostfachMail', () => { }); it('should not contain failed upload', () => { - const postfachListItem: PostfachMailListItem = postfachMailPage.getListItem(postfachMailToSend.subject); + const postfachListItem: PostfachMailListItemE2EComponent = postfachMailPage.getListItem(postfachMailToSend.subject); notExist( postfachListItem.getAttachmentContainer().getList().getLoadingOrErrorItem(TEST_FILE_WITH_CONTENT_4_MB).getRoot(), @@ -461,7 +460,7 @@ describe('PostfachMail', () => { }); describe('click on postfach mail item', () => { - let postfachMailReplyItem: PostfachMailListItem; + let postfachMailReplyItem: PostfachMailListItemE2EComponent; beforeEach(() => { postfachMailReplyItem = postfachMailPage.getListItem(postfachMailReply.subject); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.filtered-by-organisationseinheit.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.filtered-by-organisationseinheit.cy.ts index bb94de6b4b..7c15088c5a 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.filtered-by-organisationseinheit.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail.filtered-by-organisationseinheit.cy.ts @@ -27,12 +27,9 @@ import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-lis import { EingangE2E, VorgangE2E } from '../../../model/vorgang'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { PostfachMailPage } from '../../../page-objects/postfach-mail.component.po'; -import { dropCollections, visitUrl } from '../../../support/cypress-helper'; +import { visitUrl } from '../../../support/cypress-helper'; import { contains, exist, notExist } from '../../../support/cypress.util'; -import { - ORGANISATIONSEINHEITEN_ID_FOR_ADELHEIT, - ORGANISATIONSEINHEITEN_ID_FOR_SABINE, -} from '../../../support/data.util'; +import { ORGANISATIONSEINHEITEN_ID_FOR_ADELHEIT, ORGANISATIONSEINHEITEN_ID_FOR_SABINE } from '../../../support/data.util'; import { MessagesE2E } from '../../../support/messages'; import { loginAsAdelheit, loginAsSabine } from '../../../support/user-util'; import { buildVorgang, createVorgang, initVorgaenge } from '../../../support/vorgang-util'; @@ -72,10 +69,6 @@ describe('PostfachNachrichten filtered by organisationseinheit', () => { initVorgaenge([vorgangForSabine, vorgangForAdelheit]); }); - after(() => { - dropCollections(); - }); - describe('on user sabine', () => { const authorizedUrl = vorgangUrlVisibleToSabine; const forbiddenUrl = vorgangUrlVisibleToAdelheit; diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-authorize-by-role.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-authorize-by-role.cy.ts index cb4a23f20e..8fc722691d 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-authorize-by-role.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-authorize-by-role.cy.ts @@ -25,7 +25,7 @@ import { SnackBarE2EComponent } from 'apps/alfa-e2e/src/components/ui/snackbar.e import { VorgangE2E, VorgangStatusE2E } from 'apps/alfa-e2e/src/model/vorgang'; import { MainPage, waitForSpinnerToDisappear } from 'apps/alfa-e2e/src/page-objects/main.po'; import { PostfachMailPage } from 'apps/alfa-e2e/src/page-objects/postfach-mail.component.po'; -import { dropCollections, visitUrl } from 'apps/alfa-e2e/src/support/cypress-helper'; +import { visitUrl } from 'apps/alfa-e2e/src/support/cypress-helper'; import { contains, exist, notExist } from 'apps/alfa-e2e/src/support/cypress.util'; import { MessagesE2E } from 'apps/alfa-e2e/src/support/messages'; import { createPostfachUriByVorgangId } from 'apps/alfa-e2e/src/support/postfach-util'; @@ -47,47 +47,32 @@ describe('Postfach Nachricht should be authorized by role', () => { }; const vorgangInStatusInBearbeitungId = '60250b9d383c182943f6ba79'; - const vorgangInStatusInBearbeitungUrl: string = createPostfachUriByVorgangId( - vorgangInStatusInBearbeitungId, - ); + const vorgangInStatusInBearbeitungUrl: string = createPostfachUriByVorgangId(vorgangInStatusInBearbeitungId); const vorgangInStatusInBearbeitung: VorgangE2E = { ...buildVorgang(vorgangInStatusInBearbeitungId, 'vorgangInStatusInBearbeitung'), status: VorgangStatusE2E.IN_BEARBEITUNG, }; const vorgangInStatusAbgeschlossenId: string = '602566a807bb665df9a86e87'; - const vorgangInStatusAbgeschlossenUrl: string = createPostfachUriByVorgangId( - vorgangInStatusAbgeschlossenId, - ); + const vorgangInStatusAbgeschlossenUrl: string = createPostfachUriByVorgangId(vorgangInStatusAbgeschlossenId); const vorgangInStatusAbgeschlossen: VorgangE2E = { ...buildVorgang(vorgangInStatusAbgeschlossenId, 'vorgangInStatusAbgeschlossen'), status: VorgangStatusE2E.ABGESCHLOSSEN, }; const vorgangInStatusBeschiedenId: string = '602566a207bb665df9a86e86'; - const vorgangInStatusBeschiedenUrl: string = createPostfachUriByVorgangId( - vorgangInStatusBeschiedenId, - ); + const vorgangInStatusBeschiedenUrl: string = createPostfachUriByVorgangId(vorgangInStatusBeschiedenId); const vorgangInStatusBeschieden: VorgangE2E = { ...buildVorgang(vorgangInStatusBeschiedenId, 'vorgangInStatusBeschieden'), status: VorgangStatusE2E.BESCHIEDEN, }; beforeEach(() => { - initVorgaenge([ - vorgangInStatusNeu, - vorgangInStatusInBearbeitung, - vorgangInStatusAbgeschlossen, - vorgangInStatusBeschieden, - ]); + initVorgaenge([vorgangInStatusNeu, vorgangInStatusInBearbeitung, vorgangInStatusAbgeschlossen, vorgangInStatusBeschieden]); loginAsPeter(); }); - after(() => { - dropCollections(); - }); - it(`should show vorgang in Stauts ${VorgangStatusE2E.NEU}`, () => { visitUrl(vorgangInStatusNeuUrl); waitForSpinnerToDisappear(); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts new file mode 100644 index 0000000000..d2c1d45bf3 --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts @@ -0,0 +1,67 @@ +import { PostfachMailE2EComponent } from 'apps/alfa-e2e/src/components/postfach/postfach-mail.e2e.component'; +import { E2EAppHelper } from 'apps/alfa-e2e/src/helper/app.helper'; +import { E2EVorgangNavigator } from 'apps/alfa-e2e/src/helper/vorgang/vorgang.navigator'; +import { CommandE2E, CommandOrderE2E, CommandStatusE2E } from 'apps/alfa-e2e/src/model/command'; +import { createCommand, initCommand } from 'apps/alfa-e2e/src/support/command-util'; +import { + createPostfachNachrichtAttachedItem, + createSendPostfachNachrichtItem, +} from 'apps/alfa-e2e/src/support/postfach-nachricht.util'; +import { initVorgangAttachedItem } from 'apps/alfa-e2e/src/support/vorgang-attached-item-util'; +import { VorgangE2E } from '../../../model/vorgang'; +import { PostfachMailItemE2E, VorgangAttachedItemE2E } from '../../../model/vorgang-attached-item'; +import { containsSpinner } from '../../../page-objects/main.po'; +import { VorgangPage } from '../../../page-objects/vorgang.po'; +import { exist } from '../../../support/cypress.util'; +import { createVorgang, initVorgang, objectIds } from '../../../support/vorgang-util'; + +describe('Postfach Nachricht pending send', () => { + const appHelper: E2EAppHelper = new E2EAppHelper(); + + const vorgangNavigator: E2EVorgangNavigator = new E2EVorgangNavigator(); + + const vorgangPage: VorgangPage = new VorgangPage(); + const postfachMailContainer: PostfachMailE2EComponent = vorgangPage.getPostfachMailcontainer(); + + const vorgangWithPendingSendCommand: VorgangE2E = { ...createVorgang(), name: 'VorgangWithPendingSendCommand' }; + const postfachSendNachrichtItem: PostfachMailItemE2E = { + ...createSendPostfachNachrichtItem(), + sentSuccessful: false, + }; + const postfachNachrichtAttachedItem: VorgangAttachedItemE2E = { + ...createPostfachNachrichtAttachedItem(objectIds[1], vorgangWithPendingSendCommand._id.$oid), + item: postfachSendNachrichtItem, + }; + const pendingSendCommand: CommandE2E = { + ...createCommand(), + order: CommandOrderE2E.SEND_POSTFACH_NACHRICHT, + status: CommandStatusE2E.PENDING, + finishedAt: null, + body: postfachSendNachrichtItem, + }; + + before(() => { + initVorgang(vorgangWithPendingSendCommand); + initVorgangAttachedItem([postfachNachrichtAttachedItem]); + initCommand(pendingSendCommand); + + appHelper.loginAsSabine(); + }); + + describe('area in vorgang detail', () => { + it('should have item in list', () => { + vorgangNavigator.openVorgang(vorgangWithPendingSendCommand.name); + + exist(postfachMailContainer.getListItem(postfachSendNachrichtItem.subject).getRoot()); + }); + + it('should show spinner on buttons', () => { + containsSpinner(postfachMailContainer.getCreateButtonWithText()); + containsSpinner(postfachMailContainer.getCreateButtonWithoutText()); + }); + + it('should show edit button', () => { + exist(postfachMailContainer.getListItem(postfachSendNachrichtItem.subject).getEditButton()); + }); + }); +}); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-reply-button.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-reply-button.cy.ts index ad58b9e3b8..009047f787 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-reply-button.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-reply-button.cy.ts @@ -25,7 +25,6 @@ import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-lis import { VorgangE2E } 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 { exist } from '../../../support/cypress.util'; import { generate12ByteId } from '../../../support/tech.util'; import { loginAsSabine } from '../../../support/user-util'; @@ -48,9 +47,7 @@ describe('Postfach Nachricht reply button', () => { postfachAddress: [ { type: 1, - identifier: new Map([ - ['postfachId', new Object('04d39269-81c5-4838-8b73-08d9567f06d7')], - ]), + identifier: new Map([['postfachId', new Object('04d39269-81c5-4838-8b73-08d9567f06d7')]]), }, ], }, @@ -66,10 +63,6 @@ describe('Postfach Nachricht reply button', () => { exist(vorgangList.getRoot()); }); - after(() => { - dropCollections(); - }); - describe('navigate to vorgang detail with OSI service konto', () => { it('should open vorgang detail', () => { vorgangList.getListItem(vorgangWithOsiServiceKonto.name).getRoot().click(); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachrichten.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachrichten.cy.ts index d0d6d95237..fa8a20f7a6 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachrichten.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachrichten.cy.ts @@ -35,12 +35,7 @@ import { VorgangE2E } from '../../../model/vorgang'; import { PostfachMailItemE2E, VorgangAttachedItemE2E } from '../../../model/vorgang-attached-item'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { - countDownloadFiles, - deleteDownloadFolder, - dropCollections, - interceptWithResponse, -} from '../../../support/cypress-helper'; +import { countDownloadFiles, deleteDownloadFolder, interceptWithResponse } from '../../../support/cypress-helper'; import { exist, notExist } from '../../../support/cypress.util'; import { LinkRelE2E } from '../../../support/linkrels'; import { removeLinkFromResource } from '../../../support/tech.util'; @@ -73,10 +68,6 @@ describe('Postfach Nachrichten', () => { exist(vorgangList.getRoot()); }); - after(() => { - dropCollections(); - }); - describe('navigate to vorgangDetail', () => { beforeEach(() => { interceptWithResponse(HttpMethodE2E.GET, '*/vorgangs/*', (req) => modifyResponse(req)); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-forwarding.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-forwarding.cy.ts index 8634b339b8..9ac436d5a3 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-forwarding.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-forwarding.cy.ts @@ -140,7 +140,7 @@ describe('Vorgang weiterleiten', () => { }); it('should display Weiterleiten button in Status In Neu', () => { - vorgangNavigator.openVorgangDetailByName(vorgangWeiterleiten.name); + vorgangNavigator.openVorgang(vorgangWeiterleiten.name); vorgangVerifier.verifyForwardingButtonExists(); }); diff --git a/alfa-client/apps/alfa-e2e/src/fixtures/command/command.json b/alfa-client/apps/alfa-e2e/src/fixtures/command/command.json index 854f9990ea..9b286b5bbd 100644 --- a/alfa-client/apps/alfa-e2e/src/fixtures/command/command.json +++ b/alfa-client/apps/alfa-e2e/src/fixtures/command/command.json @@ -1,4 +1,7 @@ { + "_id": { + "$oid": "60d9fa33290e586b59a6b311" + }, "vorgangId": "602566a807bb665df9a86111", "createdAt": { "$date": "2022-06-20T10:51:52.073Z" @@ -10,8 +13,8 @@ "relationVersion": 0, "order": "SEND_POSTFACH_MAIL", "previousStatus": "NEU", - "_class": "Command", "finishedAt": { "$date": "2022-06-20T10:51:53.600Z" - } + }, + "_class": "Command" } diff --git a/alfa-client/apps/alfa-e2e/src/fixtures/postfach/send-postfach-nachricht.json b/alfa-client/apps/alfa-e2e/src/fixtures/postfach/send-postfach-nachricht.json new file mode 100644 index 0000000000..4954c8891b --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/fixtures/postfach/send-postfach-nachricht.json @@ -0,0 +1,23 @@ +{ + "attachment": [], + "mailBody": "\nSig für Sprint... <b>fix</b> a\n\nMfG\nIhre lustige Behörde", + "subject": "Send", + "vorgangId": "", + "sentAt": "", + "postfachAddress": { + "serviceKontoType": "OSI", + "identifier": { + "postfachId": "91ed5efa-9278-4366-e8f9-08db926b26f3" + }, + "type": "1", + "version": "1.0", + "class": "class de.ozgcloud.alfa.postfach.PostfachAddress" + }, + "createdAt": "2020-12-31T01:01:43.790Z[UTC]", + "sentSuccessful": "", + "createdBy": "", + "messageCode": "", + "id": "", + "replyOption": "POSSIBLE", + "direction": "OUT" +} diff --git a/alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_ludwig.json b/alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_ludwig.json new file mode 100644 index 0000000000..a6dd93d1bc --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_ludwig.json @@ -0,0 +1,18 @@ +{ + "_id": { + "$oid": "63284e55c39b316b2ad02a5d" + }, + "createdAt": { + "$date": "2022-08-25T08:29:45.025Z" + }, + "deleted": false, + "keycloakUserId": "108ee867-7290-466d-813f-ab1dc95d3691", + "firstName": "Ludwig", + "fullName": "Ludwig Löscher", + "lastName": "Lösher", + "email": "ludwig.loescher@e2e-ozg-sh.de", + "lastSyncTimestamp": 1663585874687, + "organisationsEinheitIds": [], + "roles": ["VERWALTUNG_LOESCHEN"], + "username": "ludwig" +} diff --git a/alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_zonk.json b/alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_zonk.json new file mode 100644 index 0000000000..8dd4e48252 --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/fixtures/usermanager/usermanager_user_zonk.json @@ -0,0 +1,18 @@ +{ + "_id": { + "$oid": "63284e54c33b916b2ad02e2a" + }, + "createdAt": { + "$date": "2022-08-25T08:29:45.025Z" + }, + "deleted": false, + "keycloakUserId": "108ee867-7290-466d-813f-ab1dc95d3691", + "firstName": "Zacharias", + "fullName": "Zacharias Zonk", + "lastName": "Zonk", + "email": "zacharias.zonk@e2e-ozg-sh.de", + "lastSyncTimestamp": 1663585874687, + "organisationsEinheitIds": [], + "roles": ["VERWALTUNG_USER"], + "username": "zonk" +} diff --git a/alfa-client/apps/alfa-e2e/src/helper/forwarding/forwarding.navigator.ts b/alfa-client/apps/alfa-e2e/src/helper/forwarding/forwarding.navigator.ts index 3400d79516..50a60041b6 100644 --- a/alfa-client/apps/alfa-e2e/src/helper/forwarding/forwarding.navigator.ts +++ b/alfa-client/apps/alfa-e2e/src/helper/forwarding/forwarding.navigator.ts @@ -10,7 +10,7 @@ export class E2EForwardingNavigator { private readonly forwardingDialog: ForwardingDialogE2EComponent = new ForwardingDialogE2EComponent(); public openForwardDialog(vorgangName: string): void { - this.vorgangNavigator.openVorgangDetailByName(vorgangName); + this.vorgangNavigator.openVorgang(vorgangName); this.formularButtons.getForwardButton().click(); exist(this.forwardingDialog.getRoot()); } diff --git a/alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.navigator.ts b/alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.navigator.ts new file mode 100644 index 0000000000..3b7a2b0a0c --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.navigator.ts @@ -0,0 +1,9 @@ +import { E2EVorgangNavigator } from '../vorgang/vorgang.navigator'; + +export class E2EPostfachNachrichtNavigator { + private readonly vorgangNavigator: E2EVorgangNavigator = new E2EVorgangNavigator(); + + public openPostfachNachricht(vorgangName: string): void { + this.vorgangNavigator.openVorgang(vorgangName); + } +} diff --git a/alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.verifier.ts b/alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.verifier.ts new file mode 100644 index 0000000000..d6f9c0cebf --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.verifier.ts @@ -0,0 +1,27 @@ +import { PostfachMailListItemE2EComponent } from '../../components/postfach/postfach-mail.e2e.component'; +import { PostfachNachrichtMessageCodeMessagesE2E } from '../../model/vorgang-attached-item'; +import { PostfachMailPage } from '../../page-objects/postfach-mail.component.po'; +import { contains, exist } from '../../support/cypress.util'; + +export class E2EPostfachNachrichtVerifier { + private readonly postfachNachrichtPage: PostfachMailPage = new PostfachMailPage(); + + public verifyFailedItemInList(subject: string): void { + const item: PostfachMailListItemE2EComponent = this.getListItem(subject); + this.verifyItemInList(subject); + contains(item.getSendErrorText(), PostfachNachrichtMessageCodeMessagesE2E.PROCESSING_FAILED); + exist(item.getSendErrorIcon()); + exist(item.getResendButton()); + } + + public verifyItemInList(subject: string): void { + const item: PostfachMailListItemE2EComponent = this.getListItem(subject); + contains(item.getSubject(), subject); + exist(item.getUserProfile().getRoot()); + exist(item.getSentAt()); + } + + private getListItem(subject: string): PostfachMailListItemE2EComponent { + return this.postfachNachrichtPage.getListItem(subject); + } +} diff --git a/alfa-client/apps/alfa-e2e/src/helper/vorgang/vorgang.navigator.ts b/alfa-client/apps/alfa-e2e/src/helper/vorgang/vorgang.navigator.ts index 3225f82b8a..9642b588d8 100644 --- a/alfa-client/apps/alfa-e2e/src/helper/vorgang/vorgang.navigator.ts +++ b/alfa-client/apps/alfa-e2e/src/helper/vorgang/vorgang.navigator.ts @@ -11,16 +11,12 @@ export class E2EVorgangNavigator { private readonly vorgangListPage = new VorgangListE2EComponent(); - public openVorgangDetailByName(vorgangName: string): void { - this.vorgangListPage.getListItem(vorgangName).getRoot().click(); - waitForSpinnerToDisappear(); - this.verifier.verifyVorgangDetailOpen(vorgangName); - } - public openVorgang(vorgangName: string): void { this.appHelper.navigateToDomain(); waitForSpinnerToDisappear(); exist(this.vorgangListPage.getRoot()); - this.openVorgangDetailByName(vorgangName); + + this.vorgangListPage.getListItem(vorgangName).getRoot().click(); + this.verifier.verifyVorgangDetailOpen(vorgangName); } } diff --git a/alfa-client/apps/alfa-e2e/src/model/command.ts b/alfa-client/apps/alfa-e2e/src/model/command.ts index 537bd8680f..bdc72b43d4 100644 --- a/alfa-client/apps/alfa-e2e/src/model/command.ts +++ b/alfa-client/apps/alfa-e2e/src/model/command.ts @@ -21,12 +21,12 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { DateE2E } from './util'; +import { DateE2E, ObjectIdE2E } from './util'; export enum CommandOrderE2E { ASSIGN_USER = 'ASSIGN_USER', CREATE_ATTACHED_ITEM = 'CREATE_ATTACHED_ITEM', - FORWARD_VORGANG = 'REDIRECT_VORGANG', //TODO Rename Order + FORWARD_VORGANG = 'REDIRECT_VORGANG', FORWARD_SUCCESSFUL = 'FORWARD_SUCCESSFULL', //TODO Rename Order FORWARD_FAILED = 'FORWARD_FAILED', PATCH_ATTACHED_ITEM = 'PATCH_ATTACHED_ITEM', @@ -38,7 +38,14 @@ export enum CommandOrderE2E { UPDATE_ATTACHED_ITEM = 'UPDATE_ATTACHED_ITEM', VORGANG_WIEDEREROEFFNEN = 'VORGANG_WIEDEREROEFFNEN', } + +export enum CommandStatusE2E { + PENDING = 'PENDING', + FINISHED = 'FINISHED', +} + export class CommandE2E { + id: ObjectIdE2E; vorgangId: string; createdAt: DateE2E; createdBy: string; diff --git a/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts b/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts index 433d726179..d47b4e62dc 100644 --- a/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts +++ b/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts @@ -33,9 +33,13 @@ import { InternalServerErrorDialogE2EComponent } from '../components/ui/internal import { SnackBarE2EComponent } from '../components/ui/snackbar.e2e.component'; import { VorgangListE2EComponent } from '../components/vorgang/vorgang-list.e2e.component'; import { VorgangViewsE2EComponent } from '../components/vorgang/vorgang-views.e2e.component'; +import { getTestElementWithClass } from '../support/cypress-helper'; +import { exist, notExist } from '../support/cypress.util'; import { HeaderE2EComponent } from './header.po'; export class MainPage { + public static readonly SPINNER: string = 'spinner'; + private readonly buildInfo: BuildInfoE2EComponent = new BuildInfoE2EComponent(); private readonly header: HeaderE2EComponent = new HeaderE2EComponent(); private readonly navigation: NavigationE2EComponent = new NavigationE2EComponent(); @@ -95,10 +99,20 @@ export class MainPage { } } -export function waitForSpinnerToDisappear(): boolean { - return cy.getTestElementWithClass('spinner').should('not.exist'); +export function waitForSpinnerToDisappear(): void { + notExist(getSpinner()); +} + +export function getSpinner(): Cypress.Chainable<JQuery<Element>> { + return getTestElementWithClass(MainPage.SPINNER); +} + +export function containsSpinner(rootElement: Cypress.Chainable<JQuery<Element>>): void { + exist(rootElement.findTestElementWithClass(MainPage.SPINNER)); } +//TODO Funktion loeschen export function waitforSpinnerToAppear(): void { // exist(cy.getTestElementWithClass('spinner')); } +// diff --git a/alfa-client/apps/alfa-e2e/src/page-objects/postfach-mail.component.po.ts b/alfa-client/apps/alfa-e2e/src/page-objects/postfach-mail.component.po.ts index c76682e1c2..50d8ffd1fb 100644 --- a/alfa-client/apps/alfa-e2e/src/page-objects/postfach-mail.component.po.ts +++ b/alfa-client/apps/alfa-e2e/src/page-objects/postfach-mail.component.po.ts @@ -23,7 +23,7 @@ */ //TODO Datei umbenennen in postfach.po.ts import { PostfachMailSubnavigation } from '../components/postfach/postfach-mail-subnavigation.e2e.component'; -import { PostfachMailListItem } from '../components/postfach/postfach-mail.e2e.component'; +import { PostfachMailListItemE2EComponent } from '../components/postfach/postfach-mail.e2e.component'; export class PostfachMailPage { private readonly root: string = 'postfach-mail-list'; @@ -50,8 +50,8 @@ export class PostfachMailPage { return this.headingText; } - getListItem(subject: string): PostfachMailListItem { - return new PostfachMailListItem(subject); + getListItem(subject: string): PostfachMailListItemE2EComponent { + return new PostfachMailListItemE2EComponent(subject); } getDownloadButton() { 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 9e520f34e3..8bdfdfa1e0 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts @@ -192,6 +192,10 @@ export function getTestElement(value: string) { return cy.getTestElement(value); } +export function getTestElementWithClass(value: string): Cypress.Chainable<JQuery<Element>> { + return cy.getTestElementWithClass(value); +} + export function getElement(value: string) { return cy.get(value); } 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 0c4ad2fdcb..335686bf99 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts @@ -160,7 +160,8 @@ function parseCommandData(commands) { return commands; } -function parseCommand(command) { +function parseCommand(command): void { + command._id = createObjectId(command); command.createdAt = createDate(command.createdAt); if (command.finishedAt) { command.finishedAt = createDate(command.finishedAt); @@ -173,7 +174,7 @@ function parseGridFsFileData(gridFsFiles) { return gridFsFiles; } -function parseGridFsFile(gridFsFile) { +function parseGridFsFile(gridFsFile): void { gridFsFile._id = createObjectId(gridFsFile); gridFsFile.length = createNumberLong(gridFsFile.length); gridFsFile.uploadDate = createDate(gridFsFile.uploadDate); @@ -184,7 +185,7 @@ function parseGridFsChunkData(gridFsChunks) { return gridFsChunks; } -function parseGridFsChunk(gridFsChunk) { +function parseGridFsChunk(gridFsChunk): void { gridFsChunk._id = createObjectId(gridFsChunk); //TODO createObjectId nutzen, sobald diese umgestellt ist gridFsChunk.files_id = new ObjectId(gridFsChunk.files_id.$oid); @@ -196,18 +197,18 @@ function parseOzgCloudFileData(ozgCloudFiles: OzgCloudFileE2E[]) { return ozgCloudFiles; } -function parseOzgCloudFile(ozgCloudFile) { +function parseOzgCloudFile(ozgCloudFile): void { ozgCloudFile._id = createObjectId(ozgCloudFile); ozgCloudFile.size = createNumberLong(ozgCloudFile.size); ozgCloudFile.version = createNumberLong(ozgCloudFile.version); } -function parseVorgangData(data) { +function parseVorgangData(data): void { data.forEach((vorgang) => parseVorgang(vorgang)); return data; } -function parseVorgang(vorgang) { +function parseVorgang(vorgang): void { vorgang._id = createObjectId(vorgang); vorgang.createdAt = createDate(vorgang.createdAt); @@ -221,7 +222,7 @@ function parseVorgang(vorgang) { } } -function parseEingang(eingang) { +function parseEingang(eingang): void { eingang.header.createdAt = createDate(eingang.header.createdAt); } @@ -229,16 +230,16 @@ function createBinData(encoded64Value) { return Binary(Buffer.from(encoded64Value, 'base64'), 0); } -function parseWiedervorlage(wiedervorlage) { +function parseWiedervorlage(wiedervorlage): void { wiedervorlage.frist = createDate(wiedervorlage.frist); wiedervorlage.createdAt = createDate(wiedervorlage.createdAt); } -function parseKommentar(kommentar) { +function parseKommentar(kommentar): void { kommentar.createdAt = createDate(kommentar.createdAt); } -function createDate(field) { +function createDate(field): Date { return new Date(field.$date); } @@ -247,7 +248,7 @@ function parseVorgangAttachedItemData(vorgangAttachedItems) { return vorgangAttachedItems; } -function parseVorgangAttachedItem(parseVorgangAttachedItem) { +function parseVorgangAttachedItem(parseVorgangAttachedItem): void { parseVorgangAttachedItem._id = createObjectId(parseVorgangAttachedItem); parseVorgangAttachedItem.version = createNumberLong(parseVorgangAttachedItem.version); } @@ -257,7 +258,7 @@ function parseUserData(data) { return data; } -function parseUser(user) { +function parseUser(user): void { user._id = createObjectId(user); user.createdAt = createDate(user.createdAt); } diff --git a/alfa-client/apps/alfa-e2e/src/support/e2e.ts b/alfa-client/apps/alfa-e2e/src/support/e2e.ts index ee993cca1c..6334e8ea63 100644 --- a/alfa-client/apps/alfa-e2e/src/support/e2e.ts +++ b/alfa-client/apps/alfa-e2e/src/support/e2e.ts @@ -41,6 +41,7 @@ import 'cypress-mochawesome-reporter/register'; import 'cypress-real-events'; import 'cypress-timestamps/support'; import './commands'; +import { dropCollections } from './cypress-helper'; import './file-upload'; Cypress.on('command:start', ({ attributes }) => { @@ -66,3 +67,9 @@ Cypress.on('fail', (err) => { Cypress.Keyboard.defaults({ keystrokeDelay: 30, }); + +after(() => { + cy.log('Cleanup after test suite...'); + dropCollections(); + cy.log('...cleanup done.'); +}); diff --git a/alfa-client/apps/alfa-e2e/src/support/postfach-nachricht.util.ts b/alfa-client/apps/alfa-e2e/src/support/postfach-nachricht.util.ts index ce4a9c9587..6b661b1da1 100644 --- a/alfa-client/apps/alfa-e2e/src/support/postfach-nachricht.util.ts +++ b/alfa-client/apps/alfa-e2e/src/support/postfach-nachricht.util.ts @@ -4,9 +4,11 @@ import { VorgangAttachedItemE2E, VorgangAttachedItemNameE2E, } from '../model/vorgang-attached-item'; +import { getUserSabineId } from './user-util'; import { VORGANG_ATTACHED_ITEM_CLASS } from './vorgang-attached-item-util'; const postfachNachrichtReplyItemFixture: PostfachMailItemE2E = require('../fixtures/postfach/postfach-nachricht-reply-item.json'); +const sendPostfachNachrichtCommandFixture: PostfachMailItemE2E = require('../fixtures/postfach/send-postfach-nachricht.json'); export function createPostfachNachrichtReplyItem(): PostfachMailItemE2E { return postfachNachrichtReplyItemFixture; @@ -27,3 +29,7 @@ export function createPostfachNachrichtAttachedItem(_id: string, vorgangId: stri _class: VORGANG_ATTACHED_ITEM_CLASS, }; } + +export function createSendPostfachNachrichtItem(): PostfachMailItemE2E { + return { ...sendPostfachNachrichtCommandFixture, createdBy: getUserSabineId() }; +} diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html index eff1422c21..33bdf6e2f6 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html @@ -24,7 +24,6 @@ --> @if (postfachMailListStateResource.resource | hasLink: postfachMailListLinkRel.SEND_POSTFACH_MAIL) { - <!-- TODO Aufteilen in 3 einzelne Komponenten --> @if (showAsIconButton) { <ods-button dataTestId="send-mail-icon-button" diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html index 948be283b5..1e21300e35 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html @@ -25,19 +25,19 @@ --> <a [routerLink]="onPage ? null : 'postfach'" data-test-class="incoming-mail-routing"> <div class="mail-head"> - <div class="subject" data-test-id="mail-subject"> - <mat-icon data-test-id="reply-icon">reply</mat-icon> + <div class="subject" data-test-class="mail-subject"> + <mat-icon data-test-class="reply-icon">reply</mat-icon> <span class="overflow">{{ postfachMail.subject }}</span> </div> <alfa-postfach-mail-date class="date" [postfachMail]="postfachMail"></alfa-postfach-mail-date> </div> <div class="second-row"> - <div class="message overflow" data-test-id="mail-text"> + <div class="message overflow" data-test-class="mail-text"> {{ postfachMail.mailBody }} </div> <mat-icon *ngIf="(postfachMail | hasLink: postfachNachrichtLinkRel.ATTACHMENTS) && !onPage" - data-test-id="postfach-nachricht-attachment-icon" + data-test-class="postfach-nachricht-attachment-icon" >attach_file</mat-icon > </div> diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts index 7ef16d31fb..6aa766b89b 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts @@ -29,7 +29,7 @@ import localeDe from '@angular/common/locales/de'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; import { provideRouter, RouterLinkWithHref, RouterModule } from '@angular/router'; -import { getDataTestClassOf, getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; import { createPostfachMailResource } from '../../../../../../../postfach-shared/test/postfach'; import { PostfachMailDateComponent } from '../postfach-mail-date/postfach-mail-date.component'; @@ -41,7 +41,7 @@ describe('IncommingMailComponent', () => { let component: IncommingMailComponent; let fixture: ComponentFixture<IncommingMailComponent>; - const attachmentIcon: string = getDataTestIdOf('postfach-nachricht-attachment-icon'); + const attachmentIcon: string = getDataTestClassOf('postfach-nachricht-attachment-icon'); const routing: string = getDataTestClassOf('incoming-mail-routing'); beforeEach(async () => { diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.html index e77aa1c04b..b0350e73cf 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.html @@ -24,7 +24,7 @@ --> <alfa-outgoing-mail-error - *ngIf="postfachMailResource | hasLink: postfachMailLinkRel.RESEND_POSTFACH_MAIL" + *ngIf="postfachMailResource | hasLink: PostfachMailLinkRel.RESEND_POSTFACH_MAIL" data-test-id="outgoing-mail-error" [postfachMailResource]="postfachMailResource" [resendPostfachMailStateResource]="resendPostfachMailStateResource$ | async" diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts index 43ea6995be..20855cb121 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts @@ -21,30 +21,33 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { OutgoingMailErrorContainerComponent } from './outgoing-mail-error-container.component'; +import { CommandResource } from '@alfa-client/command-shared'; import { PostfachMailLinkRel, PostfachService } from '@alfa-client/postfach-shared'; -import { mock } from '@alfa-client/test-utils'; -import { OutgoingMailErrorComponent } from './outgoing-mail-error/outgoing-mail-error.component'; +import { createEmptyStateResource, createStateResource, HasLinkPipe, StateResource } from '@alfa-client/tech-shared'; +import { existsAsHtmlElement, Mock, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { createCommandResource } from 'libs/command-shared/test/command'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; -import { HasLinkPipe } from '@alfa-client/tech-shared'; +import { of } from 'rxjs'; import { createPostfachMailResource } from '../../../../../../../../postfach-shared/test/postfach'; +import { singleColdCompleted } from '../../../../../../../../tech-shared/test/marbles'; +import { OutgoingMailErrorContainerComponent } from './outgoing-mail-error-container.component'; +import { OutgoingMailErrorComponent } from './outgoing-mail-error/outgoing-mail-error.component'; describe('OutgoingMailErrorContainerComponent', () => { let component: OutgoingMailErrorContainerComponent; let fixture: ComponentFixture<OutgoingMailErrorContainerComponent>; - const postfachService = mock(PostfachService); - const mailError: string = '[data-test-id="outgoing-mail-error"]'; + let postfachService: Mock<PostfachService>; + + const mailError: string = getDataTestIdOf('outgoing-mail-error'); beforeEach(async () => { + postfachService = mock(PostfachService); + await TestBed.configureTestingModule({ - declarations: [ - OutgoingMailErrorContainerComponent, - MockComponent(OutgoingMailErrorComponent), - HasLinkPipe, - ], + declarations: [OutgoingMailErrorContainerComponent, MockComponent(OutgoingMailErrorComponent), HasLinkPipe], providers: [ { provide: PostfachService, @@ -65,32 +68,42 @@ describe('OutgoingMailErrorContainerComponent', () => { }); describe('resendMail', () => { + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + + beforeEach(() => { + postfachService.resendMail.mockReturnValue(of(commandStateResource)); + }); + it('should call postfach service', () => { component.resendMail(); expect(postfachService.resendMail).toHaveBeenCalled(); }); + + it('should assign service response', () => { + component.resendPostfachMailStateResource$ = of(createEmptyStateResource<CommandResource>()); + + component.resendMail(); + + expect(component.resendPostfachMailStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); }); describe('outgoing mail error', () => { it('should show', () => { - component.postfachMailResource = createPostfachMailResource([ - PostfachMailLinkRel.RESEND_POSTFACH_MAIL, - ]); - fixture.detectChanges(); + component.postfachMailResource = createPostfachMailResource([PostfachMailLinkRel.RESEND_POSTFACH_MAIL]); - const element = fixture.nativeElement.querySelector(mailError); + fixture.detectChanges(); - expect(element).toBeInstanceOf(HTMLElement); + existsAsHtmlElement(fixture, mailError); }); it('should hide', () => { component.postfachMailResource = createPostfachMailResource(); - fixture.detectChanges(); - const element = fixture.nativeElement.querySelector(mailError); + fixture.detectChanges(); - expect(element).not.toBeInstanceOf(HTMLElement); + notExistsAsHtmlElement(fixture, mailError); }); }); }); diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts index 680063ef32..c0d8d6589d 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts @@ -21,15 +21,11 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Input, OnInit } from '@angular/core'; -import { StateResource } from '@alfa-client/tech-shared'; import { CommandResource } from '@alfa-client/command-shared'; +import { PostfachMailLinkRel, PostfachMailResource, PostfachService } from '@alfa-client/postfach-shared'; +import { StateResource } from '@alfa-client/tech-shared'; +import { Component, inject, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { - PostfachMailLinkRel, - PostfachMailResource, - PostfachService, -} from '@alfa-client/postfach-shared'; @Component({ selector: 'alfa-outgoing-mail-error-container', @@ -39,18 +35,17 @@ import { export class OutgoingMailErrorContainerComponent implements OnInit { @Input() postfachMailResource: PostfachMailResource; - resendPostfachMailStateResource$: Observable<StateResource<CommandResource>>; + private readonly postfachService: PostfachService = inject(PostfachService); - readonly postfachMailLinkRel = PostfachMailLinkRel; + public resendPostfachMailStateResource$: Observable<StateResource<CommandResource>>; - constructor(private postfachService: PostfachService) {} + public readonly PostfachMailLinkRel = PostfachMailLinkRel; - resendMail() { - this.postfachService.resendMail(this.postfachMailResource); + ngOnInit() { + this.resendPostfachMailStateResource$ = this.postfachService.getPendingSendPostfachMailCommand(); } - ngOnInit() { - this.resendPostfachMailStateResource$ = - this.postfachService.getPendingSendPostfachMailCommand(); + public resendMail(): void { + this.resendPostfachMailStateResource$ = this.postfachService.resendMail(this.postfachMailResource); } } diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error/outgoing-mail-error.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error/outgoing-mail-error.component.html index 2eaa38bcef..ee571d9b2d 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error/outgoing-mail-error.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error/outgoing-mail-error.component.html @@ -23,19 +23,13 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<div - *ngIf="postfachMailResource.messageCode" - data-test-id="mail-send-error" - class="mail-send-error" -> - <span data-test-id="mail-send-error-text">{{ message }}</span> - <mat-icon data-test-id="mail-send-error-icon" class="mail-send-error__icon text-error" - >error_outline_white</mat-icon - > +<div *ngIf="postfachMailResource.messageCode" data-test-id="mail-send-error" class="mail-send-error"> + <span data-test-class="mail-send-error-text">{{ message }}</span> + <mat-icon data-test-class="mail-send-error-icon" class="mail-send-error__icon text-error">error_outline_white</mat-icon> </div> <!-- TODO Eigene Component fuer den Button --> <ozgcloud-stroked-button-with-spinner - dataTestId="mail-resend-button" + dataTestClass="resend-nachricht-button" text="Erneut versuchen" icon="autorenew" [stateResource]="resendPostfachMailStateResource" diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html index a3d8233fcc..8c3c6ee468 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html @@ -38,18 +38,18 @@ <alfa-postfach-mail-date *ngIf="postfachMail.sentAt" [postfachMail]="postfachMail" - data-test-id="postfach-outgoing-nachricht-date" + data-test-class="postfach-outgoing-nachricht-date" ></alfa-postfach-mail-date> </div> - <span class="overflow subject" data-test-id="mail-subject">{{ postfachMail.subject }}</span> - <div class="message overflow" data-test-id="mail-text"> + <span class="overflow subject" data-test-class="mail-subject">{{ postfachMail.subject }}</span> + <div class="message overflow" data-test-class="mail-text"> {{ postfachMail.mailBody }} </div> <mat-icon *ngIf="(postfachMail | hasLink: postfachNachrichtLinkRel.ATTACHMENTS) && !onPage" - data-test-id="postfach-nachricht-attachment-icon" + data-test-class="postfach-nachricht-attachment-icon" >attach_file</mat-icon > </a> @@ -57,7 +57,7 @@ <alfa-outgoing-mail-error-container [postfachMailResource]="postfachMail"></alfa-outgoing-mail-error-container> <alfa-postfach-nachricht-edit-button-container *ngIf="(postfachMail | hasLink: postfachNachrichtLinkRel.EDIT) && !onPage" - data-test-id="postfach-nachricht-edit-button-container" + data-test-class="postfach-nachricht-edit-button-container" [postfachNachricht]="postfachMail" [vorgangStateResource]="vorgangStateResource" > diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts index 1c5549014c..09aa734b77 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts @@ -35,7 +35,7 @@ import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; import { provideRouter, RouterLinkWithHref, RouterModule } from '@angular/router'; -import { getDataTestClassOf, getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; import { createPostfachMailResource } from '../../../../../../../postfach-shared/test/postfach'; @@ -48,9 +48,9 @@ describe('OutgoingMailComponent', () => { let component: OutgoingMailComponent; let fixture: ComponentFixture<OutgoingMailComponent>; - const attachmentIcon: string = getDataTestIdOf('postfach-nachricht-attachment-icon'); - const postfachEditButton: string = getDataTestIdOf('postfach-nachricht-edit-button-container'); - const postfachOutgoingNachrichtDate: string = getDataTestIdOf('postfach-outgoing-nachricht-date'); + const attachmentIcon: string = getDataTestClassOf('postfach-nachricht-attachment-icon'); + const postfachEditButton: string = getDataTestClassOf('postfach-nachricht-edit-button-container'); + const postfachOutgoingNachrichtDate: string = getDataTestClassOf('postfach-outgoing-nachricht-date'); const routing: string = getDataTestClassOf('outgoing-mail-routing'); diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.html index 5b1bd5617b..0355d59f69 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.html @@ -24,12 +24,12 @@ --> <ng-container *ngIf="isIncomingMail; else sentAt"> - <div class="date" data-test-id="mail-created-at"> + <div class="date" data-test-class="mail-created-at"> {{ postfachMail.createdAt | formatDateWithTimePipe: false }} </div> </ng-container> <ng-template #sentAt> - <div class="date" data-test-id="mail-sent-at"> + <div class="date" data-test-class="mail-sent-at"> {{ postfachMail.sentAt | formatDateWithTimePipe: false }} </div> </ng-template> diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.spec.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.spec.ts index ca8fd42b29..80f60496e9 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.spec.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-date/postfach-mail-date.component.spec.ts @@ -28,6 +28,7 @@ import { registerLocaleData } from '@angular/common'; import localeDe from '@angular/common/locales/de'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { createPostfachMailResource } from 'libs/postfach-shared/test/postfach'; +import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; import { PostfachMailDateComponent } from './postfach-mail-date.component'; registerLocaleData(localeDe); @@ -36,8 +37,8 @@ describe('PostfachMailDateComponent', () => { let component: PostfachMailDateComponent; let fixture: ComponentFixture<PostfachMailDateComponent>; - const sentAt: string = '[data-test-id="mail-sent-at"]'; - const createdAt: string = '[data-test-id="mail-created-at"]'; + const sentAt: string = getDataTestClassOf('mail-sent-at'); + const createdAt: string = getDataTestClassOf('mail-created-at'); beforeEach(async () => { await TestBed.configureTestingModule({ diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html index 1c6c7d8515..3f72b62337 100644 --- a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html @@ -33,6 +33,7 @@ [tooltip]="toolTip" [class.with-text]="text" (click)="clickEmitter.emit($event)" + [attr.data-test-class]="dataTestClass" > <ozgcloud-button-content [icon]="icon" diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts index 36f102a0d7..48acab5184 100644 --- a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts @@ -48,6 +48,7 @@ export class OzgcloudStrokedButtonWithSpinnerComponent implements OnInit { @Input() dataTestId: string; @Input() showSpinner: boolean = false; @Input() strokedButton: boolean = true; + @Input() dataTestClass: string; @Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); -- GitLab From b3b5fff877eb48ebec8cc6124ee820a6879c1834 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Wed, 2 Apr 2025 09:38:46 +0200 Subject: [PATCH 3/8] OZG-7872 test unit test --- .../libs/kommentar-shared/src/lib/kommentar.service.spec.ts | 2 +- .../libs/postfach-shared/src/lib/postfach.service.spec.ts | 2 +- .../ozgcloud-stroked-button-with-spinner.component.html | 1 - .../ozgcloud-stroked-button-with-spinner.component.spec.ts | 2 +- .../ozgcloud-stroked-button-with-spinner.component.ts | 2 +- .../wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts index bd09cc77d8..4fa2802ae1 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts @@ -66,7 +66,7 @@ describe('KommentarService', () => { navigationService = mock(NavigationService); vorgangService = { ...mock(VorgangService), - getVorgangWithEingang: jest.fn().mockReturnValue(of(vorgangWithEingangStateResource)), + selectVorgangWithEingang: jest.fn().mockReturnValue(of(vorgangWithEingangStateResource)), }; binaryFileService = mock(BinaryFileService); diff --git a/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts b/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts index 876e08a03d..492502ba27 100644 --- a/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts +++ b/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts @@ -70,7 +70,7 @@ describe('PostfachService', () => { beforeEach(() => { vorgangService = { ...mock(VorgangService), - getVorgangWithEingang: jest.fn().mockReturnValue(of(vorgangWithEingangStateResource)), + selectVorgangWithEingang: jest.fn().mockReturnValue(of(vorgangWithEingangStateResource)), }; navigationService.urlChanged = jest.fn(); navigationService.urlChanged.mockReturnValue(of(urlChangedParams)); diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html index 3f72b62337..9370aa544d 100644 --- a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.html @@ -25,7 +25,6 @@ --> <button mat-stroked-button - data-test-class="icon-button" [attr.data-test-id]="dataTestId" [color]="color" [type]="type" diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.spec.ts b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.spec.ts index ac0307f012..003beefa16 100644 --- a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.spec.ts +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.spec.ts @@ -24,13 +24,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatButton } from '@angular/material/button'; import { MatRipple } from '@angular/material/core'; +import { TooltipDirective } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; import { MockComponent, MockDirective } from 'ng-mocks'; import { OzgcloudButtonContentComponent } from '../shared/ozgcloud-button-content/ozgcloud-button-content.component'; import { OzgcloudStrokedButtonWithSpinnerComponent } from './ozgcloud-stroked-button-with-spinner.component'; -import { TooltipDirective } from '@ods/system'; import * as ResourceUtils from 'libs/tech-shared/src/lib/resource/resource.util'; describe('OzgcloudStrokedButtonWithSpinnerComponent', () => { diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts index 48acab5184..8b9cc56e39 100644 --- a/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component.ts @@ -48,7 +48,7 @@ export class OzgcloudStrokedButtonWithSpinnerComponent implements OnInit { @Input() dataTestId: string; @Input() showSpinner: boolean = false; @Input() strokedButton: boolean = true; - @Input() dataTestClass: string; + @Input() dataTestClass: string = 'icon-button'; @Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); diff --git a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts index f2513adc3b..2f47bc7ea5 100644 --- a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts +++ b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts @@ -88,7 +88,7 @@ describe('WiedervorlageService', () => { binaryFileService = mock(BinaryFileService); vorgangService = { ...mock(VorgangService), - getVorgangWithEingang: jest.fn().mockReturnValue(of(vorgangWithEingangStateResource)), + selectVorgangWithEingang: jest.fn().mockReturnValue(of(vorgangWithEingangStateResource)), }; navigationService.urlChanged.mockReturnValue(of({})); -- GitLab From b8ddd0983e1df4a8724edb3bb7914379adfb33f9 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Thu, 3 Apr 2025 08:47:17 +0200 Subject: [PATCH 4/8] OZG-7872 add id to command --- .../e2e/main-tests/historie/historie.cy.ts | 141 ++++++------------ .../vorgang-xdomea-inhalte.cy.ts | 5 +- .../apps/alfa-e2e/src/model/command.ts | 2 +- .../apps/alfa-e2e/src/support/command-util.ts | 7 +- 4 files changed, 51 insertions(+), 104 deletions(-) diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/historie/historie.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/historie/historie.cy.ts index cd1bd8b8cc..34ed5740b5 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/historie/historie.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/historie/historie.cy.ts @@ -31,11 +31,7 @@ import { WiedervorlageHistorieItemE2EComponent, } from 'apps/alfa-e2e/src/components/vorgang/vorgang.formular-daten.historie.e2e.component'; import { CommandE2E, CommandOrderE2E } from 'apps/alfa-e2e/src/model/command'; -import { - HistorieAttachmentE2E, - HistorieHeadlineE2E, - SYSTEM_USER_NAME, -} from 'apps/alfa-e2e/src/model/historie'; +import { HistorieAttachmentE2E, HistorieHeadlineE2E, SYSTEM_USER_NAME } from 'apps/alfa-e2e/src/model/historie'; import { PostfachE2E } from 'apps/alfa-e2e/src/model/postfach-nachricht'; import { DirectionE2E } from 'apps/alfa-e2e/src/model/vorgang-attached-item'; import { buildCommand, createCommand, initCommands } from 'apps/alfa-e2e/src/support/command-util'; @@ -50,13 +46,8 @@ import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main. import { VorgangPage } from '../../../page-objects/vorgang.po'; import { dropCollections } from '../../../support/cypress-helper'; import { contains, exist, notExist } from '../../../support/cypress.util'; -import { - getUserSabine, - getUserSabineId, - initUsermanagerUsers, - loginAsSabine, -} from '../../../support/user-util'; -import { createVorgang, initVorgang } from '../../../support/vorgang-util'; +import { getUserSabine, getUserSabineId, initUsermanagerUsers, loginAsSabine } from '../../../support/user-util'; +import { createVorgang, initVorgang, objectIds } from '../../../support/vorgang-util'; registerLocaleData(localeDe, 'de', localeDeExtra); @@ -65,15 +56,14 @@ describe('Historie', () => { const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); const vorgangPage: VorgangPage = new VorgangPage(); - const vorgangDatenFormular: VorgangFormularDatenE2EComponent = - vorgangPage.getFormularDatenContainer(); + const vorgangDatenFormular: VorgangFormularDatenE2EComponent = vorgangPage.getFormularDatenContainer(); const vorgangHeader: VorgangDetailHeaderE2EComponent = vorgangPage.getVorgangDetailHeader(); const vorgang: VorgangE2E = createVorgang(); const assignUserCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.ASSIGN_USER, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[1], CommandOrderE2E.ASSIGN_USER, vorgang._id.$oid, vorgang._id.$oid), bodyObject: { assignedTo: getUserSabineId() }, createdAt: { $date: '2020-01-01T10:00:00.000Z' }, }; @@ -83,7 +73,7 @@ describe('Historie', () => { item: { ...createKommentar(), attachments: 'DummyAttachment' }, }; const createKommentarCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.CREATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[2], CommandOrderE2E.CREATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), bodyObject: createKommentarCommandBody, createdAt: { $date: '2020-01-01T10:01:00.000Z' }, }; @@ -92,21 +82,21 @@ describe('Historie', () => { item: { ...createKommentar(), attachments: ['DummyAttachment', 'DummyAttachment2'] }, }; const editKommentarCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.UPDATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[3], CommandOrderE2E.UPDATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), bodyObject: editKommentarCommandBody, createdAt: { $date: '2020-01-01T10:02:00.000Z' }, }; const forwardVorgangCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.FORWARD_VORGANG, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[4], CommandOrderE2E.FORWARD_VORGANG, vorgang._id.$oid, vorgang._id.$oid), createdAt: { $date: '2020-01-01T10:03:00.000Z' }, }; const forwardSuccessfulCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.FORWARD_SUCCESSFUL, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[5], CommandOrderE2E.FORWARD_SUCCESSFUL, vorgang._id.$oid, vorgang._id.$oid), createdAt: { $date: '2020-01-01T10:04:00.000Z' }, }; const forwardFailedCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.FORWARD_FAILED, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[6], CommandOrderE2E.FORWARD_FAILED, vorgang._id.$oid, vorgang._id.$oid), createdAt: { $date: '2020-01-01T10:05:00.000Z' }, }; @@ -117,6 +107,7 @@ describe('Historie', () => { attachments: 'dummyAttachment', }; const sendPostfachMailCommand: CommandE2E = { + _id: { $oid: objectIds[7] }, ...createCommand(), bodyObject: sendPostfachMailCommandBody, createdAt: { $date: '2020-01-01T10:06:00.000Z' }, @@ -128,12 +119,12 @@ describe('Historie', () => { attachments: ['dummyAttachment', 'dummyAttachment2'], }; const sendPostfachNachrichtCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.SEND_POSTFACH_NACHRICHT, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[8], CommandOrderE2E.SEND_POSTFACH_NACHRICHT, vorgang._id.$oid, vorgang._id.$oid), bodyObject: sendPostfachNachrichtCommandBody, createdAt: { $date: '2020-01-01T10:07:00.000Z' }, }; const receivePostfachNachrichtCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.CREATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[9], CommandOrderE2E.CREATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), bodyObject: { itemName: 'PostfachMail', item: { @@ -150,6 +141,7 @@ describe('Historie', () => { }; const resendPostfachNachricht: CommandE2E = buildCommand( + objectIds[10], CommandOrderE2E.RESEND_POSTFACH_NACHRICHT, vorgang._id.$oid, vorgang._id.$oid, @@ -160,7 +152,7 @@ describe('Historie', () => { attachments: 'DummyAttachment', }; const createWiedervorlageCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.CREATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[11], CommandOrderE2E.CREATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), bodyObject: { itemName: 'Wiedervorlage', item: createWiedervorlageCommandBody }, createdAt: { $date: '2020-01-01T10:09:00.000Z' }, }; @@ -170,17 +162,17 @@ describe('Historie', () => { done: true, }; const editWiedervorlageCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.UPDATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[12], CommandOrderE2E.UPDATE_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), bodyObject: { itemName: 'Wiedervorlage', item: editWiedervorlageCommandBody }, createdAt: { $date: '2020-01-01T10:10:00.000Z' }, }; const wiedervorlageErledigenCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.PATCH_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[13], CommandOrderE2E.PATCH_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), bodyObject: { itemName: 'Wiedervorlage', item: { done: 'true' }, vorgangId: vorgang._id.$oid }, createdAt: { $date: '2020-01-01T10:11:00.000Z' }, }; const wiedervorlageWiedereroeffnenCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.PATCH_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), + ...buildCommand(objectIds[14], CommandOrderE2E.PATCH_ATTACHED_ITEM, vorgang._id.$oid, vorgang._id.$oid), bodyObject: { itemName: 'Wiedervorlage', item: { done: 'false' }, vorgangId: vorgang._id.$oid }, createdAt: { $date: '2020-01-01T10:12:00.000Z' }, }; @@ -241,8 +233,7 @@ describe('Historie', () => { describe('vorgang order', () => { it('assign user', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(0); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(0); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.ASSIGN_USER); @@ -253,8 +244,7 @@ describe('Historie', () => { describe('kommentar order', () => { it('create kommentar', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(1); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(1); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.CREATE_KOMMENTAR); @@ -268,8 +258,7 @@ describe('Historie', () => { }); it('edit kommentar', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(2); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(2); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.EDIT_KOMMENTAR); @@ -279,17 +268,13 @@ describe('Historie', () => { const kommentarItem: KommentarHistorieItemE2EComponent = historieItem.getKommentar(); contains(kommentarItem.getText(), editKommentarCommandBody.item.text); - contains( - kommentarItem.getAttachment(), - HistorieAttachmentE2E.MULTIPLE_TEXT.replace('{countAttachments}', '2'), - ); + contains(kommentarItem.getAttachment(), HistorieAttachmentE2E.MULTIPLE_TEXT.replace('{countAttachments}', '2')); }); }); describe('forward order', () => { it('forward vorgang', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(3); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(3); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.FORWARD_VORGANG); @@ -298,8 +283,7 @@ describe('Historie', () => { }); it('forward successful', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(4); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(4); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.FORWARD_SUCCESSFUL); @@ -308,8 +292,7 @@ describe('Historie', () => { }); it('forward failed', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(5); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(5); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.FORWARD_FAILED); @@ -320,52 +303,35 @@ describe('Historie', () => { describe('postfach nachricht order', () => { it('send postfach mail', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(6); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(6); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.SEND_POSTFACH_NACHRICHT); contains(historieItem.getUser(), userName); historieItem.getExpandButton().click(); - const postfachNachtichtItem: PostfachNachrichtHistorieItemE2EComponent = - historieItem.getPostfachNachricht(); + const postfachNachtichtItem: PostfachNachrichtHistorieItemE2EComponent = historieItem.getPostfachNachricht(); - contains( - postfachNachtichtItem.getPostfachNachrichtSubject(), - sendPostfachMailCommandBody.subject, - ); - contains( - postfachNachtichtItem.getPostfachNachrichtMailBody(), - sendPostfachMailCommandBody.mailBody, - ); + contains(postfachNachtichtItem.getPostfachNachrichtSubject(), sendPostfachMailCommandBody.subject); + contains(postfachNachtichtItem.getPostfachNachrichtMailBody(), sendPostfachMailCommandBody.mailBody); }); it('send postfach nachricht', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(7); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(7); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.SEND_POSTFACH_NACHRICHT); contains(historieItem.getUser(), userName); historieItem.getExpandButton().click(); - const postfachNachtichtItem: PostfachNachrichtHistorieItemE2EComponent = - historieItem.getPostfachNachricht(); + const postfachNachtichtItem: PostfachNachrichtHistorieItemE2EComponent = historieItem.getPostfachNachricht(); - contains( - postfachNachtichtItem.getPostfachNachrichtSubject(), - sendPostfachNachrichtCommandBody.subject, - ); - contains( - postfachNachtichtItem.getPostfachNachrichtMailBody(), - sendPostfachNachrichtCommandBody.mailBody, - ); + contains(postfachNachtichtItem.getPostfachNachrichtSubject(), sendPostfachNachrichtCommandBody.subject); + contains(postfachNachtichtItem.getPostfachNachrichtMailBody(), sendPostfachNachrichtCommandBody.mailBody); }); it('receive postfach nachricht', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(8); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(8); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.RECEIVE_POSTFACH_NACHRICHT); @@ -373,8 +339,7 @@ describe('Historie', () => { contains(historieItem.getSystemUser(), SYSTEM_USER_NAME); historieItem.getExpandButton().click(); - const postfachNachtichtItem: PostfachNachrichtHistorieItemE2EComponent = - historieItem.getPostfachNachricht(); + const postfachNachtichtItem: PostfachNachrichtHistorieItemE2EComponent = historieItem.getPostfachNachricht(); contains( postfachNachtichtItem.getPostfachNachrichtSubject(), @@ -387,8 +352,7 @@ describe('Historie', () => { }); it('resend postfach nachricht', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(9); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(9); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.RESEND_POSTFACH_NACHRICHT); @@ -398,53 +362,39 @@ describe('Historie', () => { describe('wiedervorlage order', () => { it('create wiedervorlage', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(10); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(10); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.CREATE_WIEDERVORLAGE); contains(historieItem.getUser(), userName); historieItem.getExpandButton().click(); - const wiedervorlageItem: WiedervorlageHistorieItemE2EComponent = - historieItem.getWiedervorlage(); + const wiedervorlageItem: WiedervorlageHistorieItemE2EComponent = historieItem.getWiedervorlage(); contains(wiedervorlageItem.getStatus(), 'offen'); contains(wiedervorlageItem.getBetreff(), createWiedervorlageCommandBody.betreff); - contains( - wiedervorlageItem.getBeschreibung(), - createWiedervorlageCommandBody.beschreibung, - ); + contains(wiedervorlageItem.getBeschreibung(), createWiedervorlageCommandBody.beschreibung); contains(wiedervorlageItem.getAttachment(), HistorieAttachmentE2E.SINGLE_TEXT); }); it('edit wiedervorlage', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(11); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(11); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.EDIT_WIEDERVORLAGE); contains(historieItem.getUser(), userName); historieItem.getExpandButton().click(); - const wiedervorlageItem: WiedervorlageHistorieItemE2EComponent = - historieItem.getWiedervorlage(); + const wiedervorlageItem: WiedervorlageHistorieItemE2EComponent = historieItem.getWiedervorlage(); contains(wiedervorlageItem.getStatus(), 'geschlossen'); contains(wiedervorlageItem.getBetreff(), editWiedervorlageCommandBody.betreff); - contains( - wiedervorlageItem.getBeschreibung(), - editWiedervorlageCommandBody.beschreibung, - ); - contains( - wiedervorlageItem.getAttachment(), - HistorieAttachmentE2E.MULTIPLE_TEXT.replace('{countAttachments}', '2'), - ); + contains(wiedervorlageItem.getBeschreibung(), editWiedervorlageCommandBody.beschreibung); + contains(wiedervorlageItem.getAttachment(), HistorieAttachmentE2E.MULTIPLE_TEXT.replace('{countAttachments}', '2')); }); it('wiedervorlage erledigen', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(12); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(12); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.WIEDERVORLAGE_ERLEDIGEN); @@ -453,8 +403,7 @@ describe('Historie', () => { }); it('wiedervorlage wiedereroeffnen', () => { - const historieItem: VorgangFormularDatenHistorieItemE2EComponent = - vorgangDatenFormular.getHistorieItemByIndex(13); + const historieItem: VorgangFormularDatenHistorieItemE2EComponent = vorgangDatenFormular.getHistorieItemByIndex(13); exist(historieItem.getRoot()); contains(historieItem.getHeadline(), HistorieHeadlineE2E.WIEDERVORLAGE_WIEDEREROEFFNEN); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-xdomea/vorgang-xdomea-inhalte.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-xdomea/vorgang-xdomea-inhalte.cy.ts index ff177ddcff..9050a22745 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-xdomea/vorgang-xdomea-inhalte.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-xdomea/vorgang-xdomea-inhalte.cy.ts @@ -78,13 +78,14 @@ describe('check xDomea contents', () => { }; const assignUserCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.ASSIGN_USER, vorgangExportieren._id.$oid, vorgangExportieren._id.$oid), + ...buildCommand(objectIds[1], CommandOrderE2E.ASSIGN_USER, vorgangExportieren._id.$oid, vorgangExportieren._id.$oid), bodyObject: { assignedTo: getUserDorotheaId() }, finishedAt: { $date: '2024-06-20T07:25:30.000Z' }, }; const changeStatusCommand: CommandE2E = { ...buildCommand( + objectIds[2], CommandOrderE2E.VORGANG_WIEDEREROEFFNEN, vorgangExportieren._id.$oid, @@ -95,7 +96,7 @@ describe('check xDomea contents', () => { }; const setAktenzeichenCommand: CommandE2E = { - ...buildCommand(CommandOrderE2E.SET_AKTENZEICHEN, vorgangExportieren._id.$oid, vorgangExportieren._id.$oid), + ...buildCommand(objectIds[3], CommandOrderE2E.SET_AKTENZEICHEN, vorgangExportieren._id.$oid, vorgangExportieren._id.$oid), order: CommandOrderE2E.SET_AKTENZEICHEN, bodyObject: { aktenzeichen: 'AKT_ENZ_EIC_HEN1' }, finishedAt: { $date: '2024-06-19T07:25:30.000Z' }, diff --git a/alfa-client/apps/alfa-e2e/src/model/command.ts b/alfa-client/apps/alfa-e2e/src/model/command.ts index bdc72b43d4..1050041aa9 100644 --- a/alfa-client/apps/alfa-e2e/src/model/command.ts +++ b/alfa-client/apps/alfa-e2e/src/model/command.ts @@ -45,7 +45,7 @@ export enum CommandStatusE2E { } export class CommandE2E { - id: ObjectIdE2E; + _id: ObjectIdE2E; vorgangId: string; createdAt: DateE2E; createdBy: string; diff --git a/alfa-client/apps/alfa-e2e/src/support/command-util.ts b/alfa-client/apps/alfa-e2e/src/support/command-util.ts index f70f24389b..2ee84ad777 100644 --- a/alfa-client/apps/alfa-e2e/src/support/command-util.ts +++ b/alfa-client/apps/alfa-e2e/src/support/command-util.ts @@ -31,13 +31,10 @@ export function createCommand(): CommandE2E { return { ...commandFixture, createdBy: getUserSabineId() }; } -export function buildCommand( - order: CommandOrderE2E, - vorgangId: string, - relationId: string, -): CommandE2E { +export function buildCommand(id: string, order: CommandOrderE2E, vorgangId: string, relationId: string): CommandE2E { return { ...createCommand(), + _id: { $oid: id }, vorgangId, relationId, order, -- GitLab From 5a1e94b975d10db05eb06f00d10ede8e9eab9f52 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Thu, 3 Apr 2025 18:22:30 +0200 Subject: [PATCH 5/8] OZG-7872 use order and uri for command key in state; add pendingCommands to vorgang state; use matching command from state in component --- .../postfach-mail/postfach-mail-error.cy.ts | 13 +- .../postfach-nachricht-pending-send.cy.ts | 95 ++++++++++-- .../apps/alfa-e2e/src/page-objects/main.po.ts | 4 + alfa-client/apps/alfa-e2e/src/support/e2e.ts | 6 + .../src/lib/+state/command.actions.ts | 46 +++--- .../src/lib/+state/command.effects.spec.ts | 82 +++------- .../src/lib/+state/command.effects.ts | 41 ++--- .../src/lib/+state/command.reducer.spec.ts | 46 +++--- .../src/lib/+state/command.reducer.ts | 47 +++--- .../src/lib/+state/command.selectors.spec.ts | 29 ++-- .../src/lib/+state/command.selectors.ts | 22 +-- .../command-shared/src/lib/command.linkrel.ts | 1 + .../command-shared/src/lib/command.model.ts | 6 +- .../src/lib/command.repository.spec.ts | 28 ++-- .../src/lib/command.repository.ts | 19 +-- .../src/lib/command.service.spec.ts | 75 ++++----- .../command-shared/src/lib/command.service.ts | 50 +++--- .../src/lib/command.util.spec.ts | 46 ++++-- .../command-shared/src/lib/command.util.ts | 26 ++-- .../libs/command-shared/test/command.ts | 5 + .../src/lib/postfach.service.spec.ts | 35 ++++- .../src/lib/postfach.service.ts | 14 +- ...ing-mail-error-container.component.spec.ts | 37 ++++- ...outgoing-mail-error-container.component.ts | 4 +- .../src/lib/+state/vorgang.actions.ts | 144 ++++++++++-------- .../src/lib/+state/vorgang.reducer.spec.ts | 75 ++++++++- .../src/lib/+state/vorgang.reducer.ts | 27 ++++ .../src/lib/+state/vorgang.selectors.spec.ts | 131 +++++++--------- .../src/lib/+state/vorgang.selectors.ts | 117 +++++++------- .../src/lib/vorgang.service.spec.ts | 99 +++++++++++- .../vorgang-shared/src/lib/vorgang.service.ts | 38 ++++- 31 files changed, 853 insertions(+), 555 deletions(-) diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts index 7248a1727b..7a882428f3 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-mail-error.cy.ts @@ -21,9 +21,8 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { E2EAppHelper } from 'apps/alfa-e2e/src/helper/app.helper'; import { E2EPostfachNachrichtVerifier } from 'apps/alfa-e2e/src/helper/postfach-nachricht/postfach-nachricht.verifier'; -import { E2EVorgangNavigator } from 'apps/alfa-e2e/src/helper/vorgang/vorgang.navigator'; +import { APP_HELPER, VORGANG_NAVIGATOR } from 'apps/alfa-e2e/src/support/e2e'; import { createPostfachNachrichtAttachedItem, createPostfachNachrichtReplyItem, @@ -42,7 +41,7 @@ import { PostfachNachrichtSnackbarMessageE2E, VorgangAttachedItemE2E, } from '../../../model/vorgang-attached-item'; -import { containsSpinner, MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; +import { containsSpinner, MainPage, notContainsSpinner, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { PostfachMailPage } from '../../../page-objects/postfach-mail.component.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; import { contains, exist, notExist } from '../../../support/cypress.util'; @@ -51,9 +50,6 @@ import { initVorgangAttachedItem } from '../../../support/vorgang-attached-item- import { createVorgang, initVorgang, objectIds } from '../../../support/vorgang-util'; describe('PostfachMail error', () => { - const appHelper: E2EAppHelper = new E2EAppHelper(); - const vorgangNavigator: E2EVorgangNavigator = new E2EVorgangNavigator(); - const mainPage: MainPage = new MainPage(); const snackbar: SnackBarE2EComponent = mainPage.getSnackBar(); @@ -94,12 +90,12 @@ describe('PostfachMail error', () => { initVorgang(vorgang); initVorgangAttachedItem([vorgangAttachedItem, vorgangAttachedItem2]); - appHelper.loginAsSabine(); + APP_HELPER.loginAsSabine(); }); describe('navigate to vorgang detail', () => { it('should open vorgang detail', () => { - vorgangNavigator.openVorgang(vorgang.name); + VORGANG_NAVIGATOR.openVorgang(vorgang.name); waitForSpinnerToDisappear(); }); @@ -131,6 +127,7 @@ describe('PostfachMail error', () => { it('click on resend button should hide error', () => { listItem.getResendButton().click(); containsSpinner(listItem.getResendButton()); + notContainsSpinner(postfachMailPage.getListItem(postfachNachricht2.subject).getResendButton()); waitForSpinnerToDisappear(); notExist(listItem.getSendErrorIcon()); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts index d2c1d45bf3..ffcd5734aa 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/postfach-mail/postfach-nachricht-pending-send.cy.ts @@ -1,28 +1,31 @@ import { PostfachMailE2EComponent } from 'apps/alfa-e2e/src/components/postfach/postfach-mail.e2e.component'; -import { E2EAppHelper } from 'apps/alfa-e2e/src/helper/app.helper'; -import { E2EVorgangNavigator } from 'apps/alfa-e2e/src/helper/vorgang/vorgang.navigator'; import { CommandE2E, CommandOrderE2E, CommandStatusE2E } from 'apps/alfa-e2e/src/model/command'; -import { createCommand, initCommand } from 'apps/alfa-e2e/src/support/command-util'; +import { createCommand, initCommands } from 'apps/alfa-e2e/src/support/command-util'; +import { APP_HELPER, VORGANG_NAVIGATOR } from 'apps/alfa-e2e/src/support/e2e'; import { createPostfachNachrichtAttachedItem, + createPostfachNachrichtReplyItem, createSendPostfachNachrichtItem, } from 'apps/alfa-e2e/src/support/postfach-nachricht.util'; +import { getUserSabineId } from 'apps/alfa-e2e/src/support/user-util'; import { initVorgangAttachedItem } from 'apps/alfa-e2e/src/support/vorgang-attached-item-util'; import { VorgangE2E } from '../../../model/vorgang'; -import { PostfachMailItemE2E, VorgangAttachedItemE2E } from '../../../model/vorgang-attached-item'; +import { + DirectionE2E, + PostfachMailItemE2E, + PostfachNachrichtMessageCodeE2E, + VorgangAttachedItemE2E, +} from '../../../model/vorgang-attached-item'; import { containsSpinner } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; import { exist } from '../../../support/cypress.util'; -import { createVorgang, initVorgang, objectIds } from '../../../support/vorgang-util'; - -describe('Postfach Nachricht pending send', () => { - const appHelper: E2EAppHelper = new E2EAppHelper(); - - const vorgangNavigator: E2EVorgangNavigator = new E2EVorgangNavigator(); +import { buildVorgang, createVorgang, initVorgaenge, objectIds } from '../../../support/vorgang-util'; +describe('Postfach Nachricht pending', () => { const vorgangPage: VorgangPage = new VorgangPage(); const postfachMailContainer: PostfachMailE2EComponent = vorgangPage.getPostfachMailcontainer(); + //SEND const vorgangWithPendingSendCommand: VorgangE2E = { ...createVorgang(), name: 'VorgangWithPendingSendCommand' }; const postfachSendNachrichtItem: PostfachMailItemE2E = { ...createSendPostfachNachrichtItem(), @@ -40,17 +43,63 @@ describe('Postfach Nachricht pending send', () => { body: postfachSendNachrichtItem, }; + //RESEND 1 + const vorgangWithResendCommand: VorgangE2E = buildVorgang(objectIds[1], 'VorgangWithResendCommand'); + const resendPostfachNachricht1: PostfachMailItemE2E = { + ...createPostfachNachrichtReplyItem(), + subject: 'Failed Outgoing Postfach Nachricht 1', + createdBy: getUserSabineId(), + direction: DirectionE2E.OUT, + sentAt: '2022-12-02T15:00:00.790Z[UTC]', + sentSuccessful: false, + messageCode: PostfachNachrichtMessageCodeE2E.PROCESSING_FAILED, + }; + const resendPostfachNachrichtVorgangAttachedItem1: VorgangAttachedItemE2E = { + ...createPostfachNachrichtAttachedItem(objectIds[2], vorgangWithResendCommand._id.$oid), + item: resendPostfachNachricht1, + }; + const pendingResendCommand1: CommandE2E = { + ...createCommand(), + _id: { $oid: objectIds[1] }, + vorgangId: vorgangWithResendCommand._id.$oid, + order: CommandOrderE2E.RESEND_POSTFACH_NACHRICHT, + status: CommandStatusE2E.PENDING, + finishedAt: null, + body: resendPostfachNachricht1, + relationId: resendPostfachNachrichtVorgangAttachedItem1._id.$oid, + }; + + //RESEND 2 + const resendPostfachNachricht2: PostfachMailItemE2E = { + ...resendPostfachNachricht1, + subject: 'Failed Outgoing Postfach Nachricht 2', + }; + const resendPostfachNachrichtVorgangAttachedItem2: VorgangAttachedItemE2E = { + ...createPostfachNachrichtAttachedItem(objectIds[3], vorgangWithResendCommand._id.$oid), + item: resendPostfachNachricht2, + }; + const pendingResendCommand2: CommandE2E = { + ...pendingResendCommand1, + _id: { $oid: objectIds[2] }, + body: resendPostfachNachricht2, + relationId: resendPostfachNachrichtVorgangAttachedItem2._id.$oid, + }; + before(() => { - initVorgang(vorgangWithPendingSendCommand); - initVorgangAttachedItem([postfachNachrichtAttachedItem]); - initCommand(pendingSendCommand); + initVorgaenge([vorgangWithPendingSendCommand, vorgangWithResendCommand]); + initVorgangAttachedItem([ + postfachNachrichtAttachedItem, + resendPostfachNachrichtVorgangAttachedItem1, + resendPostfachNachrichtVorgangAttachedItem2, + ]); + initCommands([pendingSendCommand, pendingResendCommand1, pendingResendCommand2]); - appHelper.loginAsSabine(); + APP_HELPER.loginAsSabine(); }); - describe('area in vorgang detail', () => { + describe('send command on area in vorgang detail', () => { it('should have item in list', () => { - vorgangNavigator.openVorgang(vorgangWithPendingSendCommand.name); + VORGANG_NAVIGATOR.openVorgang(vorgangWithPendingSendCommand.name); exist(postfachMailContainer.getListItem(postfachSendNachrichtItem.subject).getRoot()); }); @@ -64,4 +113,18 @@ describe('Postfach Nachricht pending send', () => { exist(postfachMailContainer.getListItem(postfachSendNachrichtItem.subject).getEditButton()); }); }); + + describe('resend commands area in vorgang detail', () => { + it('should have items in list', () => { + VORGANG_NAVIGATOR.openVorgang(vorgangWithResendCommand.name); + + exist(postfachMailContainer.getListItem(resendPostfachNachricht1.subject).getRoot()); + exist(postfachMailContainer.getListItem(resendPostfachNachricht2.subject).getRoot()); + }); + + it('should show spinner on buttons', () => { + containsSpinner(postfachMailContainer.getListItem(resendPostfachNachricht1.subject).getResendButton()); + containsSpinner(postfachMailContainer.getListItem(resendPostfachNachricht2.subject).getResendButton()); + }); + }); }); diff --git a/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts b/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts index d47b4e62dc..69da85ac92 100644 --- a/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts +++ b/alfa-client/apps/alfa-e2e/src/page-objects/main.po.ts @@ -111,6 +111,10 @@ export function containsSpinner(rootElement: Cypress.Chainable<JQuery<Element>>) exist(rootElement.findTestElementWithClass(MainPage.SPINNER)); } +export function notContainsSpinner(rootElement: Cypress.Chainable<JQuery<Element>>): void { + notExist(rootElement.findTestElementWithClass(MainPage.SPINNER)); +} + //TODO Funktion loeschen export function waitforSpinnerToAppear(): void { // exist(cy.getTestElementWithClass('spinner')); diff --git a/alfa-client/apps/alfa-e2e/src/support/e2e.ts b/alfa-client/apps/alfa-e2e/src/support/e2e.ts index 6334e8ea63..0d31b33108 100644 --- a/alfa-client/apps/alfa-e2e/src/support/e2e.ts +++ b/alfa-client/apps/alfa-e2e/src/support/e2e.ts @@ -40,6 +40,8 @@ import 'cypress-mochawesome-reporter/register'; import 'cypress-real-events'; import 'cypress-timestamps/support'; +import { E2EAppHelper } from '../helper/app.helper'; +import { E2EVorgangNavigator } from '../helper/vorgang/vorgang.navigator'; import './commands'; import { dropCollections } from './cypress-helper'; import './file-upload'; @@ -73,3 +75,7 @@ after(() => { dropCollections(); cy.log('...cleanup done.'); }); + +export const APP_HELPER: E2EAppHelper = new E2EAppHelper(); + +export const VORGANG_NAVIGATOR: E2EVorgangNavigator = new E2EVorgangNavigator(); diff --git a/alfa-client/libs/command-shared/src/lib/+state/command.actions.ts b/alfa-client/libs/command-shared/src/lib/+state/command.actions.ts index e573a10f4a..e899a6df08 100644 --- a/alfa-client/libs/command-shared/src/lib/+state/command.actions.ts +++ b/alfa-client/libs/command-shared/src/lib/+state/command.actions.ts @@ -29,18 +29,11 @@ import { TypedActionCreatorWithProps, } from '@alfa-client/tech-shared'; import { HttpErrorResponse } from '@angular/common/http'; -import { createAction, props, Action } from '@ngrx/store'; -import { Resource } from '@ngxp/rest'; -import { - CommandListResource, - CommandResource, - CreateCommand, - CreateCommandProps, -} from '../command.model'; +import { Action, createAction, props } from '@ngrx/store'; +import { Resource, ResourceUri } from '@ngxp/rest'; +import { CommandListResource, CommandResource, CreateCommand, CreateCommandProps } from '../command.model'; -export const publishConcurrentModificationAction: TypedActionCreator = createAction( - '[Command/API] Concurrent Modification', -); +export const publishConcurrentModificationAction: TypedActionCreator = createAction('[Command/API] Concurrent Modification'); export interface CommandStateResourceProps { commandStateResource: StateResource<CommandResource>; @@ -52,9 +45,7 @@ export interface LoadCommandListSuccessProps { export interface LoadCommandListProps { resource: Resource; linkRel: string; - successAction: ( - commandList: CommandListResource, - ) => LoadCommandListSuccessProps & Action<string>; + successAction: (commandList: CommandListResource) => LoadCommandListSuccessProps & Action<string>; failureAction: (apiError: ApiError) => ApiErrorAction & Action<string>; } @@ -63,6 +54,11 @@ export const loadCommandList: TypedActionCreatorWithProps<LoadCommandListProps> props<LoadCommandListProps>(), ); +export interface UpdateCommandProps { + relatedResourceUri: ResourceUri; + command: CommandResource; +} + export interface CommandProps { command: CommandResource; } @@ -82,7 +78,13 @@ export interface RevokeCommandFailureProps { error: ApiError | unknown; } +export interface CreateCommandSuccessProps { + createCommandProps: CreateCommandProps; + command: CommandResource; +} + export interface CreateCommandFailureProps { + createCommandProps: CreateCommandProps; command: CreateCommand; error: ApiError | HttpErrorResponse | unknown; } @@ -91,12 +93,14 @@ export const createCommand: TypedActionCreatorWithProps<CreateCommandProps> = cr '[Command] Create command', props<CreateCommandProps>(), ); -export const createCommandSuccess: TypedActionCreatorWithProps<CommandProps> = createAction( +export const createCommandSuccess: TypedActionCreatorWithProps<CreateCommandSuccessProps> = createAction( '[Command] Create command Success', - props<CommandProps>(), + props<CreateCommandSuccessProps>(), +); +export const createCommandFailure: TypedActionCreatorWithProps<CreateCommandFailureProps> = createAction( + '[Command] Create command Failure', + props<CreateCommandFailureProps>(), ); -export const createCommandFailure: TypedActionCreatorWithProps<CreateCommandFailureProps> = - createAction('[Command] Create command Failure', props<CreateCommandFailureProps>()); export const pollCreatedCommand: TypedActionCreatorWithProps<PollCommandProps> = createAction( '[Command] Poll created command', props<PollCommandProps>(), @@ -119,8 +123,10 @@ export const revokeCommandSuccess: TypedActionCreatorWithProps<CommandProps> = c '[Command] Revoke command Success', props<CommandProps>(), ); -export const revokeCommandFailure: TypedActionCreatorWithProps<RevokeCommandFailureProps> = - createAction('[Command] Revoke command Failure', props<RevokeCommandFailureProps>()); +export const revokeCommandFailure: TypedActionCreatorWithProps<RevokeCommandFailureProps> = createAction( + '[Command] Revoke command Failure', + props<RevokeCommandFailureProps>(), +); export const pollRevokedCommand: TypedActionCreatorWithProps<PollCommandProps> = createAction( '[Command] Poll revoked command', props<PollCommandProps>(), diff --git a/alfa-client/libs/command-shared/src/lib/+state/command.effects.spec.ts b/alfa-client/libs/command-shared/src/lib/+state/command.effects.spec.ts index b1cb1673a7..1b2da80853 100644 --- a/alfa-client/libs/command-shared/src/lib/+state/command.effects.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/+state/command.effects.spec.ts @@ -21,12 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { - ApiError, - ApiErrorAction, - EMPTY_STRING, - TypedActionCreatorWithProps, -} from '@alfa-client/tech-shared'; +import { ApiError, ApiErrorAction, EMPTY_STRING, TypedActionCreatorWithProps } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { SnackBarService } from '@alfa-client/ui'; import { TestBed } from '@angular/core/testing'; @@ -36,11 +31,7 @@ import { provideMockStore } from '@ngrx/store/testing'; import { Resource } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { ColdObservable } from 'jest-marbles/typings/src/rxjs/cold-observable'; -import { - createCommandListResource, - createCommandResource, - createCreateCommandProps, -} from 'libs/command-shared/test/command'; +import { createCommandListResource, createCommandResource, createCreateCommandProps } from 'libs/command-shared/test/command'; import { createApiError } from 'libs/tech-shared/test/error'; import { Observable, of } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; @@ -48,12 +39,7 @@ import { CommandLinkRel } from '../command.linkrel'; import { CREATE_COMMAND_MESSAGE_BY_ORDER, CommandErrorMessage } from '../command.message'; import { CommandListResource, CommandResource, CreateCommandProps } from '../command.model'; import { CommandRepository } from '../command.repository'; -import { - CommandProps, - LoadCommandListSuccessProps, - SnackBarProps, - createCommandFailure, -} from './command.actions'; +import { CommandProps, LoadCommandListSuccessProps, SnackBarProps, createCommandFailure } from './command.actions'; import { CommandEffects } from './command.effects'; import * as CommandActions from './command.actions'; @@ -121,7 +107,7 @@ describe('CommandEffects', () => { const commandListResource: CommandListResource = createCommandListResource(); beforeEach(() => { - repository.getPendingCommands.mockReturnValue(of(commandListResource)); + repository.getCommands.mockReturnValue(of(commandListResource)); }); it('should call repository', () => { @@ -129,7 +115,7 @@ describe('CommandEffects', () => { effects.loadCommandList$.subscribe(); - expect(repository.getPendingCommands).toHaveBeenCalledWith(resource, linkRel); + expect(repository.getCommands).toHaveBeenCalledWith(resource, linkRel); }); it('should dispatch success action on no error', () => { @@ -145,7 +131,7 @@ describe('CommandEffects', () => { const apiError: ApiError = createApiError(); const error = { error: apiError }; const errorResponse = cold('-#', {}, error); - repository.getPendingCommands = jest.fn(() => errorResponse); + repository.getCommands = jest.fn(() => errorResponse); actions = hot('-a', { a: loadCommandList }); @@ -156,8 +142,7 @@ describe('CommandEffects', () => { describe('createCommand', () => { const createCommandProps: CreateCommandProps = createCreateCommandProps(); - const createCommandAction: Action<string> = - CommandActions.createCommand(createCommandProps); + const createCommandAction: Action<string> = CommandActions.createCommand(createCommandProps); const commandResource: CommandResource = createCommandResource(); @@ -184,10 +169,7 @@ describe('CommandEffects', () => { effects.createCommand$.subscribe(); - expect(effects.handleCreatedCommand).toHaveBeenCalledWith( - addCreateCommandActionType(createCommandProps), - commandResource, - ); + expect(effects.handleCreatedCommand).toHaveBeenCalledWith(addCreateCommandActionType(createCommandProps), commandResource); }); function addCreateCommandActionType(createCommandProps: CreateCommandProps) { @@ -202,7 +184,11 @@ describe('CommandEffects', () => { actions = hot('-a', { a: createCommandAction }); const expected: ColdObservable = cold('--b', { - b: CommandActions.createCommandFailure({ error, command: createCommandProps.command }), + b: CommandActions.createCommandFailure({ + createCommandProps: addCreateCommandActionType(createCommandProps), + error, + command: createCommandProps.command, + }), }); expect(effects.createCommand$).toBeObservable(expected); }); @@ -214,10 +200,7 @@ describe('CommandEffects', () => { it('should return pollCreatedCommand action on pending command', () => { const command: CommandResource = createCommandResource([CommandLinkRel.UPDATE]); - const actions: Action<string>[] = effects.handleCreatedCommand( - createCommandProps, - command, - ); + const actions: Action<string>[] = effects.handleCreatedCommand(createCommandProps, command); expect(actions.length).toBe(1); expect(actions[0].type).toBe(CommandActions.pollCreatedCommand.type); @@ -285,7 +268,7 @@ describe('CommandEffects', () => { actions = hot('-a', { a: pollCreateCommandAction }); expectObservable(effects.pollCreatedCommand$).toBe(delay + ' --c', { - c: createCommandFailure({ command, error }), + c: createCommandFailure({ createCommandProps: null, command, error }), }); }); }); @@ -298,10 +281,7 @@ describe('CommandEffects', () => { it('should return createCommandSuccess action', () => { const command: CommandResource = createCommandResource(); - const actions: Action<string>[] = effects.handleCreateCommandSuccess( - createCommandProps, - command, - ); + const actions: Action<string>[] = effects.handleCreateCommandSuccess(createCommandProps, command); expect(actions.length).toBe(2); const createCommandSuccessAction: CommandActions.CommandProps = <any>actions[0]; @@ -312,10 +292,7 @@ describe('CommandEffects', () => { it('should return showSnackbar action', () => { const command: CommandResource = createCommandResource(); - const actions: Action<string>[] = effects.handleCreateCommandSuccess( - createCommandProps, - command, - ); + const actions: Action<string>[] = effects.handleCreateCommandSuccess(createCommandProps, command); expect(actions.length).toBe(2); const showSnackbarAction: CommandActions.SnackBarProps = <any>actions[1]; @@ -328,10 +305,7 @@ describe('CommandEffects', () => { it('should return createCommandSuccess action', () => { const command: CommandResource = createCommandResource([CommandLinkRel.REVOKE]); - const actions: Action<string>[] = effects.handleCreateCommandSuccess( - createCommandProps, - command, - ); + const actions: Action<string>[] = effects.handleCreateCommandSuccess(createCommandProps, command); expect(actions.length).toBe(2); expect(actions[0].type).toBe(CommandActions.createCommandSuccess.type); @@ -341,10 +315,7 @@ describe('CommandEffects', () => { it('should return showRevokeSnackbar action', () => { const command: CommandResource = createCommandResource([CommandLinkRel.REVOKE]); - const actions: Action<string>[] = effects.handleCreateCommandSuccess( - createCommandProps, - command, - ); + const actions: Action<string>[] = effects.handleCreateCommandSuccess(createCommandProps, command); expect(actions.length).toBe(2); expect(actions[1].type).toBe(CommandActions.showRevokeSnackbar.type); @@ -372,10 +343,7 @@ describe('CommandEffects', () => { errorMessage: CommandErrorMessage.CONCURRENT_MODIFICATION, }; - const actions: Action<string>[] = effects.handleCreateCommandSuccess( - createCommandProps, - command, - ); + const actions: Action<string>[] = effects.handleCreateCommandSuccess(createCommandProps, command); expect(actions.length).toBe(2); expect(actions[0].type).toBe(CommandActions.createCommandSuccess.type); @@ -428,10 +396,7 @@ describe('CommandEffects', () => { effects.handleSnackbarByCommand(snackBarProps); - expect(snackBarService.show).toHaveBeenCalledWith( - command, - createCommandProps.snackBarMessage, - ); + expect(snackBarService.show).toHaveBeenCalledWith(command, createCommandProps.snackBarMessage); }); it('should show snackBar on undefined snackBarMessage', () => { @@ -442,10 +407,7 @@ describe('CommandEffects', () => { effects.handleSnackbarByCommand(snackBarProps); - expect(snackBarService.show).toHaveBeenCalledWith( - command, - CREATE_COMMAND_MESSAGE_BY_ORDER[command.order], - ); + expect(snackBarService.show).toHaveBeenCalledWith(command, CREATE_COMMAND_MESSAGE_BY_ORDER[command.order]); }); it('should NOT show snackBar on empty snackBarMessage', () => { diff --git a/alfa-client/libs/command-shared/src/lib/+state/command.effects.ts b/alfa-client/libs/command-shared/src/lib/+state/command.effects.ts index 6b13e23b3f..3df93f0045 100644 --- a/alfa-client/libs/command-shared/src/lib/+state/command.effects.ts +++ b/alfa-client/libs/command-shared/src/lib/+state/command.effects.ts @@ -25,19 +25,14 @@ import { EMPTY_STRING } from '@alfa-client/tech-shared'; import { SnackBarService } from '@alfa-client/ui'; import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { Store, Action } from '@ngrx/store'; +import { Action, Store } from '@ngrx/store'; import { isEmpty, isEqual, isUndefined } from 'lodash-es'; import { of } from 'rxjs'; import { catchError, delay, map, mergeMap, switchMap, tap } from 'rxjs/operators'; import { COMMAND_ERROR_MESSAGES, CREATE_COMMAND_MESSAGE_BY_ORDER } from '../command.message'; import { CommandListResource, CommandResource, CreateCommandProps } from '../command.model'; import { CommandRepository } from '../command.repository'; -import { - hasCommandError, - isConcurrentModification, - isPending, - isRevokeable, -} from '../command.util'; +import { hasCommandError, isConcurrentModification, isPending, isRevokeable } from '../command.util'; import { CommandProps, LoadCommandListProps, @@ -72,7 +67,7 @@ export class CommandEffects { this.actions$.pipe( ofType(loadCommandList), switchMap((props: LoadCommandListProps) => - this.repository.getPendingCommands(props.resource, props.linkRel).pipe( + this.repository.getCommands(props.resource, props.linkRel).pipe( map((commandList: CommandListResource) => props.successAction(commandList)), catchError((error) => of(props.failureAction(error.error))), ), @@ -86,7 +81,7 @@ export class CommandEffects { switchMap((props: CreateCommandProps) => this.repository.createCommand(props.resource, props.linkRel, props.command).pipe( mergeMap((command: CommandResource) => this.handleCreatedCommand(props, command)), - catchError((error) => of(createCommandFailure({ error, command: props.command }))), + catchError((error) => of(createCommandFailure({ createCommandProps: props, error, command: props.command }))), ), ), ), @@ -98,39 +93,34 @@ export class CommandEffects { delay(CommandEffects.POLL_DELAY), switchMap((props: PollCommandProps) => this.repository.getCommand(props.command).pipe( - mergeMap((command: CommandResource) => - this.handleCreatedCommand(props.createCommandProps, command), + mergeMap((command: CommandResource) => this.handleCreatedCommand(props.createCommandProps, command)), + catchError((error) => + of(createCommandFailure({ createCommandProps: props.createCommandProps, command: props.command, error })), ), - catchError((error) => of(createCommandFailure({ command: props.command, error }))), ), ), ), ); - handleCreatedCommand( - createCommandProps: CreateCommandProps, - command: CommandResource, - ): Action<string>[] { + handleCreatedCommand(createCommandProps: CreateCommandProps, command: CommandResource): Action<string>[] { if (isPending(command)) { return [pollCreatedCommand({ createCommandProps, command })]; } return this.handleCreateCommandSuccess(createCommandProps, command); } - handleCreateCommandSuccess( - createCommandProps: CreateCommandProps, - command: CommandResource, - ): Action<string>[] { + handleCreateCommandSuccess(createCommandProps: CreateCommandProps, command: CommandResource): Action<string>[] { if (isRevokeable(command)) { - return [createCommandSuccess({ command }), showRevokeSnackbar({ command })]; + return [createCommandSuccess({ createCommandProps, command }), showRevokeSnackbar({ command })]; } + if (hasCommandError(command) && isConcurrentModification(command.errorMessage)) { this.showError(command); //FIXME Anstelle der createCommandSucess Action sollte eine createCommandFailure Action geworfen werden. //Hierzu muss ein HttpErrorResponse für die errorMessage definieren werden - return [createCommandSuccess({ command }), publishConcurrentModificationAction()]; + return [createCommandSuccess({ createCommandProps, command }), publishConcurrentModificationAction()]; } - return [createCommandSuccess({ command }), showSnackbar({ createCommandProps, command })]; + return [createCommandSuccess({ createCommandProps, command }), showSnackbar({ createCommandProps, command })]; } private showError(command: CommandResource): void { @@ -166,10 +156,7 @@ export class CommandEffects { ); handleSnackbarByCommand(props: SnackBarProps): void { - if ( - !isEqual(props.createCommandProps.snackBarMessage, EMPTY_STRING) && - isEmpty(props.command.errorMessage) - ) { + if (!isEqual(props.createCommandProps.snackBarMessage, EMPTY_STRING) && isEmpty(props.command.errorMessage)) { this.snackbarService.show(props.command, this.getSnackBarMessage(props)); } } diff --git a/alfa-client/libs/command-shared/src/lib/+state/command.reducer.spec.ts b/alfa-client/libs/command-shared/src/lib/+state/command.reducer.spec.ts index 9a3735965f..161bddb678 100644 --- a/alfa-client/libs/command-shared/src/lib/+state/command.reducer.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/+state/command.reducer.spec.ts @@ -26,21 +26,27 @@ import { createEmptyStateResource, createErrorStateResource, createStateResource, + LinkRelationName, } from '@alfa-client/tech-shared'; import { HttpErrorResponse } from '@angular/common/http'; +import { faker } from '@faker-js/faker'; import { Action } from '@ngrx/store'; -import { Resource, ResourceUri } from '@ngxp/rest'; +import { getUrl, Resource, ResourceUri } from '@ngxp/rest'; import { createApiError, createHttpErrorResponse } from '../../../../tech-shared/test/error'; import { createDummyResource } from '../../../../tech-shared/test/resource'; -import { createCommandResource, createCreateCommand } from '../../../test/command'; -import { CommandResource, CreateCommand } from '../command.model'; -import { CommandState, initialState, reducer } from './command.reducer'; - -import { faker } from '@faker-js/faker'; +import { createCommandResource, createCreateCommand, createCreateCommandProps } from '../../../test/command'; +import { CommandResource, CreateCommand, CreateCommandProps } from '../command.model'; +import { buildCommandStateKey, CommandState, initialState, reducer } from './command.reducer'; import * as CommandActions from '../+state/command.actions'; describe('Command Reducer', () => { + const linkRel: LinkRelationName = faker.string.alpha(6); + const resource: Resource = createDummyResource([linkRel]); + const uri: ResourceUri = getUrl(resource, linkRel); + const commandResource: CommandResource = createCommandResource(); + const createCommandProps: CreateCommandProps = { ...createCreateCommandProps(), resource, linkRel }; + describe('unknown action', () => { it('should return current state', () => { const action: Action = {} as Action; @@ -52,42 +58,48 @@ describe('Command Reducer', () => { }); describe('createCommand', () => { - const resource: Resource = createDummyResource(); - const linkRel: ResourceUri = faker.internet.url(); const command: CreateCommand = createCreateCommand(); it('should create empty loading map entry', () => { - const action = CommandActions.createCommand({ resource, linkRel, command }); + const action: Action<string> = CommandActions.createCommand({ resource, linkRel, command }); const state: CommandState = reducer(initialState, action); - expect(state.commandByOrderMap[command.order]).toEqual(createEmptyStateResource(true)); + expect(state.commandMap[buildCommandStateKey(command.order, uri)]).toEqual(createEmptyStateResource(true)); }); }); describe('createCommandSuccess', () => { - const command: CommandResource = createCommandResource(); - it('should create stateResource entry by loaded resource', () => { - const action = CommandActions.createCommandSuccess({ command }); + const action: Action<string> = CommandActions.createCommandSuccess({ + createCommandProps, + command: commandResource, + }); const state: CommandState = reducer(initialState, action); - expect(state.commandByOrderMap[command.order]).toEqual(createStateResource(command)); + expect(state.commandMap[buildCommandStateKey(createCommandProps.command.order, uri)]).toEqual( + createStateResource(commandResource), + ); }); }); describe('createCommandFailure', () => { - const command: CommandResource = createCommandResource(); const error: ApiError = createApiError(); const httpErrorResponse: HttpErrorResponse = { ...createHttpErrorResponse(), error }; it('should create errorStateResource entry by occured error', () => { - const action = CommandActions.createCommandFailure({ command, error: httpErrorResponse }); + const action: Action<string> = CommandActions.createCommandFailure({ + createCommandProps, + command: commandResource, + error: httpErrorResponse, + }); const state: CommandState = reducer(initialState, action); - expect(state.commandByOrderMap[command.order]).toEqual(createErrorStateResource(error)); + expect(state.commandMap[buildCommandStateKey(createCommandProps.command.order, uri)]).toEqual( + createErrorStateResource(error), + ); }); }); }); diff --git a/alfa-client/libs/command-shared/src/lib/+state/command.reducer.ts b/alfa-client/libs/command-shared/src/lib/+state/command.reducer.ts index bce701b1df..3c9e84e51d 100644 --- a/alfa-client/libs/command-shared/src/lib/+state/command.reducer.ts +++ b/alfa-client/libs/command-shared/src/lib/+state/command.reducer.ts @@ -21,16 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { - StateResource, - createEmptyStateResource, - createErrorStateResource, - createStateResource, -} from '@alfa-client/tech-shared'; +import { createEmptyStateResource, createErrorStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { HttpErrorResponse } from '@angular/common/http'; import { Action, ActionReducer, createReducer, on } from '@ngrx/store'; -import { CommandResource, CreateCommandProps } from '../command.model'; +import { getUrl, ResourceUri } from '@ngxp/rest'; +import { CommandOrder, CommandResource, CreateCommandProps } from '../command.model'; +import { CreateCommandFailureProps, CreateCommandSuccessProps } from './command.actions'; -import { HttpErrorResponse } from '@angular/common/http'; import * as Actions from './command.actions'; export const COMMAND_FEATURE_KEY = 'CommandState'; @@ -40,11 +37,11 @@ export interface CommandPartialState { } export interface CommandState { - commandByOrderMap: { [order: string]: StateResource<CommandResource> }; + commandMap: { [key: string]: StateResource<CommandResource> }; } export const initialState: CommandState = { - commandByOrderMap: {}, + commandMap: {}, }; const commandReducer: ActionReducer<CommandState, Action> = createReducer( @@ -53,29 +50,29 @@ const commandReducer: ActionReducer<CommandState, Action> = createReducer( Actions.createCommand, (state: CommandState, props: CreateCommandProps): CommandState => ({ ...state, - commandByOrderMap: { - ...state.commandByOrderMap, - [props.command.order]: createEmptyStateResource(true), + commandMap: { + ...state.commandMap, + [createCommandStateKey(props)]: createEmptyStateResource(true), }, }), ), on( Actions.createCommandSuccess, - (state: CommandState, props: Actions.CommandProps): CommandState => ({ + (state: CommandState, props: CreateCommandSuccessProps): CommandState => ({ ...state, - commandByOrderMap: { - ...state.commandByOrderMap, - [props.command.order]: createStateResource(props.command), + commandMap: { + ...state.commandMap, + [createCommandStateKey(props.createCommandProps)]: createStateResource(props.command), }, }), ), on( Actions.createCommandFailure, - (state: CommandState, props: Actions.CreateCommandFailureProps): CommandState => ({ + (state: CommandState, props: CreateCommandFailureProps): CommandState => ({ ...state, - commandByOrderMap: { - ...state.commandByOrderMap, - [props.command.order]: createErrorStateResource((<HttpErrorResponse>props.error).error), + commandMap: { + ...state.commandMap, + [createCommandStateKey(props.createCommandProps)]: createErrorStateResource((<HttpErrorResponse>props.error).error), }, }), ), @@ -84,3 +81,11 @@ const commandReducer: ActionReducer<CommandState, Action> = createReducer( export function reducer(state: CommandState, action: Action): CommandState { return commandReducer(state, action); } + +function createCommandStateKey(props: CreateCommandProps): string { + return buildCommandStateKey(props.command.order, getUrl(props.resource, props.linkRel)); +} + +export function buildCommandStateKey(order: CommandOrder, uri: ResourceUri): string { + return `${order}_${uri}`; +} diff --git a/alfa-client/libs/command-shared/src/lib/+state/command.selectors.spec.ts b/alfa-client/libs/command-shared/src/lib/+state/command.selectors.spec.ts index cc0f81f2f5..d2e786a6f8 100644 --- a/alfa-client/libs/command-shared/src/lib/+state/command.selectors.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/+state/command.selectors.spec.ts @@ -21,10 +21,12 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { faker } from '@faker-js/faker/.'; +import { ResourceUri } from '@ngxp/rest'; import { createCreateCommand } from 'libs/command-shared/test/command'; import { CommandOrder, CommandResource, CreateCommand } from '../command.model'; -import { CommandPartialState, initialState } from './command.reducer'; +import { buildCommandStateKey, CommandPartialState, initialState } from './command.reducer'; import * as Selectors from './command.selectors'; @@ -32,38 +34,37 @@ describe('Command Selectors', () => { let state: CommandPartialState; const commandInState: CreateCommand = createCreateCommand(); + const uri: ResourceUri = faker.internet.url(); + const commandInStateOrder: CommandOrder = CommandOrder.VORGANG_ANNEHMEN; - const commandByOrderMap: any = { [commandInStateOrder]: commandInState }; + const commandMap: any = { [buildCommandStateKey(commandInStateOrder, uri)]: commandInState }; beforeEach(() => { state = { CommandState: { ...initialState, - commandByOrderMap, + commandMap, }, }; }); - describe('commandByOrderMap', () => { - it('should select commandByOrderMap', () => { - const result: any = Selectors.commandByOrderMap.projector(state.CommandState); + describe('commandMap', () => { + it('should select commandMap', () => { + const result: any = Selectors.commandMap.projector(state.CommandState); - expect(result).toBe(commandByOrderMap); + expect(result).toBe(commandMap); }); }); - describe('commandByOrder', () => { + describe('command', () => { it('should return command from map by order', () => { - const result: StateResource<CommandResource> = - Selectors.commandByOrder(commandInStateOrder).projector(commandByOrderMap); + const result: StateResource<CommandResource> = Selectors.command(commandInStateOrder, uri).projector(commandMap); expect(result).toBe(commandInState); }); it('should return empty state resource on empty object', () => { - const result: StateResource<CommandResource> = Selectors.commandByOrder( - commandInStateOrder, - ).projector({}); + const result: StateResource<CommandResource> = Selectors.command(commandInStateOrder, uri).projector({}); expect(result).toEqual(createEmptyStateResource()); }); diff --git a/alfa-client/libs/command-shared/src/lib/+state/command.selectors.ts b/alfa-client/libs/command-shared/src/lib/+state/command.selectors.ts index db561e1aec..2e31f077a0 100644 --- a/alfa-client/libs/command-shared/src/lib/+state/command.selectors.ts +++ b/alfa-client/libs/command-shared/src/lib/+state/command.selectors.ts @@ -22,21 +22,21 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { createEmptyStateResource } from '@alfa-client/tech-shared'; -import { MemoizedSelector, createFeatureSelector, createSelector } from '@ngrx/store'; +import { createFeatureSelector, createSelector, MemoizedSelector } from '@ngrx/store'; +import { ResourceUri } from '@ngxp/rest'; import { isUndefined } from 'lodash-es'; import { CommandOrder } from '../command.model'; -import { COMMAND_FEATURE_KEY, CommandState } from './command.reducer'; +import { buildCommandStateKey, COMMAND_FEATURE_KEY, CommandState } from './command.reducer'; -const getCommandState: MemoizedSelector<object, CommandState> = - createFeatureSelector<CommandState>(COMMAND_FEATURE_KEY); +const getCommandState: MemoizedSelector<object, CommandState> = createFeatureSelector<CommandState>(COMMAND_FEATURE_KEY); -export const commandByOrderMap: MemoizedSelector<CommandState, any> = createSelector( +export const commandMap: MemoizedSelector<CommandState, any> = createSelector( getCommandState, - (state: CommandState) => state.commandByOrderMap, + (state: CommandState) => state.commandMap, ); -export const commandByOrder = (order: CommandOrder) => - createSelector(commandByOrderMap, (commandByOrderMapInState: any) => - isUndefined(commandByOrderMapInState[order]) ? - createEmptyStateResource() - : commandByOrderMapInState[order], +export const command = (order: CommandOrder, uri: ResourceUri) => + createSelector(commandMap, (commandMapInState: any) => + isUndefined(commandMapInState[buildCommandStateKey(order, uri)]) ? createEmptyStateResource() : ( + commandMapInState[buildCommandStateKey(order, uri)] + ), ); diff --git a/alfa-client/libs/command-shared/src/lib/command.linkrel.ts b/alfa-client/libs/command-shared/src/lib/command.linkrel.ts index 2b92fb7620..5ef51639ba 100644 --- a/alfa-client/libs/command-shared/src/lib/command.linkrel.ts +++ b/alfa-client/libs/command-shared/src/lib/command.linkrel.ts @@ -24,6 +24,7 @@ export enum CommandLinkRel { CREATED_BY = 'createdBy', EFFECTED_RESOURCE = 'effected_resource', + RELATED_RESOURCE = 'related_resource', REVOKE = 'revoke', SELF = 'self', UPDATE = 'update', diff --git a/alfa-client/libs/command-shared/src/lib/command.model.ts b/alfa-client/libs/command-shared/src/lib/command.model.ts index b86ac920fb..cc598f0540 100644 --- a/alfa-client/libs/command-shared/src/lib/command.model.ts +++ b/alfa-client/libs/command-shared/src/lib/command.model.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ListResource } from '@alfa-client/tech-shared'; +import { ListResource, StateResource } from '@alfa-client/tech-shared'; import { Resource } from '@ngxp/rest'; export interface CreateCommand { @@ -101,3 +101,7 @@ export interface CreateCommandProps { } export type CreateCommandPropsWithoutResource = Omit<CreateCommandProps, 'resource'>; + +export interface PendingCommandMap { + [relatedResourceUri: string]: StateResource<CommandResource>; +} diff --git a/alfa-client/libs/command-shared/src/lib/command.repository.spec.ts b/alfa-client/libs/command-shared/src/lib/command.repository.spec.ts index 2d422eba29..c4cd71b0c6 100644 --- a/alfa-client/libs/command-shared/src/lib/command.repository.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/command.repository.spec.ts @@ -21,15 +21,12 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { faker } from '@faker-js/faker'; +import { LinkRelationName } from '@alfa-client/tech-shared'; import { mock, useFromMock } from '@alfa-client/test-utils'; +import { faker } from '@faker-js/faker'; import { Resource, ResourceFactory } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; -import { - createCommand, - createCommandListResource, - createCommandResource, -} from 'libs/command-shared/test/command'; +import { createCommand, createCommandListResource, createCommandResource } from 'libs/command-shared/test/command'; import { toResource } from 'libs/tech-shared/test/resource'; import { CommandLinkRel } from './command.linkrel'; import { Command, CommandListResource, CommandResource, CommandStatus } from './command.model'; @@ -41,11 +38,10 @@ describe('CommandRepository', () => { let resourceWrapper = { post: jest.fn(), get: jest.fn(), patch: jest.fn() }; const command: Command = createCommand(); - const commandResource: CommandResource = createCommandResource(); + + const linkRel: LinkRelationName = 'dummyLinkRel'; + const commandResource: CommandResource = createCommandResource([linkRel]); const commandListResource: CommandListResource = createCommandListResource(); - const commandResourceWithPendingCommand: CommandResource = createCommandResource([ - 'pendingCommands', - ]); beforeEach(() => { repository = new CommandRepository(useFromMock(resourceFactory)); @@ -131,27 +127,25 @@ describe('CommandRepository', () => { }); }); - describe('get pending commands', () => { - const linkRel = 'pendingCommands'; - + describe('get commands', () => { beforeEach(() => { resourceWrapper.get.mockReturnValue(hot('a', { a: commandListResource })); }); it('should call resourceFactory with resource', () => { - repository.getPendingCommands(commandResourceWithPendingCommand, linkRel); + repository.getCommands(commandResource, linkRel); - expect(resourceFactory.from).toHaveBeenCalledWith(commandResourceWithPendingCommand); + expect(resourceFactory.from).toHaveBeenCalledWith(commandResource); }); it('should call resourceWrapper with linkRel', () => { - repository.getPendingCommands(commandResourceWithPendingCommand, linkRel); + repository.getCommands(commandResource, linkRel); expect(resourceWrapper.get).toHaveBeenCalledWith(linkRel); }); it('should return value', () => { - const result = repository.getPendingCommands(commandResourceWithPendingCommand, linkRel); + const result = repository.getCommands(commandResource, linkRel); expect(result).toBeObservable(cold('a', { a: commandListResource })); }); diff --git a/alfa-client/libs/command-shared/src/lib/command.repository.ts b/alfa-client/libs/command-shared/src/lib/command.repository.ts index 88f4ac7457..83c5ec53fc 100644 --- a/alfa-client/libs/command-shared/src/lib/command.repository.ts +++ b/alfa-client/libs/command-shared/src/lib/command.repository.ts @@ -25,22 +25,13 @@ import { Injectable } from '@angular/core'; import { Resource, ResourceFactory } from '@ngxp/rest'; import { Observable } from 'rxjs'; import { CommandLinkRel } from './command.linkrel'; -import { - CommandListResource, - CommandResource, - CommandStatus, - CreateCommand, -} from './command.model'; +import { CommandListResource, CommandResource, CommandStatus, CreateCommand } from './command.model'; @Injectable({ providedIn: 'root' }) export class CommandRepository { constructor(private resourceFactory: ResourceFactory) {} - public createCommand( - resource: Resource, - linkrel: string, - command: CreateCommand, - ): Observable<CommandResource> { + public createCommand(resource: Resource, linkrel: string, command: CreateCommand): Observable<CommandResource> { return this.resourceFactory.from(resource).post(linkrel, command); } @@ -48,14 +39,12 @@ export class CommandRepository { return this.resourceFactory.from(resource).get(CommandLinkRel.SELF); } - public getPendingCommands(resource: Resource, linkRel: string): Observable<CommandListResource> { + public getCommands(resource: Resource, linkRel: string): Observable<CommandListResource> { return this.resourceFactory.from(resource).get(linkRel); } public revokeCommand(resource: CommandResource): Observable<CommandResource> { - return this.resourceFactory - .from(resource) - .patch(CommandLinkRel.REVOKE, { status: CommandStatus.REVOKED }); + return this.resourceFactory.from(resource).patch(CommandLinkRel.REVOKE, { status: CommandStatus.REVOKED }); } public getEffectedResource<T>(command: CommandResource): Observable<T> { diff --git a/alfa-client/libs/command-shared/src/lib/command.service.spec.ts b/alfa-client/libs/command-shared/src/lib/command.service.spec.ts index 4de92f2b51..44237dadab 100644 --- a/alfa-client/libs/command-shared/src/lib/command.service.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/command.service.spec.ts @@ -22,6 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { + LinkRelationName, StateResource, createEmptyStateResource, createErrorStateResource, @@ -32,7 +33,7 @@ import { SnackBarService } from '@alfa-client/ui'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { faker } from '@faker-js/faker'; import { Store } from '@ngrx/store'; -import { Resource } from '@ngxp/rest'; +import { Resource, ResourceUri, getUrl } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { createCommand, @@ -42,23 +43,13 @@ import { createCreateCommandProps, } from 'libs/command-shared/test/command'; import { createHttpErrorResponse } from 'libs/tech-shared/test/http'; -import { toResource } from 'libs/tech-shared/test/resource'; +import { createDummyResource, toResource } from 'libs/tech-shared/test/resource'; import { Observable, Subject, of, throwError } from 'rxjs'; import { CommandLinkRel } from './command.linkrel'; import { CommandErrorMessage } from './command.message'; -import { - Command, - CommandListResource, - CommandOrder, - CommandResource, - CreateCommandProps, -} from './command.model'; +import { Command, CommandListResource, CommandOrder, CommandResource, CreateCommandProps } from './command.model'; import { CommandRepository } from './command.repository'; -import { - CommandService, - IntervallHandleWithTickObservable, - startInterval, -} from './command.service'; +import { CommandService, IntervallHandleWithTickObservable, startInterval } from './command.service'; import * as Actions from './+state/command.actions'; import * as Selectors from './+state/command.selectors'; @@ -76,9 +67,7 @@ describe('CommandService', () => { const commandResource: CommandResource = createCommandResource(); const commandStateResource: StateResource<CommandResource> = createStateResource(commandResource); - const commandResourceWithUpdateLink: CommandResource = createCommandResource([ - CommandLinkRel.UPDATE, - ]); + const commandResourceWithUpdateLink: CommandResource = createCommandResource([CommandLinkRel.UPDATE]); beforeEach(() => { store = mock(Store); @@ -87,11 +76,7 @@ describe('CommandService', () => { store.dispatch = jest.fn(); repository = mock(CommandRepository); - service = new CommandService( - useFromMock(repository), - useFromMock(snackbarService), - useFromMock(<any>store), - ); + service = new CommandService(useFromMock(repository), useFromMock(snackbarService), useFromMock(<any>store)); }); describe('create command', () => { @@ -162,17 +147,13 @@ describe('CommandService', () => { beforeEach(() => { repository.revokeCommand.mockReturnValue(cold('a', { a: commandResourceWithUpdateLink })); service.pollCommand = jest.fn(); - (<any>service.pollCommand).mockReturnValue( - cold('a', { a: createStateResource(commandResourceWithUpdateLink, true) }), - ); + (<any>service.pollCommand).mockReturnValue(cold('a', { a: createStateResource(commandResourceWithUpdateLink, true) })); }); it('should return value with loading true', () => { const result = service.revokeCommand(commandResource); - expect(result).toBeObservable( - hot('a', { a: createStateResource(commandResourceWithUpdateLink, true) }), - ); + expect(result).toBeObservable(hot('a', { a: createStateResource(commandResourceWithUpdateLink, true) })); }); }); @@ -189,6 +170,7 @@ describe('CommandService', () => { it.skip('should call handleCommandError', () => { service.handleCommandError = jest.fn(); const commandWithError: CommandResource = createCommandErrorResource(); + service.handleCommand(commandWithError); expect(service.handleCommandError).toHaveBeenCalledWith(commandWithError); @@ -270,9 +252,7 @@ describe('CommandService', () => { it('should return stateResource still loading', () => { const result = service.getAndUpdate(commandResource); - expect(result).toBeObservable( - hot('a', { a: createStateResource(commandResourceWithUpdateLink, true) }), - ); + expect(result).toBeObservable(hot('a', { a: createStateResource(commandResourceWithUpdateLink, true) })); }); describe('command is loaded', () => { @@ -327,20 +307,17 @@ describe('CommandService', () => { }); }); - describe('get pending commands', () => { + describe('get commands', () => { const commandListResource: CommandListResource = createCommandListResource(); beforeEach(() => { - repository.getPendingCommands.mockReturnValue(cold('a', { a: commandListResource })); + repository.getCommands.mockReturnValue(cold('a', { a: commandListResource })); }); it('should call repository', () => { - service.getPendingCommands(commandResource, CommandLinkRel.SELF); + service.getCommands(commandResource, CommandLinkRel.SELF); - expect(repository.getPendingCommands).toHaveBeenLastCalledWith( - commandResource, - CommandLinkRel.SELF, - ); + expect(repository.getCommands).toHaveBeenLastCalledWith(commandResource, CommandLinkRel.SELF); }); }); @@ -380,10 +357,12 @@ describe('CommandService', () => { }); describe('createCommandByProps', () => { - const createCommandProps: CreateCommandProps = { ...createCreateCommandProps(), command }; + const linkRel: LinkRelationName = faker.string.alpha(6); + const resource: Resource = createDummyResource([linkRel]); + const createCommandProps: CreateCommandProps = { ...createCreateCommandProps(), command, resource, linkRel }; beforeEach(() => { - service.getCommandByOrder = jest.fn().mockReturnValue(of(commandStateResource)); + service._getCommand = jest.fn().mockReturnValue(of(commandStateResource)); }); it('should dispatch action', () => { @@ -394,7 +373,7 @@ describe('CommandService', () => { it('should call get command by order', (done) => { service.createCommandByProps(createCommandProps).subscribe(() => { - expect(service.getCommandByOrder).toHaveBeenCalledWith(command.order); + expect(service._getCommand).toHaveBeenCalledWith(command.order, getUrl(resource, linkRel)); done(); }); @@ -411,12 +390,14 @@ describe('CommandService', () => { }); }); - describe('get command by order', () => { + describe('get command', () => { + const uri: ResourceUri = faker.internet.url(); + it('should select from store', (done) => { - const selectorSpy = jest.spyOn(Selectors, 'commandByOrder'); + const selectorSpy = jest.spyOn(Selectors, 'command'); - service.getCommandByOrder(CommandOrder.VORGANG_ANNEHMEN).subscribe(() => { - expect(selectorSpy).toHaveBeenCalledWith(CommandOrder.VORGANG_ANNEHMEN); + service._getCommand(CommandOrder.VORGANG_ANNEHMEN, uri).subscribe(() => { + expect(selectorSpy).toHaveBeenCalledWith(CommandOrder.VORGANG_ANNEHMEN, uri); done(); }); @@ -424,7 +405,7 @@ describe('CommandService', () => { }); it('should return value', (done) => { - service.getCommandByOrder(CommandOrder.VORGANG_ANNEHMEN).subscribe((selected) => { + service._getCommand(CommandOrder.VORGANG_ANNEHMEN, uri).subscribe((selected) => { expect(selected).toBe(commandStateResource); done(); }); @@ -436,7 +417,7 @@ describe('CommandService', () => { store.select.mockReturnValue(of(null)); let success: boolean = true; - service.getCommandByOrder(<CommandOrder>'anyOrder').subscribe({ + service._getCommand(<CommandOrder>'anyOrder', uri).subscribe({ next: (commandStateResource: StateResource<CommandResource>) => { success = commandStateResource !== null; }, diff --git a/alfa-client/libs/command-shared/src/lib/command.service.ts b/alfa-client/libs/command-shared/src/lib/command.service.ts index 8c4a5ec1a7..8e035ec94f 100644 --- a/alfa-client/libs/command-shared/src/lib/command.service.ts +++ b/alfa-client/libs/command-shared/src/lib/command.service.ts @@ -32,18 +32,12 @@ import { SnackBarService } from '@alfa-client/ui'; import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Resource } from '@ngxp/rest'; +import { getUrl, Resource, ResourceUri } from '@ngxp/rest'; import { Observable, of, Subject, throwError } from 'rxjs'; import { catchError, filter, map, mergeMap, tap } from 'rxjs/operators'; import { CommandEffects } from './+state/command.effects'; import { COMMAND_ERROR_MESSAGES } from './command.message'; -import { - CommandListResource, - CommandOrder, - CommandResource, - CreateCommand, - CreateCommandProps, -} from './command.model'; +import { CommandListResource, CommandOrder, CommandResource, CreateCommand, CreateCommandProps } from './command.model'; import { CommandRepository } from './command.repository'; import { isConcurrentModification, isPending } from './command.util'; @@ -63,11 +57,7 @@ export class CommandService { /** * @deprecated Use createCommandByProps(createCommandProps: CreateCommandProps) instead. */ - public createCommand( - resource: Resource, - linkRel: string, - command: CreateCommand, - ): Observable<StateResource<CommandResource>> { + public createCommand(resource: Resource, linkRel: string, command: CreateCommand): Observable<StateResource<CommandResource>> { return this.handleCommandResponse(this.repository.createCommand(resource, linkRel, command)); } @@ -75,9 +65,7 @@ export class CommandService { return this.handleCommandResponse(this.repository.revokeCommand(resource)); } - private handleCommandResponse( - command$: Observable<CommandResource>, - ): Observable<StateResource<CommandResource>> { + private handleCommandResponse(command$: Observable<CommandResource>): Observable<StateResource<CommandResource>> { return command$.pipe( mergeMap((command) => this.handleCommand(command)), catchError((errorResponse) => this.handleHttpError(errorResponse)), @@ -99,6 +87,7 @@ export class CommandService { return this.startPolling(command); } + // TODO: Pruefen, ob die Funktion noch gebraucht wird handleCommandError(command: CommandResource): Observable<StateResource<CommandResource>> { if (isConcurrentModification(command.errorMessage)) { this.snackBarService.showError(COMMAND_ERROR_MESSAGES[command.errorMessage]); @@ -106,11 +95,10 @@ export class CommandService { } return of(createStateResource(command)); } + // startPolling(commandResource: CommandResource): Observable<StateResource<CommandResource>> { - return isPending(commandResource) ? - this.pollCommand(commandResource) - : of(createStateResource(commandResource)); + return isPending(commandResource) ? this.pollCommand(commandResource) : of(createStateResource(commandResource)); } public pollCommand(commandResource: CommandResource): Observable<StateResource<CommandResource>> { @@ -121,10 +109,7 @@ export class CommandService { ); } - handleInterval( - stateResource: StateResource<CommandResource>, - interval: IntervallHandleWithTickObservable, - ): void { + handleInterval(stateResource: StateResource<CommandResource>, interval: IntervallHandleWithTickObservable): void { if (!stateResource.loading) this.clearInterval(interval.handle); } @@ -142,23 +127,24 @@ export class CommandService { window.clearInterval(handler); } - public getPendingCommands(resource: Resource, linkRel: string): Observable<CommandListResource> { - return this.repository.getPendingCommands(resource, linkRel); + //TODO Pruefen, ob die Funktion noch gebraucht wird + public getCommands(resource: Resource, linkRel: string): Observable<CommandListResource> { + return this.repository.getCommands(resource, linkRel); } - + // + //TODO Pruefen, ob die Funktion noch gebraucht wird public getEffectedResource<T>(command: CommandResource): Observable<T> { return this.repository.getEffectedResource(command); } + // - public createCommandByProps( - createCommandProps: CreateCommandProps, - ): Observable<StateResource<CommandResource>> { + public createCommandByProps(createCommandProps: CreateCommandProps): Observable<StateResource<CommandResource>> { this.store.dispatch(Actions.createCommand(createCommandProps)); - return this.getCommandByOrder(createCommandProps.command.order); + return this._getCommand(createCommandProps.command.order, getUrl(createCommandProps.resource, createCommandProps.linkRel)); } - public getCommandByOrder(order: CommandOrder): Observable<StateResource<CommandResource>> { - return this.store.select(Selectors.commandByOrder(order)).pipe(filter(isNotNil)); + _getCommand(order: CommandOrder, uri: ResourceUri): Observable<StateResource<CommandResource>> { + return this.store.select(Selectors.command(order, uri)).pipe(filter(isNotNil)); } } diff --git a/alfa-client/libs/command-shared/src/lib/command.util.spec.ts b/alfa-client/libs/command-shared/src/lib/command.util.spec.ts index d8dc15892b..f3cd21cee6 100644 --- a/alfa-client/libs/command-shared/src/lib/command.util.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/command.util.spec.ts @@ -21,11 +21,23 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { createCommandErrorResource, createCommandListResource, createCommandResource, } from 'libs/command-shared/test/command'; +import { createStateResource } from '@alfa-client/tech-shared'; +import { getUrl } from '@ngxp/rest'; +import { createCommandErrorResource, createCommandListResource, createCommandResource } from 'libs/command-shared/test/command'; import { CommandLinkRel } from './command.linkrel'; import { CommandErrorMessage } from './command.message'; -import { CommandListResource, CommandResource } from './command.model'; -import { getPendingCommandByOrder, hasCommandError, isConcurrentModification, isDone, isPending, isRevokeable, isSuccessfulDone, notHasCommandError, } from './command.util'; +import { CommandListResource, CommandResource, CommandStatus } from './command.model'; +import { + buildPendingCommandMap, + getPendingCommandByOrder, + hasCommandError, + isConcurrentModification, + isDone, + isPending, + isRevokeable, + isSuccessfulDone, + notHasCommandError, +} from './command.util'; describe('CommandUtil', () => { describe('isRevokeable', () => { @@ -112,20 +124,34 @@ describe('CommandUtil', () => { const command: CommandResource = { ...createCommandResource(), order: anotherOrder }; const listResource: CommandListResource = createCommandListResource([command]); - const pendingCommand: CommandResource = getPendingCommandByOrder(listResource, [ - order, - anotherOrder, - ]); + const pendingCommand: CommandResource = getPendingCommandByOrder(listResource, [order, anotherOrder]); expect(pendingCommand).toBe(command); }); }); + describe('build pending command map', () => { + it('should return map', () => { + const pendingCommand1: CommandResource = { + ...createCommandResource([CommandLinkRel.RELATED_RESOURCE]), + status: CommandStatus.PENDING, + }; + const pendingCommand2: CommandResource = { + ...createCommandResource([CommandLinkRel.RELATED_RESOURCE]), + status: CommandStatus.PENDING, + }; + const commandList: CommandListResource = createCommandListResource([pendingCommand1, pendingCommand2]); + + const map = buildPendingCommandMap(commandList); + + expect(map[getUrl(pendingCommand1, CommandLinkRel.RELATED_RESOURCE)]).toEqual(createStateResource(pendingCommand1)); + expect(map[getUrl(pendingCommand2, CommandLinkRel.RELATED_RESOURCE)]).toEqual(createStateResource(pendingCommand2)); + }); + }); + describe('isConcurrentModification', () => { it('should return true on matching error message', () => { - const doesMatch: boolean = isConcurrentModification( - CommandErrorMessage.CONCURRENT_MODIFICATION, - ); + const doesMatch: boolean = isConcurrentModification(CommandErrorMessage.CONCURRENT_MODIFICATION); expect(doesMatch).toBeTruthy(); }); diff --git a/alfa-client/libs/command-shared/src/lib/command.util.ts b/alfa-client/libs/command-shared/src/lib/command.util.ts index 70e446bee6..820a223daa 100644 --- a/alfa-client/libs/command-shared/src/lib/command.util.ts +++ b/alfa-client/libs/command-shared/src/lib/command.util.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { createStateResource } from '@alfa-client/tech-shared'; import { getEmbeddedResource, getUrl, hasLink, ResourceUri } from '@ngxp/rest'; import { isEmpty, isNil, isObject } from 'lodash-es'; import { CommandLinkRel, CommandListLinkRel } from './command.linkrel'; @@ -51,21 +52,26 @@ export function notHasCommandError(commandResource: CommandResource): boolean { return !hasCommandError(commandResource); } -export function getPendingCommandByOrder( - pendingCommands: CommandListResource, - commandOrder: any[], -): CommandResource { - const commands: CommandResource[] = getEmbeddedCommandResources(pendingCommands).filter( - (command) => commandOrder.includes(command.order), +export function getPendingCommandByOrder(pendingCommands: CommandListResource, commandOrder: any[]): CommandResource { + const commands: CommandResource[] = getEmbeddedCommandResources(pendingCommands).filter((command: CommandResource) => + commandOrder.includes(command.order), ); return commands.length > 0 ? commands[0] : null; } +export function buildPendingCommandMap(commandList: CommandListResource): any { + let map = {}; + const commands: CommandResource[] = getEmbeddedCommandResources(commandList); + commands.forEach((command: CommandResource) => { + if (hasLink(command, CommandLinkRel.RELATED_RESOURCE)) { + map[getUrl(command, CommandLinkRel.RELATED_RESOURCE)] = createStateResource(command); + } + }); + return map; +} + function getEmbeddedCommandResources(commandListResource: CommandListResource): CommandResource[] { - return getEmbeddedResource<CommandResource[]>( - commandListResource, - CommandListLinkRel.COMMAND_LIST, - ); + return getEmbeddedResource<CommandResource[]>(commandListResource, CommandListLinkRel.COMMAND_LIST); } export function doIfCommandIsDone(commandResource: CommandResource, action: () => void) { diff --git a/alfa-client/libs/command-shared/test/command.ts b/alfa-client/libs/command-shared/test/command.ts index 05fec9de48..976d353de2 100644 --- a/alfa-client/libs/command-shared/test/command.ts +++ b/alfa-client/libs/command-shared/test/command.ts @@ -36,6 +36,7 @@ import { CreateCommand, CreateCommandProps, CreateCommandPropsWithoutResource, + PendingCommandMap, } from '../src/lib/command.model'; export function createCommand(): Command { @@ -104,3 +105,7 @@ export function createSuccessfullyDoneCommandStateResource(): StateResource<Comm export function createSuccessfullyDoneCommandResource(): CommandResource { return createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); } + +export function createPendingCommandMap(): PendingCommandMap { + return { [faker.internet.url()]: createCommandStateResource() }; +} diff --git a/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts b/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts index 492502ba27..5c12de2fd8 100644 --- a/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts +++ b/alfa-client/libs/postfach-shared/src/lib/postfach.service.spec.ts @@ -31,10 +31,12 @@ import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang import { TestBed } from '@angular/core/testing'; import { MatDialog } from '@angular/material/dialog'; import { expect } from '@jest/globals'; +import { getUrl } from '@ngxp/rest'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; import { createCommandErrorResource, createCommandResource } from 'libs/command-shared/test/command'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { BehaviorSubject, of } from 'rxjs'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { singleColdCompleted } from '../../../tech-shared/test/marbles'; import { createPostfachFeatures, createPostfachMail, @@ -632,4 +634,35 @@ describe('PostfachService', () => { }); }); }); + + describe('get pending resend command', () => { + const postfachNachricht: PostfachMailResource = createPostfachMailResource(); + + const command: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); + const commandStateResource: StateResource<CommandResource> = createStateResource(command); + + beforeEach(() => { + vorgangService.getAndPollPendingCommand.mockReturnValue(of(commandStateResource)); + service._refreshPostfachMailList = jest.fn(); + }); + + it('should call vorgang service', () => { + service.getPendingResendCommand(postfachNachricht); + + expect(vorgangService.getAndPollPendingCommand).toHaveBeenCalledWith(getUrl(postfachNachricht)); + }); + + it('should call refresh list on command successfully done', () => { + service.getPendingResendCommand(postfachNachricht).subscribe(); + + expect(service._refreshPostfachMailList).toHaveBeenCalled(); + }); + + it('should return value', () => { + const pendingResendCommand$: Observable<StateResource<CommandResource>> = + service.getPendingResendCommand(postfachNachricht); + + expect(pendingResendCommand$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + }); }); diff --git a/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts b/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts index b7fbfbe62d..a5dbb53f46 100644 --- a/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts +++ b/alfa-client/libs/postfach-shared/src/lib/postfach.service.ts @@ -30,6 +30,7 @@ import { hasCommandError, isDone, isPending, + tapOnCommandSuccessfullyDone, } from '@alfa-client/command-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; import { @@ -46,7 +47,7 @@ import { VorgangResource, VorgangService, VorgangWithEingangResource } from '@al import { inject, Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Params } from '@angular/router'; -import { hasLink, Resource } from '@ngxp/rest'; +import { getUrl, hasLink, Resource } from '@ngxp/rest'; import { isNil, isNull } from 'lodash-es'; import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; import { first, map, take, tap } from 'rxjs/operators'; @@ -75,11 +76,12 @@ export class PostfachService { private readonly postfachFacade = inject(PostfachFacade); private readonly binaryFileService = inject(BinaryFileService); - private readonly isPollSendPostachMail: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); postfachMailList$: BehaviorSubject<StateResource<PostfachMailListResource>> = new BehaviorSubject< StateResource<PostfachMailListResource> >(createEmptyStateResource<PostfachMailListResource>()); + private readonly isPollSendPostachMail: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); + private navigationSubscription: Subscription; private sendPostfachMailSubscription: Subscription; @@ -160,6 +162,14 @@ export class PostfachService { .pipe(map((pendingCommand) => this._pollSendPostfachMailCommand(pendingCommand))); } + public getPendingResendCommand(postfachNachricht: PostfachMailResource): Observable<StateResource<CommandResource>> { + return this.vorgangService.getAndPollPendingCommand(getUrl(postfachNachricht)).pipe( + tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => { + this._refreshPostfachMailList(commandStateResource); + }), + ); + } + _listenToNavigation(): void { this._unsubscribeToNavigation(); this.navigationSubscription = this.navigationService.urlChanged().subscribe((params) => this._onNavigation(params)); diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts index 20855cb121..c531c4853a 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.spec.ts @@ -22,11 +22,11 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { CommandResource } from '@alfa-client/command-shared'; -import { PostfachMailLinkRel, PostfachService } from '@alfa-client/postfach-shared'; +import { PostfachMailLinkRel, PostfachMailResource, PostfachService } from '@alfa-client/postfach-shared'; import { createEmptyStateResource, createStateResource, HasLinkPipe, StateResource } from '@alfa-client/tech-shared'; import { existsAsHtmlElement, Mock, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { createCommandResource } from 'libs/command-shared/test/command'; +import { createCommandResource, createCommandStateResource } from 'libs/command-shared/test/command'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; import { of } from 'rxjs'; @@ -39,19 +39,23 @@ describe('OutgoingMailErrorContainerComponent', () => { let component: OutgoingMailErrorContainerComponent; let fixture: ComponentFixture<OutgoingMailErrorContainerComponent>; - let postfachService: Mock<PostfachService>; + let service: Mock<PostfachService>; const mailError: string = getDataTestIdOf('outgoing-mail-error'); + const postfachNachricht: PostfachMailResource = createPostfachMailResource(); + + const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); + beforeEach(async () => { - postfachService = mock(PostfachService); + service = mock(PostfachService); await TestBed.configureTestingModule({ declarations: [OutgoingMailErrorContainerComponent, MockComponent(OutgoingMailErrorComponent), HasLinkPipe], providers: [ { provide: PostfachService, - useValue: postfachService, + useValue: service, }, ], }).compileComponents(); @@ -60,6 +64,7 @@ describe('OutgoingMailErrorContainerComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(OutgoingMailErrorContainerComponent); component = fixture.componentInstance; + component.postfachMailResource = postfachNachricht; fixture.detectChanges(); }); @@ -67,17 +72,35 @@ describe('OutgoingMailErrorContainerComponent', () => { expect(component).toBeTruthy(); }); + describe('on init', () => { + beforeEach(() => { + service.getPendingResendCommand.mockReturnValue(of(commandStateResource)); + }); + + it('should call postfach service to get pending resend command', () => { + component.ngOnInit(); + + expect(service.getPendingResendCommand).toHaveBeenCalledWith(postfachNachricht); + }); + + it('should assign response', () => { + component.ngOnInit(); + + expect(component.resendPostfachMailStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + }); + describe('resendMail', () => { const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); beforeEach(() => { - postfachService.resendMail.mockReturnValue(of(commandStateResource)); + service.resendMail.mockReturnValue(of(commandStateResource)); }); it('should call postfach service', () => { component.resendMail(); - expect(postfachService.resendMail).toHaveBeenCalled(); + expect(service.resendMail).toHaveBeenCalled(); }); it('should assign service response', () => { diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts index c0d8d6589d..221f5d478b 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail-error-container/outgoing-mail-error-container.component.ts @@ -41,8 +41,8 @@ export class OutgoingMailErrorContainerComponent implements OnInit { public readonly PostfachMailLinkRel = PostfachMailLinkRel; - ngOnInit() { - this.resendPostfachMailStateResource$ = this.postfachService.getPendingSendPostfachMailCommand(); + ngOnInit(): void { + this.resendPostfachMailStateResource$ = this.postfachService.getPendingResendCommand(this.postfachMailResource); } public resendMail(): void { diff --git a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts index 45eb6a8ae7..e22edf8965 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts @@ -21,25 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpErrorResponse } from '@angular/common/http'; import { ApiRootResource } from '@alfa-client/api-root-shared'; import { LoadBinaryFileListSuccessProps } from '@alfa-client/binary-file-shared'; -import { - CommandStateResourceProps, - LoadCommandListSuccessProps, -} from '@alfa-client/command-shared'; -import { - ApiErrorAction, - ResourceUriProps, - TypedActionCreator, - TypedActionCreatorWithProps, -} from '@alfa-client/tech-shared'; +import { CommandStateResourceProps, LoadCommandListSuccessProps, UpdateCommandProps } from '@alfa-client/command-shared'; +import { ApiErrorAction, ResourceUriProps, TypedActionCreator, TypedActionCreatorWithProps } from '@alfa-client/tech-shared'; +import { HttpErrorResponse } from '@angular/common/http'; import { createAction, props } from '@ngrx/store'; -import { - AdditionalActions, - VorgangListResource, - VorgangWithEingangResource, -} from '../vorgang.model'; +import { AdditionalActions, VorgangListResource, VorgangWithEingangResource } from '../vorgang.model'; export interface SearchVorgaengeByProps { apiRoot: ApiRootResource; @@ -96,16 +84,16 @@ export const searchVorgaengeBy: TypedActionCreatorWithProps<SearchVorgaengeByPro '[Vorgang] Search VorgangList', props<SearchVorgaengeByProps>(), ); -export const searchVorgaengeBySuccess: TypedActionCreatorWithProps<VorgangListAction> = - createAction('[Vorgang] Search VorgangList Success', props<VorgangListAction>()); +export const searchVorgaengeBySuccess: TypedActionCreatorWithProps<VorgangListAction> = createAction( + '[Vorgang] Search VorgangList Success', + props<VorgangListAction>(), +); export const searchVorgaengeByFailure: TypedActionCreatorWithProps<HttpErrorAction> = createAction( '[Vorgang] Search VorgangList Failure', props<HttpErrorAction>(), ); -export const loadNextPage: TypedActionCreator = createAction( - '[Vorgang] Load next VorgangList page', -); +export const loadNextPage: TypedActionCreator = createAction('[Vorgang] Load next VorgangList page'); export const loadNextPageSuccess: TypedActionCreatorWithProps<VorgangListAction> = createAction( '[Vorgang] Load next VorgangList page Success', props<VorgangListAction>(), @@ -125,9 +113,7 @@ export const searchForPreviewFailure: TypedActionCreatorWithProps<HttpErrorActio props<HttpErrorAction>(), ); -export const clearSearchPreviewList: TypedActionCreator = createAction( - '[Vorgang] Clear search preview list', -); +export const clearSearchPreviewList: TypedActionCreator = createAction('[Vorgang] Clear search preview list'); export const clearSearchString: TypedActionCreator = createAction('[Vorgang] Clear search string'); export const setSearchString: TypedActionCreatorWithProps<StringBasedProps> = createAction( @@ -136,56 +122,70 @@ export const setSearchString: TypedActionCreatorWithProps<StringBasedProps> = cr ); //VorgangWithEingang -export const loadVorgangWithEingang: TypedActionCreatorWithProps<VorgangUriWithContextProps> = - createAction('[Vorgang] Load VorgangWithEingang', props<VorgangUriWithContextProps>()); -export const loadVorgangWithEingangSuccess: TypedActionCreatorWithProps<VorgangWithEingangAction> = - createAction('[Vorgang] Load VorgangWithEingang Success', props<VorgangWithEingangAction>()); -export const loadVorgangWithEingangFailure: TypedActionCreatorWithProps<ApiErrorAction> = - createAction('[Vorgang] Load VorgangWithEingang Failure', props<ApiErrorAction>()); - -export const clearVorgangWithEingang: TypedActionCreator = createAction( - '[Vorgang] Clear VorgangWithEingang', -); -export const setReloadVorgangWithEingang: TypedActionCreator = createAction( - '[Vorgang] Set reload at VorgangWithEingang', -); - -export const loadPendingCommandList: TypedActionCreatorWithProps<VorgangWithEingangAction> = - createAction('[Vorgang] Load pending command list', props<VorgangWithEingangAction>()); -export const loadPendingCommandListSuccess: TypedActionCreatorWithProps<LoadCommandListSuccessProps> = - createAction( - '[Vorgang/API] Load pending command list Success', - props<LoadCommandListSuccessProps>(), - ); -export const loadPendingCommandListFailure: TypedActionCreatorWithProps<ApiErrorAction> = - createAction('[Vorgang/API] Load pending command list Failure', props<ApiErrorAction>()); - -export const loadAttachmentList: TypedActionCreatorWithProps<VorgangWithEingangAction> = - createAction('[Vorgang] Load AttachmentList', props<VorgangWithEingangAction>()); -export const loadAttachmentListSuccess: TypedActionCreatorWithProps<LoadBinaryFileListSuccessProps> = - createAction('[Vorgang] Load AttachmentList Success', props<LoadBinaryFileListSuccessProps>()); +export const loadVorgangWithEingang: TypedActionCreatorWithProps<VorgangUriWithContextProps> = createAction( + '[Vorgang] Load VorgangWithEingang', + props<VorgangUriWithContextProps>(), +); +export const loadVorgangWithEingangSuccess: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction( + '[Vorgang] Load VorgangWithEingang Success', + props<VorgangWithEingangAction>(), +); +export const loadVorgangWithEingangFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction( + '[Vorgang] Load VorgangWithEingang Failure', + props<ApiErrorAction>(), +); + +export const clearVorgangWithEingang: TypedActionCreator = createAction('[Vorgang] Clear VorgangWithEingang'); +export const setReloadVorgangWithEingang: TypedActionCreator = createAction('[Vorgang] Set reload at VorgangWithEingang'); + +export const loadPendingCommandList: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction( + '[Vorgang] Load pending command list', + props<VorgangWithEingangAction>(), +); +export const loadPendingCommandListSuccess: TypedActionCreatorWithProps<LoadCommandListSuccessProps> = createAction( + '[Vorgang/API] Load pending command list Success', + props<LoadCommandListSuccessProps>(), +); +export const loadPendingCommandListFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction( + '[Vorgang/API] Load pending command list Failure', + props<ApiErrorAction>(), +); + +export const loadAttachmentList: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction( + '[Vorgang] Load AttachmentList', + props<VorgangWithEingangAction>(), +); +export const loadAttachmentListSuccess: TypedActionCreatorWithProps<LoadBinaryFileListSuccessProps> = createAction( + '[Vorgang] Load AttachmentList Success', + props<LoadBinaryFileListSuccessProps>(), +); export const loadAttachmentListFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction( '[Vorgang] Load AttachmentList Failure', props<ApiErrorAction>(), ); -export const loadRepresentationList: TypedActionCreatorWithProps<VorgangWithEingangAction> = - createAction('[Vorgang] Load RepresentationList', props<VorgangWithEingangAction>()); -export const loadRepresentationListSuccess: TypedActionCreatorWithProps<LoadBinaryFileListSuccessProps> = - createAction( - '[Vorgang] Load RepresentationList Success', - props<LoadBinaryFileListSuccessProps>(), - ); -export const loadRepresentationListFailure: TypedActionCreatorWithProps<ApiErrorAction> = - createAction('[Vorgang] Load RepresentationList Failure', props<ApiErrorAction>()); +export const loadRepresentationList: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction( + '[Vorgang] Load RepresentationList', + props<VorgangWithEingangAction>(), +); +export const loadRepresentationListSuccess: TypedActionCreatorWithProps<LoadBinaryFileListSuccessProps> = createAction( + '[Vorgang] Load RepresentationList Success', + props<LoadBinaryFileListSuccessProps>(), +); +export const loadRepresentationListFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction( + '[Vorgang] Load RepresentationList Failure', + props<ApiErrorAction>(), +); -export const setForwardingSingleCommand: TypedActionCreatorWithProps<CommandStateResourceProps> = - createAction('[Vorgang] Set forward command', props<CommandStateResourceProps>()); -export const setForwardingSingleCommandLoading: TypedActionCreator = createAction( - '[Vorgang] Set forward command loading', +export const setForwardingSingleCommand: TypedActionCreatorWithProps<CommandStateResourceProps> = createAction( + '[Vorgang] Set forward command', + props<CommandStateResourceProps>(), +); +export const setForwardingSingleCommandLoading: TypedActionCreator = createAction('[Vorgang] Set forward command loading'); +export const setSendPostfachNachrichtSingleCommand: TypedActionCreatorWithProps<CommandStateResourceProps> = createAction( + '[Vorgang] Set send postfach nachricht command', + props<CommandStateResourceProps>(), ); -export const setSendPostfachNachrichtSingleCommand: TypedActionCreatorWithProps<CommandStateResourceProps> = - createAction('[Vorgang] Set send postfach nachricht command', props<CommandStateResourceProps>()); export const setSendPostfachNachrichtSingleCommandLoading: TypedActionCreator = createAction( '[Vorgang] Set send postfach nachricht command loading', ); @@ -201,3 +201,13 @@ export const exportVorgangFailure: TypedActionCreatorWithProps<ApiErrorAction> = '[Vorgang] Export Failure', props<ApiErrorAction>(), ); + +export const setPendingCommandLoading: TypedActionCreatorWithProps<ResourceUriProps> = createAction( + '[Vorgang] Set pending command loading', + props<ResourceUriProps>(), +); + +export const updatePendingCommand: TypedActionCreatorWithProps<UpdateCommandProps> = createAction( + '[Vorgang] Update pending command', + props<UpdateCommandProps>(), +); diff --git a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts index c2e342d93c..70e6360f76 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts @@ -23,7 +23,14 @@ */ import { ApiRootResource } from '@alfa-client/api-root-shared'; import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { CommandListResource, CommandOrder, CommandResource, CreateCommand } from '@alfa-client/command-shared'; +import { + CommandListResource, + CommandOrder, + CommandResource, + CreateCommand, + CreateCommandProps, + PendingCommandMap, +} from '@alfa-client/command-shared'; import { RouteData } from '@alfa-client/navigation-shared'; import { ApiError, @@ -35,11 +42,19 @@ import { } from '@alfa-client/tech-shared'; import { HttpErrorResponse } from '@angular/common/http'; import { UrlSegment } from '@angular/router'; +import { faker } from '@faker-js/faker'; import { Action } from '@ngrx/store'; import { Resource, ResourceUri } from '@ngxp/rest'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; -import { createCommand, createCommandListResource, createCommandResource } from 'libs/command-shared/test/command'; +import { + createCommand, + createCommandListResource, + createCommandResource, + createCommandStateResource, + createCreateCommandProps, + createPendingCommandMap, +} from 'libs/command-shared/test/command'; import { createRouteData } from 'libs/navigation-shared/test/navigation-test-factory'; import { createDummyResource } from 'libs/tech-shared/test/resource'; import { @@ -70,11 +85,10 @@ import { import { VorgangListAction } from './vorgang.actions'; import { VorgangState, initialState, reducer } from './vorgang.reducer'; -import { faker } from '@faker-js/faker'; - import * as Storage from '@alfa-client/app-shared'; import * as CommandActions from '@alfa-client/command-shared'; import * as NavigationActions from '@alfa-client/navigation-shared'; +import * as CommandUtil from '../../../../command-shared/src/lib/command.util'; import * as VorgangActions from './vorgang.actions'; import * as Reducer from './vorgang.reducer'; @@ -508,6 +522,52 @@ describe('Vorgang Reducer', () => { expect(state.sendPostfachNachrichtPendingCommand.resource).toBe(sendPostfachNachrichtCommand); }); + + it('should call build pending command map', () => { + const buildPendingCommandMapSpy: jest.SpyInstance = jest.spyOn(CommandUtil, 'buildPendingCommandMap'); + const commandList: CommandListResource = createCommandListResource(); + const action: Action<string> = VorgangActions.loadPendingCommandListSuccess({ commandList }); + + reducer(initialState, action); + + expect(buildPendingCommandMapSpy).toHaveBeenCalledWith(commandList); + }); + + it('should set pending commands map', () => { + const pendingCommandMap: PendingCommandMap = createPendingCommandMap(); + jest.spyOn(CommandUtil, 'buildPendingCommandMap').mockReturnValue(pendingCommandMap); + const action = VorgangActions.loadPendingCommandListSuccess({ commandList: createCommandListResource() }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.pendingCommandMap).toBe(pendingCommandMap); + }); + }); + describe('on "setPendingCommandLoading" action', () => { + it('should set map entry state resource loading by uri', () => { + const resourceUri: ResourceUri = faker.internet.url(); + const action = VorgangActions.setPendingCommandLoading({ resourceUri }); + const pendingCommandMap: PendingCommandMap = { [resourceUri]: createCommandStateResource() }; + const initialStateWithPendingCommandMap: VorgangState = { ...initialState, pendingCommandMap }; + + const state: VorgangState = reducer(initialStateWithPendingCommandMap, action); + + expect(state.pendingCommandMap[resourceUri].loading).toBeTruthy(); + }); + }); + + describe('on "updatePendingCommand" action', () => { + it('should upadte map entry state resource by uri', () => { + const relatedResourceUri: ResourceUri = faker.internet.url(); + const command: CommandResource = createCommandResource(); + const action = VorgangActions.updatePendingCommand({ relatedResourceUri, command }); + const pendingCommandMap: PendingCommandMap = { [relatedResourceUri]: { ...createCommandStateResource(), loading: true } }; + const initialStateWithPendingCommandMap: VorgangState = { ...initialState, pendingCommandMap }; + + const state: VorgangState = reducer(initialStateWithPendingCommandMap, action); + + expect(state.pendingCommandMap[relatedResourceUri]).toEqual(createStateResource(command)); + }); }); }); @@ -606,12 +666,13 @@ describe('Vorgang Reducer', () => { }); describe('createCommandSuccess', () => { + const createCommandProps: CreateCommandProps = createCreateCommandProps(); const command: CommandResource = createCommandResource(); it('should call getStatusCommandMapByCreateCommandSuccess', () => { const spy = jest.spyOn(Reducer, 'getStatusCommandMapByCreateCommandSuccess'); - const action = CommandActions.createCommandSuccess({ command }); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); reducer(initialState, action); @@ -621,7 +682,7 @@ describe('Vorgang Reducer', () => { it('should call getAssignUserCommandByCreateCommandSuccess', () => { const spy = jest.spyOn(Reducer, 'getAssignUserCommandByCreateCommandSuccess'); - const action = CommandActions.createCommandSuccess({ command }); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); reducer(initialState, action); @@ -631,7 +692,7 @@ describe('Vorgang Reducer', () => { it('should call getVorgangWithEingangStateResourceByCreateCommandSucces', () => { const spy = jest.spyOn(Reducer, 'getVorgangWithEingangStateResourceByCreateCommandSucces'); - const action = CommandActions.createCommandSuccess({ command }); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); reducer(initialState, action); diff --git a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts index 2601e8b704..f9d63437e9 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts @@ -31,12 +31,16 @@ import { CreateCommand, CreateCommandProps, LoadCommandListSuccessProps, + PendingCommandMap, + UpdateCommandProps, + buildPendingCommandMap, getPendingCommandByOrder, } from '@alfa-client/command-shared'; import { RouteData } from '@alfa-client/navigation-shared'; import { ApiErrorAction, EMPTY_STRING, + ResourceUriProps, StateResource, createEmptyStateResource, createErrorStateResource, @@ -92,6 +96,7 @@ export interface VorgangState { assignUserCommand: StateResource<CommandResource>; forwardPendingCommand: StateResource<CommandResource>; sendPostfachNachrichtPendingCommand: StateResource<CommandResource>; + pendingCommandMap: PendingCommandMap; statusCommandMap: StatusCommandMap; revokeCommand: StateResource<CommandResource>; vorgangExport: StateResource<boolean>; @@ -112,6 +117,7 @@ export const initialState: VorgangState = { assignUserCommand: createEmptyStateResource(), forwardPendingCommand: createEmptyStateResource(), sendPostfachNachrichtPendingCommand: createEmptyStateResource(), + pendingCommandMap: <any>{}, statusCommandMap: <any>{}, revokeCommand: createEmptyStateResource(), vorgangExport: createEmptyStateResource(), @@ -332,6 +338,27 @@ const vorgangReducer: ActionReducer<VorgangState, Action> = createReducer( sendPostfachNachrichtPendingCommand: createStateResource( getPendingCommandByOrder(props.commandList, [CommandOrder.SEND_POSTFACH_NACHRICHT]), ), + pendingCommandMap: buildPendingCommandMap(props.commandList), + }), + ), + on( + VorgangActions.setPendingCommandLoading, + (state: VorgangState, props: ResourceUriProps): VorgangState => ({ + ...state, + pendingCommandMap: { + ...state.pendingCommandMap, + [props.resourceUri]: { ...state.pendingCommandMap[props.resourceUri], loading: true }, + }, + }), + ), + on( + VorgangActions.updatePendingCommand, + (state: VorgangState, props: UpdateCommandProps): VorgangState => ({ + ...state, + pendingCommandMap: { + ...state.pendingCommandMap, + [props.relatedResourceUri]: createStateResource(props.command), + }, }), ), diff --git a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts index bd7c1be995..e8f4098294 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts @@ -24,8 +24,10 @@ import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { faker } from '@faker-js/faker/.'; +import { ResourceUri } from '@ngxp/rest'; import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; -import { createCommandResource } from 'libs/command-shared/test/command'; +import { createCommandResource, createCommandStateResource } from 'libs/command-shared/test/command'; import { createVorgangListResource, createVorgangResources, @@ -33,6 +35,7 @@ import { createVorgangWithEingangResource, } from 'libs/vorgang-shared/test/vorgang'; import { + PendingCommandMap, StatusCommandMap, VorgangFilter, VorgangListResource, @@ -49,42 +52,33 @@ import * as VorgangSelectors from './vorgang.selectors'; describe('Vorgang Selectors', () => { let state: VorgangPartialState; - const vorgangList: StateResource<VorgangListResource> = createStateResource( - createVorgangListResource(), - ); + const vorgangList: StateResource<VorgangListResource> = createStateResource(createVorgangListResource()); const vorgaenge: VorgangResource[] = createVorgangResources(); - const vorgangStatistic: StateResource<VorgangStatistic> = - createStateResource(createVorgangStatistic()); - const searchPreviewList: StateResource<VorgangListResource> = createStateResource( - createVorgangListResource(), - ); + const vorgangStatistic: StateResource<VorgangStatistic> = createStateResource(createVorgangStatistic()); + const searchPreviewList: StateResource<VorgangListResource> = createStateResource(createVorgangListResource()); const searchString: string = 'searchThisForMe'; const vorgangFilter: VorgangFilter = VorgangFilter.ALLE; const vorgangView: VorgangView = VorgangView.VORGANG_LIST; - const vorgangWithEingang: StateResource<VorgangWithEingangResource> = createStateResource( - createVorgangWithEingangResource(), - ); - const attachmentList: StateResource<BinaryFileListResource> = createStateResource( - createBinaryFileListResource(), - ); - const representationList: StateResource<BinaryFileListResource> = createStateResource( - createBinaryFileListResource(), - ); - const assignUserCommand: StateResource<CommandResource> = - createStateResource(createCommandResource()); - const forwardPendingCommand: StateResource<CommandResource> = - createStateResource(createCommandResource()); - const sendPostfachNachrichtPendingCommand: StateResource<CommandResource> = - createStateResource(createCommandResource()); - - const annehmenCommand: StateResource<CommandResource> = - createStateResource(createCommandResource()); + const vorgangWithEingang: StateResource<VorgangWithEingangResource> = createStateResource(createVorgangWithEingangResource()); + const attachmentList: StateResource<BinaryFileListResource> = createStateResource(createBinaryFileListResource()); + const representationList: StateResource<BinaryFileListResource> = createStateResource(createBinaryFileListResource()); + const assignUserCommand: StateResource<CommandResource> = createStateResource(createCommandResource()); + const forwardPendingCommand: StateResource<CommandResource> = createStateResource(createCommandResource()); + const sendPostfachNachrichtPendingCommand: StateResource<CommandResource> = createStateResource(createCommandResource()); + + const annehmenCommand: StateResource<CommandResource> = createStateResource(createCommandResource()); const statusCommandMap: StatusCommandMap = <any>{ [CommandOrder.VORGANG_ANNEHMEN]: annehmenCommand, }; - const revokeCommand: StateResource<CommandResource> = - createStateResource(createCommandResource()); + + const pendingCommandUri: ResourceUri = faker.internet.url(); + const pendingCommandStateResource: StateResource<CommandResource> = createCommandStateResource(); + const pendingCommandMap: PendingCommandMap = <any>{ + [pendingCommandUri]: pendingCommandStateResource, + }; + + const revokeCommand: StateResource<CommandResource> = createStateResource(createCommandResource()); const vorgangExport: StateResource<boolean> = createStateResource(false); beforeEach(() => { @@ -106,6 +100,7 @@ describe('Vorgang Selectors', () => { forwardPendingCommand, sendPostfachNachrichtPendingCommand, statusCommandMap, + pendingCommandMap, revokeCommand, vorgangExport, }, @@ -113,9 +108,7 @@ describe('Vorgang Selectors', () => { }); it('should return vorgangList', () => { - const result: StateResource<VorgangListResource> = VorgangSelectors.vorgangList.projector( - state.VorgangState, - ); + const result: StateResource<VorgangListResource> = VorgangSelectors.vorgangList.projector(state.VorgangState); expect(result).toBe(vorgangList); }); @@ -127,9 +120,7 @@ describe('Vorgang Selectors', () => { }); it('should return vorgangStatistic', () => { - const result: StateResource<VorgangStatistic> = VorgangSelectors.vorgangStatistic.projector( - state.VorgangState, - ); + const result: StateResource<VorgangStatistic> = VorgangSelectors.vorgangStatistic.projector(state.VorgangState); expect(result).toBe(vorgangStatistic); }); @@ -147,9 +138,7 @@ describe('Vorgang Selectors', () => { }); it('should return searchPreviewList', () => { - const result: StateResource<VorgangListResource> = VorgangSelectors.searchPreviewList.projector( - state.VorgangState, - ); + const result: StateResource<VorgangListResource> = VorgangSelectors.searchPreviewList.projector(state.VorgangState); expect(result).toBe(searchPreviewList); }); @@ -172,17 +161,13 @@ describe('Vorgang Selectors', () => { describe('isVorgangViewSelected', () => { it('should return true if state and view matches', () => { - const result: boolean = VorgangSelectors.isVorgangViewSelected( - VorgangView.VORGANG_LIST, - ).projector(vorgangView); + const result: boolean = VorgangSelectors.isVorgangViewSelected(VorgangView.VORGANG_LIST).projector(vorgangView); expect(result).toBeTruthy(); }); it('should return false if state and view does not match', () => { - const result: boolean = VorgangSelectors.isVorgangViewSelected(VorgangView.NEU).projector( - vorgangView, - ); + const result: boolean = VorgangSelectors.isVorgangViewSelected(VorgangView.NEU).projector(vorgangView); expect(result).toBeFalsy(); }); @@ -190,9 +175,7 @@ describe('Vorgang Selectors', () => { describe('getVorgangViewRoutePath', () => { it('should return /alle/neu', () => { - const result: string = VorgangSelectors.getVorgangViewRoutePath(VorgangView.NEU).projector( - vorgangFilter, - ); + const result: string = VorgangSelectors.getVorgangViewRoutePath(VorgangView.NEU).projector(vorgangFilter); expect(result).toBe('/alle/neu'); }); @@ -200,16 +183,14 @@ describe('Vorgang Selectors', () => { //VorgangWithEingang it('should return vorgangWithEingang', () => { - const result: StateResource<VorgangWithEingangResource> = - VorgangSelectors.vorgangWithEingang.projector(state.VorgangState); + const result: StateResource<VorgangWithEingangResource> = VorgangSelectors.vorgangWithEingang.projector(state.VorgangState); expect(result).toBe(vorgangWithEingang); }); describe('attachmentList', () => { it('should Return attachmentList from state', () => { - const result: StateResource<BinaryFileListResource> = - VorgangSelectors.attachmentList.projector(state.VorgangState); + const result: StateResource<BinaryFileListResource> = VorgangSelectors.attachmentList.projector(state.VorgangState); expect(result).toBe(attachmentList); }); @@ -217,8 +198,7 @@ describe('Vorgang Selectors', () => { describe('representationList', () => { it('should Return representationList from state', () => { - const result: StateResource<BinaryFileListResource> = - VorgangSelectors.representationList.projector(state.VorgangState); + const result: StateResource<BinaryFileListResource> = VorgangSelectors.representationList.projector(state.VorgangState); expect(result).toBe(representationList); }); @@ -226,8 +206,7 @@ describe('Vorgang Selectors', () => { describe('forwardPendingCommand', () => { it('should return command from state', () => { - const result: StateResource<CommandResource> = - VorgangSelectors.forwardPendingCommand.projector(state.VorgangState); + const result: StateResource<CommandResource> = VorgangSelectors.forwardPendingCommand.projector(state.VorgangState); expect(result).toBe(forwardPendingCommand); }); @@ -235,8 +214,9 @@ describe('Vorgang Selectors', () => { describe('send postfach command', () => { it('should return command from state', () => { - const result: StateResource<CommandResource> = - VorgangSelectors.sendPostfachNachrichtPendingCommand.projector(state.VorgangState); + const result: StateResource<CommandResource> = VorgangSelectors.sendPostfachNachrichtPendingCommand.projector( + state.VorgangState, + ); expect(result).toBe(sendPostfachNachrichtPendingCommand); }); @@ -244,9 +224,7 @@ describe('Vorgang Selectors', () => { describe('user assign command', () => { it('should return command from state', () => { - const result: StateResource<CommandResource> = VorgangSelectors.assignUserCommand.projector( - state.VorgangState, - ); + const result: StateResource<CommandResource> = VorgangSelectors.assignUserCommand.projector(state.VorgangState); expect(result).toBe(assignUserCommand); }); @@ -254,9 +232,7 @@ describe('Vorgang Selectors', () => { describe('stausCommandMap', () => { it('should select statusCommandMap from state', () => { - const result: StatusCommandMap = VorgangSelectors.statusCommandMap.projector( - state.VorgangState, - ); + const result: StatusCommandMap = VorgangSelectors.statusCommandMap.projector(state.VorgangState); expect(result).toBe(statusCommandMap); }); @@ -264,9 +240,7 @@ describe('Vorgang Selectors', () => { describe('getStatusCommand', () => { it('should return statusCommand from state by order', () => { - const result: boolean = VorgangSelectors.getStatusCommand( - CommandOrder.VORGANG_ANNEHMEN, - ).projector(statusCommandMap); + const result: boolean = VorgangSelectors.getStatusCommand(CommandOrder.VORGANG_ANNEHMEN).projector(statusCommandMap); expect(result).toBe(annehmenCommand); }); @@ -274,9 +248,7 @@ describe('Vorgang Selectors', () => { describe('revokeCommand', () => { it('should return revokeCommand from state', () => { - const result: StateResource<CommandResource> = VorgangSelectors.revokeCommand.projector( - state.VorgangState, - ); + const result: StateResource<CommandResource> = VorgangSelectors.revokeCommand.projector(state.VorgangState); expect(result).toBe(revokeCommand); }); @@ -284,11 +256,26 @@ describe('Vorgang Selectors', () => { describe('exportCommand', () => { it('should return exportCommand from state', () => { - const result: StateResource<boolean> = VorgangSelectors.vorgangExport.projector( - state.VorgangState, - ); + const result: StateResource<boolean> = VorgangSelectors.vorgangExport.projector(state.VorgangState); expect(result).toBe(vorgangExport); }); }); + + describe('pendingCommandMap', () => { + it('should select statusCommandMap from state', () => { + const result: PendingCommandMap = VorgangSelectors.pendingCommandMap.projector(state.VorgangState); + + expect(result).toBe(pendingCommandMap); + }); + }); + + describe('getPendingCommand', () => { + it('should return statusCommand from state by order', () => { + const commandStateResource: StateResource<CommandResource> = + VorgangSelectors.getPendingCommand(pendingCommandUri).projector(pendingCommandMap); + + expect(commandStateResource).toBe(pendingCommandStateResource); + }); + }); }); diff --git a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts index 581b6ead76..0eeceedcce 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts @@ -25,8 +25,10 @@ import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; import { EMPTY_STRING, StateResource } from '@alfa-client/tech-shared'; import { MemoizedSelector, createFeatureSelector, createSelector } from '@ngrx/store'; +import { ResourceUri } from '@ngxp/rest'; import { buildBackButtonUrl, buildVorgangFilterViewRoutePath } from '../vorgang-navigation.util'; import { + PendingCommandMap, StatusCommandMap, VorgangFilter, VorgangListResource, @@ -37,44 +39,36 @@ import { } from '../vorgang.model'; import { VORGANG_FEATURE_KEY, VorgangState } from './vorgang.reducer'; -export const getVorgangState: MemoizedSelector<object, VorgangState> = - createFeatureSelector<VorgangState>(VORGANG_FEATURE_KEY); +export const getVorgangState: MemoizedSelector<object, VorgangState> = createFeatureSelector<VorgangState>(VORGANG_FEATURE_KEY); // VorgangList -export const vorgangList: MemoizedSelector< - VorgangState, - StateResource<VorgangListResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.vorgangList); +export const vorgangList: MemoizedSelector<VorgangState, StateResource<VorgangListResource>> = createSelector( + getVorgangState, + (state: VorgangState) => state.vorgangList, +); export const vorgaenge: MemoizedSelector<VorgangState, VorgangResource[]> = createSelector( getVorgangState, (state: VorgangState) => state.vorgaenge, ); -export const vorgangStatistic: MemoizedSelector< - VorgangState, - StateResource<VorgangStatistic> -> = createSelector(getVorgangState, (state: VorgangState) => state.vorgangStatistic); - -export const vorgangFilter = createSelector( +export const vorgangStatistic: MemoizedSelector<VorgangState, StateResource<VorgangStatistic>> = createSelector( getVorgangState, - (state: VorgangState) => state.vorgangFilter, + (state: VorgangState) => state.vorgangStatistic, ); -export const vorgangView = createSelector( + +export const vorgangFilter = createSelector(getVorgangState, (state: VorgangState) => state.vorgangFilter); +export const vorgangView = createSelector(getVorgangState, (state: VorgangState) => state.vorgangView); + +export const searchPreviewList: MemoizedSelector<VorgangState, StateResource<VorgangListResource>> = createSelector( getVorgangState, - (state: VorgangState) => state.vorgangView, + (state: VorgangState) => state.searchPreviewList, ); - -export const searchPreviewList: MemoizedSelector< - VorgangState, - StateResource<VorgangListResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.searchPreviewList); export const searchString: MemoizedSelector<VorgangState, string> = createSelector( getVorgangState, (state: VorgangState) => state.searchString, ); -export const backButtonUrl: MemoizedSelector<VorgangState, string> = createSelector( - getVorgangState, - (state: VorgangState) => buildBackButtonUrl(state), +export const backButtonUrl: MemoizedSelector<VorgangState, string> = createSelector(getVorgangState, (state: VorgangState) => + buildBackButtonUrl(state), ); export const isVorgangViewSelected = (view: VorgangView) => @@ -85,64 +79,59 @@ export const getVorgangViewRoutePath = (view: VorgangView) => ); export const isVorgangFilterSelected = (filter: VorgangFilter) => - createSelector( - vorgangFilter, - (vorgangFilterInState: VorgangFilter) => vorgangFilterInState === filter, - ); + createSelector(vorgangFilter, (vorgangFilterInState: VorgangFilter) => vorgangFilterInState === filter); export const getVorgangFilterRoutePath = (filter: VorgangFilter) => - createSelector( - vorgangView, - searchString, - (vorgangViewInState: VorgangView, searchStringInState: string) => - buildVorgangFilterViewRoutePath(filter, vorgangViewInState, searchStringInState), + createSelector(vorgangView, searchString, (vorgangViewInState: VorgangView, searchStringInState: string) => + buildVorgangFilterViewRoutePath(filter, vorgangViewInState, searchStringInState), ); //VorgangWithEingang -export const vorgangWithEingang: MemoizedSelector< - VorgangState, - StateResource<VorgangWithEingangResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.vorgangWithEingang); -export const attachmentList: MemoizedSelector< - VorgangState, - StateResource<BinaryFileListResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.attachmentList); -export const representationList: MemoizedSelector< - VorgangState, - StateResource<BinaryFileListResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.representationList); -export const forwardPendingCommand: MemoizedSelector< - VorgangState, - StateResource<CommandResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.forwardPendingCommand); -export const sendPostfachNachrichtPendingCommand: MemoizedSelector< - VorgangState, - StateResource<CommandResource> -> = createSelector( +export const vorgangWithEingang: MemoizedSelector<VorgangState, StateResource<VorgangWithEingangResource>> = createSelector( + getVorgangState, + (state: VorgangState) => state.vorgangWithEingang, +); +export const attachmentList: MemoizedSelector<VorgangState, StateResource<BinaryFileListResource>> = createSelector( + getVorgangState, + (state: VorgangState) => state.attachmentList, +); +export const representationList: MemoizedSelector<VorgangState, StateResource<BinaryFileListResource>> = createSelector( + getVorgangState, + (state: VorgangState) => state.representationList, +); +export const forwardPendingCommand: MemoizedSelector<VorgangState, StateResource<CommandResource>> = createSelector( + getVorgangState, + (state: VorgangState) => state.forwardPendingCommand, +); +export const sendPostfachNachrichtPendingCommand: MemoizedSelector<VorgangState, StateResource<CommandResource>> = createSelector( getVorgangState, (state: VorgangState) => state.sendPostfachNachrichtPendingCommand, ); -export const assignUserCommand: MemoizedSelector< - VorgangState, - StateResource<CommandResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.assignUserCommand); +export const assignUserCommand: MemoizedSelector<VorgangState, StateResource<CommandResource>> = createSelector( + getVorgangState, + (state: VorgangState) => state.assignUserCommand, +); export const statusCommandMap: MemoizedSelector<VorgangState, StatusCommandMap> = createSelector( getVorgangState, (state: VorgangState) => state.statusCommandMap, ); export const getStatusCommand = (order: CommandOrder) => - createSelector( - statusCommandMap, - (statusCommandMapInState: StatusCommandMap) => statusCommandMapInState[order], - ); + createSelector(statusCommandMap, (statusCommandMapInState: StatusCommandMap) => statusCommandMapInState[order]); -export const revokeCommand: MemoizedSelector< - VorgangState, - StateResource<CommandResource> -> = createSelector(getVorgangState, (state: VorgangState) => state.revokeCommand); +export const revokeCommand: MemoizedSelector<VorgangState, StateResource<CommandResource>> = createSelector( + getVorgangState, + (state: VorgangState) => state.revokeCommand, +); export const vorgangExport: MemoizedSelector<VorgangState, StateResource<boolean>> = createSelector( getVorgangState, (state: VorgangState) => state.vorgangExport, ); + +export const pendingCommandMap: MemoizedSelector<VorgangState, PendingCommandMap> = createSelector( + getVorgangState, + (state: VorgangState) => state.pendingCommandMap, +); +export const getPendingCommand = (uri: ResourceUri) => + createSelector(pendingCommandMap, (pendingCommandMapInState: PendingCommandMap) => pendingCommandMapInState[uri]); diff --git a/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts b/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts index 5e9995262e..f272624b26 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts @@ -36,15 +36,21 @@ import { EMPTY_STRING, StateResource, createEmptyStateResource, createStateResou import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { HttpErrorResponse } from '@angular/common/http'; import { faker } from '@faker-js/faker'; +import { Store } from '@ngrx/store'; import { ResourceUri, getUrl } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; -import { createCommandResource, createCreateCommandPropsWithoutResource } from 'libs/command-shared/test/command'; +import { + createCommandResource, + createCommandStateResource, + createCreateCommandPropsWithoutResource, +} from 'libs/command-shared/test/command'; import { singleColdCompleted } from 'libs/tech-shared/test/marbles'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { Observable, of } from 'rxjs'; +import { setPendingCommandLoading, updatePendingCommand } from './+state/vorgang.actions'; import { VorgangFacade } from './+state/vorgang.facade'; import { VorgangWithEingangLinkRel } from './vorgang.linkrel'; import { AdditionalActions, VorgangResource, VorgangWithEingangResource } from './vorgang.model'; @@ -54,11 +60,13 @@ import * as VorgangNavigationUtil from './vorgang-navigation.util'; describe('VorgangService', () => { let service: VorgangService; + + let envConfig: Environment = <any>{ processorNames: ['dummyProcessorName'] }; let navigationService: Mock<NavigationService>; let facade: Mock<VorgangFacade>; let apiRootService: Mock<ApiRootService>; let commandService: Mock<CommandService>; - let envConfig: Environment = <any>{ processorNames: ['dummyProcessorName'] }; + let store: Mock<Store>; beforeEach(() => { navigationService = { ...mock(NavigationService) }; @@ -67,13 +75,15 @@ describe('VorgangService', () => { commandService = mock(CommandService); navigationService.urlChanged = jest.fn(); navigationService.urlChanged.mockReturnValue(of({})); + store = mock(Store); service = new VorgangService( + envConfig, useFromMock(navigationService), useFromMock(facade), useFromMock(apiRootService), useFromMock(commandService), - envConfig, + useFromMock(store), ); }); @@ -581,4 +591,87 @@ describe('VorgangService', () => { expect(service.reloadVorgang).toHaveBeenCalled(); }); }); + + describe('get and poll pending command', () => { + const resourceUri: ResourceUri = faker.internet.url(); + + const command: CommandResource = createCommandResource(); + const commandStateResource: StateResource<CommandResource> = createStateResource(command); + + beforeEach(() => { + service.selectPendingCommand = jest.fn().mockReturnValue(of(commandStateResource)); + service._pollCommand = jest.fn().mockReturnValue(of(commandStateResource)); + }); + + it('should dispatch "set pending loading" action', () => { + service.getAndPollPendingCommand(resourceUri); + + expect(store.dispatch).toHaveBeenCalledWith(setPendingCommandLoading({ resourceUri })); + }); + + it('should call select pending command', () => { + service.getAndPollPendingCommand(resourceUri); + + expect(service.selectPendingCommand).toHaveBeenCalledWith(resourceUri); + }); + + it('should call poll command', () => { + service.getAndPollPendingCommand(resourceUri).subscribe(); + + expect(service._pollCommand).toHaveBeenCalledWith(resourceUri, command); + }); + + it('should return value', () => { + const pendingCommandStateResource$: Observable<StateResource<CommandResource>> = + service.getAndPollPendingCommand(resourceUri); + + expect(pendingCommandStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + }); + + describe('select pending command', () => { + const resourceUri: ResourceUri = faker.internet.url(); + const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); + + beforeEach(() => { + store.select.mockReturnValue(of(commandStateResource)); + }); + + it('should return selected value from store', () => { + const commandStateResource$: Observable<StateResource<CommandResource>> = service.selectPendingCommand(resourceUri); + + expect(commandStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + }); + + describe('poll command', () => { + const resourceUri: ResourceUri = faker.internet.url(); + + const command: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); + const commandStateResource: StateResource<CommandResource> = createStateResource(command); + + beforeEach(() => { + commandService.pollCommand.mockReturnValue(of(commandStateResource)); + }); + + it('should call command service', () => { + service._pollCommand(resourceUri, command); + + expect(commandService.pollCommand).toHaveBeenCalledWith(command); + }); + + it('should dispatch "update pending command" action on command successfully done', () => { + service._pollCommand(resourceUri, command).subscribe(); + + expect(store.dispatch).toHaveBeenLastCalledWith( + updatePendingCommand({ relatedResourceUri: resourceUri, command: commandStateResource.resource }), + ); + }); + + it('should return value', () => { + const commandStateResource$: Observable<StateResource<CommandResource>> = service._pollCommand(resourceUri, command); + + expect(commandStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + }); }); diff --git a/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts b/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts index 7e43262e97..f946fa0aa3 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts @@ -36,10 +36,13 @@ import { ENVIRONMENT_CONFIG, Environment } from '@alfa-client/environment-shared import { NavigationService } from '@alfa-client/navigation-shared'; import { StateResource, createEmptyStateResource, doIfLoadingRequired, isNotNull, mapToResource } from '@alfa-client/tech-shared'; import { Inject, Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; import { ResourceUri, hasLink } from '@ngxp/rest'; import { Observable, combineLatest } from 'rxjs'; import { filter, first, map, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators'; +import { setPendingCommandLoading, updatePendingCommand } from './+state/vorgang.actions'; import { VorgangFacade } from './+state/vorgang.facade'; +import { getPendingCommand } from './+state/vorgang.selectors'; import { buildLinkRelFromPathSegments } from './vorgang-navigation.util'; import { VorgangWithEingangLinkRel } from './vorgang.linkrel'; import { AdditionalActions, VorgangWithEingangResource } from './vorgang.model'; @@ -50,11 +53,12 @@ export class VorgangService { public static readonly VORGANG_WITH_EINGANG_URL: string = 'vorgangWithEingangUrl'; constructor( - private navigationService: NavigationService, - private facade: VorgangFacade, - private apiRootService: ApiRootService, - private commandService: CommandService, @Inject(ENVIRONMENT_CONFIG) private envConfig: Environment, + private readonly navigationService: NavigationService, + private readonly facade: VorgangFacade, + private readonly apiRootService: ApiRootService, + private readonly commandService: CommandService, + private readonly store: Store, ) {} public getVorgangWithEingang(): Observable<StateResource<VorgangWithEingangResource>> { @@ -220,4 +224,30 @@ export class VorgangService { public reloadVorgang(commandResource: CommandResource): void { this.facade.loadVorgangWithEingang(getEffectedResourceUrl(commandResource)); } + + public getAndPollPendingCommand(resourceUri: ResourceUri): Observable<StateResource<CommandResource>> { + this.store.dispatch(setPendingCommandLoading({ resourceUri })); + return this.selectPendingCommand(resourceUri).pipe( + mapToResource(), + switchMap((command: CommandResource) => this._pollCommand(resourceUri, command)), + ); + } + + public selectPendingCommand(relatedResourceUri: ResourceUri): Observable<StateResource<CommandResource>> { + return this.store.select(getPendingCommand(relatedResourceUri)); + } + + _pollCommand(resourceUri: ResourceUri, command: CommandResource): Observable<StateResource<CommandResource>> { + return this.commandService + .pollCommand(command) + .pipe( + tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => + this.updatePendingCommand(resourceUri, commandStateResource.resource), + ), + ); + } + + private updatePendingCommand(relatedResourceUri: ResourceUri, command: CommandResource): void { + this.store.dispatch(updatePendingCommand({ relatedResourceUri, command })); + } } -- GitLab From fe42994e5aa845d16b409e1823c5faff6ff0b324 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Thu, 3 Apr 2025 19:49:29 +0200 Subject: [PATCH 6/8] OZG-7872 fix imports --- .../vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts | 3 +-- .../libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts index e8f4098294..5b79dc4eb0 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts @@ -22,7 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; +import { CommandOrder, CommandResource, PendingCommandMap } from '@alfa-client/command-shared'; import { StateResource, createStateResource } from '@alfa-client/tech-shared'; import { faker } from '@faker-js/faker/.'; import { ResourceUri } from '@ngxp/rest'; @@ -35,7 +35,6 @@ import { createVorgangWithEingangResource, } from 'libs/vorgang-shared/test/vorgang'; import { - PendingCommandMap, StatusCommandMap, VorgangFilter, VorgangListResource, diff --git a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts index 0eeceedcce..e8869224df 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts @@ -22,13 +22,12 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; +import { CommandOrder, CommandResource, PendingCommandMap } from '@alfa-client/command-shared'; import { EMPTY_STRING, StateResource } from '@alfa-client/tech-shared'; import { MemoizedSelector, createFeatureSelector, createSelector } from '@ngrx/store'; import { ResourceUri } from '@ngxp/rest'; import { buildBackButtonUrl, buildVorgangFilterViewRoutePath } from '../vorgang-navigation.util'; import { - PendingCommandMap, StatusCommandMap, VorgangFilter, VorgangListResource, -- GitLab From 97506967ecfe38099e7548bf0236c58a30367533 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Thu, 3 Apr 2025 20:27:41 +0200 Subject: [PATCH 7/8] OZG-7872 remove bescheid +state --- .../src/lib/+state/bescheid.facade.spec.ts | 87 -------------- .../src/lib/+state/bescheid.facade.ts | 61 ---------- .../src/lib/+state/bescheid.reducer.spec.ts | 109 ------------------ .../src/lib/+state/bescheid.reducer.ts | 68 ----------- .../src/lib/+state/bescheid.selectors.spec.ts | 53 --------- .../src/lib/+state/bescheid.selectors.ts | 35 ------ .../src/lib/bescheid-shared.module.ts | 4 +- .../src/lib/bescheid.service.spec.ts | 68 ++++++++--- .../src/lib/bescheid.service.ts | 37 ++++-- 9 files changed, 80 insertions(+), 442 deletions(-) delete mode 100644 alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.spec.ts delete mode 100644 alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.ts delete mode 100644 alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.spec.ts delete mode 100644 alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts delete mode 100644 alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.spec.ts delete mode 100644 alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.ts diff --git a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.spec.ts deleted file mode 100644 index 8de2643e5b..0000000000 --- a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { - CommandOrder, - CommandResource, - CommandService, - CreateCommand, -} from '@alfa-client/command-shared'; -import { createStateResource, EMPTY_STRING, StateResource } from '@alfa-client/tech-shared'; -import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; -import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { Store } from '@ngrx/store'; -import { Resource } from '@ngxp/rest'; -import { createCommandResource, createCreateCommand } from 'libs/command-shared/test/command'; -import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { Subject } from 'rxjs'; -import { BescheidFacade } from './bescheid.facade'; - -describe('BescheidFacade', () => { - const store: Mock<Store> = mock(Store); - - let facade: BescheidFacade; - let commandService: Mock<CommandService>; - let selectSubj: Subject<StateResource<Resource>>; - - beforeEach(() => { - commandService = mock(CommandService); - - selectSubj = new Subject(); - store.select.mockReturnValue(selectSubj); - - facade = new BescheidFacade(useFromMock(store), useFromMock(commandService)); - }); - - describe('getBescheidCommand', () => { - it('should return selected value', (done) => { - const expected: StateResource<CommandResource> = createStateResource(createCommandResource()); - - facade.getBescheidCommand().subscribe((userSettings) => { - expect(userSettings).toBe(expected); - done(); - }); - - selectSubj.next(expected); - }); - }); - - describe('createBescheidDraft', () => { - const createCommand: CreateCommand = createCreateCommand(CommandOrder.CREATE_BESCHEID); - - it('should create command', () => { - const vorgang: VorgangWithEingangResource = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, - ]); - - facade.createBescheidDraft(vorgang, createCommand); - - expect(commandService.createCommandByProps).toHaveBeenCalledWith({ - resource: vorgang, - linkRel: VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, - command: createCommand, - snackBarMessage: EMPTY_STRING, - }); - }); - }); -}); diff --git a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.ts b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.ts deleted file mode 100644 index 60fa7371e7..0000000000 --- a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.facade.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { - CommandResource, - CommandService, - CreateCommand, - CreateCommandProps, -} from '@alfa-client/command-shared'; -import { EMPTY_STRING, StateResource } from '@alfa-client/tech-shared'; -import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; - -import * as BescheidSelectors from './bescheid.selectors'; - -@Injectable({ providedIn: 'root' }) -export class BescheidFacade { - constructor( - private store: Store, - private commandService: CommandService, - ) {} - - public getBescheidCommand(): Observable<StateResource<CommandResource>> { - return this.store.select(BescheidSelectors.bescheidCommand); - } - - public createBescheidDraft( - vorgangWithEingang: VorgangWithEingangResource, - command: CreateCommand, - ): void { - const createCommandProps: CreateCommandProps = { - resource: vorgangWithEingang, - linkRel: VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, - command, - snackBarMessage: EMPTY_STRING, - }; - this.commandService.createCommandByProps(createCommandProps); - } -} diff --git a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.spec.ts deleted file mode 100644 index 35a608b282..0000000000 --- a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.spec.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; -import { ApiError, createEmptyStateResource } from '@alfa-client/tech-shared'; -import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { Action } from '@ngrx/store'; -import { createCommandResource, createSuccessfullyDoneCommandStateResource } from 'libs/command-shared/test/command'; -import { createApiError } from 'libs/tech-shared/test/error'; -import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { BescheidState, initialState, reducer } from './bescheid.reducer'; - -import * as CommandActions from '../../../../command-shared/src/lib/+state/command.actions'; - -describe('Bescheid Reducer', () => { - describe('unknown action', () => { - it('should return current state', () => { - const action: Action = {} as Action; - - const result = reducer(initialState, action); - - expect(result).toBe(initialState); - }); - }); - - describe('bescheidCommand', () => { - describe('on "createCommand" action', () => { - it('should set resource loading', () => { - const resource: VorgangWithEingangResource = createVorgangWithEingangResource(); - const action: Action = CommandActions.createCommand({ - resource, - linkRel: VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, - command: { ...createCommandResource(), order: CommandOrder.CREATE_BESCHEID }, - }); - - const state: BescheidState = reducer(initialState, action); - - expect(state.bescheidCommand).toEqual(createEmptyStateResource(true)); - }); - - it('should replace state resource with loading', () => { - const initialState: BescheidState = { bescheidCommand: createSuccessfullyDoneCommandStateResource() }; - const resource: VorgangWithEingangResource = createVorgangWithEingangResource(); - const action: Action = CommandActions.createCommand({ - resource, - linkRel: VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, - command: { ...createCommandResource(), order: CommandOrder.CREATE_BESCHEID }, - }); - - const state: BescheidState = reducer(initialState, action); - - expect(state.bescheidCommand).toEqual(createEmptyStateResource(true)); - }); - }); - - describe('on "createCommandSuccess" action', () => { - it('should set resource', () => { - const command: CommandResource = { - ...createCommandResource(), - order: CommandOrder.CREATE_BESCHEID, - }; - const action: Action = CommandActions.createCommandSuccess({ command }); - - const state: BescheidState = reducer(initialState, action); - - expect(state.bescheidCommand.resource).toBe(command); - }); - }); - - describe('on "createCommandFailure" action', () => { - const command: CommandResource = { - ...createCommandResource(), - order: CommandOrder.CREATE_BESCHEID, - }; - - it('should set error', () => { - const apiError: ApiError = createApiError(); - const action: Action = CommandActions.createCommandFailure({ - command, - error: { error: apiError }, - }); - - const state: BescheidState = reducer(initialState, action); - - expect(state.bescheidCommand.error).toBe(apiError); - }); - }); - }); -}); diff --git a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts deleted file mode 100644 index 870d54c9df..0000000000 --- a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { CommandProps, CommandResource, CreateCommandFailureProps, CreateCommandProps } from '@alfa-client/command-shared'; -import { ApiError, createEmptyStateResource, createErrorStateResource, createStateResource, StateResource, } from '@alfa-client/tech-shared'; -import { HttpErrorResponse } from '@angular/common/http'; -import { Action, ActionReducer, createReducer, on } from '@ngrx/store'; -import { isCreateBescheidCommand } from '../bescheid.util'; - -import * as CommandActions from '../../../../command-shared/src/lib/+state/command.actions'; - -export const BESCHEID_FEATURE_KEY = 'BescheidState'; - -export interface BescheidPartialState { - readonly [BESCHEID_FEATURE_KEY]: BescheidState; -} - -export interface BescheidState { - bescheidCommand: StateResource<CommandResource>; -} - -export const initialState: BescheidState = { - bescheidCommand: createEmptyStateResource(), -}; - -const bescheidReducer: ActionReducer<BescheidState, Action> = createReducer( - initialState, - on(CommandActions.createCommand, (state: BescheidState, props: CreateCommandProps): BescheidState => { - return isCreateBescheidCommand(props.command.order) ? { ...state, bescheidCommand: createEmptyStateResource(true) } : state; - }), - on(CommandActions.createCommandSuccess, (state: BescheidState, props: CommandProps): BescheidState => { - return isCreateBescheidCommand(props.command.order) ? - { ...state, bescheidCommand: createStateResource(props.command) } - : state; - }), - on(CommandActions.createCommandFailure, (state: BescheidState, props: CreateCommandFailureProps): BescheidState => { - return isCreateBescheidCommand(props.command.order) ? - { - ...state, - bescheidCommand: createErrorStateResource(<ApiError>(<HttpErrorResponse>props.error).error), - } - : state; - }), -); - -export function reducer(state: BescheidState, action: Action): BescheidState { - return bescheidReducer(state, action); -} diff --git a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.spec.ts deleted file mode 100644 index d8f48a9e72..0000000000 --- a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { CommandResource } from '@alfa-client/command-shared'; -import { createStateResource, StateResource } from '@alfa-client/tech-shared'; -import { createCommandResource } from 'libs/command-shared/test/command'; -import { BescheidPartialState, initialState } from './bescheid.reducer'; - -import * as BescheidSelectors from './bescheid.selectors'; - -describe('Bescheid Selectors', () => { - let state: BescheidPartialState; - - const bescheidCommand: StateResource<CommandResource> = - createStateResource(createCommandResource()); - - beforeEach(() => { - state = { - BescheidState: { - ...initialState, - bescheidCommand, - }, - }; - }); - - describe('bescheidCommand', () => { - it('should return bescheidCommand from state', () => { - expect(BescheidSelectors.bescheidCommand.projector(state.BescheidState)).toEqual( - bescheidCommand, - ); - }); - }); -}); diff --git a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.ts b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.ts deleted file mode 100644 index 01f7768b4d..0000000000 --- a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.selectors.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { CommandResource } from '@alfa-client/command-shared'; -import { StateResource } from '@alfa-client/tech-shared'; -import { createFeatureSelector, createSelector, MemoizedSelector } from '@ngrx/store'; -import { BESCHEID_FEATURE_KEY, BescheidState } from './bescheid.reducer'; - -export const getBescheidState: MemoizedSelector<object, BescheidState> = - createFeatureSelector<BescheidState>(BESCHEID_FEATURE_KEY); - -export const bescheidCommand: MemoizedSelector< - BescheidState, - StateResource<CommandResource> -> = createSelector(getBescheidState, (state: BescheidState) => state.bescheidCommand); diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid-shared.module.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid-shared.module.ts index 7c011dec93..acc61d69bf 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid-shared.module.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid-shared.module.ts @@ -23,10 +23,8 @@ */ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { StoreModule } from '@ngrx/store'; -import { BESCHEID_FEATURE_KEY, reducer } from './+state/bescheid.reducer'; @NgModule({ - imports: [CommonModule, StoreModule.forFeature(BESCHEID_FEATURE_KEY, reducer)], + imports: [CommonModule], }) export class BescheidSharedModule {} diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts index ae974499af..e3190c7e52 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts @@ -32,6 +32,7 @@ import { CommandResource, CommandResourceService, CommandService, + CreateCommand, CreateCommandProps, getEffectedResourceUrl, } from '@alfa-client/command-shared'; @@ -47,7 +48,12 @@ import { StateResource, } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; -import { VorgangCommandService, VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { + VorgangCommandService, + VorgangService, + VorgangWithEingangLinkRel, + VorgangWithEingangResource, +} from '@alfa-client/vorgang-shared'; import { TestBed } from '@angular/core/testing'; import { faker } from '@faker-js/faker'; import { getUrl, LinkRel, ResourceUri } from '@ngxp/rest'; @@ -60,6 +66,7 @@ import { createCommandErrorStateResource, createCommandResource, createCommandStateResource, + createCreateCommand, createCreateCommandProps, createSuccessfullyDoneCommandStateResource, } from '../../../command-shared/test/command'; @@ -67,7 +74,6 @@ import { createFile } from '../../../tech-shared/test/file'; import { singleCold, singleColdCompleted } from '../../../tech-shared/test/marbles'; import { createBescheid, createBescheidDocument, createBescheidListResource, createBescheidResource } from '../test/bescheid'; import { createDocumentResource } from '../test/document'; -import { BescheidFacade } from './+state/bescheid.facade'; import { BescheidLinkRel } from './bescheid.linkrel'; import { Bescheid, @@ -95,6 +101,7 @@ import { } from '@alfa-client/bescheid-shared'; import { expect } from '@jest/globals'; import { cold } from 'jest-marbles'; + import * as DateUtil from '../../../tech-shared/src/lib/date.util'; jest.mock('@alfa-client/bescheid-shared', () => { @@ -112,7 +119,6 @@ jest.mock('@alfa-client/bescheid-shared', () => { describe('BescheidService', () => { let service: BescheidService; - let facade: Mock<BescheidFacade>; let vorgangService: Mock<VorgangService>; let resourceRepository: Mock<ResourceRepository>; let commandService: Mock<CommandService>; @@ -129,7 +135,6 @@ describe('BescheidService', () => { const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); beforeEach(() => { - facade = mock(BescheidFacade); resourceRepository = mock(ResourceRepository); commandService = mock(CommandService); vorgangCommandService = mock(VorgangCommandService); @@ -144,7 +149,6 @@ describe('BescheidService', () => { TestBed.configureTestingModule({ providers: [ - { provide: BescheidFacade, useValue: facade }, { provide: VorgangService, useValue: vorgangService }, { provide: ResourceRepository, useValue: resourceRepository }, { provide: CommandService, useValue: commandService }, @@ -676,25 +680,22 @@ describe('BescheidService', () => { describe('createBescheid', () => { const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + const command: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); const commandStateResource: StateResource<CommandResource> = createStateResource(command); - beforeEach(() => { - service.updateBescheidDraft = jest.fn(); - }); + const bescheid: Bescheid = createBescheid(); beforeEach(() => { - facade.getBescheidCommand.mockReturnValue(of(commandStateResource)); + service.updateBescheidDraft = jest.fn(); + service._createBescheidDraft = jest.fn().mockReturnValue(of(commandStateResource)); (service as any).bescheidResourceService.loadByResourceUri = jest.fn(); }); - it('should call facade', () => { - service.createBescheid(vorgangWithEingang).subscribe(); + it('should call create bescheid draft', () => { + service.createBescheid(vorgangWithEingang, bescheid).subscribe(); - expect(facade.createBescheidDraft).toHaveBeenCalledWith(vorgangWithEingang, { - order: CommandOrder.CREATE_BESCHEID, - body: null, - }); + expect(service._createBescheidDraft).toHaveBeenCalledWith(vorgangWithEingang, bescheid); }); it('should update bescheid draft', () => { @@ -702,6 +703,43 @@ describe('BescheidService', () => { expect(service.updateBescheidDraft).toHaveBeenCalledWith(commandStateResource.resource); }); + + it('should return value', () => { + const commandStateResource$: Observable<StateResource<CommandResource>> = service.createBescheid(vorgangWithEingang); + + expect(commandStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + }); + + describe('create bescheid draft', () => { + const createCommand: CreateCommand = createCreateCommand(CommandOrder.CREATE_BESCHEID); + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + + beforeEach(() => { + commandService.createCommandByProps.mockReturnValue(of(commandStateResource)); + }); + + it('should call command service', () => { + const vorgang: VorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, + ]); + + service._createBescheidDraft(vorgang); + + expect(commandService.createCommandByProps).toHaveBeenCalledWith({ + resource: vorgang, + linkRel: VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, + command: createCommand, + snackBarMessage: EMPTY_STRING, + }); + }); + + it('should return created command', () => { + const commandStateResource$: Observable<StateResource<CommandResource>> = + service._createBescheidDraft(vorgangWithEingangResource); + + expect(commandStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); }); describe('updateBescheidDraft', () => { diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts index 22107e92d9..18e048dcf2 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -54,6 +54,7 @@ import { CommandOrder, CommandResource, CommandService, + CreateCommandProps, getEffectedResourceUrl, notHasCommandError, tapOnCommandSuccessfullyDone, @@ -62,6 +63,7 @@ import { PostfachService } from '@alfa-client/postfach-shared'; import { createEmptyStateResource, createStateResource, + EMPTY_STRING, filterIsLoadedOrHasError, getEmbeddedResources, hasStateResourceError, @@ -72,17 +74,20 @@ import { sortByGermanDateStr, StateResource, } from '@alfa-client/tech-shared'; -import { VorgangCommandService, VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { + VorgangCommandService, + VorgangService, + VorgangWithEingangLinkRel, + VorgangWithEingangResource, +} from '@alfa-client/vorgang-shared'; import { inject, Injectable } from '@angular/core'; import { getUrl, hasLink, LinkRel, Resource, ResourceUri } from '@ngxp/rest'; import { isNil } from 'lodash-es'; import { BehaviorSubject, filter, first, map, Observable, startWith, switchMap } from 'rxjs'; -import { BescheidFacade } from './+state/bescheid.facade'; import { DocumentLinkRel } from './document.linkrel'; @Injectable({ providedIn: 'root' }) export class BescheidService { - private readonly bescheidFacade = inject(BescheidFacade); private readonly commandService = inject(CommandService); private readonly vorgangCommandService = inject(VorgangCommandService); private readonly binaryFileService = inject(BinaryFileService); @@ -263,14 +268,24 @@ export class BescheidService { vorgangWithEingang: VorgangWithEingangResource, bescheid?: Bescheid, ): Observable<StateResource<CommandResource>> { - this.bescheidFacade.createBescheidDraft(vorgangWithEingang, buildCreateBescheidCommand(bescheid)); - return this.bescheidFacade - .getBescheidCommand() - .pipe( - tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => - this.updateBescheidDraft(commandStateResource.resource), - ), - ); + return this._createBescheidDraft(vorgangWithEingang, bescheid).pipe( + tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => + this.updateBescheidDraft(commandStateResource.resource), + ), + ); + } + + _createBescheidDraft( + vorgangWithEingang: VorgangWithEingangResource, + bescheid?: Bescheid, + ): Observable<StateResource<CommandResource>> { + const createCommandProps: CreateCommandProps = { + resource: vorgangWithEingang, + linkRel: VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, + command: buildCreateBescheidCommand(bescheid), + snackBarMessage: EMPTY_STRING, + }; + return this.commandService.createCommandByProps(createCommandProps); } updateBescheidDraft(command: CommandResource): void { -- GitLab From 79399215a27d6bdb1924af75543b106b74931b28 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Fri, 4 Apr 2025 08:53:58 +0200 Subject: [PATCH 8/8] OZG-7872 adjust tests --- .../+state/loesch-anforderung.effects.spec.ts | 30 ++++++++----------- .../+state/loesch-anforderung.reducer.spec.ts | 30 +++++++++---------- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.spec.ts b/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.spec.ts index 5f02e52761..90525b88b6 100644 --- a/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.spec.ts +++ b/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.spec.ts @@ -21,24 +21,25 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; -import { TestBed } from '@angular/core/testing'; import { COMMAND_ERROR_MESSAGES, CommandErrorMessage, CommandOrder, CommandResource, + CreateCommandProps, } from '@alfa-client/command-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; import { ApiError } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { SnackBarService } from '@alfa-client/ui'; import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; +import { TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { provideMockStore } from '@ngrx/store/testing'; import { cold, hot } from 'jest-marbles'; -import { createCommandResource } from 'libs/command-shared/test/command'; +import { createCommandResource, createCreateCommandProps } from 'libs/command-shared/test/command'; import { createApiError, createHttpErrorResponse } from 'libs/tech-shared/test/error'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { Observable, of } from 'rxjs'; @@ -131,7 +132,8 @@ describe('LoeschAnforderungEffects', () => { ...createCommandResource(), order: CommandOrder.VORGANG_LOESCHEN, }; - const action = CommandActions.createCommandSuccess({ command }); + const createCommandProps: CreateCommandProps = createCreateCommandProps(); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); it('should call sncakbarService on vorgangLoeschen command', () => { actions = of(action); @@ -143,17 +145,14 @@ describe('LoeschAnforderungEffects', () => { }); describe('loeschAnforderungZuruecknehmenFailedAndReloadVorgangSuccess', () => { - const action = - LoeschAnforderungActions.loeschAnforderungZuruecknehmenFailedAndReloadVorgangSuccess(); + const action = LoeschAnforderungActions.loeschAnforderungZuruecknehmenFailedAndReloadVorgangSuccess(); it('should show snackbar', () => { actions = of(action); effects.loeschAnforderungZuruecknehmenFailedAndReloadVorgangSuccess$.subscribe(); - expect(snackbarService.showError).toHaveBeenCalledWith( - COMMAND_ERROR_MESSAGES[CommandErrorMessage.CONCURRENT_MODIFICATION], - ); + expect(snackbarService.showError).toHaveBeenCalledWith(COMMAND_ERROR_MESSAGES[CommandErrorMessage.CONCURRENT_MODIFICATION]); }); }); @@ -164,20 +163,17 @@ describe('LoeschAnforderungEffects', () => { }; const actionError = <HttpErrorResponse>{ error }; const overviewUrl: string = faker.internet.url(); - const action = - LoeschAnforderungActions.loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailure({ - error: actionError, - overviewUrl, - }); + const action = LoeschAnforderungActions.loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailure({ + error: actionError, + overviewUrl, + }); it('should show snackbar', () => { actions = of(action); effects.loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailure$.subscribe(); - expect(snackbarService.showInfo).toHaveBeenCalledWith( - 'Der Vorgang wurde zwischenzeitlich gelöscht.', - ); + expect(snackbarService.showInfo).toHaveBeenCalledWith('Der Vorgang wurde zwischenzeitlich gelöscht.'); }); it('should navigate to overview', () => { diff --git a/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.reducer.spec.ts b/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.reducer.spec.ts index 5a25e85bc3..4ea2d3ff41 100644 --- a/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.reducer.spec.ts +++ b/alfa-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.reducer.spec.ts @@ -21,16 +21,11 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; -import { - ApiError, - createEmptyStateResource, - createErrorStateResource, - createStateResource, -} from '@alfa-client/tech-shared'; +import { CommandOrder, CommandResource, CreateCommandProps } from '@alfa-client/command-shared'; +import { ApiError, createEmptyStateResource, createErrorStateResource, createStateResource } from '@alfa-client/tech-shared'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { Action } from '@ngrx/store'; -import { createCommand, createCommandResource } from 'libs/command-shared/test/command'; +import { createCommand, createCommandResource, createCreateCommandProps } from 'libs/command-shared/test/command'; import { createLoeschAnforderungResource } from 'libs/loesch-anforderung-shared/test/loesch-anforderung'; import { createApiError } from 'libs/tech-shared/test/error'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; @@ -100,7 +95,8 @@ describe('LoeschAnforderung Reducer', () => { ...createCommandResource(), order: CommandOrder.VORGANG_ZUM_LOESCHEN_MARKIEREN, }; - const action = CommandActions.createCommandSuccess({ command }); + const createCommandProps: CreateCommandProps = createCreateCommandProps(); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); const state: LoeschAnforderungState = reducer(initialState, action); @@ -150,7 +146,8 @@ describe('LoeschAnforderung Reducer', () => { ...createCommandResource(), order: CommandOrder.VORGANG_LOESCHEN, }; - const action = CommandActions.createCommandSuccess({ command }); + const createCommandProps: CreateCommandProps = createCreateCommandProps(); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); const state: LoeschAnforderungState = reducer(initialState, action); @@ -180,7 +177,8 @@ describe('LoeschAnforderung Reducer', () => { ...createCommandResource(), order: CommandOrder.LOESCH_ANFORDERUNG_ZURUECKNEHMEN, }; - const action = CommandActions.createCommandSuccess({ command }); + const createCommandProps: CreateCommandProps = createCreateCommandProps(); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); const state: LoeschAnforderungState = reducer(initialState, action); @@ -192,7 +190,8 @@ describe('LoeschAnforderung Reducer', () => { ...createCommandResource(), order: CommandOrder.LOESCH_ANFORDERUNG_ZURUECKNEHMEN, }; - const action = CommandActions.createCommandSuccess({ command }); + const createCommandProps: CreateCommandProps = createCreateCommandProps(); + const action = CommandActions.createCommandSuccess({ command, createCommandProps }); const state: LoeschAnforderungState = reducer( { @@ -213,13 +212,12 @@ describe('LoeschAnforderung Reducer', () => { order: CommandOrder.LOESCH_ANFORDERUNG_ZURUECKNEHMEN, }; const error: ApiError = createApiError(); - const action = CommandActions.createCommandFailure({ command, error }); + const createCommandProps: CreateCommandProps = createCreateCommandProps(); + const action = CommandActions.createCommandFailure({ command, createCommandProps, error }); const state: LoeschAnforderungState = reducer(initialState, action); - expect(state.loeschAnforderungZuruecknehmenCommand).toEqual( - createErrorStateResource(error), - ); + expect(state.loeschAnforderungZuruecknehmenCommand).toEqual(createErrorStateResource(error)); }); }); -- GitLab