fix: 修复通配符匹配问题
This commit is contained in:
@@ -76,125 +76,248 @@ describe('测试 FileDiscovery 类', () => {
|
||||
expect(files[0]).toBe('test-dir/file1.js');
|
||||
});
|
||||
|
||||
it('应该使用通配符模式查找文件', async () => {
|
||||
it('应该找到通配符(*.js)形式的文件', async () => {
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
const files = await discovery.findFiles(['test-dir/*.js']);
|
||||
expect(files).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('应该递归查找文件', async () => {
|
||||
it('应该找到递归通配符(**/*.js)形式的文件', async () => {
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
const files = await discovery.findFiles(['test-dir/**/*.js']);
|
||||
expect(files).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('应该处理空目录', async () => {
|
||||
it('应该找到递归通配符开头与文件名(**/config.json)形式的文件', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
const files = await discovery.findFiles([tempDir]);
|
||||
// 创建多个目录层级的同名文件
|
||||
const configFiles = [
|
||||
path.join(tempDir, 'config.json'),
|
||||
path.join(tempDir, 'src', 'config.json'),
|
||||
path.join(tempDir, 'src', 'utils', 'config.json'),
|
||||
path.join(tempDir, 'lib', 'config.json'),
|
||||
path.join(tempDir, 'test', 'config.json'),
|
||||
];
|
||||
|
||||
expect(files).toEqual([]);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
// 创建一些其他文件用于验证不会误匹配
|
||||
const otherFiles = [
|
||||
path.join(tempDir, 'src', 'app.js'),
|
||||
path.join(tempDir, 'src', 'utils', 'helper.js'),
|
||||
path.join(tempDir, 'lib', 'main.js'),
|
||||
path.join(tempDir, 'test', 'test.js'),
|
||||
];
|
||||
|
||||
it('应该处理无效模式', async () => {
|
||||
const discovery = new FileDiscovery();
|
||||
const files = await discovery.findFiles(['nonexistent/**/*.txt']);
|
||||
|
||||
expect(files).toEqual([]);
|
||||
});
|
||||
|
||||
it('应该处理混合有效和无效模式', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await createTestFile(testFile, 'content');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '*.txt'), 'nonexistent/**/*.txt']);
|
||||
|
||||
expect(files).toContain(testFile);
|
||||
cleanupTestFiles([testFile]);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
it('应该处理无权限的目录', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const restrictedDir = path.join(tempDir, 'restricted');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
try {
|
||||
fs.mkdirSync(restrictedDir, { mode: 0o000 });
|
||||
|
||||
const files = await discovery.findFiles([restrictedDir + '/*.txt']);
|
||||
|
||||
expect(files).toEqual([]);
|
||||
} catch (error) {
|
||||
// 某些系统可能不允许创建无权限目录,这是预期的
|
||||
expect(error.code).toBe('EACCES');
|
||||
} finally {
|
||||
// 清理:先恢复权限再删除
|
||||
try {
|
||||
fs.chmodSync(restrictedDir, 0o755);
|
||||
cleanupTestDirs(tempDir);
|
||||
} catch (cleanupError) {
|
||||
// 忽略清理错误
|
||||
}
|
||||
// 创建所有文件
|
||||
for (const file of [...configFiles, ...otherFiles]) {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
await createTestFile(file, '{"test": "content"}');
|
||||
}
|
||||
});
|
||||
|
||||
it('应该忽略隐藏文件(默认)', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const hiddenFile = path.join(tempDir, '.hidden.txt');
|
||||
const visibleFile = path.join(tempDir, 'visible.txt');
|
||||
const discovery = new FileDiscovery();
|
||||
// 测试 tempDir 目录下的 **/config.json 模式(限制在测试目录内)
|
||||
const files = await discovery.findFiles([path.join(tempDir, '**/config.json')]);
|
||||
|
||||
await createTestFile(hiddenFile, 'hidden content');
|
||||
await createTestFile(visibleFile, 'visible content');
|
||||
// 应该找到所有的 config.json 文件
|
||||
expect(files).toHaveLength(configFiles.length);
|
||||
for (const configFile of configFiles) {
|
||||
expect(files).toContain(configFile);
|
||||
}
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '*.txt')]);
|
||||
|
||||
expect(files).toContain(visibleFile);
|
||||
expect(files).not.toContain(hiddenFile);
|
||||
|
||||
cleanupTestFiles(hiddenFile, visibleFile);
|
||||
// 清理测试文件
|
||||
cleanupTestFiles([...configFiles, ...otherFiles]);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
it('应该处理空文件', async () => {
|
||||
it('应该找到递归通配符开头与扩展名(**/*.md)形式的文件', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const emptyFile = path.join(tempDir, 'empty.txt');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
await createTestFile(emptyFile, '');
|
||||
// 创建多层目录结构中的 .md 文件
|
||||
const mdFiles = [
|
||||
path.join(tempDir, 'README.md'),
|
||||
path.join(tempDir, 'docs', 'guide.md'),
|
||||
path.join(tempDir, 'src', 'docs', 'api.md'),
|
||||
path.join(tempDir, 'lib', 'components', 'button.md'),
|
||||
path.join(tempDir, 'test', 'integration', 'tests.md'),
|
||||
];
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '*.txt')]);
|
||||
// 创建一些其他扩展名的文件用于验证不会误匹配
|
||||
const otherFiles = [
|
||||
path.join(tempDir, 'src', 'main.js'),
|
||||
path.join(tempDir, 'src', 'styles.css'),
|
||||
path.join(tempDir, 'docs', 'config.json'),
|
||||
path.join(tempDir, 'test', 'test.py'),
|
||||
];
|
||||
|
||||
expect(files).toContain(emptyFile);
|
||||
// 创建所有文件
|
||||
for (const file of [...mdFiles, ...otherFiles]) {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
await createTestFile(file, '# Test Content');
|
||||
}
|
||||
|
||||
cleanupTestFiles(emptyFile);
|
||||
// 测试 tempDir 目录下的 **/*.md 模式(限制在测试目录内)
|
||||
const files = await discovery.findFiles([path.join(tempDir, '**/*.md')]);
|
||||
|
||||
// 应该找到所有的 .md 文件
|
||||
expect(files).toHaveLength(mdFiles.length);
|
||||
for (const mdFile of mdFiles) {
|
||||
expect(files).toContain(mdFile);
|
||||
}
|
||||
|
||||
// 清理测试文件
|
||||
cleanupTestFiles([...mdFiles, ...otherFiles]);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
it('应该处理嵌套很深的目录', async () => {
|
||||
it('应该正确处理递归通配符前缀的复杂模式', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const deepFile = path.join(tempDir, 'level1', 'level2', 'level3', 'deep.txt');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
fs.mkdirSync(path.dirname(deepFile), { recursive: true });
|
||||
await createTestFile(deepFile, 'deep content');
|
||||
// 创建测试文件结构
|
||||
const testFiles = [
|
||||
path.join(tempDir, 'src', 'components', 'Button.js'),
|
||||
path.join(tempDir, 'src', 'components', 'Input.js'),
|
||||
path.join(tempDir, 'src', 'utils', 'helpers.js'),
|
||||
path.join(tempDir, 'lib', 'vendor', 'jquery.js'),
|
||||
path.join(tempDir, 'test', 'unit', 'Button.test.js'),
|
||||
path.join(tempDir, 'test', 'integration', 'App.test.js'),
|
||||
];
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '**/*.txt')]);
|
||||
// 创建一些非 JS 文件
|
||||
const nonJSFiles = [
|
||||
path.join(tempDir, 'src', 'styles.css'),
|
||||
path.join(tempDir, 'docs', 'README.md'),
|
||||
path.join(tempDir, 'config.json'),
|
||||
];
|
||||
|
||||
expect(files).toContain(deepFile);
|
||||
// 创建所有文件
|
||||
for (const file of [...testFiles, ...nonJSFiles]) {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
await createTestFile(file, 'console.log("test");');
|
||||
}
|
||||
|
||||
cleanupTestFiles(deepFile);
|
||||
// 测试 tempDir 目录下的 **/*.js 模式(限制在测试目录内)
|
||||
const files = await discovery.findFiles([path.join(tempDir, '**/*.js')]);
|
||||
|
||||
// 应该找到所有的 .js 文件
|
||||
expect(files).toHaveLength(testFiles.length);
|
||||
for (const jsFile of testFiles) {
|
||||
expect(files).toContain(jsFile);
|
||||
}
|
||||
|
||||
// 清理测试文件
|
||||
cleanupTestFiles([...testFiles, ...nonJSFiles]);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理空目录', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
const files = await discovery.findFiles([tempDir]);
|
||||
|
||||
expect(files).toEqual([]);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
it('应该处理无效模式', async () => {
|
||||
const discovery = new FileDiscovery();
|
||||
const files = await discovery.findFiles(['nonexistent/**/*.txt']);
|
||||
|
||||
expect(files).toEqual([]);
|
||||
});
|
||||
|
||||
it('应该处理混合有效和无效模式', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await createTestFile(testFile, 'content');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '*.txt'), 'nonexistent/**/*.txt']);
|
||||
|
||||
expect(files).toContain(testFile);
|
||||
cleanupTestFiles([testFile]);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
it('应该处理无权限的目录', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const restrictedDir = path.join(tempDir, 'restricted');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
try {
|
||||
fs.mkdirSync(restrictedDir, { mode: 0o000 });
|
||||
|
||||
const files = await discovery.findFiles([restrictedDir + '/*.txt']);
|
||||
|
||||
expect(files).toEqual([]);
|
||||
} catch (error) {
|
||||
// 某些系统可能不允许创建无权限目录,这是预期的
|
||||
expect(error.code).toBe('EACCES');
|
||||
} finally {
|
||||
// 清理:先恢复权限再删除
|
||||
try {
|
||||
fs.chmodSync(restrictedDir, 0o755);
|
||||
cleanupTestDirs(tempDir);
|
||||
} catch (cleanupError) {
|
||||
// 忽略清理错误
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('应该忽略隐藏文件(默认)', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const hiddenFile = path.join(tempDir, '.hidden.txt');
|
||||
const visibleFile = path.join(tempDir, 'visible.txt');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
await createTestFile(hiddenFile, 'hidden content');
|
||||
await createTestFile(visibleFile, 'visible content');
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '*.txt')]);
|
||||
|
||||
expect(files).toContain(visibleFile);
|
||||
expect(files).not.toContain(hiddenFile);
|
||||
|
||||
cleanupTestFiles(hiddenFile, visibleFile);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
it('应该处理空文件', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const emptyFile = path.join(tempDir, 'empty.txt');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
await createTestFile(emptyFile, '');
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '*.txt')]);
|
||||
|
||||
expect(files).toContain(emptyFile);
|
||||
|
||||
cleanupTestFiles(emptyFile);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
it('应该处理嵌套很深的目录', async () => {
|
||||
const tempDir = await createTestDir();
|
||||
const deepFile = path.join(tempDir, 'level1', 'level2', 'level3', 'deep.txt');
|
||||
const discovery = new FileDiscovery();
|
||||
|
||||
fs.mkdirSync(path.dirname(deepFile), { recursive: true });
|
||||
await createTestFile(deepFile, 'deep content');
|
||||
|
||||
const files = await discovery.findFiles([path.join(tempDir, '**/*.txt')]);
|
||||
|
||||
expect(files).toContain(deepFile);
|
||||
|
||||
cleanupTestFiles(deepFile);
|
||||
cleanupTestDirs(tempDir);
|
||||
});
|
||||
|
||||
describe('测试 FileDiscovery.validateFiles 方法', () => {
|
||||
const existingFile = 'existing-file.txt';
|
||||
const nonExistingFile = 'non-existing-file.txt';
|
||||
|
||||
Reference in New Issue
Block a user