@@ -1886,6 +1886,7 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
18861886
18871887 const mockFS = mock < typeof workspace . fs > ( ) ;
18881888 when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
1889+ when ( mockFS . stat ( anything ( ) ) ) . thenReject ( new Error ( 'File not found' ) ) ;
18891890 when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
18901891
18911892 when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
@@ -1941,6 +1942,7 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
19411942
19421943 const mockFS = mock < typeof workspace . fs > ( ) ;
19431944 when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
1945+ when ( mockFS . stat ( anything ( ) ) ) . thenReject ( new Error ( 'File not found' ) ) ;
19441946 when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
19451947
19461948 when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
@@ -1994,6 +1996,7 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
19941996
19951997 const mockFS = mock < typeof workspace . fs > ( ) ;
19961998 when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
1999+ when ( mockFS . stat ( anything ( ) ) ) . thenReject ( new Error ( 'File not found' ) ) ;
19972000 when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
19982001
19992002 when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
@@ -2043,6 +2046,7 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
20432046
20442047 const mockFS = mock < typeof workspace . fs > ( ) ;
20452048 when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
2049+ when ( mockFS . stat ( anything ( ) ) ) . thenReject ( new Error ( 'File not found' ) ) ;
20462050 when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
20472051
20482052 when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
@@ -2069,6 +2073,108 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
20692073 // Verify error message was shown
20702074 verify ( mockedVSCodeNamespaces . window . showErrorMessage ( anything ( ) ) ) . once ( ) ;
20712075 } ) ;
2076+
2077+ test ( 'should prompt for overwrite when files already exist and cancel if declined' , async ( ) => {
2078+ resetVSCodeMocks ( ) ;
2079+
2080+ const projectData = {
2081+ version : '1.0.0' ,
2082+ metadata : { createdAt : '2024-01-01T00:00:00.000Z' } ,
2083+ project : {
2084+ id : 'project-id' ,
2085+ name : 'Test Project' ,
2086+ notebooks : [
2087+ { id : 'nb-1' , name : 'Notebook 1' , blocks : [ ] , executionMode : 'block' } ,
2088+ { id : 'nb-2' , name : 'Notebook 2' , blocks : [ ] , executionMode : 'block' }
2089+ ]
2090+ }
2091+ } ;
2092+
2093+ const mockFS = mock < typeof workspace . fs > ( ) ;
2094+ when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
2095+ // Files exist - stat returns successfully
2096+ when ( mockFS . stat ( anything ( ) ) ) . thenReturn ( Promise . resolve ( { } as any ) ) ;
2097+ when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
2098+
2099+ when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
2100+ Promise . resolve ( { label : 'Jupyter Notebook (.ipynb)' , value : 'jupyter' } ) as any
2101+ ) ;
2102+ when ( mockedVSCodeNamespaces . window . showOpenDialog ( anything ( ) ) ) . thenReturn (
2103+ Promise . resolve ( [ Uri . file ( '/output/folder' ) ] )
2104+ ) ;
2105+ // User cancels overwrite
2106+ when ( mockedVSCodeNamespaces . window . showWarningMessage ( anything ( ) , anything ( ) , anything ( ) ) ) . thenReturn (
2107+ Promise . resolve ( undefined )
2108+ ) ;
2109+
2110+ const treeItem : Partial < DeepnoteTreeItem > = {
2111+ type : DeepnoteTreeItemType . ProjectFile ,
2112+ context : {
2113+ filePath : '/test/project.deepnote' ,
2114+ projectId : 'project-id'
2115+ }
2116+ } ;
2117+
2118+ await ( explorerView as any ) . exportProject ( treeItem ) ;
2119+
2120+ // Verify warning message was shown about files existing
2121+ verify ( mockedVSCodeNamespaces . window . showWarningMessage ( anything ( ) , anything ( ) , anything ( ) ) ) . once ( ) ;
2122+ // Verify no files were written
2123+ verify ( mockFS . writeFile ( anything ( ) , anything ( ) ) ) . never ( ) ;
2124+ } ) ;
2125+
2126+ test ( 'should overwrite files when user confirms' , async ( ) => {
2127+ resetVSCodeMocks ( ) ;
2128+
2129+ const projectData = {
2130+ version : '1.0.0' ,
2131+ metadata : { createdAt : '2024-01-01T00:00:00.000Z' } ,
2132+ project : {
2133+ id : 'project-id' ,
2134+ name : 'Test Project' ,
2135+ notebooks : [ { id : 'nb-1' , name : 'Notebook 1' , blocks : [ ] , executionMode : 'block' } ]
2136+ }
2137+ } ;
2138+
2139+ const mockFS = mock < typeof workspace . fs > ( ) ;
2140+ when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
2141+ // File exists - stat returns successfully
2142+ when ( mockFS . stat ( anything ( ) ) ) . thenReturn ( Promise . resolve ( { } as any ) ) ;
2143+ when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
2144+
2145+ when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
2146+ Promise . resolve ( { label : 'Jupyter Notebook (.ipynb)' , value : 'jupyter' } ) as any
2147+ ) ;
2148+ when ( mockedVSCodeNamespaces . window . showOpenDialog ( anything ( ) ) ) . thenReturn (
2149+ Promise . resolve ( [ Uri . file ( '/output/folder' ) ] )
2150+ ) ;
2151+ // User confirms overwrite
2152+ when ( mockedVSCodeNamespaces . window . showWarningMessage ( anything ( ) , anything ( ) , anything ( ) ) ) . thenReturn (
2153+ Promise . resolve ( 'Overwrite' ) as any
2154+ ) ;
2155+ when ( mockedVSCodeNamespaces . window . showInformationMessage ( anything ( ) ) ) . thenReturn (
2156+ Promise . resolve ( undefined )
2157+ ) ;
2158+
2159+ let writeCount = 0 ;
2160+ when ( mockFS . writeFile ( anything ( ) , anything ( ) ) ) . thenCall ( ( ) => {
2161+ writeCount ++ ;
2162+ return Promise . resolve ( ) ;
2163+ } ) ;
2164+
2165+ const treeItem : Partial < DeepnoteTreeItem > = {
2166+ type : DeepnoteTreeItemType . ProjectFile ,
2167+ context : {
2168+ filePath : '/test/project.deepnote' ,
2169+ projectId : 'project-id'
2170+ }
2171+ } ;
2172+
2173+ await ( explorerView as any ) . exportProject ( treeItem ) ;
2174+
2175+ // Verify file was written after user confirmed overwrite
2176+ assert . strictEqual ( writeCount , 1 ) ;
2177+ } ) ;
20722178 } ) ;
20732179
20742180 suite ( 'exportNotebook' , ( ) => {
@@ -2189,6 +2295,7 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
21892295
21902296 const mockFS = mock < typeof workspace . fs > ( ) ;
21912297 when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
2298+ when ( mockFS . stat ( anything ( ) ) ) . thenReject ( new Error ( 'File not found' ) ) ;
21922299 when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
21932300
21942301 when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
@@ -2287,6 +2394,7 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
22872394
22882395 const mockFS = mock < typeof workspace . fs > ( ) ;
22892396 when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
2397+ when ( mockFS . stat ( anything ( ) ) ) . thenReject ( new Error ( 'File not found' ) ) ;
22902398 when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
22912399
22922400 when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
@@ -2314,5 +2422,106 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
23142422 // Verify error message was shown
23152423 verify ( mockedVSCodeNamespaces . window . showErrorMessage ( anything ( ) ) ) . once ( ) ;
23162424 } ) ;
2425+
2426+ test ( 'should prompt for overwrite when file already exists and cancel if declined' , async ( ) => {
2427+ resetVSCodeMocks ( ) ;
2428+
2429+ const projectData = {
2430+ version : '1.0.0' ,
2431+ metadata : { createdAt : '2024-01-01T00:00:00.000Z' } ,
2432+ project : {
2433+ id : 'project-id' ,
2434+ name : 'Test Project' ,
2435+ notebooks : [ { id : 'nb-1' , name : 'Notebook 1' , blocks : [ ] , executionMode : 'block' } ]
2436+ }
2437+ } ;
2438+
2439+ const mockFS = mock < typeof workspace . fs > ( ) ;
2440+ when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
2441+ // File exists - stat returns successfully
2442+ when ( mockFS . stat ( anything ( ) ) ) . thenReturn ( Promise . resolve ( { } as any ) ) ;
2443+ when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
2444+
2445+ when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
2446+ Promise . resolve ( { label : 'Jupyter Notebook (.ipynb)' , value : 'jupyter' } ) as any
2447+ ) ;
2448+ when ( mockedVSCodeNamespaces . window . showOpenDialog ( anything ( ) ) ) . thenReturn (
2449+ Promise . resolve ( [ Uri . file ( '/output/folder' ) ] )
2450+ ) ;
2451+ // User cancels overwrite
2452+ when ( mockedVSCodeNamespaces . window . showWarningMessage ( anything ( ) , anything ( ) , anything ( ) ) ) . thenReturn (
2453+ Promise . resolve ( undefined )
2454+ ) ;
2455+
2456+ const treeItem : Partial < DeepnoteTreeItem > = {
2457+ type : DeepnoteTreeItemType . Notebook ,
2458+ context : {
2459+ filePath : '/test/project.deepnote' ,
2460+ projectId : 'project-id' ,
2461+ notebookId : 'nb-1'
2462+ }
2463+ } ;
2464+
2465+ await ( explorerView as any ) . exportNotebook ( treeItem ) ;
2466+
2467+ // Verify warning message was shown about file existing
2468+ verify ( mockedVSCodeNamespaces . window . showWarningMessage ( anything ( ) , anything ( ) , anything ( ) ) ) . once ( ) ;
2469+ // Verify no file was written
2470+ verify ( mockFS . writeFile ( anything ( ) , anything ( ) ) ) . never ( ) ;
2471+ } ) ;
2472+
2473+ test ( 'should overwrite file when user confirms' , async ( ) => {
2474+ resetVSCodeMocks ( ) ;
2475+
2476+ const projectData = {
2477+ version : '1.0.0' ,
2478+ metadata : { createdAt : '2024-01-01T00:00:00.000Z' } ,
2479+ project : {
2480+ id : 'project-id' ,
2481+ name : 'Test Project' ,
2482+ notebooks : [ { id : 'nb-1' , name : 'Notebook 1' , blocks : [ ] , executionMode : 'block' } ]
2483+ }
2484+ } ;
2485+
2486+ const mockFS = mock < typeof workspace . fs > ( ) ;
2487+ when ( mockFS . readFile ( anything ( ) ) ) . thenReturn ( Promise . resolve ( Buffer . from ( yaml . dump ( projectData ) ) ) ) ;
2488+ // File exists - stat returns successfully
2489+ when ( mockFS . stat ( anything ( ) ) ) . thenReturn ( Promise . resolve ( { } as any ) ) ;
2490+ when ( mockedVSCodeNamespaces . workspace . fs ) . thenReturn ( instance ( mockFS ) ) ;
2491+
2492+ when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenReturn (
2493+ Promise . resolve ( { label : 'Jupyter Notebook (.ipynb)' , value : 'jupyter' } ) as any
2494+ ) ;
2495+ when ( mockedVSCodeNamespaces . window . showOpenDialog ( anything ( ) ) ) . thenReturn (
2496+ Promise . resolve ( [ Uri . file ( '/output/folder' ) ] )
2497+ ) ;
2498+ // User confirms overwrite
2499+ when ( mockedVSCodeNamespaces . window . showWarningMessage ( anything ( ) , anything ( ) , anything ( ) ) ) . thenReturn (
2500+ Promise . resolve ( 'Overwrite' ) as any
2501+ ) ;
2502+ when ( mockedVSCodeNamespaces . window . showInformationMessage ( anything ( ) ) ) . thenReturn (
2503+ Promise . resolve ( undefined )
2504+ ) ;
2505+
2506+ let writeCount = 0 ;
2507+ when ( mockFS . writeFile ( anything ( ) , anything ( ) ) ) . thenCall ( ( ) => {
2508+ writeCount ++ ;
2509+ return Promise . resolve ( ) ;
2510+ } ) ;
2511+
2512+ const treeItem : Partial < DeepnoteTreeItem > = {
2513+ type : DeepnoteTreeItemType . Notebook ,
2514+ context : {
2515+ filePath : '/test/project.deepnote' ,
2516+ projectId : 'project-id' ,
2517+ notebookId : 'nb-1'
2518+ }
2519+ } ;
2520+
2521+ await ( explorerView as any ) . exportNotebook ( treeItem ) ;
2522+
2523+ // Verify file was written after user confirmed overwrite
2524+ assert . strictEqual ( writeCount , 1 ) ;
2525+ } ) ;
23172526 } ) ;
23182527} ) ;
0 commit comments