11import { render , fireEvent , waitFor , screen } from '@testing-library/preact' ;
22import { describe , it , expect , vi , beforeEach } from 'vitest' ;
3+ import { h } from 'preact' ;
34
45// Mock utils before importing component to avoid side effects
56vi . mock ( '../../utils' , ( ) => ( {
@@ -14,8 +15,8 @@ vi.mock('../../utils', () => ({
1415
1516// Subpath mock for Form used as default import replicating global test-setup structure
1617vi . mock ( 'react-bootstrap/Form' , ( ) => {
17- const { h } = require ( 'preact' ) ;
18- const Form = ( { children , onSubmit } : { children ?: unknown ; onSubmit ?: ( e : Event ) => void } ) => h ( 'form' , { onSubmit } , children ) ;
18+ const Form = ( { children , onSubmit } : { children ?: any ; onSubmit ?: ( e : Event ) => void } ) =>
19+ h ( 'form' , { onSubmit } , children as any ) ;
1920 Form . Group = ( { children, controlId } : { children ?: any ; controlId ?: string } ) => {
2021 if ( children && Array . isArray ( children ) ) {
2122 children = children . map ( ( child ) => {
@@ -24,11 +25,31 @@ vi.mock('react-bootstrap/Form', () => {
2425 return child ;
2526 } ) ;
2627 }
27- return h ( 'div' , { } , children ) ;
28+ return h ( 'div' , { } , children as any ) ;
2829 } ;
29- Form . Label = ( { children, controlId } : { children ?: unknown ; controlId ?: string } ) => h ( 'label' , { htmlFor : controlId } , children ) ;
30- Form . Control = ( { type, value, onChange, isInvalid, controlId } : { type : string ; value ?: string ; onChange ?: ( e : Event ) => void ; isInvalid ?: boolean ; controlId ?: string } ) =>
31- h ( 'input' , { id : controlId , type, value, onChange, 'data-testid' : `${ type } -input` , className : isInvalid ? 'invalid' : '' } ) ;
30+ Form . Label = ( { children, controlId } : { children ?: any ; controlId ?: string } ) =>
31+ h ( 'label' , { htmlFor : controlId } , children as any ) ;
32+ Form . Control = ( {
33+ type,
34+ value,
35+ onChange,
36+ isInvalid,
37+ controlId,
38+ } : {
39+ type : string ;
40+ value ?: string ;
41+ onChange ?: ( e : Event ) => void ;
42+ isInvalid ?: boolean ;
43+ controlId ?: string ;
44+ } ) =>
45+ h ( 'input' , {
46+ id : controlId ,
47+ type,
48+ value,
49+ onChange,
50+ 'data-testid' : `${ type } -input` ,
51+ className : isInvalid ? 'invalid' : '' ,
52+ } as any ) ;
3253 return { default : Form } ;
3354} ) ;
3455
@@ -52,20 +73,22 @@ describe('Login Component', () => {
5273 let mockUtils : any ;
5374 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5475 let mockAlert : any ;
55- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56- let mockBase64 : any ;
5776
5877 beforeEach ( async ( ) => {
5978 vi . clearAllMocks ( ) ;
6079 mockUtils = await import ( '../../utils' ) ;
6180 mockAlert = await import ( '../Alert' ) ;
62- mockBase64 = await import ( 'js-base64' ) ;
81+ await import ( 'js-base64' ) ;
6382
6483 // default happy path mocks
6584 mockUtils . get_salt_for_user . mockResolvedValue ( new Uint8Array ( [ 1 , 2 , 3 ] ) ) ;
6685 mockUtils . generate_hash . mockResolvedValue ( new Uint8Array ( [ 9 , 10 , 11 ] ) ) ;
6786 mockUtils . fetchClient . POST . mockResolvedValue ( { response : { status : 200 } , error : null } ) ;
68- mockUtils . fetchClient . GET . mockResolvedValue ( { data : { secret_salt : [ 5 , 6 , 7 ] } , response : { status : 200 } , error : null } ) ;
87+ mockUtils . fetchClient . GET . mockResolvedValue ( {
88+ data : { secret_salt : [ 5 , 6 , 7 ] } ,
89+ response : { status : 200 } ,
90+ error : null ,
91+ } ) ;
6992 } ) ;
7093
7194 function fillAndSubmit ( email = '[email protected] ' , password = 'ValidPass123!' ) { @@ -99,7 +122,12 @@ describe('Login Component', () => {
99122 fillAndSubmit ( ) ;
100123
101124 await waitFor ( ( ) => {
102- expect ( mockAlert . showAlert ) . toHaveBeenCalledWith ( 'login.verify_before_login' , 'danger' , 'login' , 'login.verify_before_login_heading' ) ;
125+ expect ( mockAlert . showAlert ) . toHaveBeenCalledWith (
126+ 'login.verify_before_login' ,
127+ 'danger' ,
128+ 'login' ,
129+ 'login.verify_before_login_heading'
130+ ) ;
103131 expect ( mockUtils . fetchClient . GET ) . not . toHaveBeenCalled ( ) ;
104132 } ) ;
105133 } ) ;
@@ -114,11 +142,18 @@ describe('Login Component', () => {
114142 } ) ;
115143
116144 it ( 'alerts when secret retrieval fails (non-200)' , async ( ) => {
117- mockUtils . fetchClient . GET . mockResolvedValue ( { data : null , response : { status : 500 } , error : 'boom' } ) ;
145+ mockUtils . fetchClient . GET . mockResolvedValue ( {
146+ data : null ,
147+ response : { status : 500 } ,
148+ error : 'boom' ,
149+ } ) ;
118150 fillAndSubmit ( ) ;
119151
120152 await waitFor ( ( ) => {
121- expect ( mockAlert . showAlert ) . toHaveBeenCalledWith ( 'Failed with status 500: boom' , 'danger' ) ;
153+ expect ( mockAlert . showAlert ) . toHaveBeenCalledWith (
154+ 'Failed with status 500: boom' ,
155+ 'danger'
156+ ) ;
122157 } ) ;
123158 } ) ;
124159
@@ -140,14 +175,22 @@ describe('Login Component', () => {
140175 expect ( screen . getByTestId ( 'modal' ) ) . toBeTruthy ( ) ;
141176
142177 mockUtils . fetchClient . GET . mockResolvedValueOnce ( { response : { status : 200 } } ) ;
143- const emailInput = screen . getAllByRole ( 'textbox' , { name : 'email' } ) . find ( i => ( i as HTMLInputElement ) . id === 'startRecoveryEmail' ) ! ;
178+ const emailInput = screen
179+ . getAllByRole ( 'textbox' , { name : 'email' } )
180+ . find ( ( i ) => ( i as HTMLInputElement ) . id === 'startRecoveryEmail' ) ;
181+ if ( ! emailInput ) throw new Error ( 'Recovery email input not found' ) ;
144182 fireEvent . change ( emailInput , { target :
{ value :
'[email protected] ' } } ) ; 145183
146184 const sendBtn = screen . getByRole ( 'button' , { name : 'send' } ) ;
147185 fireEvent . click ( sendBtn ) ;
148186
149187 await waitFor ( ( ) => {
150- expect ( mockAlert . showAlert ) . toHaveBeenCalledWith ( 'success_alert_text' , 'success' , 'login' , 'success_alert_heading' ) ;
188+ expect ( mockAlert . showAlert ) . toHaveBeenCalledWith (
189+ 'success_alert_text' ,
190+ 'success' ,
191+ 'login' ,
192+ 'success_alert_heading'
193+ ) ;
151194 } ) ;
152195 } ) ;
153196
@@ -156,8 +199,14 @@ describe('Login Component', () => {
156199 fireEvent . click ( screen . getByText ( 'password_recovery' ) ) ;
157200 expect ( screen . getByTestId ( 'modal' ) ) . toBeTruthy ( ) ;
158201
159- mockUtils . fetchClient . GET . mockResolvedValueOnce ( { response : { status : 500 } , error : 'fail' } ) ;
160- const emailInput = screen . getAllByRole ( 'textbox' , { name : 'email' } ) . find ( i => ( i as HTMLInputElement ) . id === 'startRecoveryEmail' ) ! ;
202+ mockUtils . fetchClient . GET . mockResolvedValueOnce ( {
203+ response : { status : 500 } ,
204+ error : 'fail' ,
205+ } ) ;
206+ const emailInput = screen
207+ . getAllByRole ( 'textbox' , { name : 'email' } )
208+ . find ( ( i ) => ( i as HTMLInputElement ) . id === 'startRecoveryEmail' ) ;
209+ if ( ! emailInput ) throw new Error ( 'Recovery email input not found' ) ;
161210 fireEvent . change ( emailInput , { target :
{ value :
'[email protected] ' } } ) ; 162211
163212 const sendBtn = screen . getByRole ( 'button' , { name : 'send' } ) ;
0 commit comments