filetree-pro
Version:
A powerful file tree generator for VS Code and Cursor. Generate beautiful file trees in multiple formats with smart exclusions and custom configurations.
452 lines (348 loc) • 11 kB
text/typescript
import * as vscode from 'vscode';
jest.mock('vscode', () => ({
workspace: {
fs: {
readFile: jest.fn(),
stat: jest.fn(),
readDirectory: jest.fn(),
},
getConfiguration: jest.fn(() => ({
get: jest.fn(() => []),
})),
},
window: {
showQuickPick: jest.fn(),
showInputBox: jest.fn(),
showInformationMessage: jest.fn(),
showErrorMessage: jest.fn(),
},
Uri: {
file: jest.fn(path => ({ fsPath: path })),
},
FileType: {
File: 1,
Directory: 2,
},
}));
describe('📁 .gitignore Integration', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('readGitignore Function', () => {
it('should read .gitignore file and parse patterns correctly', async () => {
const mockGitignoreContent = `
node_modules/
dist/
build/
*.log
npm-debug.log*
.env
.env.local
.vscode/
.idea/
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual([
'node_modules/',
'dist/',
'build/',
'*.log',
'npm-debug.log*',
'.env',
'.env.local',
'.vscode/',
'.idea/',
]);
});
it('should handle missing .gitignore file gracefully', async () => {
(vscode.workspace.fs.readFile as jest.Mock).mockRejectedValue(new Error('File not found'));
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual([]);
});
it('should filter out comments and empty lines', async () => {
const mockGitignoreContent = `
node_modules/
dist/
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual(['node_modules/', 'dist/']);
});
it('should handle empty .gitignore file', async () => {
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(Buffer.from(''));
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual([]);
});
it('should handle .gitignore with only comments', async () => {
const mockGitignoreContent = `
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual([]);
});
it('should handle .gitignore with mixed content', async () => {
const mockGitignoreContent = `
node_modules/
dist/
*.log
!important.log
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual(['node_modules/', 'dist/', '*.log', '!important.log']);
});
});
describe('Integration with shouldExclude', () => {
it('should combine .gitignore patterns with default exclusions', async () => {
const mockGitignoreContent = `
node_modules/
dist/
*.log
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
// Mock user configuration
const mockConfig = {
get: jest.fn(() => ['.env', '*.tmp']),
};
(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue(mockConfig);
const { shouldExclude } = await import('../commands/commands');
// Test that both .gitignore and default exclusions are respected
expect(shouldExclude('node_modules')).toBe(true);
expect(shouldExclude('dist')).toBe(true);
expect(shouldExclude('.env')).toBe(true);
expect(shouldExclude('temp.tmp')).toBe(true);
expect(shouldExclude('src')).toBe(false);
});
it('should handle case-insensitive matching', async () => {
const mockGitignoreContent = `
NODE_MODULES/
DIST/
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { shouldExclude } = await import('../commands/commands');
expect(shouldExclude('node_modules')).toBe(true);
expect(shouldExclude('NODE_MODULES')).toBe(true);
expect(shouldExclude('dist')).toBe(true);
expect(shouldExclude('DIST')).toBe(true);
});
it('should handle wildcard patterns', async () => {
const mockGitignoreContent = `
*.log
*.tmp
*.cache
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { shouldExclude } = await import('../commands/commands');
expect(shouldExclude('app.log')).toBe(true);
expect(shouldExclude('temp.tmp')).toBe(true);
expect(shouldExclude('cache.cache')).toBe(true);
expect(shouldExclude('important.txt')).toBe(false);
});
});
describe('Edge Cases', () => {
it('should handle .gitignore with special characters', async () => {
const mockGitignoreContent = `
my file.txt
my folder/
.config
.env.local
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual(['my file.txt', 'my folder/', '.config', '.env.local']);
});
it('should handle .gitignore with leading/trailing whitespace', async () => {
const mockGitignoreContent = `
node_modules/
dist/
*.log
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual(['node_modules/', 'dist/', '*.log']);
});
it('should handle file system errors gracefully', async () => {
(vscode.workspace.fs.readFile as jest.Mock).mockRejectedValue(new Error('Permission denied'));
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual([]);
});
it('should handle network errors gracefully', async () => {
(vscode.workspace.fs.readFile as jest.Mock).mockRejectedValue(new Error('Network error'));
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toEqual([]);
});
});
describe('Performance Tests', () => {
it('should handle large .gitignore files efficiently', async () => {
const largeGitignoreContent = Array.from({ length: 1000 }, (_, i) => `file${i}.txt`).join(
'\n'
);
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(largeGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const startTime = Date.now();
const patterns = await readGitignore('/test/path');
const endTime = Date.now();
expect(patterns).toHaveLength(1000);
expect(endTime - startTime).toBeLessThan(100); // Should complete in under 100ms
});
it('should handle multiple concurrent reads', async () => {
const mockGitignoreContent = 'node_modules/\ndist/\n*.log';
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(mockGitignoreContent)
);
const { readGitignore } = await import('../commands/commands');
const promises = Array.from({ length: 10 }, () => readGitignore('/test/path'));
const results = await Promise.all(promises);
results.forEach(result => {
expect(result).toEqual(['node_modules/', 'dist/', '*.log']);
});
});
});
describe('Real-world Scenarios', () => {
it('should handle typical Node.js .gitignore', async () => {
const typicalNodeGitignore = `
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pids
*.pid
*.seed
*.pid.lock
coverage/
.nyc_output
jspm_packages/
.npm
.node_repl_history
*.tgz
.yarn-integrity
.env
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(typicalNodeGitignore)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toContain('node_modules/');
expect(patterns).toContain('npm-debug.log*');
expect(patterns).toContain('coverage/');
expect(patterns).toContain('.env');
expect(patterns.length).toBeGreaterThan(10);
});
it('should handle typical Python .gitignore', async () => {
const typicalPythonGitignore = `
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
*.manifest
*.spec
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
`.trim();
(vscode.workspace.fs.readFile as jest.Mock).mockResolvedValue(
Buffer.from(typicalPythonGitignore)
);
const { readGitignore } = await import('../commands/commands');
const patterns = await readGitignore('/test/path');
expect(patterns).toContain('__pycache__/');
expect(patterns).toContain('*.py[cod]');
expect(patterns).toContain('build/');
expect(patterns).toContain('.env');
expect(patterns).toContain('venv/');
expect(patterns.length).toBeGreaterThan(15);
});
});
});