diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.model.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.model.ts index 9e7f485b57dde472449b84617e056c6590b56c67..0b7283732bb6d3096ed251d2281503016f739b93 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.model.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.model.ts @@ -5,6 +5,7 @@ export enum BescheidStatus { DRAFT = 'DRAFT', SENT = 'SENT', } + export interface Bescheid { status: BescheidStatus; beschiedenAm: string; @@ -30,3 +31,13 @@ export interface UploadFileInProgress { fileName?: string; error?: HttpError; } + +export enum BescheidWizardStep { + AntragBescheiden = 1, + DokumenteHochladen = 2, + BescheidVersenden = 3, +} + +export interface BescheidWizardDialogResult { + reloadVorgang: boolean; +} 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 4eb95c74da90efb1b6c868d02960da27fe81d28c..e36a84e4df075733f10c4bfce67483f094ca0daa 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 @@ -1,14 +1,5 @@ -import { - BinaryFileListResource, - BinaryFileResource, - BinaryFileService, -} from '@alfa-client/binary-file-shared'; -import { - CommandOrder, - CommandResource, - CommandService, - CreateCommandProps, -} from '@alfa-client/command-shared'; +import { BinaryFileListResource, BinaryFileResource, BinaryFileService } from '@alfa-client/binary-file-shared'; +import { CommandOrder, CommandResource, CommandService, CreateCommandProps } from '@alfa-client/command-shared'; import { ApiError, EMPTY_ARRAY, @@ -34,10 +25,7 @@ import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; import { createApiError } from 'libs/tech-shared/test/error'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { Observable, of } from 'rxjs'; -import { - createBinaryFileListResource, - createBinaryFileResource, -} from '../../../binary-file-shared/test/binary-file'; +import { createBinaryFileListResource, createBinaryFileResource } from '../../../binary-file-shared/test/binary-file'; import { createCommandErrorResource, createCommandResource, @@ -62,12 +50,14 @@ import { BescheidListResource, BescheidResource, BescheidStatus, + BescheidWizardStep, UploadFileInProgress, } from './bescheid.model'; import { BescheidService } from './bescheid.service'; import { DocumentLinkRel } from './document.linkrel'; import { DocumentResource } from './document.model'; +import { PostfachService } from '@alfa-client/postfach-shared'; import * as DateUtil from '../../../tech-shared/src/lib/date.util'; import * as BescheidUtil from './bescheid.util'; @@ -80,20 +70,21 @@ describe('BescheidService', () => { let commandService: Mock<CommandService>; let vorgangCommandService: Mock<VorgangCommandService>; let binaryFileService: Mock<BinaryFileService>; + let postfachService: Mock<PostfachService>; - const vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource> = - createStateResource(createVorgangWithEingangResource()); + const vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource> = createStateResource( + createVorgangWithEingangResource(), + ); beforeEach(() => { facade = mock(BescheidFacade); resourceRepository = mock(ResourceRepository); commandService = mock(CommandService); vorgangCommandService = mock(VorgangCommandService); - vorgangService = mock(VorgangService); vorgangService.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); - binaryFileService = mock(BinaryFileService); + postfachService = mock(PostfachService); service = new BescheidService( useFromMock(facade), @@ -102,6 +93,7 @@ describe('BescheidService', () => { useFromMock(vorgangCommandService), useFromMock(binaryFileService), useFromMock(resourceRepository), + useFromMock(postfachService), ); }); @@ -109,10 +101,25 @@ describe('BescheidService', () => { expect(service).toBeTruthy(); }); + describe('getActiveStep', () => { + it('should emit initial value', () => { + const activeStep$: Observable<number> = service.getActiveStep(); + + expect(activeStep$).toBeObservable(singleCold(BescheidWizardStep.AntragBescheiden)); + }); + }); + + describe('setActiveStep', () => { + it('should emit changed active step', () => { + service.setActiveStep(BescheidWizardStep.DokumenteHochladen); + + expect(service.activeStep$).toBeObservable(singleCold(BescheidWizardStep.DokumenteHochladen)); + }); + }); + describe('get bescheid draft', () => { const bescheidDraft: BescheidResource = createBescheidResource(); - const bescheidDraftStateResource: StateResource<BescheidResource> = - createStateResource(bescheidDraft); + const bescheidDraftStateResource: StateResource<BescheidResource> = createStateResource(bescheidDraft); it('should call resource service', () => { service.bescheidResourceService.get = jest.fn(); @@ -123,12 +130,9 @@ describe('BescheidService', () => { }); it('should return value', () => { - service.bescheidResourceService.get = jest - .fn() - .mockReturnValue(singleCold(bescheidDraftStateResource)); + service.bescheidResourceService.get = jest.fn().mockReturnValue(singleCold(bescheidDraftStateResource)); - const bescheidStateResource$: Observable<StateResource<BescheidResource>> = - service.getBescheidDraft(); + const bescheidStateResource$: Observable<StateResource<BescheidResource>> = service.getBescheidDraft(); expect(bescheidStateResource$).toBeObservable(singleCold(bescheidDraftStateResource)); }); @@ -172,21 +176,17 @@ describe('BescheidService', () => { describe('bescheidErstellungUeberspringen', () => { describe('Bescheid Draft exists', () => { - const vorgangWithEingangResource: VorgangWithEingangResource = - createVorgangWithEingangResource(); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); const bescheidResource: BescheidResource = createBescheidResource(); - const bescheidStateResource: StateResource<BescheidResource> = - createStateResource(bescheidResource); + const bescheidStateResource: StateResource<BescheidResource> = createStateResource(bescheidResource); const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); beforeEach(() => { service.existsBescheidDraft = jest.fn().mockReturnValue(true); service.getBescheidDraft = jest.fn().mockReturnValue(of(bescheidStateResource)); - service.bescheidLoeschenUndErstellungUeberspringen = jest - .fn() - .mockReturnValue(of(commandStateResource)); + service.bescheidLoeschenUndErstellungUeberspringen = jest.fn().mockReturnValue(of(commandStateResource)); }); it('should get bescheid draft', (done) => { @@ -221,8 +221,7 @@ describe('BescheidService', () => { }); describe('Bescheid Draft not exists', () => { - const vorgangWithEingangResource: VorgangWithEingangResource = - createVorgangWithEingangResource(); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); beforeEach(() => { @@ -254,9 +253,7 @@ describe('BescheidService', () => { let bescheidStateResource: StateResource<BescheidResource>; beforeEach(() => { - vorgangStateResource = createStateResource( - createVorgangWithEingangResource([VorgangWithEingangLinkRel.BESCHEID_DRAFT]), - ); + vorgangStateResource = createStateResource(createVorgangWithEingangResource([VorgangWithEingangLinkRel.BESCHEID_DRAFT])); bescheidStateResource = createBescheidStateResource(); vorgangService.getVorgangWithEingang.mockReturnValue(of(vorgangStateResource)); service.getBescheidDraft = jest.fn().mockReturnValue(of(bescheidStateResource)); @@ -269,35 +266,27 @@ describe('BescheidService', () => { }); it('should emit state resources', () => { - const bescheid$: Observable<StateResource<BescheidResource>> = - service.getBescheidDraftIfExists(); + const bescheid$: Observable<StateResource<BescheidResource>> = service.getBescheidDraftIfExists(); - expect(bescheid$).toBeObservable( - cold('(ab|)', { a: createEmptyStateResource(), b: bescheidStateResource }), - ); + expect(bescheid$).toBeObservable(cold('(ab|)', { a: createEmptyStateResource(), b: bescheidStateResource })); }); }); describe('bescheidLoeschenUndErstellungUeberspringen', () => { - const vorgangWithEingangResource: VorgangWithEingangResource = - createVorgangWithEingangResource(); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); const bescheidResource: BescheidResource = createBescheidResource(); - const vorgangAbschliessenCommandStateResource: StateResource<CommandResource> = - createCommandStateResource(); + const vorgangAbschliessenCommandStateResource: StateResource<CommandResource> = createCommandStateResource(); beforeEach(() => { - service.vorgangAbschliesen = jest - .fn() - .mockReturnValue(of(vorgangAbschliessenCommandStateResource)); + service.vorgangAbschliesen = jest.fn().mockReturnValue(of(vorgangAbschliessenCommandStateResource)); service.deleteBescheid = jest.fn().mockReturnValue(of(createCommandStateResource)); }); it('should Bescheiderstellung überspringen', (done) => { - const command$: Observable<StateResource<CommandResource>> = - service.bescheidLoeschenUndErstellungUeberspringen( - vorgangWithEingangResource, - bescheidResource, - ); + const command$: Observable<StateResource<CommandResource>> = service.bescheidLoeschenUndErstellungUeberspringen( + vorgangWithEingangResource, + bescheidResource, + ); command$.subscribe(() => { expect(service.vorgangAbschliesen).toHaveBeenCalledWith(vorgangWithEingangResource); @@ -306,15 +295,12 @@ describe('BescheidService', () => { }); it('should Bescheid löschen', (done) => { - service.vorgangAbschliesen = jest - .fn() - .mockReturnValue(of(createCommandStateResource([CommandLinkRel.EFFECTED_RESOURCE]))); - - const command$: Observable<StateResource<CommandResource>> = - service.bescheidLoeschenUndErstellungUeberspringen( - vorgangWithEingangResource, - bescheidResource, - ); + service.vorgangAbschliesen = jest.fn().mockReturnValue(of(createCommandStateResource([CommandLinkRel.EFFECTED_RESOURCE]))); + + const command$: Observable<StateResource<CommandResource>> = service.bescheidLoeschenUndErstellungUeberspringen( + vorgangWithEingangResource, + bescheidResource, + ); command$.subscribe(() => { expect(service.deleteBescheid).toHaveBeenCalledWith(bescheidResource); @@ -325,11 +311,10 @@ describe('BescheidService', () => { it('should not Bescheid löschen', (done) => { service.vorgangAbschliesen = jest.fn().mockReturnValue(of(createCommandStateResource())); - const command$: Observable<StateResource<CommandResource>> = - service.bescheidLoeschenUndErstellungUeberspringen( - vorgangWithEingangResource, - bescheidResource, - ); + const command$: Observable<StateResource<CommandResource>> = service.bescheidLoeschenUndErstellungUeberspringen( + vorgangWithEingangResource, + bescheidResource, + ); command$.subscribe(() => { expect(service.deleteBescheid).not.toHaveBeenCalledWith(bescheidResource); @@ -338,19 +323,17 @@ describe('BescheidService', () => { }); it('should emit vorgang abschliessen command', () => { - const command$: Observable<StateResource<CommandResource>> = - service.bescheidLoeschenUndErstellungUeberspringen( - vorgangWithEingangResource, - bescheidResource, - ); + const command$: Observable<StateResource<CommandResource>> = service.bescheidLoeschenUndErstellungUeberspringen( + vorgangWithEingangResource, + bescheidResource, + ); expect(command$).toBeObservable(cold('(a|)', { a: vorgangAbschliessenCommandStateResource })); }); }); describe('vorgang abschliessen', () => { - const vorgangWithEingangResource: VorgangWithEingangResource = - createVorgangWithEingangResource(); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); beforeEach(() => { @@ -358,9 +341,7 @@ describe('BescheidService', () => { }); it('should call vorgang command service', (done) => { - const command$: Observable<StateResource<CommandResource>> = service.vorgangAbschliesen( - vorgangWithEingangResource, - ); + const command$: Observable<StateResource<CommandResource>> = service.vorgangAbschliesen(vorgangWithEingangResource); command$.subscribe(() => { expect(vorgangCommandService.abschliessen).toHaveBeenCalledWith(vorgangWithEingangResource); @@ -369,9 +350,7 @@ describe('BescheidService', () => { }); it('should return command', () => { - const command$: Observable<StateResource<CommandResource>> = service.vorgangAbschliesen( - vorgangWithEingangResource, - ); + const command$: Observable<StateResource<CommandResource>> = service.vorgangAbschliesen(vorgangWithEingangResource); expect(command$).toBeObservable(cold('(a|)', { a: commandStateResource })); }); @@ -399,8 +378,7 @@ describe('BescheidService', () => { const commandStateResource: StateResource<CommandResource> = createEmptyStateResource(); commandService.createCommandByProps.mockReturnValue(commandStateResource); - const createdCommand: Observable<StateResource<CommandResource>> = - service.deleteBescheid(bescheidResource); + const createdCommand: Observable<StateResource<CommandResource>> = service.deleteBescheid(bescheidResource); expect(createdCommand).toEqual(commandStateResource); }); @@ -408,11 +386,8 @@ describe('BescheidService', () => { describe('update bescheid', () => { const bescheid: Bescheid = createBescheid(); - const commandResource: CommandResource = createCommandResource([ - CommandLinkRel.EFFECTED_RESOURCE, - ]); - const commandStateResource: StateResource<CommandResource> = - createStateResource(commandResource); + const commandResource: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); + const commandStateResource: StateResource<CommandResource> = createStateResource(commandResource); const createCommandProps: CreateCommandProps = createCreateCommandProps(); let buildUpdateBescheidCommandPropsSpy: jest.SpyInstance; @@ -428,10 +403,7 @@ describe('BescheidService', () => { it('should build update bescheid command props', () => { service.updateBescheid(bescheid); - expect(buildUpdateBescheidCommandPropsSpy).toHaveBeenCalledWith( - service.getResource(), - bescheid, - ); + expect(buildUpdateBescheidCommandPropsSpy).toHaveBeenCalledWith(service.getResource(), bescheid); }); it('should create command', () => { @@ -441,21 +413,18 @@ describe('BescheidService', () => { }); it('should return command', () => { - const updateBescheid$: Observable<StateResource<CommandResource>> = - service.updateBescheid(bescheid); + const updateBescheid$: Observable<StateResource<CommandResource>> = service.updateBescheid(bescheid); expect(updateBescheid$).toBeObservable(cold('(a|)', { a: commandStateResource })); }); it('should set resource by uri', (done) => { - service - .updateBescheid(bescheid) - .subscribe((commandStateResource: StateResource<CommandResource>) => { - expect(service.bescheidResourceService.loadByResourceUri).toHaveBeenCalledWith( - getUrl(commandStateResource.resource, CommandLinkRel.EFFECTED_RESOURCE), - ); - done(); - }); + service.updateBescheid(bescheid).subscribe((commandStateResource: StateResource<CommandResource>) => { + expect(service.bescheidResourceService.loadByResourceUri).toHaveBeenCalledWith( + getUrl(commandStateResource.resource, CommandLinkRel.EFFECTED_RESOURCE), + ); + done(); + }); }); it('should clear create bescheid document in progress', (done) => { @@ -495,9 +464,7 @@ describe('BescheidService', () => { let buildSendBescheidCommandPropsSpy: jest.SpyInstance; beforeEach(() => { - service.bescheidResourceService.get = jest - .fn() - .mockReturnValue(of(createStateResource(bescheidResource))); + service.bescheidResourceService.get = jest.fn().mockReturnValue(of(createStateResource(bescheidResource))); buildSendBescheidCommandPropsSpy = jest .spyOn(BescheidUtil, 'buildSendBescheidCommandProps') .mockReturnValue(createCommandProps); @@ -525,10 +492,7 @@ describe('BescheidService', () => { }); it('should return command', () => { - const command$: Observable<StateResource<CommandResource>> = service.sendBescheid( - bescheidResource, - linkRel, - ); + const command$: Observable<StateResource<CommandResource>> = service.sendBescheid(bescheidResource, linkRel); expect(command$).toBeObservable(cold('(a|)', { a: commandStateResource })); }); @@ -545,15 +509,11 @@ describe('BescheidService', () => { it('should call sendBescheid', () => { service.sendBescheidManually(bescheidResource); - expect(service.sendBescheid).toHaveBeenCalledWith( - bescheidResource, - BescheidLinkRel.BESCHEIDEN, - ); + expect(service.sendBescheid).toHaveBeenCalledWith(bescheidResource, BescheidLinkRel.BESCHEIDEN); }); it('should return command', () => { - const command$: Observable<StateResource<CommandResource>> = - service.sendBescheidManually(bescheidResource); + const command$: Observable<StateResource<CommandResource>> = service.sendBescheidManually(bescheidResource); expect(command$).toBeObservable(singleColdCompleted(sendBescheidCommand)); }); @@ -570,15 +530,11 @@ describe('BescheidService', () => { it('should call sendBescheid', () => { service.sendBescheidToAntragsteller(bescheidResource); - expect(service.sendBescheid).toHaveBeenCalledWith( - bescheidResource, - BescheidLinkRel.BESCHEIDEN_UND_SENDEN, - ); + expect(service.sendBescheid).toHaveBeenCalledWith(bescheidResource, BescheidLinkRel.BESCHEIDEN_UND_SENDEN); }); it('should return command', () => { - const command$: Observable<StateResource<CommandResource>> = - service.sendBescheidToAntragsteller(bescheidResource); + const command$: Observable<StateResource<CommandResource>> = service.sendBescheidToAntragsteller(bescheidResource); expect(command$).toBeObservable(singleColdCompleted(sendBescheidCommand)); }); @@ -619,9 +575,7 @@ describe('BescheidService', () => { beforeEach(() => { bescheidResource = createBescheidResource([BescheidLinkRel.ATTACHMENTS]); - service.getBescheidDraft = jest - .fn() - .mockReturnValue(of(createStateResource(bescheidResource))); + service.getBescheidDraft = jest.fn().mockReturnValue(of(createStateResource(bescheidResource))); binaryFileResource = createBinaryFileResource(); @@ -631,10 +585,7 @@ describe('BescheidService', () => { it('should get files', (done) => { service.getAttachments().subscribe(() => { - expect(binaryFileService.getFiles).toHaveBeenCalledWith( - bescheidResource, - BescheidLinkRel.ATTACHMENTS, - ); + expect(binaryFileService.getFiles).toHaveBeenCalledWith(bescheidResource, BescheidLinkRel.ATTACHMENTS); done(); }); }); @@ -664,8 +615,7 @@ describe('BescheidService', () => { describe('get bescheid document file', () => { it('should return value', (done) => { const binaryFile: BinaryFileResource = createBinaryFileResource(); - const binaryFileStateResource: StateResource<BinaryFileResource> = - createStateResource(binaryFile); + const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource(binaryFile); service.bescheidDocumentFile$.next(binaryFileStateResource); service.getBescheidDocumentFile().subscribe((result: StateResource<BinaryFileResource>) => { @@ -706,13 +656,11 @@ describe('BescheidService', () => { }); it('should return bescheid document file', (done) => { - service - .uploadBescheidDocument(bescheid, file) - .subscribe((uploadFileInProgress: UploadFileInProgress) => { - expect(uploadFileInProgress.fileName).toBe(file.name); - expect(uploadFileInProgress.loading).toBeTruthy(); - done(); - }); + service.uploadBescheidDocument(bescheid, file).subscribe((uploadFileInProgress: UploadFileInProgress) => { + expect(uploadFileInProgress.fileName).toBe(file.name); + expect(uploadFileInProgress.loading).toBeTruthy(); + done(); + }); }); }); @@ -720,9 +668,7 @@ describe('BescheidService', () => { const bescheid: BescheidResource = createBescheidResource(); const file: File = createFile(); - const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource( - createBinaryFileResource(), - ); + const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource(createBinaryFileResource()); beforeEach(() => { binaryFileService.uploadFile.mockReturnValue(of(binaryFileStateResource)); @@ -731,12 +677,7 @@ describe('BescheidService', () => { it('should call binary file service', () => { service.doUploadBescheidDocument(bescheid, file); - expect(binaryFileService.uploadFile).toHaveBeenCalledWith( - bescheid, - BescheidLinkRel.UPLOAD_BESCHEID_FILE, - file, - false, - ); + expect(binaryFileService.uploadFile).toHaveBeenCalledWith(bescheid, BescheidLinkRel.UPLOAD_BESCHEID_FILE, file, false); }); it('should call handle upload becheid document response', () => { @@ -744,10 +685,7 @@ describe('BescheidService', () => { service.doUploadBescheidDocument(bescheid, file); - expect(service.handleUploadBescheidDocumentResponse).toHaveBeenCalledWith( - bescheid, - binaryFileStateResource, - ); + expect(service.handleUploadBescheidDocumentResponse).toHaveBeenCalledWith(bescheid, binaryFileStateResource); }); }); @@ -756,10 +694,8 @@ describe('BescheidService', () => { const binaryFile: BinaryFileResource = createBinaryFileResource(); const apiError: ApiError = createApiError(); - const binaryFileErrorStateResource: StateResource<BinaryFileResource> = - createErrorStateResource(apiError); - const binaryFileStateResource: StateResource<BinaryFileResource> = - createStateResource(binaryFile); + const binaryFileErrorStateResource: StateResource<BinaryFileResource> = createErrorStateResource(apiError); + const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource(binaryFile); it('should call create bescheid document from file on success', () => { service.createBescheidDocumentFromFile = jest.fn(); @@ -813,10 +749,7 @@ describe('BescheidService', () => { it('should build create command document from file props', () => { service.createBescheidDocumentFromFile(bescheid, binaryFile); - expect(buildCreateBescheidDocumentFromFilePropsSpy).toHaveBeenCalledWith( - bescheid, - binaryFile, - ); + expect(buildCreateBescheidDocumentFromFilePropsSpy).toHaveBeenCalledWith(bescheid, binaryFile); }); it('should call handle create bescheid document from file response', () => { @@ -824,10 +757,7 @@ describe('BescheidService', () => { service.createBescheidDocumentFromFile(bescheid, binaryFile); - expect(service.handleCreateBescheidDocumentFromFileResponse).toHaveBeenCalledWith( - commandStateResource, - binaryFile, - ); + expect(service.handleCreateBescheidDocumentFromFileResponse).toHaveBeenCalledWith(commandStateResource, binaryFile); }); }); @@ -849,8 +779,7 @@ describe('BescheidService', () => { describe('on error', () => { it('should set upload bescheid in progress error', () => { const httpError: HttpError = createApiError(); - const errorStateResource: StateResource<CommandResource> = - createErrorStateResource(httpError); + const errorStateResource: StateResource<CommandResource> = createErrorStateResource(httpError); service.handleCreateBescheidDocumentFromFileResponse(errorStateResource, binaryFile); @@ -859,8 +788,7 @@ describe('BescheidService', () => { it('should set upload bescheid in progress loading false', () => { const httpError: HttpError = createApiError(); - const errorStateResource: StateResource<CommandResource> = - createErrorStateResource(httpError); + const errorStateResource: StateResource<CommandResource> = createErrorStateResource(httpError); service.handleCreateBescheidDocumentFromFileResponse(errorStateResource, binaryFile); @@ -878,9 +806,7 @@ describe('BescheidService', () => { it('should set document uri', () => { service.handleCreateBescheidDocumentFromFileResponse(commandStateResource, binaryFile); - expect(service.bescheidDocumentUri$.value).toEqual( - getUrl(command, CommandLinkRel.EFFECTED_RESOURCE), - ); + expect(service.bescheidDocumentUri$.value).toEqual(getUrl(command, CommandLinkRel.EFFECTED_RESOURCE)); }); }); }); @@ -921,42 +847,39 @@ describe('BescheidService', () => { it('should emit empty state resource document file', () => { service.init(); - expect(service.getBescheidDocumentFile()).toBeObservable( - singleCold(createEmptyStateResource()), - ); + expect(service.getBescheidDocumentFile()).toBeObservable(singleCold(createEmptyStateResource())); }); it('should emit empty state resource uploaded attachment', () => { service.init(); - expect(service.getUploadedAttachment()).toBeObservable( - singleCold(createEmptyStateResource()), - ); + expect(service.getUploadedAttachment()).toBeObservable(singleCold(createEmptyStateResource())); }); it('should emit empty upload in progress for upload bescheid document in progress', () => { service.init(); - expect(service.getUploadBescheidDocumentInProgress()).toBeObservable( - singleCold({ loading: false }), - ); + expect(service.getUploadBescheidDocumentInProgress()).toBeObservable(singleCold({ loading: false })); }); it('should emit empty upload in progress for upload attachmentdocument in progress', () => { service.init(); - expect(service.getUploadAttachmentInProgress()).toBeObservable( - singleCold({ loading: false }), - ); + expect(service.getUploadAttachmentInProgress()).toBeObservable(singleCold({ loading: false })); + }); + + it('should init active step', () => { + service.setActiveStep(2); + + service.init(); + + expect(service.activeStep$).toBeObservable(singleCold(1)); }); }); describe('create bescheid document', () => { - const commandResource: CommandResource = createCommandResource([ - CommandLinkRel.EFFECTED_RESOURCE, - ]); - const commandStateResource: StateResource<CommandResource> = - createStateResource(commandResource); + const commandResource: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); + const commandStateResource: StateResource<CommandResource> = createStateResource(commandResource); const bescheidResource: BescheidResource = createBescheidResource(); const createCommandProps: CreateCommandProps = createCreateCommandProps(); @@ -1008,9 +931,7 @@ describe('BescheidService', () => { service.createBescheidDocument(); tick(); - expect(service.loadBescheidDocumentByUri).toHaveBeenCalledWith( - getUrl(commandResource, CommandLinkRel.EFFECTED_RESOURCE), - ); + expect(service.loadBescheidDocumentByUri).toHaveBeenCalledWith(getUrl(commandResource, CommandLinkRel.EFFECTED_RESOURCE)); })); it('should update bescheid document Url', fakeAsync(() => { @@ -1019,16 +940,12 @@ describe('BescheidService', () => { service.createBescheidDocument(); tick(); - expect(service.bescheidDocumentUri$.value).toBe( - getUrl(commandResource, CommandLinkRel.EFFECTED_RESOURCE), - ); + expect(service.bescheidDocumentUri$.value).toBe(getUrl(commandResource, CommandLinkRel.EFFECTED_RESOURCE)); })); }); describe('on error', () => { - const commandErrorStateResource: StateResource<CommandResource> = createStateResource( - createCommandErrorResource(), - ); + const commandErrorStateResource: StateResource<CommandResource> = createStateResource(createCommandErrorResource()); it('should emit command state resource', () => { commandService.createCommandByProps.mockReturnValue(of(commandErrorStateResource)); @@ -1071,8 +988,7 @@ describe('BescheidService', () => { describe('get bescheid document command', () => { const commandResource: CommandResource = createCommandResource(); - const commandStateResource: StateResource<CommandResource> = - createStateResource(commandResource); + const commandStateResource: StateResource<CommandResource> = createStateResource(commandResource); beforeEach(() => { commandService.getCommandByOrder.mockReturnValue(of(commandStateResource)); @@ -1080,9 +996,7 @@ describe('BescheidService', () => { it('should call command service', (done) => { service.getBescheidDocumentCommand().subscribe(() => { - expect(commandService.getCommandByOrder).toHaveBeenCalledWith( - CommandOrder.CREATE_BESCHEID_DOCUMENT, - ); + expect(commandService.getCommandByOrder).toHaveBeenCalledWith(CommandOrder.CREATE_BESCHEID_DOCUMENT); done(); }); }); @@ -1175,9 +1089,7 @@ describe('BescheidService', () => { describe('load bescheid document file', () => { const document: DocumentResource = createDocumentResource([DocumentLinkRel.FILE]); - const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource( - createBinaryFileResource(), - ); + const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource(createBinaryFileResource()); beforeEach(() => { binaryFileService.getFile.mockReturnValue(of(binaryFileStateResource)); @@ -1186,9 +1098,7 @@ describe('BescheidService', () => { it('should call bianry file service', () => { service.loadBescheidDocumentFile(document); - expect(binaryFileService.getFile).toHaveBeenCalledWith( - getUrl(document, DocumentLinkRel.FILE), - ); + expect(binaryFileService.getFile).toHaveBeenCalledWith(getUrl(document, DocumentLinkRel.FILE)); }); it('should set bescheidDocument file', () => { @@ -1217,11 +1127,8 @@ describe('BescheidService', () => { it('should return value', () => { const bescheidList: BescheidListResource = createBescheidListResource(); - const bescheidListStateResource: StateResource<BescheidListResource> = - createStateResource(bescheidList); - service.bescheidListResourceService.getList = jest - .fn() - .mockReturnValue(singleCold(bescheidListStateResource)); + const bescheidListStateResource: StateResource<BescheidListResource> = createStateResource(bescheidList); + service.bescheidListResourceService.getList = jest.fn().mockReturnValue(singleCold(bescheidListStateResource)); const command$: Observable<StateResource<BescheidListResource>> = service.getBescheidList(); @@ -1244,8 +1151,7 @@ describe('BescheidService', () => { }); it('should return value', () => { - const documentStateResource$: Observable<StateResource<DocumentResource>> = - service.loadBescheidDocument(resourceUri); + const documentStateResource$: Observable<StateResource<DocumentResource>> = service.loadBescheidDocument(resourceUri); expect(documentStateResource$).toBeObservable( cold('(ab|)', { a: createEmptyStateResource(true), b: createStateResource(document) }), @@ -1255,13 +1161,10 @@ describe('BescheidService', () => { describe('get resource', () => { const bescheidResource: BescheidResource = createBescheidResource(); - const bescheidStateResource: StateResource<BescheidResource> = - createStateResource(bescheidResource); + const bescheidStateResource: StateResource<BescheidResource> = createStateResource(bescheidResource); it('should call bescheid resource service select resource', () => { - service.bescheidResourceService.selectResource = jest - .fn() - .mockReturnValue(of(bescheidStateResource)); + service.bescheidResourceService.selectResource = jest.fn().mockReturnValue(of(bescheidStateResource)); service.getResource(); @@ -1269,9 +1172,7 @@ describe('BescheidService', () => { }); it('should return value', () => { - service.bescheidResourceService.selectResource = jest - .fn() - .mockReturnValue(of(bescheidStateResource)); + service.bescheidResourceService.selectResource = jest.fn().mockReturnValue(of(bescheidStateResource)); const bescheidDraft: BescheidResource = service.getResource(); @@ -1281,8 +1182,7 @@ describe('BescheidService', () => { describe('getEmpfaenger', () => { it('should return Empfänger', () => { - const vorgangWithEingangResource: VorgangWithEingangResource = - createVorgangWithEingangResource(); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); const vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource> = createStateResource(vorgangWithEingangResource); vorgangService.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); @@ -1306,12 +1206,8 @@ describe('BescheidService', () => { beforeEach(() => { service.filterBySentStatus = jest.fn().mockReturnValue(bescheide); - sortByGermanDateStrSpy = jest - .spyOn(DateUtil, 'sortByGermanDateStr') - .mockReturnValue(bescheide); - getItemsSpy = service.bescheidListResourceService.getItems = jest - .fn() - .mockReturnValue(of(bescheide)); + sortByGermanDateStrSpy = jest.spyOn(DateUtil, 'sortByGermanDateStr').mockReturnValue(bescheide); + getItemsSpy = service.bescheidListResourceService.getItems = jest.fn().mockReturnValue(of(bescheide)); }); it('should get items', () => { @@ -1350,9 +1246,7 @@ describe('BescheidService', () => { beforeEach(() => { service.getBescheidList = jest.fn().mockReturnValue(of(bescheidListStateResource)); service.filterBySentStatus = jest.fn().mockReturnValue(bescheide); - getItemsSpy = service.bescheidListResourceService.getItems = jest - .fn() - .mockReturnValue(of(bescheide)); + getItemsSpy = service.bescheidListResourceService.getItems = jest.fn().mockReturnValue(of(bescheide)); }); it('should get items', () => { @@ -1389,9 +1283,7 @@ describe('BescheidService', () => { status: BescheidStatus.SENT, }; - const filteredBescheide: BescheidResource[] = service.filterBySentStatus([ - bescheidWithSentStatus, - ]); + const filteredBescheide: BescheidResource[] = service.filterBySentStatus([bescheidWithSentStatus]); expect(filteredBescheide.length).toBe(1); }); @@ -1402,9 +1294,7 @@ describe('BescheidService', () => { status: BescheidStatus.DRAFT, }; - const filteredBescheide: BescheidResource[] = service.filterBySentStatus([ - bescheidWithDraftStatus, - ]); + const filteredBescheide: BescheidResource[] = service.filterBySentStatus([bescheidWithDraftStatus]); expect(filteredBescheide.length).toBe(0); }); @@ -1427,13 +1317,9 @@ describe('BescheidService', () => { }); describe('uploadAttachment', () => { - const bescheidResource: BescheidResource = createBescheidResource([ - BescheidLinkRel.UPLOAD_ATTACHMENT, - ]); + const bescheidResource: BescheidResource = createBescheidResource([BescheidLinkRel.UPLOAD_ATTACHMENT]); const file: File = createFile(); - const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource( - createBinaryFileResource(), - ); + const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource(createBinaryFileResource()); beforeEach(() => { binaryFileService.uploadFile.mockReturnValue(of(binaryFileStateResource)); @@ -1450,12 +1336,7 @@ describe('BescheidService', () => { it('should upload file', (done) => { service.uploadAttachment(bescheidResource, file).subscribe(() => { - expect(binaryFileService.uploadFile).toBeCalledWith( - bescheidResource, - BescheidLinkRel.UPLOAD_ATTACHMENT, - file, - false, - ); + expect(binaryFileService.uploadFile).toBeCalledWith(bescheidResource, BescheidLinkRel.UPLOAD_ATTACHMENT, file, false); done(); }); }); @@ -1468,16 +1349,13 @@ describe('BescheidService', () => { }); it('should emit uploaded binary file', () => { - expect(service.uploadAttachment(bescheidResource, file)).toBeObservable( - singleColdCompleted(binaryFileStateResource), - ); + expect(service.uploadAttachment(bescheidResource, file)).toBeObservable(singleColdCompleted(binaryFileStateResource)); }); }); describe('handleAttachmentUpload', () => { describe('on error', () => { - const binaryFileStateResource: StateResource<BinaryFileResource> = - createErrorStateResource(createApiError()); + const binaryFileStateResource: StateResource<BinaryFileResource> = createErrorStateResource(createApiError()); it('should emit upload in progress', () => { service.handleAttachmentUpload(binaryFileStateResource); @@ -1498,9 +1376,7 @@ describe('BescheidService', () => { }); describe('on success', () => { - const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource( - createBinaryFileResource(), - ); + const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource(createBinaryFileResource()); it('should emit upload in progress', () => { service.handleAttachmentUpload(binaryFileStateResource); @@ -1529,4 +1405,26 @@ describe('BescheidService', () => { expect(service.uploadAttachmentInProgress$.value).toEqual(createEmptyStateResource()); }); }); + + describe('exit', () => { + it('should refresh bescheid list', () => { + service.bescheidListResourceService.refresh = jest.fn(); + + service.exit(); + + expect(service.bescheidListResourceService.refresh).toHaveBeenCalled(); + }); + + it('should reload current vorgang', () => { + service.exit(); + + expect(vorgangService.reloadCurrentVorgang).toHaveBeenCalled(); + }); + + it('should reload postfach list', () => { + service.exit(); + + expect(postfachService.setPostfachMailOnReload).toHaveBeenCalled(); + }); + }); }); 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 277891fe474da829e8b3c8adacdcbf233fee73ee..8c76ba58973ec05b98571d86ee1e26da368c5db7 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -1,8 +1,4 @@ -import { - BinaryFileListLinkRel, - BinaryFileResource, - BinaryFileService, -} from '@alfa-client/binary-file-shared'; +import { BinaryFileListLinkRel, BinaryFileResource, BinaryFileService } from '@alfa-client/binary-file-shared'; import { CommandOrder, CommandResource, @@ -12,20 +8,21 @@ import { notHasCommandError, tapOnCommandSuccessfullyDone, } from '@alfa-client/command-shared'; +import { PostfachService } from '@alfa-client/postfach-shared'; import { + EMPTY_ARRAY, + HttpError, + ResourceListService, + StateResource, createEmptyStateResource, createStateResource, - EMPTY_ARRAY, filterIsLoadedOrHasError, getEmbeddedResources, hasStateResourceError, - HttpError, isLoaded, isNotEmpty, isNotNil, - ResourceListService, sortByGermanDateStr, - StateResource, } from '@alfa-client/tech-shared'; import { VorgangCommandService, @@ -35,23 +32,9 @@ import { } from '@alfa-client/vorgang-shared'; import { getEmpfaenger } from '@alfa-client/vorgang-shared-ui'; import { Injectable } from '@angular/core'; -import { getUrl, hasLink, ResourceUri } from '@ngxp/rest'; -import { - BehaviorSubject, - filter, - first, - map, - Observable, - startWith, - Subscription, - switchMap, - take, - tap, -} from 'rxjs'; -import { - ListResourceServiceConfig, - ResourceServiceConfig, -} from '../../../tech-shared/src/lib/resource/resource.model'; +import { ResourceUri, getUrl, hasLink } from '@ngxp/rest'; +import { BehaviorSubject, Observable, Subscription, filter, first, map, startWith, switchMap, take, tap } from 'rxjs'; +import { ListResourceServiceConfig, ResourceServiceConfig } from '../../../tech-shared/src/lib/resource/resource.model'; import { ResourceRepository } from '../../../tech-shared/src/lib/resource/resource.repository'; import { ResourceService } from '../../../tech-shared/src/lib/resource/resource.service'; import { BescheidFacade } from './+state/bescheid.facade'; @@ -61,6 +44,7 @@ import { BescheidListResource, BescheidResource, BescheidStatus, + BescheidWizardStep, UploadFileInProgress, } from './bescheid.model'; import { @@ -76,38 +60,38 @@ import { DocumentResource } from './document.model'; @Injectable({ providedIn: 'root' }) export class BescheidService { + readonly activeStep$: BehaviorSubject<BescheidWizardStep> = new BehaviorSubject(BescheidWizardStep.AntragBescheiden); + bescheidResourceService: ResourceService<VorgangWithEingangResource, BescheidResource>; - bescheidListResourceService: ResourceListService< - VorgangWithEingangResource, - BescheidListResource, - BescheidResource - >; + bescheidListResourceService: ResourceListService<VorgangWithEingangResource, BescheidListResource, BescheidResource>; - readonly bescheidDocumentFile$: BehaviorSubject<StateResource<BinaryFileResource>> = - new BehaviorSubject<StateResource<BinaryFileResource>>(createEmptyStateResource()); + readonly bescheidDocumentFile$: BehaviorSubject<StateResource<BinaryFileResource>> = new BehaviorSubject< + StateResource<BinaryFileResource> + >(createEmptyStateResource()); - readonly bescheidDocumentUri$: BehaviorSubject<ResourceUri> = new BehaviorSubject<ResourceUri>( - null, - ); + readonly bescheidDocumentUri$: BehaviorSubject<ResourceUri> = new BehaviorSubject<ResourceUri>(null); - readonly bescheidDocument$: BehaviorSubject<StateResource<DocumentResource>> = - new BehaviorSubject<StateResource<DocumentResource>>(createEmptyStateResource()); + readonly bescheidDocument$: BehaviorSubject<StateResource<DocumentResource>> = new BehaviorSubject< + StateResource<DocumentResource> + >(createEmptyStateResource()); - readonly bescheidList$: BehaviorSubject<StateResource<BescheidListResource>> = - new BehaviorSubject(createEmptyStateResource<BescheidListResource>()); + readonly bescheidList$: BehaviorSubject<StateResource<BescheidListResource>> = new BehaviorSubject( + createEmptyStateResource<BescheidListResource>(), + ); - readonly createBescheidDocumentInProgress$: BehaviorSubject<StateResource<CommandResource>> = - new BehaviorSubject<StateResource<CommandResource>>(createEmptyStateResource()); + readonly createBescheidDocumentInProgress$: BehaviorSubject<StateResource<CommandResource>> = new BehaviorSubject< + StateResource<CommandResource> + >(createEmptyStateResource()); - readonly uploadBescheidDocumentInProgress$: BehaviorSubject<UploadFileInProgress> = - new BehaviorSubject<UploadFileInProgress>({ loading: false }); + readonly uploadBescheidDocumentInProgress$: BehaviorSubject<UploadFileInProgress> = new BehaviorSubject<UploadFileInProgress>({ + loading: false, + }); - readonly uploadAttachmentInProgress$: BehaviorSubject<UploadFileInProgress> = new BehaviorSubject( - { loading: false }, - ); + readonly uploadAttachmentInProgress$: BehaviorSubject<UploadFileInProgress> = new BehaviorSubject({ loading: false }); - readonly uploadedAttachment$: BehaviorSubject<StateResource<BinaryFileResource>> = - new BehaviorSubject(createEmptyStateResource()); + readonly uploadedAttachment$: BehaviorSubject<StateResource<BinaryFileResource>> = new BehaviorSubject( + createEmptyStateResource(), + ); loadBescheidDocumentSubscription: Subscription; @@ -118,16 +102,22 @@ export class BescheidService { private readonly vorgangCommandService: VorgangCommandService, private readonly binaryFileService: BinaryFileService, private readonly repository: ResourceRepository, + private readonly postfachService: PostfachService, ) { this.bescheidResourceService = new CommandResourceService( this.buildBescheidDraftServiceConfig(), repository, this.commandService, ); - this.bescheidListResourceService = new ResourceListService( - this.buildBescheidListServiceConfig(), - repository, - ); + this.bescheidListResourceService = new ResourceListService(this.buildBescheidListServiceConfig(), repository); + } + + public getActiveStep(): Observable<BescheidWizardStep> { + return this.activeStep$.asObservable(); + } + + public setActiveStep(step: BescheidWizardStep): void { + this.activeStep$.next(step); } buildBescheidDraftServiceConfig(): ResourceServiceConfig<VorgangWithEingangResource> { @@ -158,6 +148,7 @@ export class BescheidService { this.uploadBescheidDocumentInProgress$.next({ loading: false }); this.clearUploadAttachment(); this.uploadAttachmentInProgress$.next({ loading: false }); + this.activeStep$.next(BescheidWizardStep.AntragBescheiden); } public getBescheidDraft(): Observable<StateResource<BescheidResource>> { @@ -190,10 +181,7 @@ export class BescheidService { filter(isLoaded), first(), switchMap((bescheidStateResource: StateResource<BescheidResource>) => - this.bescheidLoeschenUndErstellungUeberspringen( - vorgangWithEingangResource, - bescheidStateResource.resource, - ), + this.bescheidLoeschenUndErstellungUeberspringen(vorgangWithEingangResource, bescheidStateResource.resource), ), ); } @@ -202,8 +190,7 @@ export class BescheidService { return this.vorgangService.getVorgangWithEingang().pipe( filter( (stateResource: StateResource<VorgangWithEingangResource>) => - isLoaded(stateResource) && - hasLink(stateResource.resource, VorgangWithEingangLinkRel.BESCHEID_DRAFT), + isLoaded(stateResource) && hasLink(stateResource.resource, VorgangWithEingangLinkRel.BESCHEID_DRAFT), ), switchMap(() => this.getBescheidDraft()), startWith(createEmptyStateResource<BescheidResource>()), @@ -219,9 +206,7 @@ export class BescheidService { ); } - vorgangAbschliesen( - vorgangWithEingangResource: VorgangWithEingangResource, - ): Observable<StateResource<CommandResource>> { + vorgangAbschliesen(vorgangWithEingangResource: VorgangWithEingangResource): Observable<StateResource<CommandResource>> { return this.vorgangCommandService.abschliessen(vorgangWithEingangResource); } @@ -240,39 +225,25 @@ export class BescheidService { this.uploadedAttachment$.next(createEmptyStateResource()); } - public sendBescheidToAntragsteller( - bescheidResource: BescheidResource, - ): Observable<StateResource<CommandResource>> { + public sendBescheidToAntragsteller(bescheidResource: BescheidResource): Observable<StateResource<CommandResource>> { return this.sendBescheid(bescheidResource, BescheidLinkRel.BESCHEIDEN_UND_SENDEN); } - public sendBescheidManually( - bescheidResource: BescheidResource, - ): Observable<StateResource<CommandResource>> { + public sendBescheidManually(bescheidResource: BescheidResource): Observable<StateResource<CommandResource>> { return this.sendBescheid(bescheidResource, BescheidLinkRel.BESCHEIDEN); } - sendBescheid( - bescheidResource: BescheidResource, - linkRel: string, - ): Observable<StateResource<CommandResource>> { + sendBescheid(bescheidResource: BescheidResource, linkRel: string): Observable<StateResource<CommandResource>> { return this.bescheidResourceService.get().pipe( filterIsLoadedOrHasError(), switchMap((stateResource: StateResource<BescheidResource>) => - this.commandService.createCommandByProps( - buildSendBescheidCommandProps(stateResource.resource, linkRel), - ), + this.commandService.createCommandByProps(buildSendBescheidCommandProps(stateResource.resource, linkRel)), ), ); } - doUpdateBescheid( - bescheidResource: BescheidResource, - bescheid: Bescheid, - ): Observable<StateResource<CommandResource>> { - return this.commandService.createCommandByProps( - buildUpdateBescheidCommandProps(bescheidResource, bescheid), - ); + doUpdateBescheid(bescheidResource: BescheidResource, bescheid: Bescheid): Observable<StateResource<CommandResource>> { + return this.commandService.createCommandByProps(buildUpdateBescheidCommandProps(bescheidResource, bescheid)); } private updateBescheidDraft(command: CommandResource): void { @@ -284,13 +255,9 @@ export class BescheidService { filter(isLoaded), map((stateResource: StateResource<BescheidResource>) => stateResource.resource), filter((resource: BescheidResource) => hasLink(resource, BescheidLinkRel.ATTACHMENTS)), - switchMap((resource: BescheidResource) => - this.binaryFileService.getFiles(resource, BescheidLinkRel.ATTACHMENTS), - ), + switchMap((resource: BescheidResource) => this.binaryFileService.getFiles(resource, BescheidLinkRel.ATTACHMENTS)), filter(isLoaded), - map((stateResource) => - getEmbeddedResources<BinaryFileResource>(stateResource, BinaryFileListLinkRel.FILE_LIST), - ), + map((stateResource) => getEmbeddedResources<BinaryFileResource>(stateResource, BinaryFileListLinkRel.FILE_LIST)), ); } @@ -330,10 +297,7 @@ export class BescheidService { this.bescheidDocumentFile$.next({ ...this.bescheidDocumentFile$.value, loading: true }); } - public uploadBescheidDocument( - bescheid: BescheidResource, - file: File, - ): Observable<UploadFileInProgress> { + public uploadBescheidDocument(bescheid: BescheidResource, file: File): Observable<UploadFileInProgress> { this.clearCreateBescheidDocumentInProgress(); this.initUploadBescheidDocumentInProgress(file.name); this.doUploadBescheidDocument(bescheid, file); @@ -429,14 +393,10 @@ export class BescheidService { } doCreateBescheidDocument(): Observable<StateResource<CommandResource>> { - return this.commandService.createCommandByProps( - buildCreateBescheidDocumentCommandProps(this.getResource()), - ); + return this.commandService.createCommandByProps(buildCreateBescheidDocumentCommandProps(this.getResource())); } - private handleCreateBescheidDocumentResponse( - commandStateResource: StateResource<CommandResource>, - ): void { + private handleCreateBescheidDocumentResponse(commandStateResource: StateResource<CommandResource>): void { this.createBescheidDocumentInProgress$.next(commandStateResource); if (notHasCommandError(commandStateResource.resource)) { const documentUri: ResourceUri = getEffectedResourceUrl(commandStateResource.resource); @@ -498,9 +458,7 @@ export class BescheidService { this.vorgangService.reloadCurrentVorgang(); } - public loadBescheidDocument( - resourceUri: ResourceUri, - ): Observable<StateResource<DocumentResource>> { + public loadBescheidDocument(resourceUri: ResourceUri): Observable<StateResource<DocumentResource>> { return this.repository.getResource<DocumentResource>(resourceUri).pipe( map((documentResource: DocumentResource) => createStateResource(documentResource)), startWith(createEmptyStateResource<DocumentResource>(true)), @@ -524,10 +482,7 @@ export class BescheidService { } private sortByBeschiedenAm(bescheide: BescheidResource[]): BescheidResource[] { - return sortByGermanDateStr<BescheidResource>( - bescheide, - (bescheid: BescheidResource) => bescheid.beschiedenAm, - ); + return sortByGermanDateStr<BescheidResource>(bescheide, (bescheid: BescheidResource) => bescheid.beschiedenAm); } public existBescheid(): Observable<boolean> { @@ -553,17 +508,12 @@ export class BescheidService { this.bescheidListResourceService.refresh(); } - public uploadAttachment( - bescheidResource: BescheidResource, - file: File, - ): Observable<StateResource<BinaryFileResource>> { + public uploadAttachment(bescheidResource: BescheidResource, file: File): Observable<StateResource<BinaryFileResource>> { this.uploadAttachmentInProgress$.next({ fileName: file.name, loading: true }); return this.binaryFileService .uploadFile(bescheidResource, BescheidLinkRel.UPLOAD_ATTACHMENT, file, false) .pipe( - tap((binaryFileStateResource: StateResource<BinaryFileResource>) => - this.handleAttachmentUpload(binaryFileStateResource), - ), + tap((binaryFileStateResource: StateResource<BinaryFileResource>) => this.handleAttachmentUpload(binaryFileStateResource)), ); } @@ -593,4 +543,10 @@ export class BescheidService { public clearAttachmentUpload(): void { this.uploadAttachmentInProgress$.next(createEmptyStateResource()); } + + public exit(): void { + this.bescheidListResourceService.refresh(); + this.vorgangService.reloadCurrentVorgang(); + this.postfachService.setPostfachMailOnReload(); + } } diff --git a/alfa-client/libs/bescheid/src/index.ts b/alfa-client/libs/bescheid/src/index.ts index 9f2c0268f34d8c2099f74dbb30ffae0bed8a8a21..d64bde358e1b807d3996ff5a31c233b0e80fb188 100644 --- a/alfa-client/libs/bescheid/src/index.ts +++ b/alfa-client/libs/bescheid/src/index.ts @@ -1,2 +1,3 @@ +export * from './lib/bescheid-wizard-container/bescheid-wizard-container.component'; export * from './lib/bescheid.module'; export * from './lib/beschieden-date-in-vorgang-container/beschieden-date-in-vorgang-container.component'; diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-in-vorgang-container/bescheid-in-vorgang/bescheid-in-vorgang.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-in-vorgang-container/bescheid-in-vorgang/bescheid-in-vorgang.component.spec.ts index b03163c947ea3ea59ea070eeb8ff9b651da1cd3c..2a5450489d1b8592cb1912e1a11390efe976db78 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-in-vorgang-container/bescheid-in-vorgang/bescheid-in-vorgang.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-in-vorgang-container/bescheid-in-vorgang/bescheid-in-vorgang.component.spec.ts @@ -1,11 +1,8 @@ import { BescheidResource } from '@alfa-client/bescheid-shared'; -import { - BinaryFile2ContainerComponent, - BinaryFileContainerComponent, -} from '@alfa-client/binary-file'; +import { BinaryFile2ContainerComponent, BinaryFileContainerComponent } from '@alfa-client/binary-file'; import { BinaryFileResource } from '@alfa-client/binary-file-shared'; import { StateResource, createStateResource } from '@alfa-client/tech-shared'; -import { getElementFromFixture } from '@alfa-client/test-utils'; +import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ExpansionPanelComponent, SpinnerComponent } from '@alfa-client/ui'; import { registerLocaleData } from '@angular/common'; import localeDe from '@angular/common/locales/de'; @@ -26,16 +23,12 @@ describe('BescheidInVorgangComponent', () => { let fixture: ComponentFixture<BescheidInVorgangComponent>; const bescheidResource: BescheidResource = createBescheidResource(); - const bescheidStateResource: StateResource<BescheidResource> = - createStateResource(bescheidResource); + const bescheidStateResource: StateResource<BescheidResource> = createStateResource(bescheidResource); const binaryFile: BinaryFileResource = createBinaryFileResource(); - const binaryFileStateResource: StateResource<BinaryFileResource> = - createStateResource(binaryFile); + const binaryFileStateResource: StateResource<BinaryFileResource> = createStateResource(binaryFile); - const bescheidDocumentBinaryFileContainer = getDataTestIdOf( - 'bescheid-document-in-vorgang-binary-file', - ); + const bescheidDocumentBinaryFileContainer: string = getDataTestIdOf('bescheid-document-in-vorgang-binary-file'); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -64,20 +57,18 @@ describe('BescheidInVorgangComponent', () => { describe('render', () => { it('should show bescheid document file', () => { component.bescheidDocumentFile = createStateResource(createBinaryFileResource()); - fixture.detectChanges(); - const element = getElementFromFixture(fixture, bescheidDocumentBinaryFileContainer); + fixture.detectChanges(); - expect(element).toBeInstanceOf(HTMLElement); + existsAsHtmlElement(fixture, bescheidDocumentBinaryFileContainer); }); it('should NOT show bescheid document file if resource null', () => { component.bescheidDocumentFile = { ...createStateResource(null), loaded: true }; - fixture.detectChanges(); - const element = getElementFromFixture(fixture, bescheidDocumentBinaryFileContainer); + fixture.detectChanges(); - expect(element).not.toBeInstanceOf(HTMLElement); + notExistsAsHtmlElement(fixture, bescheidDocumentBinaryFileContainer); }); it('should NOT show bescheid document file if resource loading', () => { @@ -85,11 +76,10 @@ describe('BescheidInVorgangComponent', () => { ...createStateResource(createBinaryFileResource()), loaded: false, }; - fixture.detectChanges(); - const element = getElementFromFixture(fixture, bescheidDocumentBinaryFileContainer); + fixture.detectChanges(); - expect(element).not.toBeInstanceOf(HTMLElement); + notExistsAsHtmlElement(fixture, bescheidDocumentBinaryFileContainer); }); }); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.ts index d8607e426d22e858d48673a678a3f593a2c32824..3ebd9ece73f85fe24a2817f27cef4df6acfd31b9 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.ts @@ -1,9 +1,4 @@ -import { - BescheidLinkRel, - BescheidListLinkRel, - BescheidListResource, - BescheidStatus, -} from '@alfa-client/bescheid-shared'; +import { BescheidLinkRel, BescheidListLinkRel, BescheidListResource, BescheidStatus } from '@alfa-client/bescheid-shared'; import { Component, Input } from '@angular/core'; @Component({ @@ -12,7 +7,7 @@ import { Component, Input } from '@angular/core'; styles: [], }) export class BescheidListInVorgangComponent { - @Input() bescheidList: BescheidListResource; + @Input() public bescheidList: BescheidListResource; public readonly bescheidListLinkRel = BescheidListLinkRel; public readonly bescheidLinkRel = BescheidLinkRel; diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..47feee904d3ac5176801d46f6ef374184cb5cfcb --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.html @@ -0,0 +1,30 @@ +<div class="relative z-10 duration-500 ease-in-out" aria-label="Bescheid Dialog" role="dialog" aria-modal="true"> + <div class="fixed inset-0 z-10 w-screen overflow-y-auto"> + <div class="flex h-full items-center justify-center p-8"> + <div + class="relative h-full w-full max-w-7xl transform overflow-hidden rounded-lg bg-background-200 px-6 py-10 text-left shadow-xl transition-all" + > + <ng-container *ngIf="bescheidDraftStateResource$ | async as bescheidStateResource"> + <ods-button + variant="icon" + size="fit" + class="absolute right-0 top-0 text-text" + (clickEmitter)="cancelWizard(bescheidStateResource.resource)" + data-test-id="close-bescheid" + > + <ods-close-icon icon /> + </ods-button> + <alfa-bescheid-wizard + [vorgangWithEingangResource]="vorgangWithEingangResource" + [activeStep]="bescheidService.getActiveStep() | async" + [bescheidStateResource]="bescheidStateResource" + [submitStateResource]="submitStateResource$ | async" + (weiterClickEmitter)="onWeiter($event)" + (vorgangAbgeschlossen)="onVorgangAbgeschlossen()" + data-test-id="bescheid-wizard" + ></alfa-bescheid-wizard> + </ng-container> + </div> + </div> + </div> +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e09cca2f663a659a867467375030620b111cb82b --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.spec.ts @@ -0,0 +1,557 @@ +import { BescheidResource, BescheidService, BescheidWizardDialogResult, BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { CommandOrder, CommandResource } from '@alfa-client/command-shared'; +import { + ESCAPE_KEY, + StateResource, + createEmptyStateResource, + createErrorStateResource, + createStateResource, +} from '@alfa-client/tech-shared'; +import { + DialogRefMock, + Mock, + createDialogRefMock, + existsAsHtmlElement, + getElementFromFixtureByType, + mock, + triggerEvent, + useFromMock, +} from '@alfa-client/test-utils'; +import { OzgcloudDialogService } from '@alfa-client/ui'; +import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ButtonComponent, CloseIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; +import { EMPTY, of } from 'rxjs'; +import { createBescheidResource, createBescheidStateResource } from '../../../../bescheid-shared/src/test/bescheid'; +import { + createCommandErrorResource, + createCommandResource, + createSuccessfullyDoneCommandResource, + createSuccessfullyDoneCommandStateResource, +} from '../../../../command-shared/test/command'; +import { getDataTestIdOf } from '../../../../tech-shared/test/data-test'; +import { createApiError } from '../../../../tech-shared/test/error'; +import { singleColdCompleted } from '../../../../tech-shared/test/marbles'; +import { createKeydownKeyboardEvent } from '../../../../test-utils/src/lib/keyboard'; +import { createVorgangWithEingangResource } from '../../../../vorgang-shared/test/vorgang'; +import { BescheidWizardContainerComponent } from './bescheid-wizard-container.component'; +import { BescheidWizardComponent } from './bescheid-wizard/bescheid-wizard.component'; +import { + BescheidWizardCancelDialogContainerComponent, + CancelWizardDialogData, + CancelWizardDialogResult, +} from './bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component'; +import { BescheidFormService } from './bescheid.formservice'; + +describe('BescheidWizardContainerComponent', () => { + let component: BescheidWizardContainerComponent; + let fixture: ComponentFixture<BescheidWizardContainerComponent>; + + const bescheidWizard: string = getDataTestIdOf('bescheid-wizard'); + const closeButton: string = getDataTestIdOf('close-bescheid'); + + let bescheidService: Mock<BescheidService>; + let ozgcloudDialogService: Mock<OzgcloudDialogService>; + let formService: Mock<BescheidFormService>; + let wizardDialogRef: DialogRefMock<BescheidWizardDialogResult>; + + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); + + beforeEach(() => { + bescheidService = mock(BescheidService); + ozgcloudDialogService = mock(OzgcloudDialogService); + formService = mock(BescheidFormService); + wizardDialogRef = createDialogRefMock(); + }); + + beforeEach(async () => { + TestBed.overrideComponent(BescheidWizardContainerComponent, { + set: { + providers: [ + { + provide: BescheidFormService, + useValue: formService, + }, + ], + }, + }); + + await TestBed.configureTestingModule({ + declarations: [ + BescheidWizardContainerComponent, + MockComponent(BescheidWizardComponent), + MockComponent(BescheidWizardCancelDialogContainerComponent), + MockComponent(ButtonComponent), + MockComponent(CloseIconComponent), + ], + providers: [ + { + provide: DIALOG_DATA, + useValue: { vorgangWithEingangResource }, + }, + { + provide: BescheidService, + useValue: bescheidService, + }, + { + provide: OzgcloudDialogService, + useValue: ozgcloudDialogService, + }, + { + provide: DialogRef, + useValue: wizardDialogRef, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('component', () => { + describe('constructor', () => { + it('should set vorgang with eingang resource from dialog data', () => { + expect(component.vorgangWithEingangResource).toBe(vorgangWithEingangResource); + }); + }); + + describe('ngOnInit', () => { + it('should init service', () => { + component.ngOnInit(); + + expect(bescheidService.init).toHaveBeenCalled(); + }); + + it('should set vorgang with eingang on form service', () => { + formService.setVorgangWithEingangResource = jest.fn(); + + component.ngOnInit(); + + expect(formService.setVorgangWithEingangResource).toHaveBeenCalledWith(vorgangWithEingangResource); + }); + + it('should subscribe to bescheid resource', () => { + component.subscribeToBescheidResourceIfExists = jest.fn(); + + component.ngOnInit(); + + expect(component.subscribeToBescheidResourceIfExists).toHaveBeenCalled(); + }); + + it('should subscribe escape key handler', () => { + component.handleEscapeKey = jest.fn(); + + component.ngOnInit(); + + expect(component.handleEscapeKey).toHaveBeenCalled(); + }); + }); + + describe('subscribeToBescheidResourceIfExists', () => { + const bescheidDraftStateResource: StateResource<BescheidResource> = createBescheidStateResource(); + + beforeEach(() => { + bescheidService.getBescheidDraft.mockReturnValue(of(bescheidDraftStateResource)); + }); + + describe('when bescheid draft link exist', () => { + beforeEach(() => { + component.vorgangWithEingangResource = createVorgangWithEingangResource([VorgangWithEingangLinkRel.BESCHEID_DRAFT]); + }); + + it('should get bescheid draft', () => { + component.subscribeToBescheidResourceIfExists(); + + expect(bescheidService.getBescheidDraft).toHaveBeenCalled(); + }); + + it('should emit bescheid draft state resource', () => { + component.subscribeToBescheidResourceIfExists(); + + expect(component.bescheidDraftStateResource$).toBeObservable(singleColdCompleted(bescheidDraftStateResource)); + }); + }); + + describe('when bescheid draft link NOT exists', () => { + beforeEach(() => { + component.vorgangWithEingangResource = createVorgangWithEingangResource(); + }); + + it('should not get bescheid draft', () => { + component.subscribeToBescheidResourceIfExists(); + + expect(bescheidService.getBescheidDraft).not.toHaveBeenCalled(); + }); + + it('should emit empty bescheid draft state resource', () => { + component.subscribeToBescheidResourceIfExists(); + + expect(component.bescheidDraftStateResource$).toBeObservable( + singleColdCompleted(createEmptyStateResource<BescheidResource>()), + ); + }); + + it('should not patch form values', () => { + formService.patchValues = jest.fn(); + + component.subscribeToBescheidResourceIfExists(); + + expect(formService.patchValues).not.toHaveBeenCalled(); + }); + }); + }); + + describe('onWeiter', () => { + beforeEach(() => { + formService.submit = jest.fn().mockReturnValue(of(createSuccessfullyDoneCommandStateResource())); + component.isBescheidSuccessfullyCreated = jest.fn().mockReturnValue(true); + bescheidService.getBescheidDraft.mockReturnValue(of(createBescheidStateResource())); + }); + + it('should submit form', () => { + component.onWeiter(BescheidWizardStep.DokumenteHochladen); + + expect(formService.submit).toHaveBeenCalled(); + }); + + it('should increment step on created bescheid', () => { + component.onWeiter(BescheidWizardStep.DokumenteHochladen); + + component.submitStateResource$.subscribe(); + expect(bescheidService.setActiveStep).toHaveBeenCalledWith(BescheidWizardStep.DokumenteHochladen); + }); + + it('should increment step on updated bescheid', () => { + component.isBescheidSuccessfullyCreated = jest.fn().mockReturnValue(false); + + component.onWeiter(BescheidWizardStep.DokumenteHochladen); + + component.submitStateResource$.subscribe(); + expect(bescheidService.setActiveStep).toHaveBeenCalledWith(BescheidWizardStep.DokumenteHochladen); + }); + + it('should not increment step on command resource error', () => { + component.isBescheidSuccessfullyCreated = jest.fn().mockReturnValue(false); + formService.submit = jest.fn().mockReturnValue(of(createErrorStateResource(createApiError()))); + + component.onWeiter(BescheidWizardStep.DokumenteHochladen); + + component.submitStateResource$.subscribe(); + expect(bescheidService.setActiveStep).not.toHaveBeenCalled(); + }); + + it('should not increment step on command resource loading', () => { + component.isBescheidSuccessfullyCreated = jest.fn().mockReturnValue(false); + formService.submit = jest.fn().mockReturnValue(of(createStateResource(createCommandResource(), true))); + + component.onWeiter(BescheidWizardStep.DokumenteHochladen); + + component.submitStateResource$.subscribe(); + expect(bescheidService.setActiveStep).not.toHaveBeenCalled(); + }); + + it('should not increment step on bescheid resource error', () => { + bescheidService.getBescheidDraft.mockReturnValue(of(createErrorStateResource(createApiError()))); + + component.onWeiter(BescheidWizardStep.DokumenteHochladen); + + component.submitStateResource$.subscribe(); + expect(bescheidService.setActiveStep).not.toHaveBeenCalled(); + }); + + it('should not increment step on bescheid loading', () => { + bescheidService.getBescheidDraft.mockReturnValue(of(createStateResource(null, true))); + + component.onWeiter(BescheidWizardStep.DokumenteHochladen); + + component.submitStateResource$.subscribe(); + expect(bescheidService.setActiveStep).not.toHaveBeenCalled(); + }); + }); + + describe('isBescheidSuccessfullyCreated', () => { + it('should return true', () => { + const createBescheidCommandResource: CommandResource = { + ...createSuccessfullyDoneCommandResource(), + order: CommandOrder.CREATE_BESCHEID, + }; + const isBescheidSuccessfullyCreated: boolean = component.isBescheidSuccessfullyCreated(createBescheidCommandResource); + + expect(isBescheidSuccessfullyCreated).toBeTruthy(); + }); + + it('should return false on different order', () => { + const createBescheidCommandResource: CommandResource = { + ...createSuccessfullyDoneCommandResource(), + order: CommandOrder.UPDATE_BESCHEID, + }; + const isBescheidSuccessfullyCreated: boolean = component.isBescheidSuccessfullyCreated(createBescheidCommandResource); + + expect(isBescheidSuccessfullyCreated).toBeFalsy(); + }); + + it('should return false on error command', () => { + const createBescheidCommandResource: CommandResource = createCommandErrorResource(); + const isBescheidSuccessfullyCreated: boolean = component.isBescheidSuccessfullyCreated(createBescheidCommandResource); + + expect(isBescheidSuccessfullyCreated).toBeFalsy(); + }); + }); + + describe('cancelWizard', () => { + beforeEach(() => { + component.openCancelDialog = jest.fn(); + component.subscribeToCancelDialogClosed = jest.fn(); + }); + + it('should open cancel dialog', () => { + const bescheidResource: BescheidResource = createBescheidResource(); + + component.cancelWizard(bescheidResource); + + expect(component.openCancelDialog).toHaveBeenCalledWith(bescheidResource); + }); + + it('should NOT open cancel dialog', () => { + component.cancelDialogRef = createDialogRefMock() as any; + const bescheidResource: BescheidResource = createBescheidResource(); + + component.cancelWizard(bescheidResource); + + expect(component.openCancelDialog).not.toHaveBeenCalled(); + }); + + it('should handle dialog close', () => { + const bescheidResource: BescheidResource = createBescheidResource(); + + component.cancelWizard(bescheidResource); + + expect(component.subscribeToCancelDialogClosed).toHaveBeenCalled(); + }); + + it('should NOT handle dialog close', () => { + component.cancelDialogRef = createDialogRefMock() as any; + const bescheidResource: BescheidResource = createBescheidResource(); + + component.cancelWizard(bescheidResource); + + expect(component.subscribeToCancelDialogClosed).not.toHaveBeenCalled(); + }); + }); + + describe('openCancelDialog', () => { + it('should show close dialog', () => { + const bescheidResource: BescheidResource = createBescheidResource(); + ozgcloudDialogService.openInCallingComponentContext.mockReturnValue({ + closed: EMPTY, + }); + + component.openCancelDialog(bescheidResource); + + expect(ozgcloudDialogService.openInCallingComponentContext).toHaveBeenCalledWith( + BescheidWizardCancelDialogContainerComponent, + component.viewContainerRef, + { + bescheidResource, + } as CancelWizardDialogData, + ); + }); + + it('should set cancelDialogRef', () => { + const bescheidResource: BescheidResource = createBescheidResource(); + ozgcloudDialogService.openInCallingComponentContext.mockReturnValue({ + closed: EMPTY, + }); + + component.openCancelDialog(bescheidResource); + + expect(component.cancelDialogRef).toBeDefined(); + }); + }); + + describe('subscribeToCancelDialogClosed', () => { + let cancelDialogResult: CancelWizardDialogResult; + + beforeEach(() => { + cancelDialogResult = { closeWizard: true }; + component.cancelDialogRef = { ...createDialogRefMock(), closed: of(cancelDialogResult) } as any; + component.handleCancelDialogClosed = jest.fn(); + }); + + it('should handle cancel dialog closed', () => { + component.subscribeToCancelDialogClosed(); + + expect(component.handleCancelDialogClosed).toHaveBeenCalledWith(cancelDialogResult); + }); + }); + + describe('handleCancelDialogClosed', () => { + it('should unset cancelDialogRef', () => { + component.cancelDialogRef = useFromMock(mock(DialogRef<CancelWizardDialogResult>)); + + component.handleCancelDialogClosed({ closeWizard: true }); + + expect(component.cancelDialogRef).toBeNull(); + }); + + it('should close wizard', () => { + component.handleCancelDialogClosed({ closeWizard: true }); + + expect(wizardDialogRef.close).toHaveBeenCalledWith({ reloadVorgang: true }); + }); + + it('should NOT close wizard', () => { + component.handleCancelDialogClosed({ closeWizard: false }); + + expect(wizardDialogRef.close).not.toHaveBeenCalled(); + }); + + it('should NOT close wizard on empty result', () => { + component.handleCancelDialogClosed(null); + + expect(wizardDialogRef.close).not.toHaveBeenCalled(); + }); + }); + + describe('handleEscapeKey', () => { + beforeEach(() => { + component.cancelWizard = jest.fn(); + }); + + it('should close wizard if bescheid resource loaded', () => { + const bescheidResource: BescheidResource = createBescheidResource(); + component.bescheidDraftStateResource$ = of(createStateResource(bescheidResource)); + wizardDialogRef.keydownEvents = of(createKeydownKeyboardEvent(ESCAPE_KEY)); + + component.handleEscapeKey(); + + expect(component.cancelWizard).toHaveBeenCalledWith(bescheidResource); + }); + + it('should close wizard if bescheid resource not exists', () => { + component.bescheidDraftStateResource$ = of(createEmptyStateResource<BescheidResource>()); + wizardDialogRef.keydownEvents = of(createKeydownKeyboardEvent(ESCAPE_KEY)); + + component.handleEscapeKey(); + + expect(component.cancelWizard).toHaveBeenCalledWith(null); + }); + + it('should not close if no escape key pressed', () => { + const bescheidResource: BescheidResource = createBescheidResource(); + component.bescheidDraftStateResource$ = of(createStateResource(bescheidResource)); + wizardDialogRef.keydownEvents = of(createKeydownKeyboardEvent('a')); + + component.handleEscapeKey(); + + expect(component.cancelWizard).not.toHaveBeenCalled(); + }); + + it('should not close if bescheid resource loading', () => { + const bescheidResource: BescheidResource = createBescheidResource(); + component.bescheidDraftStateResource$ = of(createStateResource(bescheidResource, true)); + wizardDialogRef.keydownEvents = of(createKeydownKeyboardEvent(ESCAPE_KEY)); + + component.handleEscapeKey(); + + expect(component.cancelWizard).not.toHaveBeenCalled(); + }); + }); + + describe('onVorgangAbgeschlossen', () => { + it('should close wizard', () => { + component.onVorgangAbgeschlossen(); + + expect(wizardDialogRef.close).toHaveBeenCalled(); + }); + }); + }); + + describe('template', () => { + describe('alfa-bescheid-wizard', () => { + function getElementComponent(): BescheidWizardComponent { + return getElementFromFixtureByType(fixture, BescheidWizardComponent); + } + + describe('input', () => { + it('should set vorgangWithEingangResource', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource(); + + fixture.detectChanges(); + + expect(getElementComponent().vorgangWithEingangResource).toBe(component.vorgangWithEingangResource); + }); + + it('should set activeStep', () => { + bescheidService.getActiveStep.mockReturnValue(of(2)); + + fixture.detectChanges(); + + expect(getElementComponent().activeStep).toBe(2); + }); + + it('should set bescheidStateResource', () => { + const bescheidStateResource: StateResource<BescheidResource> = createBescheidStateResource(); + component.bescheidDraftStateResource$ = of(bescheidStateResource); + + fixture.detectChanges(); + + expect(getElementComponent().bescheidStateResource).toBe(bescheidStateResource); + }); + }); + + describe('output', () => { + it('should call onWeiter', () => { + component.onWeiter = jest.fn(); + + triggerEvent({ + fixture, + name: 'weiterClickEmitter', + elementSelector: bescheidWizard, + }); + + expect(component.onWeiter).toHaveBeenCalled(); + }); + + it('should call onVorgangAbgeschlossen', () => { + component.onVorgangAbgeschlossen = jest.fn(); + + triggerEvent({ + fixture, + name: 'vorgangAbgeschlossen', + elementSelector: bescheidWizard, + }); + + expect(component.onVorgangAbgeschlossen).toHaveBeenCalled(); + }); + }); + }); + + describe('ods-button close', () => { + it('should show', () => { + existsAsHtmlElement(fixture, closeButton); + }); + + describe('output', () => { + it('should call cancelWizard', () => { + component.cancelWizard = jest.fn(); + + triggerEvent({ + fixture, + name: 'clickEmitter', + elementSelector: closeButton, + }); + + expect(component.cancelWizard).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e9ed187540a5921430c404a9a7a3d3020e0e70bd --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard-container.component.ts @@ -0,0 +1,138 @@ +import { BescheidResource, BescheidService, BescheidWizardDialogResult, BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { CommandOrder, CommandResource, isSuccessfulDone } from '@alfa-client/command-shared'; +import { + StateResource, + createEmptyStateResource, + hasStateResourceError, + isEscapeKey, + isLoaded, + isNotLoading, + isNotNil, +} from '@alfa-client/tech-shared'; +import { OzgcloudDialogService } from '@alfa-client/ui'; +import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; +import { Component, Inject, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; +import { Resource, hasLink } from '@ngxp/rest'; +import { Observable, Subscription, filter, first, of, switchMap, tap } from 'rxjs'; +import { + BescheidWizardCancelDialogContainerComponent, + CancelWizardDialogData, + CancelWizardDialogResult, +} from './bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component'; +import { BescheidFormService } from './bescheid.formservice'; + +export interface BescheidWizardDialogData { + vorgangWithEingangResource: VorgangWithEingangResource; +} + +@Component({ + selector: 'alfa-bescheid-wizard-container', + templateUrl: './bescheid-wizard-container.component.html', + providers: [BescheidFormService], +}) +export class BescheidWizardContainerComponent implements OnInit, OnDestroy { + public bescheidDraftStateResource$: Observable<StateResource<BescheidResource>> = + of(createEmptyStateResource<BescheidResource>()); + public submitStateResource$: Observable<StateResource<Resource>> = of(createEmptyStateResource<Resource>()); + + cancelDialogRef: DialogRef<CancelWizardDialogResult>; + vorgangWithEingangResource: VorgangWithEingangResource; + keydownEventsSubscription: Subscription; + + constructor( + @Inject(DIALOG_DATA) private readonly dialogData: BescheidWizardDialogData, + private readonly dialogRef: DialogRef<BescheidWizardDialogResult>, + readonly viewContainerRef: ViewContainerRef, + public readonly formService: BescheidFormService, + readonly bescheidService: BescheidService, + private readonly ozgcloudDialogService: OzgcloudDialogService, + ) { + this.vorgangWithEingangResource = dialogData.vorgangWithEingangResource; + } + + ngOnInit(): void { + this.bescheidService.init(); + this.formService.setVorgangWithEingangResource(this.vorgangWithEingangResource); + this.subscribeToBescheidResourceIfExists(); + this.handleEscapeKey(); + } + + ngOnDestroy(): void { + this.keydownEventsSubscription.unsubscribe(); + } + + subscribeToBescheidResourceIfExists(): void { + if (hasLink(this.vorgangWithEingangResource, VorgangWithEingangLinkRel.BESCHEID_DRAFT)) { + this.bescheidDraftStateResource$ = this.bescheidService.getBescheidDraft(); + } + } + + handleEscapeKey(): void { + this.keydownEventsSubscription = this.dialogRef.keydownEvents + .pipe( + filter(isEscapeKey), + switchMap(() => this.bescheidDraftStateResource$), + filter(isNotLoading), + ) + .subscribe((bescheidStateResource: StateResource<BescheidResource>) => { + this.cancelWizard(bescheidStateResource.resource); + }); + } + + public onWeiter(nextStep: BescheidWizardStep): void { + this.submitStateResource$ = this.formService.submit().pipe( + switchMap((commandStateResource: StateResource<CommandResource>) => { + if (this.isBescheidSuccessfullyCreated(commandStateResource.resource)) { + this.bescheidDraftStateResource$ = this.bescheidService.getBescheidDraft(); + return this.bescheidDraftStateResource$; + } + return of(commandStateResource); + }), + tap((stateResource: StateResource<Resource>) => { + if (isLoaded(stateResource) && !hasStateResourceError(stateResource)) { + this.bescheidService.setActiveStep(nextStep); + } + }), + ); + } + + isBescheidSuccessfullyCreated(commandResource: CommandResource): boolean { + return isSuccessfulDone(commandResource) && commandResource.order === CommandOrder.CREATE_BESCHEID; + } + + public cancelWizard(bescheidResource: BescheidResource): void { + if (isNotNil(this.cancelDialogRef)) return; + this.openCancelDialog(bescheidResource); + this.subscribeToCancelDialogClosed(); + } + + openCancelDialog(bescheidResource: BescheidResource) { + const dialogData: CancelWizardDialogData = { + bescheidResource, + }; + + this.cancelDialogRef = this.ozgcloudDialogService.openInCallingComponentContext( + BescheidWizardCancelDialogContainerComponent, + this.viewContainerRef, + dialogData, + ); + } + + subscribeToCancelDialogClosed() { + this.cancelDialogRef.closed + .pipe(first()) + .subscribe((result: CancelWizardDialogResult) => this.handleCancelDialogClosed(result)); + } + + handleCancelDialogClosed(closeResult: CancelWizardDialogResult): void { + this.cancelDialogRef = null; + if (isNotNil(closeResult) && closeResult.closeWizard) { + this.dialogRef.close({ reloadVorgang: true }); + } + } + + public onVorgangAbgeschlossen(): void { + this.dialogRef.close(); + } +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9e9a62b72411f5926b2753c20eb2126957b0c626 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.html @@ -0,0 +1,7 @@ +<button + (click)="onClick()" + data-test-id="bescheid-ueberspringen" + class="mt-6 select-none text-left text-primary hover:underline" +> + Bescheiderstellung überspringen<br />und abschließen +</button> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e627c499b0e4c4718fa60bdae4d29fe0641ec48 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.spec.ts @@ -0,0 +1,130 @@ +import { + createDialogRefMock, + DialogRefMock, + existsAsHtmlElement, + mock, + Mock, + triggerEvent, + useFromMock, +} from '@alfa-client/test-utils'; +import { OzgcloudDialogService } from '@alfa-client/ui'; +import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { EventEmitter } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { of } from 'rxjs'; +import { createSuccessfullyDoneCommandStateResource } from '../../../../../../command-shared/test/command'; +import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test'; +import { + createDialogResult, + OzgcloudDialogCommandResult, +} from '../../../../../../ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result'; +import { createVorgangWithEingangResource } from '../../../../../../vorgang-shared/test/vorgang'; +import { + AbschliessenDialogData, + BescheidWizardAbschliessenDialogContainerComponent, +} from '../abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component'; +import { BescheidWizardAbschliessenButtonComponent } from './bescheid-wizard-abschliessen-button.component'; + +describe('BescheidWizardAbschliessenButtonComponent', () => { + let component: BescheidWizardAbschliessenButtonComponent; + let fixture: ComponentFixture<BescheidWizardAbschliessenButtonComponent>; + + const abschliessenButton: string = getDataTestIdOf('bescheid-ueberspringen'); + + let ozgcloudDialogService: Mock<OzgcloudDialogService>; + + beforeEach(() => { + ozgcloudDialogService = mock(OzgcloudDialogService); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardAbschliessenButtonComponent], + providers: [ + { + provide: OzgcloudDialogService, + useValue: ozgcloudDialogService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardAbschliessenButtonComponent); + component = fixture.componentInstance; + component.vorgangAbgeschlossen = useFromMock(mock(EventEmitter<void>)); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('component', () => { + describe('onClick', () => { + beforeEach(() => { + component.handleDialogClosed = jest.fn(); + }); + + it('should open dialog', () => { + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); + component.vorgangWithEingangResource = vorgangWithEingangResource; + + component.onClick(); + + expect(ozgcloudDialogService.openInCallingComponentContext).toHaveBeenCalledWith( + BescheidWizardAbschliessenDialogContainerComponent, + component.viewContainerRef, + { vorgangWithEingangResource } as AbschliessenDialogData, + ); + }); + + it('should handle dialog closed', () => { + const dialogRef: DialogRefMock<OzgcloudDialogCommandResult> = createDialogRefMock(); + ozgcloudDialogService.openInCallingComponentContext.mockReturnValue(dialogRef); + + component.onClick(); + + expect(component.handleDialogClosed).toHaveBeenCalledWith(dialogRef); + }); + }); + }); + + describe('handleDialogClosed', () => { + let dialogRef: DialogRefMock<OzgcloudDialogCommandResult>; + + beforeEach(() => { + dialogRef = createDialogRefMock(); + }); + + it('should NOT emit vorgang abgeschlossen', () => { + dialogRef.closed = of(undefined); + + component.handleDialogClosed(dialogRef as never); + + expect(component.vorgangAbgeschlossen.emit).not.toHaveBeenCalled(); + }); + + it('should emit vorgang abgeschlossen', () => { + dialogRef.closed = of(createDialogResult(createSuccessfullyDoneCommandStateResource())); + + component.handleDialogClosed(dialogRef as never); + + expect(component.vorgangAbgeschlossen.emit).toHaveBeenCalled(); + }); + }); + + describe('template', () => { + it('should have button', () => { + existsAsHtmlElement(fixture, abschliessenButton); + }); + + describe('output', () => { + it('should call onClick', () => { + component.onClick = jest.fn(); + + triggerEvent({ fixture, name: 'click', elementSelector: abschliessenButton }); + + expect(component.onClick).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5e04e99d2b1342e528148b124f613a044906965 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component.ts @@ -0,0 +1,49 @@ +import { OzgcloudDialogService } from '@alfa-client/ui'; +import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { DialogRef } from '@angular/cdk/dialog'; +import { Component, EventEmitter, Input, Output, ViewContainerRef } from '@angular/core'; +import { filter } from 'rxjs'; +import { + OzgcloudDialogCommandResult, + isDialogSuccessfullyCompleted, +} from '../../../../../../ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result'; +import { + AbschliessenDialogData, + BescheidWizardAbschliessenDialogContainerComponent, +} from '../abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component'; + +@Component({ + selector: 'alfa-bescheid-wizard-abschliessen-button', + templateUrl: './bescheid-wizard-abschliessen-button.component.html', +}) +export class BescheidWizardAbschliessenButtonComponent { + @Input() vorgangWithEingangResource: VorgangWithEingangResource; + + @Output() vorgangAbgeschlossen: EventEmitter<void> = new EventEmitter<void>(); + + constructor( + private readonly ozgclouDialogService: OzgcloudDialogService, + readonly viewContainerRef: ViewContainerRef, + ) {} + + public onClick(): void { + const dialogData: AbschliessenDialogData = { + vorgangWithEingangResource: this.vorgangWithEingangResource, + }; + + const dialogRef: DialogRef<OzgcloudDialogCommandResult> = + this.ozgclouDialogService.openInCallingComponentContext( + BescheidWizardAbschliessenDialogContainerComponent, + this.viewContainerRef, + dialogData, + ); + + this.handleDialogClosed(dialogRef); + } + + handleDialogClosed(dialogRef: DialogRef<OzgcloudDialogCommandResult>): void { + dialogRef.closed + .pipe(filter(isDialogSuccessfullyCompleted)) + .subscribe(() => this.vorgangAbgeschlossen.emit()); + } +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2bdd79f315eb2a6473cd8212ecc895638d9544d4 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.html @@ -0,0 +1,40 @@ +<div class="relative m-6 max-w-2xl rounded-lg bg-modalBg p-6 shadow-xl" data-test-id="bescheid-ueberspringen-dialog"> + <button + class="absolute right-4 top-4 flex size-12 items-center justify-center rounded-full hover:bg-background-100" + (click)="onClose()" + data-test-id="close-bescheid-button" + > + <mat-icon>close</mat-icon> + </button> + + <div class="flex flex-col gap-6"> + <div> + <h4 class="text-lg font-medium text-primary">Bescheiderstellung überspringen</h4> + </div> + <div class="grow"> + <p class="text-base"> + Soll die Bescheiderstellung übersprungen und der Vorgang direkt in den Status Abgeschlossen gesetzt werden? + </p> + </div> + <div class="flex gap-4"> + <ozgcloud-stroked-button-with-spinner + (click)="onConfirm()" + data-test-id="ueberspringen-abschliessen-button" + text="Überspringen und abschließen" + type="submit" + icon="check" + [stateResource]="abschliessen$ | async" + > + </ozgcloud-stroked-button-with-spinner> + <ozgcloud-stroked-button-with-spinner + (click)="onClose()" + data-test-id="ueberspringen-abbrechen-button" + text="Abbrechen" + color="" + icon="clear" + type="submit" + > + </ozgcloud-stroked-button-with-spinner> + </div> + </div> +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa2987b0468830953c4d0a7d86c9d66f3e57e27e --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.spec.ts @@ -0,0 +1,173 @@ +import { BescheidService } from '@alfa-client/bescheid-shared'; +import { CommandResource } from '@alfa-client/command-shared'; +import { StateResource } from '@alfa-client/tech-shared'; +import { + createDialogRefMock, + DialogRefMock, + getElementComponentFromFixtureByCss, + mock, + Mock, + triggerEvent, +} from '@alfa-client/test-utils'; +import { OzgcloudStrokedButtonWithSpinnerComponent } from '@alfa-client/ui'; +import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatIcon } from '@angular/material/icon'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { createSuccessfullyDoneCommandStateResource } from '../../../../../../command-shared/test/command'; +import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test'; +import { singleColdCompleted } from '../../../../../../tech-shared/test/marbles'; +import { createDialogResult } from '../../../../../../ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result'; +import { createVorgangWithEingangResource } from '../../../../../../vorgang-shared/test/vorgang'; +import { + AbschliessenDialogData, + BescheidWizardAbschliessenDialogContainerComponent, +} from './bescheid-wizard-abschliessen-dialog-container.component'; + +describe('BescheidWizardAbschliessenDialogContainerComponent', () => { + let component: BescheidWizardAbschliessenDialogContainerComponent; + let fixture: ComponentFixture<BescheidWizardAbschliessenDialogContainerComponent>; + + const closeBescheidButton: string = getDataTestIdOf('close-bescheid-button'); + const confirmButtonDataTestId: string = getDataTestIdOf('ueberspringen-abschliessen-button'); + const cancelButtonDataTestId: string = getDataTestIdOf('ueberspringen-abbrechen-button'); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); + const dialogData: AbschliessenDialogData = { vorgangWithEingangResource }; + + let dialogRef: DialogRefMock; + let bescheidService: Mock<BescheidService>; + + beforeEach(() => { + bescheidService = mock(BescheidService); + dialogRef = createDialogRefMock(); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + BescheidWizardAbschliessenDialogContainerComponent, + MockComponent(MatIcon), + MockComponent(OzgcloudStrokedButtonWithSpinnerComponent), + ], + providers: [ + { + provide: DIALOG_DATA, + useValue: dialogData, + }, + { + provide: DialogRef, + useValue: dialogRef, + }, + { + provide: BescheidService, + useValue: bescheidService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardAbschliessenDialogContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('component', () => { + describe('onClose', () => { + it('should close dialog', () => { + component.onClose(); + + expect(dialogRef.close).toHaveBeenCalled(); + }); + }); + + describe('onConfirm', () => { + it('should call bescheid service', () => { + const commandStateResource: StateResource<CommandResource> = createSuccessfullyDoneCommandStateResource(); + bescheidService.bescheidErstellungUeberspringen.mockReturnValue(of(commandStateResource)); + + component.onConfirm(); + + expect(bescheidService.bescheidErstellungUeberspringen).toHaveBeenCalledWith(vorgangWithEingangResource); + }); + + it('should set abschliessen$', () => { + const commandStateResource: StateResource<CommandResource> = createSuccessfullyDoneCommandStateResource(); + bescheidService.bescheidErstellungUeberspringen.mockReturnValue(of(commandStateResource)); + + component.onConfirm(); + + expect(component.abschliessen$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + + it('should close dialog', () => { + const commandStateResource: StateResource<CommandResource> = createSuccessfullyDoneCommandStateResource(); + bescheidService.bescheidErstellungUeberspringen.mockReturnValue(of(commandStateResource)); + + component.onConfirm(); + component.abschliessen$.subscribe(); + + expect(dialogRef.close).toHaveBeenCalledWith(createDialogResult(commandStateResource)); + }); + }); + }); + + describe('template', () => { + describe('button close', () => { + describe('output', () => { + it('should call onClose', () => { + component.onClose = jest.fn(); + + triggerEvent({ fixture, name: 'click', elementSelector: closeBescheidButton }); + + expect(component.onClose).toHaveBeenCalled(); + }); + }); + }); + + describe('ozgcloud-stroked-button-with-spinner Überspringen und abschließen', () => { + function getElementComponent(): OzgcloudStrokedButtonWithSpinnerComponent { + return getElementComponentFromFixtureByCss(fixture, confirmButtonDataTestId); + } + + describe('input', () => { + it('should set stateResource', () => { + const commandStateResource: StateResource<CommandResource> = createSuccessfullyDoneCommandStateResource(); + component.abschliessen$ = of(commandStateResource); + + fixture.detectChanges(); + + expect(getElementComponent().stateResource).toBe(commandStateResource); + }); + }); + + describe('output', () => { + it('should call onConfirm', () => { + component.onConfirm = jest.fn(); + + triggerEvent({ + fixture, + name: 'click', + elementSelector: confirmButtonDataTestId, + }); + + expect(component.onConfirm).toHaveBeenCalled(); + }); + }); + }); + + describe('ozgcloud-stroked-button-with-spinner Abbrechen', () => { + it('should call onClose', () => { + component.onClose = jest.fn(); + + triggerEvent({ fixture, name: 'click', elementSelector: cancelButtonDataTestId }); + + expect(component.onClose).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a6fa9de521ad52203dd2cbd2bc86a2365146908 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component.ts @@ -0,0 +1,40 @@ +import { BescheidService } from '@alfa-client/bescheid-shared'; +import { CommandResource, tapOnCommandSuccessfullyDone } from '@alfa-client/command-shared'; +import { StateResource } from '@alfa-client/tech-shared'; +import { createDialogResult } from '@alfa-client/ui'; +import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; +import { Component, Inject } from '@angular/core'; +import { Observable } from 'rxjs'; + +export interface AbschliessenDialogData { + vorgangWithEingangResource: VorgangWithEingangResource; +} + +@Component({ + selector: 'alfa-bescheid-wizard-abschliessen-dialog-container', + templateUrl: './bescheid-wizard-abschliessen-dialog-container.component.html', +}) +export class BescheidWizardAbschliessenDialogContainerComponent { + public abschliessen$: Observable<StateResource<CommandResource>>; + + constructor( + @Inject(DIALOG_DATA) private readonly dialogData: AbschliessenDialogData, + private readonly dialogRef: DialogRef, + private readonly bescheidService: BescheidService, + ) {} + + public onClose(): void { + this.dialogRef.close(); + } + + public onConfirm(): void { + this.abschliessen$ = this.bescheidService + .bescheidErstellungUeberspringen(this.dialogData.vorgangWithEingangResource) + .pipe( + tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => + this.dialogRef.close(createDialogResult(commandStateResource)), + ), + ); + } +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.html new file mode 100644 index 0000000000000000000000000000000000000000..3e3e0d9f2ddf27f8d7dec24face746611c03064f --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.html @@ -0,0 +1,26 @@ +<div class="mt-2 grid h-full grid-cols-[min-content_1fr_1fr] gap-7"> + <alfa-bescheid-wizard-stepper + [activeStep]="bescheidWizardStep.AntragBescheiden" + data-test-id="wizard-stepper" + ></alfa-bescheid-wizard-stepper> + <div> + <alfa-bescheid-wizard-step-title label="Antrag bescheiden" data-test-id="wizard-step-title"></alfa-bescheid-wizard-step-title> + <alfa-bescheid-wizard-antrag-bescheiden-form + [vorgangWithEingangResource]="vorgangWithEingangResource" + [bescheidResource]="bescheidResource" + [submitStateResource]="submitStateResource" + (weiterClickEmitter)="weiterClickEmitter.emit(bescheidWizardStep.DokumenteHochladen)" + data-test-id="antrag-bescheiden-form" + ></alfa-bescheid-wizard-antrag-bescheiden-form> + <alfa-bescheid-wizard-abschliessen-button + [vorgangWithEingangResource]="vorgangWithEingangResource" + (vorgangAbgeschlossen)="vorgangAbgeschlossen.emit()" + data-test-id="wizard-abschliessen-button" + ></alfa-bescheid-wizard-abschliessen-button> + </div> + <alfa-bescheid-wizard-summary data-test-id="wizard-summary"> + <alfa-bescheid-wizard-antrag-bescheiden-summary + data-test-id="antrag-bescheiden-summary" + ></alfa-bescheid-wizard-antrag-bescheiden-summary> + </alfa-bescheid-wizard-summary> +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..76204aa7e07e108f2b6221be33170b8a754b7c5c --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.spec.ts @@ -0,0 +1,184 @@ +import { BescheidService, BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { Mock, existsAsHtmlElement, getElementFromFixtureByType, mock, triggerEvent, useFromMock } from '@alfa-client/test-utils'; +import { EventEmitter } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockComponent } from 'ng-mocks'; +import { createBescheidResource } from '../../../../../../bescheid-shared/src/test/bescheid'; +import { createCommandStateResource } from '../../../../../../command-shared/test/command'; +import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test'; +import { createVorgangWithEingangResource } from '../../../../../../vorgang-shared/test/vorgang'; +import { BescheidWizardAbschliessenButtonComponent } from '../abschliessen-button/bescheid-wizard-abschliessen-button.component'; +import { BescheidWizardStepTitleComponent } from '../step-title/bescheid-wizard-step-title.component'; +import { BescheidWizardStepperComponent } from '../stepper/bescheid-wizard-stepper.component'; +import { BescheidWizardSummaryComponent } from '../summary/bescheid-wizard-summary.component'; +import { BescheidWizardAntragBescheidenComponent } from './bescheid-wizard-antrag-bescheiden.component'; +import { BescheidWizardAntragBescheidenFormComponent } from './form/bescheid-wizard-antrag-bescheiden-form.component'; +import { BescheidWizardAntragBescheidenSummaryComponent } from './summary/bescheid-wizard-antrag-bescheiden-summary.component'; + +describe('BescheidWizardAntragBescheidenComponent', () => { + let component: BescheidWizardAntragBescheidenComponent; + let fixture: ComponentFixture<BescheidWizardAntragBescheidenComponent>; + + const wizardStepper: string = getDataTestIdOf('wizard-stepper'); + const wizardStepTitle: string = getDataTestIdOf('wizard-step-title'); + const antragBescheidenForm: string = getDataTestIdOf('antrag-bescheiden-form'); + const abschliessenButton: string = getDataTestIdOf('wizard-abschliessen-button'); + const wizardSummary: string = getDataTestIdOf('wizard-summary'); + const antragBescheidenSummary: string = getDataTestIdOf('antrag-bescheiden-summary'); + + let bescheidService: Mock<BescheidService>; + + beforeEach(() => { + bescheidService = mock(BescheidService); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + BescheidWizardAntragBescheidenComponent, + MockComponent(BescheidWizardStepTitleComponent), + MockComponent(BescheidWizardAntragBescheidenFormComponent), + MockComponent(BescheidWizardSummaryComponent), + MockComponent(BescheidWizardAntragBescheidenSummaryComponent), + MockComponent(BescheidWizardAbschliessenButtonComponent), + MockComponent(BescheidWizardStepperComponent), + ], + providers: [ + { + provide: BescheidService, + useValue: bescheidService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardAntragBescheidenComponent); + component = fixture.componentInstance; + component.weiterClickEmitter = useFromMock(mock(EventEmitter<BescheidWizardStep>)); + component.vorgangAbgeschlossen = useFromMock(mock(EventEmitter<void>)); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('alfa-bescheid-wizard-stepper', () => { + function getElementComponent(): BescheidWizardStepperComponent { + return getElementFromFixtureByType(fixture, BescheidWizardStepperComponent); + } + + it('should exists', () => { + fixture.detectChanges(); + + existsAsHtmlElement(fixture, wizardStepper); + }); + + describe('input', () => { + it('should set activeStep', () => { + fixture.detectChanges(); + + expect(getElementComponent().activeStep).toBe(BescheidWizardStep.AntragBescheiden); + }); + }); + }); + + describe('alfa-bescheid-wizard-step-title', () => { + it('should show', () => { + existsAsHtmlElement(fixture, wizardStepTitle); + }); + }); + + describe('alfa-bescheid-wizard-summary', () => { + it('should show', () => { + existsAsHtmlElement(fixture, wizardSummary); + }); + }); + + describe('alfa-bescheid-wizard-antrag-bescheiden-summary', () => { + it('should show', () => { + existsAsHtmlElement(fixture, antragBescheidenSummary); + }); + }); + + describe('alfa-bescheid-wizard-abschliessen-button', () => { + function getElementComponent(): BescheidWizardAbschliessenButtonComponent { + return getElementFromFixtureByType(fixture, BescheidWizardAbschliessenButtonComponent); + } + + it('should show', () => { + existsAsHtmlElement(fixture, abschliessenButton); + }); + + describe('input', () => { + it('should set vorgangWithEingangResource', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource(); + + fixture.detectChanges(); + + expect(getElementComponent().vorgangWithEingangResource).toBe(component.vorgangWithEingangResource); + }); + }); + + describe('output', () => { + it('should call onVorgangAbgeschlossen', () => { + triggerEvent({ + fixture, + name: 'vorgangAbgeschlossen', + elementSelector: abschliessenButton, + }); + + expect(component.vorgangAbgeschlossen.emit).toHaveBeenCalled(); + }); + }); + }); + + describe('alfa-bescheid-wizard-antrag-bescheiden-form', () => { + function getElementComponent(): BescheidWizardAntragBescheidenFormComponent { + return getElementFromFixtureByType(fixture, BescheidWizardAntragBescheidenFormComponent); + } + + it('should show', () => { + existsAsHtmlElement(fixture, antragBescheidenForm); + }); + + describe('input', () => { + it('should set vorgangWithEingangResource', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource(); + + fixture.detectChanges(); + + expect(getElementComponent().vorgangWithEingangResource).toBe(component.vorgangWithEingangResource); + }); + + it('should set bescheidResource', () => { + component.bescheidResource = createBescheidResource(); + + fixture.detectChanges(); + + expect(getElementComponent().bescheidResource).toBe(component.bescheidResource); + }); + + it('should set submitStateResource', () => { + component.submitStateResource = createCommandStateResource(); + + fixture.detectChanges(); + + expect(getElementComponent().submitStateResource).toBe(component.submitStateResource); + }); + }); + + describe('output', () => { + it('should emit weiterClickEmitter', () => { + triggerEvent({ + fixture, + name: 'weiterClickEmitter', + elementSelector: antragBescheidenForm, + }); + + expect(component.weiterClickEmitter.emit).toHaveBeenCalledWith(BescheidWizardStep.DokumenteHochladen); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad21a4d0358b3a81919618ad43a2765328c425d1 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component.ts @@ -0,0 +1,20 @@ +import { BescheidResource, BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { StateResource } from '@alfa-client/tech-shared'; +import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Resource } from '@ngxp/rest'; + +@Component({ + selector: 'alfa-bescheid-wizard-antrag-bescheiden', + templateUrl: './bescheid-wizard-antrag-bescheiden.component.html', +}) +export class BescheidWizardAntragBescheidenComponent { + @Input() vorgangWithEingangResource: VorgangWithEingangResource; + @Input() bescheidResource: BescheidResource; + @Input() submitStateResource: StateResource<Resource>; + + @Output() weiterClickEmitter: EventEmitter<BescheidWizardStep> = new EventEmitter<BescheidWizardStep>(); + @Output() vorgangAbgeschlossen: EventEmitter<void> = new EventEmitter<void>(); + + public readonly bescheidWizardStep = BescheidWizardStep; +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.html new file mode 100644 index 0000000000000000000000000000000000000000..1dc1beea0753e8e1ccdd0eff9e7a2c20438c301e --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.html @@ -0,0 +1,38 @@ +<div [formGroup]="formService.form" role="radiogroup" aria-label="Bescheidstatus"> + <div class="my-10 flex max-w-2xl gap-8"> + <ods-radio-button-card + label="bewilligt" + [name]="formServiceClass.FIELD_BEWILLIGT" + value="true" + data-test-id="button-bewilligt" + variant="bescheid_bewilligt" + ><ods-stamp-icon size="large"></ods-stamp-icon> + </ods-radio-button-card> + <ods-radio-button-card + label="abgelehnt" + [name]="formServiceClass.FIELD_BEWILLIGT" + value="false" + data-test-id="button-abgelehnt" + variant="bescheid_abgelehnt" + ><ods-close-icon size="large" class="fill-abgelehnt"></ods-close-icon> + </ods-radio-button-card> + </div> + <div class="flex w-full"> + <ozgcloud-date-editor + [formControlName]="formServiceClass.FIELD_BESCHIEDEN_AM" + label="am" + aria-label="Bescheiddatum" + [required]="true" + > + </ozgcloud-date-editor> + </div> +</div> +<alfa-bescheid-wizard-weiter-button + *ngIf=" + (vorgangWithEingangResource | hasLink: vorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT) || + (bescheidResource | hasLink: bescheidLinkRel.UPDATE) + " + [submitStateResource]="submitStateResource" + (clickEmitter)="weiterClickEmitter.emit()" + data-test-id="weiter-button" +></alfa-bescheid-wizard-weiter-button> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d81a9ee233fd8ce80910fe10ffb2b625dc3f09c4 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.spec.ts @@ -0,0 +1,112 @@ +import { BescheidLinkRel, BescheidService } from '@alfa-client/bescheid-shared'; +import { HasLinkPipe } from '@alfa-client/tech-shared'; +import { existsAsHtmlElement, mock, notExistsAsHtmlElement, triggerEvent, useFromMock } from '@alfa-client/test-utils'; +import { DateEditorComponent } from '@alfa-client/ui'; +import { VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; +import { EventEmitter } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms'; +import { CloseIconComponent, RadioButtonCardComponent, StampIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; +import { createBescheidResource } from '../../../../../../../bescheid-shared/src/test/bescheid'; +import { getDataTestIdOf } from '../../../../../../../tech-shared/test/data-test'; +import { createVorgangWithEingangResource } from '../../../../../../../vorgang-shared/test/vorgang'; +import { BescheidFormService } from '../../../bescheid.formservice'; +import { BescheidWizardWeiterButtonComponent } from '../../weiter-button/bescheid-wizard-weiter-button.component'; +import { BescheidWizardAntragBescheidenFormComponent } from './bescheid-wizard-antrag-bescheiden-form.component'; + +describe('BescheidWizardAntragBescheidenFormComponent', () => { + let component: BescheidWizardAntragBescheidenFormComponent; + let fixture: ComponentFixture<BescheidWizardAntragBescheidenFormComponent>; + + const weiterButton: string = getDataTestIdOf('weiter-button'); + + let formService: BescheidFormService; + + beforeEach(async () => { + formService = new BescheidFormService(new UntypedFormBuilder(), useFromMock(mock(BescheidService))); + + await TestBed.configureTestingModule({ + declarations: [ + BescheidWizardAntragBescheidenFormComponent, + HasLinkPipe, + MockComponent(BescheidWizardWeiterButtonComponent), + MockComponent(RadioButtonCardComponent), + MockComponent(StampIconComponent), + MockComponent(CloseIconComponent), + MockComponent(DateEditorComponent), + ], + providers: [ + { + provide: BescheidFormService, + useValue: formService, + }, + ], + imports: [ReactiveFormsModule], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardAntragBescheidenFormComponent); + component = fixture.componentInstance; + component.weiterClickEmitter = useFromMock(mock(EventEmitter<void>)); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('alfa-bescheid-wizard-weiter-button', () => { + it('should exists if bescheid update link exists', () => { + component.bescheidResource = createBescheidResource([BescheidLinkRel.UPDATE]); + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, weiterButton); + }); + + it('should exists if create link exists', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, + ]); + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, weiterButton); + }); + + it('should not exists if update and create links missing', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource(); + component.bescheidResource = createBescheidResource(); + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, weiterButton); + }); + + it('should not exists if update link missing', () => { + component.bescheidResource = createBescheidResource(); + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, weiterButton); + }); + + describe('output', () => { + it('should emit weiterClickEmitter', () => { + component.bescheidResource = createBescheidResource([BescheidLinkRel.UPDATE]); + + fixture.detectChanges(); + + triggerEvent({ + fixture, + name: 'clickEmitter', + elementSelector: weiterButton, + }); + + expect(component.weiterClickEmitter.emit).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4b99c96e31bbb2702c7c0982de44ed7abc7bdbb --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.ts @@ -0,0 +1,24 @@ +import { BescheidLinkRel, BescheidResource } from '@alfa-client/bescheid-shared'; +import { StateResource } from '@alfa-client/tech-shared'; +import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Resource } from '@ngxp/rest'; +import { BescheidFormService } from '../../../bescheid.formservice'; + +@Component({ + selector: 'alfa-bescheid-wizard-antrag-bescheiden-form', + templateUrl: './bescheid-wizard-antrag-bescheiden-form.component.html', +}) +export class BescheidWizardAntragBescheidenFormComponent { + @Input() vorgangWithEingangResource: VorgangWithEingangResource; + @Input() bescheidResource: BescheidResource; + @Input() submitStateResource: StateResource<Resource>; + + @Output() weiterClickEmitter = new EventEmitter<void>(); + + public readonly vorgangWithEingangLinkRel = VorgangWithEingangLinkRel; + public readonly bescheidLinkRel = BescheidLinkRel; + public readonly formServiceClass = BescheidFormService; + + constructor(public readonly formService: BescheidFormService) {} +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.html new file mode 100644 index 0000000000000000000000000000000000000000..995ee6afa5bd8ee9fdb5f5309264dc6ca853c769 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.html @@ -0,0 +1,7 @@ +<ods-bescheid-status-text + *ngIf="formService.getBescheidFormValueChanges() | async as bescheid" + [bewilligt]="bescheid.bewilligt" + [dateText]="bescheid.beschiedenAm | date: 'dd.MM.yyyy'" + [hasBescheidDraft]="false" + data-test-id="bescheid-status-text" +></ods-bescheid-status-text> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..588b4227c7a9ab76f83f438390a3b4ff717cd4e2 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.spec.ts @@ -0,0 +1,97 @@ +import { Bescheid } from '@alfa-client/bescheid-shared'; +import { existsAsHtmlElement, getElementFromFixtureByType, Mock, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidStatusTextComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; +import { EMPTY, of } from 'rxjs'; +import { createBescheid } from '../../../../../../../bescheid-shared/src/test/bescheid'; +import { getDataTestIdOf } from '../../../../../../../tech-shared/test/data-test'; +import { BescheidFormService } from '../../../bescheid.formservice'; +import { BescheidWizardAntragBescheidenSummaryComponent } from './bescheid-wizard-antrag-bescheiden-summary.component'; + +describe('BescheidWizardAntragBescheidenSummaryComponent', () => { + let component: BescheidWizardAntragBescheidenSummaryComponent; + let fixture: ComponentFixture<BescheidWizardAntragBescheidenSummaryComponent>; + + const bescheidStatusText: string = getDataTestIdOf('bescheid-status-text'); + + let formService: Mock<BescheidFormService>; + + beforeEach(() => { + formService = mock(BescheidFormService); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardAntragBescheidenSummaryComponent, MockComponent(BescheidStatusTextComponent)], + providers: [{ provide: BescheidFormService, useValue: formService }], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardAntragBescheidenSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('ods-bescheid-status-text', () => { + function getElementComponent(): BescheidStatusTextComponent { + return getElementFromFixtureByType(fixture, BescheidStatusTextComponent); + } + + it('should subscribe to bescheid value form changes', () => { + fixture.detectChanges(); + + expect(formService.getBescheidFormValueChanges).toHaveBeenCalled(); + }); + + it('should NOT show', () => { + formService.getBescheidFormValueChanges.mockReturnValue(EMPTY); + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, bescheidStatusText); + }); + + it('should show', () => { + formService.getBescheidFormValueChanges.mockReturnValue(of(createBescheid())); + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, bescheidStatusText); + }); + + describe('input', () => { + it('should set bewilligt', () => { + const bescheid: Bescheid = createBescheid(); + formService.getBescheidFormValueChanges.mockReturnValue(of(bescheid)); + + fixture.detectChanges(); + + expect(getElementComponent().bewilligt).toBe(bescheid.bewilligt); + }); + + it('should set dateText', () => { + const bescheid: Bescheid = { ...createBescheid(), beschiedenAm: '2024-01-01' }; + formService.getBescheidFormValueChanges.mockReturnValue(of(bescheid)); + + fixture.detectChanges(); + + expect(getElementComponent().dateText).toBe('01.01.2024'); + }); + + it('should set hasBescheidDraft', () => { + const bescheid: Bescheid = createBescheid(); + formService.getBescheidFormValueChanges.mockReturnValue(of(bescheid)); + + fixture.detectChanges(); + + expect(getElementComponent().hasBescheidDraft).toBeFalsy(); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dde93bf38267d618753bf41429fc0fceff973ad0 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { BescheidFormService } from '../../../bescheid.formservice'; + +@Component({ + selector: 'alfa-bescheid-wizard-antrag-bescheiden-summary', + templateUrl: './bescheid-wizard-antrag-bescheiden-summary.component.html', +}) +export class BescheidWizardAntragBescheidenSummaryComponent { + constructor(public readonly formService: BescheidFormService) {} +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.html new file mode 100644 index 0000000000000000000000000000000000000000..5f1b875910deb42d189e8077528fb9a6329826a7 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.html @@ -0,0 +1,24 @@ +<ozgcloud-spinner + *ngIf="(vorgangWithEingangResource | hasLink: vorgangWithEingangLinkRel.BESCHEID_DRAFT) && !bescheidStateResource.resource" + [stateResource]="bescheidStateResource" + class="absolute flex size-full items-center justify-center" + data-test-id="bescheid-loading-spinner" +></ozgcloud-spinner> + +<form [formGroup]="formService.form" class="h-full"> + <div class="grid h-full"> + <alfa-bescheid-wizard-antrag-bescheiden + *ngIf="activeStep === bescheidWizardStep.AntragBescheiden" + [vorgangWithEingangResource]="vorgangWithEingangResource" + [bescheidResource]="bescheidStateResource.resource" + [submitStateResource]="submitStateResource" + (weiterClickEmitter)="weiterClickEmitter.emit($event)" + (vorgangAbgeschlossen)="vorgangAbgeschlossen.emit()" + data-test-id="antrag-bescheiden-step" + ></alfa-bescheid-wizard-antrag-bescheiden> + <alfa-bescheid-wizard-dokumente-hochladen + *ngIf="activeStep === bescheidWizardStep.DokumenteHochladen" + data-test-id="dokumente-hochladen-step" + ></alfa-bescheid-wizard-dokumente-hochladen> + </div> +</form> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e66bad67b2d553ba8f221bd69e480b1ae3b79de --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.spec.ts @@ -0,0 +1,252 @@ +import { BescheidResource, BescheidService, BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { HasLinkPipe, StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { + Mock, + existsAsHtmlElement, + getElementFromFixtureByType, + mock, + notExistsAsHtmlElement, + triggerEvent, + useFromMock, +} from '@alfa-client/test-utils'; +import { SpinnerComponent } from '@alfa-client/ui'; +import { VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; +import { EventEmitter } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms'; +import { MockComponent } from 'ng-mocks'; +import { createBescheidStateResource } from '../../../../../bescheid-shared/src/test/bescheid'; +import { createSuccessfullyDoneCommandStateResource } from '../../../../../command-shared/test/command'; +import { getDataTestIdOf } from '../../../../../tech-shared/test/data-test'; +import { createVorgangWithEingangResource } from '../../../../../vorgang-shared/test/vorgang'; +import { BescheidFormService } from '../bescheid.formservice'; +import { BescheidWizardAntragBescheidenComponent } from './antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component'; +import { BescheidWizardComponent } from './bescheid-wizard.component'; +import { BescheidWizardDokumenteHochladenComponent } from './dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component'; +import { BescheidWizardStepperComponent } from './stepper/bescheid-wizard-stepper.component'; + +describe('BescheidWizardComponent', () => { + let component: BescheidWizardComponent; + let fixture: ComponentFixture<BescheidWizardComponent>; + + const bescheidLoadingSpinner: string = getDataTestIdOf('bescheid-loading-spinner'); + const antragBescheidenStep: string = getDataTestIdOf('antrag-bescheiden-step'); + const dokumenteHochladenStep: string = getDataTestIdOf('dokumente-hochladen-step'); + + let bescheidService: Mock<BescheidService>; + let formService: BescheidFormService; + + beforeEach(() => { + bescheidService = mock(BescheidService); + formService = new BescheidFormService(new UntypedFormBuilder(), useFromMock(bescheidService)); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + BescheidWizardComponent, + MockComponent(BescheidWizardStepperComponent), + MockComponent(BescheidWizardAntragBescheidenComponent), + MockComponent(BescheidWizardDokumenteHochladenComponent), + MockComponent(SpinnerComponent), + HasLinkPipe, + ], + providers: [ + { + provide: BescheidFormService, + useValue: formService, + }, + ], + imports: [ReactiveFormsModule], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardComponent); + component = fixture.componentInstance; + component.weiterClickEmitter = useFromMock(mock(EventEmitter<BescheidWizardStep>)); + component.vorgangAbgeschlossen = useFromMock(mock(EventEmitter<void>)); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('component', () => { + describe('set bescheidDraftStateResource', () => { + it('should patch form values', () => { + formService.patchValues = jest.fn(); + + component.bescheidStateResource = createBescheidStateResource(); + + expect(formService.patchValues).toHaveBeenCalledWith(component.bescheidStateResource.resource); + }); + + it('should NOT patch form values', () => { + formService.patchValues = jest.fn(); + + component.bescheidStateResource = createEmptyStateResource(); + + expect(formService.patchValues).not.toHaveBeenCalled(); + }); + + it('should set bescheid state resource', () => { + component.bescheidStateResource = createBescheidStateResource(); + + expect(component.bescheidStateResource).toEqual(component.bescheidStateResource); + }); + }); + }); + + describe('template', () => { + beforeEach(() => { + component.bescheidStateResource = createBescheidStateResource(); + }); + + describe('ozgcloud-spinner', () => { + it('should exists', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource([VorgangWithEingangLinkRel.BESCHEID_DRAFT]); + component.bescheidStateResource = createEmptyStateResource(); + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, bescheidLoadingSpinner); + }); + + it('should NOT exists if bescheid does not exists', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource(); + component.bescheidStateResource = createEmptyStateResource(); + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, bescheidLoadingSpinner); + }); + + it('should NOT exists if bescheid initially loaded', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource([VorgangWithEingangLinkRel.BESCHEID_DRAFT]); + component.bescheidStateResource = createBescheidStateResource(); + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, bescheidLoadingSpinner); + }); + + describe('input', () => { + it('should set stateResource', () => { + component.vorgangWithEingangResource = createVorgangWithEingangResource([VorgangWithEingangLinkRel.BESCHEID_DRAFT]); + component.bescheidStateResource = createEmptyStateResource(); + + fixture.detectChanges(); + + expect(getElementFromFixtureByType(fixture, SpinnerComponent).stateResource).toEqual(component.bescheidStateResource); + }); + }); + }); + + describe('alfa-bescheid-wizard-antrag-bescheiden', () => { + function getElementComponent(): BescheidWizardAntragBescheidenComponent { + return getElementFromFixtureByType(fixture, BescheidWizardAntragBescheidenComponent); + } + + it('should show', () => { + givenActiveStep(1); + givenLoadedBescheidStateResrouce(); + + existsAsHtmlElement(fixture, antragBescheidenStep); + }); + + it.each([2, 3])('should NOT show in step %d', (step: number) => { + givenActiveStep(step); + givenLoadedBescheidStateResrouce(); + + notExistsAsHtmlElement(fixture, antragBescheidenStep); + }); + + describe('input', () => { + it('should set vorgangWithEingangResource', () => { + givenActiveStep(1); + givenLoadedBescheidStateResrouce(); + component.vorgangWithEingangResource = createVorgangWithEingangResource(); + + fixture.detectChanges(); + + expect(getElementComponent().vorgangWithEingangResource).toBe(component.vorgangWithEingangResource); + }); + + it('should set bescheidResource', () => { + givenActiveStep(1); + givenLoadedBescheidStateResrouce(); + + fixture.detectChanges(); + + expect(getElementComponent().bescheidResource).toEqual(component.bescheidStateResource.resource); + }); + + it('should set submitStateResource', () => { + givenActiveStep(1); + givenLoadedBescheidStateResrouce(); + component.submitStateResource = createSuccessfullyDoneCommandStateResource(); + + fixture.detectChanges(); + + expect(getElementComponent().submitStateResource).toEqual(component.submitStateResource); + }); + }); + + describe('output', () => { + beforeEach(() => { + givenActiveStep(1); + givenLoadedBescheidStateResrouce(); + }); + + it('should emit weiterClickEmitter', () => { + triggerEvent({ + fixture, + name: 'weiterClickEmitter', + elementSelector: antragBescheidenStep, + data: BescheidWizardStep.DokumenteHochladen, + }); + + expect(component.weiterClickEmitter.emit).toHaveBeenCalledWith(BescheidWizardStep.DokumenteHochladen); + }); + + it('should emit vorgangAbgeschlossen', () => { + triggerEvent({ + fixture, + name: 'vorgangAbgeschlossen', + elementSelector: antragBescheidenStep, + }); + + expect(component.vorgangAbgeschlossen.emit).toHaveBeenCalled(); + }); + }); + }); + + describe('alfa-bescheid-wizard-dokumente-hochladen', () => { + it('should show', () => { + givenActiveStep(2); + givenLoadedBescheidStateResrouce(); + + existsAsHtmlElement(fixture, dokumenteHochladenStep); + }); + + it.each([1, 3])('should NOT show in step %d', (step: number) => { + givenActiveStep(step); + givenLoadedBescheidStateResrouce(); + + notExistsAsHtmlElement(fixture, dokumenteHochladenStep); + }); + }); + }); + + function givenActiveStep(step: number): void { + component.activeStep = step; + fixture.detectChanges(); + } + + function givenLoadedBescheidStateResrouce(): StateResource<BescheidResource> { + const resource: StateResource<BescheidResource> = createBescheidStateResource(); + component.bescheidStateResource = resource; + fixture.detectChanges(); + return resource; + } +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b8a55df5b58c279ccb5c8c7523b71e27f14777d --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.ts @@ -0,0 +1,38 @@ +import { BescheidResource, BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { StateResource, isLoaded } from '@alfa-client/tech-shared'; +import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Resource } from '@ngxp/rest'; +import { BescheidFormService } from '../bescheid.formservice'; + +@Component({ + selector: 'alfa-bescheid-wizard', + templateUrl: './bescheid-wizard.component.html', +}) +export class BescheidWizardComponent { + @Input() vorgangWithEingangResource: VorgangWithEingangResource; + + @Input() set bescheidStateResource(value: StateResource<BescheidResource>) { + if (isLoaded(value)) { + this.formService.patchValues(value.resource); + } + this._bescheidStateResource = value; + } + + get bescheidStateResource(): StateResource<BescheidResource> { + return this._bescheidStateResource; + } + + @Input() activeStep: number; + @Input() submitStateResource: StateResource<Resource>; + + @Output() weiterClickEmitter: EventEmitter<BescheidWizardStep> = new EventEmitter<BescheidWizardStep>(); + @Output() vorgangAbgeschlossen: EventEmitter<void> = new EventEmitter<void>(); + + private _bescheidStateResource: StateResource<BescheidResource>; + + public readonly vorgangWithEingangLinkRel = VorgangWithEingangLinkRel; + public readonly bescheidWizardStep = BescheidWizardStep; + + constructor(public readonly formService: BescheidFormService) {} +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..7cf2668837a7aa60195d8084d449b58bc2dca71e --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.html @@ -0,0 +1,36 @@ +<div + class="relative m-6 max-w-2xl rounded-lg bg-modalBg p-6 shadow-xl" + data-test-id="bescheid-close-dialog" +> + <div class="flex flex-col gap-6"> + <div> + <h4 class="text-lg font-medium text-primary">Bescheiderstellung abbrechen</h4> + </div> + <div class="grow"> + <p class="text-base"> + Soll der Bescheid-Entwurf zur späteren Bearbeitung gespeichert oder verworfen werden? + </p> + </div> + <div class="flex gap-4"> + <ozgcloud-stroked-button-with-spinner + [stateResource]="saveStateResource$ | async" + (clickEmitter)="save()" + data-test-id="bescheiderstellung-abbrechen-entwurf-speichern" + text="Entwurf speichern" + type="submit" + icon="check" + > + </ozgcloud-stroked-button-with-spinner> + <ozgcloud-stroked-button-with-spinner + [stateResource]="deleteStateResource$ | async" + (clickEmitter)="cancel()" + data-test-id="bescheiderstellung-abbrechen-entwurf-verwerfen" + text="Verwerfen" + color="" + icon="clear" + type="submit" + > + </ozgcloud-stroked-button-with-spinner> + </div> + </div> +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..df7293c3aaf58a881b74c8db3cd173eea81b2cb3 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.spec.ts @@ -0,0 +1,258 @@ +import { BescheidService } from '@alfa-client/bescheid-shared'; +import { CommandResource } from '@alfa-client/command-shared'; +import { StateResource, createErrorStateResource } from '@alfa-client/tech-shared'; +import { + DialogRefMock, + Mock, + createDialogRefMock, + existsAsHtmlElement, + getElementComponentFromFixtureByCss, + mock, + triggerEvent, +} from '@alfa-client/test-utils'; +import { OzgcloudStrokedButtonWithSpinnerComponent } from '@alfa-client/ui'; +import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockComponent } from 'ng-mocks'; +import { EMPTY, of } from 'rxjs'; +import { createBescheidResource } from '../../../../../../bescheid-shared/src/test/bescheid'; +import { + createCommandStateResource, + createSuccessfullyDoneCommandStateResource, +} from '../../../../../../command-shared/test/command'; +import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test'; +import { createApiError } from '../../../../../../tech-shared/test/error'; +import { singleColdCompleted } from '../../../../../../tech-shared/test/marbles'; +import { BescheidFormService } from '../../bescheid.formservice'; +import { + BescheidWizardCancelDialogContainerComponent, + CancelWizardDialogData, +} from './bescheid-wizard-cancel-dialog-container.component'; + +describe('BescheidWizardCancelDialogContainerComponent', () => { + let component: BescheidWizardCancelDialogContainerComponent; + let fixture: ComponentFixture<BescheidWizardCancelDialogContainerComponent>; + + const speichernButton: string = getDataTestIdOf('bescheiderstellung-abbrechen-entwurf-speichern'); + const verwerfenButton: string = getDataTestIdOf('bescheiderstellung-abbrechen-entwurf-verwerfen'); + + const dialogData: CancelWizardDialogData = { + bescheidResource: createBescheidResource(), + }; + + let formService: Mock<BescheidFormService>; + let bescheidService: Mock<BescheidService>; + let dialogRef: DialogRefMock; + + beforeEach(() => { + formService = { ...mock(BescheidFormService), submit: jest.fn().mockReturnValue(EMPTY) }; + bescheidService = { ...mock(BescheidService), bescheidVerwerfen: jest.fn().mockReturnValue(EMPTY) }; + dialogRef = createDialogRefMock(); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardCancelDialogContainerComponent, MockComponent(OzgcloudStrokedButtonWithSpinnerComponent)], + providers: [ + { + provide: BescheidService, + useValue: bescheidService, + }, + { + provide: BescheidFormService, + useValue: formService, + }, + { + provide: DIALOG_DATA, + useValue: dialogData, + }, + { + provide: DialogRef, + useValue: dialogRef, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardCancelDialogContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('component', () => { + describe('save', () => { + beforeEach(() => { + component.closeDialogWithWizard = jest.fn(); + }); + + it('should submit form', () => { + component.save(); + + expect(formService.submit).toHaveBeenCalled(); + }); + + it('should emit save command state resource', () => { + const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); + formService.submit = jest.fn().mockReturnValue(of(commandStateResource)); + + component.save(); + + expect(component.saveStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + + it('should handle successfully saved bescheid', () => { + const commandStateResource: StateResource<CommandResource> = createSuccessfullyDoneCommandStateResource(); + formService.submit = jest.fn().mockReturnValue(of(commandStateResource)); + + component.save(); + component.saveStateResource$.subscribe(); + + expect(component.closeDialogWithWizard).toHaveBeenCalled(); + }); + + it('should NOT handle successfully saved bescheid', () => { + const commandStateResource: StateResource<CommandResource> = createErrorStateResource(createApiError()); + formService.submit = jest.fn().mockReturnValue(of(commandStateResource)); + + component.save(); + component.saveStateResource$.subscribe(); + + expect(component.closeDialogWithWizard).not.toHaveBeenCalled(); + }); + }); + + describe('cancel', () => { + beforeEach(() => { + component.closeDialogWithWizard = jest.fn(); + }); + + it('should delete bescheid', () => { + component.cancel(); + + expect(bescheidService.bescheidVerwerfen).toHaveBeenCalled(); + }); + + it('should NOT delete bescheid', () => { + component.bescheidResource = null; + + component.cancel(); + + expect(bescheidService.bescheidVerwerfen).not.toHaveBeenCalled(); + }); + + it('should emit delete command state resource', () => { + const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); + bescheidService.bescheidVerwerfen = jest.fn().mockReturnValue(of(commandStateResource)); + + component.cancel(); + + expect(component.deleteStateResource$).toBeObservable(singleColdCompleted(commandStateResource)); + }); + + it('should close dialog on success', () => { + const commandStateResource: StateResource<CommandResource> = createSuccessfullyDoneCommandStateResource(); + bescheidService.bescheidVerwerfen = jest.fn().mockReturnValue(of(commandStateResource)); + + component.cancel(); + component.deleteStateResource$.subscribe(); + + expect(component.closeDialogWithWizard).toHaveBeenCalled(); + }); + + it('should NOT close dialog on error', () => { + const commandStateResource: StateResource<CommandResource> = createErrorStateResource(createApiError()); + bescheidService.bescheidVerwerfen = jest.fn().mockReturnValue(of(commandStateResource)); + + component.cancel(); + component.deleteStateResource$.subscribe(); + + expect(component.closeDialogWithWizard).not.toHaveBeenCalled(); + }); + + it('should close dialog with cancel result on non existing bescheid', () => { + component.bescheidResource = null; + + component.cancel(); + + expect(component.closeDialogWithWizard).toHaveBeenCalled(); + }); + }); + + describe('closeDialogWithWizard', () => { + it('should close', () => { + component.closeDialogWithWizard(); + + expect(dialogRef.close).toHaveBeenCalledWith({ closeWizard: true }); + }); + }); + }); + + describe('template', () => { + describe('ozgcloud-stroked-button-with-spinner Entwurf speichern', () => { + it('should show', () => { + existsAsHtmlElement(fixture, speichernButton); + }); + + describe('input', () => { + it('should set stateResource', () => { + const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); + component.saveStateResource$ = of(commandStateResource); + + const elementComponent: OzgcloudStrokedButtonWithSpinnerComponent = getElementComponentFromFixtureByCss( + fixture, + speichernButton, + ); + + fixture.detectChanges(); + + expect(elementComponent.stateResource).toBe(commandStateResource); + }); + }); + + describe('output', () => { + it('should call save', () => { + component.save = jest.fn(); + + triggerEvent({ fixture, name: 'clickEmitter', elementSelector: speichernButton }); + + expect(component.save).toHaveBeenCalled(); + }); + }); + }); + + describe('ozgcloud-stroked-button-with-spinner Verwerfen', () => { + it('should show', () => { + existsAsHtmlElement(fixture, verwerfenButton); + }); + + describe('input', () => { + it('should set stateResource', () => { + const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); + component.deleteStateResource$ = of(commandStateResource); + + const elementComponent: OzgcloudStrokedButtonWithSpinnerComponent = getElementComponentFromFixtureByCss( + fixture, + verwerfenButton, + ); + + fixture.detectChanges(); + + expect(elementComponent.stateResource).toBe(commandStateResource); + }); + }); + + describe('output', () => { + it('should call delete', () => { + component.cancel = jest.fn(); + + triggerEvent({ fixture, name: 'clickEmitter', elementSelector: verwerfenButton }); + + expect(component.cancel).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..beb6c19e288f404d7f491398f10107a61e0cdf97 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.ts @@ -0,0 +1,53 @@ +import { BescheidResource, BescheidService } from '@alfa-client/bescheid-shared'; +import { tapOnCommandSuccessfullyDone } from '@alfa-client/command-shared'; +import { StateResource, isNotNil } from '@alfa-client/tech-shared'; +import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; +import { Component, Inject } from '@angular/core'; +import { Resource } from '@ngxp/rest'; +import { Observable } from 'rxjs'; +import { BescheidFormService } from '../../bescheid.formservice'; + +export interface CancelWizardDialogData { + bescheidResource: BescheidResource; +} + +export interface CancelWizardDialogResult { + closeWizard: boolean; +} + +@Component({ + templateUrl: './bescheid-wizard-cancel-dialog-container.component.html', +}) +export class BescheidWizardCancelDialogContainerComponent { + public saveStateResource$: Observable<StateResource<Resource>>; + public deleteStateResource$: Observable<StateResource<Resource>>; + + bescheidResource: BescheidResource; + + constructor( + @Inject(DIALOG_DATA) readonly dialogData: CancelWizardDialogData, + public dialogRef: DialogRef<CancelWizardDialogResult>, + private readonly bescheidService: BescheidService, + private readonly formService: BescheidFormService, + ) { + this.bescheidResource = dialogData.bescheidResource; + } + + public save(): void { + this.saveStateResource$ = this.formService.submit().pipe(tapOnCommandSuccessfullyDone(() => this.closeDialogWithWizard())); + } + + public cancel(): void { + if (isNotNil(this.bescheidResource)) { + this.deleteStateResource$ = this.bescheidService + .bescheidVerwerfen() + .pipe(tapOnCommandSuccessfullyDone(() => this.closeDialogWithWizard())); + } else { + this.closeDialogWithWizard(); + } + } + + closeDialogWithWizard(): void { + this.dialogRef.close({ closeWizard: true }); + } +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e922efca768811f976d2a2fcc77e219e60ba54b1 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.html @@ -0,0 +1,12 @@ +<div class="mt-2 grid h-full grid-cols-[min-content_1fr_1fr] gap-7"> + <alfa-bescheid-wizard-stepper [activeStep]="bescheidWizardStep.DokumenteHochladen"></alfa-bescheid-wizard-stepper> + <div> + <alfa-bescheid-wizard-step-title label="Antrag bescheiden" [inactiveStep]="true"></alfa-bescheid-wizard-step-title> + <alfa-bescheid-wizard-step-title label="Dokumente hochladen"></alfa-bescheid-wizard-step-title> + <alfa-bescheid-wizard-dokumente-hochladen-form></alfa-bescheid-wizard-dokumente-hochladen-form> + </div> + <alfa-bescheid-wizard-summary> + <alfa-bescheid-wizard-antrag-bescheiden-summary></alfa-bescheid-wizard-antrag-bescheiden-summary> + <alfa-bescheid-wizard-dokumente-hochladen-summary></alfa-bescheid-wizard-dokumente-hochladen-summary> + </alfa-bescheid-wizard-summary> +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ccc4edf5232f58f2f191cecc7d15076e08c13fe --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.spec.ts @@ -0,0 +1,68 @@ +import { BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { getElementFromFixtureByType } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockComponent } from 'ng-mocks'; +import { BescheidWizardAntragBescheidenSummaryComponent } from '../antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component'; +import { BescheidWizardStepTitleComponent } from '../step-title/bescheid-wizard-step-title.component'; +import { BescheidWizardStepperComponent } from '../stepper/bescheid-wizard-stepper.component'; +import { BescheidWizardSummaryComponent } from '../summary/bescheid-wizard-summary.component'; +import { BescheidWizardDokumenteHochladenComponent } from './bescheid-wizard-dokumente-hochladen.component'; +import { BescheidWizardDokumenteHochladenFormComponent } from './form/bescheid-wizard-dokumente-hochladen-form.component'; +import { BescheidWizardDokumenteHochladenSummaryComponent } from './summary/bescheid-wizard-dokumente-hochladen-summary.component'; + +describe('BescheidWizardDokumenteHochladenComponent', () => { + let component: BescheidWizardDokumenteHochladenComponent; + let fixture: ComponentFixture<BescheidWizardDokumenteHochladenComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + BescheidWizardDokumenteHochladenComponent, + MockComponent(BescheidWizardSummaryComponent), + MockComponent(BescheidWizardAntragBescheidenSummaryComponent), + MockComponent(BescheidWizardDokumenteHochladenSummaryComponent), + MockComponent(BescheidWizardStepTitleComponent), + MockComponent(BescheidWizardDokumenteHochladenFormComponent), + MockComponent(BescheidWizardStepperComponent), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardDokumenteHochladenComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('alfa-bescheid-wizard-stepper', () => { + function getElementComponent(): BescheidWizardStepperComponent { + return getElementFromFixtureByType(fixture, BescheidWizardStepperComponent); + } + + describe('input', () => { + it('should set activeStep', () => { + fixture.detectChanges(); + + expect(getElementComponent().activeStep).toBe(BescheidWizardStep.DokumenteHochladen); + }); + }); + }); + + describe('alfa-bescheid-wizard-step-title', () => { + function getElementComponent(): BescheidWizardStepTitleComponent { + return getElementFromFixtureByType(fixture, BescheidWizardStepTitleComponent); + } + + describe('input', () => { + it('should set inactiveStep', () => { + fixture.detectChanges(); + + expect(getElementComponent().inactiveStep).toBeTruthy(); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3692f49cd9aa9268a6766928f34abb01b40d007b --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component.ts @@ -0,0 +1,10 @@ +import { BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'alfa-bescheid-wizard-dokumente-hochladen', + templateUrl: './bescheid-wizard-dokumente-hochladen.component.html', +}) +export class BescheidWizardDokumenteHochladenComponent { + public readonly bescheidWizardStep = BescheidWizardStep; +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4bd1961e89455800dbdddd34d3de0e7904f9479a --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.html @@ -0,0 +1 @@ +<p>bescheid-wizard-dokumente-hochladen-form works!</p> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..417a480c13b49e280112f8a4a85e69663902b722 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidWizardDokumenteHochladenFormComponent } from './bescheid-wizard-dokumente-hochladen-form.component'; + +describe('BescheidWizardDokumenteHochladenFormComponent', () => { + let component: BescheidWizardDokumenteHochladenFormComponent; + let fixture: ComponentFixture<BescheidWizardDokumenteHochladenFormComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardDokumenteHochladenFormComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardDokumenteHochladenFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..63df23e526cb61c40dbf89b8c459da46d6f6d836 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'alfa-bescheid-wizard-dokumente-hochladen-form', + templateUrl: './bescheid-wizard-dokumente-hochladen-form.component.html', +}) +export class BescheidWizardDokumenteHochladenFormComponent {} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.html new file mode 100644 index 0000000000000000000000000000000000000000..a1b71d424a4b35b04bf8b0aee379001bc748bd21 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.html @@ -0,0 +1 @@ +<p>bescheid-wizard-dokumente-hochladen-summary works!</p> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..06b8b521caf49c7ff4f93ffa573dbd1301eb7c4a --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidWizardDokumenteHochladenSummaryComponent } from './bescheid-wizard-dokumente-hochladen-summary.component'; + +describe('BescheidWizardDokumenteHochladenSummaryComponent', () => { + let component: BescheidWizardDokumenteHochladenSummaryComponent; + let fixture: ComponentFixture<BescheidWizardDokumenteHochladenSummaryComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardDokumenteHochladenSummaryComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardDokumenteHochladenSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2d4ba96fc074645474734a6d9a68e2f030e6eeb --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'alfa-bescheid-wizard-dokumente-hochladen-summary', + templateUrl: './bescheid-wizard-dokumente-hochladen-summary.component.html', +}) +export class BescheidWizardDokumenteHochladenSummaryComponent {} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.html new file mode 100644 index 0000000000000000000000000000000000000000..637fb2235c8c3971f1f0aa961dc5ad64e0aa58c0 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.html @@ -0,0 +1,3 @@ +<div class="text-base font-bold text-primary-600" data-test-class="step-caption" [class.min-h-28]="inactiveStep"> + {{ label }} +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..854259fea8c01efb45c4a5d4e7a1e23f4520bbc0 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidWizardStepTitleComponent } from './bescheid-wizard-step-title.component'; + +describe('BescheidWizardStepTitleComponent', () => { + let component: BescheidWizardStepTitleComponent; + let fixture: ComponentFixture<BescheidWizardStepTitleComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardStepTitleComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardStepTitleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2df6947c1cd9ce898d9f9400f967c403291f149 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component.ts @@ -0,0 +1,10 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'alfa-bescheid-wizard-step-title', + templateUrl: './bescheid-wizard-step-title.component.html', +}) +export class BescheidWizardStepTitleComponent { + @Input() label: string; + @Input() inactiveStep: boolean = false; +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.html new file mode 100644 index 0000000000000000000000000000000000000000..cfdb969637c673c10e0697ceff8500be625d6c6f --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.html @@ -0,0 +1,9 @@ +<div role="tablist"> + <alfa-bescheid-wizard-step + *ngFor="let step of steps" + [activeStep]="activeStep" + [step]="step" + (stepChange)="stepChange.emit($event)" + [attr.data-test-id]="'wizard-step-' + step" + ></alfa-bescheid-wizard-step> +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a964c6ed04e417cd6ab487ce18baa9447391b96e --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.spec.ts @@ -0,0 +1,151 @@ +import { BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { getElementComponentFromFixtureByCss, triggerEvent } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockComponent } from 'ng-mocks'; +import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test'; +import { BescheidWizardStepperComponent } from './bescheid-wizard-stepper.component'; +import { BescheidWizardStepComponent } from './step/bescheid-wizard-step.component'; + +describe('BescheidWizardStepperComponent', () => { + let component: BescheidWizardStepperComponent; + let fixture: ComponentFixture<BescheidWizardStepperComponent>; + + const antragBescheidenWizardStep: string = getDataTestIdOf('wizard-step-' + BescheidWizardStep.AntragBescheiden); + const dokumenteHochladenWizardStep: string = getDataTestIdOf('wizard-step-' + BescheidWizardStep.DokumenteHochladen); + const bescheidVersendenWizardStep: string = getDataTestIdOf('wizard-step-' + BescheidWizardStep.BescheidVersenden); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardStepperComponent, MockComponent(BescheidWizardStepComponent)], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardStepperComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have number of steps', () => { + expect(component.steps).toEqual([ + BescheidWizardStep.AntragBescheiden, + BescheidWizardStep.DokumenteHochladen, + BescheidWizardStep.BescheidVersenden, + ]); + }); + + describe('template', () => { + describe('alfa-bescheid-wizard-step', () => { + describe('input', () => { + it('should set activeStep for antrag bescheiden step', () => { + component.activeStep = 2; + + fixture.detectChanges(); + + const step: BescheidWizardStepComponent = getElementComponentFromFixtureByCss<BescheidWizardStepComponent>( + fixture, + antragBescheidenWizardStep, + ); + expect(step.activeStep).toEqual(component.activeStep); + }); + + it('should set activeStep for dokumente hochladen step', () => { + component.activeStep = 2; + + fixture.detectChanges(); + + const step: BescheidWizardStepComponent = getElementComponentFromFixtureByCss<BescheidWizardStepComponent>( + fixture, + dokumenteHochladenWizardStep, + ); + expect(step.activeStep).toEqual(component.activeStep); + }); + + it('should set activeStep for bescheid versenden step', () => { + component.activeStep = 2; + + fixture.detectChanges(); + + const step: BescheidWizardStepComponent = getElementComponentFromFixtureByCss<BescheidWizardStepComponent>( + fixture, + bescheidVersendenWizardStep, + ); + expect(step.activeStep).toEqual(component.activeStep); + }); + + it('should set step for antrag bescheiden', () => { + fixture.detectChanges(); + + const step: BescheidWizardStepComponent = getElementComponentFromFixtureByCss<BescheidWizardStepComponent>( + fixture, + antragBescheidenWizardStep, + ); + expect(step.step).toEqual(BescheidWizardStep.AntragBescheiden); + }); + + it('should set step for dokumente hochladen', () => { + fixture.detectChanges(); + + const step: BescheidWizardStepComponent = getElementComponentFromFixtureByCss<BescheidWizardStepComponent>( + fixture, + dokumenteHochladenWizardStep, + ); + expect(step.step).toEqual(BescheidWizardStep.DokumenteHochladen); + }); + + it('should set step for bescheid versenden', () => { + fixture.detectChanges(); + + const step: BescheidWizardStepComponent = getElementComponentFromFixtureByCss<BescheidWizardStepComponent>( + fixture, + bescheidVersendenWizardStep, + ); + expect(step.step).toEqual(BescheidWizardStep.BescheidVersenden); + }); + }); + + describe('output', () => { + it('should emit stepChange for antrag bescheiden step', () => { + component.stepChange.emit = jest.fn(); + + triggerEvent({ + fixture, + name: 'stepChange', + elementSelector: antragBescheidenWizardStep, + data: BescheidWizardStep.DokumenteHochladen, + }); + + expect(component.stepChange.emit).toHaveBeenCalledWith(BescheidWizardStep.DokumenteHochladen); + }); + + it('should emit stepChange for dokumente hochladen step', () => { + component.stepChange.emit = jest.fn(); + + triggerEvent({ + fixture, + name: 'stepChange', + elementSelector: dokumenteHochladenWizardStep, + data: BescheidWizardStep.DokumenteHochladen, + }); + + expect(component.stepChange.emit).toHaveBeenCalledWith(BescheidWizardStep.DokumenteHochladen); + }); + + it('should emit stepChange for bescheid versenden step', () => { + component.stepChange.emit = jest.fn(); + + triggerEvent({ + fixture, + name: 'stepChange', + elementSelector: bescheidVersendenWizardStep, + data: BescheidWizardStep.DokumenteHochladen, + }); + + expect(component.stepChange.emit).toHaveBeenCalledWith(BescheidWizardStep.DokumenteHochladen); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd2343419cc4cbf386721324c58576a5bc4ef214 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component.ts @@ -0,0 +1,18 @@ +import { BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'alfa-bescheid-wizard-stepper', + templateUrl: './bescheid-wizard-stepper.component.html', +}) +export class BescheidWizardStepperComponent { + @Input() activeStep: BescheidWizardStep; + + @Output() stepChange: EventEmitter<BescheidWizardStep> = new EventEmitter<BescheidWizardStep>(); + + public readonly steps: BescheidWizardStep[] = [ + BescheidWizardStep.AntragBescheiden, + BescheidWizardStep.DokumenteHochladen, + BescheidWizardStep.BescheidVersenden, + ]; +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.html new file mode 100644 index 0000000000000000000000000000000000000000..731c9cbbfa08b5112a536938d06c1d5471d53892 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.html @@ -0,0 +1,57 @@ +<div class="relative z-10 flex min-h-28 flex-col items-center"> + <div + class="-z-1 absolute w-1" + [ngClass]=" + step === bescheidWizardStep.AntragBescheiden ? + isPrevious() ? 'bottom-0 top-2 bg-primary-600' + : 'bottom-0 top-2 bg-gray-500' + : step === bescheidWizardStep.DokumenteHochladen ? + isPrevious() ? 'bottom-0 top-0 bg-primary-600' + : 'bottom-0 top-0 bg-gray-500' + : step === bescheidWizardStep.BescheidVersenden ? + isActive() ? 'top-0 h-2 bg-primary-600' + : 'top-0 h-2 bg-gray-500' + : '' + " + aria-hidden="true" + ></div> + + <button + class="z-10 flex" + (click)="clickHandler(step)" + [ngClass]="isPrevious() ? 'cursor-pointer' : 'cursor-default'" + [attr.data-test-id]=" + step === bescheidWizardStep.AntragBescheiden ? 'step-1-button' + : step === bescheidWizardStep.DokumenteHochladen ? 'step-2-button' + : step === bescheidWizardStep.BescheidVersenden ? 'step-3-button' + : '' + " + role="tab" + [attr.aria-selected]="isActive()" + [attr.aria-disabled]="!isActive()" + attr.aria-controls="vorgang-detail-bescheiden-step-content-{{ step }}" + [tabindex]=" + isActive() ? '0' + : isPrevious() ? '0' + : '-1' + " + [attr.aria-label]=" + step === bescheidWizardStep.AntragBescheiden ? 'Step 1. Antrag bescheiden' + : step === bescheidWizardStep.DokumenteHochladen ? 'Step 2. Dokumente hinzufügen' + : step === bescheidWizardStep.BescheidVersenden ? 'Step 3. Bescheid versenden' + : '' + " + > + <span + class="flex size-10 items-center justify-center rounded-full" + [ngClass]="isActive() ? 'border-4 border-primary-600 bg-background-50' : 'border-transparent bg-transparent'" + > + <span + class="flex size-7 items-center justify-center rounded-full text-sm text-whitetext" + [ngClass]="isPrevious() || isActive() ? 'bg-primary-600' : 'bg-gray-500'" + > + {{ step }} + </span> + </span> + </button> +</div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..8d880a7efefe4f8e37c492bf5905d7008ee8ce16 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.spec.ts @@ -0,0 +1,126 @@ +import { BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidWizardStepComponent } from './bescheid-wizard-step.component'; + +describe('BescheidWizardStepComponent', () => { + let component: BescheidWizardStepComponent; + let fixture: ComponentFixture<BescheidWizardStepComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardStepComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('isActive', () => { + beforeEach(() => { + component.activeStep = BescheidWizardStep.DokumenteHochladen; + }); + + it('return true if step equals activeStep', () => { + component.step = BescheidWizardStep.DokumenteHochladen; + + const isActive: boolean = component.isActive(); + + expect(isActive).toBeTruthy(); + }); + + it('return false if step not equals activeStep', () => { + component.step = BescheidWizardStep.AntragBescheiden; + + const isActive: boolean = component.isActive(); + + expect(isActive).toBeFalsy(); + }); + }); + + describe('isPrevious', () => { + beforeEach(() => { + component.activeStep = BescheidWizardStep.DokumenteHochladen; + }); + + it('return true if step is less than activeStep', () => { + component.step = BescheidWizardStep.AntragBescheiden; + + const isPrevious: boolean = component.isPrevious(); + + expect(isPrevious).toBeTruthy(); + }); + + it('return false if step equals activeStep', () => { + component.step = BescheidWizardStep.DokumenteHochladen; + + const isPrevious: boolean = component.isPrevious(); + + expect(isPrevious).toBeFalsy(); + }); + + it('return false if step is greater than activeStep', () => { + component.step = BescheidWizardStep.BescheidVersenden; + + const isPrevious: boolean = component.isPrevious(); + + expect(isPrevious).toBeFalsy(); + }); + }); + + describe('isNext', () => { + beforeEach(() => { + component.activeStep = BescheidWizardStep.DokumenteHochladen; + }); + + it('return false if step is less than activeStep', () => { + component.step = BescheidWizardStep.AntragBescheiden; + + const isNext: boolean = component.isNext(); + + expect(isNext).toBeFalsy(); + }); + + it('return false if step equals activeStep', () => { + component.step = BescheidWizardStep.DokumenteHochladen; + + const isNext: boolean = component.isNext(); + + expect(isNext).toBeFalsy(); + }); + + it('return true if step is greater than activeStep', () => { + component.step = BescheidWizardStep.BescheidVersenden; + + const isNext: boolean = component.isNext(); + + expect(isNext).toBeTruthy(); + }); + }); + + describe('clickHandler', () => { + beforeEach(() => { + component.stepChange.emit = jest.fn(); + }); + + it('should emit step', () => { + component.isPrevious = jest.fn().mockReturnValue(true); + + component.clickHandler(BescheidWizardStep.AntragBescheiden); + + expect(component.stepChange.emit).toHaveBeenCalledWith(BescheidWizardStep.AntragBescheiden); + }); + + it('should not emit step', () => { + component.isPrevious = jest.fn().mockReturnValue(false); + + component.clickHandler(BescheidWizardStep.AntragBescheiden); + + expect(component.stepChange.emit).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ddb427d422f3e5f92a71a56445543c6281a4a426 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component.ts @@ -0,0 +1,33 @@ +import { BescheidWizardStep } from '@alfa-client/bescheid-shared'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'alfa-bescheid-wizard-step', + templateUrl: './bescheid-wizard-step.component.html', +}) +export class BescheidWizardStepComponent { + @Input() step: BescheidWizardStep; + @Input() activeStep: BescheidWizardStep; + + @Output() stepChange: EventEmitter<BescheidWizardStep> = new EventEmitter<BescheidWizardStep>(); + + public readonly bescheidWizardStep = BescheidWizardStep; + + public clickHandler(step: BescheidWizardStep): void { + if (this.isPrevious()) { + this.stepChange.emit(step); + } + } + + public isActive(): boolean { + return this.step === this.activeStep; + } + + public isPrevious(): boolean { + return this.step < this.activeStep; + } + + public isNext(): boolean { + return this.step > this.activeStep; + } +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9404884a6510a2e16cb20b1fd4d81c5701f89ce7 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.html @@ -0,0 +1,4 @@ +<section class="flex h-full w-full flex-col overflow-auto rounded-xl bg-background-100 px-4 py-5"> + <h3 class="mb-4 text-base font-bold text-primary-600">Bescheid</h3> + <ng-content></ng-content> +</section> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f38f047d8dc3e1233ea44b3c8c0c73418b31f03 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidWizardSummaryComponent } from './bescheid-wizard-summary.component'; + +describe('BescheidWizardSummaryTitleComponent', () => { + let component: BescheidWizardSummaryComponent; + let fixture: ComponentFixture<BescheidWizardSummaryComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardSummaryComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..8cf4be6c484c0989f294d8384748707444b812c3 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'alfa-bescheid-wizard-summary', + templateUrl: './bescheid-wizard-summary.component.html', +}) +export class BescheidWizardSummaryComponent {} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..0baf1ff340c2b506157e0c038d85bcb44c894279 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.html @@ -0,0 +1,10 @@ +<ods-button-with-spinner + [stateResource]="submitStateResource" + (clickEmitter)="clickEmitter.emit()" + variant="primary" + size="medium" + class="mt-8 flex" + data-test-id="bescheid-weiter-button" + text="Weiter" +> +</ods-button-with-spinner> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe96a125921eb3a4625480d865e1614395b27940 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.spec.ts @@ -0,0 +1,65 @@ +import { CommandResource } from '@alfa-client/command-shared'; +import { createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { getElementFromFixtureByType, Mock, mock, triggerEvent, useFromMock } from '@alfa-client/test-utils'; +import { EventEmitter } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { MockComponent } from 'ng-mocks'; +import { createCommandResource } from '../../../../../../command-shared/test/command'; +import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test'; +import { BescheidWizardWeiterButtonComponent } from './bescheid-wizard-weiter-button.component'; + +describe('BescheidWizardWeiterButtonComponent', () => { + let component: BescheidWizardWeiterButtonComponent; + let fixture: ComponentFixture<BescheidWizardWeiterButtonComponent>; + + const button: string = getDataTestIdOf('bescheid-weiter-button'); + + let clickEmitter: Mock<EventEmitter<MouseEvent>>; + + beforeEach(() => { + clickEmitter = mock(EventEmitter); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BescheidWizardWeiterButtonComponent, MockComponent(ButtonWithSpinnerComponent)], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWizardWeiterButtonComponent); + component = fixture.componentInstance; + component.clickEmitter = useFromMock(clickEmitter); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('ods-button-with-spinner', () => { + describe('input', () => { + it('should set submitStateResource', () => { + const stateResource: StateResource<CommandResource> = createStateResource(createCommandResource(), true); + component.submitStateResource = stateResource; + + fixture.detectChanges(); + + expect(getElementFromFixtureByType(fixture, ButtonWithSpinnerComponent).stateResource).toEqual(stateResource); + }); + }); + + describe('output', () => { + it('should emit click', () => { + triggerEvent({ + fixture, + name: 'clickEmitter', + elementSelector: button, + }); + + expect(clickEmitter.emit).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..8348ca2bc0e80cd65557cfa8ae20b705a5e09598 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component.ts @@ -0,0 +1,13 @@ +import { StateResource } from '@alfa-client/tech-shared'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Resource } from '@ngxp/rest'; + +@Component({ + selector: 'alfa-bescheid-wizard-weiter-button', + templateUrl: './bescheid-wizard-weiter-button.component.html', +}) +export class BescheidWizardWeiterButtonComponent { + @Input() submitStateResource: StateResource<Resource>; + + @Output() clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..31a7953ddbbe01a16efe3ef77db557379ab94995 --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.spec.ts @@ -0,0 +1,142 @@ +import { Bescheid, BescheidLinkRel, BescheidResource, BescheidSendBy, BescheidService } from '@alfa-client/bescheid-shared'; +import { formatForDatabase } from '@alfa-client/tech-shared'; +import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; +import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { registerLocaleData } from '@angular/common'; +import localeDe from '@angular/common/locales/de'; +import { UntypedFormBuilder } from '@angular/forms'; +import { faker } from '@faker-js/faker'; +import { ResourceUri } from '@ngxp/rest'; +import { EMPTY, Observable, of } from 'rxjs'; +import { createBescheid, createBescheidResource } from '../../../../bescheid-shared/src/test/bescheid'; +import { singleCold } from '../../../../tech-shared/test/marbles'; +import { createVorgangWithEingangResource } from '../../../../vorgang-shared/test/vorgang'; +import { BescheidFormService } from './bescheid.formservice'; + +registerLocaleData(localeDe); + +describe('BescheidFormService', () => { + let service: BescheidFormService; + let bescheidService: Mock<BescheidService>; + const now: Date = new Date(); + Date.now = jest.fn().mockReturnValue(now); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource(); + + beforeEach(() => { + bescheidService = mock(BescheidService); + bescheidService.createBescheid.mockReturnValue(of(EMPTY)); + service = new BescheidFormService(new UntypedFormBuilder(), useFromMock(bescheidService)); + service.setVorgangWithEingangResource(vorgangWithEingangResource); + }); + + describe('doSubmit', () => { + it('should create bescheid', () => { + const formValue: Bescheid = createBescheid(); + service.getBescheidFormValue = jest.fn().mockReturnValue(formValue); + + service.submit(); + + expect(bescheidService.createBescheid).toHaveBeenCalledWith(vorgangWithEingangResource, formValue); + }); + }); + + describe('patchValues', () => { + let bescheidResource: BescheidResource; + let patch: jest.Mock; + + beforeEach(() => { + bescheidResource = createBescheidResource(); + patch = service.patch = jest.fn(); + }); + + it('should patch', () => { + service.patchValues(bescheidResource); + + expect(patch).toHaveBeenCalledWith({ + [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm, + [BescheidFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt), + [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: null, + [BescheidFormService.FIELD_SEND_BY]: String(bescheidResource.sendBy), + [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject, + [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText, + }); + }); + + it('should patch sendBy to default', () => { + service.patchValues({ + ...bescheidResource, + sendBy: undefined, + }); + + expect(patch).toHaveBeenCalledWith({ + [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm, + [BescheidFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt), + [BescheidFormService.FIELD_SEND_BY]: BescheidSendBy.NACHRICHT, + [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: null, + [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject, + [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText, + }); + }); + + it('should patch bescheid document uri', () => { + const bescheidDocumentUri: ResourceUri = faker.internet.url(); + service.patchValues({ + ...bescheidResource, + _links: { + ...bescheidResource._links, + [BescheidLinkRel.BESCHEID_DOCUMENT]: { href: bescheidDocumentUri }, + }, + }); + + expect(patch).toHaveBeenCalledWith({ + [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm, + [BescheidFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt), + [BescheidFormService.FIELD_SEND_BY]: BescheidSendBy.NACHRICHT, + [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: bescheidDocumentUri, + [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject, + [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText, + }); + }); + + it('should patch attachments', () => { + service.patchValues(bescheidResource); + + expect(service.getFormValue().attachments).toEqual(bescheidResource.attachments); + }); + }); + + describe('getBescheidFormValueChanges', () => { + const bescheid: Bescheid = createBescheid(); + + beforeEach(() => { + service.getBescheidFormValue = jest.fn().mockReturnValue(bescheid); + }); + + it('should emit bescheid', () => { + const bescheidFormValueChanges$: Observable<Bescheid> = service.getBescheidFormValueChanges(); + + expect(bescheidFormValueChanges$).toBeObservable(singleCold(bescheid)); + }); + }); + + describe('getBescheidFormValue', () => { + beforeEach(() => { + service.getFormValue = jest.fn().mockReturnValue({ beschiedenAm: now, bewilligt: 'true' }); + }); + + it('should getFormValue', () => { + service.getBescheidFormValue(); + + expect(service.getFormValue).toHaveBeenCalled(); + }); + + it('should return bescheid', () => { + const bescheid: Bescheid = service.getBescheidFormValue(); + + expect(bescheid).toEqual({ + bewilligt: true, + beschiedenAm: formatForDatabase(now), + } as Bescheid); + }); + }); +}); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.ts new file mode 100644 index 0000000000000000000000000000000000000000..ca522ee6a9d6dae7505db2630572da013258d13f --- /dev/null +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.ts @@ -0,0 +1,93 @@ +import { Bescheid, BescheidLinkRel, BescheidResource, BescheidSendBy, BescheidService } from '@alfa-client/bescheid-shared'; +import { AbstractFormService, StateResource, convertToBoolean, formatForDatabase } from '@alfa-client/tech-shared'; +import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { Injectable } from '@angular/core'; +import { UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { Resource, ResourceUri, getUrl, hasLink } from '@ngxp/rest'; +import { isUndefined } from 'lodash-es'; +import { Observable, startWith } from 'rxjs'; + +@Injectable() +export class BescheidFormService extends AbstractFormService { + static readonly FIELD_BESCHIEDEN_AM = 'beschiedenAm'; + static readonly FIELD_BEWILLIGT = 'bewilligt'; + static readonly FIELD_BESCHEID_DOCUMENT = 'bescheidDocument'; + static readonly FIELD_ATTACHMENTS = 'attachments'; + public static readonly FIELD_SEND_BY = 'sendBy'; + static readonly FIELD_NACHRICHT_SUBJECT = 'nachrichtSubject'; + static readonly FIELD_NACHRICHT_TEXT = 'nachrichtText'; + + static readonly FIELD_PATH_PREFIX = 'command.body'; + + private vorgangWithEingangResource: VorgangWithEingangResource; + + constructor( + formBuilder: UntypedFormBuilder, + private readonly bescheidService: BescheidService, + ) { + super(formBuilder); + } + + protected initForm(): UntypedFormGroup { + return this.formBuilder.group({ + [BescheidFormService.FIELD_BESCHIEDEN_AM]: new UntypedFormControl(new Date(Date.now())), + [BescheidFormService.FIELD_BEWILLIGT]: new UntypedFormControl('true'), + [BescheidFormService.FIELD_SEND_BY]: new UntypedFormControl(BescheidSendBy.NACHRICHT), + [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: new UntypedFormControl(null), + [BescheidFormService.FIELD_ATTACHMENTS]: new UntypedFormArray([]), + [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: new UntypedFormControl(''), + [BescheidFormService.FIELD_NACHRICHT_TEXT]: new UntypedFormControl(''), + }); + } + + protected doSubmit(): Observable<StateResource<Resource>> { + return this.bescheidService.createBescheid(this.vorgangWithEingangResource, this.getBescheidFormValue()); + } + + protected getPathPrefix(): string { + return BescheidFormService.FIELD_PATH_PREFIX; + } + + public patchValues(bescheid: BescheidResource): void { + const bescheidDocumentUri: ResourceUri = this.getBescheidDocumentUri(bescheid); + this.bescheidService.setDocumentUri(bescheidDocumentUri); + this.patch({ + [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheid.beschiedenAm, + [BescheidFormService.FIELD_BEWILLIGT]: String(bescheid.bewilligt), + [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: bescheidDocumentUri, + [BescheidFormService.FIELD_SEND_BY]: isUndefined(bescheid.sendBy) ? BescheidSendBy.NACHRICHT : bescheid.sendBy, + [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheid.nachrichtSubject, + [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheid.nachrichtText, + }); + bescheid.attachments.forEach((attachmentLink) => + (this.form.controls[BescheidFormService.FIELD_ATTACHMENTS] as UntypedFormArray).push( + new UntypedFormControl(attachmentLink), + ), + ); + } + + private getBescheidDocumentUri(bescheid: BescheidResource): ResourceUri | null { + if (hasLink(bescheid, BescheidLinkRel.BESCHEID_DOCUMENT)) { + return getUrl(bescheid, BescheidLinkRel.BESCHEID_DOCUMENT); + } + return null; + } + + public getBescheidFormValueChanges(): Observable<Bescheid> { + return this.form.valueChanges.pipe(startWith(this.getBescheidFormValue())); + } + + public getBescheidFormValue(): Bescheid { + const value: any = this.getFormValue(); + return { + ...value, + beschiedenAm: formatForDatabase(value.beschiedenAm), + bewilligt: convertToBoolean(value.bewilligt), + attachments: value.attachments, + }; + } + + public setVorgangWithEingangResource(vorgangWithEingangResource: VorgangWithEingangResource): void { + this.vorgangWithEingangResource = vorgangWithEingangResource; + } +} diff --git a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts index e80dff3c3e0610a0d46b4b5babfa41c1d713d01a..0b21469bbe360803b4356a8b03a23539b4073e97 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts @@ -13,12 +13,31 @@ import { DocumentInBescheidContainerComponent } from './bescheid-list-in-vorgang import { BeschiedenDateContainerComponent } from './beschieden-date-in-vorgang-container/beschieden-date-container/beschieden-date-container.component'; import { BeschiedenDateInVorgangContainerComponent } from './beschieden-date-in-vorgang-container/beschieden-date-in-vorgang-container.component'; +import { ButtonWithSpinnerComponent } from '@ods/component'; import { BescheidStatusTextComponent, BescheidWrapperComponent, + ButtonComponent, CloseIconComponent, + RadioButtonCardComponent, StampIconComponent, } from '@ods/system'; +import { BescheidWizardContainerComponent } from './bescheid-wizard-container/bescheid-wizard-container.component'; +import { BescheidWizardAbschliessenButtonComponent } from './bescheid-wizard-container/bescheid-wizard/abschliessen-button/bescheid-wizard-abschliessen-button.component'; +import { BescheidWizardAbschliessenDialogContainerComponent } from './bescheid-wizard-container/bescheid-wizard/abschliessen-dialog-container/bescheid-wizard-abschliessen-dialog-container.component'; +import { BescheidWizardAntragBescheidenComponent } from './bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/bescheid-wizard-antrag-bescheiden.component'; +import { BescheidWizardAntragBescheidenFormComponent } from './bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component'; +import { BescheidWizardAntragBescheidenSummaryComponent } from './bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/summary/bescheid-wizard-antrag-bescheiden-summary.component'; +import { BescheidWizardComponent } from './bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component'; +import { BescheidWizardCancelDialogContainerComponent } from './bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component'; +import { BescheidWizardDokumenteHochladenComponent } from './bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/bescheid-wizard-dokumente-hochladen.component'; +import { BescheidWizardDokumenteHochladenFormComponent } from './bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/form/bescheid-wizard-dokumente-hochladen-form.component'; +import { BescheidWizardDokumenteHochladenSummaryComponent } from './bescheid-wizard-container/bescheid-wizard/dokumente-hochladen/summary/bescheid-wizard-dokumente-hochladen-summary.component'; +import { BescheidWizardStepTitleComponent } from './bescheid-wizard-container/bescheid-wizard/step-title/bescheid-wizard-step-title.component'; +import { BescheidWizardStepperComponent } from './bescheid-wizard-container/bescheid-wizard/stepper/bescheid-wizard-stepper.component'; +import { BescheidWizardStepComponent } from './bescheid-wizard-container/bescheid-wizard/stepper/step/bescheid-wizard-step.component'; +import { BescheidWizardSummaryComponent } from './bescheid-wizard-container/bescheid-wizard/summary/bescheid-wizard-summary.component'; +import { BescheidWizardWeiterButtonComponent } from './bescheid-wizard-container/bescheid-wizard/weiter-button/bescheid-wizard-weiter-button.component'; @NgModule({ imports: [ @@ -32,6 +51,9 @@ import { BescheidWrapperComponent, StampIconComponent, CloseIconComponent, + ButtonWithSpinnerComponent, + RadioButtonCardComponent, + ButtonComponent, ], declarations: [ BescheidInVorgangContainerComponent, @@ -41,6 +63,22 @@ import { DocumentInBescheidContainerComponent, BeschiedenDateContainerComponent, BeschiedenDateInVorgangContainerComponent, + BescheidWizardContainerComponent, + BescheidWizardComponent, + BescheidWizardStepComponent, + BescheidWizardStepperComponent, + BescheidWizardStepTitleComponent, + BescheidWizardSummaryComponent, + BescheidWizardWeiterButtonComponent, + BescheidWizardCancelDialogContainerComponent, + BescheidWizardAntragBescheidenComponent, + BescheidWizardAntragBescheidenFormComponent, + BescheidWizardAntragBescheidenSummaryComponent, + BescheidWizardDokumenteHochladenComponent, + BescheidWizardDokumenteHochladenFormComponent, + BescheidWizardDokumenteHochladenSummaryComponent, + BescheidWizardAbschliessenButtonComponent, + BescheidWizardAbschliessenDialogContainerComponent, ], exports: [ BescheidInVorgangContainerComponent, diff --git a/alfa-client/libs/command-shared/test/command.ts b/alfa-client/libs/command-shared/test/command.ts index 6843ee1d96699bc59ad4aeab4b49f14b63f6cd0b..d0ffa279db7e9a62c14410cba5652806c41b5895 100644 --- a/alfa-client/libs/command-shared/test/command.ts +++ b/alfa-client/libs/command-shared/test/command.ts @@ -25,9 +25,17 @@ import { createStateResource, StateResource } from '@alfa-client/tech-shared'; import { faker } from '@faker-js/faker'; import { toResource } from 'libs/tech-shared/test/resource'; import { times } from 'lodash-es'; -import { CommandListLinkRel } from '../src/lib/command.linkrel'; +import { CommandLinkRel, CommandListLinkRel } from '../src/lib/command.linkrel'; import { CommandErrorMessage } from '../src/lib/command.message'; -import { Command, CommandListResource, CommandOrder, CommandResource, CommandStatus, CreateCommand, CreateCommandProps, } from '../src/lib/command.model'; +import { + Command, + CommandListResource, + CommandOrder, + CommandResource, + CommandStatus, + CreateCommand, + CreateCommandProps, +} from '../src/lib/command.model'; export function createCommand(): Command { return { @@ -68,15 +76,11 @@ export function createCommandErrorResource(linkRelations: string[] = []): Comman }; } -export function createCommandErrorStateResource( - linkRelations: string[] = [], -): StateResource<CommandResource> { +export function createCommandErrorStateResource(linkRelations: string[] = []): StateResource<CommandResource> { return createStateResource(createCommandErrorResource(linkRelations)); } -export function createCreateCommand( - order: CommandOrder = CommandOrder.VORGANG_ANNEHMEN, -): CreateCommand { +export function createCreateCommand(order: CommandOrder = CommandOrder.VORGANG_ANNEHMEN): CreateCommand { return { order, body: null }; } @@ -88,3 +92,11 @@ export function createCreateCommandProps(): CreateCommandProps { snackBarMessage: faker.word.sample(5), }; } + +export function createSuccessfullyDoneCommandStateResource(): StateResource<CommandResource> { + return createCommandStateResource([CommandLinkRel.EFFECTED_RESOURCE]); +} + +export function createSuccessfullyDoneCommandResource(): CommandResource { + return createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); +} diff --git a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.spec.ts b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.spec.ts index 4848c4005e5493630f498836b2807519da953cbe..bd5d2317e9642c5a633183483b806921e908ca3e 100644 --- a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.spec.ts +++ b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.spec.ts @@ -1,6 +1,8 @@ +import { ENTER_KEY, ESCAPE_KEY } from '@alfa-client/tech-shared'; import { getElementFromFixture } from '@alfa-client/test-utils'; -import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { createKeydownKeyboardEvent } from '../../../../../test-utils/src/lib/keyboard'; import { DropdownMenuComponent } from './dropdown-menu.component'; describe('DropdownMenuComponent', () => { @@ -140,27 +142,6 @@ describe('DropdownMenuComponent', () => { }); }); - describe('isEscapeKey', () => { - it('should return true', () => { - const escapeKeyEvent: KeyboardEvent = { - ...new KeyboardEvent('esc'), - key: 'Escape', - }; - - const result: boolean = component.isEscapeKey(escapeKeyEvent); - - expect(result).toBe(true); - }); - - it('should return false', () => { - const keyEvent: KeyboardEvent = new KeyboardEvent('whatever'); - - const result: boolean = component.isEscapeKey(keyEvent); - - expect(result).toBe(false); - }); - }); - describe('closePopupAndFocusButton', () => { beforeEach(() => { component.isPopupOpen = true; @@ -198,11 +179,8 @@ describe('DropdownMenuComponent', () => { }); describe('onKeydownHandler', () => { - const e: KeyboardEvent = new KeyboardEvent('test'); - beforeEach(() => { component.closePopupAndFocusButton = jest.fn(); - component.isEscapeKey = jest.fn(); }); describe('popup is closed', () => { @@ -210,10 +188,12 @@ describe('DropdownMenuComponent', () => { component.isPopupClosed = jest.fn().mockReturnValue(true); }); - it('should not check for escape key', () => { - component.onKeydownHandler(e); + it('should not close popup', () => { + component.closePopupAndFocusButton = jest.fn(); + + component.onKeydownHandler(createKeydownKeyboardEvent(ESCAPE_KEY)); - expect(component.isEscapeKey).not.toHaveBeenCalled(); + expect(component.closePopupAndFocusButton).not.toHaveBeenCalled(); }); }); @@ -222,22 +202,14 @@ describe('DropdownMenuComponent', () => { component.isPopupClosed = jest.fn().mockReturnValue(false); }); - it('should check for escape key', () => { - component.onKeydownHandler(e); - - expect(component.isEscapeKey).toHaveBeenCalled(); - }); - - it('should handle escape key', () => { - component.isEscapeKey = jest.fn().mockReturnValue(true); - - component.onKeydownHandler(e); + it('should close popup', () => { + component.onKeydownHandler(createKeydownKeyboardEvent(ESCAPE_KEY)); expect(component.closePopupAndFocusButton).toHaveBeenCalled(); }); - it('should not handle escape key', () => { - component.onKeydownHandler(e); + it('should not close popup', () => { + component.onKeydownHandler(createKeydownKeyboardEvent(ENTER_KEY)); expect(component.closePopupAndFocusButton).not.toHaveBeenCalled(); }); diff --git a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts index fd81fa2b276da1d907d9993b92e98a9fb76362d0..02a78af00276c13f33a7874dc1781f4c325624b3 100644 --- a/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts +++ b/alfa-client/libs/design-system/src/lib/dropdown-menu/dropdown-menu/dropdown-menu.component.ts @@ -1,3 +1,4 @@ +import { isEscapeKey } from '@alfa-client/tech-shared'; import { CdkTrapFocus } from '@angular/cdk/a11y'; import { CommonModule } from '@angular/common'; import { Component, ElementRef, HostListener, Input, ViewChild } from '@angular/core'; @@ -7,7 +8,7 @@ import { twMerge } from 'tailwind-merge'; selector: 'ods-dropdown-menu', standalone: true, imports: [CommonModule, CdkTrapFocus], - template: `<div class="relative w-fit"> + template: ` <div class="relative w-fit"> <button [ngClass]="[twMerge('block w-fit outline-2 outline-offset-2 outline-focus', buttonClass)]" (click)="handleButtonClick()" @@ -52,7 +53,7 @@ export class DropdownMenuComponent { @HostListener('document:keydown', ['$event']) onKeydownHandler(e: KeyboardEvent): void { if (this.isPopupClosed()) return; - if (this.isEscapeKey(e)) this.closePopupAndFocusButton(); + if (isEscapeKey(e)) this.closePopupAndFocusButton(); } @HostListener('document:click', ['$event']) @@ -85,10 +86,6 @@ export class DropdownMenuComponent { this.buttonRef.nativeElement.focus(); } - isEscapeKey(e: KeyboardEvent): boolean { - return e.key === 'Escape'; - } - isPopupClosed(): boolean { return !this.isPopupOpen; } diff --git a/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.spec.ts b/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.spec.ts index b83a6e167dbaabae7a8a6e4b6595086ff48bde93..f8f85ec82f7b0fe16fe4c828de802f913da07c57 100644 --- a/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.spec.ts +++ b/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.spec.ts @@ -1,8 +1,10 @@ +import { ENTER_KEY, ESCAPE_KEY } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { EventEmitter } from '@angular/core'; import { ComponentFixture, TestBed, discardPeriodicTasks, fakeAsync, tick } from '@angular/core/testing'; import { Resource } from '@ngxp/rest'; import { Subscription } from 'rxjs'; +import { createKeydownKeyboardEvent } from '../../../../../test-utils/src/lib/keyboard'; import { InstantSearchComponent } from './instant-search.component'; import { InstantSearchQuery, InstantSearchResult } from './instant-search.model'; @@ -391,7 +393,6 @@ describe('InstantSearchComponent', () => { beforeEach(() => { component.isSearchResultsEmpty = jest.fn(); component.isArrowNavigationKey = jest.fn(); - component.isEscapeKey = jest.fn(); component.handleArrowNavigation = jest.fn(); component.handleEscape = jest.fn(); }); @@ -413,10 +414,12 @@ describe('InstantSearchComponent', () => { expect(component.isArrowNavigationKey).not.toHaveBeenCalled(); }); - it('should ignore escape key handling', () => { - component.onKeydownHandler(keyboardEvent); + it('should not handle escape key', () => { + component.handleEscape = jest.fn(); + + component.onKeydownHandler(createKeydownKeyboardEvent(ESCAPE_KEY)); - expect(component.isEscapeKey).not.toHaveBeenCalled(); + expect(component.handleEscape).not.toHaveBeenCalled(); }); }); @@ -452,24 +455,14 @@ describe('InstantSearchComponent', () => { component.isArrowNavigationKey = jest.fn().mockReturnValue(false); }); - it('should check for escape key', () => { - component.onKeydownHandler(keyboardEvent); - - expect(component.isEscapeKey).toHaveBeenCalled(); - }); - it('should handle escape key', () => { - component.isEscapeKey = jest.fn().mockReturnValue(true); - - component.onKeydownHandler(keyboardEvent); + component.onKeydownHandler(createKeydownKeyboardEvent(ESCAPE_KEY)); expect(component.handleEscape).toHaveBeenCalled(); }); it('should not handle escape key', () => { - component.isEscapeKey = jest.fn().mockReturnValue(false); - - component.onKeydownHandler(keyboardEvent); + component.onKeydownHandler(createKeydownKeyboardEvent(ENTER_KEY)); expect(component.handleEscape).not.toHaveBeenCalled(); }); @@ -511,22 +504,6 @@ describe('InstantSearchComponent', () => { }); }); - describe('isEscapeKey', () => { - it('should return true', () => { - const escapeKeyEvent = { ...new KeyboardEvent('esc'), key: 'Escape' }; - - const result: boolean = component.isEscapeKey(escapeKeyEvent); - - expect(result).toBe(true); - }); - - it('should return false', () => { - const result: boolean = component.isEscapeKey(new KeyboardEvent('not escape')); - - expect(result).toBe(false); - }); - }); - describe('isLastItemOrOutOfArray', () => { it.each([3, 5])('should return true for %s', (index: number) => { const result: boolean = component.isLastItemOrOutOfArray(index, 4); diff --git a/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.ts b/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.ts index 8b23822f25beea96c9bdf42a52c4eba075c50b9d..f62dc5c2d201e15a711aa8e7b6275275f1669755 100644 --- a/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.ts +++ b/alfa-client/libs/design-system/src/lib/instant-search/instant-search/instant-search.component.ts @@ -1,4 +1,10 @@ -import { EMPTY_STRING, isNotNil } from '@alfa-client/tech-shared'; +import { + EMPTY_STRING, + isArrowDownKey, + isArrowUpKey, + isEscapeKey, + isNotNil, +} from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; import { Component, @@ -118,7 +124,7 @@ export class InstantSearchComponent implements OnInit, OnDestroy { onKeydownHandler(e: KeyboardEvent): void { if (this.isSearchResultsEmpty()) return; if (this.isArrowNavigationKey(e)) this.handleArrowNavigation(e); - if (this.isEscapeKey(e)) this.handleEscape(e); + if (isEscapeKey(e)) this.handleEscape(e); } @HostListener('document:click', ['$event']) @@ -209,11 +215,7 @@ export class InstantSearchComponent implements OnInit, OnDestroy { } isArrowNavigationKey(e: KeyboardEvent): boolean { - return e.key === 'ArrowDown' || e.key === 'ArrowUp'; - } - - isEscapeKey(e: KeyboardEvent): boolean { - return e.key === 'Escape'; + return isArrowDownKey(e) || isArrowUpKey(e); } onItemClicked(searchResult: InstantSearchResult<Resource>, index: number): void { diff --git a/alfa-client/libs/tech-shared/src/index.ts b/alfa-client/libs/tech-shared/src/index.ts index 970130b8c3cc71fccd268eb842d34a249f578750..91817109a1f0fefeef0d83f63bc61c1422b9d3ca 100644 --- a/alfa-client/libs/tech-shared/src/index.ts +++ b/alfa-client/libs/tech-shared/src/index.ts @@ -29,6 +29,7 @@ export * from './lib/error/error.handler'; export * from './lib/error/error.util'; export * from './lib/form.util'; export * from './lib/http.util'; +export * from './lib/keyboard.util'; export * from './lib/message-code'; export * from './lib/ngrx/actions'; export * from './lib/pipe/convert-api-error-to-error-messages.pipe'; diff --git a/alfa-client/libs/tech-shared/src/lib/keyboard.util.spec.ts b/alfa-client/libs/tech-shared/src/lib/keyboard.util.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..641555ae216d025aced3cd4bd9150b349c391cfb --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/keyboard.util.spec.ts @@ -0,0 +1,67 @@ +import { createKeydownKeyboardEvent } from '../../../test-utils/src/lib/keyboard'; +import { + ARROW_DOWN_KEY, + ARROW_UP_KEY, + ENTER_KEY, + ESCAPE_KEY, + isArrowDownKey, + isArrowUpKey, + isEnterKey, + isEscapeKey, +} from './keyboard.util'; + +describe('keyboard', () => { + describe('isEscapeKey', () => { + it('should return true', () => { + const isEscape: boolean = isEscapeKey(createKeydownKeyboardEvent(ESCAPE_KEY)); + + expect(isEscape).toBeTruthy(); + }); + + it('should return false', () => { + const isEscape: boolean = isEscapeKey(createKeydownKeyboardEvent(ENTER_KEY)); + + expect(isEscape).toBeFalsy(); + }); + }); + + describe('isEnterKey', () => { + it('should return true', () => { + const isEnter: boolean = isEnterKey(createKeydownKeyboardEvent(ENTER_KEY)); + + expect(isEnter).toBeTruthy(); + }); + + it('should return false', () => { + const isEnter: boolean = isEnterKey(createKeydownKeyboardEvent(ESCAPE_KEY)); + + expect(isEnter).toBeFalsy(); + }); + }); + describe('isArrowUpKey', () => { + it('should return true', () => { + const isArrowUp: boolean = isArrowUpKey(createKeydownKeyboardEvent(ARROW_UP_KEY)); + + expect(isArrowUp).toBeTruthy(); + }); + + it('should return false', () => { + const isArrowUp: boolean = isArrowUpKey(createKeydownKeyboardEvent(ESCAPE_KEY)); + + expect(isArrowUp).toBeFalsy(); + }); + }); + describe('isArrowDownKey', () => { + it('should return true', () => { + const isArrowDown: boolean = isArrowDownKey(createKeydownKeyboardEvent(ARROW_DOWN_KEY)); + + expect(isArrowDown).toBeTruthy(); + }); + + it('should return false', () => { + const isEnter: boolean = isArrowDownKey(createKeydownKeyboardEvent(ESCAPE_KEY)); + + expect(isEnter).toBeFalsy(); + }); + }); +}); diff --git a/alfa-client/libs/tech-shared/src/lib/keyboard.util.ts b/alfa-client/libs/tech-shared/src/lib/keyboard.util.ts new file mode 100644 index 0000000000000000000000000000000000000000..879628cefa63b44b4408d7ac977ba29a8b9bdd39 --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/keyboard.util.ts @@ -0,0 +1,20 @@ +export const ESCAPE_KEY = 'Escape'; +export const ENTER_KEY = 'Enter'; +export const ARROW_DOWN_KEY = 'ArrowDown'; +export const ARROW_UP_KEY = 'ArrowUp'; + +export function isEscapeKey(keyboardEvent: KeyboardEvent) { + return keyboardEvent.key === ESCAPE_KEY; +} + +export function isEnterKey(keyboardEvent: KeyboardEvent) { + return keyboardEvent.key === ENTER_KEY; +} + +export function isArrowDownKey(keyboardEvent: KeyboardEvent) { + return keyboardEvent.key === ARROW_DOWN_KEY; +} + +export function isArrowUpKey(keyboardEvent: KeyboardEvent) { + return keyboardEvent.key === ARROW_UP_KEY; +} diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.util.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.util.spec.ts index e5a9f28f281136f4ac735e04259cc76ce15a051e..b8d4d562732d4553910f3b0020c7f96377798179 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.util.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.util.spec.ts @@ -23,6 +23,7 @@ */ import { Resource } from '@ngxp/rest'; +import { createCommandStateResource } from '../../../../command-shared/test/command'; import { DummyListLinkRel } from '../../../test/dummy'; import { createApiError } from '../../../test/error'; import { createDummyListResource, createDummyResource, toResource } from '../../../test/resource'; @@ -38,6 +39,7 @@ import { isInvalidResourceCombination, isLoaded, isLoadingRequired, + isNotLoading, isValidStateResource, } from './resource.util'; @@ -167,19 +169,13 @@ describe('resource util', () => { describe('containsLoading', () => { it('should return true', () => { - const contains = containsLoading([ - createEmptyStateResource(true), - createEmptyStateResource(false), - ]); + const contains = containsLoading([createEmptyStateResource(true), createEmptyStateResource(false)]); expect(contains).toBeTruthy(); }); it('should return false', () => { - const contains = containsLoading([ - createEmptyStateResource(false), - createEmptyStateResource(false), - ]); + const contains = containsLoading([createEmptyStateResource(false), createEmptyStateResource(false)]); expect(contains).toBeFalsy(); }); @@ -193,10 +189,7 @@ describe('resource util', () => { }); it('should return loaded', () => { - const loaded = getSuccessfullyLoaded([ - createEmptyStateResource<Resource>(true), - loadedStateResource, - ]); + const loaded = getSuccessfullyLoaded([createEmptyStateResource<Resource>(true), loadedStateResource]); expect(loaded).toEqual([loadedStateResource]); }); @@ -230,10 +223,7 @@ describe('resource util', () => { it('should return true on loaded stateResource while configResource is null', () => { const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); - const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>( - stateResource, - null, - ); + const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>(stateResource, null); expect(isInvalidCombination).toBeTruthy(); }); @@ -242,10 +232,7 @@ describe('resource util', () => { const stateResource: StateResource<Resource> = createEmptyStateResource(); const configResource: Resource = createDummyResource(); - const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>( - stateResource, - configResource, - ); + const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>(stateResource, configResource); expect(isInvalidCombination).toBeTruthy(); }); @@ -257,10 +244,7 @@ describe('resource util', () => { }; const configResource: Resource = createDummyResource(); - const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>( - stateResource, - configResource, - ); + const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>(stateResource, configResource); expect(isInvalidCombination).toBeFalsy(); }); @@ -272,12 +256,29 @@ describe('resource util', () => { }; const configResource: Resource = createDummyResource(); - const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>( - stateResource, - configResource, - ); + const isInvalidCombination: boolean = isInvalidResourceCombination<Resource, Resource>(stateResource, configResource); expect(isInvalidCombination).toBeTruthy(); }); }); + + describe('isNotLoading', () => { + it('should return true for empty state resource', () => { + const notLoading: boolean = isNotLoading(createEmptyStateResource()); + + expect(notLoading).toBeTruthy(); + }); + + it('should return true', () => { + const notLoading: boolean = isNotLoading(createCommandStateResource()); + + expect(notLoading).toBeTruthy(); + }); + + it('should return false', () => { + const notLoading: boolean = isNotLoading(createEmptyStateResource(true)); + + expect(notLoading).toBeFalsy(); + }); + }); }); diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.util.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.util.ts index 4691f7998807aa6e0e35dbcea7d84a5fd4a09885..92900b7913c8f51f52166cbed03eeaf181557a06 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.util.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.util.ts @@ -69,6 +69,10 @@ export function isLoaded<T>(stateResource: StateResource<T>): boolean { return !stateResource.loading && !stateResource.reload && isNotNull(stateResource.resource); } +export function isNotLoading<T>(stateResource: StateResource<T>): boolean { + return !stateResource.loading; +} + export function hasStateResourceError(stateResource: StateResource<any>): boolean { return !isNil(stateResource.error); } diff --git a/alfa-client/libs/test-utils/src/lib/dialog.ts b/alfa-client/libs/test-utils/src/lib/dialog.ts index d9a32f03a17d554f48e5263d1df4c4b7bb552b9b..c882a2b8ca85ca7a09a87fb792aac41535b97249 100644 --- a/alfa-client/libs/test-utils/src/lib/dialog.ts +++ b/alfa-client/libs/test-utils/src/lib/dialog.ts @@ -1 +1,13 @@ -export const dialogRefMock = { close: jest.fn() }; +import { jest } from '@jest/globals'; +import { EMPTY, Observable } from 'rxjs'; + +export class DialogRefMock<R = unknown> { + public keydownEvents: Observable<KeyboardEvent> = EMPTY; + public closed: Observable<R> = EMPTY; + + public close = jest.fn(); +} + +export function createDialogRefMock<R = unknown>(): DialogRefMock<R> { + return new DialogRefMock(); +} diff --git a/alfa-client/libs/test-utils/src/lib/helper.ts b/alfa-client/libs/test-utils/src/lib/helper.ts index b95ce2e05c2a1912d502259ff1172ae34b16959b..95186d5b2b5cf003cbbb1a4cf33ec9388d1ebaa2 100644 --- a/alfa-client/libs/test-utils/src/lib/helper.ts +++ b/alfa-client/libs/test-utils/src/lib/helper.ts @@ -26,20 +26,22 @@ import { ComponentFixture } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { EventData } from './model'; -export function getElementFromFixtureByType<T>( - fixture: ComponentFixture<any>, - component: Type<T>, -): T { +export function getElementFromFixtureByType<T>(fixture: ComponentFixture<any>, component: Type<T>): T { return getDebugElementFromFixtureByType(fixture, component).componentInstance as T; } -function getDebugElementFromFixtureByType<T>( - fixture: ComponentFixture<any>, - component: Type<T>, -): DebugElement { +function getDebugElementFromFixtureByType<T>(fixture: ComponentFixture<any>, component: Type<T>): DebugElement { return fixture.debugElement.query(By.directive(component)); } +export function getElementsFromFixtureByType<T>(fixture: ComponentFixture<any>, component: Type<T>): T[] { + return getDebugElementsFromFixtureByType(fixture, component).map((element: DebugElement) => element.componentInstance as T); +} + +function getDebugElementsFromFixtureByType<T>(fixture: ComponentFixture<any>, component: Type<T>): DebugElement[] { + return fixture.debugElement.queryAll(By.directive(component)); +} + export function getElementFromFixture(fixture: ComponentFixture<any>, htmlElement: string): any { return fixture.nativeElement.querySelector(htmlElement); } @@ -48,45 +50,33 @@ export function getElementsFromFixture(fixture: ComponentFixture<any>, htmlEleme return fixture.nativeElement.querySelectorAll(htmlElement); } -export function dispatchEventFromFixture<T>( - fixture: ComponentFixture<T>, - elementSelector: string, - event: string, -): void { +export function dispatchEventFromFixture<T>(fixture: ComponentFixture<T>, elementSelector: string, event: string): void { const element: DebugElement = getDebugElementFromFixtureByCss(fixture, elementSelector); element.nativeElement.dispatchEvent(new Event(event)); } export function triggerEvent<T>(eventData: EventData<T>) { - const element: DebugElement = getDebugElementFromFixtureByCss( - eventData.fixture, - eventData.elementSelector, - ); + const element: DebugElement = getDebugElementFromFixtureByCss(eventData.fixture, eventData.elementSelector); element.triggerEventHandler(eventData.name, eventData.data); } -export function getDebugElementFromFixtureByCss( - fixture: ComponentFixture<any>, - query: string, -): DebugElement { +export function getDebugElementFromFixtureByCss(fixture: ComponentFixture<any>, query: string): DebugElement { return fixture.debugElement.query(By.css(query)); } +export function getElementComponentFromFixtureByCss<T>(fixture: ComponentFixture<any>, query: string): T { + return fixture.debugElement.query(By.css(query)).componentInstance as T; +} + export function getElementFromDomRoot(fixture: ComponentFixture<any>, htmlElement: string): any { return fixture.nativeElement.parentNode.querySelector(htmlElement); } -function getDebugElementFromFixtureByDirective( - fixture: ComponentFixture<any>, - query: Type<any>, -): DebugElement { +function getDebugElementFromFixtureByDirective(fixture: ComponentFixture<any>, query: Type<any>): DebugElement { return fixture.debugElement.query(By.directive(query)); } -function getAllDebugElementsFromFixtureByDirective( - fixture: ComponentFixture<any>, - query: Type<any>, -): DebugElement[] { +function getAllDebugElementsFromFixtureByDirective(fixture: ComponentFixture<any>, query: Type<any>): DebugElement[] { return fixture.debugElement.queryAll(By.directive(query)); } @@ -95,7 +85,5 @@ export function getMockComponent<T>(fixture: ComponentFixture<unknown>, componen } export function getMockComponents<T>(fixture: ComponentFixture<unknown>, component: Type<T>): T[] { - return getAllDebugElementsFromFixtureByDirective(fixture, component).map( - (debugElement) => <T>debugElement.componentInstance, - ); + return getAllDebugElementsFromFixtureByDirective(fixture, component).map((debugElement) => <T>debugElement.componentInstance); } diff --git a/alfa-client/libs/test-utils/src/lib/keyboard.ts b/alfa-client/libs/test-utils/src/lib/keyboard.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f53ef59ed1ce4dbbc6e03e35a18fd8b6432a948 --- /dev/null +++ b/alfa-client/libs/test-utils/src/lib/keyboard.ts @@ -0,0 +1,3 @@ +export function createKeydownKeyboardEvent(key: string): KeyboardEvent { + return new KeyboardEvent('keydown', { key }); +} diff --git a/alfa-client/libs/test-utils/src/lib/observable.ts b/alfa-client/libs/test-utils/src/lib/observable.ts new file mode 100644 index 0000000000000000000000000000000000000000..8748d82162cb6e4bafce46984753a2674a810b98 --- /dev/null +++ b/alfa-client/libs/test-utils/src/lib/observable.ts @@ -0,0 +1,10 @@ +import { Mock, mock } from '@alfa-client/test-utils'; +import { Observable, Subscription } from 'rxjs'; + +export function createSubscribedObservableMock<T>(): [Mock<Observable<T>>, Mock<Subscription>] { + const observableMock: Mock<Observable<T>> = mock(Observable<T>); + observableMock.pipe.mockReturnValue(observableMock); + const subscription: Mock<Subscription> = mock(Subscription); + observableMock.subscribe.mockReturnValue(subscription); + return [observableMock, subscription]; +} diff --git a/alfa-client/libs/ui/src/index.ts b/alfa-client/libs/ui/src/index.ts index ed84f3db5d20804637786f2d47d4e2ad8fed0f65..db9b583e4c67de83d5638cee0b66f31acbbb4720 100644 --- a/alfa-client/libs/ui/src/index.ts +++ b/alfa-client/libs/ui/src/index.ts @@ -50,6 +50,7 @@ export * from './lib/ui/open-url-button/open-url-button.component'; export * from './lib/ui/ozgcloud-button/ozgcloud-button-with-spinner/ozgcloud-button-with-spinner.component'; export * from './lib/ui/ozgcloud-button/ozgcloud-icon-button-primary/ozgcloud-icon-button-primary.component'; export * from './lib/ui/ozgcloud-button/ozgcloud-stroked-button-with-spinner/ozgcloud-stroked-button-with-spinner.component'; +export * from './lib/ui/ozgcloud-dialog/ozgcloud-dialog.result'; export * from './lib/ui/ozgcloud-dialog/ozgcloud-dialog.service'; export * from './lib/ui/ozgcloud-icon/ozgcloud-icon.component'; export * from './lib/ui/ozgcloud-menu/ozgcloud-menu.component'; diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result.spec.ts b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c927d248d6c5de7a207ca6ea492905a8369246c1 --- /dev/null +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result.spec.ts @@ -0,0 +1,82 @@ +import { createEmptyStateResource } from '@alfa-client/tech-shared'; +import { createSuccessfullyDoneCommandStateResource } from '../../../../../command-shared/test/command'; +import { + createDialogCancelResult, + createDialogResult, + isDialogCanceled, + isDialogSuccessfullyCompleted, + OzgcloudDialogCommandResult, +} from './ozgcloud-dialog.result'; + +describe('ozgcloud-dialog.result', () => { + describe('isDialogCanceled', () => { + it('should return true', () => { + const isDialogCancelled: boolean = isDialogCanceled({ stateResource: createEmptyStateResource() }); + + expect(isDialogCancelled).toBeTruthy(); + }); + + it('should return false for null result', () => { + const isDialogCancelled: boolean = isDialogCanceled(null); + + expect(isDialogCancelled).toBeFalsy(); + }); + + it('should return false for null state resource', () => { + const isDialogCancelled: boolean = isDialogCanceled({ stateResource: null }); + + expect(isDialogCancelled).toBeFalsy(); + }); + + it('should return false for non empty state resource', () => { + const isDialogCancelled: boolean = isDialogCanceled({ stateResource: createSuccessfullyDoneCommandStateResource() }); + + expect(isDialogCancelled).toBeFalsy(); + }); + }); + + describe('isDialogSuccessfullyCompleted', () => { + it('should return true', () => { + const isSuccessfullyCompleted: boolean = isDialogSuccessfullyCompleted({ + stateResource: createSuccessfullyDoneCommandStateResource(), + }); + expect(isSuccessfullyCompleted).toBeTruthy(); + }); + + it('should return false for null result', () => { + const isSuccessfullyCompleted: boolean = isDialogSuccessfullyCompleted(null); + + expect(isSuccessfullyCompleted).toBeFalsy(); + }); + + it('should return false for null state resource', () => { + const isSuccessfullyCompleted: boolean = isDialogSuccessfullyCompleted({ stateResource: null }); + + expect(isSuccessfullyCompleted).toBeFalsy(); + }); + + it('should return false for empty state resource', () => { + const isSuccessfullyCompleted: boolean = isDialogSuccessfullyCompleted({ stateResource: createEmptyStateResource() }); + + expect(isSuccessfullyCompleted).toBeFalsy(); + }); + }); + + describe('createDialogCancelResult', () => { + it('should create', () => { + const dialogResult: OzgcloudDialogCommandResult = createDialogCancelResult(); + + expect(dialogResult.stateResource).toEqual(createEmptyStateResource()); + }); + }); + + describe('createDialogResult', () => { + it('should create', () => { + const stateResource = createSuccessfullyDoneCommandStateResource(); + + const dialogCommandResult: OzgcloudDialogCommandResult = createDialogResult(stateResource); + + expect(dialogCommandResult.stateResource).toBe(stateResource); + }); + }); +}); diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result.ts b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result.ts new file mode 100644 index 0000000000000000000000000000000000000000..7404c7632d1965e81e92933aa4c68f18017cc1ad --- /dev/null +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.result.ts @@ -0,0 +1,29 @@ +import { CommandResource, isSuccessfulDone } from '@alfa-client/command-shared'; +import { + StateResource, + createEmptyStateResource, + isEmptyStateResource, +} from '@alfa-client/tech-shared'; + +export class OzgcloudDialogCommandResult<D = unknown> { + stateResource: StateResource<CommandResource>; + data?: D; +} + +export function createDialogCancelResult(): OzgcloudDialogCommandResult { + return { stateResource: createEmptyStateResource() }; +} + +export function createDialogResult( + stateResource: StateResource<CommandResource>, +): OzgcloudDialogCommandResult { + return { stateResource }; +} + +export function isDialogCanceled(dialogResult?: OzgcloudDialogCommandResult): boolean { + return dialogResult && isEmptyStateResource(dialogResult.stateResource); +} + +export function isDialogSuccessfullyCompleted(dialogResult?: OzgcloudDialogCommandResult): boolean { + return dialogResult && isSuccessfulDone(dialogResult.stateResource?.resource); +} diff --git a/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts index ca7bf24afd1e3f7fb1f4ba918cc6e3fe768aca33..61ff0c02a6c99af5ab62322705891bda9832e7d0 100644 --- a/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts +++ b/alfa-client/libs/ui/src/lib/ui/ozgcloud-dialog/ozgcloud-dialog.service.ts @@ -20,11 +20,11 @@ export class OzgcloudDialogService { constructor(private dialog: Dialog) {} - public openWizard<T, D>(component: ComponentType<T>, data?: D): DialogRef<T> { - return this.openDialog<T>(component, this.buildDialogConfigWithData<D>(data, this.WIZARD_DIALOG_CONFIG)); + public openWizard<C, D, R = unknown>(component: ComponentType<C>, data?: D): DialogRef<R> { + return this.openDialog<C, R>(component, this.buildDialogConfigWithData<D>(data, this.WIZARD_DIALOG_CONFIG)); } - public open<T, D = unknown>(component: ComponentType<T>, data?: D): DialogRef<T> { + public open<C, D = unknown, R = unknown>(component: ComponentType<C>, data?: D): DialogRef<R> { return this.openDialog(component, this.buildDialogConfigWithData(data)); } @@ -36,13 +36,13 @@ export class OzgcloudDialogService { return this.openInCallingComponentContext(component, viewContainerRef, data, this.GREY_BLUR_CONFIG); } - public openInCallingComponentContext<T, D = unknown>( - component: ComponentType<T>, + public openInCallingComponentContext<C, D = unknown, R = unknown>( + component: ComponentType<C>, viewContainerRef: ViewContainerRef, data?: D, dialogConfig?: DialogConfig, - ): DialogRef<T> { - return this.openDialog(component, this.buildDialogConfigWithData(data, { viewContainerRef, ...dialogConfig })); + ): DialogRef<R> { + return this.openDialog<C, R>(component, this.buildDialogConfigWithData(data, { viewContainerRef, ...dialogConfig })); } private buildDialogConfigWithData<D>(data: D, dialogConfig?: DialogConfig): DialogConfig | null { @@ -52,8 +52,8 @@ export class OzgcloudDialogService { return { ...dialogConfig, data }; } - private openDialog<T>(component: ComponentType<T>, dialogConfig?: DialogConfig): DialogRef<T> { - return this.dialog.open<T>(component, dialogConfig); + private openDialog<C, R = unknown>(component: ComponentType<C>, dialogConfig?: DialogConfig): DialogRef<R, C> { + return this.dialog.open<R, unknown, C>(component, dialogConfig); } public closeAll(): void { diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html index 5cda3412cc39266c656446c0c2e2bfc65aadb80a..d4eaeaf311e5360232e193238680f26f75bfd36e 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html @@ -30,7 +30,7 @@ [text]="buttonText" svgIcon="stamp" [stateResource]="commandStateResource$ | async" - (clickEmitter)="onClick()" + (clickEmitter)="bescheiden()" > </ozgcloud-stroked-button-with-spinner> @@ -40,7 +40,7 @@ svgIcon="stamp" [toolTip]="toolTipText" [stateResource]="commandStateResource$ | async" - (clickEmitter)="onClick()" + (clickEmitter)="bescheiden()" > </ozgcloud-icon-button-with-spinner> </ng-container> diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts index d6251e13d63c817db05ef4b8a744e5d100d88b82..9e0cd75bc9717c51e9b1a2fb8012e3906008e769 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts @@ -21,22 +21,19 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { BescheidResource, BescheidService } from '@alfa-client/bescheid-shared'; +import { BescheidResource, BescheidService, BescheidWizardDialogResult } from '@alfa-client/bescheid-shared'; import { CommandResource } from '@alfa-client/command-shared'; -import { - HasLinkPipe, - StateResource, - createEmptyStateResource, - createStateResource, -} from '@alfa-client/tech-shared'; -import { mock } from '@alfa-client/test-utils'; +import { createEmptyStateResource, createStateResource, HasLinkPipe, StateResource } from '@alfa-client/tech-shared'; +import { Mock, mock } from '@alfa-client/test-utils'; import { IconButtonWithSpinnerComponent, OzgcloudDialogService, OzgcloudStrokedButtonWithSpinnerComponent, } from '@alfa-client/ui'; +import { BescheidenDialogData } from '@alfa-client/vorgang-detail'; import { VorgangCommandService, + VorgangService, VorgangWithEingangLinkRel, VorgangWithEingangResource, } from '@alfa-client/vorgang-shared'; @@ -48,7 +45,6 @@ import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorga import { MockComponent } from 'ng-mocks'; import { Observable, of } from 'rxjs'; import { createBescheidResource } from '../../../../../bescheid-shared/src/test/bescheid'; -import { BescheidenDialogData } from '../../vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.model'; import { VorgangDetailBescheidenComponent } from '../../vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component'; import { BescheidenButtonComponent } from './bescheiden-button.component'; @@ -59,12 +55,20 @@ describe('BescheidenButtonComponent', () => { const bescheidenButton: string = getDataTestIdOf('bescheiden-button'); const bescheidenIconButton: string = getDataTestIdOf('bescheiden-icon-button'); - const vorgangCommandService = mock(VorgangCommandService); - const ozgcloudDialogService = mock(OzgcloudDialogService); - const bescheidService = mock(BescheidService); + let vorgangCommandService: Mock<VorgangCommandService>; + let ozgcloudDialogService: Mock<OzgcloudDialogService>; + let bescheidService: Mock<BescheidService>; + let vorgangService: Mock<VorgangService>; const dialogRef = <DialogRef<VorgangDetailBescheidenComponent>>{}; + beforeEach(() => { + vorgangCommandService = mock(VorgangCommandService); + ozgcloudDialogService = mock(OzgcloudDialogService); + bescheidService = mock(BescheidService); + vorgangService = mock(VorgangService); + }); + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ @@ -86,6 +90,10 @@ describe('BescheidenButtonComponent', () => { provide: BescheidService, useValue: bescheidService, }, + { + provide: VorgangService, + useValue: vorgangService, + }, ], }).compileComponents(); @@ -172,23 +180,21 @@ describe('BescheidenButtonComponent', () => { }); }); - describe('onClick', () => { + describe('bescheiden', () => { describe('should open bescheid wizard', () => { beforeEach(() => { component.openBescheidenWizard = jest.fn(); - component.vorgang = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, - ]); + component.vorgang = createVorgangWithEingangResource([VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT]); }); it('should open bescheid wizard when create bescheid draft link exists', () => { - component.onClick(); + component.bescheiden(); expect(component.openBescheidenWizard).toHaveBeenCalled(); }); it('should open bescheid wizard when bescheid draft exists', () => { - component.onClick(); + component.bescheiden(); expect(component.openBescheidenWizard).toHaveBeenCalled(); }); @@ -196,16 +202,14 @@ describe('BescheidenButtonComponent', () => { describe('should do bescheiden', () => { const command: CommandResource = createCommandResource(); - const comandStateResource$: Observable<StateResource<CommandResource>> = of( - createStateResource(command), - ); + const comandStateResource$: Observable<StateResource<CommandResource>> = of(createStateResource(command)); beforeEach(() => { vorgangCommandService.bescheiden.mockReturnValue(comandStateResource$); }); it('should call vorgangCommandService.bescheiden', () => { - component.onClick(); + component.bescheiden(); expect(vorgangCommandService.bescheiden).toHaveBeenCalled(); }); @@ -213,7 +217,7 @@ describe('BescheidenButtonComponent', () => { it('should assign response', () => { component.commandStateResource$ = of(createEmptyStateResource<CommandResource>()); - component.onClick(); + component.bescheiden(); expect(component.commandStateResource$).toBe(comandStateResource$); }); @@ -245,6 +249,37 @@ describe('BescheidenButtonComponent', () => { }); }); + // TODO: Use this version after completed Bescheid refactoring and delete the other version below. + // describe('openBescheidenWizard', () => { + // let dialogRefMock: DialogRefMock<BescheidWizardDialogResult>; + // + // beforeEach(() => { + // dialogRefMock = createDialogRefMock<BescheidWizardDialogResult>(); + // ozgcloudDialogService.openWizard.mockReturnValue(dialogRefMock); + // }); + // + // it('should open wizard dialog', () => { + // const vorgang = createVorgangWithEingangResource(); + // component.vorgang = vorgang; + // + // component.openBescheidenWizard(); + // + // expect(ozgcloudDialogService.openWizard).toHaveBeenCalledWith(BescheidWizardContainerComponent, { + // vorgangWithEingangResource: vorgang, + // }); + // }); + // + // it('should handleBescheidWizardClosed', () => { + // component.handleBescheidWizardClosed = jest.fn(); + // const dialogResult: BescheidWizardDialogResult = { reloadVorgang: true }; + // dialogRefMock.closed = of(dialogResult); + // + // component.openBescheidenWizard(); + // + // expect(component.handleBescheidWizardClosed).toHaveBeenCalledWith(dialogResult); + // }); + // }); + describe('openBescheidenWizard', () => { it('should init', () => { component.openBescheidenWizard(); @@ -253,9 +288,7 @@ describe('BescheidenButtonComponent', () => { }); it('should open bescheiden dialog with existing draft', () => { - component.vorgang = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.BESCHEID_DRAFT, - ]); + component.vorgang = createVorgangWithEingangResource([VorgangWithEingangLinkRel.BESCHEID_DRAFT]); component.openBescheidenDialogWithExistingDraft = jest.fn(); component.openBescheidenWizard(); @@ -289,11 +322,10 @@ describe('BescheidenButtonComponent', () => { describe('openBescheidenDialogWithExistingDraft', () => { const bescheidDraftResource: BescheidResource = createBescheidResource(); - const bescheidDraftStateResource: StateResource<BescheidResource> = - createStateResource(bescheidDraftResource); - const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource( - [VorgangWithEingangLinkRel.BESCHEID_DRAFT], - ); + const bescheidDraftStateResource: StateResource<BescheidResource> = createStateResource(bescheidDraftResource); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.BESCHEID_DRAFT, + ]); beforeEach(() => { component.vorgang = vorgangWithEingangResource; @@ -303,13 +335,10 @@ describe('BescheidenButtonComponent', () => { it('should open wizard if bescheid draft loaded', () => { component.openBescheidenDialogWithExistingDraft(); - expect(ozgcloudDialogService.openWizard).toHaveBeenCalledWith( - VorgangDetailBescheidenComponent, - { - bescheidDraftResource, - vorgangWithEingangResource, - }, - ); + expect(ozgcloudDialogService.openWizard).toHaveBeenCalledWith(VorgangDetailBescheidenComponent, { + bescheidDraftResource, + vorgangWithEingangResource, + }); }); it('should not open wizard if bescheid draft not loaded', () => { @@ -333,11 +362,10 @@ describe('BescheidenButtonComponent', () => { describe('openDialog', () => { const bescheidDraftResource: BescheidResource = createBescheidResource(); - const bescheidDraftStateResource: StateResource<BescheidResource> = - createStateResource(bescheidDraftResource); - const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource( - [VorgangWithEingangLinkRel.BESCHEID_DRAFT], - ); + const bescheidDraftStateResource: StateResource<BescheidResource> = createStateResource(bescheidDraftResource); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.BESCHEID_DRAFT, + ]); const dialogData: BescheidenDialogData = { bescheidDraftResource: bescheidDraftResource, @@ -347,16 +375,32 @@ describe('BescheidenButtonComponent', () => { beforeEach(() => { component.vorgang = vorgangWithEingangResource; bescheidService.getBescheidDraftIfExists.mockReturnValue(of(bescheidDraftStateResource)); - ozgcloudDialogService.openWizard; }); it('should call ozgcloudDialogService.openWizard', () => { component.openDialog(dialogData); - expect(ozgcloudDialogService.openWizard).toHaveBeenCalledWith( - VorgangDetailBescheidenComponent, - dialogData, - ); + expect(ozgcloudDialogService.openWizard).toHaveBeenCalledWith(VorgangDetailBescheidenComponent, dialogData); + }); + }); + + describe('handleBescheidWizardClosed', () => { + it('should reload current vorgang', () => { + component.handleBescheidWizardClosed({ reloadVorgang: true }); + + expect(vorgangService.reloadCurrentVorgang).toHaveBeenCalled(); + }); + + it('should not reload current vorgang', () => { + component.handleBescheidWizardClosed({ reloadVorgang: false }); + + expect(vorgangService.reloadCurrentVorgang).not.toHaveBeenCalled(); + }); + + it.each([null, undefined])('should not reload current vorgang if result nil', (dialogResult: BescheidWizardDialogResult) => { + component.handleBescheidWizardClosed(dialogResult); + + expect(vorgangService.reloadCurrentVorgang).not.toHaveBeenCalled(); }); }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts index d76a5cf53225299d7f63695281c06c13bba00cc0..09bc8b97ae156ce071a5f9bd72d497190a86b04e 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts @@ -21,12 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { BescheidResource, BescheidService } from '@alfa-client/bescheid-shared'; +import { BescheidResource, BescheidService, BescheidWizardDialogResult } from '@alfa-client/bescheid-shared'; import { CommandResource } from '@alfa-client/command-shared'; -import { StateResource, createEmptyStateResource, isLoaded } from '@alfa-client/tech-shared'; +import { StateResource, createEmptyStateResource, isLoaded, isNotNil } from '@alfa-client/tech-shared'; import { OzgcloudDialogService } from '@alfa-client/ui'; import { VorgangCommandService, + VorgangService, VorgangWithEingangLinkRel, VorgangWithEingangResource, } from '@alfa-client/vorgang-shared'; @@ -45,25 +46,23 @@ export class BescheidenButtonComponent implements OnInit { @Input() vorgang: VorgangWithEingangResource; @Input() showAsIconButton: boolean = false; - commandStateResource$: Observable<StateResource<CommandResource>> = of( - createEmptyStateResource<CommandResource>(), - ); + commandStateResource$: Observable<StateResource<CommandResource>> = of(createEmptyStateResource<CommandResource>()); readonly linkRel = VorgangWithEingangLinkRel; get buttonText(): string { - return hasLink(this.vorgang, VorgangWithEingangLinkRel.BESCHEID_DRAFT) ? 'Bescheiden fortsetzen' - : 'Bescheiden'; + return hasLink(this.vorgang, VorgangWithEingangLinkRel.BESCHEID_DRAFT) ? 'Bescheiden fortsetzen' : 'Bescheiden'; } get toolTipText(): string { - return hasLink(this.vorgang, VorgangWithEingangLinkRel.BESCHEID_DRAFT) ? - 'Vorgang bescheiden fortsetzen' - : 'Vorgang bescheiden'; + return hasLink(this.vorgang, VorgangWithEingangLinkRel.BESCHEID_DRAFT) ? 'Vorgang bescheiden fortsetzen' : ( + 'Vorgang bescheiden' + ); } constructor( private vorgangCommandService: VorgangCommandService, + private vorgangService: VorgangService, private ozgcloudDialogService: OzgcloudDialogService, private bescheidService: BescheidService, ) {} @@ -72,7 +71,7 @@ export class BescheidenButtonComponent implements OnInit { this.commandStateResource$ = this.vorgangCommandService.getBeschiedenCommand(); } - public onClick(): void { + public bescheiden(): void { if (this.shouldOpenBescheidenWizard()) { this.openBescheidenWizard(); } else { @@ -87,6 +86,23 @@ export class BescheidenButtonComponent implements OnInit { ); } + // TODO: Use this version after completed Bescheid refactoring and delete the other version below. + // public openBescheidenWizard(): void { + // const dialogData: BescheidenDialogData = { vorgangWithEingangResource: this.vorgang }; + // const dialogRef: DialogRef<BescheidWizardDialogResult> = this.ozgcloudDialogService.openWizard< + // BescheidWizardContainerComponent, + // BescheidenDialogData, + // BescheidWizardDialogResult + // >(BescheidWizardContainerComponent, dialogData); + // dialogRef.closed.subscribe((dialogResult: BescheidWizardDialogResult) => this.handleBescheidWizardClosed(dialogResult)); + // } + + handleBescheidWizardClosed(dialogResult: BescheidWizardDialogResult): void { + if (isNotNil(dialogResult) && dialogResult.reloadVorgang) { + this.vorgangService.reloadCurrentVorgang(); + } + } + public openBescheidenWizard(): void { this.bescheidService.init(); if (hasLink(this.vorgang, VorgangWithEingangLinkRel.BESCHEID_DRAFT)) { diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts index a88075f699841c8e2b5d9c085023f6f087efc854..4aa88c6ad462e183f3e7e626034f402eb4d506e4 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden.component.ts @@ -1,5 +1,6 @@ import { BescheidResource } from '@alfa-client/bescheid-shared'; import { PostfachService } from '@alfa-client/postfach-shared'; +import { isEscapeKey } from '@alfa-client/tech-shared'; import { OzgcloudDialogService } from '@alfa-client/ui'; import { VorgangService } from '@alfa-client/vorgang-shared'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; @@ -41,7 +42,7 @@ export class VorgangDetailBescheidenComponent implements OnDestroy, OnInit { handleEscapeKey(): void { this.keydownEventsSubscription = this.dialogRef.keydownEvents - .pipe(filter((event) => event.key === 'Escape')) + .pipe(filter(isEscapeKey)) .subscribe(() => this.cancelWizard()); } diff --git a/alfa-client/libs/zustaendige-stelle/src/lib/search-zustaendige-stelle-dialog/search-zustaendige-stelle-dialog.component.spec.ts b/alfa-client/libs/zustaendige-stelle/src/lib/search-zustaendige-stelle-dialog/search-zustaendige-stelle-dialog.component.spec.ts index adb12daf7e4ec5b14e8c3e505413869531193abb..a08b21697fa35477ad73dfcfef9456028c33f6a5 100644 --- a/alfa-client/libs/zustaendige-stelle/src/lib/search-zustaendige-stelle-dialog/search-zustaendige-stelle-dialog.component.spec.ts +++ b/alfa-client/libs/zustaendige-stelle/src/lib/search-zustaendige-stelle-dialog/search-zustaendige-stelle-dialog.component.spec.ts @@ -1,5 +1,13 @@ import { ToEmbeddedResourcesPipe } from '@alfa-client/tech-shared'; -import { EventData, Mock, dialogRefMock, getMockComponent, mock, triggerEvent } from '@alfa-client/test-utils'; +import { + createDialogRefMock, + DialogRefMock, + EventData, + getMockComponent, + Mock, + mock, + triggerEvent, +} from '@alfa-client/test-utils'; import { OrganisationsEinheitResource, OrganisationsEinheitService, @@ -27,8 +35,14 @@ describe('SearchZustaendigeStelleDialogComponent', () => { const service: Mock<OrganisationsEinheitService> = mock(OrganisationsEinheitService); const title: string = 'dummyTitle'; + let dialogRefMock: DialogRefMock; + const organisationsEinheitResource: OrganisationsEinheitResource = createOrganisationsEinheitResource(); + beforeEach(() => { + dialogRefMock = createDialogRefMock(); + }); + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [