Testing Guide

This guide covers testing practices, patterns, and commands for the Skelenote codebase.

Test Philosophy

Testing Pyramid

Skelenote follows the testing pyramid approach:

  1. Unit Tests (majority) - Test individual functions, utilities, and pure business logic

  2. Integration Tests - Test module interactions, hooks with mocked contexts

  3. Component Tests - Test React components with mocked dependencies

  4. E2E Tests - Currently manual; future Playwright/Tauri integration

What to Test

Always test:

  • Pure functions (utilities, calculations, transformations)

  • Business logic (recurrence calculations, search algorithms, CRDT operations)

  • Data transformations (import/export, serialization)

  • Custom hooks (state management, side effects)

  • Component behavior (user interactions, conditional rendering)

Test selectively:

  • UI styling (prefer visual regression in the future)

  • Third-party library behavior (trust their tests)

  • Generated code or trivial wrappers

Skip testing:

  • Type definitions (TypeScript handles this)

  • Constants and configuration

  • One-line passthrough functions

Test Isolation

  • Each test should be independent and not rely on state from other tests

  • Use beforeEach to reset mocks and state

  • Avoid shared mutable state between tests

  • Tests should pass in any order

Running Tests

Frontend Tests (Vitest)

Rust Tests (Cargo)

Coverage

Frontend coverage uses Istanbul:

Rust coverage with cargo-tarpaulin (install first):

Benchmark Suites

Performance benchmarks ensure critical operations remain fast. Run all benchmarks with:

Or run individual benchmark files:

Available Benchmark Suites

Suite
File
What it Measures

ObjectStore

src/lib/loro/__tests__/store.bench.ts

CRUD operations, bulk creation, query performance

CRDT Merge

src/lib/loro/__tests__/store.bench.ts

Loro document merge with 100-1000 objects

Fuzzy Search

src/lib/search/__tests__/search.bench.ts

Fuse.js indexing and query times (100-5000 items)

Vector Index

src/lib/semantic/__tests__/vector.bench.ts

Semantic search insert/search (100-5000 vectors)

Task Recurrence

src/lib/tasks/__tests__/recurrence.bench.ts

Next due date calculations

Markdown Import

src/lib/import/__tests__/import.bench.ts

Parsing and importing markdown files

Markdown Export

src/lib/export/__tests__/export.bench.ts

BlockNote to markdown conversion

Templates

src/lib/templates/__tests__/templates.bench.ts

Template queries and object creation

Daily Notes

src/lib/daily/__tests__/daily.bench.ts

Daily note ID generation and retrieval

Sync Operations

src/lib/sync/__tests__/sync.bench.ts

CRDT export, import, and merge

Writing Benchmarks

Test Patterns

Mocking Tauri Commands

Tauri commands are invoked via @tauri-apps/api/core. Mock the invoke function:

Testing Custom Hooks

Use @testing-library/react with renderHook:

Testing React Components

Components need MantineProvider and jsdom environment:

Testing Pure Functions

The simplest tests - no mocking needed:

Testing with ObjectStore

Create isolated stores for testing data operations:

Integration Tests

Testing Module Interactions

Integration tests verify multiple modules work together correctly:

Testing CRDT Sync

Test that documents merge correctly:

Writing Tests for New Features

Checklist

When adding a new feature, create tests for:

  1. Core logic - Pure function unit tests

  2. Edge cases - Null inputs, empty arrays, boundary conditions

  3. Error handling - Invalid inputs, failed operations

  4. Integration - How it interacts with existing modules

  5. Performance - Add benchmarks if the feature is performance-sensitive

Test File Organization

Tests are co-located with source files:

Or alongside the file:

Naming Conventions

  • Test files: *.test.ts or *.spec.ts

  • Benchmark files: *.bench.ts

  • Test directories: __tests__/

Test Structure

Follow the Arrange-Act-Assert pattern:

Test Descriptions

Write descriptive test names that explain the expected behavior:

Rust Test Patterns

Unit Tests in Rust

Rust tests live in the same file as the implementation:

Testing Tauri Commands

Test the underlying functions, not the Tauri command wrappers:

Environment Configuration

Vitest Configuration

The test configuration in vite.config.ts:

Per-File Environment Override

Use the magic comment at the top of files that need jsdom:

Troubleshooting

Common Issues

"Cannot find module" errors:

  • Check path aliases (@/) are correctly resolved

  • Verify the import path is correct

"window is not defined":

  • Add @vitest-environment jsdom comment to the file

  • Or add the file pattern to environmentMatchGlobs

Mantine components fail to render:

  • Wrap with MantineProvider

  • Mock window.matchMedia (see Component Testing section)

Tauri invoke errors:

  • Mock @tauri-apps/api/core before importing modules that use it

  • Clear mocks in beforeEach

Tests hang or timeout:

  • Check for unresolved promises

  • Ensure mocks return values (not undefined)

  • Use vi.useFakeTimers() for time-dependent code

Debugging Tests

Last updated