@hellaweb3/wiremock-testcontainers-nodejs 0.2.1 → 0.4.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 +148 -347
- package/dist/index.cjs +1 -194
- package/dist/index.d.cts +170 -61
- package/dist/index.d.ts +170 -61
- package/dist/index.js +1 -192
- package/package.json +31 -12
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/src/index.ts +0 -210
- package/src/sources.ts +0 -143
package/README.md
CHANGED
|
@@ -1,405 +1,206 @@
|
|
|
1
|
-
|
|
2
|
-
<div align="center">
|
|
1
|
+
# WireMock Testcontainers for Node.js
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
[](https://github.com/hella-web3/wiremock-testcontainers-nodejs/actions/workflows/main.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@hellaweb3/wiremock-testcontainers-nodejs)
|
|
5
|
+
[](https://www.npmjs.com/package/@hellaweb3/wiremock-testcontainers-nodejs)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
Run [WireMock](https://wiremock.org/) in [Testcontainers](https://node.testcontainers.org/) with a
|
|
8
|
+
fluent TypeScript API modelled on the official
|
|
9
|
+
[Java module](https://github.com/wiremock/wiremock-testcontainers-java): load stubs from
|
|
10
|
+
directories, files, npm packages or inline JSON; reset state between tests without restarting;
|
|
11
|
+
verify calls through WireMock's request journal.
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
<img alt="Build Status" src="https://github.com/hella-web3/wiremock-testcontainers-nodejs/actions/workflows/main.yml/badge.svg" />
|
|
10
|
-
</a>
|
|
11
|
-
|
|
12
|
-
<a href="https://www.npmjs.com/package/@hellaweb3/wiremock-testcontainers-nodejs">
|
|
13
|
-
<img alt="npm version" src="https://img.shields.io/npm/v/%40hellaweb3%2Fwiremock-testcontainers-nodejs" />
|
|
14
|
-
</a>
|
|
15
|
-
|
|
16
|
-
<a href="https://www.npmjs.com/package/@hellaweb3/wiremock-testcontainers-nodejs">
|
|
17
|
-
<img alt="npm downloads" src="https://img.shields.io/npm/dm/%40hellaweb3%2Fwiremock-testcontainers-nodejs" />
|
|
18
|
-
</a>
|
|
19
|
-
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
|
-
## Description
|
|
23
|
-
|
|
24
|
-
`wiremock-testcontainers-nodejs` is a Typescript nodejs library that spins up
|
|
25
|
-
a [WireMock](https://wiremock.org/)
|
|
26
|
-
instance with [Testcontainers](https://node.testcontainers.org/). It provides a
|
|
27
|
-
fluent API to configure and start WireMock
|
|
28
|
-
containers from recorded WireMock directories, individual named mappings
|
|
29
|
-
(local files, npm packages, module-relative files or inline JSON) and static
|
|
30
|
-
files – an API modelled on the official
|
|
31
|
-
[Java module](https://github.com/wiremock/wiremock-testcontainers-java).
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
13
|
+
## Install
|
|
34
14
|
|
|
35
15
|
```bash
|
|
36
|
-
|
|
16
|
+
npm i -D @hellaweb3/wiremock-testcontainers-nodejs testcontainers
|
|
37
17
|
```
|
|
38
18
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
Node.js >= 22.22 and a [container runtime](https://node.testcontainers.org/supported-container-runtimes/).
|
|
20
|
+
ESM and CJS, fully typed, `testcontainers` is the only dependency. pnpm >= 10 may report
|
|
21
|
+
`Ignored build scripts: ssh2, …` – optional native bindings of `testcontainers`, safe to ignore.
|
|
42
22
|
|
|
43
|
-
|
|
44
|
-
(`dist/index.cjs`) builds with matching declarations, `sideEffects: false`
|
|
45
|
-
for tree-shaking, `testcontainers` as the single runtime dependency (never
|
|
46
|
-
bundled), and the TypeScript source for go-to-definition.
|
|
23
|
+
## Quick start
|
|
47
24
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
back to pure JavaScript. It is safe to leave them ignored.
|
|
25
|
+
```typescript
|
|
26
|
+
import { afterAll, afterEach, beforeAll, expect, it } from "vitest"; // or Jest
|
|
27
|
+
import { type StartedWiremockContainer, WiremockContainer } from "@hellaweb3/wiremock-testcontainers-nodejs";
|
|
52
28
|
|
|
53
|
-
|
|
29
|
+
let wiremock: StartedWiremockContainer;
|
|
54
30
|
|
|
55
|
-
|
|
31
|
+
beforeAll(async () => {
|
|
32
|
+
wiremock = await new WiremockContainer()
|
|
33
|
+
.withMappings("./test/__mocks__/wiremock") // mappings/ + __files/
|
|
34
|
+
.start();
|
|
35
|
+
await wiremock.waitUntilReady();
|
|
36
|
+
}, 60_000); // the first image pull can be slow
|
|
56
37
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
`test/__mocks__/wiremock/__files/*.json`.
|
|
60
|
-
2. Start a `WiremockContainer` once per suite, point it at that directory and
|
|
61
|
-
use `rpcUrl` as the base URL of the API under test.
|
|
62
|
-
3. Stop the container when the suite finishes.
|
|
38
|
+
afterEach(() => wiremock.reset()); // pristine stubs, empty journal – no restart
|
|
39
|
+
afterAll(() => wiremock.stop());
|
|
63
40
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} from "@hellaweb3/wiremock-testcontainers-nodejs";
|
|
70
|
-
|
|
71
|
-
describe("Cards API", () => {
|
|
72
|
-
let container: StartedWiremockContainer;
|
|
73
|
-
|
|
74
|
-
beforeAll(async () => {
|
|
75
|
-
container = await new WiremockContainer() // wiremock/wiremock:latest
|
|
76
|
-
.withMappings("./test/__mocks__/wiremock") // mappings/ + __files/
|
|
77
|
-
.start(); // waits for port 8080
|
|
78
|
-
}, 60_000); // image pull can be slow
|
|
79
|
-
|
|
80
|
-
afterAll(async () => {
|
|
81
|
-
await container.stop();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("serves the recorded GET /cards stub", async () => {
|
|
85
|
-
const response = await fetch(`${container.rpcUrl}/cards`);
|
|
86
|
-
expect(response.status).toBe(200);
|
|
87
|
-
|
|
88
|
-
const body = await response.json();
|
|
89
|
-
expect(body.items).toHaveLength(6);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("exposes the WireMock admin API", async () => {
|
|
93
|
-
const response = await fetch(`${container.rpcUrl}/__admin/mappings`);
|
|
94
|
-
const { meta } = await response.json();
|
|
95
|
-
expect(meta.total).toBe(2);
|
|
96
|
-
});
|
|
41
|
+
it("serves GET /cards", async () => {
|
|
42
|
+
const response = await fetch(wiremock.getUrl("/cards"));
|
|
43
|
+
|
|
44
|
+
expect(response.status).toBe(200);
|
|
45
|
+
expect(await wiremock.countRequests({ method: "GET", url: "/cards" })).toBe(1);
|
|
97
46
|
});
|
|
98
47
|
```
|
|
99
48
|
|
|
100
|
-
|
|
101
|
-
builder method is available – for example to pin an image, name the container
|
|
102
|
-
or pass WireMock CLI flags:
|
|
49
|
+
## Registering stubs
|
|
103
50
|
|
|
104
51
|
```typescript
|
|
105
|
-
|
|
106
|
-
.
|
|
107
|
-
.
|
|
108
|
-
.
|
|
109
|
-
.
|
|
52
|
+
new WiremockContainer("wiremock/wiremock:3.13.0") // default: wiremock/wiremock
|
|
53
|
+
.withMappings("./test/__mocks__/wiremock") // a recorded directory
|
|
54
|
+
.withMapping("health", "./fixtures/health.json")
|
|
55
|
+
.withMapping("ping", { json: { request: { url: "/ping" }, response: { status: 200, body: "pong" } } })
|
|
56
|
+
.withFiles("./fixtures/body.json", { file: "./logo.png", name: "img/logo.png" })
|
|
57
|
+
.withCommand(["--global-response-templating"]); // any Testcontainers builder method
|
|
110
58
|
```
|
|
111
59
|
|
|
112
|
-
|
|
60
|
+
- **`withMappings(dir)`** copies `dir/mappings/` (required) and `dir/__files/` (optional).
|
|
61
|
+
- **`withMapping(name, source)`** registers one stub at `mappings/<name>.json`; `name` may
|
|
62
|
+
contain `/`. The file may hold one stub or `{ "mappings": [...] }`.
|
|
63
|
+
- **`withFiles(...sources)`** registers response bodies at `__files/<name>`, the base name unless
|
|
64
|
+
`name` is given. All or nothing.
|
|
113
65
|
|
|
114
|
-
|
|
115
|
-
|
|
66
|
+
| Source | Example |
|
|
67
|
+
|------------------------------------|----------------------------------------------------------------|
|
|
68
|
+
| Path (relative to cwd) or `file:` URL | `"./fixtures/a.json"`, `new URL("./a.json", import.meta.url)` |
|
|
69
|
+
| `{ file }` | `{ file: "./fixtures/a.json" }` |
|
|
70
|
+
| `{ package, file }` – any file of an installed package | `{ package: "@acme/fixtures", file: "wiremock/a.json" }` |
|
|
71
|
+
| `{ module, file }` – next to a module | `{ module: import.meta.url, file: "./a.json" }` (CJS: `__dirname`) |
|
|
72
|
+
| `{ json }` – mappings only | an object (serialised) or a string (copied verbatim) |
|
|
116
73
|
|
|
117
|
-
|
|
74
|
+
Registrations are validated before any image is pulled: sources must exist, mappings must be
|
|
75
|
+
WireMock stubs, and nothing may overwrite another registration or reuse a stub `id`.
|
|
118
76
|
|
|
119
|
-
|
|
120
|
-
test/
|
|
121
|
-
└── __mocks__/
|
|
122
|
-
└── wiremock/
|
|
123
|
-
├── mappings/
|
|
124
|
-
│ └── my-mapping.json
|
|
125
|
-
└── __files/
|
|
126
|
-
└── some-data.json
|
|
127
|
-
```
|
|
77
|
+
## Extensions
|
|
128
78
|
|
|
129
|
-
|
|
130
|
-
import { WiremockContainer } from "@hellaweb3/wiremock-testcontainers-nodejs";
|
|
131
|
-
|
|
132
|
-
const container = await new WiremockContainer()
|
|
133
|
-
.withMappings("./test/__mocks__/wiremock")
|
|
134
|
-
.start();
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Individual mappings and files
|
|
138
|
-
|
|
139
|
-
`withMapping(name, source)` registers one stub at `mappings/<name>.json` and
|
|
140
|
-
`withFiles(...files)` registers response bodies in `__files/` – no recorded
|
|
141
|
-
directory required. A mapping file holds one WireMock stub, or several under
|
|
142
|
-
`"mappings"`:
|
|
79
|
+
Load [WireMock extensions](https://wiremock.org/docs/extending-wiremock/) from compiled JARs – no JDK needed:
|
|
143
80
|
|
|
144
|
-
```
|
|
145
|
-
|
|
81
|
+
```typescript
|
|
82
|
+
new WiremockContainer()
|
|
83
|
+
.withExtensionJar("./wiremock/acme.jar") // onto WireMock's class path
|
|
84
|
+
.withExtension("com.acme.UppercaseTransformer") // --extensions com.acme.UppercaseTransformer
|
|
85
|
+
.withMapping("shout", { json: { request: { url: "/shout" }, response: { body: "hi", transformers: ["uppercase"] } } });
|
|
146
86
|
```
|
|
147
87
|
|
|
148
|
-
|
|
88
|
+
| Method | Registers |
|
|
89
|
+
|--------------------------------------------------------|---------------------------------------------------------------------------|
|
|
90
|
+
| `withExtension(ext)`, `withExtensions([...])` | A fully qualified class name, or a [TypeScript extension](#typescript-extensions) |
|
|
91
|
+
| `withExtensionJar(jar)`, `withExtensionJars([...])` | A JAR, or every `*.jar` of a directory – any source above; `{ file, name }` renames |
|
|
92
|
+
| `withExtensionOptions({ scanning, verifyClassNames })` | `scanning: false` ignores JARs' `META-INF/services`; `verifyClassNames: false` skips the class check |
|
|
149
93
|
|
|
150
|
-
|
|
94
|
+
- Class names share one `--extensions` flag, in registration order, without repeats.
|
|
95
|
+
- Validated before start: JARs must be readable `*.jar`s; each class must be in a registered JAR
|
|
96
|
+
or WireMock itself (else: *did you mean …?*); `withCommand()` must not repeat `--extensions` or
|
|
97
|
+
an extension's option – WireMock crashes on repeats. Start-up failures name the extensions.
|
|
98
|
+
- WireMock 3 runs on Java 11+, so JARs must target Java 11. Custom images must keep the official
|
|
99
|
+
`/var/wiremock/extensions` class path.
|
|
151
100
|
|
|
152
|
-
|
|
153
|
-
new WiremockContainer().withMapping("health", "./test/fixtures/health.json"); // → mappings/health.json
|
|
154
|
-
```
|
|
101
|
+
### TypeScript extensions
|
|
155
102
|
|
|
156
|
-
|
|
103
|
+
Bundle class names, JARs and options; register the class or an instance:
|
|
157
104
|
|
|
158
105
|
```typescript
|
|
159
|
-
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**`{ file }`** – a path or URL in object form:
|
|
106
|
+
import type { WireMockExtension } from "@hellaweb3/wiremock-testcontainers-nodejs";
|
|
163
107
|
|
|
164
|
-
|
|
165
|
-
|
|
108
|
+
class Uppercase implements WireMockExtension {
|
|
109
|
+
getName() { return "uppercase"; } // identifies it; implement at least one method below
|
|
110
|
+
getClassNames() { return ["com.acme.UppercaseTransformer"]; }
|
|
111
|
+
getJars() { return [{ module: import.meta.url, file: "./acme.jar" }]; }
|
|
112
|
+
getOptions() { return ["--global-response-templating"]; } // "--flag" or "--option=value"
|
|
113
|
+
}
|
|
114
|
+
new WiremockContainer().withExtension(Uppercase);
|
|
166
115
|
```
|
|
167
116
|
|
|
168
|
-
|
|
169
|
-
fixtures. Resolved like `require()` from the working directory; the file does
|
|
170
|
-
not need to be exported and may not leave the package:
|
|
117
|
+
The [guide for extension authors](./examples/extensions/README.md) has recipes, tests and publishing.
|
|
171
118
|
|
|
172
|
-
|
|
173
|
-
new WiremockContainer().withMapping("cards/list", { package: "@acme/api-fixtures", file: "wiremock/cards.json" }); // → mappings/cards/list.json
|
|
174
|
-
```
|
|
119
|
+
## Test utilities
|
|
175
120
|
|
|
176
|
-
|
|
177
|
-
resource:
|
|
121
|
+
`start()` resolves to a `StartedWiremockContainer`:
|
|
178
122
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
123
|
+
| Member | Purpose |
|
|
124
|
+
|-------------------------------------------|-------------------------------------------------------------------------|
|
|
125
|
+
| `rpcUrl` | Base URL, e.g. `http://localhost:32768` |
|
|
126
|
+
| `getUrl(path?)` | `getUrl("cards?page=1")` → `<rpcUrl>/cards?page=1` |
|
|
127
|
+
| `getAdminUrl()` / `getMappingUrl()` | `<rpcUrl>/__admin` / `<rpcUrl>/__admin/mappings` (runtime stubs) |
|
|
128
|
+
| `isRunning()` | `true` if WireMock is healthy; never throws |
|
|
129
|
+
| `waitUntilReady({ timeout?, interval? })` | Polls until healthy (defaults: 30 s, every 250 ms) |
|
|
130
|
+
| `reset()` | Restore registered stubs and scenarios, clear the journal – no restart |
|
|
131
|
+
| `resetRequests()` | Clear the journal only |
|
|
132
|
+
| `countRequests(criteria)` | Number of matching requests since the last reset |
|
|
133
|
+
| `findRequests(criteria)` | The matching `RecordedRequest`s (url, method, headers, body, …) |
|
|
134
|
+
| `stop()`, `await using` | Stop the container |
|
|
183
135
|
|
|
184
|
-
|
|
136
|
+
`criteria` (`WiremockRequestCriteria`) uses WireMock's [request matching](https://wiremock.org/docs/request-matching/):
|
|
185
137
|
|
|
186
138
|
```typescript
|
|
187
|
-
|
|
188
|
-
|
|
139
|
+
const [request] = await wiremock.findRequests({
|
|
140
|
+
method: "POST",
|
|
141
|
+
urlPath: "/cards",
|
|
142
|
+
headers: { "Content-Type": { equalTo: "application/json" } },
|
|
189
143
|
});
|
|
144
|
+
expect(JSON.parse(request.body ?? "")).toMatchObject({ cardType: "virtual" });
|
|
190
145
|
```
|
|
191
146
|
|
|
192
|
-
|
|
147
|
+
Also exported: `WaitUntilReadyOptions`, the source types `WiremockMappingSource`,
|
|
148
|
+
`WiremockStaticFile`, `WiremockFileSource`, `WiremockPath` and `WiremockStubMapping`, and the
|
|
149
|
+
extension types `WireMockExtension`, `ExtensionConstructor`, `ExtensionInput`,
|
|
150
|
+
`WiremockExtensionJar` and `WiremockExtensionOptions`.
|
|
193
151
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
**Several stubs in one mapping** – any source may hold `{ "mappings": [...] }`:
|
|
152
|
+
**Tips:** start one container per suite and `reset()` between tests, which is far faster than
|
|
153
|
+
restarting; raise the `beforeAll` timeout for the first image pull; assert with
|
|
154
|
+
`countRequests()`/`findRequests()` – they verify what actually went over the wire.
|
|
199
155
|
|
|
200
|
-
|
|
201
|
-
new WiremockContainer().withMapping("greetings", {
|
|
202
|
-
json: {
|
|
203
|
-
mappings: [
|
|
204
|
-
{ request: { url: "/hello" }, response: { status: 200, body: "Hello" } },
|
|
205
|
-
{ request: { url: "/bye" }, response: { status: 200, body: "Bye" } },
|
|
206
|
-
],
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
```
|
|
156
|
+
## Without Docker (local development only)
|
|
210
157
|
|
|
211
|
-
|
|
158
|
+
No container runtime? With Java 11+, `@hellaweb3/wiremock-testcontainers-nodejs/no-docker`
|
|
159
|
+
serves every `WiremockContainer` from a local WireMock JVM. Tests stay unchanged: stubs, files,
|
|
160
|
+
extensions and `withCommand()` flags apply as in the container.
|
|
212
161
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
`
|
|
162
|
+
> [!WARNING]
|
|
163
|
+
> Never the default, never for CI/CD: it refuses to run when CI is detected (`CI`,
|
|
164
|
+
> `GITHUB_ACTIONS`, …) and warns on every start. Results can differ from the Docker image; run
|
|
165
|
+
> with Docker before merging.
|
|
216
166
|
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
"./test/fixtures/body-cards.json", // → __files/body-cards.json
|
|
220
|
-
{ file: "./test/fixtures/logo.png", name: "images/logo.png" }, // → __files/images/logo.png
|
|
221
|
-
{ package: "@acme/api-fixtures", file: "wiremock/__files/user.json" }, // → __files/user.json
|
|
222
|
-
);
|
|
167
|
+
```bash
|
|
168
|
+
npm i -D wiremock # bundles the WireMock JAR (optional peer dependency)
|
|
223
169
|
```
|
|
224
170
|
|
|
225
|
-
#### Combining them
|
|
226
|
-
|
|
227
|
-
All three methods chain with each other and with every Testcontainers builder:
|
|
228
|
-
|
|
229
171
|
```typescript
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
172
|
+
// test/no-docker.setup.ts
|
|
173
|
+
import { installNoDockerRuntime } from "@hellaweb3/wiremock-testcontainers-nodejs/no-docker";
|
|
174
|
+
installNoDockerRuntime();
|
|
175
|
+
|
|
176
|
+
// vitest.config.ts – opt-in only
|
|
177
|
+
setupFiles: process.env.WIREMOCK_NO_DOCKER === "1" ? ["./test/no-docker.setup.ts"] : [],
|
|
236
178
|
```
|
|
237
179
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
Registering the same file at the same path again is a no-op. Sources are only
|
|
242
|
-
read – mappings are copied byte-for-byte as of registration.
|
|
243
|
-
|
|
244
|
-
## API
|
|
245
|
-
|
|
246
|
-
### `WiremockContainer`
|
|
247
|
-
|
|
248
|
-
- `constructor(image?: string)`: Create a new container. Defaults to
|
|
249
|
-
`wiremock/wiremock`.
|
|
250
|
-
- `withMappings(directory: string)`: Copies `mappings/` (required) and
|
|
251
|
-
`__files/` (optional) from the specified local directory into the container.
|
|
252
|
-
Throws immediately if `mappings/` does not exist, so a typo fails before any
|
|
253
|
-
image is pulled.
|
|
254
|
-
- `withMapping(name: string, source: WiremockMappingSource)`: Registers one stub
|
|
255
|
-
at `mappings/<name>.json` (`name` may contain `/`; `.json` is optional).
|
|
256
|
-
- `withFiles(...files: WiremockStaticFile[])`: Registers files at
|
|
257
|
-
`__files/<name>` – the base name unless an object source sets `name`. A call
|
|
258
|
-
registers all files or none.
|
|
259
|
-
- `start()`: Starts the container and returns a `StartedWiremockContainer`.
|
|
260
|
-
Runtime failures (no daemon, image pull denied, start-up timeout, name or
|
|
261
|
-
port conflicts) are propagated unchanged.
|
|
262
|
-
|
|
263
|
-
### `StartedWiremockContainer`
|
|
264
|
-
|
|
265
|
-
- `rpcUrl`: Returns the base URL of the running WireMock instance (e.g.,
|
|
266
|
-
`http://localhost:32768`).
|
|
267
|
-
- `stop()`: Stops the container (inherited from Testcontainers).
|
|
268
|
-
- `[Symbol.asyncDispose]`: `await using container = await new WiremockContainer().start();`
|
|
269
|
-
stops the container automatically when the scope exits (TypeScript 5.2+).
|
|
270
|
-
|
|
271
|
-
## Best Practices
|
|
272
|
-
|
|
273
|
-
1. **Test Isolation**: Start a new container for each test suite (or even each
|
|
274
|
-
test if performance allows) to ensure a clean state.
|
|
275
|
-
2. **Mapping Organization**: Keep your mappings organized in a dedicated
|
|
276
|
-
directory structure that mirrors your API endpoints.
|
|
277
|
-
3. **Timeouts**: Docker container startup can sometimes exceed the default test
|
|
278
|
-
runner timeout (e.g. 5 s in Jest, 10 s in Vitest). Always set a higher timeout for
|
|
279
|
-
`beforeAll` hooks that start containers.
|
|
280
|
-
4. **Resource Management**: Always ensure `container.stop()` is called in an
|
|
281
|
-
`afterAll` or `afterEach` block to prevent orphaned containers.
|
|
282
|
-
|
|
283
|
-
## Development
|
|
284
|
-
|
|
285
|
-
Tooling: [Vitest](https://vitest.dev/) (tests + v8 coverage),
|
|
286
|
-
[oxlint](https://oxc.rs/docs/guide/usage/linter) (lint), `tsc` (typecheck)
|
|
287
|
-
and [tsdown](https://tsdown.dev/) (build – rolldown + oxc, emitting
|
|
288
|
-
declarations natively via `isolatedDeclarations` in `tsconfig.build.json`).
|
|
289
|
-
Node version: see `.nvmrc`.
|
|
290
|
-
|
|
291
|
-
### Scripts
|
|
292
|
-
|
|
293
|
-
| Command | What it does | Needs Docker |
|
|
294
|
-
|----------------------|--------------------------------------------------------------------------|--------------|
|
|
295
|
-
| `pnpm build` | ESM + CJS + declarations into `dist/`, then `publint` + `attw` checks | No |
|
|
296
|
-
| `pnpm typecheck` | `tsc --noEmit` over `src/` and `test/` | No |
|
|
297
|
-
| `pnpm lint` | `oxlint --deny-warnings` with the rules in `.oxlintrc.json` | No |
|
|
298
|
-
| `pnpm test:unit` | `test/unit/**` – container wrapper logic, registration API and fixture integrity checks | No |
|
|
299
|
-
| `pnpm test:e2e` | `test/index.test.ts` – a real WireMock container serving the fixtures | **Yes** |
|
|
300
|
-
| `pnpm test` | Both suites, no coverage (fast local loop) | **Yes** |
|
|
301
|
-
| `pnpm test:coverage` | Both suites with coverage of `src/`, thresholds enforced | **Yes** |
|
|
302
|
-
| `pnpm check` | `build` + `typecheck` + `lint` + `test:coverage` – the full CI gate | **Yes** |
|
|
303
|
-
|
|
304
|
-
Append `--coverage` to any test script to collect coverage for that run, e.g.
|
|
305
|
-
`pnpm test:unit --coverage`. Coverage thresholds are **95 %** for statements,
|
|
306
|
-
branches, functions and lines; the unit suite alone currently reaches 100 %
|
|
307
|
-
and completes in well under two seconds of test time.
|
|
308
|
-
|
|
309
|
-
### Running the E2E suite in containerised environments
|
|
310
|
-
|
|
311
|
-
The E2E suite starts a real container through Testcontainers, so the test
|
|
312
|
-
process needs a container runtime it can talk to. Without one, `pnpm test:e2e`,
|
|
313
|
-
`pnpm test` and `pnpm check` fail in the E2E suite with:
|
|
180
|
+
Set `WIREMOCK_NO_DOCKER=1` from a `--no-docker` script flag – see
|
|
181
|
+
[`examples/scripts/test-e2e.mjs`](./examples/scripts/test-e2e.mjs), which supports
|
|
182
|
+
`npm test --no-docker`.
|
|
314
183
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
184
|
+
| Option (`installNoDockerRuntime`) | Environment | Default |
|
|
185
|
+
|-----------------------------------|------------------------------|------------------------------------|
|
|
186
|
+
| `jar` | `WIREMOCK_JAR` | the `wiremock` package's JAR |
|
|
187
|
+
| `java` | `WIREMOCK_JAVA`, `JAVA_HOME` | `java` |
|
|
188
|
+
| `startupTimeout` | – | `60_000` ms |
|
|
189
|
+
| `cacheDir` (`false` disables) | – | `<tmpdir>/wiremock-testcontainers` |
|
|
190
|
+
| `log` | – | `process.emitWarning` |
|
|
191
|
+
| `env` – for the CI check and defaults | – | `process.env` |
|
|
318
192
|
|
|
319
|
-
|
|
320
|
-
|
|
193
|
+
- **Speed:** a C1-only JIT and a class-data archive per JAR and Java in `cacheDir`. The first run
|
|
194
|
+
records it (a slower `stop()`); later runs boot ~2× faster and stop instantly. `JAVA_OPTS`
|
|
195
|
+
override the JVM flags.
|
|
196
|
+
- **Differences:** WireMock listens on `127.0.0.1`; extension JARs join its class path;
|
|
197
|
+
`exec()`, networks and copying after start throw; leftover JVMs are killed on exit.
|
|
198
|
+
- **Also exported** (from `…/no-docker` and the package root): `detectCI(env?)` – the CI variable
|
|
199
|
+
set, if any; `isNoDockerRequested(env?)` – whether `NO_DOCKER_ENV` (`WIREMOCK_NO_DOCKER`) is `1` or `true`.
|
|
321
200
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
201
|
+
## More
|
|
202
|
+
|
|
203
|
+
- [`examples/`](./examples) – a standalone project using the package via `npm link`.
|
|
204
|
+
- [CONTRIBUTING.md](./CONTRIBUTING.md) – scripts, CI and tests.
|
|
325
205
|
|
|
326
|
-
|
|
327
|
-
environment can provide:
|
|
328
|
-
|
|
329
|
-
| Environment | How to enable E2E |
|
|
330
|
-
|-----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
331
|
-
| GitHub Codespaces / VS Code Dev Containers | Use the provided `.devcontainer/devcontainer.json` – it adds the `docker-in-docker` feature, so a daemon runs inside the dev container. |
|
|
332
|
-
| Dev container with the host socket mounted | Add the `docker-outside-of-docker` feature (or mount `/var/run/docker.sock`). Set `TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal` if port lookups fail. |
|
|
333
|
-
| Remote or rootless daemon | `DOCKER_HOST=tcp://host:2375` (or `unix:///path/to/docker.sock`). |
|
|
334
|
-
| Podman | `DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock` and `TESTCONTAINERS_RYUK_DISABLED=true` (or run Ryuk privileged). |
|
|
335
|
-
| No runtime can be installed (locked-down sandbox) | [Testcontainers Cloud](https://testcontainers.com/cloud/): run the agent and export `TC_CLOUD_TOKEN`; containers start remotely, tests run locally. |
|
|
336
|
-
|
|
337
|
-
Sandboxes that have **no runtime binary and no socket at all** (as is the case
|
|
338
|
-
for some hosted dev VMs) cannot run the E2E suite; the suite states this in its
|
|
339
|
-
header comment and `pnpm test:unit` is the supported loop there.
|
|
340
|
-
|
|
341
|
-
CI (`.github/workflows/main.yml`) runs build, typecheck, lint, unit tests
|
|
342
|
-
(with coverage) and E2E tests as separate steps on `ubuntu-latest`, which
|
|
343
|
-
ships with Docker, for every active Node LTS line.
|
|
344
|
-
|
|
345
|
-
### Supply-chain controls
|
|
346
|
-
|
|
347
|
-
- `pnpm install --frozen-lockfile` everywhere; the lockfile is the source of truth.
|
|
348
|
-
- `pnpm-workspace.yaml` allow-lists the only packages permitted to run install
|
|
349
|
-
scripts (`ssh2`, `cpu-features`, `protobufjs` – all transitive via
|
|
350
|
-
`testcontainers`) and sets `minimumReleaseAge: 1440` so a version must be at
|
|
351
|
-
least 24 h old before it can be resolved.
|
|
352
|
-
- CI runs `pnpm audit --prod --audit-level=high` on every push and PR;
|
|
353
|
-
Dependabot (`.github/dependabot.yml`) proposes weekly updates with a 3-day
|
|
354
|
-
cooldown, grouping dev tooling and leaving `testcontainers` bumps individual.
|
|
355
|
-
- `pnpm build` runs [publint](https://publint.dev/) and
|
|
356
|
-
[Are the types wrong?](https://arethetypeswrong.github.io/) against the
|
|
357
|
-
emitted package; `size.yml` fails a PR if the tarball exceeds 25 kB.
|
|
358
|
-
- Releases publish with npm provenance (`--provenance`, OIDC) after a
|
|
359
|
-
Docker-free verify step.
|
|
360
|
-
|
|
361
|
-
### Fixtures and schema-based assertions
|
|
362
|
-
|
|
363
|
-
The recorded WireMock session lives in `test/__mocks__/wiremock`:
|
|
364
|
-
|
|
365
|
-
- `mappings/*.json` – one stub per endpoint (`request.url`, `response.status`,
|
|
366
|
-
`response.headers`, `response.bodyFileName`).
|
|
367
|
-
- `__files/*.json` – the response bodies referenced by the mappings.
|
|
368
|
-
|
|
369
|
-
`test/helpers/fixtures.ts` loads these from disk and declares the TypeScript
|
|
370
|
-
shapes of every JSON payload. `test/helpers/assertions.ts` declares a
|
|
371
|
-
`Schema<T>` per payload – stub mappings, the admin API response and each
|
|
372
|
-
recorded body. The compiler requires a schema to cover **every** field of
|
|
373
|
-
`T`; `expectObject` then asserts each field's shape, its recorded value and
|
|
374
|
-
the exact key set (`partialOf` relaxes the key set for third-party shapes).
|
|
375
|
-
|
|
376
|
-
The E2E suite runs a mapping-driven contract test for every file in
|
|
377
|
-
`mappings/` (status, headers and full JSON body), so adding a new recorded
|
|
378
|
-
mapping automatically puts it under test. Endpoint-specific suites add
|
|
379
|
-
schema assertions, which the unit suite also runs against the fixture files
|
|
380
|
-
themselves to catch drift early.
|
|
381
|
-
|
|
382
|
-
Negative cases are hand-written stubs alongside the recorded ones:
|
|
383
|
-
|
|
384
|
-
- `mapping-cards-bad-request-400.json` – `GET /cards?page=0` → 400 with a
|
|
385
|
-
validation error envelope.
|
|
386
|
-
- `mapping-account-balance-server-error-500.json` – `GET /account/balance`
|
|
387
|
-
→ 500 **only** when the request carries `X-Mock-Scenario: server-error`.
|
|
388
|
-
It sets `"priority": 1` so it wins over the recorded 200 stub for the same
|
|
389
|
-
URL; without the header the 200 stub is still served. This is the standard
|
|
390
|
-
WireMock pattern for simulating outages on demand.
|
|
391
|
-
|
|
392
|
-
Unmatched requests (unknown path, wrong method) get WireMock's default 404.
|
|
393
|
-
|
|
394
|
-
`test/__mocks__/standalone` holds loose fixtures for `withMapping()` and
|
|
395
|
-
`withFiles()`. Their unit tests need no Docker: `test/helpers/container.ts`
|
|
396
|
-
reads the tar archive Testcontainers would upload and asserts exactly which
|
|
397
|
-
files land where.
|
|
398
|
-
|
|
399
|
-
## Maintainers
|
|
400
|
-
|
|
401
|
-
Maintained by [Hella Labs](https://hellaweb3.com/).
|
|
402
|
-
|
|
403
|
-
## License
|
|
404
|
-
|
|
405
|
-
MIT
|
|
206
|
+
Maintained by [Hella Labs](https://hellaweb3.com/). [MIT](./LICENSE) licensed.
|