exisjs 0.7.12 → 0.7.14
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 +195 -39
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +3 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/config/types.d.ts +2 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/decorators/params.d.ts +21 -0
- package/dist/decorators/params.d.ts.map +1 -1
- package/dist/decorators/params.js +21 -1
- package/dist/decorators/params.js.map +1 -1
- package/dist/di/container.d.ts +15 -6
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +26 -3
- package/dist/di/container.js.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/index.js.map +1 -1
- package/dist/di/inject.d.ts +2 -1
- package/dist/di/inject.d.ts.map +1 -1
- package/dist/di/inject.js +2 -1
- package/dist/di/inject.js.map +1 -1
- package/dist/docs/ARCHITECTURE.md +130 -78
- package/dist/docs/BENCHMARKS.md +4 -4
- package/dist/docs/content/README.md +43 -44
- package/dist/docs/content/boundaries.mdx +138 -61
- package/dist/docs/content/cli/commands.mdx +151 -128
- package/dist/docs/content/cli/overview.mdx +61 -47
- package/dist/docs/content/cli/usage.mdx +107 -30
- package/dist/docs/content/config.mdx +145 -158
- package/dist/docs/content/controllers.mdx +193 -464
- package/dist/docs/content/cron.mdx +183 -69
- package/dist/docs/content/decorators.mdx +449 -154
- package/dist/docs/content/errors.mdx +202 -305
- package/dist/docs/content/exisjs-mcp.mdx +59 -49
- package/dist/docs/content/installation.mdx +243 -157
- package/dist/docs/content/introduction.mdx +220 -48
- package/dist/docs/content/middleware.mdx +171 -497
- package/dist/docs/content/modules.mdx +141 -83
- package/dist/docs/content/providers.mdx +216 -180
- package/dist/docs/content/requests.mdx +168 -488
- package/dist/docs/content/responses.mdx +200 -533
- package/dist/docs/content/routing.mdx +235 -598
- package/dist/docs/content/security.mdx +233 -57
- package/dist/docs/content/structure.mdx +151 -240
- package/dist/docs/content/swagger.mdx +192 -91
- package/dist/docs/content/testing.mdx +205 -74
- package/dist/docs/content/validation.mdx +479 -341
- package/dist/docs/reference/README.md +34 -2
- package/dist/docs/reference/config.mdx +277 -75
- package/dist/docs/reference/context.mdx +197 -106
- package/dist/docs/reference/cron.mdx +266 -152
- package/dist/docs/reference/database.mdx +135 -53
- package/dist/docs/reference/decorators.mdx +234 -116
- package/dist/docs/reference/di.mdx +146 -112
- package/dist/docs/reference/error.mdx +155 -178
- package/dist/docs/reference/exis.mdx +141 -70
- package/dist/docs/reference/logger.mdx +110 -126
- package/dist/docs/reference/middleware.mdx +235 -158
- package/dist/docs/reference/plugin.mdx +136 -64
- package/dist/docs/reference/router.mdx +275 -293
- package/dist/docs/reference/sanitize.mdx +91 -87
- package/dist/docs/reference/swagger.mdx +280 -100
- package/dist/docs/reference/testing.mdx +223 -89
- package/dist/docs/reference/validator.mdx +194 -206
- package/dist/lib/start-server.js +22 -8
- package/dist/lib/start-server.js.map +1 -1
- package/dist/module/module.d.ts +6 -3
- package/dist/module/module.d.ts.map +1 -1
- package/dist/module/module.js +17 -3
- package/dist/module/module.js.map +1 -1
- package/dist/router/controller-registrar.d.ts.map +1 -1
- package/dist/router/controller-registrar.js +26 -4
- package/dist/router/controller-registrar.js.map +1 -1
- package/dist/router/scanner/fs-scanner.d.ts.map +1 -1
- package/dist/router/scanner/fs-scanner.js +22 -0
- package/dist/router/scanner/fs-scanner.js.map +1 -1
- package/dist/router/types.d.ts +6 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/server/app.d.ts +3 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +24 -8
- package/dist/server/app.js.map +1 -1
- package/dist/server/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +19 -0
- package/dist/server/bootstrapper.js.map +1 -1
- package/dist/server/request.d.ts +2 -0
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +11 -0
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +10 -5
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +65 -18
- package/dist/server/response.js.map +1 -1
- package/dist/swagger/generator.d.ts.map +1 -1
- package/dist/swagger/generator.js +130 -10
- package/dist/swagger/generator.js.map +1 -1
- package/dist/telemetry/index.d.ts +12 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +121 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/testing/client.d.ts +1 -0
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +4 -1
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +11 -4
- package/dist/testing/index.js.map +1 -1
- package/dist/validator/builder.d.ts +26 -1
- package/dist/validator/builder.d.ts.map +1 -1
- package/dist/validator/builder.js +31 -0
- package/dist/validator/builder.js.map +1 -1
- package/dist/validator/engine.d.ts +23 -0
- package/dist/validator/engine.d.ts.map +1 -1
- package/dist/validator/engine.js +228 -31
- package/dist/validator/engine.js.map +1 -1
- package/dist/validator/index.d.ts +8 -4
- package/dist/validator/index.d.ts.map +1 -1
- package/dist/validator/index.js +4 -1
- package/dist/validator/index.js.map +1 -1
- package/dist/validator/tex-types.d.ts +19 -4
- package/dist/validator/tex-types.d.ts.map +1 -1
- package/dist/validator/tex-types.js +102 -0
- package/dist/validator/tex-types.js.map +1 -1
- package/dist/validator/tex.d.ts +1 -1
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js +3 -1
- package/dist/validator/tex.js.map +1 -1
- package/dist/validator/types.d.ts +1 -0
- package/dist/validator/types.d.ts.map +1 -1
- package/package.json +46 -14
- package/dist/docs/content/dataloader.mdx +0 -170
- package/dist/docs/reference/dataloader.mdx +0 -91
|
@@ -1,139 +1,270 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
title: "Testing"
|
|
3
|
+
description: "End-to-end HTTP integration testing, mocking, and unit test runner in ExisJS using Node.js native test runner."
|
|
4
|
+
nextUrl: "/docs/config"
|
|
5
|
+
nextTitle: "Configuration"
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# Testing
|
|
7
9
|
|
|
8
|
-
ExisJS
|
|
10
|
+
ExisJS provides an integrated testing suite built on top of the native **Node.js Test Runner** (`node:test`) and TypeScript execution environment (`tsx`). You can execute unit tests, mock dependencies, and perform full-pipeline HTTP integration testing with zero external test runners.
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The Testing Module (`exisjs/testing`)
|
|
15
|
+
|
|
16
|
+
The `exisjs/testing` module re-exports test runner primitives, assertions, mocking helpers, and the HTTP testing harness:
|
|
17
|
+
|
|
18
|
+
| Export | Source / Role | Description |
|
|
19
|
+
| :--- | :--- | :--- |
|
|
20
|
+
| `describe`, `test`, `it` | `node:test` | Test suite and test case definition functions with `.only`, `.skip`, `.todo`, and `.each`. |
|
|
21
|
+
| `before`, `after`, `beforeEach`, `afterEach` | `node:test` | Lifecycle execution hooks. |
|
|
22
|
+
| `expect` | ExisJS matcher engine | Jest-compatible fluent assertion matchers (`toBe`, `toEqual`, `toMatchObject`, `toThrow`, etc.). |
|
|
23
|
+
| `createTestContext(app)` | ExisJS HTTP harness | Boots an Exis application instance, sets up lifecycle hooks, and returns a fluent `TestApp` client. |
|
|
24
|
+
| `createTestApp(app)` | ExisJS HTTP harness | Wraps an existing `App` or `ExisAppDefinition` in a `TestApp` client without global test hooks. |
|
|
25
|
+
| `createMockRequest(opts)` | ExisJS mock helper | Creates a synthetic `ExisRequest` for unit-testing controllers or middlewares. |
|
|
26
|
+
| `createMockResponse()` | ExisJS mock helper | Creates a synthetic `ExisResponse` capturing status codes and output buffers. |
|
|
27
|
+
| `ex` | ExisJS mocking suite | Mock functions (`ex.fn`), method spies (`ex.spyOn`), module mocks (`ex.mock`), and fake timers (`ex.useFakeTimers`). |
|
|
28
|
+
| `registerCleanup(fn)` | ExisJS cleanup | Registers a teardown callback invoked after test context completion. |
|
|
11
29
|
|
|
12
30
|
---
|
|
13
31
|
|
|
14
|
-
##
|
|
32
|
+
## Integration Testing with `createTestContext`
|
|
15
33
|
|
|
16
|
-
|
|
34
|
+
`createTestContext` initializes your application definition, executes `onStart` hooks, establishes database connections (Mongoose, Prisma, or DatabaseManager), and tears down connections gracefully in `afterAll`.
|
|
17
35
|
|
|
18
|
-
###
|
|
36
|
+
### Functional Paradigm Application Test
|
|
19
37
|
|
|
20
|
-
|
|
38
|
+
Given a functional application defined in `src/http/server.ts`:
|
|
21
39
|
|
|
22
40
|
```typescript filename="tests/users.test.ts"
|
|
23
41
|
import { describe, it, expect, createTestContext } from 'exisjs/testing'
|
|
24
|
-
import app from '../src/http/server'
|
|
42
|
+
import app from '../src/http/server'
|
|
25
43
|
|
|
26
|
-
describe('Users API', () => {
|
|
27
|
-
// Automatically boots the app
|
|
44
|
+
describe('Users API Integration', () => {
|
|
45
|
+
// Automatically boots the app and manages database lifecycle
|
|
28
46
|
const api = createTestContext(app)
|
|
29
47
|
|
|
30
|
-
it('should create a new user', async () => {
|
|
31
|
-
// `api` exposes a fast, fluid HTTP client
|
|
48
|
+
it('should create a new user record', async () => {
|
|
32
49
|
const res = await api
|
|
33
50
|
.post('/users')
|
|
34
|
-
.
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
.set('authorization', 'Bearer test-token')
|
|
52
|
+
.send({
|
|
53
|
+
name: 'Jane Doe',
|
|
54
|
+
email: 'jane@example.com'
|
|
55
|
+
})
|
|
56
|
+
.expect(201)
|
|
57
|
+
|
|
58
|
+
expect(res.body).toMatchObject({
|
|
59
|
+
success: true,
|
|
60
|
+
data: {
|
|
61
|
+
id: expect.any(String),
|
|
62
|
+
name: 'Jane Doe',
|
|
63
|
+
email: 'jane@example.com'
|
|
64
|
+
}
|
|
65
|
+
})
|
|
41
66
|
})
|
|
42
67
|
|
|
43
|
-
it('should return 400
|
|
68
|
+
it('should return 400 when payload validation fails', async () => {
|
|
44
69
|
const res = await api
|
|
45
70
|
.post('/users')
|
|
46
|
-
.send({ name: '
|
|
47
|
-
.execute()
|
|
71
|
+
.send({ name: 'Jane Doe' }) // missing email
|
|
48
72
|
|
|
49
73
|
expect(res.status).toBe(400)
|
|
50
74
|
expect(res.body.success).toBe(false)
|
|
75
|
+
expect(res.body.error).toBe('Validation failed')
|
|
51
76
|
})
|
|
52
77
|
})
|
|
53
78
|
```
|
|
54
79
|
|
|
55
|
-
###
|
|
80
|
+
### Class-Based (OOP) Application Test
|
|
56
81
|
|
|
57
|
-
|
|
82
|
+
`createTestContext` natively supports `@Server` decorated entry classes:
|
|
58
83
|
|
|
59
|
-
```
|
|
60
|
-
|
|
84
|
+
```typescript filename="tests/server.test.ts"
|
|
85
|
+
import { describe, it, expect, createTestContext } from 'exisjs/testing'
|
|
86
|
+
import ServerApp from '../src/http/server'
|
|
87
|
+
|
|
88
|
+
describe('OOP Server API Integration', () => {
|
|
89
|
+
const api = createTestContext(ServerApp)
|
|
90
|
+
|
|
91
|
+
it('should retrieve status check', async () => {
|
|
92
|
+
const res = await api.get('/health').expect(200)
|
|
93
|
+
|
|
94
|
+
expect(res.body).toEqual({
|
|
95
|
+
status: 'ok',
|
|
96
|
+
uptime: expect.any(Number)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
})
|
|
61
100
|
```
|
|
62
101
|
|
|
63
|
-
|
|
102
|
+
---
|
|
64
103
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
104
|
+
## Fluid Request Builder (`TestRequest`)
|
|
105
|
+
|
|
106
|
+
The client returned by `createTestContext` and `createTestApp` provides request builder methods for standard HTTP verbs:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const res = await api
|
|
110
|
+
.get(path) // .post(path), .put(path), .patch(path), .delete(path), .head(path), .options(path)
|
|
111
|
+
.set('x-api-key', 'secret-key')
|
|
112
|
+
.send({ key: 'value' })
|
|
113
|
+
.expect(200)
|
|
114
|
+
.expect('content-type', /json/)
|
|
115
|
+
.execute()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Response Object Properties
|
|
119
|
+
|
|
120
|
+
The returned `TestResponse` contains:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
export interface TestResponse {
|
|
124
|
+
status: number // HTTP response status code (e.g. 200, 404)
|
|
125
|
+
headers: http.IncomingHttpHeaders // Normalized response headers object
|
|
126
|
+
body: any // Parsed JSON payload or raw buffer/string
|
|
127
|
+
text: string // Raw response string representation
|
|
70
128
|
}
|
|
71
129
|
```
|
|
72
130
|
|
|
73
|
-
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Unit Testing Controllers & Middlewares
|
|
134
|
+
|
|
135
|
+
For fast unit tests that do not require booting a full HTTP server pipeline, use `createMockRequest` and `createMockResponse`:
|
|
136
|
+
|
|
137
|
+
```typescript filename="tests/unit/guard.test.ts"
|
|
138
|
+
import { describe, it, expect } from 'exisjs/testing'
|
|
139
|
+
import { createMockRequest, createMockResponse, createMockNext } from 'exisjs/testing'
|
|
140
|
+
import { authGuard } from '../../src/common/guards/auth'
|
|
141
|
+
|
|
142
|
+
describe('Auth Guard Unit Test', () => {
|
|
143
|
+
it('should reject requests missing Authorization header', async () => {
|
|
144
|
+
const req = createMockRequest({
|
|
145
|
+
method: 'GET',
|
|
146
|
+
url: '/protected',
|
|
147
|
+
headers: {}
|
|
148
|
+
})
|
|
149
|
+
const res = createMockResponse()
|
|
150
|
+
const next = createMockNext()
|
|
151
|
+
|
|
152
|
+
await authGuard(req, res, next)
|
|
153
|
+
|
|
154
|
+
expect(res._statusCode).toBe(401)
|
|
155
|
+
expect(next.called).toBe(false)
|
|
156
|
+
})
|
|
74
157
|
|
|
75
|
-
|
|
158
|
+
it('should proceed when Authorization header is present', async () => {
|
|
159
|
+
const req = createMockRequest({
|
|
160
|
+
method: 'GET',
|
|
161
|
+
url: '/protected',
|
|
162
|
+
headers: {
|
|
163
|
+
authorization: 'Bearer valid-jwt-token'
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
const res = createMockResponse()
|
|
167
|
+
const next = createMockNext()
|
|
76
168
|
|
|
77
|
-
|
|
169
|
+
await authGuard(req, res, next)
|
|
170
|
+
|
|
171
|
+
expect(next.called).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
```
|
|
78
175
|
|
|
79
176
|
---
|
|
80
177
|
|
|
81
|
-
## Mocking (
|
|
178
|
+
## Mocking & Spies (`ex`)
|
|
82
179
|
|
|
83
|
-
|
|
180
|
+
ExisJS includes a built-in mocking engine via `ex`:
|
|
84
181
|
|
|
85
|
-
```typescript filename="tests/
|
|
86
|
-
import { describe, it, expect,
|
|
87
|
-
import
|
|
88
|
-
|
|
182
|
+
```typescript filename="tests/service.test.ts"
|
|
183
|
+
import { describe, it, expect, ex } from 'exisjs/testing'
|
|
184
|
+
import { PaymentService } from '../src/http/billing/service'
|
|
185
|
+
|
|
186
|
+
describe('PaymentService Unit Test', () => {
|
|
187
|
+
it('should record transactions through payment gateway', async () => {
|
|
188
|
+
const mockGateway = {
|
|
189
|
+
charge: ex.fn().mockResolvedValue({ id: 'tx_123', status: 'paid' })
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const service = new PaymentService(mockGateway as any)
|
|
193
|
+
const result = await service.processOrder('order_456', 5000)
|
|
89
194
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
BillingGateway.overrideProvider('StripeService', {
|
|
94
|
-
charge: async (amount: number) => ({ success: true, transactionId: 'test_123' })
|
|
195
|
+
expect(mockGateway.charge).toHaveBeenCalledTimes(1)
|
|
196
|
+
expect(mockGateway.charge).toHaveBeenCalledWith(5000)
|
|
197
|
+
expect(result.id).toBe('tx_123')
|
|
95
198
|
})
|
|
199
|
+
})
|
|
200
|
+
```
|
|
96
201
|
|
|
97
|
-
|
|
202
|
+
### Fake Timers
|
|
98
203
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
.post('/billing/charge')
|
|
102
|
-
.send({ amount: 500 })
|
|
103
|
-
.execute()
|
|
204
|
+
```typescript
|
|
205
|
+
import { describe, it, expect, ex } from 'exisjs/testing'
|
|
104
206
|
|
|
105
|
-
|
|
106
|
-
|
|
207
|
+
describe('Timer operations', () => {
|
|
208
|
+
it('advances timer controlled tasks', () => {
|
|
209
|
+
ex.useFakeTimers()
|
|
210
|
+
const spy = ex.fn()
|
|
211
|
+
|
|
212
|
+
setTimeout(spy, 10_000)
|
|
213
|
+
|
|
214
|
+
ex.advanceTimersByTime(10_000)
|
|
215
|
+
expect(spy).toHaveBeenCalledTimes(1)
|
|
216
|
+
|
|
217
|
+
ex.useRealTimers()
|
|
107
218
|
})
|
|
108
219
|
})
|
|
109
220
|
```
|
|
110
221
|
|
|
111
222
|
---
|
|
112
223
|
|
|
113
|
-
##
|
|
224
|
+
## Running Tests via CLI
|
|
114
225
|
|
|
115
|
-
|
|
226
|
+
Run your test suites with the built-in `exisjs test` command:
|
|
116
227
|
|
|
117
|
-
|
|
118
|
-
|
|
228
|
+
```bash
|
|
229
|
+
# Run all test files matching tests/**/*.test.ts or tests/**/*.spec.ts
|
|
230
|
+
npx exisjs test
|
|
231
|
+
|
|
232
|
+
# Run a specific test file
|
|
233
|
+
npx exisjs test tests/users.test.ts
|
|
234
|
+
|
|
235
|
+
# Run tests in watch mode
|
|
236
|
+
npx exisjs test --watch
|
|
237
|
+
|
|
238
|
+
# Run with test coverage
|
|
239
|
+
npx exisjs test --coverage
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### `package.json` Configuration
|
|
119
243
|
|
|
120
|
-
|
|
121
|
-
import { beforeEach } from 'exisjs/testing'
|
|
122
|
-
import { getActiveApp } from 'exisjs/app'
|
|
244
|
+
Add the test command to your scripts:
|
|
123
245
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
246
|
+
```json filename="package.json"
|
|
247
|
+
{
|
|
248
|
+
"scripts": {
|
|
249
|
+
"test": "exisjs test",
|
|
250
|
+
"test:watch": "exisjs test --watch"
|
|
128
251
|
}
|
|
129
|
-
}
|
|
252
|
+
}
|
|
130
253
|
```
|
|
131
254
|
|
|
132
|
-
###
|
|
133
|
-
|
|
134
|
-
|
|
255
|
+
### Test Configuration Options (`exis.config.ts`)
|
|
256
|
+
|
|
257
|
+
You can customize test runner behaviors in `exis.config.ts`:
|
|
258
|
+
|
|
259
|
+
```typescript filename="exis.config.ts"
|
|
260
|
+
import { defineConfig } from 'exisjs/config'
|
|
135
261
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
262
|
+
export default defineConfig({
|
|
263
|
+
test: {
|
|
264
|
+
include: ['tests/**/*.test.ts', 'src/**/*.spec.ts'],
|
|
265
|
+
concurrency: true,
|
|
266
|
+
coverage: false,
|
|
267
|
+
setupFiles: ['./tests/setup.ts']
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
```
|