- Added bindery-merge as a workspace in package.json. - Updated vscode-ext to include @bindery/merge as a dependency. - Refactored merge.ts to re-export merging functionalities from @bindery/merge, removing legacy code. - Simplified tool-locate.ts by re-exporting tool location logic from @bindery/merge, enhancing code reuse and maintainability. Co-authored-by: Copilot <copilot@github.com>
8 KiB
Contributing to Bindery
Thank you for contributing! This document explains how to run the tests and what CI requires before a pull request can be merged.
Running Tests Locally
Quick test (all packages)
npm test # runs all packages in parallel
Or test individual packages:
npm run test --workspace=bindery-core
npm run test --workspace=bindery-merge
npm run test --workspace=mcp-ts
npm run test --workspace=vscode-ext
npm run test --workspace=obsidian-plugin
Watch mode (auto-rerun on file change)
# Terminal 1
npm run test:watch --workspace=bindery-core
# Terminal 2
npm run test:watch --workspace=bindery-merge
# Terminal 3
npm run test:watch --workspace=mcp-ts
# Terminal 4
npm run test:watch --workspace=vscode-ext
# Terminal 5
npm run test:watch --workspace=obsidian-plugin
CI reporter (as GitHub Actions runs it)
cd mcp-ts && npm run test:ci
cd ../vscode-ext && npm run test:ci
The test:ci script writes a test-results.json file in each package directory that GitHub Actions uploads as a workflow artifact.
Test Coverage
| Layer | Location | What is tested |
|---|---|---|
| bindery-core unit tests | bindery-core/test/ |
Templates, settings, translations, formatting logic |
| bindery-merge unit tests | bindery-merge/test/merge.test.ts |
Pure merge functions, chapter discovery, dialect conversion |
| bindery-merge mocked tests | bindery-merge/test/merge-mocked.test.ts |
Pandoc/LibreOffice paths (with child_process mocked) |
| bindery-merge extended tests | bindery-merge/test/merge-extended.test.ts |
Internal functions, Pandoc helpers, typography integration |
| MCP unit tests | mcp-ts/test/tools.test.ts |
Tool logic, path safety, search indexing |
| MCP contract tests | mcp-ts/test/index-contract.test.ts |
Every registered tool has exactly one annotation hint |
| MCP stdio integration | mcp-ts/test/integration-stdio.test.ts |
Spawn real server, JSON-RPC handshake, tool calls, path-traversal defence, error handling |
| VS Code unit tests | vscode-ext/test/workspace.test.ts, vscode-ext/test/mcp.test.ts |
Workspace helpers, MCP JSON writer |
| VS Code integration | vscode-ext/test/integration-commands.test.ts |
Init workflow, registerMcp, formatDocument, settings precedence |
| Obsidian plugin tests | obsidian-plugin/test/ |
Plugin lifecycle, workspace management, AI setup, merge execution, formatter integration |
| Obsidian exporter | obsidian-plugin/test/exporter.test.ts |
Export orchestration, multi-format output |
| Obsidian merge | obsidian-plugin/test/merge.test.ts |
Chapter discovery, dialect handling, Obsidian Vault API integration |
Test Ownership Policy
To keep tests maintainable as packages grow, place tests where the production code lives:
- Unit tests live in the owning package.
- Example: template rendering logic in
bindery-core/src/templates/**should be tested primarily inbindery-core/test/.
- Example: template rendering logic in
- Cross-package tests validate wiring, contracts, and integration only.
- Example:
mcp-tsshould keep a thin shim/contract test for re-exports, not duplicate full behavior suites owned bybindery-core.
- Example:
- Coverage exclusions should reflect testability, not ownership drift.
- Do not exclude files from package coverage solely because tests were placed in a different package.
Host Feature Parity Policy
Feature parity is complete. Both vscode-ext/ and obsidian-plugin/ implement
identical Bindery authoring workflows:
- Shared logic: All merge, export, tool-location, and typography logic lives in
bindery-merge/and is consumed by both hosts. - Equivalent commands: Both hosts provide all 17+ authoring commands (format, merge, AI setup, workspace management, dialect/translation/language management).
- Equivalent tests: Each host has a full test suite covering its command wiring and host-specific integration (Obsidian Vault API, VS Code Workspace API).
Unless a feature is explicitly host-specific (e.g., VS Code's Language Model Tool API), any functional change or bug fix added to one host must be implemented in the other host in the same PR.
When adding or changing commands:
- Update logic: If logic lives in
bindery-merge/orbindery-core/, update there once - Update command wiring: Update in both
vscode-ext/src/extension.tsandobsidian-plugin/src/main.ts - Add tests: Add to both host test suites to validate host-specific integration
- Document exceptions: Clearly note any intentional host-specific behavior in the PR description
MCP stdio integration tests (mcp-ts/test/integration-stdio.test.ts)
These tests spawn node out/index.js as a real child process and drive it over
stdin/stdout using the MCP JSON-RPC protocol.
Prerequisites: run npm run build in mcp-ts/ before running these tests. The
CI workflow does this automatically (build step runs before test:ci), but when
running locally you must build first.
Security note: The tests use OS temporary directories created by mkdtempSync
and never accept user-controlled paths, eliminating path-injection risk.
VS Code extension integration tests (vscode-ext/test/integration-commands.test.ts)
These tests exercise the full command-level workflows of the extension by calling
the underlying helper functions directly (no extension-host required). The VS Code
APIs are mocked via vi.mock('vscode', ...).
Tests cover:
bindery.init— creates.bindery/settings.jsonandtranslations.jsonbindery.registerMcp— generates.vscode/mcp.jsonwith the correct server configbindery.formatDocument— applies typography transforms (curly quotes, em-dash, ellipsis)- Settings precedence (workspace file overrides defaults)
CI pipeline (.github/workflows/ci.yml)
The workflow runs on every push and pull request on all branches, on all three major platforms (Ubuntu, Windows, macOS).
A single test job runs the build and test steps sequentially:
| Step | What it does |
|---|---|
| Install all workspace deps | npm ci |
| Build + test bindery-core | npm run build → npm run test:ci → uploads test-results.json |
| Build + test bindery-merge | npm run build → npm run test:ci → uploads test-results.json |
| Compile + test mcp-ts | npm run compile → npm run test:ci → uploads test-results.json |
| Compile + test vscode-ext | npm run compile → npm run test:ci (+ VSIX smoke check on ubuntu-latest) |
| Compile + test obsidian-plugin | npm run compile → npm run bundle → npm run test:ci → uploads test-results.json |
A separate coverage job runs on ubuntu-latest only (coverage metrics don't vary by OS):
| Step | What it does |
|---|---|
| Build + coverage for each package | npm run compile → npm run test:coverage |
| Upload coverage reports | Artifacts collected for each package |
PRs cannot be merged unless the test job passes on all platforms.
Adding New Tests
- Add your test file alongside the existing ones (
mcp-ts/test/orvscode-ext/test/). - Follow the naming convention:
<feature>.test.tsfor unit tests,integration-<area>.test.tsfor integration tests. - Run
npm testlocally before pushing. - For MCP stdio integration tests that spawn a child process, always register a
kill()call inafterEachto prevent orphaned processes.
Troubleshooting
Merge tests won't compile
The merge tests have moved from vscode-ext/test/ to bindery-merge/test/. If you have
local references to the old paths, update imports to point to bindery-merge instead.
CI passes locally but fails in GitHub Actions
Ensure your package-lock.json was generated with the same npm major version as the one
used in .github/workflows/ci.yml. This prevents lockfile skew across platforms.
Future Enhancements
- E2E tests with real pandoc/LibreOffice invocation
- Code coverage reporting dashboards
- Per-commit performance benchmarks (merge speed, search latency)