Skip to content

Testing

Lokus uses Vitest for unit tests and Playwright for end-to-end tests, with @testing-library/react for component testing.

src/
├── core/
│ ├── config/store.test.js # Settings persistence
│ ├── theme/manager.test.js # Theme switching
│ ├── shortcuts/registry.test.js # Keyboard shortcut registration
│ ├── editor/live-settings.test.js # Real-time editor settings
│ └── clipboard/shortcuts.test.js # Clipboard operations
├── components/
│ └── CommandPalette.test.jsx # Command palette UI
└── utils/
└── markdown.test.js # Markdown parsing
tests/
├── e2e/
│ ├── app-navigation.spec.js # Route and sidebar navigation
│ ├── math-rendering.spec.js # KaTeX inline/block math
│ ├── editor-functionality.spec.js # Typing, formatting, wiki links
│ ├── preferences.spec.js # Settings dialog
│ ├── markdown-paste.spec.js # Rich text paste handling
│ └── file-operations.spec.js # Create, open, save, delete files
├── unit/
│ ├── editor-utils.test.js # Editor utility functions
│ └── math.test.js # Math rendering logic
└── setup.js # Global test setup and mocks
Terminal window
npm test # Run all unit tests once
npm run test:watch # Watch mode (re-runs on file changes)
npm run test:watch:silent # Watch mode with minimal output
npm run test:summary # Verbose output with test names
npm run test:failures # Show only failures

Run a specific test file:

Terminal window
npx vitest run src/core/config/store.test.js
Terminal window
npm run test:e2e # Headless (no visible browser)
npm run test:e2e:headed # Visible browser window
npm run test:e2e:ui # Playwright UI mode (interactive)

Run the dev server and tests side by side:

Terminal window
npm run dev:test

This uses concurrently to start Vite and Vitest watch mode in parallel.

Unit tests run in jsdom with comprehensive mocks for Tauri APIs and browser APIs.

The test setup (tests/setup.js) stubs Tauri’s IPC layer:

global.window.__TAURI_INTERNALS__ = {
invoke: vi.fn()
}
global.window.__TAURI_METADATA__ = {
currentWindow: { label: 'main' }
}

These browser APIs are mocked for consistent test results:

  • navigator.clipboard (read/write)
  • ResizeObserver
  • IntersectionObserver
  • matchMedia
  • CSS.supports
{
environment: 'jsdom',
setupFiles: ['./tests/setup.js'],
globals: true,
css: true,
testTimeout: 10000,
coverage: {
reporter: ['text', 'json', 'html'],
exclude: ['node_modules/', 'tests/', 'src-tauri/']
}
}
{
testDir: './tests/e2e',
timeout: 30000,
use: {
browserName: 'chromium',
headless: true,
screenshot: 'only-on-failure'
}
}
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import MyComponent from './MyComponent'
describe('MyComponent', () => {
it('renders the button', () => {
render(<MyComponent />)
expect(screen.getByRole('button')).toBeInTheDocument()
})
})
import { test, expect } from '@playwright/test'
test('creates a new note', async ({ page }) => {
await page.goto('/')
await page.click('[data-testid="new-note"]')
await expect(page.locator('.editor')).toBeVisible()
})
Terminal window
npx vitest run --reporter=verbose # Detailed unit test output
npx playwright test --headed # See the browser during E2E
npx playwright test --debug # Step through E2E tests

Common fixes for flaky tests:

  • Clear mocks between tests with vi.clearAllMocks()
  • Use explicit waits (await expect(...).toBeVisible()) instead of fixed delays
  • Increase testTimeout for slow CI environments