Merged in feat/SW-619-signup-non-happy (pull request #1083)

Feat/SW-619 signup non happy

* feat(SW-619): Added tests for Date input

* feat(SW-619): Updated date input to not allow date below 18 years old, also added form validation and tests to cover this change

* fix

* feat(SW-619): add info banner if membership verification fails

* fix(SW-619): update test description


Approved-by: Christel Westerberg
Approved-by: Arvid Norlin
This commit is contained in:
Tobias Johansson
2025-01-08 12:31:30 +00:00
parent a056b36443
commit 18dd08f10e
11 changed files with 208 additions and 7 deletions

View File

@@ -0,0 +1,138 @@
import { describe, expect, test } from "@jest/globals" // importing because of type conflict with globals from Cypress
import { render, screen } from "@testing-library/react"
import { type UserEvent, userEvent } from "@testing-library/user-event"
import { FormProvider, useForm } from "react-hook-form"
import { Lang } from "@/constants/languages"
import { dt } from "@/lib/dt"
import { getLocalizedMonthName } from "@/utils/dateFormatting"
import Date from "./index"
interface FormWrapperProps {
defaultValues: Record<string, unknown>
children: React.ReactNode
onSubmit: (data: unknown) => void
}
function FormWrapper({ defaultValues, children, onSubmit }: FormWrapperProps) {
const methods = useForm({
defaultValues,
})
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit((data) => onSubmit(data))}>
{children}
<button type="submit">Submit</button>
</form>
</FormProvider>
)
}
async function selectOption(user: UserEvent, name: RegExp, value: string) {
// since its not a proper Select element selectOptions from userEvent doesn't work
const select = screen.queryByRole("button", { name })
if (select) {
await user.click(select)
const option = screen.queryByRole("option", { name: value })
if (option) {
await user.click(option)
} else {
await user.click(select) // click select again to close it
}
}
}
const testCases = [
{
description: "date is set and submitted successfully",
defaultValue: "",
dateOfBirth: "1987-12-05",
expectedOutput: {
dateOfBirth: "1987-12-05",
year: 1987,
month: 12,
day: 5,
},
},
{
description: "sets default value and submits successfully",
defaultValue: "2000-01-01",
dateOfBirth: "",
expectedOutput: {
dateOfBirth: "2000-01-01",
year: 2000,
month: 1,
day: 1,
},
},
{
description: "accepts date exactly 18 years old",
defaultValue: "",
dateOfBirth: dt().subtract(18, "year").format("YYYY-MM-DD"),
expectedOutput: {
dateOfBirth: dt().subtract(18, "year").format("YYYY-MM-DD"),
},
},
{
description: "rejects date below 18 years old - by year",
defaultValue: "",
dateOfBirth: dt().subtract(17, "year").format("YYYY-MM-DD"),
expectedOutput: {
dateOfBirth: "",
},
},
{
description: "rejects date below 18 years old - by month",
defaultValue: "",
dateOfBirth: dt().subtract(18, "year").add(1, "month").format("YYYY-MM-DD"),
expectedOutput: {
dateOfBirth: "",
},
},
{
description: "rejects date below 18 years old - by day",
defaultValue: "",
dateOfBirth: dt().subtract(18, "year").add(1, "day").format("YYYY-MM-DD"),
expectedOutput: {
dateOfBirth: "",
},
},
]
describe("Date input", () => {
test.each(testCases)(
"$description",
async ({ defaultValue, dateOfBirth, expectedOutput }) => {
const user = userEvent.setup()
const handleSubmit = jest.fn()
render(
<FormWrapper
defaultValues={{ dateOfBirth: defaultValue }}
onSubmit={handleSubmit}
>
<Date name="dateOfBirth" />
</FormWrapper>
)
const date = dt(dateOfBirth).toDate()
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
await selectOption(user, /year/i, year.toString())
await selectOption(user, /month/i, getLocalizedMonthName(month, Lang.en))
await selectOption(user, /day/i, day.toString())
const submitButton = screen.getByRole("button", { name: /submit/i })
await user.click(submitButton)
expect(handleSubmit).toHaveBeenCalledWith(
expect.objectContaining(expectedOutput)
)
}
)
})

View File

@@ -19,6 +19,8 @@ import styles from "./date.module.css"
export default function DateSelect({ name, registerOptions = {} }: DateProps) {
const intl = useIntl()
const lang = useLang()
const { control, setValue, formState, watch } = useFormContext()
const { field, fieldState } = useController({
control,
@@ -31,14 +33,20 @@ export default function DateSelect({ name, registerOptions = {} }: DateProps) {
const month = watch(DateName.month)
const day = watch(DateName.day)
const lang = useLang()
const months = rangeArray(1, 12).map((month) => ({
const minAgeDate = dt().subtract(18, "year").toDate() // age 18
const minAgeYear = minAgeDate.getFullYear()
const minAgeMonth = year === minAgeYear ? minAgeDate.getMonth() + 1 : null
const minAgeDay =
Number(year) === minAgeYear && Number(month) === minAgeMonth
? minAgeDate.getDate()
: null
const months = rangeArray(1, minAgeMonth ?? 12).map((month) => ({
value: month,
label: getLocalizedMonthName(month, lang),
}))
const currentYear = new Date().getFullYear()
const years = rangeArray(1900, currentYear - 18)
const years = rangeArray(1900, minAgeYear)
.reverse()
.map((year) => ({ value: year, label: year.toString() }))
@@ -48,7 +56,7 @@ export default function DateSelect({ name, registerOptions = {} }: DateProps) {
month ? Number(month) - 1 : null
)
const days = rangeArray(1, daysInMonth).map((day) => ({
const days = rangeArray(1, minAgeDay ?? daysInMonth).map((day) => ({
value: day,
label: `${day}`,
}))
@@ -119,7 +127,6 @@ export default function DateSelect({ name, registerOptions = {} }: DateProps) {
ref={field.ref}
value={dateValue}
data-testid={name}
className={styles.datePicker}
>
<Group>
<DateInput className={styles.container}>