1+ import React from 'react' ;
2+ import { render , screen , fireEvent , waitFor , act } from '@testing-library/react' ;
3+ import ChatInput from '../ChatInput' ;
4+ import * as draftUtils from '@/lib/draftUtils' ;
5+
6+ // Mock the translation context
7+ jest . mock ( '@/contexts/TranslationContext' , ( ) => ( {
8+ useTranslation : ( ) => ( {
9+ t : ( key : string ) => key ,
10+ } ) ,
11+ } ) ) ;
12+
13+ // Mock draft utils
14+ jest . mock ( '@/lib/draftUtils' , ( ) => ( {
15+ saveDraft : jest . fn ( ) ,
16+ getDraft : jest . fn ( ) ,
17+ clearDraft : jest . fn ( ) ,
18+ } ) ) ;
19+
20+ // Mock Stellar Wallet context
21+ jest . mock ( '@/contexts/StellarWalletContext' , ( ) => ( {
22+ useStellarWallet : ( ) => ( {
23+ connection : { isConnected : true } ,
24+ } ) ,
25+ } ) ) ;
26+
27+ describe ( 'ChatInput - Draft Persistence' , ( ) => {
28+ const mockOnSendMessage = jest . fn ( ) ;
29+ const sessionId = 'test-session-123' ;
30+ const defaultProps = {
31+ onSendMessage : mockOnSendMessage ,
32+ isLoading : false ,
33+ placeholder : 'Type a message...' ,
34+ sessionId : sessionId ,
35+ } ;
36+
37+ beforeEach ( ( ) => {
38+ jest . clearAllMocks ( ) ;
39+ jest . useFakeTimers ( ) ;
40+ } ) ;
41+
42+ afterEach ( ( ) => {
43+ jest . useRealTimers ( ) ;
44+ } ) ;
45+
46+ it ( 'should restore draft from draftUtils on mount' , ( ) => {
47+ ( draftUtils . getDraft as jest . Mock ) . mockReturnValue ( 'Restored draft content' ) ;
48+
49+ render ( < ChatInput { ...defaultProps } /> ) ;
50+
51+ const textarea = screen . getByPlaceholderText ( 'Type a message...' ) as HTMLTextAreaElement ;
52+ expect ( textarea . value ) . toBe ( 'Restored draft content' ) ;
53+ expect ( draftUtils . getDraft ) . toHaveBeenCalledWith ( sessionId ) ;
54+ } ) ;
55+
56+ it ( 'should save draft to draft store on keystroke with 500ms debounce' , async ( ) => {
57+ render ( < ChatInput { ...defaultProps } /> ) ;
58+ const textarea = screen . getByPlaceholderText ( 'Type a message...' ) ;
59+
60+ fireEvent . change ( textarea , { target : { value : 'T' } } ) ;
61+ fireEvent . change ( textarea , { target : { value : 'Te' } } ) ;
62+ fireEvent . change ( textarea , { target : { value : 'Test' } } ) ;
63+
64+ // Should not have called saveDraft yet due to debounce
65+ expect ( draftUtils . saveDraft ) . not . toHaveBeenCalled ( ) ;
66+
67+ // Advance time by 500ms
68+ act ( ( ) => {
69+ jest . advanceTimersByTime ( 500 ) ;
70+ } ) ;
71+
72+ expect ( draftUtils . saveDraft ) . toHaveBeenCalledWith ( sessionId , 'Test' ) ;
73+ expect ( draftUtils . saveDraft ) . toHaveBeenCalledTimes ( 1 ) ;
74+ } ) ;
75+
76+ it ( 'should clear draft on successful send' , async ( ) => {
77+ ( draftUtils . getDraft as jest . Mock ) . mockReturnValue ( 'Message to send' ) ;
78+ render ( < ChatInput { ...defaultProps } /> ) ;
79+
80+ const submitButton = screen . getByRole ( 'button' , { name : / s e n d m e s s a g e / i } ) ;
81+
82+ fireEvent . click ( submitButton ) ;
83+
84+ await waitFor ( ( ) => {
85+ expect ( mockOnSendMessage ) . toHaveBeenCalledWith ( 'Message to send' ) ;
86+ expect ( draftUtils . clearDraft ) . toHaveBeenCalledWith ( sessionId ) ;
87+ } ) ;
88+ } ) ;
89+
90+ it ( 'should persist draft across "reloads" (unmount and remount)' , async ( ) => {
91+ const { unmount } = render ( < ChatInput { ...defaultProps } /> ) ;
92+ const textarea = screen . getByPlaceholderText ( 'Type a message...' ) as HTMLTextAreaElement ;
93+
94+ // Type something
95+ fireEvent . change ( textarea , { target : { value : 'Persistent message' } } ) ;
96+
97+ // Advance timers to trigger save
98+ act ( ( ) => {
99+ jest . advanceTimersByTime ( 500 ) ;
100+ } ) ;
101+ expect ( draftUtils . saveDraft ) . toHaveBeenCalledWith ( sessionId , 'Persistent message' ) ;
102+
103+ // Unmount (simulating page exit/reload context)
104+ unmount ( ) ;
105+
106+ // Mock getDraft to return the saved value for the next mount
107+ ( draftUtils . getDraft as jest . Mock ) . mockReturnValue ( 'Persistent message' ) ;
108+
109+ // Remount
110+ render ( < ChatInput { ...defaultProps } /> ) ;
111+ const newTextarea = screen . getByPlaceholderText ( 'Type a message...' ) as HTMLTextAreaElement ;
112+
113+ expect ( newTextarea . value ) . toBe ( 'Persistent message' ) ;
114+ } ) ;
115+ } ) ;
0 commit comments