@fluojs/testing 1.0.0-beta.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.ko.md +117 -0
- package/README.md +115 -0
- package/dist/app.d.ts +16 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +54 -0
- package/dist/babel-decorators-plugin.d.ts +36 -0
- package/dist/babel-decorators-plugin.d.ts.map +1 -0
- package/dist/babel-decorators-plugin.js +67 -0
- package/dist/conformance/fetch-style-websocket-conformance.d.ts +14 -0
- package/dist/conformance/fetch-style-websocket-conformance.d.ts.map +1 -0
- package/dist/conformance/fetch-style-websocket-conformance.js +34 -0
- package/dist/conformance/platform-conformance.d.ts +42 -0
- package/dist/conformance/platform-conformance.d.ts.map +1 -0
- package/dist/conformance/platform-conformance.js +193 -0
- package/dist/http.d.ts +73 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +239 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/mock.d.ts +26 -0
- package/dist/mock.d.ts.map +1 -0
- package/dist/mock.js +60 -0
- package/dist/module.d.ts +45 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +405 -0
- package/dist/portability/http-adapter-portability.d.ts +83 -0
- package/dist/portability/http-adapter-portability.d.ts.map +1 -0
- package/dist/portability/http-adapter-portability.js +528 -0
- package/dist/portability/web-runtime-adapter-portability.d.ts +26 -0
- package/dist/portability/web-runtime-adapter-portability.d.ts.map +1 -0
- package/dist/portability/web-runtime-adapter-portability.js +260 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/vitest.d.ts +9 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +11 -0
- package/package.json +102 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
|
|
2
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
3
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
4
|
+
function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
|
|
5
|
+
function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
|
|
6
|
+
// @ts-ignore Worktree-local LSP does not resolve workspace package aliases.
|
|
7
|
+
import { Controller, Get, Post, SseResponse } from '@fluojs/http';
|
|
8
|
+
// @ts-ignore Worktree-local LSP does not resolve workspace package aliases.
|
|
9
|
+
import { defineModule } from '@fluojs/runtime';
|
|
10
|
+
function decodeUtf8(input) {
|
|
11
|
+
return new TextDecoder().decode(input ?? new Uint8Array());
|
|
12
|
+
}
|
|
13
|
+
async function closeSilently(app) {
|
|
14
|
+
try {
|
|
15
|
+
await app.close();
|
|
16
|
+
} catch {}
|
|
17
|
+
}
|
|
18
|
+
export class WebRuntimeHttpAdapterPortabilityHarness {
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
async assertPreservesMalformedCookieValues() {
|
|
23
|
+
let _initProto, _initClass;
|
|
24
|
+
let _CookieController;
|
|
25
|
+
class CookieController {
|
|
26
|
+
static {
|
|
27
|
+
({
|
|
28
|
+
e: [_initProto],
|
|
29
|
+
c: [_CookieController, _initClass]
|
|
30
|
+
} = _applyDecs(this, [Controller('/cookies')], [[Get('/'), 2, "readCookies"]]));
|
|
31
|
+
}
|
|
32
|
+
constructor() {
|
|
33
|
+
_initProto(this);
|
|
34
|
+
}
|
|
35
|
+
readCookies(_input, context) {
|
|
36
|
+
return context.request.cookies;
|
|
37
|
+
}
|
|
38
|
+
static {
|
|
39
|
+
_initClass();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
class AppModule {}
|
|
43
|
+
defineModule(AppModule, {
|
|
44
|
+
controllers: [_CookieController]
|
|
45
|
+
});
|
|
46
|
+
const app = await this.options.bootstrap(AppModule, {
|
|
47
|
+
cors: false
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
const response = await app.dispatch(new Request('https://runtime.test/cookies', {
|
|
51
|
+
headers: {
|
|
52
|
+
cookie: 'good=hello%20world; bad=%E0%A4%A'
|
|
53
|
+
}
|
|
54
|
+
}));
|
|
55
|
+
if (response.status !== 200) {
|
|
56
|
+
throw new Error(`${this.options.name} adapter changed malformed-cookie handling: expected 200 but received ${String(response.status)}.`);
|
|
57
|
+
}
|
|
58
|
+
const body = await response.json();
|
|
59
|
+
if (typeof body !== 'object' || body === null || !('bad' in body) || !('good' in body) || body.bad !== '%E0%A4%A' || body.good !== 'hello world' || Object.keys(body).length !== 2) {
|
|
60
|
+
throw new Error(`${this.options.name} adapter changed malformed-cookie normalization.`);
|
|
61
|
+
}
|
|
62
|
+
} finally {
|
|
63
|
+
await closeSilently(app);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async assertPreservesRawBodyForJsonAndText() {
|
|
67
|
+
let _initProto2, _initClass2;
|
|
68
|
+
let _WebhookController;
|
|
69
|
+
class WebhookController {
|
|
70
|
+
static {
|
|
71
|
+
({
|
|
72
|
+
e: [_initProto2],
|
|
73
|
+
c: [_WebhookController, _initClass2]
|
|
74
|
+
} = _applyDecs(this, [Controller('/webhooks')], [[Post('/json'), 2, "handleJson"], [Post('/text'), 2, "handleText"]]));
|
|
75
|
+
}
|
|
76
|
+
constructor() {
|
|
77
|
+
_initProto2(this);
|
|
78
|
+
}
|
|
79
|
+
handleJson(_input, context) {
|
|
80
|
+
return {
|
|
81
|
+
parsed: context.request.body,
|
|
82
|
+
raw: decodeUtf8(context.request.rawBody)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
handleText(_input, context) {
|
|
86
|
+
return {
|
|
87
|
+
parsed: context.request.body,
|
|
88
|
+
raw: decodeUtf8(context.request.rawBody)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
static {
|
|
92
|
+
_initClass2();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
class AppModule {}
|
|
96
|
+
defineModule(AppModule, {
|
|
97
|
+
controllers: [_WebhookController]
|
|
98
|
+
});
|
|
99
|
+
const app = await this.options.bootstrap(AppModule, {
|
|
100
|
+
cors: false,
|
|
101
|
+
rawBody: true
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
const [jsonResponse, textResponse] = await Promise.all([app.dispatch(new Request('https://runtime.test/webhooks/json', {
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
provider: 'stripe'
|
|
107
|
+
}),
|
|
108
|
+
headers: {
|
|
109
|
+
'content-type': 'application/json'
|
|
110
|
+
},
|
|
111
|
+
method: 'POST'
|
|
112
|
+
})), app.dispatch(new Request('https://runtime.test/webhooks/text', {
|
|
113
|
+
body: 'ping=1',
|
|
114
|
+
headers: {
|
|
115
|
+
'content-type': 'text/plain; charset=utf-8'
|
|
116
|
+
},
|
|
117
|
+
method: 'POST'
|
|
118
|
+
}))]);
|
|
119
|
+
if (jsonResponse.status !== 201 || textResponse.status !== 201) {
|
|
120
|
+
throw new Error(`${this.options.name} adapter changed rawBody response status semantics.`);
|
|
121
|
+
}
|
|
122
|
+
const [jsonBody, textBody] = await Promise.all([jsonResponse.json(), textResponse.json()]);
|
|
123
|
+
if (JSON.stringify(jsonBody) !== JSON.stringify({
|
|
124
|
+
parsed: {
|
|
125
|
+
provider: 'stripe'
|
|
126
|
+
},
|
|
127
|
+
raw: '{"provider":"stripe"}'
|
|
128
|
+
})) {
|
|
129
|
+
throw new Error(`${this.options.name} adapter changed JSON rawBody semantics.`);
|
|
130
|
+
}
|
|
131
|
+
if (JSON.stringify(textBody) !== JSON.stringify({
|
|
132
|
+
parsed: 'ping=1',
|
|
133
|
+
raw: 'ping=1'
|
|
134
|
+
})) {
|
|
135
|
+
throw new Error(`${this.options.name} adapter changed text rawBody semantics.`);
|
|
136
|
+
}
|
|
137
|
+
} finally {
|
|
138
|
+
await closeSilently(app);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async assertExcludesRawBodyForMultipart() {
|
|
142
|
+
let _initProto3, _initClass3;
|
|
143
|
+
let _UploadController;
|
|
144
|
+
class UploadController {
|
|
145
|
+
static {
|
|
146
|
+
({
|
|
147
|
+
e: [_initProto3],
|
|
148
|
+
c: [_UploadController, _initClass3]
|
|
149
|
+
} = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
|
|
150
|
+
}
|
|
151
|
+
constructor() {
|
|
152
|
+
_initProto3(this);
|
|
153
|
+
}
|
|
154
|
+
upload(_input, context) {
|
|
155
|
+
return {
|
|
156
|
+
body: context.request.body,
|
|
157
|
+
fileCount: context.request.files?.length ?? 0,
|
|
158
|
+
hasRawBody: context.request.rawBody !== undefined
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
static {
|
|
162
|
+
_initClass3();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
class AppModule {}
|
|
166
|
+
defineModule(AppModule, {
|
|
167
|
+
controllers: [_UploadController]
|
|
168
|
+
});
|
|
169
|
+
const app = await this.options.bootstrap(AppModule, {
|
|
170
|
+
cors: false,
|
|
171
|
+
rawBody: true
|
|
172
|
+
});
|
|
173
|
+
try {
|
|
174
|
+
const form = new FormData();
|
|
175
|
+
form.set('name', 'Ada');
|
|
176
|
+
form.set('payload', new Blob(['hello'], {
|
|
177
|
+
type: 'text/plain'
|
|
178
|
+
}), 'payload.txt');
|
|
179
|
+
const response = await app.dispatch(new Request('https://runtime.test/uploads', {
|
|
180
|
+
body: form,
|
|
181
|
+
method: 'POST'
|
|
182
|
+
}));
|
|
183
|
+
if (response.status !== 201) {
|
|
184
|
+
throw new Error(`${this.options.name} adapter changed multipart response status semantics.`);
|
|
185
|
+
}
|
|
186
|
+
const body = await response.json();
|
|
187
|
+
if (JSON.stringify(body) !== JSON.stringify({
|
|
188
|
+
body: {
|
|
189
|
+
name: 'Ada'
|
|
190
|
+
},
|
|
191
|
+
fileCount: 1,
|
|
192
|
+
hasRawBody: false
|
|
193
|
+
})) {
|
|
194
|
+
throw new Error(`${this.options.name} adapter changed multipart rawBody semantics.`);
|
|
195
|
+
}
|
|
196
|
+
} finally {
|
|
197
|
+
await closeSilently(app);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async assertSupportsSseStreaming() {
|
|
201
|
+
let _initProto4, _initClass4;
|
|
202
|
+
let _EventsController;
|
|
203
|
+
class EventsController {
|
|
204
|
+
static {
|
|
205
|
+
({
|
|
206
|
+
e: [_initProto4],
|
|
207
|
+
c: [_EventsController, _initClass4]
|
|
208
|
+
} = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
|
|
209
|
+
}
|
|
210
|
+
constructor() {
|
|
211
|
+
_initProto4(this);
|
|
212
|
+
}
|
|
213
|
+
stream(_input, context) {
|
|
214
|
+
const stream = new SseResponse(context);
|
|
215
|
+
stream.comment('connected');
|
|
216
|
+
stream.send({
|
|
217
|
+
ready: true
|
|
218
|
+
}, {
|
|
219
|
+
event: 'ready',
|
|
220
|
+
id: 'evt-1'
|
|
221
|
+
});
|
|
222
|
+
stream.close();
|
|
223
|
+
return stream;
|
|
224
|
+
}
|
|
225
|
+
static {
|
|
226
|
+
_initClass4();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
class AppModule {}
|
|
230
|
+
defineModule(AppModule, {
|
|
231
|
+
controllers: [_EventsController]
|
|
232
|
+
});
|
|
233
|
+
const app = await this.options.bootstrap(AppModule, {
|
|
234
|
+
cors: false
|
|
235
|
+
});
|
|
236
|
+
try {
|
|
237
|
+
const response = await app.dispatch(new Request('https://runtime.test/events', {
|
|
238
|
+
headers: {
|
|
239
|
+
accept: 'text/event-stream'
|
|
240
|
+
}
|
|
241
|
+
}));
|
|
242
|
+
const body = await response.text();
|
|
243
|
+
if (response.status !== 200) {
|
|
244
|
+
throw new Error(`${this.options.name} adapter changed SSE response status semantics.`);
|
|
245
|
+
}
|
|
246
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
247
|
+
if (!contentType.includes('text/event-stream')) {
|
|
248
|
+
throw new Error(`${this.options.name} adapter does not expose text/event-stream content-type.`);
|
|
249
|
+
}
|
|
250
|
+
if (!body.includes('event: ready') || !body.includes('data: {"ready":true}')) {
|
|
251
|
+
throw new Error(`${this.options.name} adapter changed SSE body framing.`);
|
|
252
|
+
}
|
|
253
|
+
} finally {
|
|
254
|
+
await closeSilently(app);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
export function createWebRuntimeHttpAdapterPortabilityHarness(options) {
|
|
259
|
+
return new WebRuntimeHttpAdapterPortabilityHarness(options);
|
|
260
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Mock } from 'vitest';
|
|
2
|
+
import type { MaybePromise, Token } from '@fluojs/core';
|
|
3
|
+
import type { ClassType, Container, ForwardRefFn, OptionalToken, Provider } from '@fluojs/di';
|
|
4
|
+
import type { BootstrapResult, BootstrapModuleOptions, ModuleType } from '@fluojs/runtime';
|
|
5
|
+
import type { Guard, Interceptor } from '@fluojs/http';
|
|
6
|
+
import type { RequestBuilder, TestPrincipal, TestRequest, TestRequestWithOptions, TestResponse } from './http.js';
|
|
7
|
+
/**
|
|
8
|
+
* Bootstrap options accepted by `createTestingModule(...)`.
|
|
9
|
+
*/
|
|
10
|
+
export interface TestingModuleOptions extends BootstrapModuleOptions {
|
|
11
|
+
rootModule: ModuleType;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Optional request extras accepted by `TestApp.request(...)` overloads.
|
|
15
|
+
*/
|
|
16
|
+
export interface TestRequestOptions {
|
|
17
|
+
principal?: TestPrincipal;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Compiled testing-module facade that exposes sync/async resolution and request dispatch helpers.
|
|
21
|
+
*/
|
|
22
|
+
export interface TestingModuleRef extends BootstrapResult {
|
|
23
|
+
has(token: Token): boolean;
|
|
24
|
+
get<T>(token: Token<T>): T;
|
|
25
|
+
resolve<T>(token: Token<T>): Promise<T>;
|
|
26
|
+
resolveAll<T>(tokens: Token<T>[]): Promise<T[]>;
|
|
27
|
+
dispatch(request: TestRequestWithOptions): Promise<TestResponse>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fluent override builder returned by `overrideProvider(token)`.
|
|
31
|
+
*/
|
|
32
|
+
export interface OverrideProviderBuilder<T> {
|
|
33
|
+
useValue(value: T): TestingModuleBuilder;
|
|
34
|
+
useClass(cls: ClassType<T>): TestingModuleBuilder;
|
|
35
|
+
useFactory(factory: (...args: unknown[]) => MaybePromise<T>, inject?: Array<Token | ForwardRefFn | OptionalToken>): TestingModuleBuilder;
|
|
36
|
+
useExisting(token: Token<T>): TestingModuleBuilder;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Builder contract for compiling an application module graph with targeted test overrides.
|
|
40
|
+
*/
|
|
41
|
+
export interface TestingModuleBuilder {
|
|
42
|
+
compile(): Promise<TestingModuleRef>;
|
|
43
|
+
overrideProvider<T>(token: Token<T>): OverrideProviderBuilder<T>;
|
|
44
|
+
overrideProvider<T>(token: Token<T>, provider: Provider<T>): this;
|
|
45
|
+
overrideProvider<T>(token: Token<T>, value: T): this;
|
|
46
|
+
overrideProviders(overrides: Array<[Token, unknown]>): this;
|
|
47
|
+
overrideGuard(guard: Token<Guard>, fake?: Partial<Guard>): this;
|
|
48
|
+
overrideInterceptor(interceptor: Token<Interceptor>, fake?: Partial<Interceptor>): this;
|
|
49
|
+
overrideFilter(filter: Token<unknown>, fake?: unknown): this;
|
|
50
|
+
overrideModule(module: ModuleType, replacement: ModuleType): this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Minimal testing bootstrap snapshot used by lower-level helpers.
|
|
54
|
+
*/
|
|
55
|
+
export interface TestingBootstrapResult {
|
|
56
|
+
container: Container;
|
|
57
|
+
modules: BootstrapResult['modules'];
|
|
58
|
+
rootModule: ModuleType;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Lightweight request-dispatch facade for integration-style tests.
|
|
62
|
+
*/
|
|
63
|
+
export interface TestApp {
|
|
64
|
+
request(method: string, path: string, options?: TestRequestOptions): RequestBuilder;
|
|
65
|
+
request(request: TestRequest): RequestBuilder;
|
|
66
|
+
request(request: TestRequestWithOptions): RequestBuilder;
|
|
67
|
+
dispatch(request: TestRequestWithOptions): Promise<TestResponse>;
|
|
68
|
+
close(): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Shallow method-mocked version of a type where function properties become `vitest` mocks.
|
|
72
|
+
*/
|
|
73
|
+
export type DeepMocked<T> = {
|
|
74
|
+
[K in keyof T]: T[K] extends (...args: infer A) => infer R ? Mock<(...args: A) => R> & T[K] : T[K];
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/vitest.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type FluoBabelDecoratorsPlugin } from './babel-decorators-plugin.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a Vitest-compatible Babel plugin that enables fluo decorator support
|
|
4
|
+
* using the nearest configuration file.
|
|
5
|
+
*
|
|
6
|
+
* @returns A {@link FluoBabelDecoratorsPlugin} instance.
|
|
7
|
+
*/
|
|
8
|
+
export declare function fluoBabelDecoratorsPlugin(): FluoBabelDecoratorsPlugin;
|
|
9
|
+
//# sourceMappingURL=vitest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,yBAAyB,EAE/B,MAAM,8BAA8B,CAAC;AAEtC;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,yBAAyB,CAErE"}
|
package/dist/vitest.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createFluoBabelDecoratorsPlugin, resolveNearestBabelConfigFile } from './babel-decorators-plugin.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Vitest-compatible Babel plugin that enables fluo decorator support
|
|
5
|
+
* using the nearest configuration file.
|
|
6
|
+
*
|
|
7
|
+
* @returns A {@link FluoBabelDecoratorsPlugin} instance.
|
|
8
|
+
*/
|
|
9
|
+
export function fluoBabelDecoratorsPlugin() {
|
|
10
|
+
return createFluoBabelDecoratorsPlugin(resolveNearestBabelConfigFile);
|
|
11
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fluojs/testing",
|
|
3
|
+
"description": "Testing module construction and provider override utilities for Fluo applications.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"fluo",
|
|
6
|
+
"testing",
|
|
7
|
+
"test",
|
|
8
|
+
"mock",
|
|
9
|
+
"override",
|
|
10
|
+
"module-builder"
|
|
11
|
+
],
|
|
12
|
+
"version": "1.0.0-beta.1",
|
|
13
|
+
"private": false,
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/fluojs/fluo.git",
|
|
18
|
+
"directory": "packages/testing"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.0.0"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./app": {
|
|
33
|
+
"types": "./dist/app.d.ts",
|
|
34
|
+
"import": "./dist/app.js"
|
|
35
|
+
},
|
|
36
|
+
"./module": {
|
|
37
|
+
"types": "./dist/module.d.ts",
|
|
38
|
+
"import": "./dist/module.js"
|
|
39
|
+
},
|
|
40
|
+
"./http": {
|
|
41
|
+
"types": "./dist/http.d.ts",
|
|
42
|
+
"import": "./dist/http.js"
|
|
43
|
+
},
|
|
44
|
+
"./mock": {
|
|
45
|
+
"types": "./dist/mock.d.ts",
|
|
46
|
+
"import": "./dist/mock.js"
|
|
47
|
+
},
|
|
48
|
+
"./platform-conformance": {
|
|
49
|
+
"types": "./dist/conformance/platform-conformance.d.ts",
|
|
50
|
+
"import": "./dist/conformance/platform-conformance.js"
|
|
51
|
+
},
|
|
52
|
+
"./http-adapter-portability": {
|
|
53
|
+
"types": "./dist/portability/http-adapter-portability.d.ts",
|
|
54
|
+
"import": "./dist/portability/http-adapter-portability.js"
|
|
55
|
+
},
|
|
56
|
+
"./web-runtime-adapter-portability": {
|
|
57
|
+
"types": "./dist/portability/web-runtime-adapter-portability.d.ts",
|
|
58
|
+
"import": "./dist/portability/web-runtime-adapter-portability.js"
|
|
59
|
+
},
|
|
60
|
+
"./fetch-style-websocket-conformance": {
|
|
61
|
+
"types": "./dist/conformance/fetch-style-websocket-conformance.d.ts",
|
|
62
|
+
"import": "./dist/conformance/fetch-style-websocket-conformance.js"
|
|
63
|
+
},
|
|
64
|
+
"./types": {
|
|
65
|
+
"types": "./dist/types.d.ts",
|
|
66
|
+
"import": "./dist/types.js"
|
|
67
|
+
},
|
|
68
|
+
"./vitest": {
|
|
69
|
+
"types": "./dist/vitest.d.ts",
|
|
70
|
+
"import": "./dist/vitest.js"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"main": "./dist/index.js",
|
|
74
|
+
"types": "./dist/index.d.ts",
|
|
75
|
+
"files": [
|
|
76
|
+
"dist"
|
|
77
|
+
],
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"@fluojs/di": "^1.0.0-beta.1",
|
|
80
|
+
"@fluojs/core": "^1.0.0-beta.1",
|
|
81
|
+
"@fluojs/http": "^1.0.0-beta.1",
|
|
82
|
+
"@fluojs/runtime": "^1.0.0-beta.1",
|
|
83
|
+
"@fluojs/config": "^1.0.0-beta.1"
|
|
84
|
+
},
|
|
85
|
+
"peerDependencies": {
|
|
86
|
+
"@babel/core": ">=7.0.0",
|
|
87
|
+
"vitest": "^3.0.8"
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"vitest": "^3.2.4",
|
|
91
|
+
"@fluojs/platform-nodejs": "^1.0.0-beta.1",
|
|
92
|
+
"@fluojs/platform-fastify": "^1.0.0-beta.1",
|
|
93
|
+
"@fluojs/platform-express": "^1.0.0-beta.1"
|
|
94
|
+
},
|
|
95
|
+
"scripts": {
|
|
96
|
+
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|
|
97
|
+
"build": "pnpm exec babel src --extensions .ts --ignore 'src/**/*.test.ts' --out-dir dist --config-file ../../tooling/babel/babel.config.cjs && pnpm exec tsc -p tsconfig.build.json",
|
|
98
|
+
"typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
|
|
99
|
+
"test": "pnpm exec vitest run -c vitest.config.ts",
|
|
100
|
+
"test:watch": "pnpm exec vitest -c vitest.config.ts"
|
|
101
|
+
}
|
|
102
|
+
}
|