Skip to content

Commit

Permalink
Replace PATH_KEY with pathKey(), delete fixtureEnv
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mbland committed Dec 28, 2023
1 parent b5e59a8 commit 82d7b06
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 44 deletions.
26 changes: 14 additions & 12 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>} - 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}`)
}

/**
Expand Down
25 changes: 0 additions & 25 deletions test/fixtures/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

/**
Expand All @@ -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)}
}
20 changes: 15 additions & 5 deletions test/getPath.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`)
})
Expand Down
4 changes: 3 additions & 1 deletion test/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
* 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'
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()
Expand Down
4 changes: 3 additions & 1 deletion test/runJsdoc.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down

0 comments on commit 82d7b06

Please sign in to comment.