Compare commits
No commits in common. "649796b0784dfae17ad953511b37d32d6ca66dce" and "9df431ef9a9d41dc176f8f87288f4be63967b876" have entirely different histories.
649796b078
...
9df431ef9a
|
|
@ -13,7 +13,7 @@ Transcript-based shell integration test runner. Bun + TypeScript.
|
||||||
- `--path <path>` — prepend to `$PATH` (repeatable)
|
- `--path <path>` — prepend to `$PATH` (repeatable)
|
||||||
- `--timeout <dur>` — per-command timeout (default `10s`)
|
- `--timeout <dur>` — per-command timeout (default `10s`)
|
||||||
- `-v, --verbose` — print each command as it runs
|
- `-v, --verbose` — print each command as it runs
|
||||||
- `--port-from <n>` — auto-assign `$PORT` starting from n (default `5400`)
|
- `--port-from <n>` — auto-assign `$PORT` starting from n
|
||||||
- `--parallel` — run files in parallel
|
- `--parallel` — run files in parallel
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
@ -45,8 +45,6 @@ Transcript-based shell integration test runner. Bun + TypeScript.
|
||||||
- Setup command failures abort the test with an error
|
- Setup command failures abort the test with an error
|
||||||
- Each file runs in a fresh temp dir with a single `/bin/sh` session
|
- Each file runs in a fresh temp dir with a single `/bin/sh` session
|
||||||
- `$HOME` and `$SHOUT_DIR` are set to the temp dir automatically
|
- `$HOME` and `$SHOUT_DIR` are set to the temp dir automatically
|
||||||
- `$SHOUT_SOURCE_DIR` is set to the directory containing the `.shout` file
|
|
||||||
- `$SHOUT_PROJECT_DIR` is set to `cwd` where `shout` was invoked
|
|
||||||
- stdout and stderr are merged (`exec 2>&1`)
|
- stdout and stderr are merged (`exec 2>&1`)
|
||||||
|
|
||||||
## Style
|
## Style
|
||||||
|
|
|
||||||
22
README.md
22
README.md
|
|
@ -89,28 +89,6 @@ Options:
|
||||||
-h, --help display help for command
|
-h, --help display help for command
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
### Set automatically
|
|
||||||
|
|
||||||
| Variable | Value |
|
|
||||||
|---|---|
|
|
||||||
| `HOME` | Path to the temp directory created for the test |
|
|
||||||
| `SHOUT_DIR` | Same as `HOME` — the temp directory for the test |
|
|
||||||
| `PORT` | Auto-assigned when `--port-from <n>` is used (increments per file). Not set if `PORT` is already defined via `@env` or `@setup`. |
|
|
||||||
|
|
||||||
### Inherited
|
|
||||||
|
|
||||||
By default, the test shell inherits all environment variables from the parent process. Use `--clean-env` to start with an empty environment instead.
|
|
||||||
|
|
||||||
### Modified
|
|
||||||
|
|
||||||
`PATH` is prepended with any directories passed via `--path <path>`.
|
|
||||||
|
|
||||||
### User-defined
|
|
||||||
|
|
||||||
Use `@env KEY=VALUE` directives to set arbitrary variables. See [Directives](#directives).
|
|
||||||
|
|
||||||
Print an example `.shout` file:
|
Print an example `.shout` file:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ program
|
||||||
.option("--path <path>", "Prepend <path> to PATH (repeatable)", (val: string, acc: string[]) => [...acc, val], [])
|
.option("--path <path>", "Prepend <path> to PATH (repeatable)", (val: string, acc: string[]) => [...acc, val], [])
|
||||||
.option("--timeout <dur>", "Per-command timeout", "10s")
|
.option("--timeout <dur>", "Per-command timeout", "10s")
|
||||||
.option("-v, --verbose", "Print each command as it runs")
|
.option("-v, --verbose", "Print each command as it runs")
|
||||||
.option("--port-from <n>", "Auto-assign $PORT starting from <n>", "5400")
|
.option("--port-from <n>", "Auto-assign $PORT starting from <n>")
|
||||||
.option("--parallel", "Run files in parallel")
|
.option("--parallel", "Run files in parallel")
|
||||||
.action(async (fileArgs: string[], opts) => {
|
.action(async (fileArgs: string[], opts) => {
|
||||||
const timeoutMs = parseDuration(opts.timeout)
|
const timeoutMs = parseDuration(opts.timeout)
|
||||||
|
|
@ -94,14 +94,14 @@ program
|
||||||
const start = performance.now()
|
const start = performance.now()
|
||||||
const results: TestResult[] = []
|
const results: TestResult[] = []
|
||||||
const cwd = process.cwd()
|
const cwd = process.cwd()
|
||||||
const portFrom = parseInt(opts.portFrom, 10)
|
const portFrom = opts.portFrom ? parseInt(opts.portFrom, 10) : undefined
|
||||||
if (Number.isNaN(portFrom)) {
|
if (portFrom !== undefined && Number.isNaN(portFrom)) {
|
||||||
console.error("--port-from must be an integer")
|
console.error("--port-from must be an integer")
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
let nextPort = portFrom
|
let nextPort = portFrom
|
||||||
|
|
||||||
const runOne = async (filePath: string, port: number) => {
|
const runOne = async (filePath: string, port: number | undefined) => {
|
||||||
const content = await readFile(filePath, "utf-8")
|
const content = await readFile(filePath, "utf-8")
|
||||||
const parsed = parse(relative(cwd, filePath), content)
|
const parsed = parse(relative(cwd, filePath), content)
|
||||||
|
|
||||||
|
|
@ -125,7 +125,7 @@ program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object.assign(envVars, setupEnvVars, userEnvVars)
|
Object.assign(envVars, setupEnvVars, userEnvVars)
|
||||||
if (!("PORT" in userEnvVars) && !("PORT" in setupEnvVars)) {
|
if (port !== undefined && !("PORT" in userEnvVars) && !("PORT" in setupEnvVars)) {
|
||||||
envVars["PORT"] = String(port)
|
envVars["PORT"] = String(port)
|
||||||
}
|
}
|
||||||
const merged: ShoutFile = { ...parsed, commands: [...setupCommands, ...parsed.commands] }
|
const merged: ShoutFile = { ...parsed, commands: [...setupCommands, ...parsed.commands] }
|
||||||
|
|
@ -134,8 +134,6 @@ program
|
||||||
cleanEnv: opts.cleanEnv ?? false,
|
cleanEnv: opts.cleanEnv ?? false,
|
||||||
pathDirs: opts.path,
|
pathDirs: opts.path,
|
||||||
envVars,
|
envVars,
|
||||||
sourceDir: resolve(dirname(filePath)),
|
|
||||||
projectDir: cwd,
|
|
||||||
timeout: timeoutMs,
|
timeout: timeoutMs,
|
||||||
verbose: opts.verbose ?? false,
|
verbose: opts.verbose ?? false,
|
||||||
onCommand: opts.verbose
|
onCommand: opts.verbose
|
||||||
|
|
@ -205,7 +203,7 @@ program
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.parallel) {
|
if (opts.parallel) {
|
||||||
const all = await Promise.all(files.map(f => runOne(f, nextPort++)))
|
const all = await Promise.all(files.map(f => runOne(f, nextPort !== undefined ? nextPort++ : undefined)))
|
||||||
for (const r of all) {
|
for (const r of all) {
|
||||||
printDots(r)
|
printDots(r)
|
||||||
results.push(r)
|
results.push(r)
|
||||||
|
|
@ -213,7 +211,7 @@ program
|
||||||
process.stdout.write("\n")
|
process.stdout.write("\n")
|
||||||
} else {
|
} else {
|
||||||
for (const filePath of files) {
|
for (const filePath of files) {
|
||||||
const r = await runOne(filePath, nextPort++)
|
const r = await runOne(filePath, nextPort !== undefined ? nextPort++ : undefined)
|
||||||
printDots(r)
|
printDots(r)
|
||||||
results.push(r)
|
results.push(r)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ type RunOptions = {
|
||||||
cleanEnv: boolean
|
cleanEnv: boolean
|
||||||
pathDirs?: string[]
|
pathDirs?: string[]
|
||||||
envVars?: Record<string, string>
|
envVars?: Record<string, string>
|
||||||
sourceDir?: string
|
|
||||||
projectDir?: string
|
|
||||||
timeout: number
|
timeout: number
|
||||||
verbose: boolean
|
verbose: boolean
|
||||||
onCommand?: (cmd: Command) => void
|
onCommand?: (cmd: Command) => void
|
||||||
|
|
@ -130,12 +128,6 @@ export async function runFile(
|
||||||
|
|
||||||
env["HOME"] = tmpDir
|
env["HOME"] = tmpDir
|
||||||
env["SHOUT_DIR"] = tmpDir
|
env["SHOUT_DIR"] = tmpDir
|
||||||
if (options.sourceDir) {
|
|
||||||
env["SHOUT_SOURCE_DIR"] = options.sourceDir
|
|
||||||
}
|
|
||||||
if (options.projectDir) {
|
|
||||||
env["SHOUT_PROJECT_DIR"] = options.projectDir
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.envVars) {
|
if (options.envVars) {
|
||||||
Object.assign(env, options.envVars)
|
Object.assign(env, options.envVars)
|
||||||
|
|
|
||||||
|
|
@ -237,10 +237,8 @@ Options:
|
||||||
<h2>Environment</h2>
|
<h2>Environment</h2>
|
||||||
<p>Shout sets these variables before running your commands:</p>
|
<p>Shout sets these variables before running your commands:</p>
|
||||||
<pre><code><span class="bright">HOME</span> <span class="dim">→</span> <span class="output">temp directory for this test file</span>
|
<pre><code><span class="bright">HOME</span> <span class="dim">→</span> <span class="output">temp directory for this test file</span>
|
||||||
<span class="bright">SHOUT_DIR</span> <span class="dim">→</span> <span class="output">same temp directory</span>
|
<span class="bright">SHOUT_DIR</span> <span class="dim">→</span> <span class="output">same temp directory</span>
|
||||||
<span class="bright">SHOUT_SOURCE_DIR</span> <span class="dim">→</span> <span class="output">directory containing the .shout file</span>
|
<span class="bright">PATH</span> <span class="dim">→</span> <span class="output">prepended with --path dirs, if any</span></code></pre>
|
||||||
<span class="bright">SHOUT_PROJECT_DIR</span> <span class="dim">→</span> <span class="output">directory where shout was invoked</span>
|
|
||||||
<span class="bright">PATH</span> <span class="dim">→</span> <span class="output">prepended with --path dirs, if any</span></code></pre>
|
|
||||||
<p>Each file runs in its own temp directory. <code>--clean-env</code> starts with an empty environment instead of inheriting yours.</p>
|
<p>Each file runs in its own temp directory. <code>--clean-env</code> starts with an empty environment instead of inheriting yours.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user