diff --git a/src/pick.ts b/src/pick.ts index 72ccb4f..a2e1c48 100644 --- a/src/pick.ts +++ b/src/pick.ts @@ -149,9 +149,9 @@ export async function pickFiles(options: PickOptions): Promise { } /** - * Get file content with markdown format (copied from main.ts but uses cwd) + * Get file content with markdown format */ -async function getFileContent( +export async function getFileContent( filePath: string, maxLines?: number, showLineNumbers?: boolean, diff --git a/tests/apply.test.ts b/tests/apply.test.ts index b26ab0f..39e00b1 100644 --- a/tests/apply.test.ts +++ b/tests/apply.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { writeFile, mkdir, stat, readFile } from 'fs/promises'; +import { writeFile, mkdir, stat } from 'fs/promises'; +import path from 'path'; import { parseCodeBlocks, applyFiles } from '../src/apply'; vi.mock('fs/promises'); @@ -73,9 +74,10 @@ And more ${bt(4)}more code${bt(4)} }); it('identifies binary files', () => { - const input = `${bt(3)}png\n// image.png\n// [BINARY FILE] - Size: 1.000 MB\n${bt(3)}`; + const input = `${bt(3)}png\n// image.png\n\n\n// [BINARY FILE] - Size: 1.000 MB\n${bt(3)}`; const files = parseCodeBlocks(input); + console.log(files); expect(files).toHaveLength(1); expect(files[0].isBinary).toBe(true); }); @@ -210,7 +212,6 @@ describe('applyFiles', () => { filePath: 'src/index.ts', content: 'console.log("hello");', isBinary: false, - isTruncated: false, }, ]; @@ -232,7 +233,6 @@ describe('applyFiles', () => { filePath: 'src/index.ts', content: 'console.log("updated");', isBinary: false, - isTruncated: false, }, ]; @@ -248,14 +248,13 @@ describe('applyFiles', () => { filePath: 'image.png', content: '// [BINARY FILE]', isBinary: true, - isTruncated: false, }, ]; const result = await applyFiles(files); expect(result.skipped).toHaveLength(1); - expect(result.skipped[0]).toContain('image.png'); + expect(result.skipped[0].path).toContain('image.png'); expect(writeFile).not.toHaveBeenCalled(); }); @@ -269,7 +268,6 @@ describe('applyFiles', () => { filePath: 'large.ts', content: 'line1\n// ... (100 more lines truncated)', isBinary: false, - isTruncated: true, }, ]; @@ -288,15 +286,17 @@ describe('applyFiles', () => { filePath: 'src/index.ts', content: 'content', isBinary: false, - isTruncated: false, }, ]; await applyFiles(files, '/custom/path'); - expect(mkdir).toHaveBeenCalledWith('/custom/path/src', { recursive: true }); + expect(mkdir).toHaveBeenCalledWith( + expect.stringContaining(path.join('custom', 'path', 'src')), + { recursive: true }, + ); expect(writeFile).toHaveBeenCalledWith( - '/custom/path/src/index.ts', + expect.stringContaining(path.join('custom', 'path', 'src', 'index.ts')), 'content', 'utf-8', ); @@ -312,13 +312,15 @@ describe('applyFiles', () => { filePath: 'src/deep/nested/file.ts', content: 'content', isBinary: false, - isTruncated: false, }, ]; await applyFiles(files); - expect(mkdir).toHaveBeenCalledWith('src/deep/nested', { recursive: true }); + expect(mkdir).toHaveBeenCalledWith( + expect.stringContaining(path.join('src', 'deep', 'nested')), + { recursive: true }, + ); }); it('handles multiple files with mixed states', async () => { @@ -334,19 +336,16 @@ describe('applyFiles', () => { filePath: 'new.ts', content: 'new', isBinary: false, - isTruncated: false, }, { filePath: 'existing.ts', content: 'updated', isBinary: false, - isTruncated: false, }, { filePath: 'image.png', content: '// [BINARY FILE]', isBinary: true, - isTruncated: false, }, ]; diff --git a/tests/main.test.ts b/tests/main.test.ts index 131ca46..f3ea39f 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1,53 +1,43 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { readFile, stat } from 'fs/promises'; +import glob from 'fast-glob'; import { isBinaryFile } from '../src/utils/binary'; import { addLineNumbers, formatSizeInMB } from '../src/utils/pipes'; -import { findGitignoreFiles, loadGitignoreRules } from '../src/utils/gitignore'; -import { - filterByGitignore, - findMaxConsecutiveBackticks, - getFileContent, - main, -} from '../src/main'; +import { filterByIgnoreFile } from '../src/utils/ignore'; +import { findMaxConsecutiveBackticks, getFileContent } from '../src/pick'; +import { main } from '../src/main'; vi.mock('fs/promises'); -vi.mock('fast-glob', () => ({ default: async () => [] })); -vi.mock('clipboardy'); +vi.mock('fast-glob', () => ({ default: vi.fn() })); +vi.mock('clipboardy', () => ({ default: { read: vi.fn(), write: vi.fn() } })); vi.mock('../src/utils/binary'); vi.mock('../src/utils/pipes'); -vi.mock('../src/utils/gitignore'); describe('filterByGitignore', () => { beforeEach(() => { vi.resetAllMocks(); }); - // it('returns all files when no gitignore files found', async () => { - vi.mocked(findGitignoreFiles).mockResolvedValue([]); + vi.mocked(glob).mockResolvedValue([]); const files = ['file1.ts', 'file2.ts']; - const result = await filterByGitignore(files); + const result = await filterByIgnoreFile(files, '.gitignore'); expect(result).toEqual(files); }); - // it('filters files based on gitignore rules', async () => { - vi.mocked(findGitignoreFiles).mockResolvedValue(['.gitignore']); - vi.mocked(loadGitignoreRules).mockResolvedValue(['node_modules']); - - const mockIg = { ignores: vi.fn().mockReturnValue(true) }; - vi.doMock('ignore', () => () => mockIg); + vi.mocked(glob).mockResolvedValue(['.gitignore']); + vi.mocked(readFile).mockResolvedValue('node_modules'); const files = ['src/index.ts', 'node_modules/test.js']; - const result = await filterByGitignore(files); - expect(result).toHaveLength(1); + const result = await filterByIgnoreFile(files); + expect(result).toEqual(['src/index.ts']); }); - // it('returns all files when gitignore processing fails', async () => { - vi.mocked(findGitignoreFiles).mockRejectedValue(new Error('Failed')); + vi.mocked(glob).mockRejectedValue(new Error('Failed')); const files = ['file1.ts']; - const result = await filterByGitignore(files); + const result = await filterByIgnoreFile(files); expect(result).toEqual(files); }); }); @@ -73,8 +63,10 @@ describe('getFileContent', () => { it('returns empty string on file read error', async () => { vi.mocked(readFile).mockRejectedValue(new Error('Read error')); + const mockError = vi.spyOn(console, 'error').mockImplementation(() => {}); const result = await getFileContent('test.txt'); expect(result).toBe(''); + mockError.mockRestore(); }); it('handles binary files', async () => { @@ -133,29 +125,39 @@ describe('getFileContent', () => { }); describe('CLI Integration', () => { + beforeEach(() => { + vi.resetAllMocks(); + process.exitCode = 0; + }); + it('exits with error when no patterns provided', async () => { - vi.mocked(findGitignoreFiles).mockResolvedValue([]); - const mockExit = vi - .spyOn(process, 'exit') - .mockImplementation(() => undefined as never); const mockError = vi.spyOn(console, 'error').mockImplementation(() => {}); + + process.argv = ['node', 'codepicker']; await main(); - expect(mockExit).toHaveBeenCalledWith(1); - mockExit.mockRestore(); + expect(process.exitCode).toBe(1); + expect(mockError).toHaveBeenCalledWith( + '✖ Error:', + 'Provide at least one glob pattern.', + ); + mockError.mockRestore(); }); it('exits with error when no files matched', async () => { - vi.mocked(findGitignoreFiles).mockResolvedValue([]); - const mockExit = vi - .spyOn(process, 'exit') - .mockImplementation(() => undefined as never); + vi.mocked(glob).mockResolvedValue([]); const mockError = vi.spyOn(console, 'error').mockImplementation(() => {}); + + process.argv = ['node', 'codepicker', '**/*.ts']; await main(); - expect(mockExit).toHaveBeenCalledWith(1); - mockExit.mockRestore(); + expect(process.exitCode).toBe(1); + expect(mockError).toHaveBeenCalledWith( + '✖ Error:', + 'No files matched the given patterns.', + ); + mockError.mockRestore(); }); }); diff --git a/tests/utils/gitignore.test.ts b/tests/utils/ignore.test.ts similarity index 70% rename from tests/utils/gitignore.test.ts rename to tests/utils/ignore.test.ts index 82106bf..167b820 100644 --- a/tests/utils/gitignore.test.ts +++ b/tests/utils/ignore.test.ts @@ -1,24 +1,21 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { readFile } from 'fs/promises'; -import { - findGitignoreFiles, - loadGitignoreRules, -} from '../../src/utils/gitignore'; import glob from 'fast-glob'; +import { findIgnoreFiles, loadIgnoreRules } from '../../src/utils/ignore'; vi.mock('fs/promises'); -vi.mock('fast-glob'); +vi.mock('fast-glob', () => ({ default: vi.fn() })); -describe('findGitignoreFiles', () => { +describe('findIgnoreFiles', () => { beforeEach(() => { vi.resetAllMocks(); }); - it('finds gitignore files', async () => { + it('finds ignore files', async () => { const mockGlob = vi.mocked(glob); mockGlob.mockResolvedValue(['.gitignore', 'src/.gitignore']); - const files = await findGitignoreFiles(); + const files = await findIgnoreFiles('.gitignore'); expect(files).toEqual(['.gitignore', 'src/.gitignore']); }); @@ -26,22 +23,22 @@ describe('findGitignoreFiles', () => { const mockGlob = vi.mocked(glob); mockGlob.mockRejectedValue(new Error('Failed')); - const files = await findGitignoreFiles(); + const files = await findIgnoreFiles('.gitignore'); expect(files).toEqual([]); }); }); -describe('loadGitignoreRules', () => { +describe('loadIgnoreRules', () => { beforeEach(() => { vi.resetAllMocks(); }); - it('loads rules from gitignore files', async () => { + it('loads rules from ignore files', async () => { vi.mocked(readFile) .mockResolvedValueOnce('node_modules\ndist\n!keep.js') .mockResolvedValueOnce('*.log\ncache/'); - const rules = await loadGitignoreRules(['.gitignore', 'src/.gitignore']); + const rules = await loadIgnoreRules(['.gitignore', 'src/.gitignore']); expect(rules).toContain('node_modules'); expect(rules).toContain('dist'); @@ -55,7 +52,7 @@ describe('loadGitignoreRules', () => { '# comment\n\nnode_modules\n# another\n*.js', ); - const rules = await loadGitignoreRules(['.gitignore']); + const rules = await loadIgnoreRules(['.gitignore']); expect(rules).toEqual(['node_modules', '*.js']); }); @@ -65,7 +62,7 @@ describe('loadGitignoreRules', () => { .mockRejectedValueOnce(new Error('Failed')) .mockResolvedValueOnce('*.log'); - const rules = await loadGitignoreRules(['.gitignore', 'src/.gitignore']); + const rules = await loadIgnoreRules(['.gitignore', 'src/.gitignore']); expect(rules).toEqual(['src/*.log']); }); @@ -73,7 +70,7 @@ describe('loadGitignoreRules', () => { it('handles negated patterns correctly', async () => { vi.mocked(readFile).mockResolvedValue('!important.js'); - const rules = await loadGitignoreRules(['src/.gitignore']); + const rules = await loadIgnoreRules(['src/.gitignore']); expect(rules).toEqual(['!src/important.js']); });