Skip to content

Test Reference

Coverage License NPM Version Open Issues Size

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:

Terminal window
1
pnpm add -D @lou.codes/test
2
# or
3
npm install -D @lou.codes/test
4
# or
5
yarn 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:

Terminal window
1
pnpm add -D tsx
2
# or
3
npm install -D tsx
4
# or
5
yarn 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:

Terminal window
1
pnpm add -D c8
2
# or
3
npm install -D c8
4
# or
5
yarn 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:

Terminal window
1
pnpm test
2
# or
3
npm test
4
# or
5
yarn test

🦕 Deno

Import @lou.codes/test using the npm: prefix, and use it directly:

1
import { evaluate } from "npm:@lou.codes/test";
2
import { add } from "../src/add.js";
3
4
evaluate({
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

1
import type { Tests } from "@lou.codes/test";
2
import { add } from "../src/add.js";
3
4
export 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

1
import { add } from "../src/add.js";
2
3
/** @satisfies {import("@lou.codes/test").Tests<number>} */
4
export 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:

1
import type { Test } from "@lou.codes/test";
2
import { add } from "../src/add.js";
3
4
export 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:

1
import type { Test } from "@lou.codes/test";
2
import { add } from "../src/add.js";
3
4
export 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
11
export 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):

1
import { evaluate } from "@lou.codes/test";
2
import { customFormatter } from "./customFormatter.js";
3
4
evaluate({
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.ts
2
× 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.ts
2
× 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.

File System

ReadOnlyURL

1
type ReadOnlyURL: Readonly<URL>;

Read-only URL.

View source


ReadOnlyURLs

1
type ReadOnlyURLs: IsomorphicIterable<ReadOnlyURL>;

Iterable of ReadOnlyURLs.

See

ReadOnlyURL

View source


TestTuple<Value>

1
type TestTuple<Value>: readonly [ReadOnlyURL, Test<Value>];

Tuple used to describe a test result and its path.

See

Type parameters

Type parameterValue
Valueunknown

View source


TestsImport<Value>

1
type TestsImport<Value>: Promise<ReadOnlyRecord<PropertyKey, Test<Value> | Tests<Value>>>;

Promise import of a file containing Test or Tests.

See

Type parameters

Type parameterValue
Valueunknown

View source


importTest()

1
function importTest(
2
url: Readonly<URL>,
3
): AsyncGenerator<Test<unknown>, void, unknown>;

Import a file that exports a Test or an Iterable of Test.

Parameters

ParameterType
urlReadonly<URL>

Returns

AsyncGenerator<Test<unknown>, void, unknown>

Example

1
testImport(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.

View source


importTests()

1
function importTests(
2
urls: ReadOnlyURLs,
3
): AsyncGenerator<TestTuple, void, unknown>;

Imports all the tests of the given Iterable of urls and yields TestTuple.

Parameters

ParameterTypeDescription
urlsReadOnlyURLsArray of urls of tests.

Returns

AsyncGenerator<TestTuple, void, unknown>

Example

1
testsImport([
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.

View source

Internal

EXCEPTION

1
const EXCEPTION: 8 = 8;

Exception difference kind.

View source


UNKNOWN_ERROR

1
const UNKNOWN_ERROR: "Unknown Error" = "Unknown Error";

Unknown error.

View source

Output

FAIL

1
const FAIL: `${string}`;

Fail message with colors.

View source


FAILED_TESTS

1
const FAILED_TESTS: string;

Failed test title with colors.

View source


PASS

1
const PASS: `${string}`;

Pass message with colors.

View source


TEST

1
const TEST: `${string}`;

Test message to be shown next to the test path.

View source


formatValueDictionary

1
const formatValueDictionary: ReadOnlyRecord<
2
TypeOfValue,
3
(value: never) => string
4
>;

Dictionary type->formatter to be used by formatValue.

View source


stringifyDifferenceDictionary

1
const stringifyDifferenceDictionary: object;

Dictionary Difference kind->formatter.

Type declaration

MemberTypeValue
1(difference: Difference & object) => string
2(difference: Difference & object) => string
4(difference: Difference & object) => string
8(difference: Difference & object) => `${string}`

View source


formatPropertyPath()

1
function formatPropertyPath(propertyPath: ReadOnlyArray<PropertyKey>): string;

Stringifies and colorizes an array representing a property path.

Parameters

ParameterTypeDescription
propertyPathReadOnlyArray<PropertyKey>Path to format.

Returns

string

String with formatted path.

Example

1
formatPropertyPath(["foo", "bar"]); // "foo.bar" (with colors)
2
formatPropertyPath([]); // "it"

View source


formatValue()

1
function formatValue(value: unknown): string;

Colorizes and formats a value based on its type.

Parameters

ParameterTypeDescription
valueunknownValue to colorize.

Returns

string

Colorized value as a string.

Example

1
formatValue(1); // "1" (with colors)
2
formatValue(BigInt(1)); // "1n" (with colors)
3
formatValue([]); // "Array([])" (with colors)
4
formatValue({}); // "Object({})" (with colors)

View source


stringifyDifference()

1
function stringifyDifference(difference: Difference): string;

Takes a Difference object and returns a string using stringifyDifferenceDictionary.

Parameters

ParameterTypeDescription
differenceDifferenceDifference object.

Returns

string

Formatted string.

Example

1
stringifyDifference({
2
kind: "DELETE",
3
left: "🟢",
4
path: ["🟢", "🟩"],
5
}); // "🟢.🟩 is missing."
6
7
stringifyDifference({
8
kind: 8,
9
error: "",
10
}); // "there was an uncaught error: ❌."

View source


stringifyTest()

1
function stringifyTest(testResult: TestResult): string;

Takes a TestResult and returns a readable string..

Parameters

ParameterTypeDescription
testResultTestResultTest result object.

Returns

string

Readable string.

Example

1
stringifyTest({
2
given: "🟢",
3
must: "🟩",
4
}); // "✓ Given 🟢, does 🟩."
5
stringifyTest({
6
differences: […],
7
given: "🟢",
8
must: "🟩",
9
}); // "× Given 🟢, must 🟩, but…"

View source

Test

Difference

1
type Difference: CreateDifference | DeleteDifference | UpdateDifference | object;

Difference object from @lou.codes/diff, with an added “EXCEPTION” kind.

Example

1
const difference: Difference = {
2
kind: "UPDATE",
3
left: "🟢",
4
path: ["🟢", "🟩"],
5
right: "🟩",
6
};

See

Template

Type of value being compared.

View source


Differences

1
type Differences: ReadOnlyArray<Difference>;

Array of Difference.

Example

1
const differences: Differences<string> = [
2
{
3
kind: "UPDATE",
4
left: "🟢",
5
path: ["🟢", "🟩"],
6
right: "🟩",
7
},
8
];

See

Template

Type of values being compared.

View source


Test<Value>

1
type Test<Value>: object;

Object that describes a test.

Example

1
const test: Test<number> = {
2
given: "a number",
3
must: "make it double",
4
received: () => double(2),
5
wanted: () => 4,
6
};

Type parameters

Type parameterValueDescription
ValueunknownType of value being tested.

Type declaration

MemberTypeDescription
givenstringDescription of the given value.
muststringDescription of the wanted value.
received() => Awaitable<Value>Function that returns a value being tested.
wanted() => Awaitable<Value>Functions that returns the expected value.

View source


TestResult

1
type TestResult: Pick<Test, "given" | "must"> & object;

Object that describes a test result (given, must and differences).

Example

1
const 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

MemberTypeDescription
differencesDifferencesDifferences between given and must (undefined when equal).

View source


TestTuple<Value>

1
type TestTuple<Value>: readonly [ReadOnlyURL, Test<Value>];

Tuple used to describe a test result and its path.

See

Type parameters

Type parameterValue
Valueunknown

View source


Tests<Value>

1
type Tests<Value>: IsomorphicIterable<Test<Value>>;

Iterable of Test.

Example

1
const tests: Tests<number> = [
2
{
3
given: "a number",
4
must: "make it double",
5
received: () => double(2),
6
wanted: () => 4,
7
},
8
];

See

Test

Type parameters

Type parameterValueDescription
ValueunknownType of value being tested.

View source


TestsImport<Value>

1
type TestsImport<Value>: Promise<ReadOnlyRecord<PropertyKey, Test<Value> | Tests<Value>>>;

Promise import of a file containing Test or Tests.

See

Type parameters

Type parameterValue
Valueunknown

View source


evaluate()

1
function evaluate<Value>(testDescription: Test<Value>): Promise<object>;

Takes a Test object and returns a promise with a TestResult.

Type parameters

Type parameter
Value

Parameters

ParameterTypeDescription
testDescriptionTest<Value>A Test object.

Returns

Promise<object>

A promise with a TestResult object.

MemberTypeValue
differencesundefined | Differences-
givenstringtestDescription.given
muststringtestDescription.must

Example

1
evaluate({
2
given: "🟢",
3
must: "🟩",
4
received: () => "🟩",
5
wanted: () => "🟩",
6
}); // Promise<{ given: "🟢", , must: "🟩" }>
7
evaluate({
8
given: "🟢",
9
must: "🟩",
10
received: () => "",
11
wanted: () => "🟩",
12
}); // Promise<{ differences: […], given: "🟢", , must: "🟩" }>

View source


isTest()

1
function isTest<Actual>(value: Test | Actual): value is Test;

Check if given value is a Test.

Type parameters

Type parameterValue
Actualunknown

Parameters

ParameterTypeDescription
valueTest | ActualValue to check.

Returns

value is Test

true if is a Test, false otherwise.

Example

1
isTest({ given: "🟢", must: "🟩", received: () => "🟩", wanted: () => "🟩" }); // true
2
isTest({ given: "🟢", must: "🟩", received: "🟩", wanted: "🟩" }); // false
3
isTest({ given: 1, must: 2, received: 3, wanted: 4 }); // false
4
isTest(); // false

View source