@rcompat/test 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +362 -0
- package/lib/private/types/fn.d.ts +1 -1
- package/lib/private/types/fn.js +1 -1
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# @rcompat/test
|
|
2
|
+
|
|
3
|
+
Testing library for JavaScript runtimes.
|
|
4
|
+
|
|
5
|
+
## What is @rcompat/test?
|
|
6
|
+
|
|
7
|
+
A cross-runtime testing library with a fluent assertion API. Provides deep
|
|
8
|
+
equality checks, type assertions, and exception testing. Designed to work
|
|
9
|
+
with the `proby` test runner. Works consistently across Node, Deno, and Bun.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @rcompat/test
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @rcompat/test
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn add @rcompat/test
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bun add @rcompat/test
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Writing tests
|
|
32
|
+
|
|
33
|
+
Create a `.spec.ts` or `.spec.js` file:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import test from "@rcompat/test";
|
|
37
|
+
|
|
38
|
+
test.case("addition works", assert => {
|
|
39
|
+
assert(1 + 1).equals(2);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test.case("string concatenation", assert => {
|
|
43
|
+
assert("hello" + " " + "world").equals("hello world");
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Running tests
|
|
48
|
+
|
|
49
|
+
Use the `proby` test runner:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx proby
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Equality assertions
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
import test from "@rcompat/test";
|
|
59
|
+
|
|
60
|
+
test.case("equals", assert => {
|
|
61
|
+
// primitives
|
|
62
|
+
assert(42).equals(42);
|
|
63
|
+
assert("hello").equals("hello");
|
|
64
|
+
assert(true).equals(true);
|
|
65
|
+
|
|
66
|
+
// objects (deep equality)
|
|
67
|
+
assert({ a: 1, b: 2 }).equals({ a: 1, b: 2 });
|
|
68
|
+
assert([1, 2, 3]).equals([1, 2, 3]);
|
|
69
|
+
|
|
70
|
+
// nested structures
|
|
71
|
+
assert({ users: [{ name: "Alice" }] }).equals({
|
|
72
|
+
users: [{ name: "Alice" }],
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test.case("nequals", assert => {
|
|
77
|
+
assert(1).nequals(2);
|
|
78
|
+
assert("foo").nequals("bar");
|
|
79
|
+
assert({ a: 1 }).nequals({ a: 2 });
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Boolean assertions
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
import test from "@rcompat/test";
|
|
87
|
+
|
|
88
|
+
test.case("boolean checks", assert => {
|
|
89
|
+
assert(1 === 1).true();
|
|
90
|
+
assert(1 === 2).false();
|
|
91
|
+
assert(null).null();
|
|
92
|
+
assert(undefined).undefined();
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Instance assertions
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import test from "@rcompat/test";
|
|
100
|
+
|
|
101
|
+
test.case("instanceof", assert => {
|
|
102
|
+
assert(new Date()).instance(Date);
|
|
103
|
+
assert(new Map()).instance(Map);
|
|
104
|
+
assert([1, 2, 3]).instance(Array);
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Exception assertions
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
import test from "@rcompat/test";
|
|
112
|
+
|
|
113
|
+
test.case("throws", assert => {
|
|
114
|
+
// check that function throws
|
|
115
|
+
assert(() => {
|
|
116
|
+
throw new Error("oops");
|
|
117
|
+
}).throws();
|
|
118
|
+
|
|
119
|
+
// Check specific error message
|
|
120
|
+
assert(() => {
|
|
121
|
+
throw new Error("invalid input");
|
|
122
|
+
}).throws("invalid input");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test.case("tries (does not throw)", assert => {
|
|
126
|
+
assert(() => {
|
|
127
|
+
return 42;
|
|
128
|
+
}).tries();
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Type assertions
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import test from "@rcompat/test";
|
|
136
|
+
|
|
137
|
+
test.case("type checking", assert => {
|
|
138
|
+
// compile-time type checks
|
|
139
|
+
assert<string>().type<string>();
|
|
140
|
+
assert("hello").type<string>();
|
|
141
|
+
|
|
142
|
+
// check types don't match
|
|
143
|
+
assert<string>().nottype<number>();
|
|
144
|
+
assert(42).nottype<string>();
|
|
145
|
+
|
|
146
|
+
// literal types
|
|
147
|
+
assert<"foo">().type<"foo">();
|
|
148
|
+
assert<"foo">().nottype<"bar">();
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Async tests
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
import test from "@rcompat/test";
|
|
156
|
+
|
|
157
|
+
test.case("async operations", async assert => {
|
|
158
|
+
const result = await Promise.resolve(42);
|
|
159
|
+
assert(result).equals(42);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test.case("fetch data", async assert => {
|
|
163
|
+
const response = await fetch("https://api.example.com/data");
|
|
164
|
+
assert(response.ok).true();
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Cleanup with ended
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
import test from "@rcompat/test";
|
|
172
|
+
|
|
173
|
+
test.case("database test", async assert => {
|
|
174
|
+
const db = await openDatabase();
|
|
175
|
+
const user = await db.createUser({ name: "Alice" });
|
|
176
|
+
assert(user.name).equals("Alice");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test.ended(async () => {
|
|
180
|
+
// cleanup after all tests in this file
|
|
181
|
+
await closeDatabase();
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## API Reference
|
|
186
|
+
|
|
187
|
+
### `test.case`
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
test.case(name: string, body: (assert: Asserter) => void | Promise<void>): void;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Define a test case.
|
|
194
|
+
|
|
195
|
+
| Parameter | Type | Description |
|
|
196
|
+
| --------- | ---------- | ------------------------------------- |
|
|
197
|
+
| `name` | `string` | Test case name |
|
|
198
|
+
| `body` | `function` | Test function receiving assert helper |
|
|
199
|
+
|
|
200
|
+
### `test.ended`
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
test.ended(callback: () => void | Promise<void>): void;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Register a cleanup callback to run after all tests in the file.
|
|
207
|
+
|
|
208
|
+
### `Asserter`
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
type Asserter = <T>(actual?: T) => Assert<T>;
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The assert function passed to test cases.
|
|
215
|
+
|
|
216
|
+
### `Assert<T>`
|
|
217
|
+
|
|
218
|
+
| Method | Description |
|
|
219
|
+
| ----------------------- | ----------------------------------------- |
|
|
220
|
+
| `equals(expected)` | Deep equality check |
|
|
221
|
+
| `nequals(expected)` | Deep inequality check |
|
|
222
|
+
| `true()` | Assert value is `true` |
|
|
223
|
+
| `false()` | Assert value is `false` |
|
|
224
|
+
| `null()` | Assert value is `null` |
|
|
225
|
+
| `undefined()` | Assert value is `undefined` |
|
|
226
|
+
| `instance(constructor)` | Assert value is instance of class |
|
|
227
|
+
| `throws(message?)` | Assert function throws (optional message) |
|
|
228
|
+
| `tries()` | Assert function does not throw |
|
|
229
|
+
| `type<T>()` | Compile-time type assertion |
|
|
230
|
+
| `nottype<T>()` | Compile-time negative type assertion |
|
|
231
|
+
| `pass()` | Manually pass the assertion |
|
|
232
|
+
| `fail(reason?)` | Manually fail the assertion |
|
|
233
|
+
|
|
234
|
+
### Utilities
|
|
235
|
+
|
|
236
|
+
#### `equals`
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
import equals from "@rcompat/test/equals";
|
|
240
|
+
|
|
241
|
+
equals(a: unknown, b: unknown): boolean;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Deep equality check supporting primitives, objects, arrays, maps, sets, dates.
|
|
245
|
+
|
|
246
|
+
#### `any`
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import any from "@rcompat/test/any";
|
|
250
|
+
|
|
251
|
+
any(value: unknown): never;
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Cast any value to `never` type for testing purposes.
|
|
255
|
+
|
|
256
|
+
#### `undef`
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import undef from "@rcompat/test/undef";
|
|
260
|
+
|
|
261
|
+
const value: never = undef;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Pre-cast `undefined` value typed as `never`.
|
|
265
|
+
|
|
266
|
+
#### `E`
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import E from "@rcompat/test/E";
|
|
270
|
+
|
|
271
|
+
E(error: unknown): { message: string };
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Extract error message from unknown error type.
|
|
275
|
+
|
|
276
|
+
## Examples
|
|
277
|
+
|
|
278
|
+
### Testing a utility function
|
|
279
|
+
|
|
280
|
+
```js
|
|
281
|
+
// sum.ts
|
|
282
|
+
export default (a, b) => a + b;
|
|
283
|
+
|
|
284
|
+
// sum.spec.ts
|
|
285
|
+
import test from "@rcompat/test";
|
|
286
|
+
import sum from "./sum.js";
|
|
287
|
+
|
|
288
|
+
test.case("sum adds two numbers", assert => {
|
|
289
|
+
assert(sum(1, 2)).equals(3);
|
|
290
|
+
assert(sum(-1, 1)).equals(0);
|
|
291
|
+
assert(sum(0, 0)).equals(0);
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Testing a class
|
|
296
|
+
|
|
297
|
+
```js
|
|
298
|
+
import test from "@rcompat/test";
|
|
299
|
+
|
|
300
|
+
class Calculator {
|
|
301
|
+
#value = 0;
|
|
302
|
+
add(n) {
|
|
303
|
+
this.#value += n;
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
subtract(n) {
|
|
307
|
+
this.#value -= n;
|
|
308
|
+
return this;
|
|
309
|
+
}
|
|
310
|
+
get value() {
|
|
311
|
+
return this.#value;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
test.case("calculator operations", assert => {
|
|
316
|
+
const calc = new Calculator();
|
|
317
|
+
calc.add(10).subtract(3);
|
|
318
|
+
assert(calc.value).equals(7);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test.case("calculator is instance", assert => {
|
|
322
|
+
assert(new Calculator()).instance(Calculator);
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Testing error handling
|
|
327
|
+
|
|
328
|
+
```js
|
|
329
|
+
import test from "@rcompat/test";
|
|
330
|
+
|
|
331
|
+
function divide(a, b) {
|
|
332
|
+
if (b === 0) throw new Error("Division by zero");
|
|
333
|
+
return a / b;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
test.case("divide throws on zero", assert => {
|
|
337
|
+
assert(() => divide(10, 0)).throws("Division by zero");
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test.case("divide works normally", assert => {
|
|
341
|
+
assert(() => divide(10, 2)).tries();
|
|
342
|
+
assert(divide(10, 2)).equals(5);
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Cross-Runtime Compatibility
|
|
347
|
+
|
|
348
|
+
| Runtime | Supported |
|
|
349
|
+
| ------- | --------- |
|
|
350
|
+
| Node.js | ✓ |
|
|
351
|
+
| Deno | ✓ |
|
|
352
|
+
| Bun | ✓ |
|
|
353
|
+
|
|
354
|
+
No configuration required — just import and use.
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
MIT
|
|
359
|
+
|
|
360
|
+
## Contributing
|
|
361
|
+
|
|
362
|
+
See [CONTRIBUTING.md](../../CONTRIBUTING.md) in the repository root.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type UnknownFunction from "@rcompat/type/UnknownFunction";
|
|
2
2
|
declare const _default: {
|
|
3
|
-
is: (x: unknown) => x is UnknownFunction;
|
|
4
3
|
equal: <T extends UnknownFunction>(x: T, y: T) => boolean;
|
|
4
|
+
is: (x: unknown) => x is UnknownFunction;
|
|
5
5
|
};
|
|
6
6
|
export default _default;
|
|
7
7
|
//# sourceMappingURL=fn.d.ts.map
|
package/lib/private/types/fn.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcompat/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Standard library testing",
|
|
5
5
|
"bugs": "https://github.com/rcompat/rcompat/issues",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"directory": "packages/test"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@rcompat/fs": "^0.
|
|
18
|
+
"@rcompat/fs": "^0.23.0"
|
|
19
19
|
},
|
|
20
20
|
"type": "module",
|
|
21
21
|
"imports": {
|