Unit tests
Vitest with happy-dom and the WXT test plugin. Fast, no browser, no Logseq. They run on every commit.
Logseq Copilot is tested at three levels. Each level covers what the one below cannot.
Unit tests
Vitest with happy-dom and the WXT test plugin. Fast, no browser, no Logseq. They run on every commit.
Integration tests
The extension’s Logseq client and service code against a real Logseq desktop app, for file graphs and DB graphs.
End-to-end tests
The built extension in Playwright’s Chromium and Firefox, against a real Logseq.
| Command | What it runs |
|---|---|
pnpm test |
The unit tests (vitest run) |
pnpm test:coverage |
The unit tests with V8 coverage |
pnpm typecheck |
tsc --noEmit |
pnpm test:integration |
The integration tests, against a real Logseq |
pnpm test:e2e |
Builds both browsers, then runs the E2E tests in Chromium and Firefox |
pnpm test:e2e:chromium, pnpm test:e2e:firefox |
Build and run the E2E tests of one browser |
pnpm test:e2e:run |
Run the E2E tests without building first |
The Git pre-commit hook (husky) runs pnpm test, so a commit with a failing unit test is rejected.
Unit tests sit next to the code they cover, as src/**/*.test.ts and src/**/*.test.tsx. They are configured in vitest.config.ts:
environment.WxtVitest(), from wxt/testing, is a Vite plugin that sets up what WXT code needs: the auto-imports and an in-memory fakeBrowser in place of the real browser APIs, such as storage, tabs and messaging.@testing-library/react) renders and drives the React components, in the options pages, the side panel and the content script UI.src/**/*.{ts,tsx}, except the test files and the generated UI primitives in src/components/ui. It writes a JSON summary, a text report and lcov.A typical test runs real code and fakes only the edge. For example src/entrypoints/background/background.test.ts runs the real background entrypoint against fakeBrowser and talks to it with the real @webext-core/messaging client, so message names and payloads are checked at both ends. Only the Logseq service is replaced.
The integration tests (tests/integration) run the extension’s Logseq client and service code against a real Logseq desktop app. They use their own Vitest config, vitest.integration.config.ts, which includes only tests/integration/**/*.test.ts and starts Logseq in a global setup. The unit test run skips them.
The same tests run for both graph types:
| Variant | Logseq | Graph |
|---|---|---|
file |
0.10.x | The fixture graph in tests/integration/fixtures/file-graph, opened from disk |
db |
2.x | The default “Demo” DB graph, seeded through the API |
The global setup starts Logseq through a launcher shared with the E2E tests (tests/support/logseq-app.ts). It drives the Electron app with Playwright, in a throwaway home and user data directory, with the HTTP API server switched on and a known token. Your own Logseq is untouched, and can run next to it as long as the API port differs.
The tests check the HTTP API itself (a wrong token is rejected, the graph type is reported correctly) and the extension services on top of it: the matching service is picked for the graph type, search and URL search find the seeded blocks, the clip path appends a block to a page, and the task status of a block can be changed. One Logseq is shared, so the test files run one after another.
Set these variables to run them:
BIN=$(scripts/download-logseq.sh db 2.0.2 .logseq-bin | tail -n 1)LOGSEQ_VARIANT=db LOGSEQ_BIN=$BIN pnpm test:integration
BIN=$(scripts/download-logseq.sh file 0.10.15 .logseq-bin | tail -n 1)LOGSEQ_VARIANT=file LOGSEQ_BIN=$BIN pnpm test:integrationOn Linux, run them under xvfb-run -a, because Logseq needs a display. On macOS, give Logseq a free LOGSEQ_API_PORT, because the launcher refuses a port that is taken. Set LOGSEQ_DEBUG=1 to see the logs of Logseq. See tests/integration/README.md for the details.
The E2E tests (tests/e2e) use Playwright to load the built extension in a real browser, with a real Logseq behind it. The config is tests/e2e/playwright.config.ts. It defines two projects, chromium and firefox, and both run the same specs. The tests run one at a time and share a single Logseq.
The specs cover the user flows: clipping, the floating button, highlights and the comment popup, the connection and recall rule pages in the options, the browser sidebar and which sidebar the toolbar button opens, the search engine panel, and changing a task status from the results.
pnpm test:e2e makes two builds for each browser:
wxt build), where the in-page UI sits in closed shadow roots, as shipped. Only closed-shadow.spec.ts uses it, to check that every in-page UI is mounted in a closed shadow root.WXT_E2E=true), which has open shadow roots so that Playwright locators can see inside the in-page UI. It goes to its own -e2e output folders so that it cannot be published by mistake.--load-extension. Playwright’s full Chromium runs extensions in headless mode.web-ext run does. The same specs then run in both browsers, with a small abstraction for the extension pages.Websites are fixtures: the tests route a minimal Google results page and a few articles, and abort every other request, so nothing reaches the internet.
A failing test keeps a screenshot of every open page and a Playwright trace in test-results/e2e.
See tests/e2e/README.md for the details of both browsers and how to run the tests locally.
pnpm typecheck, pnpm test:coverage and pnpm run build.logseq runs the integration tests for Logseq 0.10.15 (file graph) and 2.0.2 (DB graph). e2e runs the E2E tests for each browser and each graph type, so four combinations.docs/, and publishes it from main and for published releases.