@@ -2,7 +2,7 @@ import * as path from "path"
22
33import type { MockedFunction } from "vitest"
44
5- import { fileExistsAtPath } from "../../../utils/fs"
5+ import { fileExistsAtPath , createDirectoriesForFile } from "../../../utils/fs"
66import { detectCodeOmission } from "../../../integrations/editor/detect-omission"
77import { isPathOutsideWorkspace } from "../../../utils/pathUtils"
88import { getReadablePath } from "../../../utils/path"
@@ -29,6 +29,7 @@ vi.mock("delay", () => ({
2929
3030vi . mock ( "../../../utils/fs" , ( ) => ( {
3131 fileExistsAtPath : vi . fn ( ) . mockResolvedValue ( false ) ,
32+ createDirectoriesForFile : vi . fn ( ) . mockResolvedValue ( [ ] ) ,
3233} ) )
3334
3435vi . mock ( "../../prompts/responses" , ( ) => ( {
@@ -101,6 +102,7 @@ describe("writeToFileTool", () => {
101102
102103 // Mocked functions with correct types
103104 const mockedFileExistsAtPath = fileExistsAtPath as MockedFunction < typeof fileExistsAtPath >
105+ const mockedCreateDirectoriesForFile = createDirectoriesForFile as MockedFunction < typeof createDirectoriesForFile >
104106 const mockedDetectCodeOmission = detectCodeOmission as MockedFunction < typeof detectCodeOmission >
105107 const mockedIsPathOutsideWorkspace = isPathOutsideWorkspace as MockedFunction < typeof isPathOutsideWorkspace >
106108 const mockedGetReadablePath = getReadablePath as MockedFunction < typeof getReadablePath >
@@ -276,6 +278,48 @@ describe("writeToFileTool", () => {
276278 } )
277279 } )
278280
281+ describe ( "directory creation for new files" , ( ) => {
282+ it . skipIf ( process . platform === "win32" ) (
283+ "creates parent directories early when file does not exist (execute)" ,
284+ async ( ) => {
285+ await executeWriteFileTool ( { } , { fileExists : false } )
286+
287+ expect ( mockedCreateDirectoriesForFile ) . toHaveBeenCalledWith ( absoluteFilePath )
288+ } ,
289+ )
290+
291+ it . skipIf ( process . platform === "win32" ) (
292+ "creates parent directories early when file does not exist (partial)" ,
293+ async ( ) => {
294+ await executeWriteFileTool ( { } , { fileExists : false , isPartial : true } )
295+
296+ expect ( mockedCreateDirectoriesForFile ) . toHaveBeenCalledWith ( absoluteFilePath )
297+ } ,
298+ )
299+
300+ it ( "does not create directories when file exists" , async ( ) => {
301+ await executeWriteFileTool ( { } , { fileExists : true } )
302+
303+ expect ( mockedCreateDirectoriesForFile ) . not . toHaveBeenCalled ( )
304+ } )
305+
306+ it ( "does not create directories when editType is cached as modify" , async ( ) => {
307+ mockCline . diffViewProvider . editType = "modify"
308+
309+ await executeWriteFileTool ( { } )
310+
311+ expect ( mockedCreateDirectoriesForFile ) . not . toHaveBeenCalled ( )
312+ } )
313+
314+ it . skipIf ( process . platform === "win32" ) ( "creates directories when editType is cached as create" , async ( ) => {
315+ mockCline . diffViewProvider . editType = "create"
316+
317+ await executeWriteFileTool ( { } )
318+
319+ expect ( mockedCreateDirectoriesForFile ) . toHaveBeenCalledWith ( absoluteFilePath )
320+ } )
321+ } )
322+
279323 describe ( "content preprocessing" , ( ) => {
280324 it ( "removes markdown code block markers from content" , async ( ) => {
281325 await executeWriteFileTool ( { content : testContentWithMarkdown } )
0 commit comments