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

Ζ¬ ReadOnlyURL: ReadOnly<URL>

Read-only URL.

View source


ReadOnlyURLs

Ζ¬ ReadOnlyURLs: IsomorphicIterable<ReadOnlyURL>

Iterable of ReadOnlyURLs.

See

ReadOnlyURL

View source


TestTuple

Ζ¬ TestTuple<Value>: readonly [path: ReadOnlyURL, test: Test<Value>]

Tuple used to describe a test result and its path.

See

Type parameters

NameType
Valueunknown

View source


TestsImport

Ζ¬ TestsImport<Value>: Promise<ReadOnlyRecord<PropertyKey, Test<Value> | Tests<Value>>>

Promise import of a file containing Test or Tests.

See

Type parameters

NameType
Valueunknown

View source


testImport

β–Έ testImport(url): AsyncGenerator<Test<unknown>, void, unknown>

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

Parameters

NameTypeDescription
urlObject-
url.hashstringMDN Reference
url.hoststringMDN Reference
url.hostnamestringMDN Reference
url.hrefstringMDN Reference
url.originstringMDN Reference
url.passwordstringMDN Reference
url.pathnamestringMDN Reference
url.portstringMDN Reference
url.protocolstringMDN Reference
url.searchstringMDN Reference
url.searchParamsReadonly<ReadonlyMap<string, null | string>>MDN Reference
url.usernamestringMDN Reference
url.toJSON() => string-
url.toString() => string-

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


testsImport

β–Έ testsImport(urls): AsyncGenerator<TestTuple, void, unknown>

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

Parameters

NameTypeDescription
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

β€’ Const EXCEPTION: "EXCEPTION"

Exception difference kind.

View source


UNKNOWN_ERROR

β€’ Const UNKNOWN_ERROR: "Unknown Error"

Unknown error.

View source

Output

FAIL

β€’ Const FAIL: `${string}`

Fail message with colors.

View source


FAILED_TESTS

β€’ Const FAILED_TESTS: string

Failed test title with colors.

View source


PASS

β€’ Const PASS: `${string}`

Pass message with colors.

View source


TEST

β€’ Const TEST: `${string}`

Test message to be shown next to the test path.

View source


formatValueDictionary

β€’ Const formatValueDictionary: ReadOnlyRecord<TypeOfValue, (value: unknown) => string>

Dictionary type->formatter to be used by formatValue.

View source


stringifyDifferenceDictionary

β€’ Const stringifyDifferenceDictionary: { readonly [Kind in Difference[β€œkind”]]: Function }

Dictionary Difference kind->formatter.

View source


formatPropertyPath

β–Έ formatPropertyPath(propertyPath): string

Stringifies and colorizes an array representing a property path.

Parameters

NameTypeDescription
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

β–Έ formatValue(value): string

Colorizes and formats a value based on its type.

Parameters

NameTypeDescription
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

β–Έ stringifyDifference(difference): string

Takes a Difference object and returns a string using stringifyDifferenceDictionary.

Parameters

NameTypeDescription
differenceDifferenceDifference object.

Returns

string

Formatted string.

Example

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

View source


stringifyTest

β–Έ stringifyTest(testResult): string

Takes a TestResult and returns a readable string..

Parameters

NameTypeDescription
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

Ζ¬ Difference: CreateDifference | DeleteDifference | UpdateDifference | { error: unknown ; kind: typeof EXCEPTION }

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

Ζ¬ 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

Ζ¬ 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

NameTypeDescription
ValueunknownType of value being tested.

Type declaration

NameTypeDescription
givenstringDescription of the given value.
muststringDescription of the wanted value.
received() => Awaitable<Value>-
wanted() => Awaitable<Value>-

View source


TestResult

Ζ¬ TestResult: Pick<Test, "given" | "must"> & { differences?: Differences }

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.

View source


TestTuple

Ζ¬ TestTuple<Value>: readonly [path: ReadOnlyURL, test: Test<Value>]

Tuple used to describe a test result and its path.

See

Type parameters

NameType
Valueunknown

View source


Tests

Ζ¬ 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

NameTypeDescription
ValueunknownType of value being tested.

View source


TestsImport

Ζ¬ TestsImport<Value>: Promise<ReadOnlyRecord<PropertyKey, Test<Value> | Tests<Value>>>

Promise import of a file containing Test or Tests.

See

Type parameters

NameType
Valueunknown

View source


isTest

β–Έ isTest<Actual>(value): value is Test

Check if given value is a Test.

Type parameters

NameType
Actualunknown

Parameters

NameTypeDescription
valueActual | TestValue 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


test

β–Έ test<Value>(testDescription): Promise<{ differences: undefined | Differences ; given: string = testDescription.given; must: string = testDescription.must }>

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

Type parameters

Name
Value

Parameters

NameTypeDescription
testDescriptionTest<Value>A Test object.

Returns

Promise<{ differences: undefined | Differences ; given: string = testDescription.given; must: string = testDescription.must }>

A promise with a TestResult object.

Example

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

View source