@@ -62,7 +62,7 @@ describe('CodeEditorModal', () => {
6262 expect ( screen . getByText ( / E d g e I D : e d g e - 1 2 3 / i) ) . toBeInTheDocument ( ) ;
6363 } ) ;
6464
65- it ( 'calls onSave when Save is clicked and shows success feedback ' , async ( ) => {
65+ it ( 'calls onSave when Save is clicked and modal stays open ' , async ( ) => {
6666 const onSave = jest . fn ( ) . mockResolvedValue ( undefined ) ;
6767 const onClose = jest . fn ( ) ;
6868
@@ -74,7 +74,7 @@ describe('CodeEditorModal', () => {
7474 /> ,
7575 ) ;
7676
77- fireEvent . click ( screen . getByRole ( 'button' , { name : / S a v e / i } ) ) ;
77+ fireEvent . click ( screen . getByRole ( 'button' , { name : / 💾 S a v e / i } ) ) ;
7878
7979 await waitFor ( ( ) => {
8080 expect ( onSave ) . toHaveBeenCalledWith ( 'initial code' ) ;
@@ -93,9 +93,182 @@ describe('CodeEditorModal', () => {
9393 /> ,
9494 ) ;
9595
96- fireEvent . click ( screen . getByRole ( 'button' , { name : / S a v e / i } ) ) ;
96+ fireEvent . click ( screen . getByRole ( 'button' , { name : / 💾 S a v e / i } ) ) ;
9797
9898 expect ( await screen . findByTestId ( 'confirm-Unable to save file' ) ) . toBeInTheDocument ( ) ;
9999 } ) ;
100- } ) ;
101100
101+ it ( 'does not render when isOpen is false' , ( ) => {
102+ render ( < CodeEditorModal { ...defaultProps } isOpen = { false } /> ) ;
103+ expect ( screen . queryByText ( / R e a c t i o n E d i t o r / i) ) . not . toBeInTheDocument ( ) ;
104+ } ) ;
105+
106+ it ( 'updates code when editor content changes' , ( ) => {
107+ render ( < CodeEditorModal { ...defaultProps } /> ) ;
108+
109+ const editor = screen . getByTestId ( 'monaco-editor' ) ;
110+ fireEvent . change ( editor , { target : { value : 'new code' } } ) ;
111+
112+ expect ( editor ) . toHaveValue ( 'new code' ) ;
113+ } ) ;
114+
115+ it ( 'shows unsaved changes indicator after editing' , async ( ) => {
116+ render ( < CodeEditorModal { ...defaultProps } /> ) ;
117+
118+ const editor = screen . getByTestId ( 'monaco-editor' ) ;
119+ fireEvent . change ( editor , { target : { value : 'modified code' } } ) ;
120+
121+ expect ( screen . getByText ( / U n s a v e d c h a n g e s / i) ) . toBeInTheDocument ( ) ;
122+ } ) ;
123+
124+ it ( 'hides unsaved changes indicator when code matches initial code' , ( ) => {
125+ render ( < CodeEditorModal { ...defaultProps } /> ) ;
126+
127+ // Initially no unsaved changes
128+ expect ( screen . queryByText ( / U n s a v e d c h a n g e s / i) ) . not . toBeInTheDocument ( ) ;
129+ } ) ;
130+
131+ it ( 'shows unsaved changes dialog when closing with unsaved changes' , async ( ) => {
132+ const onClose = jest . fn ( ) ;
133+ render (
134+ < CodeEditorModal
135+ { ...defaultProps }
136+ onClose = { onClose }
137+ /> ,
138+ ) ;
139+
140+ // Modify the code
141+ fireEvent . change ( screen . getByTestId ( 'monaco-editor' ) , {
142+ target : { value : 'modified code' } ,
143+ } ) ;
144+
145+ // Click the X close button
146+ fireEvent . click ( screen . getByTitle ( 'Close (Esc)' ) ) ;
147+
148+ // Should show confirmation dialog, not close immediately
149+ expect ( onClose ) . not . toHaveBeenCalled ( ) ;
150+ expect ( screen . getByTestId ( 'confirm-Unsaved Changes' ) ) . toBeInTheDocument ( ) ;
151+ } ) ;
152+
153+ it ( 'closes without saving when confirming unsaved changes dialog' , async ( ) => {
154+ const onClose = jest . fn ( ) ;
155+ render (
156+ < CodeEditorModal
157+ { ...defaultProps }
158+ onClose = { onClose }
159+ /> ,
160+ ) ;
161+
162+ fireEvent . change ( screen . getByTestId ( 'monaco-editor' ) , {
163+ target : { value : 'modified code' } ,
164+ } ) ;
165+
166+ fireEvent . click ( screen . getByTitle ( 'Close (Esc)' ) ) ;
167+
168+ // Click "Discard" / confirm button in dialog
169+ const dialog = screen . getByTestId ( 'confirm-Unsaved Changes' ) ;
170+ fireEvent . click ( dialog . querySelector ( 'button' ) ! ) ;
171+
172+ expect ( onClose ) . toHaveBeenCalled ( ) ;
173+ } ) ;
174+
175+ it ( 'closes without confirmation when no unsaved changes' , ( ) => {
176+ const onClose = jest . fn ( ) ;
177+ render (
178+ < CodeEditorModal
179+ { ...defaultProps }
180+ onClose = { onClose }
181+ /> ,
182+ ) ;
183+
184+ // Click close without making changes
185+ fireEvent . click ( screen . getByTitle ( 'Close (Esc)' ) ) ;
186+
187+ // Should close immediately without confirmation
188+ expect ( onClose ) . toHaveBeenCalled ( ) ;
189+ expect ( screen . queryByTestId ( 'confirm-Unsaved Changes' ) ) . not . toBeInTheDocument ( ) ;
190+ } ) ;
191+
192+ it ( 'hides unsaved changes indicator after successful save' , async ( ) => {
193+ const onSave = jest . fn ( ) . mockResolvedValue ( undefined ) ;
194+ render (
195+ < CodeEditorModal
196+ { ...defaultProps }
197+ onSave = { onSave }
198+ /> ,
199+ ) ;
200+
201+ // Make a change
202+ fireEvent . change ( screen . getByTestId ( 'monaco-editor' ) , {
203+ target : { value : 'modified code' } ,
204+ } ) ;
205+
206+ expect ( screen . getByText ( / U n s a v e d c h a n g e s / i) ) . toBeInTheDocument ( ) ;
207+
208+ // Save
209+ fireEvent . click ( screen . getByRole ( 'button' , { name : / 💾 S a v e / i } ) ) ;
210+
211+ await waitFor ( ( ) => {
212+ expect ( screen . queryByText ( / U n s a v e d c h a n g e s / i) ) . not . toBeInTheDocument ( ) ;
213+ } ) ;
214+ } ) ;
215+
216+ it ( 'calls onDelete and onClose when Delete is clicked and confirmed' , async ( ) => {
217+ const onDelete = jest . fn ( ) ;
218+ const onClose = jest . fn ( ) ;
219+
220+ render (
221+ < CodeEditorModal
222+ { ...defaultProps }
223+ onDelete = { onDelete }
224+ onClose = { onClose }
225+ /> ,
226+ ) ;
227+
228+ fireEvent . click ( screen . getByTitle ( 'Delete relation' ) ) ;
229+
230+ // Confirm the deletion dialog
231+ const dialog = await screen . findByTestId ( 'confirm-Delete Relation' ) ;
232+ fireEvent . click ( dialog . querySelector ( 'button' ) ! ) ;
233+
234+ expect ( onDelete ) . toHaveBeenCalled ( ) ;
235+ } ) ;
236+
237+ it ( 'does not call onDelete when deletion is cancelled' , async ( ) => {
238+ const onDelete = jest . fn ( ) ;
239+
240+ render (
241+ < CodeEditorModal
242+ { ...defaultProps }
243+ onDelete = { onDelete }
244+ /> ,
245+ ) ;
246+
247+ fireEvent . click ( screen . getByTitle ( 'Delete relation' ) ) ;
248+
249+ const dialog = await screen . findByTestId ( 'confirm-Delete Relation' ) ;
250+ const buttons = dialog . querySelectorAll ( 'button' ) ;
251+ // Click cancel (second button)
252+ fireEvent . click ( buttons [ 1 ] ) ;
253+
254+ expect ( onDelete ) . not . toHaveBeenCalled ( ) ;
255+ } ) ;
256+
257+ it ( 'resets code and unsaved state when initialCode prop changes' , async ( ) => {
258+ const { rerender } = render ( < CodeEditorModal { ...defaultProps } /> ) ;
259+
260+ // Modify code
261+ fireEvent . change ( screen . getByTestId ( 'monaco-editor' ) , {
262+ target : { value : 'modified code' } ,
263+ } ) ;
264+ expect ( screen . getByText ( / U n s a v e d c h a n g e s / i) ) . toBeInTheDocument ( ) ;
265+
266+ // Rerender with new initialCode (simulates reopening editor)
267+ rerender ( < CodeEditorModal { ...defaultProps } initialCode = "new initial code" /> ) ;
268+
269+ await waitFor ( ( ) => {
270+ expect ( screen . getByTestId ( 'monaco-editor' ) ) . toHaveValue ( 'new initial code' ) ;
271+ expect ( screen . queryByText ( / U n s a v e d c h a n g e s / i) ) . not . toBeInTheDocument ( ) ;
272+ } ) ;
273+ } ) ;
274+ } ) ;
0 commit comments