1+ import * as assert from 'assert' ;
2+ import * as path from 'path' ;
3+ import * as fs from 'fs' ;
4+ import * as os from 'os' ;
5+ import {
6+ parseGitignoreContent ,
7+ parseGitignoreFile ,
8+ matchesGitignorePatterns ,
9+ collectDirectoryGitignorePatterns ,
10+ collectGitignorePatterns ,
11+ shouldExcludeByGitignore ,
12+ GitignorePattern
13+ } from '../gitignore-utils' ;
14+ import { buildTreeNode } from '../extension' ;
15+
16+ suite ( 'Gitignore Utils Test Suite' , ( ) => {
17+ suite ( 'parseGitignoreContent' , ( ) => {
18+ test ( 'parses empty content correctly' , ( ) => {
19+ const patterns = parseGitignoreContent ( '' ) ;
20+ assert . strictEqual ( patterns . length , 0 , 'Empty content should result in empty patterns array' ) ;
21+ } ) ;
22+
23+ test ( 'parses basic patterns correctly' , ( ) => {
24+ const content = `
25+ # This is a comment
26+ node_modules
27+ *.log
28+ !important.log
29+ /dist
30+ build/
31+ ` ;
32+ const patterns = parseGitignoreContent ( content ) ;
33+
34+ assert . strictEqual ( patterns . length , 5 , 'Should parse 5 patterns' ) ;
35+
36+ // Check node_modules pattern
37+ const nodeModulesPattern = patterns . find ( p => p . pattern === 'node_modules' ) ;
38+ assert . ok ( nodeModulesPattern , 'node_modules pattern should exist' ) ;
39+ assert . strictEqual ( nodeModulesPattern ! . isNegated , false , 'node_modules should not be negated' ) ;
40+ assert . strictEqual ( nodeModulesPattern ! . isDirectory , false , 'node_modules should not be marked as directory' ) ;
41+ assert . strictEqual ( nodeModulesPattern ! . isAbsolute , false , 'node_modules should not be absolute' ) ;
42+
43+ // Check *.log pattern
44+ const logPattern = patterns . find ( p => p . pattern === '*.log' ) ;
45+ assert . ok ( logPattern , '*.log pattern should exist' ) ;
46+ assert . strictEqual ( logPattern ! . isNegated , false , '*.log should not be negated' ) ;
47+
48+ // Check !important.log pattern
49+ const importantLogPattern = patterns . find ( p => p . pattern === 'important.log' ) ;
50+ assert . ok ( importantLogPattern , 'important.log pattern should exist' ) ;
51+ assert . strictEqual ( importantLogPattern ! . isNegated , true , 'important.log should be negated' ) ;
52+
53+ // Check /dist pattern
54+ const distPattern = patterns . find ( p => p . pattern === 'dist' ) ;
55+ assert . ok ( distPattern , 'dist pattern should exist' ) ;
56+ assert . strictEqual ( distPattern ! . isAbsolute , true , 'dist should be absolute' ) ;
57+
58+ // Check build/ pattern
59+ const buildPattern = patterns . find ( p => p . pattern === 'build/' ) ;
60+ assert . ok ( buildPattern , 'build/ pattern should exist' ) ;
61+ assert . strictEqual ( buildPattern ! . isDirectory , true , 'build/ should be marked as directory' ) ;
62+ } ) ;
63+ } ) ;
64+
65+ suite ( 'matchesGitignorePatterns' , ( ) => {
66+ test ( 'matches basic patterns correctly' , ( ) => {
67+ const patterns : GitignorePattern [ ] = [
68+ { pattern : 'node_modules' , isNegated : false , isDirectory : false , isAbsolute : false } ,
69+ { pattern : '*.log' , isNegated : false , isDirectory : false , isAbsolute : false } ,
70+ { pattern : 'important.log' , isNegated : true , isDirectory : false , isAbsolute : false } ,
71+ { pattern : 'dist' , isNegated : false , isDirectory : false , isAbsolute : true } ,
72+ { pattern : 'build/' , isNegated : false , isDirectory : true , isAbsolute : false }
73+ ] ;
74+
75+ // Should match
76+ assert . strictEqual (
77+ matchesGitignorePatterns ( 'node_modules' , patterns , false ) ,
78+ true ,
79+ 'node_modules should match'
80+ ) ;
81+
82+ assert . strictEqual (
83+ matchesGitignorePatterns ( 'logs/error.log' , patterns , false ) ,
84+ true ,
85+ '*.log should match error.log'
86+ ) ;
87+
88+ assert . strictEqual (
89+ matchesGitignorePatterns ( 'dist' , patterns , false ) ,
90+ true ,
91+ 'dist should match'
92+ ) ;
93+
94+ assert . strictEqual (
95+ matchesGitignorePatterns ( 'build' , patterns , true ) ,
96+ true ,
97+ 'build/ should match build directory'
98+ ) ;
99+
100+ // Should not match
101+ assert . strictEqual (
102+ matchesGitignorePatterns ( 'important.log' , patterns , false ) ,
103+ false ,
104+ 'important.log should not match due to negation'
105+ ) ;
106+
107+ assert . strictEqual (
108+ matchesGitignorePatterns ( 'src' , patterns , false ) ,
109+ false ,
110+ 'src should not match any pattern'
111+ ) ;
112+
113+ assert . strictEqual (
114+ matchesGitignorePatterns ( 'build' , patterns , false ) ,
115+ false ,
116+ 'build/ should not match build file (only directory)'
117+ ) ;
118+ } ) ;
119+ } ) ;
120+
121+ suite ( 'Integration with file system' , ( ) => {
122+ let tmpDir : string ;
123+
124+ suiteSetup ( ( ) => {
125+ // Create a temp directory
126+ tmpDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'gitignore-test-' ) ) ;
127+
128+ // Create a .gitignore file
129+ fs . writeFileSync ( path . join ( tmpDir , '.gitignore' ) , `
130+ # Test gitignore file
131+ node_modules
132+ *.log
133+ !important.log
134+ /dist
135+ build/
136+ ` ) ;
137+
138+ // Create some files and directories
139+ fs . writeFileSync ( path . join ( tmpDir , 'file.txt' ) , 'content' ) ;
140+ fs . writeFileSync ( path . join ( tmpDir , 'error.log' ) , 'error' ) ;
141+ fs . writeFileSync ( path . join ( tmpDir , 'important.log' ) , 'important' ) ;
142+
143+ fs . mkdirSync ( path . join ( tmpDir , 'src' ) ) ;
144+ fs . writeFileSync ( path . join ( tmpDir , 'src' , 'index.js' ) , 'console.log("Hello")' ) ;
145+
146+ fs . mkdirSync ( path . join ( tmpDir , 'dist' ) ) ;
147+ fs . writeFileSync ( path . join ( tmpDir , 'dist' , 'bundle.js' ) , 'bundled code' ) ;
148+
149+ fs . mkdirSync ( path . join ( tmpDir , 'build' ) ) ;
150+ fs . writeFileSync ( path . join ( tmpDir , 'build' , 'output.js' ) , 'output' ) ;
151+
152+ fs . mkdirSync ( path . join ( tmpDir , 'node_modules' ) ) ;
153+ fs . writeFileSync ( path . join ( tmpDir , 'node_modules' , 'package.json' ) , '{}' ) ;
154+
155+ // Create a subdirectory with its own .gitignore
156+ fs . mkdirSync ( path . join ( tmpDir , 'subdir' ) ) ;
157+ fs . writeFileSync ( path . join ( tmpDir , 'subdir' , '.gitignore' ) , `
158+ # Subdir gitignore
159+ *.txt
160+ !important.txt
161+ ` ) ;
162+ fs . writeFileSync ( path . join ( tmpDir , 'subdir' , 'file.txt' ) , 'content' ) ;
163+ fs . writeFileSync ( path . join ( tmpDir , 'subdir' , 'important.txt' ) , 'important' ) ;
164+ fs . writeFileSync ( path . join ( tmpDir , 'subdir' , 'code.js' ) , 'code' ) ;
165+ } ) ;
166+
167+ suiteTeardown ( ( ) => {
168+ // Clean up
169+ fs . rmSync ( tmpDir , { recursive : true , force : true } ) ;
170+ } ) ;
171+
172+ test ( 'parseGitignoreFile reads file correctly' , ( ) => {
173+ const patterns = parseGitignoreFile ( path . join ( tmpDir , '.gitignore' ) ) ;
174+ assert . ok ( patterns . length > 0 , 'Should parse patterns from file' ) ;
175+ assert . ok ( patterns . some ( p => p . pattern === 'node_modules' ) , 'Should include node_modules pattern' ) ;
176+ } ) ;
177+
178+ test ( 'collectDirectoryGitignorePatterns collects patterns from directory' , ( ) => {
179+ const patterns = collectDirectoryGitignorePatterns ( tmpDir ) ;
180+ assert . ok ( patterns . length > 0 , 'Should collect patterns from directory' ) ;
181+ assert . ok ( patterns . some ( p => p . pattern === 'node_modules' ) , 'Should include node_modules pattern' ) ;
182+ } ) ;
183+
184+ test ( 'collectGitignorePatterns collects patterns from directory and parents' , ( ) => {
185+ const patterns = collectGitignorePatterns ( path . join ( tmpDir , 'subdir' ) ) ;
186+ assert . ok ( patterns . length > 0 , 'Should collect patterns from directory and parents' ) ;
187+ assert . ok ( patterns . some ( p => p . pattern === '*.txt' ) , 'Should include *.txt pattern from subdir' ) ;
188+ } ) ;
189+
190+ test ( 'shouldExcludeByGitignore correctly identifies excluded files' , ( ) => {
191+ // Files that should be excluded
192+ assert . strictEqual (
193+ shouldExcludeByGitignore (
194+ path . join ( tmpDir , 'node_modules' ) ,
195+ tmpDir ,
196+ collectDirectoryGitignorePatterns ( tmpDir ) ,
197+ true
198+ ) ,
199+ true ,
200+ 'node_modules directory should be excluded'
201+ ) ;
202+
203+ assert . strictEqual (
204+ shouldExcludeByGitignore (
205+ path . join ( tmpDir , 'error.log' ) ,
206+ tmpDir ,
207+ collectDirectoryGitignorePatterns ( tmpDir ) ,
208+ false
209+ ) ,
210+ true ,
211+ 'error.log should be excluded'
212+ ) ;
213+
214+ assert . strictEqual (
215+ shouldExcludeByGitignore (
216+ path . join ( tmpDir , 'dist' ) ,
217+ tmpDir ,
218+ collectDirectoryGitignorePatterns ( tmpDir ) ,
219+ true
220+ ) ,
221+ true ,
222+ 'dist directory should be excluded'
223+ ) ;
224+
225+ assert . strictEqual (
226+ shouldExcludeByGitignore (
227+ path . join ( tmpDir , 'build' ) ,
228+ tmpDir ,
229+ collectDirectoryGitignorePatterns ( tmpDir ) ,
230+ true
231+ ) ,
232+ true ,
233+ 'build directory should be excluded'
234+ ) ;
235+
236+ // Files that should not be excluded
237+ assert . strictEqual (
238+ shouldExcludeByGitignore (
239+ path . join ( tmpDir , 'important.log' ) ,
240+ tmpDir ,
241+ collectDirectoryGitignorePatterns ( tmpDir ) ,
242+ false
243+ ) ,
244+ false ,
245+ 'important.log should not be excluded due to negation'
246+ ) ;
247+
248+ assert . strictEqual (
249+ shouldExcludeByGitignore (
250+ path . join ( tmpDir , 'file.txt' ) ,
251+ tmpDir ,
252+ collectDirectoryGitignorePatterns ( tmpDir ) ,
253+ false
254+ ) ,
255+ false ,
256+ 'file.txt should not be excluded'
257+ ) ;
258+
259+ assert . strictEqual (
260+ shouldExcludeByGitignore (
261+ path . join ( tmpDir , 'src' ) ,
262+ tmpDir ,
263+ collectDirectoryGitignorePatterns ( tmpDir ) ,
264+ true
265+ ) ,
266+ false ,
267+ 'src directory should not be excluded'
268+ ) ;
269+ } ) ;
270+
271+ test ( 'buildTreeNode respects gitignore patterns' , ( ) => {
272+ const tree = buildTreeNode ( tmpDir ) ;
273+
274+ // Check that the tree has the correct structure
275+ assert . strictEqual ( tree . name , path . basename ( tmpDir ) , 'Root node should have correct name' ) ;
276+ assert . strictEqual ( tree . type , 'folder' , 'Root node should be a folder' ) ;
277+
278+ // Find nodes for various files/directories
279+ const findNode = ( name : string ) => {
280+ return tree . children ! . find ( node => node . name === name ) ;
281+ } ;
282+
283+ // Files/directories that should be included but unchecked
284+ const nodeModulesNode = findNode ( 'node_modules' ) ;
285+ assert . ok ( nodeModulesNode , 'node_modules should be in the tree' ) ;
286+ assert . strictEqual ( nodeModulesNode ! . checked , false , 'node_modules should be unchecked' ) ;
287+
288+ const errorLogNode = findNode ( 'error.log' ) ;
289+ assert . ok ( errorLogNode , 'error.log should be in the tree' ) ;
290+ assert . strictEqual ( errorLogNode ! . checked , false , 'error.log should be unchecked' ) ;
291+
292+ const distNode = findNode ( 'dist' ) ;
293+ assert . ok ( distNode , 'dist should be in the tree' ) ;
294+ assert . strictEqual ( distNode ! . checked , false , 'dist should be unchecked' ) ;
295+
296+ const buildNode = findNode ( 'build' ) ;
297+ assert . ok ( buildNode , 'build should be in the tree' ) ;
298+ assert . strictEqual ( buildNode ! . checked , false , 'build should be unchecked' ) ;
299+
300+ // Files/directories that should be included and checked
301+ const importantLogNode = findNode ( 'important.log' ) ;
302+ assert . ok ( importantLogNode , 'important.log should be in the tree' ) ;
303+ assert . strictEqual ( importantLogNode ! . checked , true , 'important.log should be checked' ) ;
304+
305+ const fileTxtNode = findNode ( 'file.txt' ) ;
306+ assert . ok ( fileTxtNode , 'file.txt should be in the tree' ) ;
307+ assert . strictEqual ( fileTxtNode ! . checked , true , 'file.txt should be checked' ) ;
308+
309+ const srcNode = findNode ( 'src' ) ;
310+ assert . ok ( srcNode , 'src should be in the tree' ) ;
311+ assert . strictEqual ( srcNode ! . checked , true , 'src should be checked' ) ;
312+
313+ // Check subdirectory with its own gitignore
314+ const subdirNode = findNode ( 'subdir' ) ;
315+ assert . ok ( subdirNode , 'subdir should be in the tree' ) ;
316+ assert . strictEqual ( subdirNode ! . checked , true , 'subdir should be checked' ) ;
317+
318+ // Find nodes in the subdirectory
319+ const subdirChildren = subdirNode ! . children ! ;
320+ const findSubdirNode = ( name : string ) => {
321+ return subdirChildren . find ( node => node . name === name ) ;
322+ } ;
323+
324+ const subdirFileTxtNode = findSubdirNode ( 'file.txt' ) ;
325+ assert . ok ( subdirFileTxtNode , 'subdir/file.txt should be in the tree' ) ;
326+ assert . strictEqual ( subdirFileTxtNode ! . checked , false , 'subdir/file.txt should be unchecked' ) ;
327+
328+ const subdirImportantTxtNode = findSubdirNode ( 'important.txt' ) ;
329+ assert . ok ( subdirImportantTxtNode , 'subdir/important.txt should be in the tree' ) ;
330+ assert . strictEqual ( subdirImportantTxtNode ! . checked , true , 'subdir/important.txt should be checked' ) ;
331+
332+ const subdirCodeJsNode = findSubdirNode ( 'code.js' ) ;
333+ assert . ok ( subdirCodeJsNode , 'subdir/code.js should be in the tree' ) ;
334+ assert . strictEqual ( subdirCodeJsNode ! . checked , true , 'subdir/code.js should be checked' ) ;
335+ } ) ;
336+ } ) ;
337+ } ) ;
0 commit comments