11/**
22 * @vitest -environment jsdom
33 */
4- import { act , type ComponentProps , Suspense } from 'react'
4+ import { act , type ComponentProps , createRef , Suspense } from 'react'
55import { createRoot , type Root } from 'react-dom/client'
66import { beforeEach , describe , expect , it , vi } from 'vitest'
7+ import type { FileDownloadSource } from '@/lib/uploads/client/download'
78import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace'
89import { SIM_PAGE_CONTENT_TYPE } from '@/lib/workspace-files/page-compile'
910import { TextEditor } from '@/app/workspace/[workspaceId]/files/components/file-viewer/text-editor'
@@ -18,6 +19,8 @@ interface MockMonacoProps {
1819const state = vi . hoisted ( ( ) => ( {
1920 content : 'initial' ,
2021 streaming : false ,
22+ loading : false ,
23+ error : false ,
2124 editorProps : null as MockMonacoProps | null ,
2225} ) )
2326
@@ -37,8 +40,8 @@ vi.mock(
3740 state . content = content
3841 } ,
3942 isStreamInteractionLocked : state . streaming ,
40- isContentLoading : false ,
41- hasContentError : false ,
43+ isContentLoading : state . loading ,
44+ hasContentError : state . error ,
4245 saveImmediately : vi . fn ( ) ,
4346 } ) ,
4447 } )
@@ -161,10 +164,49 @@ describe('TextEditor content synchronization', () => {
161164 ; ( globalThis as { IS_REACT_ACT_ENVIRONMENT ?: boolean } ) . IS_REACT_ACT_ENVIRONMENT = true
162165 state . content = 'initial'
163166 state . streaming = false
167+ state . loading = false
168+ state . error = false
164169 state . editorProps = null
165170 useFileViewerStore . getState ( ) . reset ( )
166171 } )
167172
173+ it ( 'exports the current source draft synchronously without saving and clears it on unmount' , ( ) => {
174+ const downloadSourceRef = createRef < FileDownloadSource | null > ( )
175+ const root = createRoot ( document . createElement ( 'div' ) )
176+ act ( ( ) => root . render ( < TextEditor { ...props } downloadSourceRef = { downloadSourceRef } /> ) )
177+ expect ( downloadSourceRef . current ) . toMatchObject ( {
178+ fileId : file . id ,
179+ workspaceId : file . workspaceId ,
180+ } )
181+ expect ( downloadSourceRef . current ?. getContent ( ) ) . toBe ( 'initial' )
182+ const draft = '# Source \n\n| a | b |\n| - | - |\n'
183+ act ( ( ) => state . editorProps ?. onChange ?.( draft ) )
184+ expect ( downloadSourceRef . current ?. getContent ( ) ) . toBe ( draft )
185+ act ( ( ) => state . editorProps ?. onChange ?.( '' ) )
186+ expect ( downloadSourceRef . current ?. getContent ( ) ) . toBe ( '' )
187+ act ( ( ) => root . unmount ( ) )
188+ expect ( downloadSourceRef . current ) . toBeNull ( )
189+ } )
190+
191+ it . each ( [ 'loading' , 'error' ] as const ) (
192+ 'does not expose unavailable source content: %s' ,
193+ ( condition ) => {
194+ state [ condition ] = true
195+ const downloadSourceRef = createRef < FileDownloadSource | null > ( )
196+ const root = createRoot ( document . createElement ( 'div' ) )
197+ act ( ( ) => root . render ( < TextEditor { ...props } downloadSourceRef = { downloadSourceRef } /> ) )
198+ expect ( downloadSourceRef . current ) . toBeNull ( )
199+ state [ condition ] = false
200+ act ( ( ) =>
201+ root . render (
202+ < TextEditor { ...props } file = { { ...file } } downloadSourceRef = { downloadSourceRef } />
203+ )
204+ )
205+ expect ( downloadSourceRef . current ?. getContent ( ) ) . toBe ( 'initial' )
206+ act ( ( ) => root . unmount ( ) )
207+ }
208+ )
209+
168210 it ( 'shares page recognition with already mounted viewers and keeps it across remounts' , ( ) => {
169211 const pageFile = { ...file , id : 'page-a' , name : 'page.html' , type : 'text/html' }
170212 const container = document . createElement ( 'div' )
0 commit comments