Compare commits

..

2 Commits

Author SHA1 Message Date
7e152e4432 feat(portal-self-service): surface submit failures with a retryable alert (refs #68)
All checks were successful
CI / lint (pull_request) Successful in 1m11s
CI / build (pull_request) Successful in 54s
CI / unit (pull_request) Successful in 1m0s
CI / frontend (pull_request) Successful in 1m49s
CI / mutation (pull_request) Successful in 4m3s
CI / verify-stack (pull_request) Successful in 7m42s
Add an error branch to submit(): on a failed BFF call, set a `failed` signal,
re-enable the button, and render a role="alert" message so the user knows the
submit did not go through and can retry — instead of the click silently doing
nothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:33:09 +02:00
5bf25f094d test(portal-self-service): submit surfaces BFF failures instead of swallowing them (refs #68)
Failing test: when postSelfServiceRegistrations errors, the page should show an
alert, not the confirmation, and keep the submit button available for retry.
Currently submit() has no error handler, so the rejection is swallowed and the
page silently stays put — exactly the failure mode that hid the missing-token
bug behind a 90s e2e timeout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:32:30 +02:00
3 changed files with 32 additions and 5 deletions

View File

@@ -8,6 +8,11 @@
</p> </p>
} @else { } @else {
<p utrecht-paragraph>U bent ingelogd met BSN {{ bsn() }}.</p> <p utrecht-paragraph>U bent ingelogd met BSN {{ bsn() }}.</p>
@if (failed()) {
<p utrecht-paragraph role="alert">
Er ging iets mis bij het indienen van uw registratie. Probeer het opnieuw.
</p>
}
<button <button
utrecht-button utrecht-button
appearance="primary-action-button" appearance="primary-action-button"

View File

@@ -1,6 +1,6 @@
import { signal } from '@angular/core'; import { signal } from '@angular/core';
import { fireEvent, render, screen } from '@testing-library/angular'; import { fireEvent, render, screen } from '@testing-library/angular';
import { of } from 'rxjs'; import { of, throwError } from 'rxjs';
import { AuthService } from 'auth'; import { AuthService } from 'auth';
import { BffApiV1Service } from 'api-client'; import { BffApiV1Service } from 'api-client';
import { axe } from 'vitest-axe'; import { axe } from 'vitest-axe';
@@ -43,6 +43,19 @@ describe('RegistrationPage', () => {
expect(await screen.findByText(/ontvangen/i)).toBeTruthy(); expect(await screen.findByText(/ontvangen/i)).toBeTruthy();
}); });
it('shows an error and keeps the submit available when the BFF call fails', async () => {
const { post, providers: p } = providers(vi.fn().mockReturnValue(throwError(() => new Error('BFF rejected'))));
await render(RegistrationPage, { providers: p });
fireEvent.click(screen.getByRole('button', { name: /indienen/i }));
expect(post).toHaveBeenCalledTimes(1);
// The failure is surfaced (not swallowed), the confirmation is not shown, and the user can retry.
expect(await screen.findByRole('alert')).toBeTruthy();
expect(screen.queryByText(/ontvangen/i)).toBeNull();
expect(screen.getByRole('button', { name: /indienen/i })).toBeTruthy();
});
it('has no WCAG 2.1 AA violations on the submit page', async () => { it('has no WCAG 2.1 AA violations on the submit page', async () => {
// The portal is Dutch; the real index.html sets lang. Set it here so the document-level // The portal is Dutch; the real index.html sets lang. Set it here so the document-level
// html-has-lang rule reflects the app, not the bare jsdom document. // html-has-lang rule reflects the app, not the bare jsdom document.

View File

@@ -21,13 +21,22 @@ export class RegistrationPage {
protected readonly submitting = signal(false); protected readonly submitting = signal(false);
protected readonly reference = signal<string | undefined>(undefined); protected readonly reference = signal<string | undefined>(undefined);
protected readonly submitted = signal(false); protected readonly submitted = signal(false);
protected readonly failed = signal(false);
submit(): void { submit(): void {
this.submitting.set(true); this.submitting.set(true);
this.bff.postSelfServiceRegistrations().subscribe((accepted: SubmitAccepted) => { this.failed.set(false);
this.bff.postSelfServiceRegistrations().subscribe({
next: (accepted: SubmitAccepted) => {
this.reference.set(accepted.registrationId); this.reference.set(accepted.registrationId);
this.submitted.set(true); this.submitted.set(true);
this.submitting.set(false); this.submitting.set(false);
},
// Surface the failure instead of swallowing it: re-enable the button so the user can retry.
error: () => {
this.failed.set(true);
this.submitting.set(false);
},
}); });
} }
} }