@@ -34,17 +34,53 @@ describe('Home Config Directory Integration Tests', () => {
3434 }
3535
3636 // Helper to clean up root-level home dotfiles for a specific test name/alias
37- const _cleanupHomeDotfiles = ( testName : string , alias ?: string ) => {
37+ const cleanupHomeDotfiles = ( testName : string , alias ?: string ) => {
3838 const home = homedir ( )
39- const files = [ `.${ testName } .config.ts` , `.${ testName } .config.js` , `.${ testName } .config.mjs` , `.${ testName } .config.cjs` , `.${ testName } .config.json` ]
39+ const homeConfigDir = resolve ( home , '.config' )
40+
41+ // Clean up root-level dotfiles (~/.<name>.config.* and ~/.<name>.*)
42+ const rootFiles = [
43+ `.${ testName } .config.ts` ,
44+ `.${ testName } .config.js` ,
45+ `.${ testName } .config.mjs` ,
46+ `.${ testName } .config.cjs` ,
47+ `.${ testName } .config.json` ,
48+ `.${ testName } .ts` ,
49+ `.${ testName } .js` ,
50+ `.${ testName } .mjs` ,
51+ `.${ testName } .cjs` ,
52+ `.${ testName } .json` ,
53+ ]
4054 if ( alias ) {
41- files . push ( `.${ alias } .config.ts` , `.${ alias } .config.js` , `.${ alias } .config.mjs` , `.${ alias } .config.cjs` , `.${ alias } .config.json` )
55+ rootFiles . push (
56+ `.${ alias } .config.ts` ,
57+ `.${ alias } .config.js` ,
58+ `.${ alias } .config.mjs` ,
59+ `.${ alias } .config.cjs` ,
60+ `.${ alias } .config.json` ,
61+ `.${ alias } .ts` ,
62+ `.${ alias } .js` ,
63+ `.${ alias } .mjs` ,
64+ `.${ alias } .cjs` ,
65+ `.${ alias } .json` ,
66+ )
4267 }
43- for ( const f of files ) {
68+ for ( const f of rootFiles ) {
4469 const p = resolve ( home , f )
4570 if ( existsSync ( p ) )
4671 rmSync ( p )
4772 }
73+
74+ // Clean up ~/.config dotfiles (~/.config/.<name>.config.*)
75+ const configDirFiles = [ `.${ testName } .config.ts` , `.${ testName } .config.js` , `.${ testName } .config.mjs` , `.${ testName } .config.cjs` , `.${ testName } .config.json` ]
76+ if ( alias ) {
77+ configDirFiles . push ( `.${ alias } .config.ts` , `.${ alias } .config.js` , `.${ alias } .config.mjs` , `.${ alias } .config.cjs` , `.${ alias } .config.json` )
78+ }
79+ for ( const f of configDirFiles ) {
80+ const p = resolve ( homeConfigDir , f )
81+ if ( existsSync ( p ) )
82+ rmSync ( p )
83+ }
4884 }
4985
5086 describe ( 'Real home config loading' , ( ) => {
@@ -76,7 +112,7 @@ describe('Home Config Directory Integration Tests', () => {
76112 } )
77113 }
78114 finally {
79- _cleanupHomeDotfiles ( testName )
115+ cleanupHomeDotfiles ( testName )
80116 }
81117 } )
82118
@@ -393,4 +429,284 @@ describe('Home Config Directory Integration Tests', () => {
393429 }
394430 } )
395431 } )
432+
433+ describe ( 'Dotfile config patterns' , ( ) => {
434+ describe ( '~/.config/.<name>.config.* patterns' , ( ) => {
435+ it ( 'should load config from ~/.config/.<name>.config.ts' , async ( ) => {
436+ const testName = generateTestName ( )
437+ const homeConfigDir = resolve ( homedir ( ) , '.config' )
438+
439+ try {
440+ mkdirSync ( homeConfigDir , { recursive : true } )
441+
442+ const configPath = resolve ( homeConfigDir , `.${ testName } .config.ts` )
443+ writeFileSync ( configPath , `export default { source: 'config-dir-dotfile', value: 42 }` )
444+
445+ const result = await loadConfig ( {
446+ name : testName ,
447+ cwd : testCwd ,
448+ defaultConfig : { source : 'default' , value : 0 } ,
449+ } )
450+
451+ expect ( result ) . toEqual ( { source : 'config-dir-dotfile' , value : 42 } )
452+ }
453+ finally {
454+ cleanupHomeDotfiles ( testName )
455+ }
456+ } )
457+
458+ it ( 'should load config from ~/.config/.<alias>.config.ts when using alias' , async ( ) => {
459+ const testName = generateTestName ( )
460+ const testAlias = `${ testName } -alias`
461+ const homeConfigDir = resolve ( homedir ( ) , '.config' )
462+
463+ try {
464+ mkdirSync ( homeConfigDir , { recursive : true } )
465+
466+ const configPath = resolve ( homeConfigDir , `.${ testAlias } .config.ts` )
467+ writeFileSync ( configPath , `export default { source: 'config-dir-alias-dotfile', alias: true }` )
468+
469+ const result = await loadConfig ( {
470+ name : testName ,
471+ alias : testAlias ,
472+ cwd : testCwd ,
473+ defaultConfig : { source : 'default' , alias : false } ,
474+ } )
475+
476+ expect ( result ) . toEqual ( { source : 'config-dir-alias-dotfile' , alias : true } )
477+ }
478+ finally {
479+ cleanupHomeDotfiles ( testName , testAlias )
480+ }
481+ } )
482+
483+ it ( 'should handle different extensions for ~/.config/.<name>.config.*' , async ( ) => {
484+ const testName = generateTestName ( )
485+ const homeConfigDir = resolve ( homedir ( ) , '.config' )
486+
487+ try {
488+ mkdirSync ( homeConfigDir , { recursive : true } )
489+
490+ const configPath = resolve ( homeConfigDir , `.${ testName } .config.json` )
491+ writeFileSync ( configPath , JSON . stringify ( { source : 'config-dir-dotfile-json' , format : 'json' } ) )
492+
493+ const result = await loadConfig ( {
494+ name : testName ,
495+ cwd : testCwd ,
496+ defaultConfig : { source : 'default' , format : 'unknown' } ,
497+ } )
498+
499+ expect ( result ) . toEqual ( { source : 'config-dir-dotfile-json' , format : 'json' } )
500+ }
501+ finally {
502+ cleanupHomeDotfiles ( testName )
503+ }
504+ } )
505+ } )
506+
507+ describe ( '~/.<name>.* patterns (without .config suffix)' , ( ) => {
508+ it ( 'should load config from ~/.<name>.ts' , async ( ) => {
509+ const testName = generateTestName ( )
510+ const homeDir = homedir ( )
511+
512+ try {
513+ const configPath = resolve ( homeDir , `.${ testName } .ts` )
514+ writeFileSync ( configPath , `export default { source: 'home-root-dotfile', simple: true }` )
515+
516+ const result = await loadConfig ( {
517+ name : testName ,
518+ cwd : testCwd ,
519+ defaultConfig : { source : 'default' , simple : false } ,
520+ } )
521+
522+ expect ( result ) . toEqual ( { source : 'home-root-dotfile' , simple : true } )
523+ }
524+ finally {
525+ cleanupHomeDotfiles ( testName )
526+ }
527+ } )
528+
529+ it ( 'should load config from ~/.<alias>.ts when using alias' , async ( ) => {
530+ const testName = generateTestName ( )
531+ const testAlias = `${ testName } -alias`
532+ const homeDir = homedir ( )
533+
534+ try {
535+ const configPath = resolve ( homeDir , `.${ testAlias } .ts` )
536+ writeFileSync ( configPath , `export default { source: 'home-root-alias-dotfile', aliased: true }` )
537+
538+ const result = await loadConfig ( {
539+ name : testName ,
540+ alias : testAlias ,
541+ cwd : testCwd ,
542+ defaultConfig : { source : 'default' , aliased : false } ,
543+ } )
544+
545+ expect ( result ) . toEqual ( { source : 'home-root-alias-dotfile' , aliased : true } )
546+ }
547+ finally {
548+ cleanupHomeDotfiles ( testName , testAlias )
549+ }
550+ } )
551+
552+ it ( 'should handle different extensions for ~/.<name>.*' , async ( ) => {
553+ const testName = generateTestName ( )
554+ const homeDir = homedir ( )
555+
556+ try {
557+ const configPath = resolve ( homeDir , `.${ testName } .json` )
558+ writeFileSync ( configPath , JSON . stringify ( { source : 'home-root-dotfile-json' , extension : 'json' } ) )
559+
560+ const result = await loadConfig ( {
561+ name : testName ,
562+ cwd : testCwd ,
563+ defaultConfig : { source : 'default' , extension : 'unknown' } ,
564+ } )
565+
566+ expect ( result ) . toEqual ( { source : 'home-root-dotfile-json' , extension : 'json' } )
567+ }
568+ finally {
569+ cleanupHomeDotfiles ( testName )
570+ }
571+ } )
572+ } )
573+
574+ describe ( 'Config loading priority' , ( ) => {
575+ it ( 'should prefer ~/.config/.<name>.config.* over ~/.<name>.*' , async ( ) => {
576+ const testName = generateTestName ( )
577+ const homeDir = homedir ( )
578+ const homeConfigDir = resolve ( homeDir , '.config' )
579+
580+ try {
581+ mkdirSync ( homeConfigDir , { recursive : true } )
582+
583+ // Create both patterns
584+ const configDirPath = resolve ( homeConfigDir , `.${ testName } .config.ts` )
585+ const rootPath = resolve ( homeDir , `.${ testName } .ts` )
586+
587+ writeFileSync ( configDirPath , `export default { source: 'config-dir-dotfile', priority: 'high' }` )
588+ writeFileSync ( rootPath , `export default { source: 'home-root-dotfile', priority: 'low' }` )
589+
590+ const result = await loadConfig ( {
591+ name : testName ,
592+ cwd : testCwd ,
593+ defaultConfig : { source : 'default' , priority : 'none' } ,
594+ } )
595+
596+ // Should prefer ~/.config/.<name>.config.* over ~/.<name>.*
597+ expect ( result ) . toEqual ( { source : 'config-dir-dotfile' , priority : 'high' } )
598+ }
599+ finally {
600+ cleanupHomeDotfiles ( testName )
601+ }
602+ } )
603+
604+ it ( 'should prefer ~/.<name>.config.* over ~/.<name>.*' , async ( ) => {
605+ const testName = generateTestName ( )
606+ const homeDir = homedir ( )
607+
608+ try {
609+ // Create both patterns in home root
610+ const configPath = resolve ( homeDir , `.${ testName } .config.ts` )
611+ const simplePath = resolve ( homeDir , `.${ testName } .ts` )
612+
613+ writeFileSync ( configPath , `export default { source: 'home-config-dotfile', priority: 'high' }` )
614+ writeFileSync ( simplePath , `export default { source: 'home-simple-dotfile', priority: 'low' }` )
615+
616+ const result = await loadConfig ( {
617+ name : testName ,
618+ cwd : testCwd ,
619+ defaultConfig : { source : 'default' , priority : 'none' } ,
620+ } )
621+
622+ // Should prefer ~/.<name>.config.* over ~/.<name>.*
623+ expect ( result ) . toEqual ( { source : 'home-config-dotfile' , priority : 'high' } )
624+ }
625+ finally {
626+ cleanupHomeDotfiles ( testName )
627+ }
628+ } )
629+
630+ it ( 'should prefer local config over all dotfile patterns' , async ( ) => {
631+ const testName = generateTestName ( )
632+ const homeDir = homedir ( )
633+ const homeConfigDir = resolve ( homeDir , '.config' )
634+
635+ try {
636+ mkdirSync ( homeConfigDir , { recursive : true } )
637+
638+ // Create local config
639+ const localConfigPath = resolve ( testCwd , `${ testName } .config.ts` )
640+ writeFileSync ( localConfigPath , `export default { source: 'local', priority: 'highest' }` )
641+
642+ // Create all dotfile patterns
643+ const configDirPath = resolve ( homeConfigDir , `.${ testName } .config.ts` )
644+ const homeConfigPath = resolve ( homeDir , `.${ testName } .config.ts` )
645+ const homeSimplePath = resolve ( homeDir , `.${ testName } .ts` )
646+
647+ writeFileSync ( configDirPath , `export default { source: 'config-dir-dotfile', priority: 'high' }` )
648+ writeFileSync ( homeConfigPath , `export default { source: 'home-config-dotfile', priority: 'medium' }` )
649+ writeFileSync ( homeSimplePath , `export default { source: 'home-simple-dotfile', priority: 'low' }` )
650+
651+ const result = await loadConfig ( {
652+ name : testName ,
653+ cwd : testCwd ,
654+ defaultConfig : { source : 'default' , priority : 'none' } ,
655+ } )
656+
657+ // Should prefer local config over all dotfile patterns
658+ expect ( result ) . toEqual ( { source : 'local' , priority : 'highest' } )
659+ }
660+ finally {
661+ cleanupHomeDotfiles ( testName )
662+ }
663+ } )
664+ } )
665+
666+ describe ( 'Error handling for dotfile patterns' , ( ) => {
667+ it ( 'should handle invalid ~/.config/.<name>.config.* gracefully' , async ( ) => {
668+ const testName = generateTestName ( )
669+ const homeConfigDir = resolve ( homedir ( ) , '.config' )
670+
671+ try {
672+ mkdirSync ( homeConfigDir , { recursive : true } )
673+
674+ const configPath = resolve ( homeConfigDir , `.${ testName } .config.ts` )
675+ writeFileSync ( configPath , `export default "invalid config"` )
676+
677+ const result = await loadConfig ( {
678+ name : testName ,
679+ cwd : testCwd ,
680+ defaultConfig : { source : 'default' , valid : true } ,
681+ } )
682+
683+ expect ( result ) . toEqual ( { source : 'default' , valid : true } )
684+ }
685+ finally {
686+ cleanupHomeDotfiles ( testName )
687+ }
688+ } )
689+
690+ it ( 'should handle invalid ~/.<name>.* gracefully' , async ( ) => {
691+ const testName = generateTestName ( )
692+ const homeDir = homedir ( )
693+
694+ try {
695+ const configPath = resolve ( homeDir , `.${ testName } .ts` )
696+ writeFileSync ( configPath , `export default null` )
697+
698+ const result = await loadConfig ( {
699+ name : testName ,
700+ cwd : testCwd ,
701+ defaultConfig : { source : 'default' , valid : true } ,
702+ } )
703+
704+ expect ( result ) . toEqual ( { source : 'default' , valid : true } )
705+ }
706+ finally {
707+ cleanupHomeDotfiles ( testName )
708+ }
709+ } )
710+ } )
711+ } )
396712} )
0 commit comments