bdd-vitest 0.1.1 → 1.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 +88 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# bdd-vitest
|
|
2
2
|
|
|
3
|
-
Enforced Given/When/Then for Vitest. Tests become documentation.
|
|
3
|
+
Enforced Given/When/Then for Vitest. Tests become documentation. Zero config.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { unit, feature, expect } from "bdd-vitest";
|
|
@@ -14,7 +14,7 @@ feature("Checkout", () => {
|
|
|
14
14
|
});
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Read just the descriptions
|
|
17
|
+
Read just the descriptions. You understand the system without opening production code.
|
|
18
18
|
|
|
19
19
|
## Why
|
|
20
20
|
|
|
@@ -23,7 +23,7 @@ Most test frameworks let you write `it("does something", () => {})` with no stru
|
|
|
23
23
|
bdd-vitest makes it impossible:
|
|
24
24
|
|
|
25
25
|
- **Descriptions are required.** Every phase is a `["description", fn]` tuple. TypeScript rejects missing descriptions at compile time.
|
|
26
|
-
- **Levels are required.** No generic `scenario
|
|
26
|
+
- **Levels are required.** No generic `scenario`. You must pick `unit`, `component`, `integration`, or `e2e`. Each has enforced timeouts.
|
|
27
27
|
- **Assertions are required.** `then` is mandatory. No test without a check.
|
|
28
28
|
|
|
29
29
|
## Install
|
|
@@ -44,7 +44,7 @@ import { unit, component, integration, e2e } from "bdd-vitest";
|
|
|
44
44
|
|-------|---------|-------------------|
|
|
45
45
|
| `unit` | 100ms | Pure functions, calculations, parsing, validation |
|
|
46
46
|
| `component` | 5s | One service with mocked deps (mockServer, mockFetch) |
|
|
47
|
-
| `integration` | 30s |
|
|
47
|
+
| `integration` | 30s | Real services, real files, real I/O |
|
|
48
48
|
| `e2e` | 120s | Full system, browser, real network, real database |
|
|
49
49
|
|
|
50
50
|
```
|
|
@@ -74,7 +74,7 @@ component("health check", {
|
|
|
74
74
|
});
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
`when` receives `given`'s return. `then` receives both:
|
|
78
78
|
|
|
79
79
|
```ts
|
|
80
80
|
unit("FIFO order", {
|
|
@@ -138,6 +138,25 @@ component("handles 404", {
|
|
|
138
138
|
});
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
## Database tests
|
|
142
|
+
|
|
143
|
+
No special API needed. `given` sets up, `cleanup` tears down:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
integration("finds user by email", {
|
|
147
|
+
given: ["a seeded database", async () => {
|
|
148
|
+
const db = await createTestDb();
|
|
149
|
+
await db.users.insert({ email: "alice@test.com", name: "Alice" });
|
|
150
|
+
return db;
|
|
151
|
+
}],
|
|
152
|
+
when: ["querying", (db) => db.users.findBy({ email: "alice@test.com" })],
|
|
153
|
+
then: ["returns Alice", (user) => expect(user.name).toBe("Alice")],
|
|
154
|
+
cleanup: (db) => db.destroy(),
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Works with any database library. Bring your own setup.
|
|
159
|
+
|
|
141
160
|
## Table-driven
|
|
142
161
|
|
|
143
162
|
```ts
|
|
@@ -164,6 +183,70 @@ feature("Auth", () => {
|
|
|
164
183
|
});
|
|
165
184
|
```
|
|
166
185
|
|
|
186
|
+
## Real world example
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { unit, component, integration, feature, rule, expect } from "bdd-vitest";
|
|
190
|
+
import { mockServer } from "bdd-vitest/mock-server";
|
|
191
|
+
|
|
192
|
+
feature("Ship AI", () => {
|
|
193
|
+
rule("airlock access", () => {
|
|
194
|
+
unit("denies crew without clearance", {
|
|
195
|
+
given: ["a crew member with no override", () => ({ crew: "Kai", clearance: 0 })],
|
|
196
|
+
when: ["requesting airlock", (ctx) => shipAI.evaluateRequest(ctx)],
|
|
197
|
+
then: ["denies the request", (res) => {
|
|
198
|
+
expect(res.granted).toBe(false);
|
|
199
|
+
expect(res.reason).toContain("insufficient clearance");
|
|
200
|
+
}],
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
unit.outline("clearance levels", [
|
|
204
|
+
{ name: "cadet: denied", clearance: 0, expected: false },
|
|
205
|
+
{ name: "engineer: denied", clearance: 1, expected: false },
|
|
206
|
+
{ name: "commander: granted", clearance: 9, expected: true },
|
|
207
|
+
], {
|
|
208
|
+
given: (row) => ({ crew: "Kai", clearance: row.clearance as number }),
|
|
209
|
+
when: (ctx) => shipAI.evaluateRequest(ctx),
|
|
210
|
+
then: (res, _ctx, row) => expect(res.granted).toBe(row.expected),
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
rule("crew monitoring API", () => {
|
|
215
|
+
component("reports life signs", {
|
|
216
|
+
given: ["a sensor API", mockServer({
|
|
217
|
+
"GET /crew/kai/vitals": { heartRate: 72, o2: 98, status: "nominal" },
|
|
218
|
+
})],
|
|
219
|
+
when: ["checking vitals", (server) => shipAI.checkCrew("kai", server.url)],
|
|
220
|
+
then: ["reports nominal", (report) => expect(report.status).toBe("nominal")],
|
|
221
|
+
cleanup: (server) => server.close(),
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
component("handles sensor failure", {
|
|
225
|
+
given: ["a failing sensor API", mockServer({
|
|
226
|
+
"GET /crew/kai/vitals": 503,
|
|
227
|
+
})],
|
|
228
|
+
when: ["checking vitals", (server) => shipAI.checkCrew("kai", server.url)],
|
|
229
|
+
then: ["triggers alert", (report) => expect(report.status).toBe("sensor_failure")],
|
|
230
|
+
cleanup: (server) => server.close(),
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
rule("mission log", () => {
|
|
235
|
+
integration("logs all crew requests", {
|
|
236
|
+
given: ["a mission database", async () => {
|
|
237
|
+
const db = await createMissionDb();
|
|
238
|
+
await shipAI.logRequest(db, { crew: "Kai", action: "open_airlock" });
|
|
239
|
+
await shipAI.logRequest(db, { crew: "Yara", action: "check_antenna" });
|
|
240
|
+
return db;
|
|
241
|
+
}],
|
|
242
|
+
when: ["querying the log", (db) => db.logs.findAll()],
|
|
243
|
+
then: ["contains both entries", (logs) => expect(logs).toHaveLength(2)],
|
|
244
|
+
cleanup: (db) => db.destroy(),
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
167
250
|
## API
|
|
168
251
|
|
|
169
252
|
| Export | What |
|