@jterrazz/test 13.0.1 → 14.0.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 +20 -20
- package/dist/checker.js +1079 -895
- package/dist/index.d.ts +15 -15
- package/dist/index.js +23 -23
- package/dist/literate.js +11 -11
- package/dist/match.js +44 -7
- package/dist/oxlint.cjs +179 -43
- package/dist/oxlint.js +179 -43
- package/dist/spec-document.js +46 -4
- package/package.json +1 -1
- package/schema/spec.schema.json +1 -1
package/README.md
CHANGED
|
@@ -32,10 +32,10 @@ import { expect, test } from 'vitest';
|
|
|
32
32
|
import { api } from '../api.specification.js';
|
|
33
33
|
|
|
34
34
|
test('creates a user', async () => {
|
|
35
|
-
// Given - the complete request from
|
|
35
|
+
// Given - the complete request from _requests/create-user.http
|
|
36
36
|
const result = await api.request('create-user.http');
|
|
37
37
|
|
|
38
|
-
// Then - status + headers + body from
|
|
38
|
+
// Then - status + headers + body from _expected/user-created.http; row in db
|
|
39
39
|
expect(result.response).toMatch('user-created.http');
|
|
40
40
|
await expect(result.table('users')).toMatchRows({
|
|
41
41
|
columns: ['name'],
|
|
@@ -97,7 +97,7 @@ import { specification } from '@jterrazz/test';
|
|
|
97
97
|
import { afterAll } from 'vitest';
|
|
98
98
|
|
|
99
99
|
export const { cleanup, website } = await specification.website({
|
|
100
|
-
server: { command: 'node specs/
|
|
100
|
+
server: { command: 'node specs/_fixtures/website-app/server.mjs', ready: '/' },
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
afterAll(cleanup);
|
|
@@ -225,7 +225,7 @@ Tests a rendered website: `.fetch(path)` for a raw HTTP exchange (redirects neve
|
|
|
225
225
|
|
|
226
226
|
```typescript
|
|
227
227
|
export const { website, cleanup } = await specification.website({
|
|
228
|
-
server: { command: 'node specs/
|
|
228
|
+
server: { command: 'node specs/_fixtures/website-app/server.mjs', ready: '/' },
|
|
229
229
|
});
|
|
230
230
|
|
|
231
231
|
// Raw exchange — status + headers, redirects surface as 3xx
|
|
@@ -268,9 +268,9 @@ When `root` is absent, the framework walks up from the specification file to the
|
|
|
268
268
|
|
|
269
269
|
| Method | Facets | Description |
|
|
270
270
|
| --------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
|
|
271
|
-
| `.seed("file.sql", { database? })` | all | Load SQL from `
|
|
272
|
-
| `.fixture("file")` | cli | Copy the feature-local `
|
|
273
|
-
| `.fixture("$FIXTURES/name/")` | cli | Spread the shared `specs/
|
|
271
|
+
| `.seed("file.sql", { database? })` | all | Load SQL from `_seeds/` — `database` is the record key (mandatory with ≥ 2 databases, forbidden with 1) |
|
|
272
|
+
| `.fixture("file")` | cli | Copy the feature-local `_fixtures/file` into the working directory |
|
|
273
|
+
| `.fixture("$FIXTURES/name/")` | cli | Spread the shared `specs/_fixtures/name/` project into the cwd (trailing `/` = contents; layers) |
|
|
274
274
|
| `.env({ KEY: "value" })` | cli | Set env vars on the child (`null` unsets, `$WORKDIR` expands, calls merge) |
|
|
275
275
|
| `.headers({ "Accept-Language": "fr" })` | api, website | Set HTTP request headers (merge on top of `.http` file headers, or on the browser context) |
|
|
276
276
|
| `.intercept(contracts)` | all but cli | Declare the world: a `defineContracts(...)` composite — MSW on api/jobs, the stub backend on website/mobile |
|
|
@@ -281,7 +281,7 @@ When `root` is absent, the framework walks up from the specification file to the
|
|
|
281
281
|
|
|
282
282
|
| Method | Facet | Resolves to | Description |
|
|
283
283
|
| ------------------------------------------ | ------- | -------------- | ---------------------------------------------------------------------------------------- |
|
|
284
|
-
| `.request("create-user.http")` | api | `HttpResult` | Send the COMPLETE request from `
|
|
284
|
+
| `.request("create-user.http")` | api | `HttpResult` | Send the COMPLETE request from `_requests/<file>` (method, path, headers, raw body) |
|
|
285
285
|
| `.get(path)` / `.delete(path)` | api | `HttpResult` | Inline requests for simple cases |
|
|
286
286
|
| `.post(path, body?)` / `.put(path, body?)` | api | `HttpResult` | Inline body: plain object, JSON-serialized |
|
|
287
287
|
| `.trigger("name")` | jobs | `BaseResult` | Execute a registered job |
|
|
@@ -302,7 +302,7 @@ Accessors are **read-only**; the framework registers subject-typed matchers on v
|
|
|
302
302
|
```typescript
|
|
303
303
|
// HTTP
|
|
304
304
|
expect(result.status).toBe(201);
|
|
305
|
-
expect(result.response).toMatch('user-created.http'); //
|
|
305
|
+
expect(result.response).toMatch('user-created.http'); // _expected/<name> — status + header subset + body
|
|
306
306
|
expect(result.response.body).toEqual({ error: 'User 999 not found' });
|
|
307
307
|
|
|
308
308
|
// Tables (async — queries the database)
|
|
@@ -314,33 +314,33 @@ await expect(result.table('orders', { database: 'db' })).toBeEmpty();
|
|
|
314
314
|
|
|
315
315
|
// Streams (ANSI stripped by default; .text stays raw)
|
|
316
316
|
expect(result.stdout).toContain('Build completed');
|
|
317
|
-
expect(result.stdout).toMatch('help.txt'); //
|
|
318
|
-
expect(result.json).toMatch('config.json'); //
|
|
317
|
+
expect(result.stdout).toMatch('help.txt'); // _expected/help.txt — {{token}}-aware
|
|
318
|
+
expect(result.json).toMatch('config.json'); // _expected/config.json
|
|
319
319
|
expect(result.json.value).toMatchObject({ name: 'shoply' });
|
|
320
320
|
|
|
321
321
|
// Files & trees
|
|
322
322
|
expect(result.file('my-shop/shoply.yaml').content).toContain('name: my-shop');
|
|
323
|
-
await expect(result.directory('my-shop')).toMatch('shop-scaffold'); //
|
|
323
|
+
await expect(result.directory('my-shop')).toMatch('shop-scaffold'); // _expected/shop-scaffold/
|
|
324
324
|
await expect(result.filesystem).toMatch('upgraded-shop'); // whole cwd
|
|
325
325
|
|
|
326
326
|
// Containers (docker-aware cli)
|
|
327
327
|
await expect(result.container('alpha')).toBeRunning();
|
|
328
328
|
```
|
|
329
329
|
|
|
330
|
-
`toMatch` always resolves against `
|
|
330
|
+
`toMatch` always resolves against `_expected/<name>` — every subject, no exceptions (only `.request()` reads `_requests/`). The folder is flat: a slash in the name creates a subfolder; the extension is part of the name and required, except for tree snapshots which are directories.
|
|
331
331
|
|
|
332
332
|
**Updating snapshots:** `TEST_UPDATE=1` or `vitest -u`. Update mode writes **tokens**, not values — segments covered by an existing placeholder are preserved, and `{{workdir}}` is substituted automatically.
|
|
333
333
|
|
|
334
334
|
## Dynamic values — one `{{token}}` grammar
|
|
335
335
|
|
|
336
|
-
The same vocabulary works in `
|
|
336
|
+
The same vocabulary works in `_expected/*.http` (body AND headers), `_expected/*.json`, text snapshots, and tree-snapshot file contents — and in code via `match.*`:
|
|
337
337
|
|
|
338
338
|
`uuid` `ulid` `iso8601` `date` `time` `duration` `number` `int` `float` `semver` `sha` `hex` `base64` `port` `ip` `url` `email` `path` `workdir` `string` `any`
|
|
339
339
|
|
|
340
340
|
Each token is capturable via `{{type#ref}}`: the first occurrence captures, later occurrences must be equal (scope: one spec). Code-side: `match.ref('order')`, `match.ref('intent', { not: 'order' })`, `match.regex(/…/)`.
|
|
341
341
|
|
|
342
342
|
```http
|
|
343
|
-
###
|
|
343
|
+
### _expected/order-created.http
|
|
344
344
|
HTTP/1.1 201 Created
|
|
345
345
|
Content-Type: application/json
|
|
346
346
|
Location: /orders/{{uuid#order}}
|
|
@@ -436,14 +436,14 @@ specs/<facet>/ # api | jobs | cli | integrations | lint
|
|
|
436
436
|
├── <facet>.specification.ts # runner(s) at the facet ROOT (rule C1)
|
|
437
437
|
└── <domain>/ # a product command/area — 1..n test files
|
|
438
438
|
├── <aspect>.test.ts
|
|
439
|
-
├──
|
|
440
|
-
├──
|
|
439
|
+
├── _seeds/ # *.sql ONLY — database state
|
|
440
|
+
├── _requests/ # *.http — inputs: COMPLETE request (method, path, headers, body)
|
|
441
441
|
├── contracts/ # <name>.contracts.ts facade + <provider>/<name>.ts units + their .response.json / .request.ts data
|
|
442
|
-
├──
|
|
443
|
-
└──
|
|
442
|
+
├── _fixtures/ # domain-local files/dirs copied into the cwd (cli) — shared pool lives at specs/_fixtures/
|
|
443
|
+
└── _expected/ # ALL expected fixtures, FLAT (incl. response *.http) — a slash in the name creates a subfolder
|
|
444
444
|
```
|
|
445
445
|
|
|
446
|
-
A test with its OWN asset dirs gets its own domain folder; tests without local assets group as sibling `<aspect>.test.ts` files inside a named group folder (the folder follows the assets). `.fixture(path)` is the one verb that copies into the cwd: domain-local (`
|
|
446
|
+
A test with its OWN asset dirs gets its own domain folder; tests without local assets group as sibling `<aspect>.test.ts` files inside a named group folder (the folder follows the assets). `.fixture(path)` is the one verb that copies into the cwd: domain-local (`_fixtures/…`) or shared (`$FIXTURES/…` → `specs/_fixtures/…`), with rsync trailing-slash semantics and layering. `.seed()` is SQL-only.
|
|
447
447
|
|
|
448
448
|
Every test contains `// Given -` and `// Then -` comments (always both; `// When -` only if the action is not obvious — the chain IS the when). User-facing framework env vars: `TEST_MODE` and `TEST_UPDATE` — the only ones you set; the framework also reads vitest's `VITEST_POOL_ID` for per-worker isolation.
|
|
449
449
|
|