@dotpress/testing 0.0.0-stage → 2.0.0-alpha.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/LICENSE +21 -0
- package/README.md +108 -2
- package/dist/index.d.ts +270 -0
- package/dist/index.js +1813 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present Renaud Guyon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,109 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @dotpress/testing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Testing helpers for [Dotpress](https://dotpress.dev): a test app, the adapter conformance suite, and assertions for events and mail.
|
|
4
|
+
|
|
5
|
+
> 🚧 Dotpress V2 is in alpha (`2.0.0-alpha`): the API may still change before 2.0.0.
|
|
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
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
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
|
+
```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
|
|
52
|
+
|
|
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
|
+
```
|
|
60
|
+
|
|
61
|
+
## Event assertions
|
|
62
|
+
|
|
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' })
|
|
98
|
+
```
|
|
99
|
+
|
|
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.
|
|
104
|
+
|
|
105
|
+
Failed assertions throw an `ExpectationError` that lists what was emitted or sent.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { AppOptions, Logger, HttpAdapter, CompiledRoute, App } from '@dotpress/core';
|
|
2
|
+
|
|
3
|
+
/** Error of a failed assertion, shown by the test runner. */
|
|
4
|
+
declare class ExpectationError extends Error {
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Running application under test.
|
|
10
|
+
*/
|
|
11
|
+
interface ConformanceTarget {
|
|
12
|
+
/** Sends a request to the application. `path` includes the query. */
|
|
13
|
+
fetch(path: string, init?: RequestInit): Promise<Response>;
|
|
14
|
+
/** Stops the application. */
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
type ConformanceAppOptions = Omit<AppOptions, 'adapter'>;
|
|
18
|
+
interface ConformanceSuiteOptions {
|
|
19
|
+
/** Adapter name, used in test titles. */
|
|
20
|
+
name: string;
|
|
21
|
+
/**
|
|
22
|
+
* Creates an app with the adapter under test and the given options, and
|
|
23
|
+
* starts it. Called once per test.
|
|
24
|
+
*/
|
|
25
|
+
setup(options: ConformanceAppOptions): Promise<ConformanceTarget>;
|
|
26
|
+
/**
|
|
27
|
+
* The adapter sends responses once complete (AWS Lambda without response
|
|
28
|
+
* streaming). The scenarios that need incremental streaming or client
|
|
29
|
+
* disconnects are skipped.
|
|
30
|
+
*/
|
|
31
|
+
bufferedResponses?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Target for adapters listening on an HTTP port.
|
|
35
|
+
*/
|
|
36
|
+
declare const createHttpTarget: (baseUrl: string, close: () => Promise<void>) => ConformanceTarget;
|
|
37
|
+
/**
|
|
38
|
+
* Target for adapters handling Web requests in memory (`fetch()` of the
|
|
39
|
+
* adapter, serverless handlers). `path` is relative to `http://localhost`.
|
|
40
|
+
*/
|
|
41
|
+
declare const createFetchTarget: (handle: (request: Request) => Promise<Response>, close: () => Promise<void>) => ConformanceTarget;
|
|
42
|
+
/**
|
|
43
|
+
* Registers the adapter conformance suite (vitest). An adapter is ready
|
|
44
|
+
* when it passes every test.
|
|
45
|
+
*/
|
|
46
|
+
declare const runConformanceSuite: (options: ConformanceSuiteOptions) => void;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Lambda event formats: API Gateway REST API (payload v1), HTTP API and
|
|
50
|
+
* Function URLs (payload v2), Application Load Balancer (with multi-value
|
|
51
|
+
* headers, required for repeated query keys and several cookies).
|
|
52
|
+
*/
|
|
53
|
+
type LambdaEventFormat = 'v1' | 'v2' | 'alb';
|
|
54
|
+
interface LambdaTargetOptions {
|
|
55
|
+
format: LambdaEventFormat;
|
|
56
|
+
/** Stops the application. */
|
|
57
|
+
close: () => Promise<void>;
|
|
58
|
+
/** Client IP address of the events. Defaults to `127.0.0.1`. */
|
|
59
|
+
sourceIp?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Lambda handler under test (`createLambdaHandler()` of an adapter). */
|
|
62
|
+
type LambdaTestHandler = (event: never, context: never) => Promise<unknown>;
|
|
63
|
+
/** Creates the Lambda event of a request. */
|
|
64
|
+
declare const toLambdaEvent: (request: Request, format: LambdaEventFormat, sourceIp?: string) => Promise<Record<string, unknown>>;
|
|
65
|
+
/** Converts the result of a Lambda handler into a Web response. */
|
|
66
|
+
declare const fromLambdaResult: (result: unknown) => Response;
|
|
67
|
+
/**
|
|
68
|
+
* Target for AWS Lambda handlers: each request is sent as a simulated
|
|
69
|
+
* event of the given format, and the result converted back. A failed
|
|
70
|
+
* invocation gets a `502` response, like API Gateway.
|
|
71
|
+
*/
|
|
72
|
+
declare const createLambdaTarget: (handler: LambdaTestHandler, options: LambdaTargetOptions) => ConformanceTarget;
|
|
73
|
+
|
|
74
|
+
interface LogEntry {
|
|
75
|
+
readonly level: 'debug' | 'info' | 'warn' | 'error';
|
|
76
|
+
readonly args: readonly unknown[];
|
|
77
|
+
}
|
|
78
|
+
interface MemoryLogger extends Logger {
|
|
79
|
+
/** Entries logged by this logger and its children. */
|
|
80
|
+
readonly entries: LogEntry[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Logger that records entries instead of printing them.
|
|
84
|
+
*/
|
|
85
|
+
declare const createMemoryLogger: () => MemoryLogger;
|
|
86
|
+
|
|
87
|
+
interface MemoryAdapterOptions {
|
|
88
|
+
/** Maximum request body size, in bytes. Defaults to 1 MiB. */
|
|
89
|
+
bodyLimit?: number;
|
|
90
|
+
/** Client IP address given to requests. Defaults to `127.0.0.1`. */
|
|
91
|
+
ip?: string;
|
|
92
|
+
}
|
|
93
|
+
interface MemoryAdapter extends HttpAdapter<{
|
|
94
|
+
readonly routes: readonly CompiledRoute[];
|
|
95
|
+
}> {
|
|
96
|
+
/** Handles a Web `Request` in memory, without opening a port. */
|
|
97
|
+
fetch(request: Request): Promise<Response>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* HTTP adapter running requests in memory, for tests. It follows the
|
|
101
|
+
* adapter contract like any other adapter, and passes the conformance
|
|
102
|
+
* suite.
|
|
103
|
+
*/
|
|
104
|
+
declare const memoryAdapter: (options?: MemoryAdapterOptions) => MemoryAdapter;
|
|
105
|
+
interface TestAppOptions extends Omit<AppOptions, 'adapter' | 'logger'> {
|
|
106
|
+
/** Options of the memory adapter. */
|
|
107
|
+
adapter?: MemoryAdapterOptions;
|
|
108
|
+
/** Defaults to a new memory logger. */
|
|
109
|
+
logger?: MemoryLogger;
|
|
110
|
+
}
|
|
111
|
+
interface TestApp {
|
|
112
|
+
readonly app: App<{
|
|
113
|
+
readonly routes: readonly CompiledRoute[];
|
|
114
|
+
}>;
|
|
115
|
+
readonly adapter: MemoryAdapter;
|
|
116
|
+
/** Logger of the app, recording the log entries. */
|
|
117
|
+
readonly logger: MemoryLogger;
|
|
118
|
+
/**
|
|
119
|
+
* Sends a request to the app, like `fetch()`. `path` is relative to
|
|
120
|
+
* `http://localhost`.
|
|
121
|
+
*/
|
|
122
|
+
fetch(path: string, init?: RequestInit): Promise<Response>;
|
|
123
|
+
close(): Promise<void>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Creates an app on the memory adapter.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* const { fetch } = await createTestApp({ routers: [router], plugins: [authPlugin(auth)] })
|
|
130
|
+
* const res = await fetch('/auth/me', { headers: { authorization: `Bearer ${token}` } })
|
|
131
|
+
* expect(res.status).toBe(200)
|
|
132
|
+
*/
|
|
133
|
+
declare const createTestApp: (options?: TestAppOptions) => Promise<TestApp>;
|
|
134
|
+
|
|
135
|
+
/** An emission, as recorded by `createFakeEventBus()` of `@dotpress/events`. */
|
|
136
|
+
interface Emission {
|
|
137
|
+
readonly emissionId: string;
|
|
138
|
+
readonly event: string;
|
|
139
|
+
readonly payload: unknown;
|
|
140
|
+
readonly delay: number | undefined;
|
|
141
|
+
readonly metadata: Readonly<Record<string, string>>;
|
|
142
|
+
readonly emittedAt: Date;
|
|
143
|
+
}
|
|
144
|
+
/** The fields of an envelope read here (`Envelope` of `@dotpress/events`). */
|
|
145
|
+
interface EnvelopeLike {
|
|
146
|
+
readonly emissionId: string;
|
|
147
|
+
readonly event: string;
|
|
148
|
+
readonly payload: unknown;
|
|
149
|
+
readonly metadata: Readonly<Record<string, string>>;
|
|
150
|
+
readonly emittedAt: string;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Where emissions are read: a fake event bus (`createFakeEventBus()`), or a
|
|
154
|
+
* memory transport (`createMemoryTransport()`), which only sees the events
|
|
155
|
+
* that have listeners on it.
|
|
156
|
+
*/
|
|
157
|
+
type EmissionSource<E extends Emission = Emission> = {
|
|
158
|
+
readonly emitted: readonly E[];
|
|
159
|
+
} | {
|
|
160
|
+
readonly sent: readonly EnvelopeLike[];
|
|
161
|
+
};
|
|
162
|
+
/** Options of `expectEventEmitted()`. */
|
|
163
|
+
interface ExpectEventOptions {
|
|
164
|
+
/** Exact number of matching emissions. Defaults to at least one. */
|
|
165
|
+
times?: number | undefined;
|
|
166
|
+
/** Metadata of the emission, matched partially. */
|
|
167
|
+
metadata?: Readonly<Record<string, unknown>> | undefined;
|
|
168
|
+
/** Delay of the emission, in seconds (fake event bus only). */
|
|
169
|
+
delay?: number | undefined;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Asserts that an event was emitted, at least once or exactly `times`
|
|
173
|
+
* times, with a payload (and metadata) matching the expected values:
|
|
174
|
+
* objects are matched partially, and asymmetric matchers are accepted.
|
|
175
|
+
* Without payload (or with `undefined`), any payload matches.
|
|
176
|
+
* Returns the last matching emission.
|
|
177
|
+
*
|
|
178
|
+
* ```ts
|
|
179
|
+
* const events = createFakeEventBus()
|
|
180
|
+
* await signUp(events, 'ada@example.com')
|
|
181
|
+
* expectEventEmitted(events, userCreated, { email: 'ada@example.com' })
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
declare function expectEventEmitted<E extends Emission>(source: EmissionSource<E>, event: {
|
|
185
|
+
readonly name: string;
|
|
186
|
+
} | string, ...rest: [payload?: unknown, options?: ExpectEventOptions]): E;
|
|
187
|
+
/**
|
|
188
|
+
* Asserts that an event was not emitted (with this payload, if given).
|
|
189
|
+
*
|
|
190
|
+
* ```ts
|
|
191
|
+
* expectEventNotEmitted(events, userCreated)
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare function expectEventNotEmitted(source: EmissionSource, event: {
|
|
195
|
+
readonly name: string;
|
|
196
|
+
} | string, ...rest: [payload?: unknown]): void;
|
|
197
|
+
|
|
198
|
+
interface Address {
|
|
199
|
+
readonly name?: string;
|
|
200
|
+
readonly address: string;
|
|
201
|
+
}
|
|
202
|
+
/** The fields of a message read here (`SentMail` of `@dotpress/mailer`). */
|
|
203
|
+
interface SentMail {
|
|
204
|
+
readonly from: Address;
|
|
205
|
+
readonly to: readonly Address[];
|
|
206
|
+
readonly cc: readonly Address[];
|
|
207
|
+
readonly bcc: readonly Address[];
|
|
208
|
+
readonly replyTo: readonly Address[];
|
|
209
|
+
readonly subject: string;
|
|
210
|
+
readonly template: string | undefined;
|
|
211
|
+
}
|
|
212
|
+
/** Where messages are read: `createMemoryProvider()` of `@dotpress/mailer`. */
|
|
213
|
+
interface MailOutbox<M extends SentMail = SentMail> {
|
|
214
|
+
readonly sent: readonly M[];
|
|
215
|
+
}
|
|
216
|
+
/** Recipients: every expected address must be among them (any case). */
|
|
217
|
+
type ExpectedAddresses = string | readonly string[];
|
|
218
|
+
/**
|
|
219
|
+
* Expected properties of a message. Recipients and `from` are compared by
|
|
220
|
+
* address; the other properties are matched like `toMatchObject()`
|
|
221
|
+
* (asymmetric matchers accepted: `expect.stringContaining()`…).
|
|
222
|
+
*/
|
|
223
|
+
interface MailCriteria {
|
|
224
|
+
to?: ExpectedAddresses | undefined;
|
|
225
|
+
cc?: ExpectedAddresses | undefined;
|
|
226
|
+
bcc?: ExpectedAddresses | undefined;
|
|
227
|
+
replyTo?: ExpectedAddresses | undefined;
|
|
228
|
+
from?: string | undefined;
|
|
229
|
+
/** The template, or its name. */
|
|
230
|
+
template?: string | {
|
|
231
|
+
readonly name: string;
|
|
232
|
+
} | undefined;
|
|
233
|
+
locale?: unknown;
|
|
234
|
+
subject?: unknown;
|
|
235
|
+
html?: unknown;
|
|
236
|
+
text?: unknown;
|
|
237
|
+
tags?: unknown;
|
|
238
|
+
metadata?: unknown;
|
|
239
|
+
headers?: unknown;
|
|
240
|
+
attachments?: unknown;
|
|
241
|
+
}
|
|
242
|
+
/** Options of `expectMailSent()`. */
|
|
243
|
+
interface ExpectMailOptions {
|
|
244
|
+
/** Exact number of matching messages. Defaults to at least one. */
|
|
245
|
+
times?: number | undefined;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Asserts that a message matching the criteria was sent, at least once or
|
|
249
|
+
* exactly `times` times. Returns the last matching message.
|
|
250
|
+
*
|
|
251
|
+
* ```ts
|
|
252
|
+
* const outbox = createMemoryProvider()
|
|
253
|
+
* const mailer = createMailer({ provider: outbox, from: 'noreply@example.com' })
|
|
254
|
+
* await signUp(mailer, 'ada@example.com')
|
|
255
|
+
* const mail = expectMailSent(outbox, { to: 'ada@example.com', template: welcomeMail })
|
|
256
|
+
* expect(mail.html).toContain('https://example.com/confirm')
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
declare const expectMailSent: <M extends SentMail>(outbox: MailOutbox<M>, criteria?: MailCriteria, options?: ExpectMailOptions) => M;
|
|
260
|
+
/**
|
|
261
|
+
* Asserts that no message (matching the criteria, if given) was sent.
|
|
262
|
+
*
|
|
263
|
+
* ```ts
|
|
264
|
+
* expectNoMailSent(outbox, { to: 'ada@example.com' })
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
declare const expectNoMailSent: (outbox: MailOutbox, criteria?: MailCriteria) => void;
|
|
268
|
+
|
|
269
|
+
export { ExpectationError, createFetchTarget, createHttpTarget, createLambdaTarget, createMemoryLogger, createTestApp, expectEventEmitted, expectEventNotEmitted, expectMailSent, expectNoMailSent, fromLambdaResult, memoryAdapter, runConformanceSuite, toLambdaEvent };
|
|
270
|
+
export type { ConformanceAppOptions, ConformanceSuiteOptions, ConformanceTarget, Emission, EmissionSource, ExpectEventOptions, ExpectMailOptions, LambdaEventFormat, LambdaTargetOptions, LambdaTestHandler, LogEntry, MailCriteria, MailOutbox, MemoryAdapter, MemoryAdapterOptions, MemoryLogger, SentMail, TestApp, TestAppOptions };
|