| title | Testing |
|---|---|
| description | Testing strategy and patterns across all components |
clauderon uses a comprehensive testing strategy across all components: Rust backend/TUI, Web, and Mobile.
Run all tests by component:
# Rust (Backend + TUI/CLI)
cargo nextest run # All tests (faster than cargo test)
cargo nextest run --run-ignored all # Include E2E tests
mise run test-fast # via mise
# Web (TypeScript/React)
cd web && bun run test # All web tests
# Mobile (React Native)
cd mobile && bun test # Jest tests
# Full build + test (CI simulation)
dagger call ci44 test files across the codebase:
tests/directory: Integration and E2E tests (24 files)src/directory: Unit tests with#[cfg(test)](colocated with implementation)
cargo nextest run # Run all tests
cargo nextest run --run-ignored all # Include E2E tests
cargo nextest run -E 'test(/pattern/)' # Filter by pattern
mise run test-fast # Run via miseIf nextest is not installed: mise run setup-tools
- tokio-test: Async testing runtime
- proptest: Property-based testing
- tempfile: Temporary file/directory creation
- assert_cmd: CLI testing
- predicates: Assertion helpers
Colocated with implementation using #[cfg(test)]:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_function() {
// Test logic
}
}Example: src/utils/random.rs:46-76
Located in tests/ directory with #[tokio::test]:
Example: tests/api_tests.rs
Tests marked with #[ignore] skip when dependencies (Docker, Kubernetes) are unavailable:
Example: tests/e2e_docker.rs:44-77
#[tokio::test]
#[ignore]
async fn test_docker_session() {
skip_if_no_docker!();
// Test logic
}Mock backends for testing without external dependencies:
MockGitBackend: Simulates git operationsMockApiClient: Simulates API calls
Example: src/backends/mock.rs
Located in tests/common/mod.rs:
Functions to check if required dependencies are available:
pub fn docker_available() -> bool
pub fn kubernetes_available() -> boolGracefully skip tests when dependencies are unavailable:
skip_if_no_docker!()
skip_if_no_kubernetes!()pub fn init_git_repo(path: &Path)Automatic cleanup using Drop trait:
pub struct SpriteCleanupGuardReference: tests/common/mod.rs
9 test files across web packages:
web/frontend/src/: React component and utility testsweb/client/src/: Client library tests
cd web && bun run test # All web tests
cd web/frontend && bun test # Frontend only
cd web/client && bun test # Client onlyUses Bun's native bun:test runner:
- Fast execution
- Built-in TypeScript support
- Native mocking capabilities
import { describe, test, expect } from "bun:test";
describe("Component", () => {
test("should render correctly", () => {
// Test logic
});
});Example: web/frontend/src/components/ThemeToggle.test.tsx:7-24
Mocking localStorage and matchMedia:
const mockLocalStorage = {
getItem: () => null,
setItem: () => {},
};Example: web/client/src/ConsoleClient.test.ts:12-50
Controlled WebSocket behavior for testing:
class MockWebSocket {
readyState = WebSocket.CONNECTING;
onopen?: () => void;
onerror?: () => void;
// ...
}Example: web/client/src/ConsoleClient.test.ts:90-123
Using await new Promise(resolve => setTimeout(resolve, 0)) for async state updates.
Example: web/frontend/src/lib/claudeParser.test.ts
Creating test data with factory functions for consistency.
1 test file currently:
mobile/src/lib/historyParser.test.ts
Infrastructure ready for component tests (react-test-renderer installed).
cd mobile && bun test # Jest tests
cd mobile && bun test:windows # Windows-specificJest with @rnx-kit/jest-preset:
- React Native preset configuration
- TypeScript transformation
- Module name mapping
Example: mobile/src/lib/historyParser.test.ts
Located in /.dagger/src/index.ts (see clauderonCi function).
Pipeline Steps:
- Build docs package (
cd docs && bun run build) - Build web package (
cd web && bun run build) - Build Rust binary (
cargo build) - Run clippy (
cargo clippy) - Run tests (
cargo nextest run) - Create release artifacts
Optimized caching for faster builds:
- Cargo registry
- Git dependencies
- Target directory
- sccache (compiler cache)
dagger call ci- Purpose: Test individual functions/modules in isolation
- Location:
- Rust: In-module with
#[cfg(test)] - Web/Mobile: Colocated
.test.ts(x)files
- Rust: In-module with
- Characteristics: Fast, no external dependencies
- Purpose: Test component interactions
- Location:
- Rust:
tests/directory - Web/Mobile: Colocated with source files
- Rust:
- Characteristics: May use test helpers, mock dependencies
- Purpose: Test complete workflows with real dependencies
- Location: Rust
tests/directory - Characteristics:
- Marked with
#[ignore]attribute - Conditional execution based on environment
- Require Docker, Kubernetes, or other services
- Marked with
Check availability before running tests requiring external dependencies:
#[tokio::test]
#[ignore]
async fn test_requires_docker() {
skip_if_no_docker!();
// Test logic that requires Docker
}Reference: tests/common/mod.rs for availability checks and skip macros.
Reference: web/frontend/src/components/ThemeToggle.test.tsx:7-24
Mock localStorage, matchMedia, and other browser APIs:
const mockLocalStorage = {
getItem: vi.fn(),
setItem: vi.fn(),
};
Object.defineProperty(window, "localStorage", {
value: mockLocalStorage,
});Reference: web/client/src/ConsoleClient.test.ts:12-50
Create controlled WebSocket instances for testing connection states:
class MockWebSocket {
readyState = WebSocket.CONNECTING;
simulateOpen() {
this.readyState = WebSocket.OPEN;
this.onopen?.();
}
simulateError() {
this.onerror?.(new Event("error"));
}
}Use Drop trait for automatic cleanup:
pub struct TestCleanup {
path: PathBuf,
}
impl Drop for TestCleanup {
fn drop(&mut self) {
// Cleanup logic
}
}Example: SpriteCleanupGuard in tests/common/mod.rs
Install test tools:
mise run setup-toolsCheck Docker/Kubernetes availability:
docker ps # Check Docker
kubectl cluster-info # Check KubernetesE2E tests are designed to skip gracefully when dependencies are unavailable.
This is expected behavior when:
- Docker is not running
- Kubernetes cluster is not configured
- Required credentials are not available
To run E2E tests:
cargo nextest run --run-ignored allUse controlled async timing:
// Wait for async updates
await new Promise((resolve) => setTimeout(resolve, 0));Reference: web/client/src/ConsoleClient.test.ts