Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: open URL in browser on WSL #128

Merged
merged 4 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,19 @@ const open = (_args, opts = {}, extra = {}) => {

let platform = process.platform
// process.platform === 'linux' may actually indicate WSL, if that's the case
// we want to treat things as win32 anyway so the host can open the argument
// open the argument with sensible-browser which is pre-installed
// In WSL, set the default browser using, for example,
// export BROWSER="/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"
// or
// export BROWSER="/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
// To permanently set the default browser, add the appropriate entry to your shell's
// RC file, e.g. .bashrc or .zshrc.
if (platform === 'linux' && os.release().toLowerCase().includes('microsoft')) {
platform = 'win32'
platform = 'wsl'
if (!process.env.BROWSER) {
return Promise.reject(
new Error('Set the BROWSER environment variable to your desired browser.'))
}
}

let command = options.command
Expand All @@ -146,6 +156,8 @@ const open = (_args, opts = {}, extra = {}) => {
// accidentally interpret the first arg as the title, we stick an empty
// string immediately after the start command
command = 'start ""'
} else if (platform === 'wsl') {
command = 'sensible-browser'
} else if (platform === 'darwin') {
command = 'open'
} else {
Expand Down
67 changes: 45 additions & 22 deletions test/open.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const spawk = require('spawk')
const t = require('tap')
const os = require('node:os')

const promiseSpawn = require('../lib/index.js')

Expand All @@ -10,6 +11,8 @@ t.afterEach(() => {
spawk.clean()
})

const isWSL = process.platform === 'linux' && os.release().toLowerCase().includes('microsoft')

t.test('process.platform === win32', (t) => {
const comSpec = process.env.ComSpec
const platformDesc = Object.getOwnPropertyDescriptor(process, 'platform')
Expand Down Expand Up @@ -118,7 +121,8 @@ t.test('process.platform === linux', (t) => {
Object.defineProperty(process, 'platform', platformDesc)
})

t.test('uses xdg-open in a shell', async (t) => {
// xdg-open is not installed in WSL by default
t.test('uses xdg-open in a shell', { skip: isWSL }, async (t) => {
const proc = spawk.spawn('sh', ['-c', 'xdg-open https://google.com'], { shell: false })

const result = await promiseSpawn.open('https://google.com')
Expand All @@ -130,7 +134,8 @@ t.test('process.platform === linux', (t) => {
t.ok(proc.called)
})

t.test('ignores shell = false', async (t) => {
// xdg-open is not installed in WSL by default
t.test('ignores shell = false', { skip: isWSL }, async (t) => {
const proc = spawk.spawn('sh', ['-c', 'xdg-open https://google.com'], { shell: false })

const result = await promiseSpawn.open('https://google.com', { shell: false })
Expand All @@ -154,58 +159,76 @@ t.test('process.platform === linux', (t) => {
t.ok(proc.called)
})

t.test('when os.release() includes Microsoft treats as win32', async (t) => {
const comSpec = process.env.ComSpec
process.env.ComSpec = 'C:\\Windows\\System32\\cmd.exe'
t.teardown(() => {
process.env.ComSPec = comSpec
})

t.test('when os.release() includes Microsoft treats as WSL', async (t) => {
const promiseSpawnMock = t.mock('../lib/index.js', {
os: {
release: () => 'Microsoft',
},
})
const browser = process.env.BROWSER
process.env.BROWSER = '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe'

const proc = spawk.spawn('C:\\Windows\\System32\\cmd.exe',
['/d', '/s', '/c', 'start "" https://google.com'],
{ shell: false })
const proc = spawk.spawn('sh', ['-c', 'sensible-browser https://google.com'], { shell: false })

const result = await promiseSpawnMock.open('https://google.com')
t.hasStrict(result, {
code: 0,
signal: undefined,
})

t.ok(proc.called)
})

t.test('when os.release() includes microsoft treats as win32', async (t) => {
const comSpec = process.env.ComSpec
process.env.ComSpec = 'C:\\Windows\\System32\\cmd.exe'
t.teardown(() => {
process.env.ComSPec = comSpec
process.env.BROWSER = browser
})

t.ok(proc.called)
})

t.test('when os.release() includes microsoft treats as WSL', async (t) => {
const promiseSpawnMock = t.mock('../lib/index.js', {
os: {
release: () => 'microsoft',
},
})
const browser = process.env.BROWSER
process.env.BROWSER = '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe'

const proc = spawk.spawn('C:\\Windows\\System32\\cmd.exe',
['/d', '/s', '/c', 'start "" https://google.com'],
{ shell: false })
const proc = spawk.spawn('sh', ['-c', 'sensible-browser https://google.com'], { shell: false })

const result = await promiseSpawnMock.open('https://google.com')
t.hasStrict(result, {
code: 0,
signal: undefined,
})

t.teardown(() => {
process.env.BROWSER = browser
})

t.ok(proc.called)
})

t.test('fails on WSL if BROWSER is not set', async (t) => {
const promiseSpawnMock = t.mock('../lib/index.js', {
os: {
release: () => 'microsoft',
},
})
const browser = process.env.BROWSER
delete process.env.BROWSER

const proc = spawk.spawn('sh', ['-c', 'sensible-browser https://google.com'], { shell: false })

await t.rejects(promiseSpawnMock.open('https://google.com'), {
message: 'Set the BROWSER environment variable to your desired browser.',
})

t.teardown(() => {
process.env.BROWSER = browser
})

t.notOk(proc.called)
})

t.end()
})

Expand Down
Loading