|
1 | | -# Repterm |
| 1 | +<p align="center"> |
| 2 | + <strong>Repterm</strong> |
| 3 | +</p> |
| 4 | + |
| 5 | +<p align="center"> |
| 6 | + Terminal-first test framework for CLI/TUI apps — run tests in a real PTY, not just stdout. |
| 7 | +</p> |
| 8 | + |
| 9 | +<p align="center"> |
| 10 | + <a href="https://www.npmjs.com/package/repterm"><img alt="npm" src="https://img.shields.io/npm/v/repterm?style=flat-square&color=CB3837"></a> |
| 11 | + <a href="https://github.com/NexusGPU/repterm/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/NexusGPU/repterm/ci.yml?style=flat-square&label=CI"></a> |
| 12 | + <a href="https://github.com/NexusGPU/repterm/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/NexusGPU/repterm?style=flat-square"></a> |
| 13 | + <a href="https://repterm.ai"><img alt="Docs" src="https://img.shields.io/badge/docs-repterm.ai-blue?style=flat-square"></a> |
| 14 | +</p> |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +```typescript |
| 19 | +import { test, expect } from 'repterm'; |
| 20 | + |
| 21 | +test('greet the world', async ({ $ }) => { |
| 22 | + const result = await $`echo "Hello, Repterm!"`; |
| 23 | + expect(result).toSucceed(); |
| 24 | + expect(result).toHaveStdout('Hello, Repterm!'); |
| 25 | +}); |
| 26 | +``` |
| 27 | + |
| 28 | +```bash |
| 29 | +$ bunx repterm tests/ |
| 30 | + PASS greet the world (120ms) |
| 31 | +``` |
2 | 32 |
|
3 | | -Repterm is a terminal-first test framework for CLI/TUI applications. |
4 | | -It runs tests in a real PTY so you can assert on interactive terminal behavior, not just plain stdout. |
| 33 | +## Why Repterm? |
5 | 34 |
|
6 | | -## Packages |
| 35 | +**Write tests the way your users use your CLI.** Simple commands run via `Bun.spawn` for precise stdout/stderr/exitCode. When you need interactive testing — prompts, TUI redraws, progress bars — flip on `{ interactive: true }` and Repterm spawns a real PTY with full send/expect control. |
| 36 | + |
| 37 | +**Familiar, structured API.** If you've used Playwright or Vitest, you already know how to use Repterm. `test()`, `describe()`, `expect()` — plus a `$` tagged template for running commands with automatic shell escaping. |
7 | 38 |
|
8 | | -- `repterm`: runner + CLI |
9 | | -- `repterm-api`: plugin/matcher API for extension authors |
10 | | -- `@nexusgpu/repterm-plugin-kubectl`: kubectl-focused plugin |
| 39 | +**Tests become documentation.** Run with `--record` and every test produces an [asciinema](https://asciinema.org/) recording. Your test suite generates always-up-to-date terminal demos — no manual recording sessions. |
11 | 40 |
|
12 | | -Plugin contributors: see [Plugin Contributor Guide](CONTRIBUTING-PLUGINS.md). |
| 41 | +**Parallel and fast.** Scale with `--workers N`. Each test gets its own isolated session. |
13 | 42 |
|
14 | | -## AI Agent Skill (skills.sh) |
| 43 | +**Extensible by design.** Build plugins to add domain-specific commands and matchers. The official [kubectl plugin](https://repterm.ai/docs/kubectl/overview) adds Kubernetes-aware testing with assertions like `toBeRunning()` and `toHaveReplicas()`. |
15 | 44 |
|
16 | | -This repository includes the `repterm` agent skill at `skills/repterm/`. |
| 45 | +## Features |
17 | 46 |
|
18 | | -Install from GitHub: |
| 47 | +- **Dual execution modes** — precise `Bun.spawn` by default; opt into a real PTY with `{ interactive: true }` for colors, cursor control, and TUI testing |
| 48 | +- **Playwright-style API** — familiar `expect()` assertions, `proc.send()` / `proc.expect()` for interactive flows |
| 49 | +- **`$` tagged templates** — run commands with automatic shell escaping: `` await $`echo ${userInput}` `` |
| 50 | +- **Parallel test runner** — execute tests concurrently with `--workers N` |
| 51 | +- **Terminal recording** — generate [asciinema](https://asciinema.org/) recordings with `--record` |
| 52 | +- **Plugin system** — extend test contexts with custom helpers, matchers, and lifecycle hooks |
| 53 | +- **Multi-terminal** — spin up multiple PTY sessions in a single test for client-server scenarios |
| 54 | + |
| 55 | +## Quick Start |
| 56 | + |
| 57 | +### Install |
19 | 58 |
|
20 | 59 | ```bash |
21 | | -npx skills add NexusGPU/repterm --skill repterm |
| 60 | +bun add -d repterm |
22 | 61 | ``` |
23 | 62 |
|
24 | | -Local discovery check: |
| 63 | +Or install the standalone binary: |
25 | 64 |
|
26 | 65 | ```bash |
27 | | -npx skills add . --list |
| 66 | +curl -fsSL https://cdn.tensor-fusion.ai/archive/repterm/install.sh | sh |
28 | 67 | ``` |
29 | 68 |
|
30 | | -## Install |
| 69 | +<details> |
| 70 | +<summary>Windows (PowerShell)</summary> |
31 | 71 |
|
32 | | -```bash |
33 | | -bun add -d repterm |
| 72 | +```powershell |
| 73 | +iwr https://cdn.tensor-fusion.ai/archive/repterm/install.ps1 -UseBasicParsing | iex |
34 | 74 | ``` |
35 | 75 |
|
36 | | -Run tests: |
| 76 | +</details> |
| 77 | + |
| 78 | +### Write a Test |
| 79 | + |
| 80 | +Create `tests/demo.ts`: |
| 81 | + |
| 82 | +```typescript |
| 83 | +import { test, expect, describe } from 'repterm'; |
| 84 | + |
| 85 | +describe('my CLI', () => { |
| 86 | + test('exits with code 0', async ({ $ }) => { |
| 87 | + const result = await $`echo "it works"`; |
| 88 | + expect(result).toSucceed(); |
| 89 | + expect(result).toHaveStdout('it works'); |
| 90 | + }); |
| 91 | + |
| 92 | + test('handles stderr', async ({ $ }) => { |
| 93 | + const result = await $`echo "oops" >&2`; |
| 94 | + expect(result).toHaveStderr('oops'); |
| 95 | + }); |
| 96 | +}); |
| 97 | +``` |
| 98 | + |
| 99 | +### Run |
37 | 100 |
|
38 | 101 | ```bash |
39 | 102 | bunx repterm tests/ |
40 | | -bunx repterm --workers 4 tests/ |
41 | | -bunx repterm --record tests/ |
42 | 103 | ``` |
43 | 104 |
|
44 | | -## Install Binary (from R2/CDN) |
| 105 | +## Interactive Testing |
| 106 | + |
| 107 | +Test interactive programs like prompts, editors, or TUI apps: |
| 108 | + |
| 109 | +```typescript |
| 110 | +test('interactive prompt', async ({ $ }) => { |
| 111 | + const proc = $({ interactive: true })`bash -c 'read -p "Name: " n; echo "Hi $n"'`; |
45 | 112 |
|
46 | | -The release workflow uploads standalone binaries to Cloudflare R2 using this layout: |
| 113 | + await proc.expect('Name:'); |
| 114 | + await proc.send('Alice'); |
| 115 | + await proc.expect('Hi Alice'); |
| 116 | +}); |
| 117 | +``` |
47 | 118 |
|
48 | | -- `.../archive/repterm/latest/repterm-<os>-<arch>` |
49 | | -- `.../archive/repterm/v<version>/repterm-<os>-<arch>` |
| 119 | +## Parallel Execution |
50 | 120 |
|
51 | | -### Linux/macOS |
| 121 | +Run tests across multiple workers: |
52 | 122 |
|
53 | 123 | ```bash |
54 | | -curl -fsSL https://cdn.tensor-fusion.ai/archive/repterm/install.sh | sh |
| 124 | +bunx repterm --workers 4 tests/ |
55 | 125 | ``` |
56 | 126 |
|
57 | | -Install a specific version and custom source: |
| 127 | +## Recording |
| 128 | + |
| 129 | +Generate terminal recordings for docs or CI artifacts: |
58 | 130 |
|
59 | 131 | ```bash |
60 | | -curl -fsSL https://cdn.tensor-fusion.ai/archive/repterm/install.sh \ |
61 | | - | REPTERM_VERSION=v0.2.0 REPTERM_BASE_URL=https://cdn.tensor-fusion.ai/archive/repterm sh |
| 132 | +bunx repterm --record tests/ |
62 | 133 | ``` |
63 | 134 |
|
64 | | -Optional environment variables for `scripts/install.sh`: |
| 135 | +Produces [asciinema](https://asciinema.org/)-compatible `.cast` files you can embed or replay. |
65 | 136 |
|
66 | | -- `REPTERM_VERSION`: default `latest` |
67 | | -- `REPTERM_BASE_URL`: default `https://cdn.tensor-fusion.ai/archive/repterm` |
68 | | -- `REPTERM_INSTALL_DIR`: default `/usr/local/bin` |
| 137 | +## Plugins |
69 | 138 |
|
70 | | -### Windows (PowerShell) |
| 139 | +Repterm has a plugin system for domain-specific testing. The first official plugin adds Kubernetes support: |
71 | 140 |
|
72 | | -```powershell |
73 | | -$env:REPTERM_VERSION = "latest" |
74 | | -$env:REPTERM_BASE_URL = "https://cdn.tensor-fusion.ai/archive/repterm" |
75 | | -iwr https://cdn.tensor-fusion.ai/archive/repterm/install.ps1 -UseBasicParsing | iex |
| 141 | +```typescript |
| 142 | +import { defineConfig, createTestWithPlugins, expect } from 'repterm'; |
| 143 | +import { kubectlPlugin, pod } from '@nexusgpu/repterm-plugin-kubectl'; |
| 144 | + |
| 145 | +const config = defineConfig({ |
| 146 | + plugins: [kubectlPlugin({ namespace: 'default' })] as const, |
| 147 | +}); |
| 148 | + |
| 149 | +const test = createTestWithPlugins(config); |
| 150 | + |
| 151 | +test('pod is running', async (ctx) => { |
| 152 | + const { kubectl } = ctx.plugins; |
| 153 | + await kubectl.apply('manifests/nginx.yaml'); |
| 154 | + await kubectl.waitForPod('nginx', 'Running'); |
| 155 | + await expect(pod('nginx')).toBeRunning(); |
| 156 | +}); |
76 | 157 | ``` |
77 | 158 |
|
78 | | -## Examples |
| 159 | +Build your own plugins with [`repterm-api`](https://www.npmjs.com/package/repterm-api) — see the [Plugin Guide](https://repterm.ai/docs/plugins/overview). |
| 160 | + |
| 161 | +## Packages |
| 162 | + |
| 163 | +| Package | Description | |
| 164 | +|---------|-------------| |
| 165 | +| [`repterm`](https://www.npmjs.com/package/repterm) | Core framework + CLI runner | |
| 166 | +| [`repterm-api`](https://www.npmjs.com/package/repterm-api) | Plugin/matcher API for extension authors | |
| 167 | +| [`@nexusgpu/repterm-plugin-kubectl`](https://www.npmjs.com/package/@nexusgpu/repterm-plugin-kubectl) | Kubernetes testing plugin | |
| 168 | + |
| 169 | +## Documentation |
| 170 | + |
| 171 | +Full documentation is available at **[repterm.ai](https://repterm.ai)**. |
| 172 | + |
| 173 | +- [Getting Started](https://repterm.ai/docs/getting-started) |
| 174 | +- [Writing Tests](https://repterm.ai/docs/guides/writing-tests) |
| 175 | +- [Interactive Commands](https://repterm.ai/docs/guides/interactive-commands) |
| 176 | +- [Recording](https://repterm.ai/docs/guides/recording) |
| 177 | +- [Plugin API](https://repterm.ai/docs/plugins/overview) |
| 178 | +- [Kubectl Plugin](https://repterm.ai/docs/kubectl/overview) |
| 179 | +- [API Reference](https://repterm.ai/docs/api/assertions) |
| 180 | + |
| 181 | +## Contributing |
| 182 | + |
| 183 | +See the [Plugin Development Guide](https://repterm.ai/docs/plugins/creating-plugins) to build your own plugins. |
| 184 | + |
| 185 | +## License |
79 | 186 |
|
80 | | -- Repterm examples: `packages/repterm/examples/README.md` |
81 | | -- Kubectl plugin examples: `packages/plugin-kubectl/examples/README.md` |
| 187 | +[Apache-2.0](LICENSE) |
0 commit comments