type ExecFileCallback = ( error: (Error & { code?: string | number }) | null, stdout: string, stderr: string, ) => void type ExecFileMock = ( command: string, args: readonly string[], options: { encoding: string; signal: AbortSignal; windowsHide: boolean }, callback: ExecFileCallback, ) => void const { execFileMock } = vi.hoisted(() => ({ execFileMock: vi.fn() })) vi.mock('node:child_process', () => ({ execFile: execFileMock })) import { release as osRelease } from 'node:os' import { describe, expect, it, vi } from 'vitest' import { openNativePath, openNativeTextFile, type PathOpenerRunner } from '../src/native-path-opener.ts' const signal = () => new AbortController().signal describe('native path opener', () => { it('opens with macOS open(1)', async () => { const run = vi.fn(async () => ({ stdout: '', stderr: '' })) await openNativePath('/Users/test/file.txt', signal(), { platform: 'darwin', run }) expect(run).toHaveBeenCalledWith('open', ['/Users/test/file.txt'], expect.any(AbortSignal)) }) it('bypasses macOS file associations for text documents', async () => { const run = vi.fn(async () => ({ stdout: '', stderr: '' })) await openNativeTextFile('/Users/test/settings.yaml', signal(), { platform: 'darwin', run }) expect(run).toHaveBeenCalledWith('open', ['-t', '/Users/test/settings.yaml'], expect.any(AbortSignal)) }) it('uses the Linux desktop association for text documents', async () => { const run = vi.fn(async () => ({ stdout: '', stderr: '' })) await openNativeTextFile('/tmp/settings.yaml', signal(), { platform: 'linux', osRelease: '6.8.0-generic', env: {}, run, }) expect(run).toHaveBeenCalledWith('xdg-open', ['/tmp/settings.yaml'], expect.any(AbortSignal)) }) it.each([ ['distribution marker', { WSL_DISTRO_NAME: 'Ubuntu' }, '6.8.0-generic'], ['interop marker', { WSL_INTEROP: '/run/WSL/123_interop' }, '6.8.0-generic'], ['kernel release', {}, '5.15.153.1-microsoft-standard-WSL2'], ])('hands WSL text documents to the Windows desktop from the %s', async (_label, env, osRelease) => { const requestSignal = signal() const run = vi.fn(async command => command === 'wslpath' ? { stdout: '\\\\wsl.localhost\\Ubuntu\\home\\test user\\settings.yaml\r\n', stderr: '' } : { stdout: '', stderr: '' }) await openNativeTextFile('/home/test user/settings.yaml', requestSignal, { platform: 'linux', osRelease, env, run, }) expect(run.mock.calls).toEqual([ ['wslpath', ['-w', '/home/test user/settings.yaml'], requestSignal], [ 'powershell.exe', [ '-NoProfile', '-Command', "Invoke-Item -LiteralPath '\\\\wsl.localhost\\Ubuntu\\home\\test user\\settings.yaml'", ], requestSignal, ], ]) }) it('rejects an empty WSL path translation before invoking Windows', async () => { const run = vi.fn(async () => ({ stdout: '\r\n', stderr: '' })) await expect(openNativeTextFile('/home/test/settings.yaml', signal(), { platform: 'linux', osRelease: '6.8.0-generic', env: { WSL_DISTRO_NAME: 'Ubuntu' }, run, })).rejects.toThrow('wslpath returned no Windows path') expect(run).toHaveBeenCalledOnce() }) it('does not invoke Windows when the request aborts during WSL path translation', async () => { const abort = new AbortController() const run = vi.fn(async () => { abort.abort(new Error('closed')) return { stdout: '\\\\wsl.localhost\\Ubuntu\\home\\test\\settings.yaml\n', stderr: '' } }) await expect(openNativeTextFile('/home/test/settings.yaml', abort.signal, { platform: 'linux', osRelease: '6.8.0-generic', env: { WSL_DISTRO_NAME: 'Ubuntu' }, run, })).rejects.toThrow('closed') expect(run).toHaveBeenCalledOnce() }) it('opens with Windows Invoke-Item and escapes single quotes', async () => { const run = vi.fn(async () => ({ stdout: '', stderr: '' })) await openNativePath("C:\\work\\o'reilly.txt", signal(), { platform: 'win32', run }) expect(run).toHaveBeenCalledWith( 'powershell.exe', ['-NoProfile', '-Command', "Invoke-Item -LiteralPath 'C:\\work\\o''reilly.txt'"], expect.any(AbortSignal), ) }) it('uses the Windows desktop association for text documents', async () => { const run = vi.fn(async () => ({ stdout: '', stderr: '' })) await openNativeTextFile('C:\\work\\settings.yaml', signal(), { platform: 'win32', run }) expect(run).toHaveBeenCalledWith( 'powershell.exe', ['-NoProfile', '-Command', "Invoke-Item -LiteralPath 'C:\\work\\settings.yaml'"], expect.any(AbortSignal), ) }) it('opens with Linux xdg-open', async () => { const run = vi.fn(async () => ({ stdout: '', stderr: '' })) await openNativePath('/tmp/a.txt', signal(), { platform: 'linux', osRelease: '6.8.0-generic', env: { WSL_DISTRO_NAME: '', WSL_INTEROP: '' }, run, }) expect(run).toHaveBeenCalledWith('xdg-open', ['/tmp/a.txt'], expect.any(AbortSignal)) }) it('rejects unsupported platforms', async () => { await expect(openNativePath('/x', signal(), { platform: 'freebsd' as NodeJS.Platform })) .rejects.toThrow('unsupported on freebsd') }) it('uses the current process platform when no platform override is supplied', async () => { const run = vi.fn(async () => ({ stdout: '', stderr: '' })) await openNativePath('/tmp/platform-default.txt', signal(), { osRelease: '6.8.0-generic', env: {}, run, }) const expected = process.platform === 'win32' ? 'powershell.exe' : process.platform === 'linux' ? 'xdg-open' : 'open' expect(run.mock.calls[0]?.[0]).toBe(expected) }) it('samples ambient WSL markers and kernel release when no fact overrides are supplied', async () => { const ambientWsl = [process.env.WSL_DISTRO_NAME, process.env.WSL_INTEROP] .some(value => value !== undefined && value !== '') || osRelease().toLowerCase().includes('microsoft') const run = vi.fn(async command => command === 'wslpath' ? { stdout: 'C:\\settings.yaml\n', stderr: '' } : { stdout: '', stderr: '' }) await openNativePath('/tmp/ambient-facts.yaml', signal(), { platform: 'linux', run }) expect(run.mock.calls[0]?.[0]).toBe(ambientWsl ? 'wslpath' : 'xdg-open') }) it('runs the default command adapter without a shell and preserves command failures', async () => { execFileMock.mockImplementationOnce((_command, _args, _options, callback) => { callback(null, '', '') }) await openNativePath('/tmp/default.txt', signal(), { platform: 'darwin' }) const [command, args, options] = execFileMock.mock.calls[0]! expect(command).toBe('open') expect(args).toEqual(['/tmp/default.txt']) expect(options.encoding).toBe('utf8') expect(options.windowsHide).toBe(true) expect(options.signal).toBeInstanceOf(AbortSignal) const commandError = Object.assign(new Error('open failed'), { code: 1 }) execFileMock.mockImplementationOnce((_command, _args, _options, callback) => { callback(commandError, 'partial output', 'failure details') }) await expect(openNativePath('/tmp/missing.txt', signal(), { platform: 'darwin' })).rejects.toMatchObject({ message: 'open failed', cause: commandError, code: 1, stdout: 'partial output', stderr: 'failure details', }) }) })