This directory contains comprehensive tests for the Worklog project using Vitest.
# Run all tests once
npm test
# Run tests in watch mode (auto-rerun on file changes)
npm run test:watch
# Run tests with coverage report
npm run test:coverage
# Generate per-test timings
npm run test:timings
# or
node ./scripts/test-timings.cjsUse npm run test:timings (or node ./scripts/test-timings.cjs) to run Vitest with the JSON reporter and write test-timings.json at the repository root.
The report shape is intentionally small:
{
"generatedAt": "2026-05-21T12:00:00.000Z",
"rows": [
{ "file": "tests/cli/status.test.ts", "title": "reports status output", "durationMs": 842 }
]
}Use durationMs to find slow tests. As a rule of thumb, treat tests over 5s as candidates to refactor, mock more aggressively, or move to integration-only coverage.
cat test-timings.json | jq '.rows | sort_by(-.durationMs) | .[0:20]'Related work:
WL-0MLLG2HTE1CJ71LZ— parent epic for reducing test-suite runtime and preventing CI timeouts.WL-0MLLHF9GX1VYY0H0— direct child task that implements the collector and report under that epic.WL-0MLIGVY450A3936K— audit/precedent item used to validate the 5s threshold and output shape.
Tests are spread across two top-level directories:
tests/— the main test directory (this folder)test/— additional tests (migrations, TUI integration, doctor checks, etc.)
database.test.ts— WorklogDatabase CRUD, queries, comments, parent-child relationshipsjsonl.test.ts— JSONL import/export, backward compatibility, round-trip integritysync.test.ts— Work item merging, field-level conflict resolution, tag/comment mergingsync-worktree.test.ts— Git worktree sync scenariosconfig.test.ts— Configuration loading, defaults, validation, prefix managementvalidator.test.ts— Work item field validation rulesfts-search.test.ts— Full-text search across titles, descriptions, comments, tagssort-operations.test.ts— Sort index operations and rebalancinggrouping.test.ts— Work item grouping logicfile-lock.test.ts— File locking and concurrent accessnormalize-sqlite-bindings.test.ts— SQLite binding normalizationplugin-loader.test.ts/plugin-integration.test.ts— Plugin discovery and loadinggithub-*.test.ts— GitHub sync, push state, pre-filter, comments, deleted items, self-link, output
issue-management.test.ts— End-to-end create/update/delete/show workflowsissue-status.test.ts— Status transitionsstatus.test.ts—wl statuscommand outputteam.test.ts— Team/sync CLI commandscreate-description-file.test.ts—--description-fileflaginit.test.ts—wl initworkflowfresh-install.test.ts— Clean install scenarioupdate-batch.test.ts— Batch update operationsupdate-do-not-delegate.test.ts— Do-not-delegate flag handlingreviewed.test.ts—wl reviewedtogglemisc.test.ts— Miscellaneous CLI edge caseshelpers-tree-rendering.test.ts— Tree display formattingaction-opts-normalization.test.ts— Option normalizationinproc-harness.test.ts/debug-inproc.test.ts— In-process test harnessinitialization-check.test.ts— Pre-init guardvalid-json-output.test.ts— Comprehensive validation that every--jsoncommand emits pure, parseable JSON with no preamble textunlock.test.ts— Lock file removalgit-mock-roundtrip.test.ts— Git mock for sync testsgithub-*.test.ts— GitHub push/filter CLI tests
tui-state.test.ts/state.test.ts— TUI state managementcontroller.test.ts— TUI controller logiclayout.test.ts— Layout renderingfilter.test.ts— Item filteringmove-mode.test.ts— Move/reparent modeautocomplete.test.ts/autocomplete-widget.test.ts— Autocompleteagent-*.test.ts— Agent integration, prompt, sessions, layoutpersistence*.test.ts— TUI persistencefocus-cycling-integration.test.ts— Focus cyclingwidget-create-destroy*.test.ts— Widget lifecyclestatus-stage-validation.test.ts— Status/stage rule enforcement in TUItui-update-dialog.test.ts— Update dialogtui-mouse-guard.test.ts— Mouse event handlingshutdown-flow.test.ts/event-cleanup.test.ts— Cleanup on exitnext-dialog-wrap.test.ts— Next dialog wrappingtoggle-do-not-delegate.test.ts— Do-not-delegate toggle in TUI
migrations.test.ts— Database migration testsdoctor-dependency-check.test.ts/doctor-status-stage.test.ts—wl doctorcheckscomment-update.test.ts— Comment update operationsvalidator.test.ts— Additional validation teststui-integration.test.ts— TUI integrationtui-opencode-sse-handler.test.ts— OpenCode SSE handler (legacy)tui-chords.test.ts— Keyboard chord handlingtui-style.test.ts— TUI styling
Current test coverage: 894 tests passing, 0 skipped across 82 test files.
The test-utils.ts file provides shared utilities for tests:
createTempDir()- Creates a temporary directory for test isolationcleanupTempDir(dir)- Cleans up temporary directories after testscreateTempJsonlPath(dir)- Generates a temp path for JSONL filescreateTempDbPath(dir)- Generates a temp path for database fileswait(ms)- Async delay utility
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { createTempDir, cleanupTempDir } from './test-utils.js';
describe('MyFeature', () => {
let tempDir: string;
beforeEach(() => {
tempDir = createTempDir();
// Setup code
});
afterEach(() => {
cleanupTempDir(tempDir);
});
it('should do something', () => {
// Test code
expect(result).toBe(expected);
});
});- Isolate tests - Each test should be independent and use temp directories
- Clean up - Always clean up temp files and directories in
afterEach - Descriptive names - Test names should clearly describe what is being tested
- Arrange-Act-Assert - Structure tests with clear setup, execution, and verification phases
- Test edge cases - Include tests for error conditions and boundary cases
Tests run automatically on:
- Pull requests
- Pushes to main branch
- Manual workflow dispatch
None at this time. All 894 tests pass with 0 skipped.
- Add API endpoint integration tests
- Increase code coverage measurement
- Add mutation testing
Some tests in this repository are intentionally long-running (load or simulation tests) and are gated so they do not run in CI by default. The gating mechanism is implemented in tests/test-utils.ts:
- Wrapper helpers:
describeLong(name, fn)anditLong(name, fn)– these skip the suite/test unless the environment variableWL_RUN_LONG_TESTSis set totrue. - Naming convention: long tests often use the
.long.test.tsfilename suffix (for discoverability), but the gate is enforced by the helper functions above.
How to run long or gated tests locally:
-
Run all tests but skip long tests (default CI behaviour):
npm test
-
Run the full test-suite including long tests:
WL_RUN_LONG_TESTS=true npm test
-
Run only the long tests (by filename pattern):
WL_RUN_LONG_TESTS=true npx vitest run "tests/**/*.long.test.ts"
-
Run a single long test file:
WL_RUN_LONG_TESTS=true npx vitest run tests/github-sync-load.long.test.ts
Running subsets of tests
-
Run unit tests only (tests under
tests/):npx vitest run tests
-
Run integration tests only (tests under
test/):npx vitest run test
End-to-end tests exercise the real wl CLI and verify agent-driven flows:
# Run all E2E tests
npx vitest run testsThe Pi-based TUI E2E tests (tests/e2e/headless-tui.test.ts — built-CLI
wl list/next/show smoke tests, and tests/e2e/agent-flow.test.ts —
chat-pane/action-palette routing) were removed together with the TUI; work
item browsing now lives in the Herdr plugin (packages/herdr/).
Guidance for authors
- Mark legitimately long simulations with the
describeLong/itLonghelpers fromtests/test-utils.ts. This ensures CI remains fast and reliable while still allowing engineers to run exhaustive load simulations locally when needed. - Keep long tests deterministic: use injectable clocks, network stubs, and spies rather than real external services.
- Prefer splitting long integration/load tests into separate files (or
.long.test.tssuffix) so they are easy to find and run.
Example
import { describeLong, itLong } from './test-utils.ts';
describeLong('github-sync long load simulations (gated)', () => {
itLong('schedules many calls through throttler under simulated load', async () => {
// ...long-running simulation here
});
});