@@ -21,7 +21,21 @@ vi.mock("@/context/ExtensionStateContext", () => ({
2121
2222vi . mock ( "@/i18n/TranslationContext" , ( ) => ( {
2323 useAppTranslation : ( ) => ( {
24- t : ( key : string ) => key ,
24+ t : ( key : string ) => {
25+ const translations : Record < string , string > = {
26+ "chat:modeSelector.global" : "Global" ,
27+ "chat:modeSelector.project" : "Project" ,
28+ "chat:modeSelector.globalShort" : "G" ,
29+ "chat:modeSelector.projectShort" : "P" ,
30+ "chat:modeSelector.description" : "Select a mode to change how the assistant responds." ,
31+ "chat:modeSelector.searchPlaceholder" : "Search modes..." ,
32+ "chat:modeSelector.noResults" : "No modes found" ,
33+ "chat:modeSelector.marketplace" : "Browse Marketplace" ,
34+ "chat:modeSelector.settings" : "Mode Settings" ,
35+ "chat:modeSelector.title" : "Modes" ,
36+ }
37+ return translations [ key ] || key
38+ } ,
2539 } ) ,
2640} ) )
2741
@@ -93,7 +107,7 @@ describe("ModeSelector", () => {
93107 expect ( screen . getByTestId ( "mode-search-input" ) ) . toBeInTheDocument ( )
94108
95109 // Info icon should be visible
96- expect ( screen . getByText ( "chat:modeSelector.title " ) ) . toBeInTheDocument ( )
110+ expect ( screen . getByText ( "Modes " ) ) . toBeInTheDocument ( )
97111 const infoIcon = document . querySelector ( ".codicon-info" )
98112 expect ( infoIcon ) . toBeInTheDocument ( )
99113 } )
@@ -117,7 +131,7 @@ describe("ModeSelector", () => {
117131 expect ( screen . queryByTestId ( "mode-search-input" ) ) . not . toBeInTheDocument ( )
118132
119133 // Info blurb should be visible
120- expect ( screen . getByText ( / c h a t : m o d e S e l e c t o r . d e s c r i p t i o n / ) ) . toBeInTheDocument ( )
134+ expect ( screen . getByText ( / S e l e c t a m o d e t o c h a n g e h o w t h e a s s i s t a n t r e s p o n d s . / ) ) . toBeInTheDocument ( )
121135
122136 // Info icon should NOT be visible
123137 const infoIcon = document . querySelector ( ".codicon-info" )
@@ -169,7 +183,7 @@ describe("ModeSelector", () => {
169183 expect ( screen . queryByTestId ( "mode-search-input" ) ) . not . toBeInTheDocument ( )
170184
171185 // Info blurb should be visible instead
172- expect ( screen . getByText ( / c h a t : m o d e S e l e c t o r . d e s c r i p t i o n / ) ) . toBeInTheDocument ( )
186+ expect ( screen . getByText ( / S e l e c t a m o d e t o c h a n g e h o w t h e a s s i s t a n t r e s p o n d s . / ) ) . toBeInTheDocument ( )
173187
174188 // Info icon should NOT be visible
175189 const infoIcon = document . querySelector ( ".codicon-info" )
@@ -199,4 +213,129 @@ describe("ModeSelector", () => {
199213 const infoIcon = document . querySelector ( ".codicon-info" )
200214 expect ( infoIcon ) . toBeInTheDocument ( )
201215 } )
216+
217+ test ( "shows source indicator for custom modes" , ( ) => {
218+ const customModesWithSource : ModeConfig [ ] = [
219+ {
220+ slug : "custom-global" ,
221+ name : "Custom Global Mode" ,
222+ roleDefinition : "Role" ,
223+ groups : [ "read" ] as ModeConfig [ "groups" ] ,
224+ source : "global" ,
225+ } ,
226+ {
227+ slug : "custom-project" ,
228+ name : "Custom Project Mode" ,
229+ roleDefinition : "Role" ,
230+ groups : [ "read" ] as ModeConfig [ "groups" ] ,
231+ source : "project" ,
232+ } ,
233+ ]
234+
235+ // Set up mock to return custom modes
236+ mockModes = [
237+ ...customModesWithSource ,
238+ {
239+ slug : "code" ,
240+ name : "Code" ,
241+ roleDefinition : "Role" ,
242+ groups : [ "read" ] as ModeConfig [ "groups" ] ,
243+ } ,
244+ ]
245+
246+ render (
247+ < ModeSelector
248+ value = { "custom-global" as Mode }
249+ onChange = { vi . fn ( ) }
250+ modeShortcutText = "Ctrl+M"
251+ customModes = { customModesWithSource }
252+ /> ,
253+ )
254+
255+ // Check selected mode shows full indicator
256+ const trigger = screen . getByTestId ( "mode-selector-trigger" )
257+ expect ( trigger ) . toHaveTextContent ( "Custom Global Mode" )
258+ expect ( trigger ) . toHaveTextContent ( "(Global)" )
259+
260+ // Open dropdown
261+ fireEvent . click ( trigger )
262+
263+ // Check dropdown shows short indicators
264+ const items = screen . getAllByTestId ( "mode-selector-item" )
265+ const globalItem = items . find ( ( item ) => item . textContent ?. includes ( "Custom Global Mode" ) )
266+ const projectItem = items . find ( ( item ) => item . textContent ?. includes ( "Custom Project Mode" ) )
267+
268+ expect ( globalItem ) . toHaveTextContent ( "(G)" )
269+ expect ( projectItem ) . toHaveTextContent ( "(P)" )
270+ } )
271+
272+ test ( "shows no indicator for built-in modes" , ( ) => {
273+ // Set up mock to return only built-in modes
274+ mockModes = [
275+ {
276+ slug : "code" ,
277+ name : "Code" ,
278+ roleDefinition : "Role" ,
279+ groups : [ "read" ] as ModeConfig [ "groups" ] ,
280+ } ,
281+ {
282+ slug : "architect" ,
283+ name : "Architect" ,
284+ roleDefinition : "Role" ,
285+ groups : [ "read" ] as ModeConfig [ "groups" ] ,
286+ } ,
287+ ]
288+
289+ render ( < ModeSelector value = { "code" as Mode } onChange = { vi . fn ( ) } modeShortcutText = "Ctrl+M" /> )
290+
291+ const trigger = screen . getByTestId ( "mode-selector-trigger" )
292+ expect ( trigger ) . toHaveTextContent ( "Code" )
293+ expect ( trigger ) . not . toHaveTextContent ( "(" )
294+
295+ // Open dropdown
296+ fireEvent . click ( trigger )
297+
298+ // Check that no items have indicators
299+ const items = screen . getAllByTestId ( "mode-selector-item" )
300+ items . forEach ( ( item ) => {
301+ expect ( item ) . not . toHaveTextContent ( "(Global" )
302+ expect ( item ) . not . toHaveTextContent ( "(Project" )
303+ } )
304+ } )
305+
306+ test ( "defaults to global for custom modes without source" , ( ) => {
307+ const customModesNoSource : ModeConfig [ ] = [
308+ {
309+ slug : "custom-old" ,
310+ name : "Old Custom Mode" ,
311+ roleDefinition : "Role" ,
312+ groups : [ "read" ] as ModeConfig [ "groups" ] ,
313+ // No source field
314+ } ,
315+ ]
316+
317+ // Set up mock to include the custom mode
318+ mockModes = [
319+ ...customModesNoSource ,
320+ {
321+ slug : "code" ,
322+ name : "Code" ,
323+ roleDefinition : "Role" ,
324+ groups : [ "read" ] as ModeConfig [ "groups" ] ,
325+ } ,
326+ ]
327+
328+ render (
329+ < ModeSelector
330+ value = { "custom-old" as Mode }
331+ onChange = { vi . fn ( ) }
332+ modeShortcutText = "Ctrl+M"
333+ customModes = { customModesNoSource }
334+ /> ,
335+ )
336+
337+ const trigger = screen . getByTestId ( "mode-selector-trigger" )
338+ expect ( trigger ) . toHaveTextContent ( "Old Custom Mode" )
339+ expect ( trigger ) . toHaveTextContent ( "(Global)" )
340+ } )
202341} )
0 commit comments