Testing
Kangentic uses a three-tier testing strategy. Each tier targets a different layer of the application, and each has its own tradeoffs between speed, coverage, and realism.
Test tiers
Section titled “Test tiers”The four test tiers and the captures project, from pure logic to a real Electron window.
Unit tests
Section titled “Unit tests”npm run test:unit- Runner: Vitest
- Speed: Sub-second
- What to test: Pure logic - parsers, filters, state machines, utility functions
- No build needed, no browser, no Electron
Unit tests live in tests/unit/. They’re fast and focused - test a function’s input/output without any framework overhead.
There is also an opt-in integration tier in tests/integration/. Those tests hit real CLIs, the file system, or the network, so while vitest.config.ts includes them, npm run test:unit pins the tests/unit path and skips them. Run them on demand with npx vitest run tests/integration/.
UI tests
Section titled “UI tests”npx playwright test --project=ui- Runner: Playwright with headless Chromium
- Speed: Fast - hundreds of tests run headless in parallel
- What to test: React components, forms, dialogs, drag-and-drop, board interactions
- No build needed - runs against the Vite dev server (auto-started by Playwright)
UI tests live in tests/ui/. They use a full in-memory mock of window.electronAPI (injected via addInitScript()) that supports complete CRUD for projects, tasks, swimlanes, automations, sessions, config, and attachments.
Use window.__mockPreConfigure(fn) to set up mock state before React mounts.
E2E tests
Section titled “E2E tests”npm run buildnpx playwright test --project=electron- Runner: Playwright with
_electron.launch() - Speed: Slower - opens real windows (no headless mode on Windows)
- What to test: PTY sessions, terminal rendering, session lifecycle, shell detection, config persistence
- Build required before running
E2E tests live in tests/e2e/. These exercise the full Electron application with real IPC, real file I/O, and real terminal sessions.
Web demo smoke
Section titled “Web demo smoke”npm run build:demonpm run test:demoPlaywright’s config also declares a demo project (tests/demo/), the fourth alongside ui, electron, and captures, since v0.42.0. It is a small smoke tier for the static web build of the renderer that the website embeds: npm run build:demo writes dist/demo/, and the spec serves that folder itself on an ephemeral port and loads it in headless Chromium at the site frame’s 1600x1000 viewport, one worker, a handful of page loads. It has no entry in the shared webServer block on purpose, so a missing demo build surfaces as one named error rather than breaking the UI tier. CI runs it as its own always-on job, Web demo (build + smoke).
Captures
Section titled “Captures”npm run capturePlaywright also declares a captures project (tests/captures/, *.capture.ts files). It is not a test tier: it produces screenshots and screen recordings of staged app states for marketing and documentation. npm run capture builds the web demo first; the scene captures render against that static build, while the walkthrough and orchestration captures drive the same mocked renderer as the UI tier through the Vite dev server. Run it on demand.
Decision guide
Section titled “Decision guide”| What you’re testing | Tier |
|---|---|
| Pure function, parser, utility | Unit |
| Component rendering, user interaction, form validation | UI |
| Real IPC, PTY spawning, terminal output, file I/O | E2E |
Running all tests
Section titled “Running all tests”npm run test:unit # Unit tests (Vitest)npx playwright test --project=ui --project=electron # UI + E2E tests (Playwright)Continuous integration
Section titled “Continuous integration”Every push to main and every pull request runs the full suite on GitHub Actions. Lint, type check, and a production build run as three independently reported steps of one Lint, Typecheck, Build job, so a failing lint step never hides a type error. The web demo builds and smokes in its own job. The three test tiers run as sharded matrices on Linux (Node 24 since v0.44.0), each behind a thin gate job that is the required status check:
| Tier | Where it runs in CI | Shards |
|---|---|---|
| Unit (Vitest) | Linux | 3 |
| UI (Playwright, headless Chromium) | Linux | 11 |
| E2E (Electron) | Linux under xvfb | 5 |
The UI tier is the critical path: every other job finishes in about two minutes while the slowest UI shard runs closer to four, which is why it has the most shards. Playwright balances shards by test count, not by cost, so splitting a heavy spec file does not move it.
The E2E tier runs only on Linux in CI. The suite is platform-neutral, so Linux covers the macOS and Linux users while the team dogfoods the Windows build locally. On CI each E2E shard runs at workers=8; any local (non-CI) run, on every OS, pins the E2E project to workers=1 - Windows cannot run concurrent electron.launch(), and a local run on any platform should not spawn a swarm of app windows - so a local E2E run is serial and opens real windows.
See also
Section titled “See also”- Contributing - development setup and conventions
- Architecture - what the three tiers map onto
Kangentic is free and open source. A star helps other people find it.
Star on GitHub