From de76f8fb47ba5be7eab5e274843c7e1f3fb3d96c Mon Sep 17 00:00:00 2001 From: David Roper Date: Wed, 21 Oct 2026 10:52:39 -0400 Subject: [PATCH 1/2] feat: add limit to date of birth to today --- .../src/components/StartSessionForm/StartSessionForm.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/web/src/components/StartSessionForm/StartSessionForm.tsx b/apps/web/src/components/StartSessionForm/StartSessionForm.tsx index 31d8218ef..f07488170 100644 --- a/apps/web/src/components/StartSessionForm/StartSessionForm.tsx +++ b/apps/web/src/components/StartSessionForm/StartSessionForm.tsx @@ -224,6 +224,13 @@ export const StartSessionForm = ({ subjectId: z.string().optional(), subjectDateOfBirth: z .date() + .max(currentDate, { + message: t({ + en: 'Date of birth must be in the past', + es: 'La fecha de nacimiento debe estar en el pasado', + fr: 'La date de naissance doit ĂȘtre dans le passĂ©' + }) + }) .optional() .refine( (date) => { From c784869b13506fa58a96e941113679978d2ddeac Mon Sep 17 00:00:00 2001 From: David Roper Date: Wed, 21 Oct 2026 10:53:02 -0400 Subject: [PATCH 2/2] test: add tests for new limit to dob --- .../__tests__/StartSessionForm.test.tsx | 36 +++++++++++++++++++ .../pages/_app/session/start-session.page.ts | 6 ++++ testing/src/specs/start-session.spec.ts | 18 ++++++++++ 3 files changed, 60 insertions(+) diff --git a/apps/web/src/components/StartSessionForm/__tests__/StartSessionForm.test.tsx b/apps/web/src/components/StartSessionForm/__tests__/StartSessionForm.test.tsx index 336771abb..7276e289b 100644 --- a/apps/web/src/components/StartSessionForm/__tests__/StartSessionForm.test.tsx +++ b/apps/web/src/components/StartSessionForm/__tests__/StartSessionForm.test.tsx @@ -404,3 +404,39 @@ describe('StartSessionForm session type', () => { expect(screen.getByText('Date Assessed')).toBeTruthy(); }); }); + +describe('StartSessionForm date of birth', () => { + /** The date input commits typed text when it loses focus, not on each keystroke. */ + const enterDateOfBirth = (date: string) => { + fireEvent.focus(dateOfBirthInput()); + fireEvent.change(dateOfBirthInput(), { target: { value: date } }); + fireEvent.blur(dateOfBirthInput()); + }; + + const enterNewIdentifier = () => { + typeIdentifier('gamma'); + closeIdentifierPopup(); + }; + + it('should reject a date of birth in the future, since no subject can be born after today', async () => { + const form = renderForm([]); + enterNewIdentifier(); + enterDateOfBirth(`${new Date().getFullYear() + 1}-01-01`); + submit(form); + await waitFor(() => + expect(errorMessages()).toContainEqual({ + field: 'subjectDateOfBirth', + message: 'Date of birth must be in the past' + }) + ); + expect(onSubmit).not.toHaveBeenCalled(); + }); + + it('should accept a date of birth in the past, so the check does not block a valid session', async () => { + const form = renderForm([]); + enterNewIdentifier(); + enterDateOfBirth('1990-01-01'); + await submitAndSettle(form); + expect(onSubmit.mock.lastCall?.[0].subjectData.dateOfBirth).toStrictEqual(new Date('1990-01-01')); + }); +}); diff --git a/testing/src/pages/_app/session/start-session.page.ts b/testing/src/pages/_app/session/start-session.page.ts index 5e14132ab..fab1d2b45 100644 --- a/testing/src/pages/_app/session/start-session.page.ts +++ b/testing/src/pages/_app/session/start-session.page.ts @@ -49,6 +49,12 @@ export class StartSessionPage extends AppPage { await this.fillSessionDetails(sex); } + /** The date input commits what was typed only when it loses focus, so it is blurred explicitly. */ + async fillDateOfBirth(date: string) { + await this.dateOfBirthField.fill(date); + await this.dateOfBirthField.blur(); + } + /** Everything the form needs beyond how the subject was identified. */ async fillSessionDetails(sex: string) { await this.fillSubjectDetails(sex); diff --git a/testing/src/specs/start-session.spec.ts b/testing/src/specs/start-session.spec.ts index 8977e0a9d..99f5bbc7f 100644 --- a/testing/src/specs/start-session.spec.ts +++ b/testing/src/specs/start-session.spec.ts @@ -214,6 +214,24 @@ test.describe('start session', () => { await expect(startSessionPage.subjectIdOption(identifier)).toBeVisible(); }); + test('should refuse to start a session for a subject whose date of birth is in the future', async ({ + getPageModel, + uniqueId + }) => { + const startSessionPage = await getPageModel('/session/start-session'); + await startSessionPage.sessionForm.waitFor({ state: 'visible' }); + await startSessionPage.selectIdentificationMethod('CUSTOM_ID'); + await startSessionPage.typeSubjectId(`future-${uniqueId}`); + await startSessionPage.dismissSubjectIdOptions(); + + await startSessionPage.fillDateOfBirth(`${new Date().getFullYear() + 1}-01-01`); + await startSessionPage.sexField.selectOption('Male'); + await startSessionPage.submitForm(); + + await expect(startSessionPage.errorMessages).toContainText(['Date of birth must be in the past']); + await expect(startSessionPage.successMessage).toHaveCount(0); + }); + test('should show a required-field error for every missing field when submitting the personal information form empty', async ({ getPageModel }) => {