@@ -87,6 +87,24 @@ import * as git from "../../../utils/git"
8787import { getWorkspacePath } from "../../../utils/path"
8888; ( getWorkspacePath as jest . Mock ) . mockReturnValue ( "/test/workspace" )
8989
90+ jest . mock ( "fs/promises" , ( ) => ( {
91+ stat : jest . fn ( ) ,
92+ readdir : jest . fn ( ) ,
93+ } ) )
94+ import fs from "fs/promises"
95+ import * as path from "path"
96+
97+ jest . mock ( "../../../integrations/misc/open-file" , ( ) => ( {
98+ openFile : jest . fn ( ) ,
99+ } ) )
100+ import { openFile } from "../../../integrations/misc/open-file"
101+
102+ jest . mock ( "../../../integrations/misc/extract-text" , ( ) => ( {
103+ extractTextFromFile : jest . fn ( ) ,
104+ } ) )
105+
106+ import * as vscode from "vscode"
107+
90108describe ( "mentions" , ( ) => {
91109 const mockCwd = "/test/workspace"
92110 let mockUrlContentFetcher : UrlContentFetcher
@@ -112,6 +130,16 @@ describe("mentions", () => {
112130 } )
113131
114132 describe ( "parseMentions" , ( ) => {
133+ let mockUrlFetcher : UrlContentFetcher
134+
135+ beforeEach ( ( ) => {
136+ mockUrlFetcher = new ( UrlContentFetcher as jest . Mock < UrlContentFetcher > ) ( )
137+ ; ( fs . stat as jest . Mock ) . mockResolvedValue ( { isFile : ( ) => true , isDirectory : ( ) => false } )
138+ ; ( require ( "../../../integrations/misc/extract-text" ) . extractTextFromFile as jest . Mock ) . mockResolvedValue (
139+ "Mock file content" ,
140+ )
141+ } )
142+
115143 it ( "should parse git commit mentions" , async ( ) => {
116144 const commitHash = "abc1234"
117145 const commitInfo = `abc1234 Fix bug in parser
@@ -144,35 +172,72 @@ Detailed commit message with multiple lines
144172 expect ( result ) . toContain ( `<git_commit hash="${ commitHash } ">` )
145173 expect ( result ) . toContain ( `Error fetching commit info: ${ errorMessage } ` )
146174 } )
147- } )
148175
149- describe ( "openMention" , ( ) => {
150- it ( "should handle file paths and problems" , async ( ) => {
151- // Mock stat to simulate file not existing
152- mockVscode . workspace . fs . stat . mockRejectedValueOnce ( new Error ( "File does not exist" ) )
176+ it ( "should correctly parse mentions with escaped spaces and fetch content" , async ( ) => {
177+ const text = "Please check the file @/path/to/file\\ with\\ spaces.txt"
178+ const expectedUnescaped = "path/to/file with spaces.txt" // Note: leading '/' removed by slice(1) in parseMentions
179+ const expectedAbsPath = path . resolve ( mockCwd , expectedUnescaped )
153180
154- // Call openMention and wait for it to complete
155- await openMention ( "/path/to/file" )
181+ const result = await parseMentions ( text , mockCwd , mockUrlFetcher )
156182
157- // Verify error handling
158- expect ( mockExecuteCommand ) . not . toHaveBeenCalled ( )
159- expect ( mockOpenExternal ) . not . toHaveBeenCalled ( )
160- expect ( mockVscode . window . showErrorMessage ) . toHaveBeenCalledWith ( "Could not open file: File does not exist" )
183+ // Check if fs.stat was called with the unescaped path
184+ expect ( fs . stat ) . toHaveBeenCalledWith ( expectedAbsPath )
185+ // Check if extractTextFromFile was called with the unescaped path
186+ expect ( require ( "../../../integrations/misc/extract-text" ) . extractTextFromFile ) . toHaveBeenCalledWith (
187+ expectedAbsPath ,
188+ )
161189
162- // Reset mocks for next test
163- jest . clearAllMocks ( )
190+ // Check the output format
191+ expect ( result ) . toContain ( `'path/to/file\\ with\\ spaces.txt' (see below for file content)` )
192+ expect ( result ) . toContain (
193+ `<file_content path="path/to/file\\ with\\ spaces.txt">\nMock file content\n</file_content>` ,
194+ )
195+ } )
164196
165- // Test problems command
166- await openMention ( "problems" )
167- expect ( mockExecuteCommand ) . toHaveBeenCalledWith ( "workbench.actions.view.problems" )
197+ it ( "should handle folder mentions with escaped spaces" , async ( ) => {
198+ const text = "Look in @/my\\ documents/folder\\ name/"
199+ const expectedUnescaped = "my documents/folder name/"
200+ const expectedAbsPath = path . resolve ( mockCwd , expectedUnescaped )
201+ ; ( fs . stat as jest . Mock ) . mockResolvedValue ( { isFile : ( ) => false , isDirectory : ( ) => true } )
202+ ; ( fs . readdir as jest . Mock ) . mockResolvedValue ( [ ] ) // Empty directory
203+
204+ const result = await parseMentions ( text , mockCwd , mockUrlFetcher )
205+
206+ expect ( fs . stat ) . toHaveBeenCalledWith ( expectedAbsPath )
207+ expect ( fs . readdir ) . toHaveBeenCalledWith ( expectedAbsPath , { withFileTypes : true } )
208+ expect ( result ) . toContain ( `'my\\ documents/folder\\ name/' (see below for folder content)` )
209+ expect ( result ) . toContain ( `<folder_content path="my\\ documents/folder\\ name/">` ) // Content check might be more complex
210+ } )
211+
212+ it ( "should handle errors when accessing paths with escaped spaces" , async ( ) => {
213+ const text = "Check @/nonexistent\\ file.txt"
214+ const expectedUnescaped = "nonexistent file.txt"
215+ const expectedAbsPath = path . resolve ( mockCwd , expectedUnescaped )
216+ const mockError = new Error ( "ENOENT: no such file or directory" )
217+ ; ( fs . stat as jest . Mock ) . mockRejectedValue ( mockError )
218+
219+ const result = await parseMentions ( text , mockCwd , mockUrlFetcher )
220+
221+ expect ( fs . stat ) . toHaveBeenCalledWith ( expectedAbsPath )
222+ expect ( result ) . toContain (
223+ `<file_content path="nonexistent\\ file.txt">\nError fetching content: Failed to access path "nonexistent\\ file.txt": ${ mockError . message } \n</file_content>` ,
224+ )
225+ } )
226+
227+ // Add more tests for parseMentions if needed (URLs, other mentions combined with escaped paths etc.)
228+ } )
229+
230+ describe ( "openMention" , ( ) => {
231+ beforeEach ( ( ) => {
232+ ; ( getWorkspacePath as jest . Mock ) . mockReturnValue ( mockCwd )
168233 } )
169234
170235 it ( "should handle URLs" , async ( ) => {
171236 const url = "https://example.com"
172237 await openMention ( url )
173- const mockUri = mockVscode . Uri . parse ( url )
174- expect ( mockVscode . env . openExternal ) . toHaveBeenCalled ( )
175- const calledArg = mockVscode . env . openExternal . mock . calls [ 0 ] [ 0 ]
238+ const mockUri = vscode . Uri . parse ( url )
239+ expect ( vscode . env . openExternal ) . toHaveBeenCalled ( )
240+ const calledArg = ( vscode . env . openExternal as jest . Mock ) . mock . calls [ 0 ] [ 0 ]
176241 expect ( calledArg ) . toEqual (
177242 expect . objectContaining ( {
178243 scheme : mockUri . scheme ,
@@ -183,5 +248,62 @@ Detailed commit message with multiple lines
183248 } ) ,
184249 )
185250 } )
251+
252+ it ( "should unescape file path before opening" , async ( ) => {
253+ const mention = "/file\\ with\\ spaces.txt"
254+ const expectedUnescaped = "file with spaces.txt"
255+ const expectedAbsPath = path . resolve ( mockCwd , expectedUnescaped )
256+
257+ await openMention ( mention )
258+
259+ expect ( openFile ) . toHaveBeenCalledWith ( expectedAbsPath )
260+ expect ( vscode . commands . executeCommand ) . not . toHaveBeenCalled ( )
261+ } )
262+
263+ it ( "should unescape folder path before revealing" , async ( ) => {
264+ const mention = "/folder\\ with\\ spaces/"
265+ const expectedUnescaped = "folder with spaces/"
266+ const expectedAbsPath = path . resolve ( mockCwd , expectedUnescaped )
267+ const expectedUri = { fsPath : expectedAbsPath } // From mock
268+ ; ( vscode . Uri . file as jest . Mock ) . mockReturnValue ( expectedUri )
269+
270+ await openMention ( mention )
271+
272+ expect ( vscode . commands . executeCommand ) . toHaveBeenCalledWith ( "revealInExplorer" , expectedUri )
273+ expect ( vscode . Uri . file ) . toHaveBeenCalledWith ( expectedAbsPath )
274+ expect ( openFile ) . not . toHaveBeenCalled ( )
275+ } )
276+
277+ it ( "should handle mentions without paths correctly" , async ( ) => {
278+ await openMention ( "problems" )
279+ expect ( vscode . commands . executeCommand ) . toHaveBeenCalledWith ( "workbench.actions.view.problems" )
280+
281+ await openMention ( "terminal" )
282+ expect ( vscode . commands . executeCommand ) . toHaveBeenCalledWith ( "workbench.action.terminal.focus" )
283+
284+ await openMention ( "http://example.com" )
285+ expect ( vscode . env . openExternal ) . toHaveBeenCalled ( ) // Check if called, specific URI mock might be needed for detailed check
286+
287+ await openMention ( "git-changes" ) // Assuming no specific action for this yet
288+ // Add expectations if an action is defined for git-changes
289+
290+ await openMention ( "a1b2c3d" ) // Assuming no specific action for commit hashes yet
291+ // Add expectations if an action is defined for commit hashes
292+ } )
293+
294+ it ( "should do nothing if mention is undefined or empty" , async ( ) => {
295+ await openMention ( undefined )
296+ await openMention ( "" )
297+ expect ( openFile ) . not . toHaveBeenCalled ( )
298+ expect ( vscode . commands . executeCommand ) . not . toHaveBeenCalled ( )
299+ expect ( vscode . env . openExternal ) . not . toHaveBeenCalled ( )
300+ } )
301+
302+ it ( "should do nothing if cwd is not available" , async ( ) => {
303+ ; ( getWorkspacePath as jest . Mock ) . mockReturnValue ( undefined )
304+ await openMention ( "/some\\ path.txt" )
305+ expect ( openFile ) . not . toHaveBeenCalled ( )
306+ expect ( vscode . commands . executeCommand ) . not . toHaveBeenCalled ( )
307+ } )
186308 } )
187309} )
0 commit comments