@fluojs/testing 1.0.0-beta.2 → 1.0.0-beta.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.
- package/README.ko.md +66 -17
- package/README.md +64 -15
- package/dist/app.d.ts +2 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +1 -1
- package/dist/babel-decorators-plugin.d.ts +2 -2
- package/dist/babel-decorators-plugin.d.ts.map +1 -1
- package/dist/babel-decorators-plugin.js +26 -12
- package/dist/conformance/fetch-style-websocket-conformance.d.ts +12 -0
- package/dist/conformance/fetch-style-websocket-conformance.d.ts.map +1 -1
- package/dist/conformance/fetch-style-websocket-conformance.js +14 -0
- package/dist/conformance/platform-conformance.d.ts +21 -0
- package/dist/conformance/platform-conformance.d.ts.map +1 -1
- package/dist/conformance/platform-conformance.js +27 -0
- package/dist/mock.d.ts +17 -0
- package/dist/mock.d.ts.map +1 -1
- package/dist/mock.js +19 -0
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +106 -11
- package/dist/portability/http-adapter-portability.d.ts +9 -0
- package/dist/portability/http-adapter-portability.d.ts.map +1 -1
- package/dist/portability/http-adapter-portability.js +102 -42
- package/dist/portability/web-runtime-adapter-portability.d.ts +13 -0
- package/dist/portability/web-runtime-adapter-portability.d.ts.map +1 -1
- package/dist/portability/web-runtime-adapter-portability.js +76 -20
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -9
package/README.ko.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
4
|
|
|
5
|
-
fluo
|
|
5
|
+
fluo 애플리케이션을 위한 기본 request-level 테스트 헬퍼, 모듈 구성, 프로바이더 오버라이드 유틸리티입니다.
|
|
6
6
|
|
|
7
|
-
`@fluojs/testing`은 fluo 애플리케이션 테스트를 위한 공식적인 기준(Baseline)을 제공합니다. 격리된 테스트 환경을 구축하고, 의존성을 가짜(Fake)나 목(Mock)으로 교체하며, 모듈 그래프에서 직접 컴포넌트를 resolve하거나 가상 HTTP 요청을
|
|
7
|
+
`@fluojs/testing`은 fluo 애플리케이션 테스트를 위한 공식적인 기준(Baseline)을 제공합니다. 격리된 테스트 환경을 구축하고, 의존성을 가짜(Fake)나 목(Mock)으로 교체하며, 모듈 그래프에서 직접 컴포넌트를 resolve하거나 `createTestApp(...).request(...).send()`로 가상 HTTP 요청을 실행하여 e2e 스타일 테스트를 수행할 수 있게 합니다.
|
|
8
8
|
|
|
9
9
|
## 목차
|
|
10
10
|
|
|
@@ -12,6 +12,7 @@ fluo 애플리케이션 테스트를 위한 모듈 구성 및 프로바이더
|
|
|
12
12
|
- [사용 시점](#사용-시점)
|
|
13
13
|
- [빠른 시작](#빠른-시작)
|
|
14
14
|
- [주요 패턴](#주요-패턴)
|
|
15
|
+
- [canonical TDD ladder](#canonical-tdd-ladder)
|
|
15
16
|
- [공개 API](#공개-api)
|
|
16
17
|
- [관련 패키지](#관련-패키지)
|
|
17
18
|
- [예제 소스](#예제-소스)
|
|
@@ -24,7 +25,7 @@ pnpm add -D @fluojs/testing vitest
|
|
|
24
25
|
|
|
25
26
|
`vitest`는 mock 헬퍼와 `@fluojs/testing/vitest` 엔트리포인트가 요구하는 peer dependency입니다.
|
|
26
27
|
|
|
27
|
-
`@fluojs/testing/vitest`를 사용할 때는 `fluoBabelDecoratorsPlugin()`이 런타임에 Babel을 호출하므로, 사용하는 워크스페이스에 `@babel/core`도 함께 설치해야 합니다.
|
|
28
|
+
`@fluojs/testing/vitest`를 사용할 때는 `fluoBabelDecoratorsPlugin()`이 런타임에 Babel을 호출하므로, 사용하는 워크스페이스에 `@babel/core`도 함께 설치해야 합니다. Vitest 플러그인은 Vite query/hash suffix를 제거한 뒤 `.ts`, `.tsx`, `.mts`, `.cts` 소스 id를 변환하고, `node_modules`는 건너뛰며, 가장 가까운 root Babel config인 `babel.config.cjs`, `babel.config.mjs`, `babel.config.js`, `babel.config.json`을 해석합니다.
|
|
28
29
|
|
|
29
30
|
```bash
|
|
30
31
|
pnpm add -D @babel/core
|
|
@@ -39,6 +40,30 @@ pnpm add -D @babel/core
|
|
|
39
40
|
|
|
40
41
|
## 빠른 시작
|
|
41
42
|
|
|
43
|
+
```typescript
|
|
44
|
+
import { createTestApp } from '@fluojs/testing';
|
|
45
|
+
|
|
46
|
+
const app = await createTestApp({ rootModule: AppModule });
|
|
47
|
+
|
|
48
|
+
const response = await app
|
|
49
|
+
.request('POST', '/users/')
|
|
50
|
+
.header('x-request-id', 'test-request-1')
|
|
51
|
+
.query('include', 'profile')
|
|
52
|
+
.principal({ subject: 'user-1', roles: ['admin'] })
|
|
53
|
+
.body({ name: 'Ada' })
|
|
54
|
+
.send();
|
|
55
|
+
|
|
56
|
+
expect(response.status).toBe(201);
|
|
57
|
+
|
|
58
|
+
await app.close();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
애플리케이션 route, guard, interceptor, DTO validation, request body, query parameter, header, synthetic principal, serialized response를 검증하는 기본 HTTP/e2e 스타일 경로로는 `createTestApp({ rootModule })`을 사용하세요. 하나의 slice 안에서 module wiring, provider visibility, provider/guard/interceptor override가 계약일 때는 `createTestingModule(...)`을 사용합니다.
|
|
62
|
+
|
|
63
|
+
## 주요 패턴
|
|
64
|
+
|
|
65
|
+
### 컴파일 전 프로바이더 오버라이드
|
|
66
|
+
|
|
42
67
|
```typescript
|
|
43
68
|
import { createTestingModule } from '@fluojs/testing';
|
|
44
69
|
import { vi } from 'vitest';
|
|
@@ -52,18 +77,7 @@ const module = await createTestingModule({ rootModule: AppModule })
|
|
|
52
77
|
const service = await module.resolve(UserService);
|
|
53
78
|
```
|
|
54
79
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
### 오버라이드를 이용한 유닛 테스트
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
const module = await createTestingModule({ rootModule: AppModule })
|
|
61
|
-
.overrideProviders([
|
|
62
|
-
[USER_REPOSITORY, fakeUserRepo],
|
|
63
|
-
[EMAIL_SERVICE, fakeEmailService],
|
|
64
|
-
])
|
|
65
|
-
.compile();
|
|
66
|
-
```
|
|
80
|
+
Testing builder는 route-pipeline 테스트에서 cross-cutting behavior를 교체할 수 있도록 `overrideGuard(...)`, `overrideInterceptor(...)`, `overrideFilter(...)`도 지원합니다.
|
|
67
81
|
|
|
68
82
|
### `overrideModule()` 사용 시 모듈 identity 보존
|
|
69
83
|
|
|
@@ -78,7 +92,7 @@ expect(module.rootModule).toBe(AppModule);
|
|
|
78
92
|
expect(module.modules.some((compiledModule) => compiledModule.type === BillingModule)).toBe(true);
|
|
79
93
|
```
|
|
80
94
|
|
|
81
|
-
###
|
|
95
|
+
### `createTestApp()` 기반 request-level 테스트
|
|
82
96
|
|
|
83
97
|
```typescript
|
|
84
98
|
import { createTestApp } from '@fluojs/testing';
|
|
@@ -86,13 +100,22 @@ import { createTestApp } from '@fluojs/testing';
|
|
|
86
100
|
const app = await createTestApp({ rootModule: AppModule });
|
|
87
101
|
|
|
88
102
|
const response = await app
|
|
89
|
-
.request('
|
|
103
|
+
.request('POST', '/users/')
|
|
104
|
+
.header('authorization', 'Bearer test-token')
|
|
105
|
+
.query('include', ['profile', 'settings'])
|
|
90
106
|
.principal({ subject: 'user-1', roles: ['member'] })
|
|
107
|
+
.body({ name: 'Ada' })
|
|
91
108
|
.send();
|
|
92
109
|
|
|
110
|
+
expect(response.status).toBe(201);
|
|
111
|
+
|
|
93
112
|
await app.close();
|
|
94
113
|
```
|
|
95
114
|
|
|
115
|
+
`app.request(...).send()`는 수동 `FrameworkRequest`/`FrameworkResponse` stub 없이 HTTP 의미에 가까운 테스트를 작성하게 해 주므로 애플리케이션 개발자의 기본 경로입니다. `app.dispatch(...)`, `makeRequest(...)`, raw `FluoFactory.create(...)` 테스트는 adapter/runtime contract, framework internal, 또는 low-level dispatch boundary 자체를 증명해야 하는 compatibility case에 남겨 둡니다.
|
|
116
|
+
|
|
117
|
+
`createTestApp(...)`은 runtime HTTP bootstrap과 같은 application bootstrap option을 받습니다. 여기에는 `providers`, `filters`, `converters`, `interceptors`, `middleware`, `observers`, `versioning`, diagnostics option이 포함됩니다. 테스트 헬퍼는 request-context middleware를 앞에 추가하되, 호출자가 넘긴 middleware를 같은 app middleware chain 안에 보존합니다.
|
|
118
|
+
|
|
96
119
|
### 명시적 서브패스의 mock 헬퍼
|
|
97
120
|
|
|
98
121
|
```typescript
|
|
@@ -103,15 +126,41 @@ const repo = createMock<UserRepository>({ findById: vi.fn() });
|
|
|
103
126
|
const mailer = createDeepMock(MailService);
|
|
104
127
|
```
|
|
105
128
|
|
|
129
|
+
`asMock(value)`는 기존 값을 mock-friendly 타입으로 좁히고, `mockToken(token, value)`는 token 기반 dependency를 위한 provider override tuple을 만듭니다. `createMock(..., { strict: true })`는 지정하지 않은 member 접근을 거부합니다.
|
|
130
|
+
|
|
106
131
|
배포된 런타임 import가 안정적으로 해석되도록, mock 헬퍼를 사용할 워크스페이스에는 `vitest`를 함께 설치해야 합니다.
|
|
107
132
|
|
|
108
133
|
### 적합성 및 이식성 하니스
|
|
109
134
|
|
|
110
135
|
프레임워크 지향 플랫폼 패키지를 작성할 때는 `@fluojs/testing/platform-conformance`, `@fluojs/testing/http-adapter-portability`, `@fluojs/testing/web-runtime-adapter-portability` 같은 서브패스를 사용해 적합성 및 이식성 검증을 수행합니다.
|
|
111
136
|
|
|
137
|
+
`HttpAdapterPortabilityHarness` 메서드는 공개 어댑터 계약 체크입니다. 직접 같은 검증을 다시 만들기보다 `assertPreservesMalformedCookieValues()`, `assertSupportsSseStreaming()`, `assertPreservesRawBodyForJsonAndText()`, `assertPreservesExactRawBodyBytesForByteSensitivePayloads()`, `assertExcludesRawBodyForMultipart()`, `assertDefaultsMultipartTotalLimitToMaxBodySize()`, `assertSettlesStreamDrainWaitOnClose()`, `assertReportsConfiguredHostInStartupLogs()`, `assertReportsHttpsStartupUrl(...)`, `assertRemovesShutdownSignalListenersAfterClose()`처럼 초점이 분명한 assertion을 사용하세요.
|
|
138
|
+
|
|
139
|
+
## canonical TDD ladder
|
|
140
|
+
|
|
141
|
+
애플리케이션 기능 테스트는 가장 작은 명시적 dependency boundary에서 시작해 바깥쪽으로 확장합니다.
|
|
142
|
+
|
|
143
|
+
1. **Unit**: `src/**` 아래 service, controller, helper, failure branch 가까이에 `*.test.ts` 파일을 둡니다. 클래스를 직접 구성하고 명시적 fake를 넘기거나, typed mock이 설정을 읽기 쉽게 만들 때 `@fluojs/testing/mock` 헬퍼를 사용합니다.
|
|
144
|
+
2. **Slice/module integration**: DI wiring과 provider override coverage에는 `createTestingModule({ rootModule })` 또는 `Test.createTestingModule({ rootModule })` 기반 `*.slice.test.ts` 파일을 추가합니다.
|
|
145
|
+
3. **HTTP e2e-style**: `test/app.e2e.test.ts` 같은 app-level 테스트는 `createTestApp({ rootModule })`와 기본 route assertion helper인 `app.request(...).send()`로 virtual request pipeline을 검증합니다. 더 낮은 수준의 dispatch contract 자체가 테스트 대상일 때만 `app.dispatch(...)`를 사용합니다.
|
|
146
|
+
4. **Platform/conformance**: harness subpath는 일반 애플리케이션 기능 coverage가 아니라 adapter/runtime package contract에만 사용합니다.
|
|
147
|
+
|
|
148
|
+
```txt
|
|
149
|
+
src/users/
|
|
150
|
+
users.service.test.ts
|
|
151
|
+
users.controller.test.ts
|
|
152
|
+
users.slice.test.ts
|
|
153
|
+
|
|
154
|
+
test/
|
|
155
|
+
app.e2e.test.ts
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
fluo는 테스트가 명시적인 `rootModule`을 이름으로 지정해야 한다는 점에서 NestJS와 다릅니다. 테스트 유틸리티는 legacy TypeScript design metadata나 reflection flag에서 dependency를 추론하지 않고, 작성자가 만든 module graph를 컴파일합니다.
|
|
159
|
+
|
|
112
160
|
## 공개 API
|
|
113
161
|
|
|
114
162
|
- **루트 패키지**: `createTestingModule(...)`, `createTestApp(...)`, 모듈 introspection 헬퍼, 공용 테스트 타입
|
|
163
|
+
- **서브패스**: `@fluojs/testing/app`, `@fluojs/testing/module`, `@fluojs/testing/http`, `@fluojs/testing/mock`, `@fluojs/testing/types`, `@fluojs/testing/vitest`
|
|
115
164
|
- **Mock 서브패스**: `@fluojs/testing/mock`
|
|
116
165
|
- **HTTP 헬퍼**: `@fluojs/testing/http`
|
|
117
166
|
- **하니스 서브패스**: `platform-conformance`, `http-adapter-portability`, `web-runtime-adapter-portability`, `fetch-style-websocket-conformance`
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Default request-level testing helpers, testing module construction, and provider overrides for fluo applications.
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
@@ -10,6 +10,7 @@ Testing module construction, provider overrides, and request-level test helpers
|
|
|
10
10
|
- [When to Use](#when-to-use)
|
|
11
11
|
- [Quick Start](#quick-start)
|
|
12
12
|
- [Common Patterns](#common-patterns)
|
|
13
|
+
- [Canonical TDD Ladder](#canonical-tdd-ladder)
|
|
13
14
|
- [Public API](#public-api)
|
|
14
15
|
- [Related Packages](#related-packages)
|
|
15
16
|
- [Example Sources](#example-sources)
|
|
@@ -22,7 +23,7 @@ npm install --save-dev @fluojs/testing vitest
|
|
|
22
23
|
|
|
23
24
|
`vitest` is a required peer dependency for the mock helpers and the `@fluojs/testing/vitest` entrypoint.
|
|
24
25
|
|
|
25
|
-
If you use `@fluojs/testing/vitest`, install `@babel/core` in the consuming workspace as well because `fluoBabelDecoratorsPlugin()` invokes Babel at runtime
|
|
26
|
+
If you use `@fluojs/testing/vitest`, install `@babel/core` in the consuming workspace as well because `fluoBabelDecoratorsPlugin()` invokes Babel at runtime. The Vitest plugin transforms `.ts`, `.tsx`, `.mts`, and `.cts` source ids after removing Vite query/hash suffixes, skips `node_modules`, and resolves the nearest root Babel config named `babel.config.cjs`, `babel.config.mjs`, `babel.config.js`, or `babel.config.json`:
|
|
26
27
|
|
|
27
28
|
```bash
|
|
28
29
|
npm install --save-dev @babel/core
|
|
@@ -38,31 +39,44 @@ npm install --save-dev @babel/core
|
|
|
38
39
|
## Quick Start
|
|
39
40
|
|
|
40
41
|
```ts
|
|
41
|
-
import {
|
|
42
|
-
import { vi } from 'vitest';
|
|
42
|
+
import { createTestApp } from '@fluojs/testing';
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
.overrideProvider(USER_REPOSITORY, {
|
|
46
|
-
create: vi.fn().mockResolvedValue({ id: '1', name: 'Alice' }),
|
|
47
|
-
})
|
|
48
|
-
.compile();
|
|
44
|
+
const app = await createTestApp({ rootModule: AppModule });
|
|
49
45
|
|
|
50
|
-
const
|
|
46
|
+
const response = await app
|
|
47
|
+
.request('POST', '/users/')
|
|
48
|
+
.header('x-request-id', 'test-request-1')
|
|
49
|
+
.query('include', 'profile')
|
|
50
|
+
.principal({ subject: 'user-1', roles: ['admin'] })
|
|
51
|
+
.body({ name: 'Ada' })
|
|
52
|
+
.send();
|
|
53
|
+
|
|
54
|
+
expect(response.status).toBe(201);
|
|
55
|
+
|
|
56
|
+
await app.close();
|
|
51
57
|
```
|
|
52
58
|
|
|
59
|
+
Use `createTestApp({ rootModule })` as the default HTTP/e2e-style path for application routes, guards, interceptors, DTO validation, request bodies, query parameters, headers, synthetic principals, and serialized responses. Reach for `createTestingModule(...)` when the contract is module wiring, provider visibility, or provider/guard/interceptor overrides inside one slice.
|
|
60
|
+
|
|
53
61
|
## Common Patterns
|
|
54
62
|
|
|
55
63
|
### Override providers before compilation
|
|
56
64
|
|
|
57
65
|
```ts
|
|
66
|
+
import { createTestingModule } from '@fluojs/testing';
|
|
67
|
+
import { vi } from 'vitest';
|
|
68
|
+
|
|
58
69
|
const module = await createTestingModule({ rootModule: AppModule })
|
|
59
|
-
.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
])
|
|
70
|
+
.overrideProvider(USER_REPOSITORY, {
|
|
71
|
+
create: vi.fn().mockResolvedValue({ id: '1', name: 'Alice' }),
|
|
72
|
+
})
|
|
63
73
|
.compile();
|
|
74
|
+
|
|
75
|
+
const service = await module.resolve(UserService);
|
|
64
76
|
```
|
|
65
77
|
|
|
78
|
+
The testing builder also supports `overrideGuard(...)`, `overrideInterceptor(...)`, and `overrideFilter(...)` for route-pipeline tests that need to replace cross-cutting behavior.
|
|
79
|
+
|
|
66
80
|
### Preserve module identity with `overrideModule()`
|
|
67
81
|
|
|
68
82
|
`createTestingModule({ rootModule })` requires an explicit root module so tests compile the same module graph shape that production bootstrap uses. When `overrideModule(source, replacement)` swaps imported modules, the compiled testing module preserves the original `rootModule` and compiled `modules[].type` identities while using the replacement imports for provider resolution. This keeps diagnostics, graph assertions, and module-introspection helpers tied to the application module classes you authored instead of synthetic test-only wrapper classes.
|
|
@@ -84,13 +98,22 @@ import { createTestApp } from '@fluojs/testing';
|
|
|
84
98
|
const app = await createTestApp({ rootModule: AppModule });
|
|
85
99
|
|
|
86
100
|
const response = await app
|
|
87
|
-
.request('
|
|
101
|
+
.request('POST', '/users/')
|
|
102
|
+
.header('authorization', 'Bearer test-token')
|
|
103
|
+
.query('include', ['profile', 'settings'])
|
|
88
104
|
.principal({ subject: 'user-1', roles: ['member'] })
|
|
105
|
+
.body({ name: 'Ada' })
|
|
89
106
|
.send();
|
|
90
107
|
|
|
108
|
+
expect(response.status).toBe(201);
|
|
109
|
+
|
|
91
110
|
await app.close();
|
|
92
111
|
```
|
|
93
112
|
|
|
113
|
+
`app.request(...).send()` is the preferred app-developer path because it keeps tests close to HTTP semantics without manual `FrameworkRequest`/`FrameworkResponse` stubs. Keep `app.dispatch(...)`, `makeRequest(...)`, and raw `FluoFactory.create(...)` tests for adapter/runtime contracts, framework internals, or compatibility cases where the low-level dispatch boundary itself is what the test must prove.
|
|
114
|
+
|
|
115
|
+
`createTestApp(...)` accepts the same application bootstrap options as the runtime HTTP bootstrap, including `providers`, `filters`, `converters`, `interceptors`, `middleware`, `observers`, `versioning`, and diagnostics options. The testing helper prepends its request-context middleware while preserving caller-provided middleware in the same app middleware chain.
|
|
116
|
+
|
|
94
117
|
### Mock helpers from explicit subpaths
|
|
95
118
|
|
|
96
119
|
```ts
|
|
@@ -101,15 +124,41 @@ const repo = createMock<UserRepository>({ findById: vi.fn() });
|
|
|
101
124
|
const mailer = createDeepMock(MailService);
|
|
102
125
|
```
|
|
103
126
|
|
|
127
|
+
`asMock(value)` narrows an existing value to a mock-friendly type, and `mockToken(token, value)` creates a provider override tuple for token-based dependencies. `createMock(..., { strict: true })` rejects access to unspecified members.
|
|
128
|
+
|
|
104
129
|
Install `vitest` in the consuming workspace before using the mock helpers so the published runtime import resolves consistently.
|
|
105
130
|
|
|
106
131
|
### Conformance and portability harnesses
|
|
107
132
|
|
|
108
133
|
Use subpaths like `@fluojs/testing/platform-conformance`, `@fluojs/testing/http-adapter-portability`, and `@fluojs/testing/web-runtime-adapter-portability` when authoring framework-facing platform packages.
|
|
109
134
|
|
|
135
|
+
`HttpAdapterPortabilityHarness` methods are the public adapter contract checks. Prefer focused assertions such as `assertPreservesMalformedCookieValues()`, `assertSupportsSseStreaming()`, `assertPreservesRawBodyForJsonAndText()`, `assertPreservesExactRawBodyBytesForByteSensitivePayloads()`, `assertExcludesRawBodyForMultipart()`, `assertDefaultsMultipartTotalLimitToMaxBodySize()`, `assertSettlesStreamDrainWaitOnClose()`, `assertReportsConfiguredHostInStartupLogs()`, `assertReportsHttpsStartupUrl(...)`, and `assertRemovesShutdownSignalListenersAfterClose()` instead of hand-rolled equivalents.
|
|
136
|
+
|
|
137
|
+
## Canonical TDD Ladder
|
|
138
|
+
|
|
139
|
+
For application features, build tests from the smallest explicit dependency boundary outward:
|
|
140
|
+
|
|
141
|
+
1. **Unit**: place `*.test.ts` files next to the service, controller, helper, or failure branch under `src/**`. Construct the class directly with explicit fakes, or use `@fluojs/testing/mock` helpers when typed mocks keep setup readable.
|
|
142
|
+
2. **Slice/module integration**: add `*.slice.test.ts` files for DI wiring and provider override coverage with `createTestingModule({ rootModule })` or `Test.createTestingModule({ rootModule })`.
|
|
143
|
+
3. **HTTP e2e-style**: place app-level tests such as `test/app.e2e.test.ts` around the virtual request pipeline with `createTestApp({ rootModule })` and `app.request(...).send()` as the default route assertion helper. Use `app.dispatch(...)` only when a lower-level dispatch contract is the subject of the test.
|
|
144
|
+
4. **Platform/conformance**: use harness subpaths only for adapter/runtime package contracts, not ordinary application feature coverage.
|
|
145
|
+
|
|
146
|
+
```txt
|
|
147
|
+
src/users/
|
|
148
|
+
users.service.test.ts
|
|
149
|
+
users.controller.test.ts
|
|
150
|
+
users.slice.test.ts
|
|
151
|
+
|
|
152
|
+
test/
|
|
153
|
+
app.e2e.test.ts
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
fluo differs from NestJS by requiring tests to name an explicit `rootModule`. The testing utilities compile the module graph you authored instead of inferring dependencies from legacy TypeScript design metadata or reflection flags.
|
|
157
|
+
|
|
110
158
|
## Public API
|
|
111
159
|
|
|
112
160
|
- **Root package**: `createTestingModule(...)`, `createTestApp(...)`, module introspection helpers, shared testing types
|
|
161
|
+
- **Subpaths**: `@fluojs/testing/app`, `@fluojs/testing/module`, `@fluojs/testing/http`, `@fluojs/testing/mock`, `@fluojs/testing/types`, `@fluojs/testing/vitest`
|
|
113
162
|
- **Mock subpath**: `@fluojs/testing/mock`
|
|
114
163
|
- **HTTP helpers**: `@fluojs/testing/http`
|
|
115
164
|
- **Harness subpaths**: `platform-conformance`, `http-adapter-portability`, `web-runtime-adapter-portability`, `fetch-style-websocket-conformance`
|
package/dist/app.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TestApp,
|
|
1
|
+
import type { TestApp, TestingApplicationOptions } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Boots a lightweight test app with the real dispatcher and a fluent request client.
|
|
4
4
|
*
|
|
@@ -12,5 +12,5 @@ import type { TestApp, TestingModuleOptions } from './types.js';
|
|
|
12
12
|
* await app.close();
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
|
-
export declare function createTestApp(options:
|
|
15
|
+
export declare function createTestApp(options: TestingApplicationOptions): Promise<TestApp>;
|
|
16
16
|
//# sourceMappingURL=app.d.ts.map
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,OAAO,EACP,
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,OAAO,EACP,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AA6BpB;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBxF"}
|
package/dist/app.js
CHANGED
|
@@ -36,7 +36,7 @@ function normalizeRequestInput(methodOrRequest, pathOrOptions, options) {
|
|
|
36
36
|
export async function createTestApp(options) {
|
|
37
37
|
const app = await bootstrapApplication({
|
|
38
38
|
...options,
|
|
39
|
-
middleware: [createTestRequestContextMiddleware()]
|
|
39
|
+
middleware: [createTestRequestContextMiddleware(), ...(options.middleware ?? [])]
|
|
40
40
|
});
|
|
41
41
|
const request = (methodOrRequest, pathOrOptions, options) => {
|
|
42
42
|
return createRequestBuilder(app.dispatcher, normalizeRequestInput(methodOrRequest, pathOrOptions, options));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Resolves the nearest
|
|
2
|
+
* Resolves the nearest Babel root configuration file starting from the given file path
|
|
3
3
|
* and searching upwards through the directory hierarchy.
|
|
4
4
|
*
|
|
5
5
|
* @param filePath - The path to the file whose nearest Babel configuration should be found.
|
|
6
|
-
* @returns The absolute path to the nearest
|
|
6
|
+
* @returns The absolute path to the nearest Babel root configuration file.
|
|
7
7
|
* @throws Error if no configuration file can be located.
|
|
8
8
|
*/
|
|
9
9
|
export declare function resolveNearestBabelConfigFile(filePath: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"babel-decorators-plugin.d.ts","sourceRoot":"","sources":["../src/babel-decorators-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"babel-decorators-plugin.d.ts","sourceRoot":"","sources":["../src/babel-decorators-plugin.ts"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA2BtE;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM;;oBAIvB,MAAM,MAAM,MAAM;;;;;;;;;;;;EAwB3C;AAED;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAAC,OAAO,+BAA+B,CAAC,CAAC"}
|
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
2
|
+
import { dirname, extname, join } from 'node:path';
|
|
3
3
|
import { transformAsync } from '@babel/core';
|
|
4
4
|
const babelConfigFileCache = new Map();
|
|
5
|
+
const babelConfigFileNames = ['babel.config.cjs', 'babel.config.mjs', 'babel.config.js', 'babel.config.json'];
|
|
6
|
+
function normalizeTransformId(id) {
|
|
7
|
+
const withoutQuery = id.split(/[?#]/, 1)[0] ?? id;
|
|
8
|
+
return withoutQuery.startsWith('\0') ? withoutQuery.slice(1) : withoutQuery;
|
|
9
|
+
}
|
|
10
|
+
function isNodeModulesPath(filePath) {
|
|
11
|
+
return /(?:^|[/\\])node_modules(?:[/\\]|$)/.test(filePath);
|
|
12
|
+
}
|
|
13
|
+
function isTypeScriptSource(filePath) {
|
|
14
|
+
return ['.cts', '.mts', '.ts', '.tsx'].includes(extname(filePath));
|
|
15
|
+
}
|
|
5
16
|
|
|
6
17
|
/**
|
|
7
|
-
* Resolves the nearest
|
|
18
|
+
* Resolves the nearest Babel root configuration file starting from the given file path
|
|
8
19
|
* and searching upwards through the directory hierarchy.
|
|
9
20
|
*
|
|
10
21
|
* @param filePath - The path to the file whose nearest Babel configuration should be found.
|
|
11
|
-
* @returns The absolute path to the nearest
|
|
22
|
+
* @returns The absolute path to the nearest Babel root configuration file.
|
|
12
23
|
* @throws Error if no configuration file can be located.
|
|
13
24
|
*/
|
|
14
25
|
export function resolveNearestBabelConfigFile(filePath) {
|
|
15
|
-
let currentDirectory = dirname(filePath);
|
|
26
|
+
let currentDirectory = dirname(normalizeTransformId(filePath));
|
|
16
27
|
while (true) {
|
|
17
28
|
const cachedConfigFile = babelConfigFileCache.get(currentDirectory);
|
|
18
29
|
if (cachedConfigFile) {
|
|
19
30
|
return cachedConfigFile;
|
|
20
31
|
}
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
for (const configFileName of babelConfigFileNames) {
|
|
33
|
+
const configFile = join(currentDirectory, configFileName);
|
|
34
|
+
if (existsSync(configFile)) {
|
|
35
|
+
babelConfigFileCache.set(currentDirectory, configFile);
|
|
36
|
+
return configFile;
|
|
37
|
+
}
|
|
25
38
|
}
|
|
26
39
|
const parentDirectory = dirname(currentDirectory);
|
|
27
40
|
if (parentDirectory === currentDirectory) {
|
|
28
|
-
throw new Error(`Unable to locate
|
|
41
|
+
throw new Error(`Unable to locate a Babel root config (${babelConfigFileNames.join(', ')}) for ${filePath}.`);
|
|
29
42
|
}
|
|
30
43
|
currentDirectory = parentDirectory;
|
|
31
44
|
}
|
|
@@ -42,13 +55,14 @@ export function createFluoBabelDecoratorsPlugin(resolveConfigFile) {
|
|
|
42
55
|
return {
|
|
43
56
|
name: 'fluo-babel-decorators',
|
|
44
57
|
async transform(code, id) {
|
|
45
|
-
|
|
58
|
+
const filePath = normalizeTransformId(id);
|
|
59
|
+
if (!isTypeScriptSource(filePath) || isNodeModulesPath(filePath)) {
|
|
46
60
|
return null;
|
|
47
61
|
}
|
|
48
62
|
const result = await transformAsync(code, {
|
|
49
63
|
babelrc: false,
|
|
50
|
-
configFile: resolveConfigFile(
|
|
51
|
-
filename:
|
|
64
|
+
configFile: resolveConfigFile(filePath),
|
|
65
|
+
filename: filePath,
|
|
52
66
|
sourceMaps: true
|
|
53
67
|
});
|
|
54
68
|
if (!result?.code) {
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import type { FetchStyleHttpAdapterRealtimeCapability, HttpApplicationAdapter } from '@fluojs/http';
|
|
2
|
+
/**
|
|
3
|
+
* Describes the fetch style web socket conformance harness options contract.
|
|
4
|
+
*/
|
|
2
5
|
export interface FetchStyleWebSocketConformanceHarnessOptions<TAdapter extends HttpApplicationAdapter = HttpApplicationAdapter> {
|
|
3
6
|
createAdapter: () => TAdapter;
|
|
4
7
|
expectedReason: string;
|
|
5
8
|
expectedSupport?: FetchStyleHttpAdapterRealtimeCapability['support'];
|
|
6
9
|
name: string;
|
|
7
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Represents the fetch style web socket conformance harness.
|
|
13
|
+
*/
|
|
8
14
|
export declare class FetchStyleWebSocketConformanceHarness<TAdapter extends HttpApplicationAdapter = HttpApplicationAdapter> {
|
|
9
15
|
private readonly options;
|
|
10
16
|
constructor(options: FetchStyleWebSocketConformanceHarnessOptions<TAdapter>);
|
|
11
17
|
assertExposesRawWebSocketExpansionContract(): void;
|
|
12
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Create fetch style web socket conformance harness.
|
|
21
|
+
*
|
|
22
|
+
* @param options The options.
|
|
23
|
+
* @returns The create fetch style web socket conformance harness result.
|
|
24
|
+
*/
|
|
13
25
|
export declare function createFetchStyleWebSocketConformanceHarness<TAdapter extends HttpApplicationAdapter = HttpApplicationAdapter>(options: FetchStyleWebSocketConformanceHarnessOptions<TAdapter>): FetchStyleWebSocketConformanceHarness<TAdapter>;
|
|
14
26
|
//# sourceMappingURL=fetch-style-websocket-conformance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-style-websocket-conformance.d.ts","sourceRoot":"","sources":["../../src/conformance/fetch-style-websocket-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uCAAuC,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEpG,MAAM,WAAW,4CAA4C,CAC3D,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAEhE,aAAa,EAAE,MAAM,QAAQ,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,uCAAuC,CAAC,SAAS,CAAC,CAAC;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,qCAAqC,CAChD,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAEpD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,4CAA4C,CAAC,QAAQ,CAAC;IAE5F,0CAA0C,IAAI,IAAI;CAoCnD;AAED,wBAAgB,2CAA2C,CACzD,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB,EAEhE,OAAO,EAAE,4CAA4C,CAAC,QAAQ,CAAC,GAC9D,qCAAqC,CAAC,QAAQ,CAAC,CAEjD"}
|
|
1
|
+
{"version":3,"file":"fetch-style-websocket-conformance.d.ts","sourceRoot":"","sources":["../../src/conformance/fetch-style-websocket-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uCAAuC,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEpG;;GAEG;AACH,MAAM,WAAW,4CAA4C,CAC3D,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAEhE,aAAa,EAAE,MAAM,QAAQ,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,uCAAuC,CAAC,SAAS,CAAC,CAAC;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,qBAAa,qCAAqC,CAChD,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAEpD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,4CAA4C,CAAC,QAAQ,CAAC;IAE5F,0CAA0C,IAAI,IAAI;CAoCnD;AAED;;;;;GAKG;AACH,wBAAgB,2CAA2C,CACzD,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB,EAEhE,OAAO,EAAE,4CAA4C,CAAC,QAAQ,CAAC,GAC9D,qCAAqC,CAAC,QAAQ,CAAC,CAEjD"}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes the fetch style web socket conformance harness options contract.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents the fetch style web socket conformance harness.
|
|
7
|
+
*/
|
|
1
8
|
export class FetchStyleWebSocketConformanceHarness {
|
|
2
9
|
constructor(options) {
|
|
3
10
|
this.options = options;
|
|
@@ -29,6 +36,13 @@ export class FetchStyleWebSocketConformanceHarness {
|
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create fetch style web socket conformance harness.
|
|
42
|
+
*
|
|
43
|
+
* @param options The options.
|
|
44
|
+
* @returns The create fetch style web socket conformance harness result.
|
|
45
|
+
*/
|
|
32
46
|
export function createFetchStyleWebSocketConformanceHarness(options) {
|
|
33
47
|
return new FetchStyleWebSocketConformanceHarness(options);
|
|
34
48
|
}
|
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
import type { MaybePromise } from '@fluojs/core';
|
|
2
2
|
import type { PlatformComponent, PlatformDiagnosticIssue, PlatformSnapshot, PlatformState, PlatformValidationResult } from '@fluojs/runtime';
|
|
3
|
+
/**
|
|
4
|
+
* Describes the platform conformance scenario contract.
|
|
5
|
+
*/
|
|
3
6
|
export interface PlatformConformanceScenario {
|
|
4
7
|
createComponent: () => PlatformComponent;
|
|
5
8
|
enterState: (component: PlatformComponent) => MaybePromise<void>;
|
|
6
9
|
name: string;
|
|
7
10
|
expectedState?: PlatformState;
|
|
8
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Describes the platform conformance diagnostics options contract.
|
|
14
|
+
*/
|
|
9
15
|
export interface PlatformConformanceDiagnosticsOptions {
|
|
10
16
|
collect?: (component: PlatformComponent, validation: PlatformValidationResult) => MaybePromise<readonly PlatformDiagnosticIssue[]>;
|
|
11
17
|
expectedCodes?: readonly string[];
|
|
12
18
|
requireFixHintForSeverities?: ReadonlyArray<PlatformDiagnosticIssue['severity']>;
|
|
13
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Describes the platform conformance snapshot options contract.
|
|
22
|
+
*/
|
|
14
23
|
export interface PlatformConformanceSnapshotOptions {
|
|
15
24
|
allowKeyPatterns?: readonly RegExp[];
|
|
16
25
|
compare?: (left: unknown, right: unknown) => boolean;
|
|
17
26
|
forbiddenKeyPatterns?: readonly RegExp[];
|
|
18
27
|
sanitize?: (snapshot: PlatformSnapshot) => PlatformSnapshot;
|
|
19
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Describes the platform conformance harness options contract.
|
|
31
|
+
*/
|
|
20
32
|
export interface PlatformConformanceHarnessOptions {
|
|
21
33
|
captureValidationSideEffects?: (component: PlatformComponent) => MaybePromise<unknown>;
|
|
22
34
|
createComponent: () => PlatformComponent;
|
|
@@ -27,6 +39,9 @@ export interface PlatformConformanceHarnessOptions {
|
|
|
27
39
|
};
|
|
28
40
|
snapshot?: PlatformConformanceSnapshotOptions;
|
|
29
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Represents the platform conformance harness.
|
|
44
|
+
*/
|
|
30
45
|
export declare class PlatformConformanceHarness {
|
|
31
46
|
private readonly options;
|
|
32
47
|
constructor(options: PlatformConformanceHarnessOptions);
|
|
@@ -38,5 +53,11 @@ export declare class PlatformConformanceHarness {
|
|
|
38
53
|
assertSnapshotSanitized(): Promise<void>;
|
|
39
54
|
assertAll(): Promise<void>;
|
|
40
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Create platform conformance harness.
|
|
58
|
+
*
|
|
59
|
+
* @param options The options.
|
|
60
|
+
* @returns The create platform conformance harness result.
|
|
61
|
+
*/
|
|
41
62
|
export declare function createPlatformConformanceHarness(options: PlatformConformanceHarnessOptions): PlatformConformanceHarness;
|
|
42
63
|
//# sourceMappingURL=platform-conformance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-conformance.d.ts","sourceRoot":"","sources":["../../src/conformance/platform-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,EACb,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,2BAA2B;IAC1C,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,UAAU,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,qCAAqC;IACpD,OAAO,CAAC,EAAE,CACR,SAAS,EAAE,iBAAiB,EAC5B,UAAU,EAAE,wBAAwB,KACjC,YAAY,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAAC;IACtD,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2BAA2B,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC;CAClF;AAED,MAAM,WAAW,kCAAkC;IACjD,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACrD,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;CAC7D;AAED,MAAM,WAAW,iCAAiC;IAChD,4BAA4B,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IACvF,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,WAAW,CAAC,EAAE,qCAAqC,CAAC;IACpD,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,2BAA2B,CAAC;QACtC,MAAM,EAAE,2BAA2B,CAAC;KACrC,CAAC;IACF,QAAQ,CAAC,EAAE,kCAAkC,CAAC;CAC/C;AA4ED,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iCAAiC;IAEjE,yCAAyC,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B1D,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB3C,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCvC,2CAA2C,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5D,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCxC,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBxC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAQjC;AAED,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,0BAA0B,CAE5B"}
|
|
1
|
+
{"version":3,"file":"platform-conformance.d.ts","sourceRoot":"","sources":["../../src/conformance/platform-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,EACb,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,UAAU,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,qCAAqC;IACpD,OAAO,CAAC,EAAE,CACR,SAAS,EAAE,iBAAiB,EAC5B,UAAU,EAAE,wBAAwB,KACjC,YAAY,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAAC;IACtD,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2BAA2B,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC;CAClF;AAED;;GAEG;AACH,MAAM,WAAW,kCAAkC;IACjD,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACrD,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;CAC7D;AAED;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,4BAA4B,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IACvF,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,WAAW,CAAC,EAAE,qCAAqC,CAAC;IACpD,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,2BAA2B,CAAC;QACtC,MAAM,EAAE,2BAA2B,CAAC;KACrC,CAAC;IACF,QAAQ,CAAC,EAAE,kCAAkC,CAAC;CAC/C;AA4ED;;GAEG;AACH,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iCAAiC;IAEjE,yCAAyC,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B1D,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB3C,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCvC,2CAA2C,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5D,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCxC,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBxC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAQjC;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,0BAA0B,CAE5B"}
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes the platform conformance scenario contract.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Describes the platform conformance diagnostics options contract.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Describes the platform conformance snapshot options contract.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Describes the platform conformance harness options contract.
|
|
15
|
+
*/
|
|
16
|
+
|
|
1
17
|
const DEFAULT_FORBIDDEN_KEY_PATTERNS = [/secret/i, /password/i, /token/i, /credential/i, /api[-_]?key/i];
|
|
2
18
|
const DEFAULT_REQUIRED_FIX_HINT_SEVERITIES = ['error'];
|
|
3
19
|
function isRecord(value) {
|
|
@@ -52,6 +68,10 @@ function collectForbiddenKeyPaths(value, patterns, allowPatterns, currentPath, v
|
|
|
52
68
|
collectForbiddenKeyPaths(entry, patterns, allowPatterns, nextPath, violations);
|
|
53
69
|
}
|
|
54
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Represents the platform conformance harness.
|
|
74
|
+
*/
|
|
55
75
|
export class PlatformConformanceHarness {
|
|
56
76
|
constructor(options) {
|
|
57
77
|
this.options = options;
|
|
@@ -188,6 +208,13 @@ export class PlatformConformanceHarness {
|
|
|
188
208
|
await this.assertSnapshotSanitized();
|
|
189
209
|
}
|
|
190
210
|
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Create platform conformance harness.
|
|
214
|
+
*
|
|
215
|
+
* @param options The options.
|
|
216
|
+
* @returns The create platform conformance harness result.
|
|
217
|
+
*/
|
|
191
218
|
export function createPlatformConformanceHarness(options) {
|
|
192
219
|
return new PlatformConformanceHarness(options);
|
|
193
220
|
}
|
package/dist/mock.d.ts
CHANGED
|
@@ -2,25 +2,42 @@ import type { Mock } from 'vitest';
|
|
|
2
2
|
import type { Token } from '@fluojs/core';
|
|
3
3
|
import type { ValueProvider } from '@fluojs/di';
|
|
4
4
|
import type { DeepMocked } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Defines the mocked methods type.
|
|
7
|
+
*/
|
|
5
8
|
export type MockedMethods<T> = {
|
|
6
9
|
[K in keyof T]: T[K] extends (...args: never[]) => unknown ? Mock<T[K]> : T[K];
|
|
7
10
|
};
|
|
8
11
|
/**
|
|
9
12
|
* Creates a proxy mock object with optional strict missing-property checks.
|
|
13
|
+
*
|
|
14
|
+
* @param partial The partial.
|
|
15
|
+
* @param options The options.
|
|
16
|
+
* @returns The create mock result.
|
|
10
17
|
*/
|
|
11
18
|
export declare function createMock<T extends object>(partial?: Partial<MockedMethods<T>>, options?: {
|
|
12
19
|
strict?: boolean;
|
|
13
20
|
}): MockedMethods<T>;
|
|
14
21
|
/**
|
|
15
22
|
* Casts a function to a strongly typed Vitest mock.
|
|
23
|
+
*
|
|
24
|
+
* @param fn The fn.
|
|
25
|
+
* @returns The as mock result.
|
|
16
26
|
*/
|
|
17
27
|
export declare function asMock<T extends (...args: never[]) => unknown>(fn: T): Mock<T>;
|
|
18
28
|
/**
|
|
19
29
|
* Creates a deep mock by replacing prototype methods with `vi.fn()` spies.
|
|
30
|
+
*
|
|
31
|
+
* @param type The type.
|
|
32
|
+
* @returns The create deep mock result.
|
|
20
33
|
*/
|
|
21
34
|
export declare function createDeepMock<T extends object>(type: new (...args: unknown[]) => T): DeepMocked<T>;
|
|
22
35
|
/**
|
|
23
36
|
* Creates a `useValue` provider for overriding a token in tests.
|
|
37
|
+
*
|
|
38
|
+
* @param token The token.
|
|
39
|
+
* @param partial The partial.
|
|
40
|
+
* @returns The mock token result.
|
|
24
41
|
*/
|
|
25
42
|
export declare function mockToken<T>(token: Token<T>, partial?: Partial<T>): ValueProvider<T>;
|
|
26
43
|
//# sourceMappingURL=mock.d.ts.map
|
package/dist/mock.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC/E,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC/E,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,OAAO,GAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAM,EACvC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,aAAa,CAAC,CAAC,CAAC,CAsBlB;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAE9E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAkBnG;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,OAAO,CAAC,CAAC,CAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAExF"}
|