Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/check-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ on:
workflow_dispatch:

concurrency:
group: pages
cancel-in-progress: false
group: docs-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
build:
Expand Down
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@

### Added

- Homepage example with a Bash function, test, run command and passing output
- Blog posts for releases 0.43.0 through 0.52.0, including the 0.50.1 cleanup patch

### Fixed

- Snapshots record and update literal `-n`, `-e`, `-E` and combined echo flag values correctly (#1402)
- Generated workflows run test directories containing whitespace as a single path without requiring a committed `.env` (#1425)
- Annotations, tags, header counts and duplicate checks support functions declared without parentheses (#1422)
- Echo data providers keep `#` and parentheses, and report malformed rows without dropping later rows. Quoted values keep carriage returns on Windows (#1419)
- Homepage logo follows reduced-motion preferences and stops tracking the pointer after navigation
- Primary buttons use dark text in both themes, including hover and active states
- Quickstart commands match install.sh, npm project and npm global installs
- A green file or filtered subset preserves rerun-cache entries for tests it did not execute (#1418)
- SIGTERM and SIGHUP run teardown hooks, stop parallel workers, and remove the run's scratch files before exiting with status 1 (#1417)
- Sequential stop-on-failure runs write requested reports and the rerun cache for the tests that ran (#1415)
Expand Down Expand Up @@ -38,8 +43,9 @@

### Changed

- Documentation uses shorter, clearer wording across guides, reference pages and release posts
- Large provider and annotation maps use an index instead of scanning earlier entries, speeding up test files on Bash 3.0 and newer (#1392)
- Common patterns appears under Writing Tests; Quickstart links to function and script testing recipes
- Shortened guides, reference pages and release posts
- Large provider and annotation maps use indexed lookups to speed up test files on Bash 3.0 and newer (#1392)

## [0.52.0](https://github.com/TypedDevs/bashunit/compare/0.51.0...0.52.0) - 2026-10-11

Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export default defineConfig({
text: 'Writing Tests',
collapsed: false,
items: [
{ text: 'Common patterns', link: '/common-patterns' },
{ text: 'Assertions', link: '/assertions' },
{ text: 'Custom asserts', link: '/custom-asserts' },
{ text: 'Data providers', link: '/data-providers' },
Expand All @@ -170,7 +171,6 @@ export default defineConfig({
{ text: 'Coverage', link: '/coverage' },
{ text: 'Benchmarks', link: '/benchmarks' },
{ text: 'Standalone', link: '/standalone' },
{ text: 'Common patterns', link: '/common-patterns' },
{ text: 'Agentic coding', link: '/ai-agents' },
],
}, {
Expand Down
3 changes: 3 additions & 0 deletions docs/.vitepress/theme/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
:root {
--vp-font-family-base: Heebo, sans-serif;
--vp-font-family-mono: Cousine, monospace;
--vp-button-brand-text: #171a1b;
--vp-button-brand-hover-text: #171a1b;
--vp-button-brand-active-text: #171a1b;
}

h1,
Expand Down
97 changes: 89 additions & 8 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,98 @@ features:
details: Run the same tests on Linux, macOS and Windows via WSL.
---

<div class="home-example">

## Test your first Bash function

Install bashunit and create folders for your script and test:

```bash
curl -s https://bashunit.com/install.sh | bash
mkdir -p src tests
```

Save these two files:

::: code-group
```bash [src/greet.sh]
#!/usr/bin/env bash

function greet() {
printf 'Hello, %s!\n' "${1:-world}"
}
```

```bash [tests/greet_test.sh]
#!/usr/bin/env bash

function set_up() {
source src/greet.sh
}

function test_greet_by_name() {
assert_same "Hello, Bash!" "$(greet Bash)"
}
```
:::

Run your test:

```bash
./lib/bashunit tests/
```

A passing run reports:

```text
✓ Passed: Greet by name

Tests: 1 passed, 1 total
Assertions: 1 passed, 1 total
```

Continue with the [Quickstart](/quickstart) for other install methods and running options.

</div>

<style scoped>
.home-example {
max-width: 760px;
margin: 64px auto 0;
}
</style>

<script setup lang="ts">
import { onMounted } from 'vue';
import VanillaTilt from 'vanilla-tilt';
import { onMounted, onUnmounted } from 'vue';
import VanillaTilt, { type HTMLVanillaTiltElement } from 'vanilla-tilt';

let cleanupTilt = () => {};

onMounted(() => {
const heroImage = document.querySelector('.VPHero .VPImage');
const heroImage = document.querySelector<HTMLVanillaTiltElement>('.VPHero .VPImage');
if (!heroImage) return;

const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
const updateTilt = () => {
if (reducedMotion.matches) {
heroImage.vanillaTilt?.destroy();
return;
}

VanillaTilt.init(heroImage, {
'full-page-listening': true,
reverse: true,
gyroscope: false
});
VanillaTilt.init(heroImage, {
'full-page-listening': true,
reverse: true,
gyroscope: false
});
};

updateTilt();
reducedMotion.addEventListener('change', updateTilt);
cleanupTilt = () => {
reducedMotion.removeEventListener('change', updateTilt);
heroImage.vanillaTilt?.destroy();
};
});

onUnmounted(() => cleanupTilt());
</script>
38 changes: 29 additions & 9 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ Pick whichever fits your project. See [installation](/installation) for the comp
curl -s https://bashunit.com/install.sh | bash
```

```bash [npm]
# Per-project (pinned in package.json), run via npx
```bash [npm project]
npm install --save-dev bashunit
npx bashunit tests/
```

# Global install, run directly on PATH
```bash [npm global]
npm install -g bashunit
bashunit tests/
```

:::
Expand All @@ -34,10 +32,20 @@ The `install.sh` route creates `lib/bashunit`; the npm route exposes `bashunit`

Create a test suite with the `init` subcommand:

```bash
::: code-group
```bash [install.sh]
./lib/bashunit init tests
```

```bash [npm project]
npx bashunit init tests
```

```bash [npm global]
bashunit init tests
```
:::

It creates, in the current directory:

- `tests/bootstrap.sh`: sourced before your tests; put shared setup here
Expand Down Expand Up @@ -66,10 +74,20 @@ Alternatively, create your tests manually:
:::

3. Run the test:
```bash
./lib/bashunit tests
::: code-group
```bash [install.sh]
./lib/bashunit tests/
```

```bash [npm project]
npx bashunit tests/
```

```bash [npm global]
bashunit tests/
```
:::

4. The output should look like this:
```-vue
bashunit - {{ pkg.version }} | Tests: 1
Expand All @@ -86,9 +104,11 @@ Alternatively, create your tests manually:
A per-test duration column appears when the platform has a cheap clock source;
`BASHUNIT_SHOW_EXECUTION_TIME` controls it.

Next, test one of your own [functions](/common-patterns#testing-individual-functions) or [complete scripts](/common-patterns#testing-complete-scripts).

## Running your suite

With no path, bashunit runs `tests/` (`BASHUNIT_DEFAULT_PATH`):
With no path, bashunit runs `tests/` (`BASHUNIT_DEFAULT_PATH`). The examples below use the `install.sh` path; for an npm project, replace `./lib/bashunit` with `npx bashunit`, or with `bashunit` for a global install:

```bash
./lib/bashunit # run tests/
Expand Down
4 changes: 2 additions & 2 deletions src/assert/snapshot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ function bashunit::snapshot::initialize() {
local path="$1"
local content="$2"
mkdir -p "$(dirname "$path")"
echo "$content" >"$path"
printf '%s\n' "$content" >"$path"
bashunit::state::add_assertions_snapshot
}

Expand Down Expand Up @@ -431,7 +431,7 @@ function bashunit::snapshot::update() {
;;
esac

echo "$actual" >"$path"
printf '%s\n' "$actual" >"$path"
bashunit::state::add_assertions_snapshot
return 0
}
Expand Down
4 changes: 2 additions & 2 deletions tests/acceptance/install_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ fi
case "$INSTALL_TEST_DOWNLOAD_RESULT" in
failure) printf 'partial download\n' >"$target"; exit 22 ;;
empty) : >"$target" ;;
*) printf 'new binary\n' >"$target" ;;
*) printf '#!/bin/sh\nexit 0\n' >"$target" ;;
esac
SH
chmod +x shims/curl
Expand All @@ -249,7 +249,7 @@ SH

if [ "$result" = success ]; then
assert_successful_code "" "" "$status"
assert_same 'new binary' "$(cat destination/bashunit)"
assert_same "$(printf '#!/bin/sh\nexit 0')" "$(cat destination/bashunit)"
assert_is_file_executable destination/bashunit
assert_not_empty "$(find destination/bashunit -perm 0744 -print)"
else
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/assert/snapshot_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,27 @@ function test_regex_metacharacters_around_a_placeholder_are_literal() {
assert_empty "$(assert_match_snapshot 'cost $5.00 (x) Z' "$snapshot")"
assert_not_empty "$(assert_match_snapshot 'cost 999 (x) Z' "$snapshot")"
}

function provide_snapshot_option_values() {
bashunit::data_set -n
bashunit::data_set -e
bashunit::data_set -E
bashunit::data_set -neE
}

# @data_provider provide_snapshot_option_values
function test_snapshot_records_option_values_and_matches_again() {
local path="$(bashunit::temp_dir)/flag.snapshot"
assert_match_snapshot "$1" "$path"
assert_same "$1" "$(<"$path")"
assert_empty "$(assert_match_snapshot "$1" "$path")"
}

# @data_provider provide_snapshot_option_values
function test_snapshot_update_writes_option_values() {
local path="$(bashunit::temp_dir)/flag.snapshot"
printf 'previous\n' >"$path"
BASHUNIT_SNAPSHOT_UPDATE=true bashunit::snapshot::update "$path" "$1"
assert_same "$1" "$(<"$path")"
assert_empty "$(assert_match_snapshot "$1" "$path")"
}
Loading