@@ -34,17 +34,53 @@ describe('Home Config Directory Integration Tests', () => {
34
34
}
35
35
36
36
// 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 ) => {
38
38
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
+ ]
40
54
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
+ )
42
67
}
43
- for ( const f of files ) {
68
+ for ( const f of rootFiles ) {
44
69
const p = resolve ( home , f )
45
70
if ( existsSync ( p ) )
46
71
rmSync ( p )
47
72
}
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
+ }
48
84
}
49
85
50
86
describe ( 'Real home config loading' , ( ) => {
@@ -76,7 +112,7 @@ describe('Home Config Directory Integration Tests', () => {
76
112
} )
77
113
}
78
114
finally {
79
- _cleanupHomeDotfiles ( testName )
115
+ cleanupHomeDotfiles ( testName )
80
116
}
81
117
} )
82
118
@@ -393,4 +429,284 @@ describe('Home Config Directory Integration Tests', () => {
393
429
}
394
430
} )
395
431
} )
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
+ } )
396
712
} )
0 commit comments