Test Reference
✅ Equality test with enforced readability, based on the concept of RITEway and inspired by uvu.
Usage
📦 Node
Install @lou.codes/tests
as a dev dependency:
1pnpm add -D @lou.codes/test2# or3npm install -D @lou.codes/test4# or5yarn add --dev @lou.codes/test
Add a test
script to package.json
:
1{2 "scripts": {3 "test": "test"4 }5}
Add TypeScript support
To support TypeScript, install tsx as a dev dependency:
1pnpm add -D tsx2# or3npm install -D tsx4# or5yarn add --dev tsx
And update package.json
:
1{2 "scripts": {3 "test": "NODE_OPTIONS='--import tsx' test"4 }5}
Add coverage
To add coverage, install c8
as a dev dependency:
1pnpm add -D c82# or3npm install -D c84# or5yarn add --dev c8
And update package.json
:
1{2 "scripts": {3 "test": "c8 test"4 }5}
If you added TypeScript support, then update package.json
like this instead:
And update package.json
:
1{2 "scripts": {3 "test": "NODE_OPTIONS='--import tsx' c8 test"4 }5}
Run tests:
1pnpm test2# or3npm test4# or5yarn test
🦕 Deno
Import @lou.codes/test
using the npm:
prefix, and use it directly:
1import { evaluate } from "npm:@lou.codes/test";2import { add } from "../src/add.js";3
4evaluate({5 given: "a 1 and a 2",6 must: "return 3",7 received: () => add(2)(1),8 wanted: () => 3,9}).then(console.log);
🌎 Browser
Import @lou.codes/test
using esm.sh, and use it directly:
1<script type="module">2 import { evaluate } from "https://esm.sh/@lou.codes/test";3 import { add } from "../src/add.js";4
5 evaluate({6 given: "a 1 and a 2",7 must: "return 3",8 received: () => add(2)(1),9 wanted: () => 3,10 }).then(console.log);11</script>
Writing tests
TypeScript
1import type { Tests } from "@lou.codes/test";2import { add } from "../src/add.js";3
4export default [5 {6 given: "a 1 and a 2",7 must: "return 3",8 received: () => add(2)(1),9 wanted: () => 3,10 },11 {12 given: "a 1 and a -2",13 must: "return -1",14 received: () => add(-2)(1),15 wanted: () => -1,16 },17] satisfies Tests<number>;
JavaScript
1import { add } from "../src/add.js";2
3/** @satisfies {import("@lou.codes/test").Tests<number>} */4export default [5 {6 given: "a 1 and a 2",7 must: "return 3",8 received: () => add(2)(1),9 wanted: () => 3,10 },11 {12 given: "a 1 and a -2",13 must: "return -1",14 received: () => add(-2)(1),15 wanted: () => -1,16 },17];
Other alternatives
Instead of exporting an Array
of Test
as default
, the export can also be a
single Test
:
1import type { Test } from "@lou.codes/test";2import { add } from "../src/add.js";3
4export default {5 given: "a 1 and a 2",6 must: "return 3",7 received: () => add(2)(1),8 wanted: () => 3,9} satisfies Test<number>;
Or multiple exports with different tests:
1import type { Test } from "@lou.codes/test";2import { add } from "../src/add.js";3
4export const test1: Test<number> = {5 given: "a 1 and a 2",6 must: "return 3",7 received: () => add(2)(1),8 wanted: () => 3,9};10
11export const test2: Test<number> = {12 given: "a 1 and a -2",13 must: "return -1",14 received: () => add(-2)(1),15 wanted: () => -1,16};
It can also be used directly without the test
bin by importing the different
utils directly (like with the Deno and Browser examples above):
1import { evaluate } from "@lou.codes/test";2import { customFormatter } from "./customFormatter.js";3
4evaluate({5 given: "a 1 and a 2",6 must: "return 3",7 received: () => add(2)(1),8 wanted: () => 3,9}).then(customFormatter);
Default output
@lou.codes/tests
provides a default output for the tests. It looks like this:
1❯ ./tests/example.test.ts2× Given a 1 and a 2, must return 3, but…3└ it has the wrong value. Wanted 3 but received 4.
And if the wanted/received type is more complex, like an object, then the output goes into details about the error:
1❯ ./tests/example.test.ts2× Given an object, must add a single property, but…3├ foo.bar has the wrong value. Wanted 1 but received 2.4├ foo.baz.1 is missing.5└ bar was set with the value "bar".
But developers can choose to run test
directly and use their own formatter, as
it was pointed out in the previous section.
Useful links
- 📝 Documentation: TypeDoc generated documentation.
- ⏳ Changelog: List of changes between versions.
- ✅ Tests Coverage: Coveralls page with tests coverage.
File System
ReadOnlyURL
1type ReadOnlyURL: Readonly<URL>;
Read-only URL
.
ReadOnlyURLs
1type ReadOnlyURLs: IsomorphicIterable<ReadOnlyURL>;
Iterable of ReadOnlyURL
s.
See
TestTuple<Value>
1type TestTuple<Value>: readonly [ReadOnlyURL, Test<Value>];
Tuple used to describe a test result and its path.
See
Type parameters
Type parameter | Value |
---|---|
Value | unknown |
TestsImport<Value>
1type TestsImport<Value>: Promise<ReadOnlyRecord<PropertyKey, Test<Value> | Tests<Value>>>;
Promise import of a file containing Test
or Tests
.
See
Type parameters
Type parameter | Value |
---|---|
Value | unknown |
importTest()
1function importTest(2 url: Readonly<URL>,3): AsyncGenerator<Test<unknown>, void, unknown>;
Import a file that exports a Test
or an Iterable of Test
.
Parameters
Parameter | Type |
---|---|
url | Readonly <URL > |
Returns
AsyncGenerator
<Test
<unknown
>,
void
, unknown
>
Example
1testImport(new URL("file:///example/test.test.js"));2// AsyncIterable<[3// {4// given: "example 1",5// must: "example 1",6// received: () => "value 1",7// wanted: () => "value 1"8// },9// {10// given: "example 2",11// must: "example 2",12// received: () => "value 2",13// wanted: () => "value 2"14// },15// ]>
Yields
Imported tests.
importTests()
1function importTests(2 urls: ReadOnlyURLs,3): AsyncGenerator<TestTuple, void, unknown>;
Imports all the tests of the given Iterable of urls and yields TestTuple
.
Parameters
Parameter | Type | Description |
---|---|---|
urls | ReadOnlyURLs | Array of urls of tests. |
Returns
AsyncGenerator
<TestTuple
,
void
, unknown
>
Example
1testsImport([2 "file:///example/test-1.test.js",3 "file:///example/test-2.test.js",4]);5// AsyncIterable<6// [7// [8// "file:///example/test-1.test.js",9// {10// given: "example",11// must: "example",12// received: () => "value",13// wanted: () => "value",14// },15// ],16// [17// "file:///example/test-2.test.js",18// {19// given: "example",20// must: "example",21// received: () => "value",22// wanted: () => "value",23// },24// ],25// ]26// >;
Yields
TestTuple
containing url and test for it.
Internal
EXCEPTION
1const EXCEPTION: 8 = 8;
Exception difference kind.
UNKNOWN_ERROR
1const UNKNOWN_ERROR: "Unknown Error" = "Unknown Error";
Unknown error.
Output
FAIL
1const FAIL: `[31m${string}[39m`;
Fail message with colors.
FAILED_TESTS
1const FAILED_TESTS: string;
Failed test title with colors.
PASS
1const PASS: `[32m${string}[39m`;
Pass message with colors.
TEST
1const TEST: `[33m${string}[39m`;
Test message to be shown next to the test path.
formatValueDictionary
1const formatValueDictionary: ReadOnlyRecord<2 TypeOfValue,3 (value: never) => string4>;
Dictionary type->formatter to be used by formatValue
.
stringifyDifferenceDictionary
1const stringifyDifferenceDictionary: object;
Dictionary Difference
kind->formatter.
Type declaration
Member | Type | Value |
---|---|---|
1 | (difference : Difference & object ) => string | … |
2 | (difference : Difference & object ) => string | … |
4 | (difference : Difference & object ) => string | … |
8 | (difference : Difference & object ) => `[31m${string}[39m` | … |
formatPropertyPath()
1function formatPropertyPath(propertyPath: ReadOnlyArray<PropertyKey>): string;
Stringifies and colorizes an array representing a property path.
Parameters
Parameter | Type | Description |
---|---|---|
propertyPath | ReadOnlyArray <PropertyKey > | Path to format. |
Returns
string
String with formatted path.
Example
1formatPropertyPath(["foo", "bar"]); // "foo.bar" (with colors)2formatPropertyPath([]); // "it"
formatValue()
1function formatValue(value: unknown): string;
Colorizes and formats a value based on its type.
Parameters
Parameter | Type | Description |
---|---|---|
value | unknown | Value to colorize. |
Returns
string
Colorized value as a string.
Example
1formatValue(1); // "1" (with colors)2formatValue(BigInt(1)); // "1n" (with colors)3formatValue([]); // "Array([])" (with colors)4formatValue({}); // "Object({})" (with colors)
stringifyDifference()
1function stringifyDifference(difference: Difference): string;
Takes a Difference
object and returns a string using
stringifyDifferenceDictionary
.
Parameters
Parameter | Type | Description |
---|---|---|
difference | Difference | Difference object. |
Returns
string
Formatted string.
Example
1stringifyDifference({2 kind: "DELETE",3 left: "🟢",4 path: ["🟢", "🟩"],5}); // "🟢.🟩 is missing."6
7stringifyDifference({8 kind: 8,9 error: "❌",10}); // "there was an uncaught error: ❌."
stringifyTest()
1function stringifyTest(testResult: TestResult): string;
Takes a TestResult
and returns a readable string..
Parameters
Parameter | Type | Description |
---|---|---|
testResult | TestResult | Test result object. |
Returns
string
Readable string.
Example
1stringifyTest({2 given: "🟢",3 must: "🟩",4}); // "✓ Given 🟢, does 🟩."5stringifyTest({6 differences: […],7 given: "🟢",8 must: "🟩",9}); // "× Given 🟢, must 🟩, but…"
Test
Difference
1type Difference: CreateDifference | DeleteDifference | UpdateDifference | object;
Difference object from @lou.codes/diff
, with an added “EXCEPTION” kind.
Example
1const difference: Difference = {2 kind: "UPDATE",3 left: "🟢",4 path: ["🟢", "🟩"],5 right: "🟩",6};
See
Template
Type of value being compared.
Differences
1type Differences: ReadOnlyArray<Difference>;
Array of Difference
.
Example
1const differences: Differences<string> = [2 {3 kind: "UPDATE",4 left: "🟢",5 path: ["🟢", "🟩"],6 right: "🟩",7 },8];
See
Template
Type of values being compared.
Test<Value>
1type Test<Value>: object;
Object that describes a test.
Example
1const test: Test<number> = {2 given: "a number",3 must: "make it double",4 received: () => double(2),5 wanted: () => 4,6};
Type parameters
Type parameter | Value | Description |
---|---|---|
Value | unknown | Type of value being tested. |
Type declaration
Member | Type | Description |
---|---|---|
given | string | Description of the given value. |
must | string | Description of the wanted value. |
received | () => Awaitable <Value > | Function that returns a value being tested. |
wanted | () => Awaitable <Value > | Functions that returns the expected value. |
TestResult
1type TestResult: Pick<Test, "given" | "must"> & object;
Object that describes a test result (given, must and differences).
Example
1const testResult: TestResult<string> = {2 given: "🟢",3 must: "🟩",4 differences: [5 {6 kind: "UPDATE",7 path: ["🟢", "🟩"],8 left: "🟢",9 right: "🟩",10 },11 ],12};
See
Template
Type of value being tested.
Type declaration
Member | Type | Description |
---|---|---|
differences | Differences | Differences between given and must (undefined when equal). |
TestTuple<Value>
1type TestTuple<Value>: readonly [ReadOnlyURL, Test<Value>];
Tuple used to describe a test result and its path.
See
Type parameters
Type parameter | Value |
---|---|
Value | unknown |
Tests<Value>
1type Tests<Value>: IsomorphicIterable<Test<Value>>;
Iterable of Test
.
Example
1const tests: Tests<number> = [2 {3 given: "a number",4 must: "make it double",5 received: () => double(2),6 wanted: () => 4,7 },8];
See
Type parameters
Type parameter | Value | Description |
---|---|---|
Value | unknown | Type of value being tested. |
TestsImport<Value>
1type TestsImport<Value>: Promise<ReadOnlyRecord<PropertyKey, Test<Value> | Tests<Value>>>;
Promise import of a file containing Test
or Tests
.
See
Type parameters
Type parameter | Value |
---|---|
Value | unknown |
evaluate()
1function evaluate<Value>(testDescription: Test<Value>): Promise<object>;
Takes a Test
object and returns a promise with a TestResult
.
Type parameters
Type parameter |
---|
Value |
Parameters
Parameter | Type | Description |
---|---|---|
testDescription | Test <Value > | A Test object. |
Returns
Promise
<object
>
A promise with a TestResult
object.
Member | Type | Value |
---|---|---|
differences | undefined | Differences | - |
given | string | testDescription.given |
must | string | testDescription.must |
Example
1evaluate({2 given: "🟢",3 must: "🟩",4 received: () => "🟩",5 wanted: () => "🟩",6}); // Promise<{ given: "🟢", , must: "🟩" }>7evaluate({8 given: "🟢",9 must: "🟩",10 received: () => "❌",11 wanted: () => "🟩",12}); // Promise<{ differences: […], given: "🟢", , must: "🟩" }>
isTest()
1function isTest<Actual>(value: Test | Actual): value is Test;
Check if given value is a Test
.
Type parameters
Type parameter | Value |
---|---|
Actual | unknown |
Parameters
Parameter | Type | Description |
---|---|---|
value | Test | Actual | Value to check. |
Returns
value is Test
true
if is a Test
, false
otherwise.
Example
1isTest({ given: "🟢", must: "🟩", received: () => "🟩", wanted: () => "🟩" }); // true2isTest({ given: "🟢", must: "🟩", received: "🟩", wanted: "🟩" }); // false3isTest({ given: 1, must: 2, received: 3, wanted: 4 }); // false4isTest(); // false