I have a NumericFormat as part of a react-hook-form Controller which vitest renders with jest-dom. When trying to enter some test data into it, its value does not change.
The Component:
// MoneyValueInputForm.tsx
import { Controller, useFormContext } from "react-hook-form";
import { ReactElement } from "react";
import { TextField } from "@mui/material";
import { NumericFormat } from "react-number-format";
import FormInputPropsT from "@/components/form/FormInputPropsT";
const MoneyValueFormInput = ({
fieldName,
label,
}: FormInputPropsT): ReactElement => {
const { control } = useFormContext();
return (
<Controller
name={fieldName}
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => {
const muiProps = {
error: !!error,
helperText: error ? "Must enter a value, even if 0" : null,
label: label,
fullWidth: true,
};
return (
<NumericFormat
thousandSeparator
decimalScale={2}
fixedDecimalScale
onChange={onChange}
value={value}
customInput={TextField}
inputProps={{ "data-testid": "money-value-input" }}
{...muiProps}
/>
);
}}
/>
);
};
The test code:
// my.test.tsx
import { beforeEach, describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
describe("Given parent form", () => {
beforeEach(() => {
// properly render parent form with initialization data
});
describe("When entering amount 1234", async () => {
let amount: HtmlElement;
beforeEach(async () => {
// this operation is actually successful
// invoking getter method in debugger shows correct initialized value
amount = await screen.findByTestId("money-value-input"),
// this one isn't
// invoking getter method in debugger still shows initialized value
await user.type(amount, "1234");
});
it("Then should display 1,234.00", () => {
// consequently, this fails
expect(amount).toHaveValue("1,234.00);
});
});
}):
The component of course works when rendering the app in the browser; the element in question looks like this, clearly showing an <input ..> with my data-testid and a value of 0.00, which is the initialization:
<div class="MuiFormControl-root MuiFormControl-fullWidth MuiTextField-root mui-wb57ya-MuiFormControl-root-MuiTextField-root" inputmode="numeric">
<label class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-sizeMedium MuiInputLabel-outlined MuiFormLabel-colorPrimary MuiFormLabel-filled MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-sizeMedium MuiInputLabel-outlined mui-zv3br2-MuiFormLabel-root-MuiInputLabel-root" data-shrink="true" for=":r4:" id=":r4:-label">
Amount
</label>
<div class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-colorPrimary MuiInputBase-fullWidth MuiInputBase-formControl mui-1rbu2s8-MuiInputBase-root-MuiOutlinedInput-root">
<input aria-invalid="false" id=":r4:" data-testid="money-value-input" class="MuiInputBase-input MuiOutlinedInput-input mui-1t8l2tu-MuiInputBase-input-MuiOutlinedInput-input" type="text" value="0.00">
<fieldset aria-hidden="true" class="MuiOutlinedInput-notchedOutline mui-1d3z3hw-MuiOutlinedInput-notchedOutline">
<legend class="mui-14lo706">
<span>Amount</span>
</legend>
</fieldset>
</div>
</div>
I have even tried something hopeless like this:
async function enterAmount(input: HTMLElement, amount: string) {
await user.click(input);
for (let i = 0; i < amount.length; i++) {
await user.keyboard(amount[i]);
}
}
I must be missing something obvious ...