Files
2026-07-13 12:47:58 +08:00

329 lines
13 KiB
TypeScript

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)
})
})