-
-
Notifications
You must be signed in to change notification settings - Fork 217
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
192 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import {statSync} from 'node:fs'; | ||
import {resolve} from 'node:path'; | ||
import {fileURLToPath} from 'node:url'; | ||
import process from 'node:process'; | ||
|
||
export const getDefaultCwd = () => { | ||
try { | ||
return process.cwd(); | ||
} catch (error) { | ||
error.message = `The current directory does not exist.\n${error.message}`; | ||
throw error; | ||
} | ||
}; | ||
|
||
export const normalizeCwd = cwd => { | ||
const cwdString = safeNormalizeFileUrl(cwd, 'The "cwd" option'); | ||
return resolve(cwdString); | ||
}; | ||
|
||
export const safeNormalizeFileUrl = (file, name) => { | ||
const fileString = normalizeFileUrl(file); | ||
|
||
if (typeof fileString !== 'string') { | ||
throw new TypeError(`${name} must be a string or a file URL: ${fileString}.`); | ||
} | ||
|
||
return fileString; | ||
}; | ||
|
||
export const normalizeFileUrl = file => file instanceof URL ? fileURLToPath(file) : file; | ||
|
||
export const fixCwdError = (originalMessage, cwd) => { | ||
if (cwd === getDefaultCwd()) { | ||
return originalMessage; | ||
} | ||
|
||
let cwdStat; | ||
try { | ||
cwdStat = statSync(cwd); | ||
} catch (error) { | ||
return `The "cwd" option is invalid: ${cwd}.\n${error.message}\n${originalMessage}`; | ||
} | ||
|
||
if (!cwdStat.isDirectory()) { | ||
return `The "cwd" option is not a directory: ${cwd}.\n${originalMessage}`; | ||
} | ||
|
||
return originalMessage; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import {mkdir, rmdir} from 'node:fs/promises'; | ||
import {relative, toNamespacedPath} from 'node:path'; | ||
import process from 'node:process'; | ||
import {pathToFileURL, fileURLToPath} from 'node:url'; | ||
import tempfile from 'tempfile'; | ||
import test from 'ava'; | ||
import {execa, execaSync} from '../index.js'; | ||
import {FIXTURES_DIR, setFixtureDir} from './helpers/fixtures-dir.js'; | ||
|
||
setFixtureDir(); | ||
|
||
const isWindows = process.platform === 'win32'; | ||
|
||
const testOptionCwdString = async (t, execaMethod) => { | ||
const cwd = '/'; | ||
const {stdout} = await execaMethod('node', ['-p', 'process.cwd()'], {cwd}); | ||
t.is(toNamespacedPath(stdout), toNamespacedPath(cwd)); | ||
}; | ||
|
||
test('The "cwd" option can be a string', testOptionCwdString, execa); | ||
test('The "cwd" option can be a string - sync', testOptionCwdString, execaSync); | ||
|
||
const testOptionCwdUrl = async (t, execaMethod) => { | ||
const cwd = '/'; | ||
const cwdUrl = pathToFileURL(cwd); | ||
const {stdout} = await execaMethod('node', ['-p', 'process.cwd()'], {cwd: cwdUrl}); | ||
t.is(toNamespacedPath(stdout), toNamespacedPath(cwd)); | ||
}; | ||
|
||
test('The "cwd" option can be a URL', testOptionCwdUrl, execa); | ||
test('The "cwd" option can be a URL - sync', testOptionCwdUrl, execaSync); | ||
|
||
const testOptionCwdInvalid = (t, execaMethod) => { | ||
t.throws(() => { | ||
execaMethod('empty.js', {cwd: true}); | ||
}, {message: /The "cwd" option must be a string or a file URL: true/}); | ||
}; | ||
|
||
test('The "cwd" option cannot be an invalid type', testOptionCwdInvalid, execa); | ||
test('The "cwd" option cannot be an invalid type - sync', testOptionCwdInvalid, execaSync); | ||
|
||
const testErrorCwdDefault = async (t, execaMethod) => { | ||
const {cwd} = await execaMethod('empty.js'); | ||
t.is(cwd, process.cwd()); | ||
}; | ||
|
||
test('The "cwd" option defaults to process.cwd()', testErrorCwdDefault, execa); | ||
test('The "cwd" option defaults to process.cwd() - sync', testErrorCwdDefault, execaSync); | ||
|
||
// Windows does not allow removing a directory used as `cwd` of a running process | ||
if (!isWindows) { | ||
const testCwdPreSpawn = async (t, execaMethod) => { | ||
const currentCwd = process.cwd(); | ||
const filePath = tempfile(); | ||
await mkdir(filePath); | ||
process.chdir(filePath); | ||
await rmdir(filePath); | ||
|
||
try { | ||
t.throws(() => { | ||
execaMethod('empty.js'); | ||
}, {message: /The current directory does not exist/}); | ||
} finally { | ||
process.chdir(currentCwd); | ||
} | ||
}; | ||
|
||
test.serial('The "cwd" option default fails if current cwd is missing', testCwdPreSpawn, execa); | ||
test.serial('The "cwd" option default fails if current cwd is missing - sync', testCwdPreSpawn, execaSync); | ||
} | ||
|
||
const cwdNotExisting = {cwd: 'does_not_exist', expectedCode: 'ENOENT', expectedMessage: 'The "cwd" option is invalid'}; | ||
const cwdTooLong = {cwd: '.'.repeat(1e5), expectedCode: 'ENAMETOOLONG', expectedMessage: 'The "cwd" option is invalid'}; | ||
const cwdNotDir = {cwd: fileURLToPath(import.meta.url), expectedCode: isWindows ? 'ENOENT' : 'ENOTDIR', expectedMessage: 'The "cwd" option is not a directory'}; | ||
|
||
const testCwdPostSpawn = async (t, {cwd, expectedCode, expectedMessage}, execaMethod) => { | ||
const {failed, code, message} = await execaMethod('empty.js', {cwd, reject: false}); | ||
t.true(failed); | ||
t.is(code, expectedCode); | ||
t.true(message.includes(expectedMessage)); | ||
t.true(message.includes(cwd)); | ||
}; | ||
|
||
test('The "cwd" option must be an existing file', testCwdPostSpawn, cwdNotExisting, execa); | ||
test('The "cwd" option must be an existing file - sync', testCwdPostSpawn, cwdNotExisting, execaSync); | ||
test('The "cwd" option must not be too long', testCwdPostSpawn, cwdTooLong, execa); | ||
test('The "cwd" option must not be too long - sync', testCwdPostSpawn, cwdTooLong, execaSync); | ||
test('The "cwd" option must be a directory', testCwdPostSpawn, cwdNotDir, execa); | ||
test('The "cwd" option must be a directory - sync', testCwdPostSpawn, cwdNotDir, execaSync); | ||
|
||
const successProperties = {fixtureName: 'empty.js', expectedFailed: false}; | ||
const errorProperties = {fixtureName: 'fail.js', expectedFailed: true}; | ||
|
||
const testErrorCwd = async (t, execaMethod, {fixtureName, expectedFailed}) => { | ||
const {failed, cwd} = await execaMethod(fixtureName, {cwd: relative('.', FIXTURES_DIR), reject: false}); | ||
t.is(failed, expectedFailed); | ||
t.is(cwd, FIXTURES_DIR); | ||
}; | ||
|
||
test('result.cwd is defined', testErrorCwd, execa, successProperties); | ||
test('result.cwd is defined - sync', testErrorCwd, execaSync, successProperties); | ||
test('error.cwd is defined', testErrorCwd, execa, errorProperties); | ||
test('error.cwd is defined - sync', testErrorCwd, execaSync, errorProperties); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,15 @@ | ||
import path from 'node:path'; | ||
import {delimiter, resolve} from 'node:path'; | ||
import process from 'node:process'; | ||
import {fileURLToPath} from 'node:url'; | ||
import pathKey from 'path-key'; | ||
|
||
export const PATH_KEY = pathKey(); | ||
export const FIXTURES_DIR_URL = new URL('../fixtures/', import.meta.url); | ||
export const FIXTURES_DIR = fileURLToPath(FIXTURES_DIR_URL); | ||
export const FIXTURES_DIR = resolve(fileURLToPath(FIXTURES_DIR_URL)); | ||
|
||
// Add the fixtures directory to PATH so fixtures can be executed without adding | ||
// `node`. This is only meant to make writing tests simpler. | ||
export const setFixtureDir = () => { | ||
process.env[PATH_KEY] = FIXTURES_DIR + path.delimiter + process.env[PATH_KEY]; | ||
process.env[PATH_KEY] = FIXTURES_DIR + delimiter + process.env[PATH_KEY]; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.