99 computeMovedFilePath ,
1010 computeTreeFilter ,
1111 defaultTreeSortComparator ,
12+ doesFilenameMatchExtension ,
1213 findNodeByPath ,
1314 findNodeInDirectory ,
1415 flattenWorkspaceTreeWithPaths ,
@@ -21,6 +22,7 @@ import {
2122 mapWorkspaceTreePaths ,
2223 removeFirstPathSegment ,
2324 removeRedundantNodes ,
25+ replaceFileExtension ,
2426 separateFilenameFromPath ,
2527 shouldNodeBeVisible ,
2628 sortWorkspaceTree ,
@@ -1383,6 +1385,95 @@ describe('Workspace utility function tests', () => {
13831385 } ) ;
13841386 } ) ;
13851387
1388+ describe ( 'doesFileMatchExtension' , ( ) => {
1389+ test ( 'Should match extension without leading dot' , ( ) => {
1390+ expect ( doesFilenameMatchExtension ( 'txt' , 'file.txt' ) ) . toBe ( true ) ;
1391+ expect ( doesFilenameMatchExtension ( 'seq' , 'test.seq' ) ) . toBe ( true ) ;
1392+ expect ( doesFilenameMatchExtension ( 'json' , 'data.json' ) ) . toBe ( true ) ;
1393+ } ) ;
1394+
1395+ test ( 'Should match extension with leading dot' , ( ) => {
1396+ expect ( doesFilenameMatchExtension ( '.txt' , 'file.txt' ) ) . toBe ( true ) ;
1397+ expect ( doesFilenameMatchExtension ( '.seq' , 'test.seq' ) ) . toBe ( true ) ;
1398+ expect ( doesFilenameMatchExtension ( '.json' , 'data.json' ) ) . toBe ( true ) ;
1399+ } ) ;
1400+
1401+ test ( 'Should be case-insensitive' , ( ) => {
1402+ expect ( doesFilenameMatchExtension ( 'TXT' , 'file.txt' ) ) . toBe ( true ) ;
1403+ expect ( doesFilenameMatchExtension ( 'txt' , 'file.TXT' ) ) . toBe ( true ) ;
1404+ expect ( doesFilenameMatchExtension ( 'TxT' , 'file.TxT' ) ) . toBe ( true ) ;
1405+ expect ( doesFilenameMatchExtension ( 'SEQ' , 'test.seq' ) ) . toBe ( true ) ;
1406+ } ) ;
1407+
1408+ test ( 'Should not match wrong extension' , ( ) => {
1409+ expect ( doesFilenameMatchExtension ( 'txt' , 'file.json' ) ) . toBe ( false ) ;
1410+ expect ( doesFilenameMatchExtension ( 'seq' , 'test.txt' ) ) . toBe ( false ) ;
1411+ expect ( doesFilenameMatchExtension ( 'json' , 'data.xml' ) ) . toBe ( false ) ;
1412+ } ) ;
1413+
1414+ test ( 'Should only match extension at the end of filename' , ( ) => {
1415+ expect ( doesFilenameMatchExtension ( 'txt' , 'file.txt.bak' ) ) . toBe ( false ) ;
1416+ expect ( doesFilenameMatchExtension ( 'txt' , 'txt.json' ) ) . toBe ( false ) ;
1417+ } ) ;
1418+
1419+ test ( 'Should handle files with multiple dots' , ( ) => {
1420+ expect ( doesFilenameMatchExtension ( 'js' , 'script.min.js' ) ) . toBe ( true ) ;
1421+ expect ( doesFilenameMatchExtension ( 'json' , 'config.test.json' ) ) . toBe ( true ) ;
1422+ expect ( doesFilenameMatchExtension ( 'txt' , 'my.file.name.txt' ) ) . toBe ( true ) ;
1423+ } ) ;
1424+
1425+ test ( 'Should not match files without extension' , ( ) => {
1426+ expect ( doesFilenameMatchExtension ( 'txt' , 'README' ) ) . toBe ( false ) ;
1427+ expect ( doesFilenameMatchExtension ( 'js' , 'Makefile' ) ) . toBe ( false ) ;
1428+ } ) ;
1429+
1430+ test ( 'Should not match partial extension' , ( ) => {
1431+ expect ( doesFilenameMatchExtension ( 'tx' , 'file.txt' ) ) . toBe ( false ) ;
1432+ expect ( doesFilenameMatchExtension ( 't' , 'file.txt' ) ) . toBe ( false ) ;
1433+ expect ( doesFilenameMatchExtension ( 'son' , 'file.json' ) ) . toBe ( false ) ;
1434+ } ) ;
1435+
1436+ test ( 'Should handle empty filename' , ( ) => {
1437+ expect ( doesFilenameMatchExtension ( 'txt' , '' ) ) . toBe ( false ) ;
1438+ } ) ;
1439+
1440+ test ( 'Should handle empty extension' , ( ) => {
1441+ expect ( doesFilenameMatchExtension ( '' , 'file.' ) ) . toBe ( true ) ; // matches files ending with a dot
1442+ expect ( doesFilenameMatchExtension ( '' , 'file.txt' ) ) . toBe ( false ) ;
1443+ expect ( doesFilenameMatchExtension ( '' , 'file' ) ) . toBe ( false ) ;
1444+ } ) ;
1445+
1446+ test ( 'Should handle filename with path' , ( ) => {
1447+ expect ( doesFilenameMatchExtension ( 'txt' , 'path/to/file.txt' ) ) . toBe ( true ) ;
1448+ expect ( doesFilenameMatchExtension ( 'seq' , 'folder/subfolder/test.seq' ) ) . toBe ( true ) ;
1449+ expect ( doesFilenameMatchExtension ( 'json' , 'a/b/c/data.json' ) ) . toBe ( true ) ;
1450+ } ) ;
1451+
1452+ test ( 'Should handle hidden files (starting with dot)' , ( ) => {
1453+ expect ( doesFilenameMatchExtension ( 'json' , '.config.json' ) ) . toBe ( true ) ;
1454+ expect ( doesFilenameMatchExtension ( 'txt' , '.gitignore.txt' ) ) . toBe ( true ) ;
1455+ } ) ;
1456+
1457+ test ( 'Should not match if extension appears in middle of filename' , ( ) => {
1458+ expect ( doesFilenameMatchExtension ( 'txt' , 'file-txt.json' ) ) . toBe ( false ) ;
1459+ expect ( doesFilenameMatchExtension ( 'seq' , 'sequential.js' ) ) . toBe ( false ) ;
1460+ } ) ;
1461+
1462+ test ( 'Should handle special regex characters in extension' , ( ) => {
1463+ // Note: The function does NOT escape regex special characters
1464+ // So these are treated as regex patterns, not literal strings
1465+ expect ( doesFilenameMatchExtension ( 't*t' , 'file.tt' ) ) . toBe ( true ) ; // t*t means "zero or more t" + "t"
1466+ expect ( doesFilenameMatchExtension ( 't+t' , 'file.ttt' ) ) . toBe ( true ) ; // t+t means "one or more t" + "t"
1467+ expect ( doesFilenameMatchExtension ( 't*t' , 'file.t' ) ) . toBe ( true ) ; // matches .t (zero t's + t)
1468+ } ) ;
1469+
1470+ test ( 'Should match exact extension only' , ( ) => {
1471+ expect ( doesFilenameMatchExtension ( 'js' , 'file.js' ) ) . toBe ( true ) ;
1472+ expect ( doesFilenameMatchExtension ( 'js' , 'file.json' ) ) . toBe ( false ) ;
1473+ expect ( doesFilenameMatchExtension ( 'js' , 'file.jsx' ) ) . toBe ( false ) ;
1474+ } ) ;
1475+ } ) ;
1476+
13861477 describe ( 'getCommonPathPrefix' , ( ) => {
13871478 test ( 'Should return empty string for empty array' , ( ) => {
13881479 const result = getCommonPathPrefix ( [ ] ) ;
@@ -1434,4 +1525,91 @@ describe('Workspace utility function tests', () => {
14341525 expect ( result ) . toBe ( 'a/b' ) ;
14351526 } ) ;
14361527 } ) ;
1528+
1529+ describe ( 'replaceFileExtension' , ( ) => {
1530+ test ( 'Should replace extension without leading dots' , ( ) => {
1531+ expect ( replaceFileExtension ( 'file.txt' , 'txt' , 'json' ) ) . toBe ( 'file.json' ) ;
1532+ expect ( replaceFileExtension ( 'test.seq.json' , 'seq.json' , 'seqN.txt' ) ) . toBe ( 'test.seqN.txt' ) ;
1533+ expect ( replaceFileExtension ( 'data.xml' , 'xml' , 'json' ) ) . toBe ( 'data.json' ) ;
1534+ } ) ;
1535+
1536+ test ( 'Should replace extension with leading dots' , ( ) => {
1537+ expect ( replaceFileExtension ( 'file.txt' , '.txt' , '.json' ) ) . toBe ( 'file.json' ) ;
1538+ expect ( replaceFileExtension ( 'test.seq' , '.seq' , '.seqN' ) ) . toBe ( 'test.seqN' ) ;
1539+ expect ( replaceFileExtension ( 'data.xml' , '.xml' , '.json' ) ) . toBe ( 'data.json' ) ;
1540+ } ) ;
1541+
1542+ test ( 'Should replace extension with mixed dot usage' , ( ) => {
1543+ expect ( replaceFileExtension ( 'file.txt' , 'txt' , '.json' ) ) . toBe ( 'file.json' ) ;
1544+ expect ( replaceFileExtension ( 'test.seq' , '.seq' , 'seqN' ) ) . toBe ( 'test.seqN' ) ;
1545+ } ) ;
1546+
1547+ test ( 'Should be case-insensitive when matching extension' , ( ) => {
1548+ expect ( replaceFileExtension ( 'file.TXT' , 'txt' , 'json' ) ) . toBe ( 'file.json' ) ;
1549+ expect ( replaceFileExtension ( 'test.SEQ' , 'seq' , 'seqN' ) ) . toBe ( 'test.seqN' ) ;
1550+ expect ( replaceFileExtension ( 'data.Xml' , 'XML' , 'json' ) ) . toBe ( 'data.json' ) ;
1551+ } ) ;
1552+
1553+ test ( 'Should not replace if extension does not match' , ( ) => {
1554+ expect ( replaceFileExtension ( 'file.txt' , 'json' , 'xml' ) ) . toBe ( 'file.txt' ) ;
1555+ expect ( replaceFileExtension ( 'test.seq' , 'txt' , 'json' ) ) . toBe ( 'test.seq' ) ;
1556+ expect ( replaceFileExtension ( 'data.xml' , 'seq' , 'json' ) ) . toBe ( 'data.xml' ) ;
1557+ } ) ;
1558+
1559+ test ( 'Should only replace extension at the end of filename' , ( ) => {
1560+ expect ( replaceFileExtension ( 'file.txt.bak' , 'txt' , 'json' ) ) . toBe ( 'file.txt.bak' ) ;
1561+ expect ( replaceFileExtension ( 'test.seq.old' , 'seq' , 'seqN' ) ) . toBe ( 'test.seq.old' ) ;
1562+ } ) ;
1563+
1564+ test ( 'Should handle files with multiple dots correctly' , ( ) => {
1565+ expect ( replaceFileExtension ( 'script.min.js' , 'min.js' , 'ts' ) ) . toBe ( 'script.ts' ) ;
1566+ expect ( replaceFileExtension ( 'config.test.json' , 'json' , 'xml' ) ) . toBe ( 'config.test.xml' ) ;
1567+ expect ( replaceFileExtension ( 'my.file.name.txt' , 'txt' , 'md' ) ) . toBe ( 'my.file.name.md' ) ;
1568+ } ) ;
1569+
1570+ test ( 'Should not replace if filename has no extension' , ( ) => {
1571+ expect ( replaceFileExtension ( 'README' , 'txt' , 'md' ) ) . toBe ( 'README' ) ;
1572+ expect ( replaceFileExtension ( 'Makefile' , 'txt' , 'json' ) ) . toBe ( 'Makefile' ) ;
1573+ } ) ;
1574+
1575+ test ( 'Should handle files with path' , ( ) => {
1576+ expect ( replaceFileExtension ( 'path/to/file.txt' , 'txt' , 'json' ) ) . toBe ( 'path/to/file.json' ) ;
1577+ expect ( replaceFileExtension ( 'folder/subfolder/test.seq' , 'seq' , 'seqN' ) ) . toBe ( 'folder/subfolder/test.seqN' ) ;
1578+ expect ( replaceFileExtension ( 'a/b/c/data.xml' , 'xml' , 'json' ) ) . toBe ( 'a/b/c/data.json' ) ;
1579+ } ) ;
1580+
1581+ test ( 'Should handle hidden files (starting with dot)' , ( ) => {
1582+ expect ( replaceFileExtension ( '.config.json' , 'json' , 'xml' ) ) . toBe ( '.config.xml' ) ;
1583+ expect ( replaceFileExtension ( '.gitignore.txt' , 'txt' , 'bak' ) ) . toBe ( '.gitignore.bak' ) ;
1584+ } ) ;
1585+
1586+ test ( 'Should not match partial extension' , ( ) => {
1587+ expect ( replaceFileExtension ( 'file.txt' , 'tx' , 'json' ) ) . toBe ( 'file.txt' ) ;
1588+ expect ( replaceFileExtension ( 'file.txt' , 't' , 'json' ) ) . toBe ( 'file.txt' ) ;
1589+ expect ( replaceFileExtension ( 'file.json' , 'son' , 'xml' ) ) . toBe ( 'file.json' ) ;
1590+ } ) ;
1591+
1592+ test ( 'Should not replace extension in middle of filename' , ( ) => {
1593+ expect ( replaceFileExtension ( 'file-txt.json' , 'txt' , 'xml' ) ) . toBe ( 'file-txt.json' ) ;
1594+ expect ( replaceFileExtension ( 'sequential.js' , 'seq' , 'seqN' ) ) . toBe ( 'sequential.js' ) ;
1595+ } ) ;
1596+
1597+ test ( 'Should handle empty filename' , ( ) => {
1598+ expect ( replaceFileExtension ( '' , 'txt' , 'json' ) ) . toBe ( '' ) ;
1599+ } ) ;
1600+
1601+ test ( 'Should preserve target extension case' , ( ) => {
1602+ expect ( replaceFileExtension ( 'file.txt' , 'txt' , 'JSON' ) ) . toBe ( 'file.JSON' ) ;
1603+ expect ( replaceFileExtension ( 'test.seq' , 'seq' , 'SeqN' ) ) . toBe ( 'test.SeqN' ) ;
1604+ } ) ;
1605+
1606+ test ( 'Should handle complex file extensions' , ( ) => {
1607+ expect ( replaceFileExtension ( 'archive.tar.gz' , 'gz' , 'bz2' ) ) . toBe ( 'archive.tar.bz2' ) ;
1608+ expect ( replaceFileExtension ( 'script.min.js' , 'min.js' , 'js' ) ) . toBe ( 'script.js' ) ;
1609+ } ) ;
1610+
1611+ test ( 'Should replace when only extension matches without filename' , ( ) => {
1612+ expect ( replaceFileExtension ( '.txt' , 'txt' , 'json' ) ) . toBe ( '.json' ) ;
1613+ } ) ;
1614+ } ) ;
14371615} ) ;
0 commit comments