cj-vana/buttonmash

GitHub: cj-vana/buttonmash

Stars: 0 | Forks: 0

# 🐒 buttonmash [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/5942c99a5e173427.svg)](https://github.com/cj-vana/buttonmash/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/buttonmash.svg)](https://www.npmjs.com/package/buttonmash) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) **A CI chaos monkey for web apps.** Point it at your site and it **crawls every page on its own** — discovering links and in-app (SPA) navigations as it goes — then on each page finds every button/link/input and mashes them: clicking, double-clicking, typing random keystrokes, selecting, scrolling, resizing, navigating. It even **completes create-flows** — filling forms with valid data and submitting them — so empty apps populate themselves and deep editors get exercised. When something breaks (an uncaught error, a 500, a crash, a blank screen, a broken image…) it writes a report and **fails your build**. It's deterministic (seeded, so any failure replays), bounded (action/time budgets), and **safe by default**: it stays on your origin, skips destructive controls, refuses to run against live payment keys, and redacts secrets. npx buttonmash run https://staging.example.com ## Why Existing in-page monkeys (gremlins.js and friends) inject synthetic events and **never actually fail your CI** — they just log to the console. buttonmash flips that around: it drives the page from the **harness** side with Playwright, so it owns the verdict and the exit code. It also enumerates real elements (so it hits buttons below the fold, unlike coordinate-based clickers), dispatches **trusted** input, and deduplicates findings into an actionable report with a reproducible seed. ## Install npm install --save-dev buttonmash npx playwright install --with-deps chromium # one-time browser install Requires Node 20+. ## Quickstart # 1. (optional) capture an authenticated session — opens a browser, you log in npx buttonmash auth https://staging.example.com/login # → saves cookies/localStorage to playwright/.auth/user.json # 2. scaffold a config (optional) npx buttonmash init # 3. run it npx buttonmash run https://staging.example.com --auth playwright/.auth/user.json # 4. reproduce a failure exactly (the seed is printed on every run) npx buttonmash run https://staging.example.com --seed When it finishes you get a `buttonmash-report/` folder with `report.html` (self-contained), `results.json`, and `junit.xml`. Exit code is `1` if anything broke at or above your fail threshold. ## Use in CI (GitHub Actions) The quickest way is the bundled composite action (installs the browser + runs buttonmash + uploads the report): name: buttonmash on: [pull_request] jobs: buttonmash: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v5 # start your app under test here (e.g. npm ci && npm run start &) and wait for it… - uses: cj-vana/buttonmash@v0.1.8 with: target: http://localhost:3000 args: --seed ci --max-actions 800 Or wire it by hand for full control: name: buttonmash on: [pull_request] jobs: buttonmash: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v5 with: { node-version: 20, cache: npm } - run: npm ci - run: npx playwright install --with-deps chromium # start your app under test here (e.g. npm run start &) and wait for it… - run: npx buttonmash run http://localhost:3000 --seed ci --fail-on high env: # storageState captured locally and stored as a secret (base64 or file) STORAGE_STATE: ${{ secrets.BUTTONMASH_STORAGE_STATE }} - uses: actions/upload-artifact@v5 if: ${{ !cancelled() }} with: { name: buttonmash-report, path: buttonmash-report/ } buttonmash auto-detects GitHub Actions and emits inline annotations plus a job-summary table. The non-zero exit code fails the job. ## Crawling the whole site By default buttonmash **auto-crawls**: starting from your target, it discovers every same-origin `` link *and* every client-side route the app navigates to via buttons/`navigate()` (it hooks `pushState`/`popstate`), queues them, and works through them breadth-first. When the link frontier runs dry it returns to the start and keeps clicking — so button-driven SPA shells (where the nav isn't ``) still get fully covered. One run, the whole reachable site: npx buttonmash run https://staging.example.com # crawls everything it can reach Controls: - `budget.maxPages` — cap on distinct pages per run (default 100), so CI stays bounded. - `routes` — optional **hints**: pages nothing links to (e.g. a deep editor URL). They seed the frontier; the crawl finds the rest. Also available as `--route `. - `explore.crawl: false` — disable auto-crawl and only sweep `target` + `routes`. Dangerous paths (logout/delete/cancel) and off-origin URLs are never enqueued. Discovery also reaches **inside open shadow DOM** (web-component design systems — Salesforce LWC, Ionic, Shoelace/Lit/Material Web) and **same-origin iframes** (embedded editors, wizards), so component-based apps aren't invisible to it. It's built to survive messy real apps on long CI sweeps: it **recovers from renderer crashes** (recreates the page and continues, skipping the page that crashed), opens **custom ARIA dropdowns** and picks an option, **declines file pickers** so a file input can't hang the run, and you can **scope the crawl** with `guardrails.includePaths` / `excludePaths`. ## Self-populating (form completion) A fresh app is mostly empty lists — so buttonmash **creates its own data**. When it finds a fillable form (or opens a "New/Add/Create" flow), it fills every required field — and a fraction of optional ones — with **valid, deterministic** values inferred from each field's type/label/pattern/min-max/options (real emails, in-range numbers, seeded dates, a chosen `