Compare commits
4 Commits
7578b73f63
...
59246fb460
| Author | SHA1 | Date | |
|---|---|---|---|
| 59246fb460 | |||
| bc9f087bb4 | |||
| bc99a1e2cb | |||
| 11ab691577 |
26
README.md
26
README.md
|
|
@ -44,6 +44,32 @@ $ shout test
|
|||
|
||||
Each line in a `.shout` file is run sequentially, unless `--parallel` is passed.
|
||||
|
||||
## Directives
|
||||
|
||||
Directives go at the top of a `.shout` file, before any commands.
|
||||
|
||||
### `@env`
|
||||
|
||||
Set environment variables for the test:
|
||||
|
||||
```
|
||||
@env GREETING=hello
|
||||
@env TARGET=world
|
||||
|
||||
$ echo "$GREETING $TARGET"
|
||||
hello world
|
||||
```
|
||||
|
||||
### `@setup`
|
||||
|
||||
Prepend commands (and `@env` directives) from another `.shout` file:
|
||||
|
||||
```
|
||||
@setup setup-shared.shout
|
||||
```
|
||||
|
||||
Setup commands run first and their failures abort the test. Setup files cannot themselves contain `@setup` — no nesting. If both the setup file and the user file define the same `@env`, the user file wins.
|
||||
|
||||
```
|
||||
Usage: shout test [options] [files...]
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,26 @@ import type { TestResult } from "../format.ts"
|
|||
import { parseDuration } from "../duration.ts"
|
||||
import { rewriteFile } from "../update.ts"
|
||||
|
||||
async function filterGitignored(files: string[]): Promise<string[]> {
|
||||
if (files.length === 0) return files
|
||||
try {
|
||||
const proc = Bun.spawn(["git", "check-ignore", "--stdin"], {
|
||||
stdin: new Blob([files.join("\n")]),
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
})
|
||||
const output = await new Response(proc.stdout).text()
|
||||
await proc.exited
|
||||
const ignored = new Set(output.trim().split("\n").filter(Boolean))
|
||||
return files.filter(f => !ignored.has(f))
|
||||
} catch {
|
||||
return files
|
||||
}
|
||||
}
|
||||
|
||||
async function findShoutFiles(paths: string[]): Promise<string[]> {
|
||||
const files: string[] = []
|
||||
const explicit: string[] = []
|
||||
const discovered: string[] = []
|
||||
|
||||
for (const p of paths) {
|
||||
const abs = resolve(p)
|
||||
|
|
@ -22,7 +40,7 @@ async function findShoutFiles(paths: string[]): Promise<string[]> {
|
|||
: null
|
||||
|
||||
if (stat && abs.endsWith(".shout")) {
|
||||
files.push(abs)
|
||||
explicit.push(abs)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -31,16 +49,17 @@ async function findShoutFiles(paths: string[]): Promise<string[]> {
|
|||
const entries = await readdir(abs, { recursive: true })
|
||||
for (const entry of entries) {
|
||||
if (entry.endsWith(".shout")) {
|
||||
files.push(resolve(abs, entry))
|
||||
discovered.push(resolve(abs, entry))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// If not a directory, try as file anyway
|
||||
if (abs.endsWith(".shout")) files.push(abs)
|
||||
if (abs.endsWith(".shout")) explicit.push(abs)
|
||||
}
|
||||
}
|
||||
|
||||
return files.sort()
|
||||
const filtered = await filterGitignored(discovered)
|
||||
return [...explicit, ...filtered].sort()
|
||||
}
|
||||
|
||||
import pkg from "../../package.json"
|
||||
|
|
@ -57,7 +76,7 @@ program
|
|||
.option("-u, --update", "Rewrite expected output in-place with actual output")
|
||||
.option("-k, --keep", "Keep temp directories after run")
|
||||
.option("--clean-env", "Start with empty environment")
|
||||
.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("-v, --verbose", "Print each command as it runs")
|
||||
.option("--port-from <n>", "Auto-assign $PORT starting from <n>")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user