import { expect, test, describe, beforeAll, afterAll, beforeEach } from 'vitest' import { writeFile, mkdir, rm } from 'fs/promises' import { join, basename } from 'path' import { getAllFilesInPath } from '../../../src/template/utils' describe('getAllFilesInPath', () => { const testDir = join(__dirname, 'folder') beforeAll(async () => { await rm(testDir, { recursive: true, force: true }) await mkdir(testDir, { recursive: true }) }) afterAll(async () => { await rm(testDir, { recursive: true, force: true }) }) beforeEach(async () => { await rm(testDir, { recursive: true, force: true }) await mkdir(testDir, { recursive: true }) }) test('should return files matching a simple pattern', async () => { // Create test files await writeFile(join(testDir, 'file1.txt'), 'content1') await writeFile(join(testDir, 'file2.txt'), 'content2') await writeFile(join(testDir, 'file3.js'), 'content3') const files = await getAllFilesInPath('*.txt', testDir, []) expect(files).toHaveLength(2) expect(files.some((f) => f.fullpath().endsWith('file1.txt'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('file2.txt'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('file3.js'))).toBe(false) }) test('should handle directory patterns recursively', async () => { // Create nested directory structure await mkdir(join(testDir, 'src'), { recursive: true }) await mkdir(join(testDir, 'src', 'components'), { recursive: true }) await mkdir(join(testDir, 'src', 'utils'), { recursive: true }) await writeFile(join(testDir, 'src', 'index.ts'), 'index content') await writeFile( join(testDir, 'src', 'components', 'Button.tsx'), 'button content' ) await writeFile( join(testDir, 'src', 'utils', 'helper.ts'), 'helper content' ) await writeFile(join(testDir, 'README.md'), 'readme content') const files = await getAllFilesInPath('src', testDir, []) expect(files).toHaveLength(6) // 3 files + 3 directories (src, components, utils) expect(files.some((f) => f.fullpath().endsWith('index.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('Button.tsx'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('helper.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('README.md'))).toBe(false) }) test('should respect ignore patterns', async () => { // Create test files await writeFile(join(testDir, 'file1.txt'), 'content1') await writeFile(join(testDir, 'file2.txt'), 'content2') await writeFile(join(testDir, 'temp.txt'), 'temp content') await writeFile(join(testDir, 'backup.txt'), 'backup content') const files = await getAllFilesInPath('*.txt', testDir, [ 'temp*', 'backup*', ]) expect(files).toHaveLength(2) expect(files.some((f) => f.fullpath().endsWith('file1.txt'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('file2.txt'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('temp.txt'))).toBe(false) expect(files.some((f) => f.fullpath().endsWith('backup.txt'))).toBe(false) }) test('should handle complex ignore patterns', async () => { // Create nested structure with various file types await mkdir(join(testDir, 'src'), { recursive: true }) await mkdir(join(testDir, 'src', 'components'), { recursive: true }) await mkdir(join(testDir, 'src', 'utils'), { recursive: true }) await mkdir(join(testDir, 'tests'), { recursive: true }) await writeFile(join(testDir, 'src', 'index.ts'), 'index content') await writeFile( join(testDir, 'src', 'components', 'Button.tsx'), 'button content' ) await writeFile( join(testDir, 'src', 'utils', 'helper.ts'), 'helper content' ) await writeFile(join(testDir, 'tests', 'test.spec.ts'), 'test content') await writeFile( join(testDir, 'src', 'components', 'Button.test.tsx'), 'test content' ) await writeFile( join(testDir, 'src', 'utils', 'helper.spec.ts'), 'spec content' ) const files = await getAllFilesInPath('src', testDir, [ '**/*.test.*', '**/*.spec.*', ]) expect(files).toHaveLength(6) // 3 files + 3 directories (src, components, utils) expect(files.some((f) => f.fullpath().endsWith('index.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('Button.tsx'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('helper.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('Button.test.tsx'))).toBe( false ) expect(files.some((f) => f.fullpath().endsWith('helper.spec.ts'))).toBe( false ) }) test('should handle empty directories', async () => { await mkdir(join(testDir, 'empty'), { recursive: true }) await writeFile(join(testDir, 'file.txt'), 'content') const files = await getAllFilesInPath('empty', testDir, []) expect(files).toHaveLength(1) // The empty directory itself }) test('should handle mixed files and directories', async () => { // Create a mix of files and directories await writeFile(join(testDir, 'file1.txt'), 'content1') await mkdir(join(testDir, 'dir1'), { recursive: true }) await writeFile(join(testDir, 'dir1', 'file2.txt'), 'content2') await writeFile(join(testDir, 'file3.txt'), 'content3') const files = await getAllFilesInPath('*', testDir, []) expect(files).toHaveLength(4) // 3 files + 1 directory expect(files.some((f) => f.fullpath().endsWith('file1.txt'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('file2.txt'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('file3.txt'))).toBe(true) }) test('should handle glob patterns with subdirectories', async () => { // Create nested structure await mkdir(join(testDir, 'src'), { recursive: true }) await mkdir(join(testDir, 'src', 'components'), { recursive: true }) await mkdir(join(testDir, 'src', 'utils'), { recursive: true }) await writeFile(join(testDir, 'src', 'index.ts'), 'index content') await writeFile( join(testDir, 'src', 'components', 'Button.tsx'), 'button content' ) await writeFile( join(testDir, 'src', 'utils', 'helper.ts'), 'helper content' ) await writeFile( join(testDir, 'src', 'components', 'Button.css'), 'css content' ) const files = await getAllFilesInPath('src/**/*', testDir, []) expect(files).toHaveLength(6) // 4 files + 2 directories (components, utils) expect(files.some((f) => f.fullpath().endsWith('index.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('Button.tsx'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('helper.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('Button.css'))).toBe(true) }) test('should handle specific file extensions', async () => { await writeFile(join(testDir, 'file1.ts'), 'ts content') await writeFile(join(testDir, 'file2.js'), 'js content') await writeFile(join(testDir, 'file3.tsx'), 'tsx content') await writeFile(join(testDir, 'file4.css'), 'css content') const files = await getAllFilesInPath('*.ts', testDir, []) expect(files).toHaveLength(1) expect(files.some((f) => f.fullpath().endsWith('file1.ts'))).toBe(true) }) test('should return sorted files', async () => { await writeFile(join(testDir, 'zebra.txt'), 'z content') await writeFile(join(testDir, 'apple.txt'), 'a content') await writeFile(join(testDir, 'banana.txt'), 'b content') const files = await getAllFilesInPath('*.txt', testDir, []) expect(files).toHaveLength(3) // Files must be returned sorted by full path so the files hash is // independent of filesystem traversal order const fileNames = files.map((f) => basename(f.fullpath())) expect(fileNames).toEqual(['apple.txt', 'banana.txt', 'zebra.txt']) }) test('should return nested files sorted by full path', async () => { await mkdir(join(testDir, 'b'), { recursive: true }) await mkdir(join(testDir, 'a'), { recursive: true }) await writeFile(join(testDir, 'zebra.txt'), 'z content') await writeFile(join(testDir, 'b', 'file.txt'), 'b content') await writeFile(join(testDir, 'a', 'file.txt'), 'a content') const files = await getAllFilesInPath('*', testDir, []) const paths = files.map((f) => f.fullpath()) expect(paths).toEqual([...paths].sort()) }) test('should handle no matching files', async () => { await writeFile(join(testDir, 'file.txt'), 'content') const files = await getAllFilesInPath('*.js', testDir, []) expect(files).toHaveLength(0) }) test('should include dotfiles', async () => { // Create regular and dotfiles await writeFile(join(testDir, 'file.txt'), 'content') await writeFile(join(testDir, '.env'), 'SECRET=123') await writeFile(join(testDir, '.gitignore'), 'node_modules') const files = await getAllFilesInPath('*', testDir, []) expect(files).toHaveLength(3) expect(files.some((f) => f.fullpath().endsWith('file.txt'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('.env'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('.gitignore'))).toBe(true) }) test('should include dotfiles in subdirectories', async () => { await mkdir(join(testDir, 'src'), { recursive: true }) await writeFile(join(testDir, 'src', 'index.ts'), 'content') await writeFile(join(testDir, 'src', '.env.local'), 'SECRET=123') const files = await getAllFilesInPath('src', testDir, []) expect(files.some((f) => f.fullpath().endsWith('index.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('.env.local'))).toBe(true) }) test('should include dotdirectories and their contents', async () => { await mkdir(join(testDir, '.hidden'), { recursive: true }) await writeFile(join(testDir, '.hidden', 'config.json'), '{}') await writeFile(join(testDir, 'visible.txt'), 'content') const files = await getAllFilesInPath('*', testDir, []) expect(files.some((f) => f.fullpath().endsWith('.hidden'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('config.json'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('visible.txt'))).toBe(true) }) test('should respect ignore patterns for dotfiles', async () => { await writeFile(join(testDir, '.env'), 'SECRET=123') await writeFile(join(testDir, '.gitignore'), 'node_modules') await writeFile(join(testDir, 'file.txt'), 'content') const files = await getAllFilesInPath('*', testDir, ['.env']) expect(files).toHaveLength(2) expect(files.some((f) => f.fullpath().endsWith('.env'))).toBe(false) expect(files.some((f) => f.fullpath().endsWith('.gitignore'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('file.txt'))).toBe(true) }) test('should handle listing all files in current directory with dot pattern', async () => { // Create a small directory tree inside the test directory await writeFile(join(testDir, 'root.txt'), 'root') await mkdir(join(testDir, 'subdir'), { recursive: true }) await writeFile(join(testDir, 'subdir', 'nested.txt'), 'nested') const files = await getAllFilesInPath('.', testDir, []) // We should get at least the testDir itself (.) plus its children expect(files.length).toBeGreaterThanOrEqual(3) // All returned paths must stay within the testDir - we must not traverse the whole filesystem const allWithinTestDir = files.every((f) => f.fullpath().startsWith(testDir) ) expect(allWithinTestDir).toBe(true) }) test('should handle complex ignore patterns with directories', async () => { // Create a complex structure await mkdir(join(testDir, 'src'), { recursive: true }) await mkdir(join(testDir, 'src', 'components'), { recursive: true }) await mkdir(join(testDir, 'src', 'utils'), { recursive: true }) await mkdir(join(testDir, 'src', 'tests'), { recursive: true }) await mkdir(join(testDir, 'dist'), { recursive: true }) await writeFile(join(testDir, 'src', 'index.ts'), 'index content') await writeFile( join(testDir, 'src', 'components', 'Button.tsx'), 'button content' ) await writeFile( join(testDir, 'src', 'utils', 'helper.ts'), 'helper content' ) await writeFile( join(testDir, 'src', 'tests', 'test.spec.ts'), 'test content' ) await writeFile(join(testDir, 'dist', 'bundle.js'), 'bundle content') await writeFile(join(testDir, 'README.md'), 'readme content') const files = await getAllFilesInPath('src', testDir, [ '**/tests/**', '**/*.spec.*', ]) expect(files).toHaveLength(6) // 3 files + 3 directories (src, components, utils) expect(files.some((f) => f.fullpath().endsWith('index.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('Button.tsx'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('helper.ts'))).toBe(true) expect(files.some((f) => f.fullpath().endsWith('test.spec.ts'))).toBe(false) }) })