Testing workflow
Daily Notes NG uses a multi-layered testing strategy combining unit tests, Obsidian CLI automation, and E2E tests via wdio-obsidian-service.
Testing layers
graph TD E2E["Layer 3: E2E — wdio-obsidian-service<br/>Full UI automation with sandboxed Obsidian<br/><i>test/e2e/**/*.spec.ts</i>"] CLI["Layer 2: CLI Integration<br/>Obsidian CLI commands against live vault<br/><i>Build → reload → execute → inspect → screenshot</i>"] Unit["Layer 1: Unit Tests — Bun<br/>Pure logic tests with mocked Obsidian API<br/><i>tests/**/*.test.ts</i>"]
E2E --> CLI --> Unit
style E2E fill:#991b1b,color:#fff style CLI fill:#d97706,color:#fff style Unit fill:#059669,color:#fffLayer 1: Unit tests
Run with Bun’s built-in test runner:
bun test # Run all testsbun test --watch # Watch modeTests live in tests/ mirroring the src/ structure. The Obsidian API is mocked via tests/preload.ts (Bun plugin that provides a mock obsidian module).
What to unit test
- JournalResolver scope filtering and journal availability
- DevicePreferences localStorage read/write
- Frontmatter parsing utilities
- Todo parser (checkbox extraction)
- Date utilities and template variable resolution
What NOT to unit test
- Obsidian UI components (no DOM in unit tests)
- Plugin lifecycle (onload/onunload)
- File creation (requires real vault)
Layer 2: Obsidian CLI integration
The Obsidian CLI (v1.12+) enables testing against a running Obsidian instance from the terminal. This is the primary testing workflow for development.
Setup
# Set the CLI alias (WSL2)OBS="/mnt/c/Users/Cybersader/AppData/Local/Obsidian/Obsidian.com"
# Verify connection$OBS vault=dnng-test-vault eval "code=1+1"# => 2WSL2 note: Use
Obsidian.com(not.exe). The.comfile is a terminal redirector that provides proper stdin/stdout. Requires Catalyst license.
Build-reload-test cycle
The core development loop:
# 1. Build and deploybun run build && node copy-files.mjs
# 2. Reload plugin in Obsidian$OBS vault=dnng-test-vault plugin:reload id=daily-notes-ng
# 3. Run a command$OBS vault=dnng-test-vault eval \ "code=app.commands.executeCommandById('daily-notes-ng:open-today')"
# 4. Inspect result$OBS vault=dnng-test-vault eval \ "code=app.workspace.getActiveFile()?.path"
# 5. Check for errors$OBS vault=dnng-test-vault dev:errorsUseful CLI commands for testing
# Inspect plugin settings at runtime$OBS vault=dnng-test-vault eval \ "code=JSON.stringify(app.plugins.plugins['daily-notes-ng'].settings.identity, null, 2)"
# List all files in vault$OBS vault=dnng-test-vault files
# Read a specific note$OBS vault=dnng-test-vault read path="Test-Journal/Daily/2026-03-24.md"
# Take a screenshot$OBS vault=dnng-test-vault dev:screenshot
# Check console errors$OBS vault=dnng-test-vault dev:errors
# List registered commands$OBS vault=dnng-test-vault commands | grep daily-notes-ngTesting the identity system via CLI
# Check current device registration$OBS vault=dnng-test-vault eval \ "code=app.plugins.plugins['daily-notes-ng'].userRegistry.getCurrentUser() ?? 'not registered'"
# Inspect all device mappings$OBS vault=dnng-test-vault eval \ "code=JSON.stringify(app.plugins.plugins['daily-notes-ng'].settings.identity.deviceUserMappings, null, 2)"Layer 3: E2E tests (wdio-obsidian-service)
wdio-obsidian-service is a WebDriverIO service purpose-built for Obsidian plugin testing. It automatically downloads Obsidian, runs tests in a sandboxed environment, and supports multi-version testing.
Why wdio over Playwright?
| Feature | wdio-obsidian-service | Playwright CDP |
|---|---|---|
| Setup | bun add + config file | Custom 229-line launcher |
| Obsidian download | Automatic | Manual binary detection |
| Multi-version testing | Built-in (OBSIDIAN_VERSIONS env) | Not supported |
| Vault isolation | Sandboxed per run | Manual |
| Trust dialog | Automatic | Custom click logic |
| Command execution | browser.executeObsidianCommand() | eval hacks |
| CI/CD | Templates included | DIY |
Running E2E tests
# Run all E2E testsbun run e2e
# Run against specific Obsidian versionsOBSIDIAN_VERSIONS="1.5.0,1.6.0,latest" bun run e2eWriting E2E tests
Tests live in test/e2e/ and use Mocha + WebDriverIO syntax:
describe('Daily Notes NG', () => { it('can open today\'s daily note', async () => { await browser.executeObsidianCommand('daily-notes-ng:open-today'); await browser.waitUntil(async () => { const title = await $('.view-header-title'); return await title.isDisplayed(); }, { timeout: 10000 }); const title = await $('.view-header-title'); await expect(title).toBeDisplayed(); });
it('settings tab renders', async () => { await browser.executeObsidianCommand('app:open-settings'); const modal = await $('.modal'); await expect(modal).toBeDisplayed(); await browser.keys(['Escape']); });});When to use E2E vs CLI
| Scenario | Use |
|---|---|
| Verify a command works | CLI (eval + executeCommandById) |
| Check file was created correctly | CLI (read) |
| Inspect settings at runtime | CLI (eval) |
| Test modal interactions | E2E (wdio) |
| Test drag-and-drop in calendar | E2E (wdio) |
| Screenshot for documentation | CLI (dev:screenshot) |
| Automated regression suite in CI | E2E (wdio) |
| Multi-version compatibility | E2E (wdio) |
Test fixtures plugin
The test fixtures plugin generates reproducible sample data for testing. Use it to quickly set up and tear down test scenarios:
# Generate all test data$OBS vault=dnng-test-vault eval \ "code=app.commands.executeCommandById('daily-notes-ng-test-fixtures:full-setup')"
# Clean up all test data$OBS vault=dnng-test-vault eval \ "code=app.commands.executeCommandById('daily-notes-ng-test-fixtures:remove-all')"Agentic development workflow
When working with AI coding assistants (Claude Code, etc.), the recommended workflow is:
1. Agent writes/modifies source code2. Agent runs: bun run build && node copy-files.mjs3. Agent runs: $OBS plugin:reload id=daily-notes-ng4. Agent runs: $OBS eval "code=..." to test the change5. Agent runs: $OBS dev:errors to check for issues6. Agent reads result and iteratesThis enables a fully autonomous build-reload-test loop without human interaction for most changes.
Agent-specific tips
- Use
evalfor most testing — it’s the most reliable CLI command - Use
dev:errorsafter every reload to catch silent failures - Use
files folder=<path>to verify directory structures - Avoid
plugin:reloadin rapid succession — addsleep 1between reload and test - If CLI hangs, Obsidian may be showing a modal — use
dev:screenshotto diagnose
Continuous integration
GitHub Actions runs on every push to main:
- Plugin release (
.github/workflows/release.yml): Auto-detects version change, builds, tags, releases - Docs deploy (
.github/workflows/deploy-docs.yml): Builds Starlight, deploys to GitHub Pages - E2E tests (
.github/workflows/e2e.yml): Runs wdio-obsidian-service against latest Obsidian