11import { render , screen , fireEvent } from '@testing-library/react' ;
2- import { describe , it , expect , vi } from 'vitest' ;
2+ import { describe , it , expect , vi , beforeEach } from 'vitest' ;
33import { AddTodo } from '../add-todo' ;
44import { createMemoryRouter , RouterProvider } from 'react-router-dom' ;
5+ import type { ReactElement , ReactNode , ChangeEvent , FormEvent } from 'react' ;
56
6- function renderWithRouter ( ui : React . ReactElement ) {
7+ // Create a stateful mock for the input field
8+ let testInputValue = '' ;
9+
10+ // Mock lucide-react icons
11+ vi . mock ( 'lucide-react' , ( ) => ( {
12+ Plus : ( ) => null ,
13+ } ) ) ;
14+
15+ // Mock the @lambdacurry /forms components
16+ interface TextFieldProps {
17+ name : string ;
18+ placeholder : string ;
19+ className : string ;
20+ }
21+
22+ vi . mock ( '@lambdacurry/forms' , ( ) => ( {
23+ TextField : ( { name, placeholder, className } : TextFieldProps ) => (
24+ < input
25+ name = { name }
26+ placeholder = { placeholder }
27+ className = { className }
28+ type = "text"
29+ value = { testInputValue }
30+ onChange = { ( e ) => { testInputValue = e . target . value ; } }
31+ />
32+ ) ,
33+ FormError : ( ) => null ,
34+ } ) ) ;
35+
36+ interface ButtonProps {
37+ children : ReactNode ;
38+ onClick : ( ) => void ;
39+ type : 'button' | 'submit' | 'reset' ;
40+ }
41+
42+ vi . mock ( '@lambdacurry/forms/ui' , ( ) => ( {
43+ Button : ( { children, onClick, type } : ButtonProps ) => (
44+ < button type = { type } onClick = { onClick } >
45+ { children }
46+ </ button >
47+ ) ,
48+ } ) ) ;
49+
50+ // Mock the remix-hook-form module
51+ interface RemixFormConfig {
52+ submitHandlers ?: {
53+ onValid : ( data : { text : string } ) => void ;
54+ } ;
55+ [ key : string ] : unknown ;
56+ }
57+
58+ vi . mock ( 'remix-hook-form' , ( ) => ( {
59+ RemixFormProvider : ( { children } : { children : ReactNode } ) => children ,
60+ useRemixForm : ( config : RemixFormConfig ) => {
61+ return {
62+ ...config ,
63+ getValues : ( _name : string ) => testInputValue ,
64+ reset : vi . fn ( ( ) => {
65+ testInputValue = '' ;
66+ // Force re-render by dispatching a custom event
67+ const inputs = document . querySelectorAll ( 'input[name="text"]' ) ;
68+ inputs . forEach ( input => {
69+ ( input as HTMLInputElement ) . value = '' ;
70+ } ) ;
71+ } ) ,
72+ setValue : vi . fn ( ( _name : string , value : string ) => {
73+ testInputValue = value ;
74+ } ) ,
75+ register : vi . fn ( ( name : string ) => ( {
76+ name,
77+ onChange : ( e : ChangeEvent < HTMLInputElement > ) => {
78+ testInputValue = e . target . value ;
79+ } ,
80+ value : testInputValue
81+ } ) ) ,
82+ handleSubmit : vi . fn ( ( onValid : ( data : { text : string } ) => void ) => ( e : FormEvent ) => {
83+ e . preventDefault ( ) ;
84+ if ( testInputValue ?. trim ( ) ) {
85+ onValid ( { text : testInputValue . trim ( ) } ) ;
86+ }
87+ } ) ,
88+ formState : { errors : { } } ,
89+ watch : vi . fn ( ( _name : string ) => testInputValue ) ,
90+ } ;
91+ }
92+ } ) ) ;
93+
94+ function renderWithRouter ( ui : ReactElement ) {
795 const router = createMemoryRouter ( [
896 { path : '/' , element : ui }
997 ] , { initialEntries : [ '/' ] } ) ;
@@ -13,10 +101,12 @@ function renderWithRouter(ui: React.ReactElement) {
13101// hoist regex literals to top-level to satisfy biome's useTopLevelRegex
14102const ADD_REGEX = / a d d / i;
15103
16- // hoist regex literals to top-level to satisfy biome's useTopLevelRegex
17- const ADD_REGEX = / a d d / i;
18-
19104describe ( 'AddTodo' , ( ) => {
105+ beforeEach ( ( ) => {
106+ // Reset the test state before each test
107+ testInputValue = '' ;
108+ } ) ;
109+
20110 it ( 'renders input and button' , ( ) => {
21111 const mockOnAdd = vi . fn ( ) ;
22112 renderWithRouter ( < AddTodo onAdd = { mockOnAdd } /> ) ;
@@ -53,7 +143,7 @@ describe('AddTodo', () => {
53143
54144 it ( 'does not call onAdd with empty text' , ( ) => {
55145 const mockOnAdd = vi . fn ( ) ;
56- render ( < AddTodo onAdd = { mockOnAdd } /> ) ;
146+ renderWithRouter ( < AddTodo onAdd = { mockOnAdd } /> ) ;
57147
58148 const button = screen . getByRole ( 'button' , { name : ADD_REGEX } ) ;
59149 fireEvent . click ( button ) ;
0 commit comments