@dral/toml-tests
v5.2.0
Published
Testing utility that loads TOML test suites and runs them against a function
Readme
toml-test
A framework-agnostic testing utility that parses TOML test suites and runs them against a function. Define test cases and expected outputs in TOML, pass in your test framework's describe/it/equal, and toml_test registers everything for you.
Install
npm install toml-testUsage
import { describe, it } from "node:test";
import { strict as assert } from "node:assert";
import { readFile } from "node:fs/promises";
import { toml_test } from "toml-test";
import type { TestHarness, TomlValue } from "toml-test";
const harness: TestHarness = {
describe,
it,
equal: assert.deepStrictEqual,
};
const toml = await readFile("./tests/my-parser.toml", "utf-8");
toml_test(
toml,
(input, options) => {
return myParser(input, options);
},
harness,
"my-parser.toml",
);Run with Node's test runner:
node --test src/**/*.test.tsWorks with any framework that provides describe, it, and a deep-equal assertion — node:test, vitest, jest, mocha, etc.
TOML test file format
Test files use TOML 1.1, which allows multiline inline tables — handy for writing expected output that is a nested object. Parsed by smol-toml which supports TOML 1.1.
[[tests]]
name = "string input"
input = "hello world"
output = "HELLO WORLD"
[[tests]]
name = "numeric input"
input = 42
output = 84
[[tests]]
name = "boolean input"
input = true
output = false
[[tests]]
name = "object input"
input = { x = 1, y = 2 }
output = 3
[[tests]]
name = "array input"
input = [10, 20, 30]
output = 60
[[suites]]
name = "JSON parsing"
[[suites.tests]]
name = "One-key object"
input = '{ "key": "value" }'
output = { key = "value" }
[[suites.tests]]
name = "Complex"
input = """
{
"number": 10,
"object": {
"nested": {
"key": "}"
}
}
}"""
output = {
number = 10,
object = {
nested = {
key = "}"
}
}
}Schema
interface TestCase {
name?: string;
input: TomlValue;
options?: Record<string, TomlValue>;
// Exactly one of the following three:
output?: TomlValue;
throws?: true | string;
doesntThrow?: true;
}
interface TestSuite {
name?: string;
options?: Record<string, TomlValue>;
tests?: TestCase[];
suites?: TestSuite[];
}Assertion types
Each test case must have exactly one of output, throws, or doesntThrow.
output — compare return value
The function's return value is deep-compared against the expected value. Non-TOML-expressible values in the result cause an explicit failure (see Non-TOML result detection).
[[tests]]
name = "uppercases input"
input = "hello"
output = "HELLO"throws — expect an error
Set throws = true to assert that the function throws any error. Set throws to a string to assert that the error message matches a regular expression pattern.
[[tests]]
name = "rejects bad input"
input = "bad"
throws = true
[[tests]]
name = "error message matches pattern"
input = "bad"
throws = "invalid.*format"doesntThrow — expect no error
Set doesntThrow = true to assert that the function completes without throwing. The return value is ignored — only the absence of an error is checked.
[[tests]]
name = "handles edge case gracefully"
input = ""
doesntThrow = trueOptions merging
Options are shallowly merged from outer to inner scope:
{ ...fileSuiteOptions, ...nestedSuiteOptions, ..., ...testOptions }This lets you set defaults at the file level and override per-suite or per-test.
Named vs nameless suites
- A suite with a
namecreates adescribe()block in the test output. - A suite without a
namedoes not create any grouping — its tests and sub-suites appear at the parent's nesting level. Use nameless suites to combine options or split tests across files without adding noise to the output.
Non-TOML result detection
If the function under test returns a value containing types that cannot be expressed in TOML (undefined, null, Symbol, Function, Set, Map, RegExp, class instances, etc.), the test fails with a message listing the offending paths:
my-parser.toml:12: Result contains non-TOML-expressible values at: result.foo, result.items[2].barThis catches accidental leakage of internal types into what should be a plain-data result.
Source line numbers
When a test fails, the error message includes the line number in the TOML file where the test case was defined. If you pass a source label (typically the filename), errors use source:line format:
my-parser.toml:12: Expected values to be strictly deep-equalWithout a source label, errors use line N format:
line 12: Expected values to be strictly deep-equalAPI
toml_test(toml, fn, harness, source?)
function toml_test(
toml: string,
fn: TomlTestFn,
harness: TestHarness,
source?: string,
): void;Parses a TOML test document and registers test suites/cases using the provided harness.
- toml — TOML source string describing the test suite
- fn — the function under test, called as
fn(input, mergedOptions) - harness — test framework bindings (
describe,it,equal) - source — optional label (e.g. filename) included in error messages
TestHarness
interface TestHarness {
describe: (name: string, fn: () => void) => void;
it: (name: string, fn: () => void) => void;
equal: (actual: unknown, expected: unknown) => void;
}TomlTestFn
type TomlTestFn = (input: TomlValue, options: any) => any;The input can be any TOML-expressible value — string, number, boolean, Date, array, or object. This allows test cases to pass structured data directly:
[[tests]]
name = "string input"
input = "hello"
output = "HELLO"
[[tests]]
name = "numeric input"
input = 42
output = 84
[[tests]]
name = "object input"
input = { x = 1, y = 2 }
output = 3
[[tests]]
name = "array input"
input = [1, 2, 3]
output = 6TomlValue
type TomlValue =
| string
| number
| bigint
| boolean
| Date
| TomlValue[]
| { [key: string]: TomlValue };Represents any value expressible in TOML. Used for both input and output fields in test cases.
Development
npm install
npm test # run tests
npx tsc --noEmit -p tsconfig.src.json # type-check source (no node types)
npx tsc --noEmit -p tsconfig.test.json # type-check testsLicense
ISC
