UNPKG

reviewit

Version:

A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view

215 lines (214 loc) 12.3 kB
import { describe, it, expect } from 'vitest'; import { validateCommitish, validateDiffArguments, shortHash, parseGitHubPrUrl } from './utils'; describe('CLI Utils', () => { describe('validateCommitish', () => { it('should validate full SHA hashes', () => { expect(validateCommitish('a1b2c3d4e5f6789012345678901234567890abcd')).toBe(true); expect(validateCommitish('abc123')).toBe(true); }); it('should validate SHA hashes with parent references', () => { expect(validateCommitish('a1b2c3d4e5f6789012345678901234567890abcd^')).toBe(true); expect(validateCommitish('abc123^')).toBe(true); expect(validateCommitish('abc123^^')).toBe(true); expect(validateCommitish('bd4b7513e075b5b245284c38fd23427b9bd0f42e^')).toBe(true); }); it('should validate SHA hashes with ancestor references', () => { expect(validateCommitish('a1b2c3d4e5f6789012345678901234567890abcd~1')).toBe(true); expect(validateCommitish('abc123~5')).toBe(true); expect(validateCommitish('abc123~10')).toBe(true); expect(validateCommitish('bd4b7513e075b5b245284c38fd23427b9bd0f42e~2')).toBe(true); }); it('should validate HEAD references', () => { expect(validateCommitish('HEAD')).toBe(true); expect(validateCommitish('HEAD~1')).toBe(true); expect(validateCommitish('HEAD~10')).toBe(true); expect(validateCommitish('HEAD^')).toBe(true); expect(validateCommitish('HEAD^1')).toBe(true); expect(validateCommitish('HEAD^2')).toBe(true); expect(validateCommitish('HEAD~2^1')).toBe(true); }); it('should validate branch names', () => { expect(validateCommitish('main')).toBe(true); expect(validateCommitish('feature/new-feature')).toBe(true); expect(validateCommitish('develop')).toBe(true); }); it('should validate special cases', () => { expect(validateCommitish('.')).toBe(true); // working directory diff }); it('should reject invalid input', () => { expect(validateCommitish('')).toBe(false); expect(validateCommitish(' ')).toBe(false); expect(validateCommitish('HEAD~')).toBe(false); expect(validateCommitish('abc')).toBe(true); // short hashes are valid }); it('should reject non-string input', () => { expect(validateCommitish(null)).toBe(false); expect(validateCommitish(undefined)).toBe(false); expect(validateCommitish(123)).toBe(false); }); }); describe('validateDiffArguments', () => { describe('format validation', () => { it('should accept valid commitish formats', () => { expect(validateDiffArguments('HEAD', 'HEAD^')).toEqual({ valid: true }); expect(validateDiffArguments('main', 'develop')).toEqual({ valid: true }); expect(validateDiffArguments('abc123', 'def456')).toEqual({ valid: true }); expect(validateDiffArguments('working')).toEqual({ valid: true }); expect(validateDiffArguments('staged', 'HEAD')).toEqual({ valid: true }); expect(validateDiffArguments('.', 'main')).toEqual({ valid: true }); }); it('should reject invalid target commitish format', () => { const result = validateDiffArguments('', 'HEAD'); expect(result.valid).toBe(false); expect(result.error).toBe('Invalid target commit-ish format'); }); it('should reject invalid base commitish format', () => { const result = validateDiffArguments('HEAD', ''); expect(result.valid).toBe(false); expect(result.error).toBe('Invalid base commit-ish format'); }); }); describe('special argument restrictions', () => { it('should reject special arguments in base position', () => { const result1 = validateDiffArguments('HEAD', 'working'); expect(result1.valid).toBe(false); expect(result1.error).toBe('Special arguments (working, staged, .) are only allowed as target, not base. Got base: working'); const result2 = validateDiffArguments('main', 'staged'); expect(result2.valid).toBe(false); expect(result2.error).toBe('Special arguments (working, staged, .) are only allowed as target, not base. Got base: staged'); const result3 = validateDiffArguments('HEAD', '.'); expect(result3.valid).toBe(false); expect(result3.error).toBe('Special arguments (working, staged, .) are only allowed as target, not base. Got base: .'); }); it('should allow special arguments in target position', () => { expect(validateDiffArguments('working')).toEqual({ valid: true }); expect(validateDiffArguments('staged', 'HEAD')).toEqual({ valid: true }); expect(validateDiffArguments('.', 'main')).toEqual({ valid: true }); }); it('should allow staged as base only with working target', () => { expect(validateDiffArguments('working', 'staged')).toEqual({ valid: true }); const result = validateDiffArguments('HEAD', 'staged'); expect(result.valid).toBe(false); expect(result.error).toBe('Special arguments (working, staged, .) are only allowed as target, not base. Got base: staged'); }); }); describe('same value comparison', () => { it('should reject same target and base values', () => { const result1 = validateDiffArguments('HEAD', 'HEAD'); expect(result1.valid).toBe(false); expect(result1.error).toBe('Cannot compare HEAD with itself'); const result2 = validateDiffArguments('main', 'main'); expect(result2.valid).toBe(false); expect(result2.error).toBe('Cannot compare main with itself'); }); it('should allow different values', () => { expect(validateDiffArguments('HEAD', 'HEAD^')).toEqual({ valid: true }); expect(validateDiffArguments('main', 'develop')).toEqual({ valid: true }); }); }); describe('working directory restrictions', () => { it('should reject working with compareWith', () => { const result = validateDiffArguments('working', 'HEAD'); expect(result.valid).toBe(false); expect(result.error).toBe('"working" shows unstaged changes and cannot be compared with another commit. Use "." instead to compare all uncommitted changes with a specific commit.'); }); it('should allow working without compareWith', () => { expect(validateDiffArguments('working')).toEqual({ valid: true }); }); it('should allow working with staged', () => { expect(validateDiffArguments('working', 'staged')).toEqual({ valid: true }); }); it('should reject working with other commits', () => { const result1 = validateDiffArguments('working', 'main'); expect(result1.valid).toBe(false); expect(result1.error).toBe('"working" shows unstaged changes and cannot be compared with another commit. Use "." instead to compare all uncommitted changes with a specific commit.'); const result2 = validateDiffArguments('working', 'abc123'); expect(result2.valid).toBe(false); expect(result2.error).toBe('"working" shows unstaged changes and cannot be compared with another commit. Use "." instead to compare all uncommitted changes with a specific commit.'); }); it('should allow other special args with compareWith', () => { expect(validateDiffArguments('staged', 'HEAD')).toEqual({ valid: true }); expect(validateDiffArguments('.', 'main')).toEqual({ valid: true }); }); }); describe('edge cases', () => { it('should handle undefined base', () => { expect(validateDiffArguments('HEAD')).toEqual({ valid: true }); expect(validateDiffArguments('main')).toEqual({ valid: true }); }); it('should handle complex git references', () => { expect(validateDiffArguments('HEAD~2', 'HEAD~3')).toEqual({ valid: true }); expect(validateDiffArguments('HEAD^1', 'HEAD^2')).toEqual({ valid: true }); expect(validateDiffArguments('feature/branch-name', 'origin/main')).toEqual({ valid: true, }); }); it('should handle SHA hashes with parent/ancestor references', () => { expect(validateDiffArguments('bd4b7513e075b5b245284c38fd23427b9bd0f42e^', 'abc123')).toEqual({ valid: true }); expect(validateDiffArguments('abc123', 'def456^')).toEqual({ valid: true }); expect(validateDiffArguments('abc123~1', 'def456~2')).toEqual({ valid: true }); expect(validateDiffArguments('a1b2c3d4e5f6789012345678901234567890abcd^', 'HEAD')).toEqual({ valid: true, }); }); }); }); describe('shortHash', () => { it('should return first 7 characters of hash', () => { expect(shortHash('a1b2c3d4e5f6789012345678901234567890abcd')).toBe('a1b2c3d'); expect(shortHash('1234567890abcdef')).toBe('1234567'); expect(shortHash('abc123')).toBe('abc123'); }); it('should handle short hashes', () => { expect(shortHash('abc')).toBe('abc'); expect(shortHash('')).toBe(''); }); }); describe('parseGitHubPrUrl', () => { it('should parse valid GitHub PR URLs', () => { const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/123'); expect(result).toEqual({ owner: 'owner', repo: 'repo', pullNumber: 123, }); }); it('should parse GitHub PR URLs with additional path segments', () => { const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/456/files'); expect(result).toEqual({ owner: 'owner', repo: 'repo', pullNumber: 456, }); }); it('should parse GitHub PR URLs with query parameters', () => { const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/789?tab=files'); expect(result).toEqual({ owner: 'owner', repo: 'repo', pullNumber: 789, }); }); it('should handle URLs with hyphens and underscores in owner/repo names', () => { const result = parseGitHubPrUrl('https://github.com/owner-name/repo_name/pull/123'); expect(result).toEqual({ owner: 'owner-name', repo: 'repo_name', pullNumber: 123, }); }); it('should return null for invalid URLs', () => { expect(parseGitHubPrUrl('not-a-url')).toBe(null); expect(parseGitHubPrUrl('https://example.com/owner/repo/pull/123')).toBe(null); expect(parseGitHubPrUrl('https://github.com/owner/repo/issues/123')).toBe(null); expect(parseGitHubPrUrl('https://github.com/owner/repo')).toBe(null); expect(parseGitHubPrUrl('https://github.com/owner/repo/pull/abc')).toBe(null); }); it('should handle malformed URLs gracefully', () => { expect(parseGitHubPrUrl('')).toBe(null); expect(parseGitHubPrUrl('https://github.com')).toBe(null); expect(parseGitHubPrUrl('https://github.com/owner')).toBe(null); expect(parseGitHubPrUrl('https://github.com/owner/repo/pull')).toBe(null); }); }); });