11// npx jest src/shared/__tests__/modes.test.ts
22
3- import type { ModeConfig } from "@roo-code/types"
3+ import type { ModeConfig , PromptComponent } from "@roo-code/types"
44
55// Mock setup must come before imports
66jest . mock ( "vscode" )
@@ -11,7 +11,7 @@ jest.mock("../../core/prompts/sections/custom-instructions", () => ({
1111 addCustomInstructions : mockAddCustomInstructions ,
1212} ) )
1313
14- import { isToolAllowedForMode , FileRestrictionError , getFullModeDetails , modes } from "../modes"
14+ import { isToolAllowedForMode , FileRestrictionError , getFullModeDetails , modes , getModeSelection } from "../modes"
1515import { addCustomInstructions } from "../../core/prompts/sections/custom-instructions"
1616
1717describe ( "isToolAllowedForMode" , ( ) => {
@@ -371,3 +371,209 @@ describe("FileRestrictionError", () => {
371371 expect ( error . name ) . toBe ( "FileRestrictionError" )
372372 } )
373373} )
374+
375+ describe ( "getModeSelection" , ( ) => {
376+ const builtInAskMode = modes . find ( ( m ) => m . slug === "ask" ) !
377+ const customModesList : ModeConfig [ ] = [
378+ {
379+ slug : "code" , // Override
380+ name : "Custom Code Mode" ,
381+ roleDefinition : "Custom Code Role" ,
382+ customInstructions : "Custom Code Instructions" ,
383+ groups : [ "read" ] ,
384+ } ,
385+ {
386+ slug : "new-custom" ,
387+ name : "New Custom Mode" ,
388+ roleDefinition : "New Custom Role" ,
389+ customInstructions : "New Custom Instructions" ,
390+ groups : [ "edit" ] ,
391+ } ,
392+ ]
393+
394+ const promptComponentCode : PromptComponent = {
395+ roleDefinition : "Prompt Component Code Role" ,
396+ customInstructions : "Prompt Component Code Instructions" ,
397+ }
398+
399+ const promptComponentAsk : PromptComponent = {
400+ roleDefinition : "Prompt Component Ask Role" ,
401+ customInstructions : "Prompt Component Ask Instructions" ,
402+ }
403+
404+ test ( "should return built-in mode details if no overrides" , ( ) => {
405+ const selection = getModeSelection ( "ask" )
406+ expect ( selection . roleDefinition ) . toBe ( builtInAskMode . roleDefinition )
407+ expect ( selection . baseInstructions ) . toBe ( builtInAskMode . customInstructions || "" )
408+ } )
409+
410+ test ( "should prioritize promptComponent for built-in mode if no custom mode exists for that slug" , ( ) => {
411+ const selection = getModeSelection ( "ask" , promptComponentAsk ) // "ask" is not in customModesList
412+ expect ( selection . roleDefinition ) . toBe ( promptComponentAsk . roleDefinition )
413+ expect ( selection . baseInstructions ) . toBe ( promptComponentAsk . customInstructions )
414+ } )
415+
416+ test ( "should prioritize customMode over built-in mode" , ( ) => {
417+ const selection = getModeSelection ( "code" , undefined , customModesList )
418+ const customCode = customModesList . find ( ( m ) => m . slug === "code" ) !
419+ expect ( selection . roleDefinition ) . toBe ( customCode . roleDefinition )
420+ expect ( selection . baseInstructions ) . toBe ( customCode . customInstructions )
421+ } )
422+
423+ test ( "should prioritize customMode over promptComponent and built-in mode" , ( ) => {
424+ const selection = getModeSelection ( "code" , promptComponentCode , customModesList )
425+ const customCode = customModesList . find ( ( m ) => m . slug === "code" ) !
426+ expect ( selection . roleDefinition ) . toBe ( customCode . roleDefinition )
427+ expect ( selection . baseInstructions ) . toBe ( customCode . customInstructions )
428+ } )
429+
430+ test ( "should return new custom mode details if it exists" , ( ) => {
431+ const selection = getModeSelection ( "new-custom" , undefined , customModesList )
432+ const newCustom = customModesList . find ( ( m ) => m . slug === "new-custom" ) !
433+ expect ( selection . roleDefinition ) . toBe ( newCustom . roleDefinition )
434+ expect ( selection . baseInstructions ) . toBe ( newCustom . customInstructions )
435+ } )
436+
437+ test ( "customMode takes precedence for a new custom mode even if promptComponent is provided" , ( ) => {
438+ const promptComponentNew : PromptComponent = {
439+ roleDefinition : "Prompt New Custom Role" ,
440+ customInstructions : "Prompt New Custom Instructions" ,
441+ }
442+ const selection = getModeSelection ( "new-custom" , promptComponentNew , customModesList )
443+ const newCustomMode = customModesList . find ( ( m ) => m . slug === "new-custom" ) !
444+ expect ( selection . roleDefinition ) . toBe ( newCustomMode . roleDefinition )
445+ expect ( selection . baseInstructions ) . toBe ( newCustomMode . customInstructions )
446+ } )
447+
448+ test ( "should return empty strings if slug does not exist in custom, prompt, or built-in modes" , ( ) => {
449+ const selection = getModeSelection ( "non-existent-mode" , undefined , customModesList )
450+ expect ( selection . roleDefinition ) . toBe ( "" )
451+ expect ( selection . baseInstructions ) . toBe ( "" )
452+ } )
453+
454+ test ( "customMode's properties are used if customMode exists, ignoring promptComponent's properties" , ( ) => {
455+ const selection = getModeSelection (
456+ "code" ,
457+ { roleDefinition : "Prompt Role Only" , customInstructions : "Prompt Instructions Only" } ,
458+ customModesList ,
459+ )
460+ const customCodeMode = customModesList . find ( ( m ) => m . slug === "code" ) !
461+ expect ( selection . roleDefinition ) . toBe ( customCodeMode . roleDefinition ) // Takes from customCodeMode
462+ expect ( selection . baseInstructions ) . toBe ( customCodeMode . customInstructions ) // Takes from customCodeMode
463+ } )
464+
465+ test ( "handles undefined customInstructions in customMode gracefully" , ( ) => {
466+ const modesWithoutCustomInstructions : ModeConfig [ ] = [
467+ {
468+ slug : "no-instr" ,
469+ name : "No Instructions Mode" ,
470+ roleDefinition : "Role for no instructions" ,
471+ groups : [ "read" ] ,
472+ // customInstructions is undefined
473+ } ,
474+ ]
475+ const selection = getModeSelection ( "no-instr" , undefined , modesWithoutCustomInstructions )
476+ expect ( selection . roleDefinition ) . toBe ( "Role for no instructions" )
477+ expect ( selection . baseInstructions ) . toBe ( "" ) // Defaults to empty string
478+ } )
479+
480+ test ( "handles empty or undefined roleDefinition in customMode gracefully" , ( ) => {
481+ const modesWithEmptyRoleDef : ModeConfig [ ] = [
482+ {
483+ slug : "empty-role" ,
484+ name : "Empty Role Mode" ,
485+ roleDefinition : "" ,
486+ customInstructions : "Instructions for empty role" ,
487+ groups : [ "read" ] ,
488+ } ,
489+ ]
490+ const selection = getModeSelection ( "empty-role" , undefined , modesWithEmptyRoleDef )
491+ expect ( selection . roleDefinition ) . toBe ( "" )
492+ expect ( selection . baseInstructions ) . toBe ( "Instructions for empty role" )
493+
494+ const modesWithUndefinedRoleDef : ModeConfig [ ] = [
495+ {
496+ slug : "undefined-role" ,
497+ name : "Undefined Role Mode" ,
498+ roleDefinition : "" , // Test undefined explicitly by using an empty string
499+ customInstructions : "Instructions for undefined role" ,
500+ groups : [ "read" ] ,
501+ } ,
502+ ]
503+ const selection2 = getModeSelection ( "undefined-role" , undefined , modesWithUndefinedRoleDef )
504+ expect ( selection2 . roleDefinition ) . toBe ( "" )
505+ expect ( selection2 . baseInstructions ) . toBe ( "Instructions for undefined role" )
506+ } )
507+
508+ test ( "customMode's defined properties take precedence, undefined ones in customMode result in ''" , ( ) => {
509+ const customModeRoleOnlyList : ModeConfig [ ] = [
510+ // Renamed for clarity
511+ {
512+ slug : "role-custom" ,
513+ name : "Role Custom" ,
514+ roleDefinition : "Custom Role Only" ,
515+ groups : [ "read" ] /* customInstructions undefined */ ,
516+ } ,
517+ ]
518+ const promptComponentInstrOnly : PromptComponent = { customInstructions : "Prompt Instructions Only" }
519+ // "role-custom" exists in customModeRoleOnlyList
520+ const selection = getModeSelection ( "role-custom" , promptComponentInstrOnly , customModeRoleOnlyList )
521+ // customMode is chosen.
522+ expect ( selection . roleDefinition ) . toBe ( "Custom Role Only" ) // From customMode
523+ expect ( selection . baseInstructions ) . toBe ( "" ) // From customMode (undefined || '' -> '')
524+ } )
525+
526+ test ( "customMode's defined properties take precedence, empty string ones in customMode are used" , ( ) => {
527+ const customModeInstrOnlyList : ModeConfig [ ] = [
528+ // Renamed for clarity
529+ {
530+ slug : "instr-custom" ,
531+ name : "Instr Custom" ,
532+ roleDefinition : "" , // Explicitly empty
533+ customInstructions : "Custom Instructions Only" ,
534+ groups : [ "read" ] ,
535+ } ,
536+ ]
537+ const promptComponentRoleOnly : PromptComponent = { roleDefinition : "Prompt Role Only" }
538+ // "instr-custom" exists in customModeInstrOnlyList
539+ const selection = getModeSelection ( "instr-custom" , promptComponentRoleOnly , customModeInstrOnlyList )
540+ // customMode is chosen
541+ expect ( selection . roleDefinition ) . toBe ( "" ) // From customMode ( "" || '' -> "")
542+ expect ( selection . baseInstructions ) . toBe ( "Custom Instructions Only" ) // From customMode
543+ } )
544+
545+ test ( "customMode with empty/undefined fields takes precedence over promptComponent and builtInMode" , ( ) => {
546+ const customModeMinimal : ModeConfig [ ] = [
547+ { slug : "ask" , name : "Custom Ask Minimal" , roleDefinition : "" , groups : [ "read" ] } , // roleDef empty, customInstr undefined
548+ ]
549+ const promptComponentMinimal : PromptComponent = {
550+ roleDefinition : "Prompt Min Role" ,
551+ customInstructions : "Prompt Min Instr" ,
552+ }
553+ // "ask" is in customModeMinimal
554+ const selection = getModeSelection ( "ask" , promptComponentMinimal , customModeMinimal )
555+ // customMode is chosen
556+ expect ( selection . roleDefinition ) . toBe ( "" ) // From customModeMinimal
557+ expect ( selection . baseInstructions ) . toBe ( "" ) // From customModeMinimal
558+ } )
559+
560+ test ( "promptComponent is used if customMode for slug does not exist, even if customModesList is provided" , ( ) => {
561+ // 'ask' is not in customModesList, but 'code' and 'new-custom' are.
562+ const selection = getModeSelection ( "ask" , promptComponentAsk , customModesList )
563+ expect ( selection . roleDefinition ) . toBe ( promptComponentAsk . roleDefinition )
564+ expect ( selection . baseInstructions ) . toBe ( promptComponentAsk . customInstructions )
565+ } )
566+
567+ test ( "builtInMode is used if customMode for slug does not exist and promptComponent is not provided" , ( ) => {
568+ // 'ask' is not in customModesList
569+ const selection = getModeSelection ( "ask" , undefined , customModesList )
570+ expect ( selection . roleDefinition ) . toBe ( builtInAskMode . roleDefinition )
571+ expect ( selection . baseInstructions ) . toBe ( builtInAskMode . customInstructions || "" )
572+ } )
573+
574+ test ( "promptComponent is used if customMode is not provided (undefined customModesList)" , ( ) => {
575+ const selection = getModeSelection ( "ask" , promptComponentAsk , undefined )
576+ expect ( selection . roleDefinition ) . toBe ( promptComponentAsk . roleDefinition )
577+ expect ( selection . baseInstructions ) . toBe ( promptComponentAsk . customInstructions )
578+ } )
579+ } )
0 commit comments