@dotpress/testing 2.0.0-alpha.1 → 2.0.0-alpha.3

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.
Files changed (2) hide show
  1. package/README.md +18 -91
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -1,108 +1,35 @@
1
1
  # @dotpress/testing
2
2
 
3
- Testing helpers for [Dotpress](https://dotpress.dev): a test app, the adapter conformance suite, and assertions for events and mail.
3
+ Testing helpers for [Dotpress](https://dotpress.dev): a test app in memory, the adapter conformance suite, simulated AWS Lambda events, a memory logger, and assertions for events and emails.
4
4
 
5
5
  > 🚧 Dotpress V2 is in alpha (`2.0.0-alpha`): the API may still change before 2.0.0.
6
6
 
7
- ## Adapter conformance suite
8
-
9
- Every Dotpress HTTP adapter must pass the conformance suite, which checks that routing, request parsing, validation, errors, middlewares and responses (including streams and Server-Sent Events) behave the same on every server.
10
-
11
- ```ts
12
- // conformance.test.ts (run with vitest)
13
- import type { AddressInfo } from 'node:net'
14
- import { createApp } from '@dotpress/core'
15
- import { createHttpTarget, runConformanceSuite } from '@dotpress/testing'
16
- import { myAdapter } from '../src/index.js'
17
-
18
- runConformanceSuite({
19
- name: 'my-adapter',
20
- async setup(options) {
21
- const adapter = myAdapter()
22
- const app = await createApp({ ...options, adapter })
23
- await app.listen(0, '127.0.0.1')
24
-
25
- const { port } = adapter.server().address() as AddressInfo
26
- return createHttpTarget(`http://127.0.0.1:${port}`, () => app.close())
27
- },
28
- })
7
+ ```sh
8
+ npm install --save-dev @dotpress/testing
29
9
  ```
30
10
 
31
- `setup()` is called once per test with the app options of the scenario. Adapters that do not listen on a port return `createFetchTarget(handle, close)`, where `handle` takes a Web `Request` and returns a `Response` (in-memory `fetch()`).
32
-
33
- ### AWS Lambda
34
-
35
- `createLambdaTarget(handler, { format, close })` sends each request to a Lambda handler as a simulated event, and converts the result back: `format` is `'v1'` (API Gateway REST API), `'v2'` (HTTP API, Function URLs) or `'alb'` (Application Load Balancer, with multi-value headers). A failed invocation gets a `502` response, like API Gateway. `toLambdaEvent()` and `fromLambdaResult()` are exported for handler tests.
36
-
37
11
  ```ts
38
- runConformanceSuite({
39
- name: 'my-adapter (AWS Lambda)',
40
- // Responses sent once complete: skips the scenarios that need incremental
41
- // streaming or client disconnects.
42
- bufferedResponses: true,
43
- setup: async (options) => {
44
- let app
45
- const handler = createLambdaHandler(async () => (app = await createApp({ ...options, adapter: myAdapter() })))
46
- return createLambdaTarget(handler, { format: 'v2', close: async () => app?.close() })
47
- },
48
- })
49
- ```
50
-
51
- ## Memory logger
12
+ import { createTestApp } from '@dotpress/testing'
13
+ import { expect, it } from 'vitest'
52
14
 
53
- `createMemoryLogger()` records log entries instead of printing them:
54
-
55
- ```ts
56
- const logger = createMemoryLogger()
57
- await createApp({ adapter, logger })
58
- expect(logger.entries).toContainEqual({ level: 'error', args: [error, 'Unhandled error while processing request'] })
59
- ```
15
+ it('creates a task', async () => {
16
+ const api = await createTestApp({ routers: [tasksRouter] })
60
17
 
61
- ## Event assertions
18
+ const res = await api.fetch('/tasks', {
19
+ method: 'POST',
20
+ headers: { 'content-type': 'application/json' },
21
+ body: JSON.stringify({ title: 'Write the tests' }),
22
+ })
62
23
 
63
- `expectEventEmitted(source, event, payload?, options?)` asserts that an event was emitted, and returns the last matching emission. The source is a fake event bus (`createFakeEventBus()` of `@dotpress/events`: it checks emissions like a real bus, without running listeners), or a memory transport (`createMemoryTransport()`: it only sees the events that have listeners on it).
64
-
65
- ```ts
66
- import { createFakeEventBus } from '@dotpress/events'
67
- import { expectEventEmitted, expectEventNotEmitted } from '@dotpress/testing'
68
-
69
- const events = createFakeEventBus()
70
- await signUp(events, 'ada@example.com')
71
-
72
- expectEventEmitted(events, userCreated, { email: 'ada@example.com' })
73
- expectEventEmitted(events, 'user.created', undefined, { times: 1, metadata: { requestId: 'req-1' } })
74
- expectEventNotEmitted(events, userDeleted)
75
- ```
76
-
77
- - `event`: the definition, or its name.
78
- - `payload`: objects are matched partially (like `toMatchObject()`), other values exactly; asymmetric matchers are accepted (`expect.any(String)`). Without it, any payload matches.
79
- - `options`: `times` (exact number of emissions; at least one by default), `metadata` (matched partially), `delay` (fake event bus only).
80
-
81
- With the Dotpress plugin, pass the fake bus to `eventsPlugin()`: `ctx.events` records into it, with the `requestId` in the metadata.
82
-
83
- ## Mail assertions
84
-
85
- `expectMailSent(outbox, criteria?, options?)` asserts that a message was sent through the memory provider of the mailer (`createMemoryProvider()` of `@dotpress/mailer`), and returns the last matching message.
86
-
87
- ```ts
88
- import { createMailer, createMemoryProvider } from '@dotpress/mailer'
89
- import { expectMailSent, expectNoMailSent } from '@dotpress/testing'
90
-
91
- const outbox = createMemoryProvider()
92
- const mailer = createMailer({ provider: outbox, from: 'noreply@example.com' })
93
- await signUp(mailer, 'ada@example.com')
94
-
95
- const mail = expectMailSent(outbox, { to: 'ada@example.com', template: welcomeMail, locale: 'en' })
96
- expect(mail.html).toContain('https://example.com/confirm')
97
- expectNoMailSent(outbox, { to: 'grace@example.com' })
24
+ expect(res.status).toBe(201)
25
+ await api.close()
26
+ })
98
27
  ```
99
28
 
100
- - `to`, `cc`, `bcc`, `replyTo`: an address or an array of addresses, all among the recipients (any case). `from`: the address of the sender.
101
- - `template`: the template, or its name.
102
- - `subject`, `html`, `text`, `locale`, `tags`, `metadata`, `headers`, `attachments`: matched like `toMatchObject()`, with asymmetric matchers (`expect.stringContaining('invoice')`).
103
- - `options.times`: exact number of messages; at least one by default.
29
+ ## Documentation
104
30
 
105
- Failed assertions throw an `ExpectationError` that lists what was emitted or sent.
31
+ - [Testing routes](https://dotpress.dev/routing/testing): the test app, authenticated requests, a real server
32
+ - [Testing helpers](https://dotpress.dev/services/testing): events, emails, logs, AWS Lambda, the conformance suite for adapters
106
33
 
107
34
  ## License
108
35
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotpress/testing",
3
- "version": "2.0.0-alpha.1",
3
+ "version": "2.0.0-alpha.3",
4
4
  "description": "Testing helpers for Dotpress: test app, conformance suite, event and mail assertions",
5
5
  "author": "Renaud Guyon",
6
6
  "license": "MIT",
@@ -30,11 +30,11 @@
30
30
  "node": ">=22.12"
31
31
  },
32
32
  "peerDependencies": {
33
- "@dotpress/core": "^2.0.0-alpha.1",
33
+ "@dotpress/core": "^2.0.0-alpha.3",
34
34
  "vitest": "^5.0.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@dotpress/core": "2.0.0-alpha.1",
37
+ "@dotpress/core": "2.0.0-alpha.3",
38
38
  "zod": "^4.6.7"
39
39
  },
40
40
  "keywords": [