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.
|
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...]
|
Usage: shout test [options] [files...]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,26 @@ import type { TestResult } from "../format.ts"
|
||||||
import { parseDuration } from "../duration.ts"
|
import { parseDuration } from "../duration.ts"
|
||||||
import { rewriteFile } from "../update.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[]> {
|
async function findShoutFiles(paths: string[]): Promise<string[]> {
|
||||||
const files: string[] = []
|
const explicit: string[] = []
|
||||||
|
const discovered: string[] = []
|
||||||
|
|
||||||
for (const p of paths) {
|
for (const p of paths) {
|
||||||
const abs = resolve(p)
|
const abs = resolve(p)
|
||||||
|
|
@ -22,7 +40,7 @@ async function findShoutFiles(paths: string[]): Promise<string[]> {
|
||||||
: null
|
: null
|
||||||
|
|
||||||
if (stat && abs.endsWith(".shout")) {
|
if (stat && abs.endsWith(".shout")) {
|
||||||
files.push(abs)
|
explicit.push(abs)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,16 +49,17 @@ async function findShoutFiles(paths: string[]): Promise<string[]> {
|
||||||
const entries = await readdir(abs, { recursive: true })
|
const entries = await readdir(abs, { recursive: true })
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry.endsWith(".shout")) {
|
if (entry.endsWith(".shout")) {
|
||||||
files.push(resolve(abs, entry))
|
discovered.push(resolve(abs, entry))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// If not a directory, try as file anyway
|
// 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"
|
import pkg from "../../package.json"
|
||||||
|
|
@ -57,7 +76,7 @@ program
|
||||||
.option("-u, --update", "Rewrite expected output in-place with actual output")
|
.option("-u, --update", "Rewrite expected output in-place with actual output")
|
||||||
.option("-k, --keep", "Keep temp directories after run")
|
.option("-k, --keep", "Keep temp directories after run")
|
||||||
.option("--clean-env", "Start with empty environment")
|
.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("--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>")
|
.option("--port-from <n>", "Auto-assign $PORT starting from <n>")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user