import { HttpErrorResponse } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of, throwError } from 'rxjs'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { CaseDetailService } from '../case-detail.service'; import { ActionLink, ApplicantDetailsRequest } from '../case-detail.types'; import { EditApplicantDetails } from './edit-applicant-details'; const initial: ApplicantDetailsRequest = { surname: 'de Vries', initials: 'A.', address: { street: 'Kerkweg', number: '12', postalCode: '3512JK', city: 'Utrecht' }, email: 'anna@example.nl', phone: null, preferredChannel: 'Post', }; describe('EditApplicantDetails', () => { let fixture: ComponentFixture; let updateDetails: ReturnType; function render(actionLink: ActionLink): HTMLElement { fixture = TestBed.createComponent(EditApplicantDetails); fixture.componentRef.setInput('actionLink', actionLink); fixture.componentRef.setInput('initial', initial); fixture.detectChanges(); return fixture.nativeElement as HTMLElement; } function submitForm(el: HTMLElement): void { el.querySelector('form')!.dispatchEvent(new Event('submit', { cancelable: true })); fixture.detectChanges(); } beforeEach(async () => { updateDetails = vi.fn(); await TestBed.configureTestingModule({ imports: [EditApplicantDetails], providers: [{ provide: CaseDetailService, useValue: { updateDetails } }], }).compileComponents(); }); it('shows the write-through disclaimer only in writeThrough mode', () => { const writeThroughEl = render({ mode: 'writeThrough', href: '/api/worklist/legacy/1001/details' }); expect(writeThroughEl.querySelector('[data-testid="write-through-disclaimer"]')).toBeTruthy(); const ownedEl = render({ mode: 'owned', href: '/api/worklist/owned/abc/details' }); expect(ownedEl.querySelector('[data-testid="write-through-disclaimer"]')).toBeFalsy(); }); it('submits the form body to the given href and emits saved on success', () => { updateDetails.mockReturnValue(of(undefined)); const el = render({ mode: 'owned', href: '/api/worklist/owned/abc/details' }); const savedSpy = vi.fn(); fixture.componentInstance.saved.subscribe(savedSpy); submitForm(el); expect(updateDetails).toHaveBeenCalledWith('/api/worklist/owned/abc/details', { surname: 'de Vries', initials: 'A.', address: { street: 'Kerkweg', number: '12', postalCode: '3512JK', city: 'Utrecht' }, email: 'anna@example.nl', phone: null, preferredChannel: 'Post', }); expect(savedSpy).toHaveBeenCalled(); }); it('maps a 400 ErrorsResponse to per-field inline errors', () => { updateDetails.mockReturnValue( throwError( () => new HttpErrorResponse({ status: 400, error: { errors: [ { field: 'surname', message: 'Achternaam is verplicht.' }, { field: 'address.number', message: 'Huisnummer is verplicht.' }, { field: 'address.postalCode', message: 'Ongeldige postcode.' }, ], }, }), ), ); const el = render({ mode: 'writeThrough', href: '/api/worklist/legacy/1001/details' }); submitForm(el); expect(el.querySelector('[data-testid="field-error-surname"]')?.textContent).toContain('Achternaam is verplicht.'); expect(el.querySelector('[data-testid="field-error-address.number"]')?.textContent).toContain('Huisnummer is verplicht.'); expect(el.querySelector('[data-testid="field-error-address.postalCode"]')?.textContent).toContain('Ongeldige postcode.'); }); it('shows a 422 InvariantViolationResponse as an invariant + message banner', () => { updateDetails.mockReturnValue( throwError(() => new HttpErrorResponse({ status: 422, error: { invariant: 'Address.AllPartsRequired', message: 'All address parts are required.' } })), ); const el = render({ mode: 'owned', href: '/api/worklist/owned/abc/details' }); submitForm(el); expect(el.querySelector('[data-testid="edit-details-banner"]')?.textContent).toContain('Address.AllPartsRequired'); expect(el.querySelector('[data-testid="edit-details-banner"]')?.textContent).toContain('All address parts are required.'); }); it('shows a 409 MessageResponse as a banner', () => { updateDetails.mockReturnValue( throwError(() => new HttpErrorResponse({ status: 409, error: { message: 'This aanvraag has already been migrated.' } })), ); const el = render({ mode: 'writeThrough', href: '/api/worklist/legacy/1002/details' }); submitForm(el); expect(el.querySelector('[data-testid="edit-details-banner"]')?.textContent).toContain('This aanvraag has already been migrated.'); }); });