@stratal/testing 0.0.26 → 0.1.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/CHANGELOG.md +562 -0
- package/README.md +137 -36
- package/dist/database/index.d.mts +2 -2
- package/dist/database/index.mjs +2 -2
- package/dist/database-Rojs69r3.mjs +453 -0
- package/dist/database-Rojs69r3.mjs.map +1 -0
- package/dist/{decorate-B7nr7eBl.mjs → decorate-RQD1h28J.mjs} +1 -1
- package/dist/feature-flags/index.mjs +1 -1
- package/dist/{feature-flags-BiLhfSGh.mjs → feature-flags-CZ1a4M2g.mjs} +2 -2
- package/dist/{feature-flags-BiLhfSGh.mjs.map → feature-flags-CZ1a4M2g.mjs.map} +1 -1
- package/dist/index-B8Mw1Dxk.d.mts +175 -0
- package/dist/index-B8Mw1Dxk.d.mts.map +1 -0
- package/dist/{index-CrHzUDKX.d.mts → index-D7FM6EKp.d.mts} +21 -3
- package/dist/index-D7FM6EKp.d.mts.map +1 -0
- package/dist/index-qgWNJRdC.d.mts.map +1 -1
- package/dist/index.d.mts +105 -46
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +218 -108
- package/dist/index.mjs.map +1 -1
- package/dist/mocks/cloudflare-workers.d.mts +24 -0
- package/dist/mocks/cloudflare-workers.d.mts.map +1 -0
- package/dist/mocks/cloudflare-workers.mjs +28 -0
- package/dist/mocks/cloudflare-workers.mjs.map +1 -0
- package/dist/mocks/index.d.mts +2 -2
- package/dist/mocks/index.mjs +2 -2
- package/dist/mocks/noop-rate-limiter-store.d.mts +1 -3
- package/dist/mocks/noop-rate-limiter-store.d.mts.map +1 -1
- package/dist/mocks/zenstack-language.d.mts +26 -29
- package/dist/mocks/zenstack-language.d.mts.map +1 -1
- package/dist/mocks/zenstack-language.mjs +1 -1
- package/dist/mocks/zenstack-language.mjs.map +1 -1
- package/dist/storage/index.d.mts +1 -1
- package/dist/storage/index.mjs +1 -1
- package/dist/{storage-DhoxWqyF.mjs → storage-BiGamMA8.mjs} +73 -6
- package/dist/storage-BiGamMA8.mjs.map +1 -0
- package/dist/test-worker-exports-dCRARYEc.d.mts +144 -0
- package/dist/test-worker-exports-dCRARYEc.d.mts.map +1 -0
- package/dist/test-workers-cache-D02Pt_dE.mjs +183 -0
- package/dist/test-workers-cache-D02Pt_dE.mjs.map +1 -0
- package/dist/vitest-plugin/index.d.mts +14 -28
- package/dist/vitest-plugin/index.d.mts.map +1 -1
- package/dist/vitest-plugin/index.mjs +32 -20
- package/dist/vitest-plugin/index.mjs.map +1 -1
- package/package.json +30 -21
- package/dist/database-B02eYKhE.mjs +0 -334
- package/dist/database-B02eYKhE.mjs.map +0 -1
- package/dist/index-BIr5nLof.d.mts +0 -122
- package/dist/index-BIr5nLof.d.mts.map +0 -1
- package/dist/index-CrHzUDKX.d.mts.map +0 -1
- package/dist/storage-DhoxWqyF.mjs.map +0 -1
- package/dist/test-email-provider-B7cjj97-.d.mts +0 -21
- package/dist/test-email-provider-B7cjj97-.d.mts.map +0 -1
- package/dist/test-email-provider-Dr-nhE0x.mjs +0 -34
- package/dist/test-email-provider-Dr-nhE0x.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# @stratal/testing
|
|
2
2
|
|
|
3
|
-
Testing utilities and mocks for [Stratal](https://
|
|
3
|
+
Testing utilities and mocks for [Stratal](https://stratal.dev) applications.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@stratal/testing)
|
|
6
|
+
[](https://github.com/strataljs/stratal/actions/workflows/ci.yml)
|
|
6
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.npmjs.com/package/@stratal/testing)
|
|
9
|
+
[](https://www.typescriptlang.org/)
|
|
10
|
+
[](https://github.com/strataljs/stratal/pulls)
|
|
11
|
+
[](https://github.com/strataljs/stratal)
|
|
7
12
|
|
|
8
13
|
## Installation
|
|
9
14
|
|
|
@@ -29,7 +34,22 @@ npx skills add strataljs/stratal
|
|
|
29
34
|
|
|
30
35
|
| Skill | Description |
|
|
31
36
|
|---|---|
|
|
32
|
-
| `stratal` | Build Cloudflare Workers apps with the Stratal framework — modules, DI, controllers, routing, OpenAPI, queues, cron, events, seeders, CLI, auth, database,
|
|
37
|
+
| `stratal` | Build Cloudflare Workers apps with the Stratal framework — modules, DI, controllers, routing, OpenAPI, queues, cron, events, seeders, CLI, auth, database, access control, testing, and more |
|
|
38
|
+
|
|
39
|
+
## Vitest setup
|
|
40
|
+
|
|
41
|
+
`stratalTest()` wraps [`@cloudflare/vitest-plugin`](https://developers.cloudflare.com/workers/testing/vitest-integration/) with Stratal defaults (tslib alias, ZenStack mocks, SSR externals). It needs no database — `Test.createTestingModule()` and unit tests run with no DB wiring at all.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// vitest.config.ts
|
|
45
|
+
import { fixNobleHashesCjs, fixPgCjs, stratalTest } from '@stratal/testing/vitest-plugin'
|
|
46
|
+
|
|
47
|
+
export default defineConfig({
|
|
48
|
+
plugins: [fixPgCjs(), fixNobleHashesCjs(), stratalTest()],
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
A database is opt-in via the `database` option, which gives **each test file** its own database cloned from a migrated template. Omit it entirely for suites that don't touch Postgres.
|
|
33
53
|
|
|
34
54
|
## Quick Start
|
|
35
55
|
|
|
@@ -120,9 +140,17 @@ module.http
|
|
|
120
140
|
// Access fake storage for assertions
|
|
121
141
|
module.storage
|
|
122
142
|
|
|
143
|
+
// Other test clients and fakes
|
|
144
|
+
module.ws('/ws/chat') // WebSocket request builder
|
|
145
|
+
module.sse('/streaming/events') // Server-sent events request builder
|
|
146
|
+
module.quarry('users:create') // Quarry command request builder
|
|
147
|
+
module.cache // Workers Cache stub — inspect purges
|
|
148
|
+
module.featureFlags // Fake feature flag service
|
|
149
|
+
module.sentEmails // Emails sent during the test
|
|
150
|
+
|
|
123
151
|
// Execute code in a request-scoped container
|
|
124
|
-
await module.runInRequestScope(async () => {
|
|
125
|
-
const scoped =
|
|
152
|
+
await module.runInRequestScope(async (container) => {
|
|
153
|
+
const scoped = container.resolve(REQUEST_SCOPED_TOKEN)
|
|
126
154
|
})
|
|
127
155
|
|
|
128
156
|
// Cleanup in afterAll
|
|
@@ -146,6 +174,14 @@ const response = await module.http
|
|
|
146
174
|
|
|
147
175
|
All HTTP methods are supported: `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`.
|
|
148
176
|
|
|
177
|
+
Requests can also act as a user, force JSON, or carry a locale:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
await module.http.get('/api/v1/profile').actingAs({ id: user.id }).send()
|
|
181
|
+
await module.http.get('/api/v1/notes').asJson().send()
|
|
182
|
+
await module.http.get('/api/v1/notes').withLocale('fr').send()
|
|
183
|
+
```
|
|
184
|
+
|
|
149
185
|
### Response assertions
|
|
150
186
|
|
|
151
187
|
```typescript
|
|
@@ -218,59 +254,60 @@ const module = await Test.createTestingModule({
|
|
|
218
254
|
|
|
219
255
|
## Fetch Mocking
|
|
220
256
|
|
|
221
|
-
Mock external HTTP calls with `
|
|
257
|
+
Mock external HTTP calls with `createMockFetch()`, backed by [Mock Service Worker](https://mswjs.io). `http` and `HttpResponse` are re-exported for convenience.
|
|
222
258
|
|
|
223
259
|
```typescript
|
|
224
|
-
import {
|
|
260
|
+
import { createMockFetch, http, HttpResponse, type MockFetch } from '@stratal/testing'
|
|
225
261
|
|
|
226
262
|
describe('GeoService', () => {
|
|
227
|
-
let
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
263
|
+
let mockFetch: MockFetch
|
|
264
|
+
|
|
265
|
+
beforeAll(() => {
|
|
266
|
+
mockFetch = createMockFetch([
|
|
267
|
+
http.get('https://geo.api.com/lookup', () => {
|
|
268
|
+
return HttpResponse.json({ lat: 40.7128, lng: -74.006 })
|
|
269
|
+
}),
|
|
270
|
+
])
|
|
271
|
+
mockFetch.listen()
|
|
234
272
|
})
|
|
235
273
|
|
|
236
|
-
afterEach(() =>
|
|
237
|
-
|
|
238
|
-
})
|
|
274
|
+
afterEach(() => mockFetch.reset())
|
|
275
|
+
afterAll(() => mockFetch.close())
|
|
239
276
|
|
|
240
277
|
it('looks up coordinates', async () => {
|
|
241
|
-
fetchMock.mockJsonResponse('https://geo.api.com/lookup', {
|
|
242
|
-
lat: 40.7128,
|
|
243
|
-
lng: -74.006,
|
|
244
|
-
})
|
|
245
|
-
|
|
246
278
|
const response = await module.http
|
|
247
279
|
.get('/api/geo/lookup?address=NYC')
|
|
248
280
|
.send()
|
|
249
281
|
|
|
250
282
|
response.assertOk()
|
|
251
283
|
await response.assertJsonPath('data.lat', 40.7128)
|
|
252
|
-
fetchMock.assertNoPendingInterceptors()
|
|
253
284
|
})
|
|
285
|
+
})
|
|
286
|
+
```
|
|
254
287
|
|
|
255
|
-
|
|
256
|
-
fetchMock.mockError('https://geo.api.com/lookup', 503, 'Service Unavailable')
|
|
288
|
+
### Lifecycle
|
|
257
289
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
290
|
+
```typescript
|
|
291
|
+
mockFetch.listen() // start intercepting
|
|
292
|
+
mockFetch.reset() // clear runtime handlers, between tests
|
|
293
|
+
mockFetch.close() // stop intercepting
|
|
294
|
+
```
|
|
261
295
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
296
|
+
### Adding handlers for a single test
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
mockFetch.use(
|
|
300
|
+
http.post('https://geo.api.com/submit', () => {
|
|
301
|
+
return HttpResponse.json({ success: true }, { status: 201 })
|
|
302
|
+
}),
|
|
303
|
+
)
|
|
265
304
|
```
|
|
266
305
|
|
|
267
|
-
|
|
306
|
+
### Shorthands
|
|
268
307
|
|
|
269
308
|
```typescript
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
.intercept({ path: '/users', method: 'POST' })
|
|
273
|
-
.reply(201, { id: '1' })
|
|
309
|
+
mockFetch.mockJsonResponse('https://geo.api.com/lookup', { lat: 40.7128 })
|
|
310
|
+
mockFetch.mockError('https://geo.api.com/lookup', 503, 'Service Unavailable')
|
|
274
311
|
```
|
|
275
312
|
|
|
276
313
|
## Storage Testing
|
|
@@ -300,6 +337,59 @@ afterEach(() => {
|
|
|
300
337
|
})
|
|
301
338
|
```
|
|
302
339
|
|
|
340
|
+
## WebSocket Testing
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
const ws = await module.ws('/ws/chat')
|
|
344
|
+
.actingAs({ id: user.id })
|
|
345
|
+
.connect()
|
|
346
|
+
|
|
347
|
+
ws.send('hello')
|
|
348
|
+
await ws.assertMessage('echo:hello')
|
|
349
|
+
|
|
350
|
+
ws.close()
|
|
351
|
+
await ws.waitForClose()
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## SSE Testing
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const sse = await module.sse('/streaming/events')
|
|
358
|
+
.actingAs({ id: user.id })
|
|
359
|
+
.connect()
|
|
360
|
+
|
|
361
|
+
await sse.assertEvent({ event: 'message', data: 'hello' })
|
|
362
|
+
await sse.assertJsonEventData({ status: 'done' })
|
|
363
|
+
await sse.waitForEnd()
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Command Testing
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const result = await module
|
|
370
|
+
.quarry('users:create')
|
|
371
|
+
.withInput({ email: 'test@example.com', admin: true })
|
|
372
|
+
.run()
|
|
373
|
+
|
|
374
|
+
result.assertSuccessful()
|
|
375
|
+
result.assertOutputContains('User created')
|
|
376
|
+
result.assertExitCode(0)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Response Cache
|
|
380
|
+
|
|
381
|
+
A `ctx.cache` stub is installed by default, so `@Cacheable` / `@PurgesCache` routes are testable with no configuration.
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
const response = await module.http.get('/blog/hello-world').send()
|
|
385
|
+
response.assertHeader('Cache-Control', 'public, max-age=300')
|
|
386
|
+
|
|
387
|
+
await module.http.post('/posts/hello-world/publish').send()
|
|
388
|
+
expect(module.cache.purges).toEqual([{ tags: ['post:hello-world'] }])
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Pass `cache: false` to `Test.createTestingModule()` to reproduce a runtime where Workers Caching is genuinely unconfigured.
|
|
392
|
+
|
|
303
393
|
## Deep Mocking
|
|
304
394
|
|
|
305
395
|
Create deeply-mocked instances of any interface or class with `createMock()` from [`@golevelup/ts-vitest`](https://github.com/golevelup/nestjs/tree/master/packages/ts-vitest):
|
|
@@ -318,10 +408,21 @@ beforeEach(() => {
|
|
|
318
408
|
## Sub-path Exports
|
|
319
409
|
|
|
320
410
|
```typescript
|
|
321
|
-
import { Test, TestingModule,
|
|
411
|
+
import { Test, TestingModule, createMockFetch, http, HttpResponse } from '@stratal/testing'
|
|
322
412
|
import { createMock, type DeepMocked } from '@stratal/testing/mocks'
|
|
413
|
+
import { stratalTest, fixPgCjs, fixNobleHashesCjs } from '@stratal/testing/vitest-plugin'
|
|
414
|
+
import { FakeStorageService } from '@stratal/testing/storage'
|
|
415
|
+
import { FakeFeatureFlagService } from '@stratal/testing/feature-flags'
|
|
323
416
|
```
|
|
324
417
|
|
|
418
|
+
## Support the project
|
|
419
|
+
|
|
420
|
+
If Stratal is useful to you, **[star the repository](https://github.com/strataljs/stratal)** — it is the simplest way to help others find it.
|
|
421
|
+
|
|
422
|
+
## Maintainer
|
|
423
|
+
|
|
424
|
+
Built and maintained by **Temitayo Fadojutimi** — [@adesege_](https://x.com/adesege_).
|
|
425
|
+
|
|
325
426
|
## License
|
|
326
427
|
|
|
327
428
|
MIT
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { BINDING_ENV_VAR, DEFAULT_DB_BINDING, type
|
|
1
|
+
import { a as BINDING_ENV_VAR, c as WorkerDatabaseLease, d as createTestDatabaseGlobalSetup, f as databasePrefix, g as leaseWorkerDatabase, h as deriveWorkerDbName, i as resetWorkerDatabase, l as buildConnectionString, m as deriveTemplateName, n as buildTableDiscoverySql, o as DEFAULT_DB_BINDING, p as deriveAdminConnectionString, r as buildTruncateSql, s as TestDatabaseGlobalSetupOptions, t as ResetOptions, u as cloneWorkerDatabase } from "../index-B8Mw1Dxk.mjs";
|
|
2
|
+
export { BINDING_ENV_VAR, DEFAULT_DB_BINDING, type ResetOptions, type TestDatabaseGlobalSetupOptions, type WorkerDatabaseLease, buildConnectionString, buildTableDiscoverySql, buildTruncateSql, cloneWorkerDatabase, createTestDatabaseGlobalSetup, databasePrefix, deriveAdminConnectionString, deriveTemplateName, deriveWorkerDbName, leaseWorkerDatabase, resetWorkerDatabase };
|
package/dist/database/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { BINDING_ENV_VAR, DEFAULT_DB_BINDING,
|
|
1
|
+
import { a as DEFAULT_DB_BINDING, c as createTestDatabaseGlobalSetup, d as deriveTemplateName, f as deriveWorkerDbName, i as BINDING_ENV_VAR, l as databasePrefix, n as buildTruncateSql, o as buildConnectionString, p as leaseWorkerDatabase, r as resetWorkerDatabase, s as cloneWorkerDatabase, t as buildTableDiscoverySql, u as deriveAdminConnectionString } from "../database-Rojs69r3.mjs";
|
|
2
|
+
export { BINDING_ENV_VAR, DEFAULT_DB_BINDING, buildConnectionString, buildTableDiscoverySql, buildTruncateSql, cloneWorkerDatabase, createTestDatabaseGlobalSetup, databasePrefix, deriveAdminConnectionString, deriveTemplateName, deriveWorkerDbName, leaseWorkerDatabase, resetWorkerDatabase };
|