@@ -3156,6 +3156,32 @@ describe('form api', () => {
31563156 await form . handleSubmit ( )
31573157 } )
31583158
3159+ it ( 'should call onSubmitInvalid when submitting while canSubmit is false (e.g., onMount error present)' , async ( ) => {
3160+ const onInvalid = vi . fn ( )
3161+
3162+ const form = new FormApi ( {
3163+ defaultValues : { name : '' } ,
3164+ validators : {
3165+ onMount : ( { value } ) => ( ! value . name ? 'Name required' : undefined ) ,
3166+ } ,
3167+ onSubmitInvalid : ( { value, formApi } ) => {
3168+ onInvalid ( value , formApi )
3169+ } ,
3170+ } )
3171+
3172+ form . mount ( )
3173+
3174+ // Mount a field to participate in touched/dirty state
3175+ new FieldApi ( { form, name : 'name' } ) . mount ( )
3176+
3177+ // With an onMount error present, the form is invalid and cannot submit
3178+ expect ( form . state . canSubmit ) . toBe ( false )
3179+
3180+ await form . handleSubmit ( )
3181+
3182+ expect ( onInvalid ) . toHaveBeenCalledTimes ( 1 )
3183+ } )
3184+
31593185 it ( 'should pass the handleSubmit default meta data to onSubmitInvalid' , async ( ) => {
31603186 const form = new FormApi ( {
31613187 onSubmitMeta : { dinosaur : 'Frank' } as { dinosaur : string } ,
@@ -3955,6 +3981,83 @@ it('should accept formId and return it', () => {
39553981 expect ( form . formId ) . toEqual ( 'age' )
39563982} )
39573983
3984+ it ( 'should call onSubmitInvalid when submitted with onMount error' , async ( ) => {
3985+ const onInvalidSpy = vi . fn ( )
3986+
3987+ const form = new FormApi ( {
3988+ defaultValues : { name : '' } ,
3989+ validators : {
3990+ onMount : ( ) => ( { name : 'Name is required' } ) ,
3991+ } ,
3992+ onSubmitInvalid : ( ) => onInvalidSpy ( ) ,
3993+ } )
3994+ form . mount ( )
3995+
3996+ const field = new FieldApi ( { form, name : 'name' } )
3997+ field . mount ( )
3998+
3999+ expect ( form . state . canSubmit ) . toBe ( false )
4000+
4001+ await form . handleSubmit ( )
4002+
4003+ expect ( onInvalidSpy ) . toHaveBeenCalledTimes ( 1 )
4004+ } )
4005+
4006+ it ( 'should not run submit validation when canSubmit is false' , async ( ) => {
4007+ const onSubmitValidatorSpy = vi
4008+ . fn ( )
4009+ . mockImplementation ( ( ) => 'Submit validation failed' )
4010+ const onInvalidSpy = vi . fn ( )
4011+
4012+ const form = new FormApi ( {
4013+ defaultValues : { name : '' } ,
4014+ validators : {
4015+ onMount : ( ) => 'Name required' ,
4016+ onSubmit : ( ) => onSubmitValidatorSpy ,
4017+ } ,
4018+ onSubmitInvalid : ( ) => onInvalidSpy ( ) ,
4019+ } )
4020+ form . mount ( )
4021+
4022+ const field = new FieldApi ( { form, name : 'name' } )
4023+ field . mount ( )
4024+
4025+ expect ( form . state . canSubmit ) . toBe ( false )
4026+
4027+ await form . handleSubmit ( )
4028+
4029+ expect ( onSubmitValidatorSpy ) . not . toHaveBeenCalled ( )
4030+ expect ( onInvalidSpy ) . toHaveBeenCalledTimes ( 1 )
4031+ } )
4032+
4033+ it ( 'should respect canSubmitWhenInvalid option and run validation even when canSubmit is false' , async ( ) => {
4034+ const onSubmitValidatorSpy = vi
4035+ . fn ( )
4036+ . mockImplementation ( ( ) => 'Submit validation failed' )
4037+ const onInvalidSpy = vi . fn ( )
4038+
4039+ const form = new FormApi ( {
4040+ defaultValues : { name : '' } ,
4041+ canSubmitWhenInvalid : true ,
4042+ validators : {
4043+ onMount : ( ) => 'Name required' ,
4044+ onSubmit : ( ) => onSubmitValidatorSpy ( ) ,
4045+ } ,
4046+ onSubmitInvalid : ( ) => onInvalidSpy ( ) ,
4047+ } )
4048+ form . mount ( )
4049+
4050+ const field = new FieldApi ( { form, name : 'name' } )
4051+ field . mount ( )
4052+
4053+ expect ( form . state . canSubmit ) . toBe ( true )
4054+
4055+ await form . handleSubmit ( )
4056+
4057+ expect ( onSubmitValidatorSpy ) . toHaveBeenCalledTimes ( 1 )
4058+ expect ( onInvalidSpy ) . toHaveBeenCalledTimes ( 1 )
4059+ } )
4060+
39584061it ( 'should generate a formId if not provided' , ( ) => {
39594062 const form = new FormApi ( {
39604063 defaultValues : { age : 0 } ,
0 commit comments