blob: 5c26e7f38ea5387ebe7fd925f2d8c73535511268 [file] [edit]
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
'use strict'
// Runs binaryPath() in a child process so each case gets its own platform,
// environment and module search path, with packages laid out the way npm
// installs the published platform packages.
const { test } = require('node:test')
const assert = require('node:assert/strict')
const { spawnSync } = require('node:child_process')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const INDEX = path.join(__dirname, '..', 'index.js')
function fixture(t) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'selenium-manager-'))
t.after(() => fs.rmSync(root, { recursive: true, force: true }))
const shim = path.join(root, 'platform-shim.js')
fs.writeFileSync(
shim,
`Object.defineProperty(process, 'platform', { value: process.env.FAKE_PLATFORM })
Object.defineProperty(process, 'arch', { value: process.env.FAKE_ARCH })
`,
)
const modules = path.join(root, 'node_modules')
fs.mkdirSync(modules)
return { root, shim, modules }
}
function installPackage(modules, name, binary) {
const dir = path.join(modules, ...name.split('/'))
fs.mkdirSync(path.join(dir, 'bin'), { recursive: true })
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name }))
if (binary) {
fs.writeFileSync(path.join(dir, 'bin', binary), '')
}
return path.join(dir, 'bin', binary ?? '')
}
function binaryPath(fx, platform, arch, env = {}) {
// Inherit the environment: under Bazel, process.execPath is a launcher that
// needs the JS_BINARY__* variables to find the real node.
const childEnv = { ...process.env, NODE_PATH: fx.modules, FAKE_PLATFORM: platform, FAKE_ARCH: arch, ...env }
if (!('SE_MANAGER_PATH' in env)) {
delete childEnv.SE_MANAGER_PATH
}
const result = spawnSync(
process.execPath,
['-r', fx.shim, '-e', `process.stdout.write(require(${JSON.stringify(INDEX)}).binaryPath())`],
{
encoding: 'utf8',
env: childEnv,
},
)
return { path: result.stdout, error: result.stderr, status: result.status }
}
const CASES = [
['linux', 'x64', '@selenium/manager-linux-x64', 'selenium-manager'],
['linux', 'arm64', '@selenium/manager-linux-arm64', 'selenium-manager'],
['darwin', 'x64', '@selenium/manager-darwin', 'selenium-manager'],
['darwin', 'arm64', '@selenium/manager-darwin', 'selenium-manager'],
['win32', 'ia32', '@selenium/manager-win32-ia32', 'selenium-manager.exe'],
['win32', 'x64', '@selenium/manager-win32-ia32', 'selenium-manager.exe'],
['win32', 'arm64', '@selenium/manager-win32-ia32', 'selenium-manager.exe'],
]
for (const [platform, arch, pkg, binary] of CASES) {
test(`${platform} ${arch} resolves the binary in ${pkg}`, (t) => {
const fx = fixture(t)
const expected = installPackage(fx.modules, pkg, binary)
const result = binaryPath(fx, platform, arch)
assert.equal(result.status, 0, result.error)
assert.equal(fs.realpathSync(result.path), fs.realpathSync(expected))
})
}
test('SE_MANAGER_PATH overrides the platform package', (t) => {
const fx = fixture(t)
const result = binaryPath(fx, 'linux', 'x64', { SE_MANAGER_PATH: '/opt/selenium-manager' })
assert.equal(result.status, 0, result.error)
assert.equal(result.path, '/opt/selenium-manager')
})
test('an unsupported platform lists the supported ones', (t) => {
const fx = fixture(t)
const result = binaryPath(fx, 'freebsd', 'x64')
assert.notEqual(result.status, 0)
assert.match(result.error, /Unsupported platform: freebsd x64\. Supported: linux \(x64, arm64\), darwin, win32/)
})
test('an unsupported linux architecture is rejected', (t) => {
const fx = fixture(t)
const result = binaryPath(fx, 'linux', 'ppc64')
assert.notEqual(result.status, 0)
assert.match(result.error, /Unsupported platform: linux ppc64/)
})
test('a missing platform package names the package to install', (t) => {
const fx = fixture(t)
const result = binaryPath(fx, 'linux', 'x64')
assert.notEqual(result.status, 0)
assert.match(result.error, /Run: npm install @selenium\/manager-linux-x64/)
})
test('a platform package without its binary is reported', (t) => {
const fx = fixture(t)
installPackage(fx.modules, '@selenium/manager-linux-x64')
const result = binaryPath(fx, 'linux', 'x64')
assert.notEqual(result.status, 0)
assert.match(result.error, /Binary not found at expected path: .*selenium-manager/)
})
const LAUNCHER = path.join(__dirname, '..', 'bin', 'selenium-manager.js')
// Points SE_MANAGER_PATH at node itself, so the arguments decide what the
// "manager" does and the launcher is exercised without a real binary.
function launch(managerPath, args, env = {}) {
const childEnv = { ...process.env, SE_MANAGER_PATH: managerPath, ...env }
if (!('SE_AVOID_STATS' in env)) {
delete childEnv.SE_AVOID_STATS
}
const result = spawnSync(process.execPath, [LAUNCHER, ...args], { encoding: 'utf8', env: childEnv })
return { stdout: result.stdout, error: result.stderr, status: result.status }
}
test('the launcher passes through the manager exit code', () => {
const result = launch(process.execPath, ['-e', 'process.exit(3)'])
assert.equal(result.status, 3, result.error)
})
test('the launcher turns usage stats off unless asked', () => {
const script = 'process.stdout.write(process.env.SE_AVOID_STATS)'
assert.equal(launch(process.execPath, ['-e', script]).stdout, 'true')
assert.equal(launch(process.execPath, ['-e', script], { SE_AVOID_STATS: 'false' }).stdout, 'false')
})
test('a manager that cannot start is reported', () => {
const missing = path.join(os.tmpdir(), 'no-such-selenium-manager')
const result = launch(missing, ['--version'])
assert.equal(result.status, 1)
assert.match(result.error, /Failed to run .*no-such-selenium-manager: .*ENOENT/)
})