@jterrazz/test 5.1.0 → 5.3.1
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 +81 -79
- package/dist/index.cjs +1051 -1036
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +226 -199
- package/dist/index.d.ts +226 -199
- package/dist/index.js +1040 -1031
- package/dist/index.js.map +1 -1
- package/package.json +58 -54
package/README.md
CHANGED
|
@@ -12,16 +12,16 @@ npm install -D @jterrazz/test vitest
|
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
14
|
// tests/setup/integration.specification.ts
|
|
15
|
-
import { afterAll } from
|
|
16
|
-
import { integration, postgres } from
|
|
17
|
-
import { createApp } from
|
|
15
|
+
import { afterAll } from 'vitest';
|
|
16
|
+
import { integration, postgres } from '@jterrazz/test';
|
|
17
|
+
import { createApp } from '../../src/app.js';
|
|
18
18
|
|
|
19
|
-
const db = postgres({ compose:
|
|
19
|
+
const db = postgres({ compose: 'db' });
|
|
20
20
|
|
|
21
21
|
export const spec = await integration({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
services: [db],
|
|
23
|
+
app: () => createApp({ databaseUrl: db.connectionString }),
|
|
24
|
+
root: '../../',
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
afterAll(() => spec.cleanup());
|
|
@@ -29,21 +29,21 @@ afterAll(() => spec.cleanup());
|
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
31
|
// tests/e2e/users/users.e2e.test.ts
|
|
32
|
-
import { spec } from
|
|
33
|
-
|
|
34
|
-
test(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
import { spec } from '../../setup/integration.specification.js';
|
|
33
|
+
|
|
34
|
+
test('creates a user', async () => {
|
|
35
|
+
// Given — one existing user
|
|
36
|
+
const result = await spec('creates user')
|
|
37
|
+
.seed('initial-users.sql')
|
|
38
|
+
.post('/users', 'new-user.json')
|
|
39
|
+
.run();
|
|
40
|
+
|
|
41
|
+
// Then — user created
|
|
42
|
+
expect(result.status).toBe(201);
|
|
43
|
+
await result.table('users').toMatch({
|
|
44
|
+
columns: ['name'],
|
|
45
|
+
rows: [['Alice'], ['Bob']],
|
|
46
|
+
});
|
|
47
47
|
});
|
|
48
48
|
```
|
|
49
49
|
|
|
@@ -51,29 +51,29 @@ test("creates a user", async () => {
|
|
|
51
51
|
|
|
52
52
|
```typescript
|
|
53
53
|
// tests/setup/cli.specification.ts
|
|
54
|
-
import { resolve } from
|
|
55
|
-
import { cli } from
|
|
54
|
+
import { resolve } from 'node:path';
|
|
55
|
+
import { cli } from '@jterrazz/test';
|
|
56
56
|
|
|
57
57
|
export const spec = await cli({
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
command: resolve(import.meta.dirname, '../../bin/my-cli.sh'),
|
|
59
|
+
root: '../fixtures',
|
|
60
60
|
});
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
```typescript
|
|
64
64
|
// tests/e2e/build/build.e2e.test.ts
|
|
65
|
-
import { spec } from
|
|
66
|
-
|
|
67
|
-
test(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
import { spec } from '../../setup/cli.specification.js';
|
|
66
|
+
|
|
67
|
+
test('builds the project', async () => {
|
|
68
|
+
// Given — sample app project
|
|
69
|
+
const result = await spec('build').project('sample-app').exec('build').run();
|
|
70
|
+
|
|
71
|
+
// Then — ESM output with source maps
|
|
72
|
+
expect(result.exitCode).toBe(0);
|
|
73
|
+
expect(result.stdout).toContain('Build completed');
|
|
74
|
+
expect(result.file('dist/index.js').exists).toBe(true);
|
|
75
|
+
expect(result.file('dist/index.cjs').exists).toBe(false);
|
|
76
|
+
expect(result.file('dist/index.js').content).toContain('Hello');
|
|
77
77
|
});
|
|
78
78
|
```
|
|
79
79
|
|
|
@@ -86,15 +86,15 @@ Three modes, same builder API. Each handles infrastructure and cleanup automatic
|
|
|
86
86
|
Starts real containers via testcontainers. App runs in-process (Hono). Fastest feedback loop.
|
|
87
87
|
|
|
88
88
|
```typescript
|
|
89
|
-
import { integration, postgres, redis } from
|
|
89
|
+
import { integration, postgres, redis } from '@jterrazz/test';
|
|
90
90
|
|
|
91
|
-
const db = postgres({ compose:
|
|
92
|
-
const cache = redis({ compose:
|
|
91
|
+
const db = postgres({ compose: 'db' });
|
|
92
|
+
const cache = redis({ compose: 'cache' });
|
|
93
93
|
|
|
94
94
|
export const spec = await integration({
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
services: [db, cache],
|
|
96
|
+
app: () => createApp({ databaseUrl: db.connectionString }),
|
|
97
|
+
root: '../../',
|
|
98
98
|
});
|
|
99
99
|
```
|
|
100
100
|
|
|
@@ -103,10 +103,10 @@ export const spec = await integration({
|
|
|
103
103
|
Starts the full `docker/compose.test.yaml` stack. App URL and databases auto-detected.
|
|
104
104
|
|
|
105
105
|
```typescript
|
|
106
|
-
import { e2e } from
|
|
106
|
+
import { e2e } from '@jterrazz/test';
|
|
107
107
|
|
|
108
108
|
export const spec = await e2e({
|
|
109
|
-
|
|
109
|
+
root: '../../',
|
|
110
110
|
});
|
|
111
111
|
```
|
|
112
112
|
|
|
@@ -115,18 +115,18 @@ export const spec = await e2e({
|
|
|
115
115
|
Runs CLI commands against fixture projects in temp directories. Optionally starts infrastructure.
|
|
116
116
|
|
|
117
117
|
```typescript
|
|
118
|
-
import { cli } from
|
|
118
|
+
import { cli } from '@jterrazz/test';
|
|
119
119
|
|
|
120
120
|
export const spec = await cli({
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
command: resolve(import.meta.dirname, '../../bin/my-cli.sh'),
|
|
122
|
+
root: '../fixtures',
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
// With infrastructure (CLI that needs a database)
|
|
126
126
|
export const spec = await cli({
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
command: 'my-migrate-tool',
|
|
128
|
+
root: '../fixtures',
|
|
129
|
+
services: [db],
|
|
130
130
|
});
|
|
131
131
|
```
|
|
132
132
|
|
|
@@ -136,13 +136,13 @@ Every test follows the same pattern: `spec("label") → setup → action → ass
|
|
|
136
136
|
|
|
137
137
|
### Setup (cross-mode)
|
|
138
138
|
|
|
139
|
-
| Method | Description
|
|
140
|
-
| ---------------------------------------- |
|
|
141
|
-
| `.seed("file.sql")` | Load SQL from `seeds/file.sql` into the default database
|
|
142
|
-
| `.seed("file.sql", { service: "name" })` | Load SQL into a specific database
|
|
143
|
-
| `.fixture("file")` | Copy `fixtures/file` into the CLI working directory
|
|
144
|
-
| `.project("name")` |
|
|
145
|
-
| `.mock("file.json")` | Register mocked external API response (MSW, planned)
|
|
139
|
+
| Method | Description |
|
|
140
|
+
| ---------------------------------------- | --------------------------------------------------------- |
|
|
141
|
+
| `.seed("file.sql")` | Load SQL from `seeds/file.sql` into the default database |
|
|
142
|
+
| `.seed("file.sql", { service: "name" })` | Load SQL into a specific database |
|
|
143
|
+
| `.fixture("file")` | Copy `fixtures/file` into the CLI working directory |
|
|
144
|
+
| `.project("name")` | Copy `fixtures/name/` into a fresh temp dir and run there |
|
|
145
|
+
| `.mock("file.json")` | Register mocked external API response (MSW, planned) |
|
|
146
146
|
|
|
147
147
|
### Actions (one per spec, mutually exclusive)
|
|
148
148
|
|
|
@@ -164,6 +164,8 @@ Every test follows the same pattern: `spec("label") → setup → action → ass
|
|
|
164
164
|
| `.spawn("args", { waitFor, timeout })` | Run long-lived process, resolve on pattern match or timeout |
|
|
165
165
|
| `.env({ KEY: "value" })` | Set env vars on the child process (`null` unsets, `$WORKDIR` expands to the temp cwd) |
|
|
166
166
|
|
|
167
|
+
Every CLI spec runs in a **fresh, empty temp directory** by default. `.project("name")` starts from a copy of `fixtures/name/`; `.fixture("file")` seeds specific files into the temp dir. Bare specs (`spec("x").exec("...")`) get an empty dir — ideal for testing scaffolding CLIs that write into their cwd.
|
|
168
|
+
|
|
167
169
|
### Assertions
|
|
168
170
|
|
|
169
171
|
Result properties are raw values — use vitest `expect()` for assertions. Database and response file assertions use custom async methods.
|
|
@@ -198,10 +200,10 @@ Run with `JTERRAZZ_TEST_UPDATE=1` (or vitest `-u`) to overwrite fixtures with th
|
|
|
198
200
|
**Grep (scoped text matching):**
|
|
199
201
|
|
|
200
202
|
```typescript
|
|
201
|
-
import { grep } from
|
|
203
|
+
import { grep } from '@jterrazz/test';
|
|
202
204
|
|
|
203
|
-
expect(grep(result.stdout,
|
|
204
|
-
expect(grep(result.stdout,
|
|
205
|
+
expect(grep(result.stdout, 'unused-var.ts')).toContain('no-unused-vars');
|
|
206
|
+
expect(grep(result.stdout, 'valid/sorted.ts')).not.toContain('sort-imports');
|
|
205
207
|
```
|
|
206
208
|
|
|
207
209
|
`grep(output, pattern)` filters multi-line output to the block matching `pattern`, returning a string for vitest assertions.
|
|
@@ -251,10 +253,10 @@ await result.table("events", { service: "analytics-db" }).toMatch({
|
|
|
251
253
|
## Service factories
|
|
252
254
|
|
|
253
255
|
```typescript
|
|
254
|
-
import { postgres, redis } from
|
|
256
|
+
import { postgres, redis } from '@jterrazz/test';
|
|
255
257
|
|
|
256
|
-
const db = postgres({ compose:
|
|
257
|
-
const cache = redis({ compose:
|
|
258
|
+
const db = postgres({ compose: 'db' });
|
|
259
|
+
const cache = redis({ compose: 'cache' });
|
|
258
260
|
```
|
|
259
261
|
|
|
260
262
|
Service handles read image and environment from `docker/compose.test.yaml`. After the runner starts, `.connectionString` is populated from the running container.
|
|
@@ -267,7 +269,7 @@ Service handles read image and environment from `docker/compose.test.yaml`. Afte
|
|
|
267
269
|
## Mocking utilities
|
|
268
270
|
|
|
269
271
|
```typescript
|
|
270
|
-
import { mockOf, mockOfDate } from
|
|
272
|
+
import { mockOf, mockOfDate } from '@jterrazz/test';
|
|
271
273
|
```
|
|
272
274
|
|
|
273
275
|
| Export | Description |
|
|
@@ -318,19 +320,19 @@ tests/
|
|
|
318
320
|
Every test uses `// Given` and `// Then` comments. Always both, never one without the other.
|
|
319
321
|
|
|
320
322
|
```typescript
|
|
321
|
-
test(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
323
|
+
test('creates a user and returns 201', async () => {
|
|
324
|
+
// Given — two existing users
|
|
325
|
+
const result = await spec('creates user')
|
|
326
|
+
.seed('initial-users.sql')
|
|
327
|
+
.post('/users', 'new-user.json')
|
|
328
|
+
.run();
|
|
329
|
+
|
|
330
|
+
// Then — user created with all three in table
|
|
331
|
+
expect(result.status).toBe(201);
|
|
332
|
+
await result.table('users').toMatch({
|
|
333
|
+
columns: ['name'],
|
|
334
|
+
rows: [['Alice'], ['Bob'], ['Charlie']],
|
|
335
|
+
});
|
|
334
336
|
});
|
|
335
337
|
```
|
|
336
338
|
|