From 82d7b0600c48220040388e6ae3ac6122b230a8d7 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 28 Dec 2023 16:02:26 -0500 Subject: [PATCH] Replace PATH_KEY with pathKey(), delete fixtureEnv The original motivation was the loss of branch coverage from the PATH_KEY initialization expression only ever following one branch. But it also made the getPath.test.js tests more self-documenting. The same goes for hoisting the logic of the former fixtureEnv() helper into the getPath test fixture itself. Also, today I learned two things: 1. Forward slashes are legal Windows path separators, per: https://stackoverflow.com/a/63251716 2. Node's path.join() will convert any forward slashes in any of its arguments to backslashes on Windows: ```pwsh > node -e "console.log(path.join('foo/bar\baz/quux', 'xyzzy/plugh'))" foo\baaz\quux\xyzzy\plugh ``` Hence the replacement of path.join(...) with plain 'usr/local/bin', etc. --- lib/index.js | 26 ++++++++++++++------------ test/fixtures/index.js | 25 ------------------------- test/getPath.test.js | 20 +++++++++++++++----- test/main.test.js | 4 +++- test/runJsdoc.test.js | 4 +++- 5 files changed, 35 insertions(+), 44 deletions(-) diff --git a/lib/index.js b/lib/index.js index 72a784b..915b122 100644 --- a/lib/index.js +++ b/lib/index.js @@ -54,33 +54,35 @@ export async function runJsdoc(argv, env, platform) { } /** - * The key for the command search path within process.env. - * - * On every system other than Windows, this will be "PATH". On Windows, this - * will be "Path". + * Determines the key for the command search path within process.env. + * @param {string} platform - the process.platform string + * @returns {string} - On every platform other than 'win32', this will be "PATH" + * On 'win32', this will be "Path". */ -export const PATH_KEY = process.platform !== 'win32' ? 'PATH' : 'Path' +export const pathKey = (platform) => platform !== 'win32' ? 'PATH' : 'Path' /** * Returns the full path to the specified command - * @param {string} cmdName - command to find in env[PATH_KEY] + * @param {string} cmdName - command to find in env[pathKey(platform)] * @param {object} env - environment variables, presumably process.env * @param {string} platform - the process.platform string * @returns {Promise} - path to the command */ export async function getPath(cmdName, env, platform) { - for (const p of env[PATH_KEY].split(path.delimiter)) { - // pnpm will install both the original script and versions ending with .CMD - // and .ps1. We'll just default to .CMD. - const extension = (platform === 'win32') ? '.CMD' : '' - const candidate = path.join(p, cmdName) + extension + const pk = pathKey(platform) + + // pnpm will install both the original script and versions ending with .CMD + // and .ps1. We'll just default to .CMD. + if (platform === 'win32') cmdName += '.CMD' + for (const p of env[pk].split(path.delimiter)) { try { + const candidate = path.join(p, cmdName) await access(candidate) return candidate } catch { /* try next candidate */ } } - return Promise.reject(`${cmdName} not found in ${PATH_KEY}`) + return Promise.reject(`${cmdName} not found in ${pk}`) } /** diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 6793369..3315e8f 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -4,7 +4,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import path from 'node:path' import { fileURLToPath } from 'node:url' /** @@ -15,27 +14,3 @@ import { fileURLToPath } from 'node:url' export function fixturePath(fixtureName) { return fileURLToPath(new URL(fixtureName, import.meta.url)) } - -/** - * A fake environment variable object - * @typedef {object} FakeEnv - * @property {string} PATH - platform-correct PATH variable - */ - -/** - * Returns an environment object containing PATH set for the test fixture. - * - * The PATH will always contain ['usr/local/bin', 'usr/bin', 'bin'] relative to - * the fixture root. It will be constructed using the Node.js path module to - * ensure it's native to the host platform. - * @param {string} fixtureName - test fixture name - * @returns {FakeEnv} - a fake environment for the specified test fixture - */ -export function fixtureEnv(fixtureName) { - const root = fixturePath(fixtureName) - const paths = [ - path.join('usr', 'local', 'bin'), path.join('usr', 'bin'), 'bin' - ] - const pathKey = process.platform !== 'win32' ? 'PATH' : 'Path' - return {[pathKey]: paths.map(p => path.join(root, p)).join(path.delimiter)} -} diff --git a/test/getPath.test.js b/test/getPath.test.js index aab376c..1c055c8 100644 --- a/test/getPath.test.js +++ b/test/getPath.test.js @@ -5,26 +5,36 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { getPath, PATH_KEY } from '../lib' -import { fixturePath, fixtureEnv } from './fixtures' +import { getPath, pathKey } from '../lib' +import { fixturePath } from './fixtures' import { describe, expect, test } from 'vitest' import path from 'node:path' +const PATH_KEY = pathKey(process.platform) + describe('getPath', () => { const root = fixturePath('getPath') - const env = fixtureEnv('getPath') + const envPath = ['usr/local/bin', 'usr/bin', 'bin'] + .map(p => path.join(root, p)) + .join(path.delimiter) test('finds command on POSIX system', async() => { + const env = {[pathKey('linux')]: envPath} + await expect(getPath('testcmd', env, 'linux')).resolves - .toBe(path.join(root, 'bin', 'testcmd')) + .toBe(path.join(root, 'bin/testcmd')) }) test('finds command on Windows system', async() => { + const env = {[pathKey('win32')]: envPath} + await expect(getPath('testcmd', env, 'win32')).resolves - .toBe(path.join(root, 'usr', 'bin', 'testcmd.CMD')) + .toBe(path.join(root, 'usr/bin/testcmd.CMD')) }) test('rejects when command isn\'t found', async () => { + const env = {[PATH_KEY]: envPath} + await expect(getPath('nonexistent', env, process.platform)).rejects .toBe(`nonexistent not found in ${PATH_KEY}`) }) diff --git a/test/main.test.js b/test/main.test.js index 42d0889..2793ea0 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -5,7 +5,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { getPath, PATH_KEY } from '../lib' +import { getPath, pathKey } from '../lib' import { fixturePath } from './fixtures' import DestDirHelper from './DestDirHelper' import { afterEach, describe, expect, test } from 'vitest' @@ -13,6 +13,8 @@ import { spawn } from 'node:child_process' import path from 'node:path' import { fileURLToPath } from 'node:url' +const PATH_KEY = pathKey(process.platform) + describe('jsdoc-cli-wrapper', () => { const root = fixturePath('jsdocStub') const destDirHelper = new DestDirHelper() diff --git a/test/runJsdoc.test.js b/test/runJsdoc.test.js index dd0eb95..63c9fc2 100644 --- a/test/runJsdoc.test.js +++ b/test/runJsdoc.test.js @@ -5,12 +5,14 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { runJsdoc, PATH_KEY } from '../lib' +import { runJsdoc, pathKey } from '../lib' import { fixturePath } from './fixtures' import DestDirHelper from './DestDirHelper' import { afterEach, beforeEach, describe, expect, test } from 'vitest' import path from 'node:path' +const PATH_KEY = pathKey(process.platform) + describe('runJsdoc', () => { const root = fixturePath('jsdocStub') const env = { [PATH_KEY]: root }