From e5201c9c2a5863a70b5aec1febb3aaef0996978f Mon Sep 17 00:00:00 2001 From: alex-dembele Date: Wed, 7 Oct 2026 09:30:28 +0100 Subject: [PATCH] fix(frontend): give LoginForm its notice prop back (#893) Merging master into the #872 branch (990b93f3) resolved LoginForm's signature to the old one while its caller and body still used notice. Since PR #880 the sign-in screen threw 'notice is not defined' on render for everyone, tsc -b failed and 27 auth unit tests were red. The added test walks the only path that hands a notice up to AuthScreen: sign-up, enrolment on an expired token, back to the sign-in form with the reason. Signed-off-by: alex-dembele --- frontend/src/features/auth/AuthScreen.tsx | 2 +- .../features/auth/__tests__/signup.test.tsx | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/auth/AuthScreen.tsx b/frontend/src/features/auth/AuthScreen.tsx index d0204dbe..276ec680 100644 --- a/frontend/src/features/auth/AuthScreen.tsx +++ b/frontend/src/features/auth/AuthScreen.tsx @@ -87,7 +87,7 @@ function throttleMinutes(err: unknown): number | null | undefined { return seconds === null ? null : Math.max(1, Math.ceil(seconds / 60)); } -function LoginForm({ onRegister }: { onRegister: () => void }) { +function LoginForm({ notice = '', onRegister }: { notice?: string; onRegister: () => void }) { const navigate = useNavigate(); const [params, setParams] = useSearchParams(); const lang = useUIStore((s) => s.lang); diff --git a/frontend/src/features/auth/__tests__/signup.test.tsx b/frontend/src/features/auth/__tests__/signup.test.tsx index d6e326a0..e8ef2b58 100644 --- a/frontend/src/features/auth/__tests__/signup.test.tsx +++ b/frontend/src/features/auth/__tests__/signup.test.tsx @@ -10,6 +10,7 @@ // user straight back to the login screen. Registration was broken for everyone, // not in an edge case. +import { AxiosError, AxiosHeaders, type AxiosResponse } from 'axios'; import { describe, expect, it, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -48,6 +49,7 @@ vi.mock('../../../hooks/useAuthStore', () => { }); import { AuthScreen } from '../AuthScreen'; +import { authCopy } from '../authStrings'; function renderSignup() { return render( @@ -209,4 +211,28 @@ describe('signing up', () => { await waitFor(() => expect(navigate).toHaveBeenCalled()); expect(setupMFA).not.toHaveBeenCalled(); }); + + it('lands on the sign-in form with the reason when enrolment meets an expired token (#893)', async () => { + // The sign-up path is the one that hands its reason UP to AuthScreen, which + // passes it to the sign-in form as `notice` (#872). A merge dropped that prop + // and the sign-in form crashed on render for everyone. + login.mockResolvedValue({ status: 'mfa_enrollment_required', mfa_token: 'enrol-token' }); + const response = { + status: 401, + statusText: '', + data: { code: 'TOKEN_EXPIRED' }, + headers: {}, + config: { headers: new AxiosHeaders() }, + } as AxiosResponse; + setupMFA.mockReset(); + setupMFA.mockRejectedValueOnce( + new AxiosError('refused', '401', response.config, null, response), + ); + + renderSignup(); + await fillAndSubmit(); + + expect(await screen.findByTestId('login-password')).toBeInTheDocument(); + expect(screen.getByTestId('auth-error')).toHaveTextContent(authCopy('fr').mfaExpired); + }); });