Morphee — Testing Guide
Comprehensive testing documentation for Morphee backend and frontend.
📋 Testing Overview
Morphee uses a multi-layered testing approach across backend, frontend, and Tauri Rust:
Backend (Python / pytest)
- Unit Tests - Individual components in isolation
- Integration Tests - API endpoints and system integration
- Performance Tests - Load and stress testing
Frontend (TypeScript / Vitest + Playwright)
- Unit Tests - Stores, hooks, and lib utilities (Vitest + jsdom)
- E2E Tests - Full user flows in real browser (Playwright + Chromium)
- Coverage - v8 provider via @vitest/coverage-v8
Current Test Counts
| Layer | Framework | Tests | Coverage |
|---|---|---|---|
| Backend | pytest + pytest-asyncio | 2014 | 84% overall |
| Tauri Rust | cargo test | 140 | embeddings, vector, git, vault, fs, tokenizer, llm, model_manager (140 desktop + 9 mobile-ml) |
| Frontend Unit | Vitest (jsdom) | 3016 | 105 test files, 75% statements, 68% branches, 77% functions |
| Frontend E2E | Playwright (Chromium) | 60 | Auth, tasks, chat, mobile, conversations, search, settings, spaces |
| Total | 5230 |
Coverage Goals
- Backend: >80% (currently 84%)
- Frontend overall: >75% statements (currently 75.29%), >65% branches (currently 68.18%)
- Frontend stores/hooks/lib: >80% (currently 85-100%)
- Frontend components: unit tests + E2E coverage
🚀 Quick Start
Running All Tests
# Run all tests
cd backend
pytest
# Run with coverage
pytest --cov=. --cov-report=html
# Run specific test file
pytest tests/integration/test_api_endpoints.py
# Run specific test class
pytest tests/integration/test_api_endpoints.py::TestHealthEndpoint
# Run specific test
pytest tests/integration/test_api_endpoints.py::TestHealthEndpoint::test_health_check_returns_200
Running Tests in Docker
# Run tests in Docker container
docker compose -f docker-compose.dev.yml exec backend pytest
# With coverage
docker compose -f docker-compose.dev.yml exec backend pytest --cov
# Watch mode — requires pytest-watch (not in default requirements)
# docker compose -f docker-compose.dev.yml exec backend ptw
📁 Test Structure
backend/tests/
├── __init__.py
├── conftest.py # Shared fixtures
├── test_*.py # Unit tests (30 files)
├── integration/ # Integration tests
│ ├── __init__.py
│ ├── test_api_auth.py
│ ├── test_api_endpoints.py
│ ├── test_api_groups.py
│ ├── test_api_tasks_auth.py
│ ├── test_interface_system.py
│ └── test_websocket_auth.py
└── performance/ # Performance tests
└── test_load.py
🧪 Test Types
1. Unit Tests
Test individual components in isolation.
Location: backend/tests/test_*.py
Examples:
test_task_models.py- Task model validationtest_event_bus.py- Event bus pub/subtest_interfaces.py- Interface definitions
Running:
pytest tests/test_task_models.py -v
2. Integration Tests
Test API endpoints and component interactions.
Location: backend/tests/integration/
What's Tested:
- HTTP endpoint responses
- Interface registration and execution
- Event publishing
- Error handling
- WebSocket connections
Running:
pytest tests/integration/ -v
3. Performance Tests
Test system under load.
Location: backend/tests/performance/
What's Tested:
- Concurrent request handling
- Response times under load
- Event bus throughput
- Interface execution performance
Running:
pytest tests/performance/ -v --timeout=300
🎯 Testing Specific Features
Testing Interface System
# Test interface registration
pytest tests/integration/test_interface_system.py::TestInterfaceManager
# Test action execution
pytest tests/integration/test_interface_system.py::TestActionExecutor
# Test concurrent execution
pytest tests/integration/test_interface_system.py::TestConcurrentExecution
Testing API Endpoints
# Test health endpoint
pytest tests/integration/test_api_endpoints.py::TestHealthEndpoint
# Test interface endpoints
pytest tests/integration/test_api_endpoints.py::TestInterfaceEndpoints
# Test action execution
pytest tests/integration/test_api_endpoints.py::TestActionExecution
# Test error handling
pytest tests/integration/test_api_endpoints.py::TestErrorHandling
Testing Event System
# Test event bus
pytest tests/test_event_bus.py -v
# Test event publishing
pytest tests/test_task_processor.py::test_event_publishing -v
🔧 Test Configuration
pytest.ini
[pytest]
asyncio_mode = auto
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
-v
--strict-markers
--tb=short
--cov-report=term-missing
markers =
slow: marks tests as slow (deselect with '-m "not slow"')
integration: marks tests as integration tests
performance: marks tests as performance tests
Coverage Configuration
Create .coveragerc:
[run]
source = .
omit =
tests/*
*/site-packages/*
*/__pycache__/*
[report]
exclude_lines =
pragma: no cover
def __repr__
raise AssertionError
raise NotImplementedError
if __name__ == .__main__.:
📊 Test Results
Viewing Coverage Reports
# Generate HTML coverage report
pytest --cov --cov-report=html
# Open in browser
open htmlcov/index.html
CI/CD Integration
Add to .github/workflows/ci.yml:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies
run: |
cd backend
pip install -r requirements.txt
- name: Run tests
run: |
cd backend
pytest --cov --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
✅ Test Best Practices
1. Test Naming
- Use descriptive names:
test_execute_echo_action_returns_correct_result - Follow pattern:
test_<what>_<expected_behavior> - Group related tests in classes
2. Test Independence
- Tests should not depend on each other
- Use fixtures for setup/teardown
- Clean up resources after tests
3. Async Testing
@pytest.mark.asyncio
async def test_async_function():
result = await some_async_function()
assert result is not None
4. Fixtures
@pytest.fixture
async def test_interface():
"""Create test interface"""
interface = TestInterface()
await manager.register_interface(interface)
yield interface
await manager.unregister_interface(interface.name)
5. Parametrized Tests
@pytest.mark.parametrize("input,expected", [
("hello", "HELLO"),
("world", "WORLD"),
("test", "TEST"),
])
async def test_uppercase(input, expected):
result = await uppercase_function(input)
assert result == expected
🐛 Debugging Failed Tests
Run with Verbose Output
pytest -vv tests/integration/test_api_endpoints.py
Run with Print Statements
pytest -s tests/integration/test_api_endpoints.py
Run with Debugger
pytest --pdb tests/integration/test_api_endpoints.py
Stop on First Failure
pytest -x tests/integration/
📝 Writing New Tests
Template for Integration Test
"""
Test description
"""
import pytest
from httpx import AsyncClient, ASGITransport
@pytest.fixture
async def client():
from main import app
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
@pytest.mark.asyncio
class TestFeature:
"""Test feature description"""
async def test_something(self, client):
"""Test should do something"""
response = await client.get("/endpoint")
assert response.status_code == 200
data = response.json()
assert data["field"] == "expected_value"
Template for Unit Test
"""
Test module description
"""
import pytest
@pytest.mark.asyncio
async def test_function():
"""Test should validate something"""
result = await function_to_test()
assert result is not None
assert result.status == "expected"
🎯 Test Checklist
Before committing code, ensure:
- All tests pass locally
- New features have tests
- Bug fixes have regression tests
- Tests are independent
- Coverage remains >80%
- No print statements in tests (use logging)
- Async tests use
@pytest.mark.asyncio - Fixtures clean up resources
📚 Additional Resources
🆘 Common Issues
Issue: Tests timeout
Solution: Increase timeout or mark as slow:
@pytest.mark.slow
@pytest.mark.timeout(300)
async def test_slow_operation():
...
Issue: Database connection errors
Solution: Ensure test database is running:
docker compose -f docker-compose.dev.yml up -d supabase-db
Issue: Import errors
Solution: Run tests from backend directory:
cd backend && pytest
Issue: Async tests not working
Solution: Add @pytest.mark.asyncio decorator:
@pytest.mark.asyncio
async def test_async_function():
...
🖥️ Frontend Testing
Framework Setup
- Unit Tests: Vitest 4 + jsdom + @testing-library/react
- E2E Tests: Playwright 1.58 + Chromium
- Coverage: @vitest/coverage-v8
- Config:
frontend/vitest.config.ts,frontend/playwright.config.ts
Running Frontend Tests
cd frontend
# Unit tests (3016 tests)
npm run test
# Unit tests in watch mode
npm run test:watch
# Coverage report
npm run test:coverage
# E2E tests (60 tests, requires running backend)
npm run test:e2e
# E2E tests with UI
npm run test:e2e:ui
Frontend Test Structure
frontend/
├── src/
│ ├── store/__tests__/
│ │ ├── authStore.test.ts # 5 tests - auth state management
│ │ ├── taskStore.test.ts # 11 tests - task state management
│ │ ├── spaceStore.test.ts # 11 tests - space state management
│ │ ├── chatStore.test.ts # 29 tests - chat state + tool calls + approvals
│ │ ├── notificationStore.test.ts # 9 tests - notification state + unread count
│ │ └── componentStore.test.ts # 26 tests - component lifecycle + render/update/dismiss
│ ├── hooks/__tests__/
│ │ ├── useAuth.test.ts # 13 tests - auth hook + SSO
│ │ ├── useTasks.test.ts # 12 tests - task hook + WebSocket events
│ │ ├── useSpaces.test.ts # 5 tests - space hook
│ │ ├── useChat.test.ts # 38 tests - chat hook + streaming + tool callbacks + edit/regenerate
│ │ ├── useOnboarding.test.ts # 9 tests - onboarding hook + SSE
│ │ ├── useNotifications.test.ts # 8 tests - notification hook + WS events
│ │ └── use-toast.test.ts # 12 tests - toast hook
│ ├── lib/__tests__/
│ │ ├── auth.test.ts # 12 tests - token helpers
│ │ ├── api.test.ts # 86 tests - API client + interceptor + notifications + OAuth + push
│ │ ├── runtime.test.ts # 21 tests - Tauri runtime detection + mobile/desktop/hasLocalML
│ │ ├── memory-client.test.ts # 10 tests - memory client routing
│ │ ├── fs-client.test.ts # 12 tests - filesystem client routing
│ │ ├── component-events.test.ts # 21 tests - event tier classification + dispatch
│ │ ├── sse.test.ts # 16 tests - SSE client + streaming + reconnect
│ │ ├── websocket.test.ts # 19 tests - WebSocket client + auth + reconnect
│ │ └── utils.test.ts # 49 tests - humanizeToolCall + formatToolName + utilities
│ ├── components/auth/__tests__/
│ │ └── AuthForm.test.tsx # 13 tests - auth form rendering + SSO buttons
│ ├── components/chat/__tests__/
│ │ ├── ToolCallCard.test.tsx # 20 tests - tool call rendering + humanized display
│ │ ├── ApprovalCard.test.tsx # 9 tests - approval UI + callbacks + timeout
│ │ ├── ComponentRegistry.test.tsx # 9 tests - component registry
│ │ ├── InfoCard.test.tsx # 6 tests - info card rendering
│ │ ├── TaskListRenderer.test.tsx # 5 tests - task list rendering
│ │ ├── ChatBubble.test.tsx # 23 tests - message rendering + markdown + tool calls + edit/copy
│ │ ├── ConversationList.test.tsx # 9 tests - conversation list + CRUD + active state
│ │ ├── PinnedMessages.test.tsx # 10 tests - pinned messages display
│ │ ├── ConversationSettingsDialog.test.tsx # 8 tests - conversation settings
│ │ └── renderers.test.tsx # 70 tests - all 10 component renderers via ComponentRenderer
│ ├── components/layout/__tests__/
│ │ ├── Sidebar.test.tsx # 3 tests - sidebar rendering
│ │ ├── MobileSidebar.test.tsx # 3 tests - mobile sidebar
│ │ ├── Header.test.tsx # 7 tests - header rendering
│ │ ├── NotificationBell.test.tsx # 22 tests - notification bell + badge + dropdown
│ │ ├── FeatureTour.test.tsx # 11 tests - feature tour overlay
│ │ ├── Breadcrumbs.test.tsx # 6 tests - breadcrumb navigation
│ │ └── BottomNav.test.tsx # 4 tests - mobile bottom navigation
│ ├── components/settings/__tests__/
│ │ ├── GoogleConnect.test.tsx # 13 tests - OAuth connection flow
│ │ └── InterfaceConfigCard.test.tsx # 9 tests - interface config CRUD
│ ├── components/tasks/__tests__/
│ │ ├── TaskList.test.tsx # 9 tests - task list rendering
│ │ ├── TaskDetail.test.tsx # 31 tests - task detail view + inline editing
│ │ ├── CreateTaskDialog.test.tsx # 12 tests - task creation dialog
│ │ └── ConnectionStatus.test.tsx # 9 tests - WebSocket connection status
│ ├── components/spaces/__tests__/
│ │ ├── SpaceList.test.tsx # 7 tests - space list rendering
│ │ ├── SpaceDetail.test.tsx # 12 tests - space detail view
│ │ ├── SpaceCard.test.tsx # 3 tests - space card rendering
│ │ └── CreateSpaceDialog.test.tsx # 5 tests - space creation dialog
│ ├── components/ui/__tests__/
│ │ └── empty-state.test.tsx # 5 tests - empty state component
│ ├── components/search/__tests__/
│ │ └── SearchDialog.test.tsx # 10 tests - global search dialog
│ └── pages/__tests__/
│ ├── Dashboard.test.tsx # 15 tests - dashboard stats + recent tasks + conversations
│ ├── Login.test.tsx # 3 tests - login page
│ ├── AuthCallback.test.tsx # 8 tests - SSO callback + token extraction
│ ├── Settings.test.tsx # 24 tests - profile + notifications + integrations + oauth
│ ├── Chat.test.tsx # 15 tests - chat page + skeleton loaders + retry + search
│ ├── Tasks.test.tsx # 10 tests - task page + sorting
│ ├── Spaces.test.tsx # 8 tests - spaces page
│ ├── Onboarding.test.tsx # 9 tests - onboarding page
│ ├── AcceptInvite.test.tsx # 5 tests - invite acceptance
│ ├── ParentConsentPending.test.tsx # 7 tests - parent consent pending
│ └── ParentConsentVerify.test.tsx # 11 tests - parent consent verify
├── e2e/
│ ├── auth.spec.ts # 8 tests - signup, login, logout, onboarding flow
│ ├── tasks.spec.ts # 7 tests - create, detail, filter, dashboard
│ └── chat.spec.ts # 7 tests - empty state, send, streaming, error
└── vitest.config.ts # Vitest + coverage config
Frontend Coverage Report
Run: npm run test:coverage
| File / Area | Stmts | Branch | Funcs | Lines |
|---|---|---|---|---|
src/store/authStore.ts | 100% | 100% | 100% | 100% |
src/store/taskStore.ts | 100% | 100% | 100% | 100% |
src/store/spaceStore.ts | 100% | 100% | 100% | 100% |
src/store/chatStore.ts | 100% | 100% | 100% | 100% |
src/store/notificationStore.ts | 100% | 100% | 100% | 100% |
src/store/componentStore.ts | 100% | 100% | 100% | 100% |
src/types/index.ts | 100% | 100% | 100% | 100% |
src/lib/auth.ts | 100% | 90% | 100% | 100% |
src/hooks/useAuth.ts | 94% | 100% | 100% | 94% |
src/hooks/useTasks.ts | 86% | 38% | 100% | 87% |
src/hooks/useSpaces.ts | ~85% | ~40% | 100% | ~85% |
src/hooks/useChat.ts | ~80% | ~50% | 100% | ~80% |
src/lib/api.ts | 76% | 91% | 33% | 76% |
| Chat components | ~70% | ~50% | 100% | ~70% |
| Other Components / Pages | 0% | 0% | 0% | 0% |
Strategy: Stores, hooks, and lib are well covered by unit tests. Chat components (ToolCallCard, ApprovalCard, ChatBubble, ConversationList, all 10 renderers) have dedicated unit tests. SSE and WebSocket clients have full unit test coverage. Other components and pages are covered by E2E tests (22 Playwright tests) which aren't reflected in the unit coverage numbers.
Last Updated: February 14, 2026