Overview
The Test plugin enables literate testing directly within your org-mode documents using Vitest. Write tests alongside your code, run them from the CLI, and see results rendered inline in your documents.
Features
- ๐งช Literate Testing - Write tests in the same document as your code
- โก Vitest Powered - Fast, modern test runner with excellent DX
- ๐ Inline Results - Test results render directly in your documents
- ๐ Coverage Support - Optional code coverage reporting
- ๐ Watch Mode - Re-run tests automatically on changes
- ๐ฏ Filtering - Run tests by file or block name
Installation
npm install @org-press/block-test vitest
Register in .org-press/config.ts:
import { testPlugin } from '@org-press/block-test';
export default {
plugins: [testPlugin]
};
Basic Usage
Simple Example
Write tests directly in your org file:
describe('Math Operations', () => {
it('adds numbers correctly', () => {
expect(1 + 2).toBe(3);
});
it('multiplies numbers correctly', () => {
expect(3 * 4).toBe(12);
});
});
Syntax
#+NAME: my-tests
#+begin_src typescript :use test
import { describe, it, expect } from 'vitest';
describe('feature', () => {
it('works correctly', () => {
expect(true).toBe(true);
});
});
#+end_src
Parameters
| Parameter | Required | Description | Example |
|---|---|---|---|
:use test | Yes | Activates test block processing | :use test |
:name | No | Block name for filtering | :name my-tests |
:coverage | No | Enable coverage display | :coverage |
CLI Commands
Running All Tests
orgp test
Running Tests in a Specific File
orgp test content/math.org
Filtering by Block Name
orgp test --name math-tests
Watch Mode
orgp test --watch
With Coverage
orgp test --coverage
Hashbang Support
Make your org file executable:
#!/usr/bin/env orgp
#+TITLE: My Tests
#+NAME: tests
#+begin_src typescript :use test
describe('tests', () => {
it('works', () => expect(1).toBe(1));
});
#+end_src
Then run directly:
chmod +x my-tests.org
./my-tests.org test
Testing Patterns
Testing Functions from Other Blocks
Import functions from other code blocks in your org files:
// In math.org - define the function
#+NAME: add-function
#+begin_src typescript :use sourceOnly
export function add(a: number, b: number): number {
return a + b;
}
#+end_src
// In the same or another file - test it
#+NAME: add-tests
#+begin_src typescript :use test
import { add } from './math.org?name=add-function';
describe('add', () => {
it('adds positive numbers', () => {
expect(add(1, 2)).toBe(3);
});
it('adds negative numbers', () => {
expect(add(-1, -2)).toBe(-3);
});
it('handles zero', () => {
expect(add(0, 5)).toBe(5);
});
});
#+end_src
Async Tests
describe('Async Operations', () => {
it('resolves promises', async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});
it('handles async/await', async () => {
const fetchData = async () => ({ value: 'test' });
const data = await fetchData();
expect(data.value).toBe('test');
});
});
Mocking
import { vi } from 'vitest';
describe('Mocking', () => {
it('mocks functions', () => {
const mockFn = vi.fn().mockReturnValue(42);
expect(mockFn()).toBe(42);
expect(mockFn).toHaveBeenCalled();
});
it('spies on methods', () => {
const obj = {
method: () => 'original'
};
const spy = vi.spyOn(obj, 'method').mockReturnValue('mocked');
expect(obj.method()).toBe('mocked');
spy.mockRestore();
});
});
Setup and Teardown
describe('With Setup/Teardown', () => {
let testData: string[];
beforeEach(() => {
testData = ['a', 'b', 'c'];
});
afterEach(() => {
testData = [];
});
it('has initial data', () => {
expect(testData).toHaveLength(3);
});
it('can modify data', () => {
testData.push('d');
expect(testData).toHaveLength(4);
});
});
Inline Results Display
When viewing your org file in the browser during development, test blocks show results inline:
Result Components
- Summary Bar - Shows pass/fail/skip counts with color coding
- Test List - Individual test results with expand/collapse
- Error Details - Stack traces and diff output for failures
- Coverage Bars - Visual coverage metrics (when enabled)
Result States
| State | Color | Description |
|---|---|---|
| Pass | Green | All tests passed |
| Fail | Red | One or more tests failed |
| Pending | Yellow | Tests not yet run |
| Skip | Blue | Tests marked as skipped |
Project Setup Example
Directory Structure
my-project/
โโโ .org-press/
โ โโโ config.ts # Plugin registration
โโโ content/
โ โโโ index.org # Main content
โ โโโ math.org # Math functions + tests
โ โโโ utils.org # Utility functions + tests
โโโ package.json
Config File
// .org-press/config.ts
import { testPlugin } from '@org-press/block-test';
export default {
contentDir: 'content',
plugins: [testPlugin],
};
Package Scripts
{
"scripts": {
"dev": "orgp dev",
"build": "orgp build",
"test": "orgp test",
"test:watch": "orgp test --watch",
"test:coverage": "orgp test --coverage"
}
}
Tips and Best Practices
Organization
- Group related tests in the same org file as the code they test
- Use descriptive
#+NAME:blocks for easy filtering - Keep test blocks close to the code they test
Performance
- Use
--watchduring development for fast feedback - Filter by file or name when working on specific features
- Run full test suite before committing
Debugging
- Use
console.log()in tests - output appears in CLI - Check the generated test files in
node_modules/.cache/org-test/ - Run with
--reporter=verbosefor detailed output
Coverage
- Enable coverage to find untested code paths
- Aim for meaningful coverage, not 100%
- Focus coverage on critical business logic
API Reference
Plugin Configuration
interface TestPlugin extends BlockPlugin {
name: 'test';
defaultExtension: 'js';
// Match blocks with :use test
matches: (block: CodeBlock) => boolean;
// Transform block for rendering
transform: (block: CodeBlock, context: TransformContext) => Promise<TransformResult>;
// CLI command registration
cli: {
command: 'test';
description: 'Run test blocks in org files';
execute: (args: string[], context: CliContext) => Promise<number>;
};
}
Test Result Types
interface TestResult {
name: string;
status: 'pass' | 'fail' | 'skip';
duration: number;
error?: {
message: string;
expected?: unknown;
actual?: unknown;
stack?: string;
};
}
interface TestBlockResult {
blockId: string;
blockName?: string;
tests: TestResult[];
coverage?: CoverageData;
duration: number;
}
Troubleshooting
Tests Not Found
- Verify
:use testparameter is present on the block - Check that the block language is TypeScript or JavaScript
- Ensure the content directory is correctly configured
Import Errors
- Use relative paths:
./file.org?name=block-name - Verify the target block exists and exports correctly
- Check that Vitest can resolve the imports
CLI Not Working
- Ensure
@org-press/block-testis installed - Verify the plugin is registered in config
- Check that
orgpis available in PATH
Results Not Displaying
- Run
orgp testto generate results first - Check browser console for errors
- Verify the wrapper component is loading
See Also
- Block Plugin System - How plugins work
- Block Imports Guide - Importing code between blocks
- Vitest Documentation - Full Vitest API reference
- JSCad Plugin - 3D modeling plugin
- Excalidraw Plugin - Diagram plugin