mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
import { EventEmitter } from 'node:events';
|
|
import type { ChildProcess } from 'node:child_process';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
import { createBrowserOpenInvocation, openBrowser } from '../src/browser-open.js';
|
|
|
|
describe('browser open helper', () => {
|
|
it('opens URLs on Windows through cmd.exe instead of spawning the shell builtin directly', () => {
|
|
const invocation = createBrowserOpenInvocation('win32', 'http://127.0.0.1:7456/');
|
|
|
|
expect(invocation.command).toMatch(/cmd(?:\.exe)?$/i);
|
|
expect(invocation.args.slice(0, 3)).toEqual(['/d', '/s', '/c']);
|
|
expect(invocation.args[3]).toContain('start "" "http://127.0.0.1:7456/"');
|
|
expect(invocation.options).toMatchObject({ windowsVerbatimArguments: true });
|
|
});
|
|
|
|
it('escapes cmd.exe metacharacters before opening Windows URLs', () => {
|
|
const invocation = createBrowserOpenInvocation('win32', 'https://example.test/a b?q=%USERPROFILE%&name="A"');
|
|
|
|
expect(invocation.args[3]).toMatch(/^"start "" /);
|
|
expect(invocation.args[3]).toContain('"^%"USERPROFILE"^%"');
|
|
expect(invocation.args[3]).toContain('name=""A""');
|
|
});
|
|
|
|
it('handles opener spawn errors without crashing the daemon', () => {
|
|
const child = new EventEmitter() as ChildProcess;
|
|
child.unref = vi.fn() as ChildProcess['unref'];
|
|
const spawn = vi.fn(() => child);
|
|
const warn = vi.fn();
|
|
|
|
openBrowser('http://127.0.0.1:7456/', {
|
|
platform: 'linux',
|
|
spawn,
|
|
warn,
|
|
});
|
|
|
|
expect(() => child.emit('error', new Error('spawn xdg-open ENOENT'))).not.toThrow();
|
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining('failed to open browser'));
|
|
});
|
|
});
|