// Mock platform module for Unix before importing path utilities jest.mock('platform', () => ({ os: { family: 'Unix' } })); import path from 'path'; import { getRelativePath, getBasename, getAbsoluteFilePath, getRelativePathWithinBasePath, isPathExternalToBasePath } from './path'; describe('Path Utilities - Unix Platform', () => { describe('getRelativePath', () => { it('should return relative path between two directories', () => { expect(getRelativePath('/users/john/projects', '/users/john/projects/app')).toBe('app'); }); it('should return "." when both paths are the same', () => { expect(getRelativePath('/users/john/projects', '/users/john/projects')).toBe('.'); }); it('should return parent directory path', () => { expect(getRelativePath('/users/john/projects', '/users/john/docs/readme.md')).toBe('../docs/readme.md'); }); it('should return nested subdirectory path', () => { expect(getRelativePath('/users/john/projects', '/users/john/projects/src/components')).toBe('src/components'); }); it('should return ".." for direct parent directory', () => { expect(getRelativePath('/users/john/projects', '/users/john')).toBe('..'); }); it('should handle null/undefined inputs', () => { expect(getRelativePath(null, '/users/john/projects')).toBe('/users/john/projects'); expect(getRelativePath(undefined, '/users/john/projects')).toBe('/users/john/projects'); }); }); describe('getBasename', () => { it('should return filename from relative path', () => { expect(getBasename('/users/john/projects', '../docs/readme.md')).toBe('readme.md'); }); it('should return filename from subdirectory path', () => { expect(getBasename('/users/john/projects', 'subfolder/config.json')).toBe('config.json'); }); it('should return filename from direct file path', () => { expect(getBasename('/users/john/projects', 'package.json')).toBe('package.json'); }); it('should return directory name for parent directory', () => { expect(getBasename('/users/john/projects', '..')).toBe('john'); }); it('should return directory name for current directory', () => { expect(getBasename('/users/john/projects', '.')).toBe('projects'); }); it('should return filename from nested path', () => { expect(getBasename('/users/john/projects', 'src/components/Button.jsx')).toBe('Button.jsx'); }); it('should return empty string for falsy relativePath', () => { expect(getBasename('/users/john/projects', '')).toBe(''); expect(getBasename('/users/john/projects', null)).toBe(''); expect(getBasename('/users/john/projects', undefined)).toBe(''); }); it('should handle complex relative paths', () => { expect(getBasename('/users/john/projects', '../../docs/api/spec.md')).toBe('spec.md'); }); it('should handle paths with multiple extensions', () => { expect(getBasename('/users/john/projects', 'src/utils/common/path.spec.js')).toBe('path.spec.js'); }); }); describe('getAbsoluteFilePath', () => { it('should resolve relative file path against collection path', () => { const result = getAbsoluteFilePath('/users/john/collections', 'config/settings.json'); expect(result).toBe('/users/john/collections/config/settings.json'); }); it('should handle nested file paths', () => { const result = getAbsoluteFilePath('/users/john/collections', 'api/v1/users.json'); expect(result).toBe('/users/john/collections/api/v1/users.json'); }); it('should handle parent directory references', () => { const result = getAbsoluteFilePath('/users/john/collections/api', '../shared/config.json'); expect(result).toBe('/users/john/collections/shared/config.json'); }); it('should handle empty file path', () => { const result = getAbsoluteFilePath('/users/john/collections', ''); expect(result).toBe('/users/john/collections'); }); it('should handle current directory reference', () => { const result = getAbsoluteFilePath('/users/john/collections', '.'); expect(result).toBe('/users/john/collections'); }); it('should handle previous directory reference', () => { const result = getAbsoluteFilePath('/users/john/collections', '..'); expect(result).toBe('/users/john'); }); it('should handle root file path', () => { const result = getAbsoluteFilePath('/users/john/collections', '/absolute/path/file.json'); expect(result).toBe('/absolute/path/file.json'); }); it('should handle current directory reference', () => { const result = getAbsoluteFilePath('/users/john/collections', './local-file.json'); expect(result).toBe('/users/john/collections/local-file.json'); }); }); describe('getRelativePathWithinBasePath', () => { it('should store in-collection files as relative paths', () => { const result = getRelativePathWithinBasePath('/users/john/collections/api', '/users/john/collections/api/files/payload.txt'); expect(result).toBe('files/payload.txt'); }); it('should handle collection paths with trailing separators', () => { const result = getRelativePathWithinBasePath('/users/john/collections/api/', '/users/john/collections/api/files/payload.txt'); expect(result).toBe('files/payload.txt'); }); it('should resolve dot segments before deciding whether a file is inside the collection', () => { const result = getRelativePathWithinBasePath('/users/john/collections/api', '/users/john/collections/api/files/../payload.txt'); expect(result).toBe('payload.txt'); }); it('should keep paths that resolve outside the collection absolute', () => { const filePath = '/users/john/collections/api/../payload.txt'; const result = getRelativePathWithinBasePath('/users/john/collections/api', filePath); expect(result).toBe(filePath); }); it('should keep outside collection paths absolute', () => { const filePath = '/users/john/downloads/payload.txt'; const result = getRelativePathWithinBasePath('/users/john/collections/api', filePath); expect(result).toBe(filePath); }); it('should keep sibling prefix paths absolute', () => { const filePath = '/users/john/collections/api-other/payload.txt'; const result = getRelativePathWithinBasePath('/users/john/collections/api', filePath); expect(result).toBe(filePath); }); it('should keep same-path values unchanged', () => { const filePath = '/users/john/collections/api'; const result = getRelativePathWithinBasePath('/users/john/collections/api', filePath); expect(result).toBe(filePath); }); it('should store in-collection paths whose names begin with two dots as relative paths', () => { const result = getRelativePathWithinBasePath('/users/john/collections/api', '/users/john/collections/api/..payload.txt'); expect(result).toBe('..payload.txt'); }); it('should keep the original file path when inputs are missing', () => { expect(getRelativePathWithinBasePath('', '/users/john/downloads/payload.txt')).toBe('/users/john/downloads/payload.txt'); expect(getRelativePathWithinBasePath('/users/john/collections/api', '')).toBe(''); }); it('should treat relative collection path as cwd-relative when file path is absolute', () => { const collectionPath = 'collections/api'; const filePath = path.resolve(collectionPath, 'files/payload.txt'); const result = getRelativePathWithinBasePath(collectionPath, filePath); expect(result).toBe('files/payload.txt'); }); it('should treat relative file path as cwd-relative when collection path is absolute', () => { const collectionPath = path.resolve('collections/api'); const filePath = 'collections/api/files/payload.txt'; const result = getRelativePathWithinBasePath(collectionPath, filePath); expect(result).toBe('files/payload.txt'); }); it('should treat both relative paths as cwd-relative for containment checks', () => { const collectionPath = 'collections/api'; const filePath = 'collections/api/files/payload.txt'; const result = getRelativePathWithinBasePath(collectionPath, filePath); expect(result).toBe('files/payload.txt'); }); }); describe('isPathExternalToBasePath', () => { it('should return false for a collection directly inside the base path', () => { expect(isPathExternalToBasePath('/users/john/workspace', '/users/john/workspace/collections/api')).toBe(false); }); it('should return false for a deeply nested collection', () => { expect(isPathExternalToBasePath('/users/john/workspace', '/users/john/workspace/a/b/c')).toBe(false); }); it('should return true for a collection outside the base path', () => { expect(isPathExternalToBasePath('/users/john/workspace', '/users/john/downloads/api')).toBe(true); }); it('should return true for a sibling whose name is a prefix of the base path', () => { // /workspace-other must not be treated as inside /workspace expect(isPathExternalToBasePath('/users/john/workspace', '/users/john/workspace-other/api')).toBe(true); }); it('should resolve dot segments before deciding', () => { // resolves to /users/john/workspace/b — inside expect(isPathExternalToBasePath('/users/john/workspace', '/users/john/workspace/a/../b')).toBe(false); // resolves to /users/john/b — outside expect(isPathExternalToBasePath('/users/john/workspace/a', '/users/john/workspace/a/../../b')).toBe(true); }); it('should ignore a trailing separator on the base path', () => { expect(isPathExternalToBasePath('/users/john/workspace/', '/users/john/workspace/collections/api')).toBe(false); }); it('should return false when either input is missing', () => { expect(isPathExternalToBasePath('', '/users/john/workspace/api')).toBe(false); expect(isPathExternalToBasePath('/users/john/workspace', '')).toBe(false); expect(isPathExternalToBasePath(undefined, undefined)).toBe(false); }); }); describe('Edge cases', () => { it('should handle very long paths', () => { const longPath = '/users/john/projects/' + 'a'.repeat(100); const result = getBasename(longPath, 'file.txt'); expect(result).toBe('file.txt'); }); it('should handle paths with special characters', () => { expect(getBasename('/users/john/projects', 'file with spaces.txt')).toBe('file with spaces.txt'); expect(getBasename('/users/john/projects', 'file-with-dashes.txt')).toBe('file-with-dashes.txt'); expect(getBasename('/users/john/projects', 'file_with_underscores.txt')).toBe('file_with_underscores.txt'); }); it('should handle paths with unicode characters', () => { expect(getBasename('/users/john/projects', 'файл.txt')).toBe('файл.txt'); expect(getBasename('/users/john/projects', '文件.txt')).toBe('文件.txt'); }); }); });