@frontmcp/testing 0.5.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/LICENSE +201 -0
- package/README.md +1358 -0
- package/jest-preset.js +61 -0
- package/package.json +94 -0
- package/src/assertions/index.d.ts +5 -0
- package/src/assertions/index.js +18 -0
- package/src/assertions/index.js.map +1 -0
- package/src/assertions/mcp-assertions.d.ts +81 -0
- package/src/assertions/mcp-assertions.js +220 -0
- package/src/assertions/mcp-assertions.js.map +1 -0
- package/src/auth/auth-headers.d.ts +29 -0
- package/src/auth/auth-headers.js +62 -0
- package/src/auth/auth-headers.js.map +1 -0
- package/src/auth/index.d.ts +9 -0
- package/src/auth/index.js +15 -0
- package/src/auth/index.js.map +1 -0
- package/src/auth/token-factory.d.ts +94 -0
- package/src/auth/token-factory.js +181 -0
- package/src/auth/token-factory.js.map +1 -0
- package/src/auth/user-fixtures.d.ts +26 -0
- package/src/auth/user-fixtures.js +92 -0
- package/src/auth/user-fixtures.js.map +1 -0
- package/src/client/index.d.ts +7 -0
- package/src/client/index.js +12 -0
- package/src/client/index.js.map +1 -0
- package/src/client/mcp-test-client.builder.d.ts +72 -0
- package/src/client/mcp-test-client.builder.js +111 -0
- package/src/client/mcp-test-client.builder.js.map +1 -0
- package/src/client/mcp-test-client.d.ts +360 -0
- package/src/client/mcp-test-client.js +929 -0
- package/src/client/mcp-test-client.js.map +1 -0
- package/src/client/mcp-test-client.types.d.ts +216 -0
- package/src/client/mcp-test-client.types.js +7 -0
- package/src/client/mcp-test-client.types.js.map +1 -0
- package/src/errors/index.d.ts +45 -0
- package/src/errors/index.js +85 -0
- package/src/errors/index.js.map +1 -0
- package/src/expect.d.ts +67 -0
- package/src/expect.js +31 -0
- package/src/expect.js.map +1 -0
- package/src/fixtures/fixture-types.d.ts +166 -0
- package/src/fixtures/fixture-types.js +7 -0
- package/src/fixtures/fixture-types.js.map +1 -0
- package/src/fixtures/index.d.ts +7 -0
- package/src/fixtures/index.js +16 -0
- package/src/fixtures/index.js.map +1 -0
- package/src/fixtures/test-fixture.d.ts +41 -0
- package/src/fixtures/test-fixture.js +280 -0
- package/src/fixtures/test-fixture.js.map +1 -0
- package/src/http-mock/http-mock.d.ts +84 -0
- package/src/http-mock/http-mock.js +544 -0
- package/src/http-mock/http-mock.js.map +1 -0
- package/src/http-mock/http-mock.types.d.ts +124 -0
- package/src/http-mock/http-mock.types.js +10 -0
- package/src/http-mock/http-mock.types.js.map +1 -0
- package/src/http-mock/index.d.ts +6 -0
- package/src/http-mock/index.js +11 -0
- package/src/http-mock/index.js.map +1 -0
- package/src/index.d.ts +65 -0
- package/src/index.js +128 -0
- package/src/index.js.map +1 -0
- package/src/interceptor/index.d.ts +7 -0
- package/src/interceptor/index.js +15 -0
- package/src/interceptor/index.js.map +1 -0
- package/src/interceptor/interceptor-chain.d.ts +77 -0
- package/src/interceptor/interceptor-chain.js +207 -0
- package/src/interceptor/interceptor-chain.js.map +1 -0
- package/src/interceptor/interceptor.types.d.ts +131 -0
- package/src/interceptor/interceptor.types.js +7 -0
- package/src/interceptor/interceptor.types.js.map +1 -0
- package/src/interceptor/mock-registry.d.ts +82 -0
- package/src/interceptor/mock-registry.js +189 -0
- package/src/interceptor/mock-registry.js.map +1 -0
- package/src/matchers/index.d.ts +7 -0
- package/src/matchers/index.js +12 -0
- package/src/matchers/index.js.map +1 -0
- package/src/matchers/matcher-types.d.ts +266 -0
- package/src/matchers/matcher-types.js +10 -0
- package/src/matchers/matcher-types.js.map +1 -0
- package/src/matchers/mcp-matchers.d.ts +47 -0
- package/src/matchers/mcp-matchers.js +391 -0
- package/src/matchers/mcp-matchers.js.map +1 -0
- package/src/playwright/index.d.ts +37 -0
- package/src/playwright/index.js +49 -0
- package/src/playwright/index.js.map +1 -0
- package/src/server/index.d.ts +6 -0
- package/src/server/index.js +10 -0
- package/src/server/index.js.map +1 -0
- package/src/server/test-server.d.ts +99 -0
- package/src/server/test-server.js +286 -0
- package/src/server/test-server.js.map +1 -0
- package/src/setup.d.ts +22 -0
- package/src/setup.js +30 -0
- package/src/setup.js.map +1 -0
- package/src/transport/index.d.ts +6 -0
- package/src/transport/index.js +10 -0
- package/src/transport/index.js.map +1 -0
- package/src/transport/streamable-http.transport.d.ts +65 -0
- package/src/transport/streamable-http.transport.js +432 -0
- package/src/transport/streamable-http.transport.js.map +1 -0
- package/src/transport/transport.interface.d.ts +124 -0
- package/src/transport/transport.interface.js +7 -0
- package/src/transport/transport.interface.js.map +1 -0
- package/src/ui/index.d.ts +17 -0
- package/src/ui/index.js +23 -0
- package/src/ui/index.js.map +1 -0
- package/src/ui/ui-assertions.d.ts +94 -0
- package/src/ui/ui-assertions.js +215 -0
- package/src/ui/ui-assertions.js.map +1 -0
- package/src/ui/ui-matchers.d.ts +39 -0
- package/src/ui/ui-matchers.js +275 -0
- package/src/ui/ui-matchers.js.map +1 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file mcp-matchers.ts
|
|
4
|
+
* @description Custom Jest matchers for MCP testing
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { test, expect } from '@frontmcp/testing';
|
|
9
|
+
*
|
|
10
|
+
* test('tools work', async ({ mcp }) => {
|
|
11
|
+
* const tools = await mcp.tools.list();
|
|
12
|
+
* expect(tools).toContainTool('my-tool');
|
|
13
|
+
*
|
|
14
|
+
* const result = await mcp.tools.call('my-tool', {});
|
|
15
|
+
* expect(result).toBeSuccessful();
|
|
16
|
+
* expect(result).toHaveTextContent();
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.mcpMatchers = void 0;
|
|
22
|
+
const ui_matchers_1 = require("../ui/ui-matchers");
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
24
|
+
// TOOL MATCHERS
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
26
|
+
/**
|
|
27
|
+
* Check if tools array contains a tool with the given name
|
|
28
|
+
*/
|
|
29
|
+
const toContainTool = function (received, toolName) {
|
|
30
|
+
const tools = received;
|
|
31
|
+
if (!Array.isArray(tools)) {
|
|
32
|
+
return {
|
|
33
|
+
pass: false,
|
|
34
|
+
message: () => `Expected an array of tools, but received ${typeof received}`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const pass = tools.some((t) => t.name === toolName);
|
|
38
|
+
const availableTools = tools.map((t) => t.name).join(', ');
|
|
39
|
+
return {
|
|
40
|
+
pass,
|
|
41
|
+
message: () => pass
|
|
42
|
+
? `Expected tools not to contain "${toolName}"`
|
|
43
|
+
: `Expected tools to contain "${toolName}", but got: [${availableTools}]`,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Check if result is successful (not an error)
|
|
48
|
+
*/
|
|
49
|
+
const toBeSuccessful = function (received) {
|
|
50
|
+
const result = received;
|
|
51
|
+
if (typeof result !== 'object' || result === null || !('isSuccess' in result)) {
|
|
52
|
+
return {
|
|
53
|
+
pass: false,
|
|
54
|
+
message: () => `Expected a result wrapper object with isSuccess property`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const pass = result.isSuccess;
|
|
58
|
+
return {
|
|
59
|
+
pass,
|
|
60
|
+
message: () => pass
|
|
61
|
+
? 'Expected result not to be successful'
|
|
62
|
+
: `Expected result to be successful, but got error: ${result.error?.message ?? 'unknown error'}`,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Check if result is an error, optionally with a specific error code
|
|
67
|
+
*/
|
|
68
|
+
const toBeError = function (received, expectedCode) {
|
|
69
|
+
const result = received;
|
|
70
|
+
if (typeof result !== 'object' || result === null || !('isError' in result)) {
|
|
71
|
+
return {
|
|
72
|
+
pass: false,
|
|
73
|
+
message: () => `Expected a result wrapper object with isError property`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
let pass = result.isError;
|
|
77
|
+
if (pass && expectedCode !== undefined) {
|
|
78
|
+
pass = result.error?.code === expectedCode;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
pass,
|
|
82
|
+
message: () => {
|
|
83
|
+
if (!result.isError) {
|
|
84
|
+
return 'Expected result to be an error, but it was successful';
|
|
85
|
+
}
|
|
86
|
+
if (expectedCode !== undefined && result.error?.code !== expectedCode) {
|
|
87
|
+
return `Expected error code ${expectedCode}, but got ${result.error?.code}`;
|
|
88
|
+
}
|
|
89
|
+
return 'Expected result not to be an error';
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Check if tool result has text content, optionally containing specific text
|
|
95
|
+
*/
|
|
96
|
+
const toHaveTextContent = function (received, expectedText) {
|
|
97
|
+
const result = received;
|
|
98
|
+
if (typeof result !== 'object' || result === null || !('hasTextContent' in result)) {
|
|
99
|
+
return {
|
|
100
|
+
pass: false,
|
|
101
|
+
message: () => `Expected a ToolResultWrapper object with hasTextContent method`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const hasText = result.hasTextContent();
|
|
105
|
+
const text = result.text();
|
|
106
|
+
let pass = hasText;
|
|
107
|
+
if (pass && expectedText !== undefined) {
|
|
108
|
+
pass = text?.includes(expectedText) ?? false;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
pass,
|
|
112
|
+
message: () => {
|
|
113
|
+
if (!hasText) {
|
|
114
|
+
return 'Expected result to have text content';
|
|
115
|
+
}
|
|
116
|
+
if (expectedText !== undefined && !text?.includes(expectedText)) {
|
|
117
|
+
return `Expected text to contain "${expectedText}", but got: "${text}"`;
|
|
118
|
+
}
|
|
119
|
+
return 'Expected result not to have text content';
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Check if tool result has image content
|
|
125
|
+
*/
|
|
126
|
+
const toHaveImageContent = function (received) {
|
|
127
|
+
const result = received;
|
|
128
|
+
if (typeof result !== 'object' || result === null || !('hasImageContent' in result)) {
|
|
129
|
+
return {
|
|
130
|
+
pass: false,
|
|
131
|
+
message: () => `Expected a ToolResultWrapper object with hasImageContent method`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const pass = result.hasImageContent();
|
|
135
|
+
return {
|
|
136
|
+
pass,
|
|
137
|
+
message: () => (pass ? 'Expected result not to have image content' : 'Expected result to have image content'),
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Check if tool result has resource content
|
|
142
|
+
*/
|
|
143
|
+
const toHaveResourceContent = function (received) {
|
|
144
|
+
const result = received;
|
|
145
|
+
if (typeof result !== 'object' || result === null || !('hasResourceContent' in result)) {
|
|
146
|
+
return {
|
|
147
|
+
pass: false,
|
|
148
|
+
message: () => `Expected a ToolResultWrapper object with hasResourceContent method`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const pass = result.hasResourceContent();
|
|
152
|
+
return {
|
|
153
|
+
pass,
|
|
154
|
+
message: () => (pass ? 'Expected result not to have resource content' : 'Expected result to have resource content'),
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
158
|
+
// RESOURCE MATCHERS
|
|
159
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
160
|
+
/**
|
|
161
|
+
* Check if resources array contains a resource with the given URI
|
|
162
|
+
*/
|
|
163
|
+
const toContainResource = function (received, uri) {
|
|
164
|
+
const resources = received;
|
|
165
|
+
if (!Array.isArray(resources)) {
|
|
166
|
+
return {
|
|
167
|
+
pass: false,
|
|
168
|
+
message: () => `Expected an array of resources, but received ${typeof received}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const pass = resources.some((r) => r.uri === uri);
|
|
172
|
+
const availableUris = resources.map((r) => r.uri).join(', ');
|
|
173
|
+
return {
|
|
174
|
+
pass,
|
|
175
|
+
message: () => pass
|
|
176
|
+
? `Expected resources not to contain "${uri}"`
|
|
177
|
+
: `Expected resources to contain "${uri}", but got: [${availableUris}]`,
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Check if resource templates array contains a template with the given URI template
|
|
182
|
+
*/
|
|
183
|
+
const toContainResourceTemplate = function (received, uriTemplate) {
|
|
184
|
+
const templates = received;
|
|
185
|
+
if (!Array.isArray(templates)) {
|
|
186
|
+
return {
|
|
187
|
+
pass: false,
|
|
188
|
+
message: () => `Expected an array of resource templates, but received ${typeof received}`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const pass = templates.some((t) => t.uriTemplate === uriTemplate);
|
|
192
|
+
const availableTemplates = templates.map((t) => t.uriTemplate).join(', ');
|
|
193
|
+
return {
|
|
194
|
+
pass,
|
|
195
|
+
message: () => pass
|
|
196
|
+
? `Expected templates not to contain "${uriTemplate}"`
|
|
197
|
+
: `Expected templates to contain "${uriTemplate}", but got: [${availableTemplates}]`,
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Check if resource content has a specific MIME type
|
|
202
|
+
*/
|
|
203
|
+
const toHaveMimeType = function (received, mimeType) {
|
|
204
|
+
const result = received;
|
|
205
|
+
if (typeof result !== 'object' || result === null || !('hasMimeType' in result)) {
|
|
206
|
+
return {
|
|
207
|
+
pass: false,
|
|
208
|
+
message: () => `Expected a ResourceContentWrapper object with hasMimeType method`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const pass = result.hasMimeType(mimeType);
|
|
212
|
+
const actualMimeType = result.mimeType();
|
|
213
|
+
return {
|
|
214
|
+
pass,
|
|
215
|
+
message: () => pass
|
|
216
|
+
? `Expected content not to have MIME type "${mimeType}"`
|
|
217
|
+
: `Expected MIME type "${mimeType}", but got "${actualMimeType}"`,
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
221
|
+
// PROMPT MATCHERS
|
|
222
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
223
|
+
/**
|
|
224
|
+
* Check if prompts array contains a prompt with the given name
|
|
225
|
+
*/
|
|
226
|
+
const toContainPrompt = function (received, name) {
|
|
227
|
+
const prompts = received;
|
|
228
|
+
if (!Array.isArray(prompts)) {
|
|
229
|
+
return {
|
|
230
|
+
pass: false,
|
|
231
|
+
message: () => `Expected an array of prompts, but received ${typeof received}`,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const pass = prompts.some((p) => p.name === name);
|
|
235
|
+
const availablePrompts = prompts.map((p) => p.name).join(', ');
|
|
236
|
+
return {
|
|
237
|
+
pass,
|
|
238
|
+
message: () => pass
|
|
239
|
+
? `Expected prompts not to contain "${name}"`
|
|
240
|
+
: `Expected prompts to contain "${name}", but got: [${availablePrompts}]`,
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Check if prompt result has a specific number of messages
|
|
245
|
+
*/
|
|
246
|
+
const toHaveMessages = function (received, count) {
|
|
247
|
+
const result = received;
|
|
248
|
+
if (typeof result !== 'object' || result === null || !('messages' in result)) {
|
|
249
|
+
return {
|
|
250
|
+
pass: false,
|
|
251
|
+
message: () => `Expected a PromptResultWrapper object with messages property`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
const actualCount = result.messages?.length ?? 0;
|
|
255
|
+
const pass = actualCount === count;
|
|
256
|
+
return {
|
|
257
|
+
pass,
|
|
258
|
+
message: () => pass
|
|
259
|
+
? `Expected prompt not to have ${count} messages`
|
|
260
|
+
: `Expected prompt to have ${count} messages, but got ${actualCount}`,
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
264
|
+
// PROTOCOL MATCHERS
|
|
265
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
266
|
+
/**
|
|
267
|
+
* Check if response is valid JSON-RPC 2.0
|
|
268
|
+
* A valid JSON-RPC 2.0 response must have:
|
|
269
|
+
* - jsonrpc: "2.0"
|
|
270
|
+
* - id (matching the request, can be null for notifications)
|
|
271
|
+
* - Either result OR error (but not both)
|
|
272
|
+
*/
|
|
273
|
+
const toBeValidJsonRpc = function (received) {
|
|
274
|
+
const response = received;
|
|
275
|
+
if (typeof response !== 'object' || response === null) {
|
|
276
|
+
return {
|
|
277
|
+
pass: false,
|
|
278
|
+
message: () => `Expected an object, but received ${typeof received}`,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const hasJsonRpc = response['jsonrpc'] === '2.0';
|
|
282
|
+
const hasId = 'id' in response;
|
|
283
|
+
const hasResult = 'result' in response;
|
|
284
|
+
const hasError = 'error' in response;
|
|
285
|
+
const hasExactlyOneResultOrError = (hasResult || hasError) && !(hasResult && hasError);
|
|
286
|
+
const pass = hasJsonRpc && hasId && hasExactlyOneResultOrError;
|
|
287
|
+
return {
|
|
288
|
+
pass,
|
|
289
|
+
message: () => {
|
|
290
|
+
if (pass) {
|
|
291
|
+
return 'Expected response not to be valid JSON-RPC';
|
|
292
|
+
}
|
|
293
|
+
const issues = [];
|
|
294
|
+
if (!hasJsonRpc)
|
|
295
|
+
issues.push('missing or invalid "jsonrpc": "2.0"');
|
|
296
|
+
if (!hasId)
|
|
297
|
+
issues.push('missing "id" field');
|
|
298
|
+
if (!hasExactlyOneResultOrError) {
|
|
299
|
+
if (!hasResult && !hasError)
|
|
300
|
+
issues.push('missing "result" or "error"');
|
|
301
|
+
else
|
|
302
|
+
issues.push('cannot have both "result" and "error"');
|
|
303
|
+
}
|
|
304
|
+
return `Expected valid JSON-RPC 2.0 response: ${issues.join(', ')}`;
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Check if JSON-RPC response has a result
|
|
310
|
+
*/
|
|
311
|
+
const toHaveResult = function (received) {
|
|
312
|
+
const response = received;
|
|
313
|
+
if (typeof response !== 'object' || response === null) {
|
|
314
|
+
return {
|
|
315
|
+
pass: false,
|
|
316
|
+
message: () => `Expected an object, but received ${typeof received}`,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const pass = 'result' in response;
|
|
320
|
+
return {
|
|
321
|
+
pass,
|
|
322
|
+
message: () => (pass ? 'Expected response not to have result' : 'Expected response to have result'),
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* Check if JSON-RPC response has an error
|
|
327
|
+
*/
|
|
328
|
+
const toHaveError = function (received) {
|
|
329
|
+
const response = received;
|
|
330
|
+
if (typeof response !== 'object' || response === null) {
|
|
331
|
+
return {
|
|
332
|
+
pass: false,
|
|
333
|
+
message: () => `Expected an object, but received ${typeof received}`,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const pass = 'error' in response;
|
|
337
|
+
return {
|
|
338
|
+
pass,
|
|
339
|
+
message: () => (pass ? 'Expected response not to have error' : 'Expected response to have error'),
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Check if JSON-RPC response has a specific error code
|
|
344
|
+
*/
|
|
345
|
+
const toHaveErrorCode = function (received, code) {
|
|
346
|
+
const response = received;
|
|
347
|
+
if (typeof response !== 'object' || response === null) {
|
|
348
|
+
return {
|
|
349
|
+
pass: false,
|
|
350
|
+
message: () => `Expected an object, but received ${typeof received}`,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const actualCode = response.error?.code;
|
|
354
|
+
const pass = actualCode === code;
|
|
355
|
+
return {
|
|
356
|
+
pass,
|
|
357
|
+
message: () => pass
|
|
358
|
+
? `Expected response not to have error code ${code}`
|
|
359
|
+
: `Expected error code ${code}, but got ${actualCode ?? 'no error'}`,
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
363
|
+
// EXPORTS
|
|
364
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
365
|
+
/**
|
|
366
|
+
* All MCP matchers as an object for expect.extend()
|
|
367
|
+
*/
|
|
368
|
+
exports.mcpMatchers = {
|
|
369
|
+
// Tool matchers
|
|
370
|
+
toContainTool,
|
|
371
|
+
toBeSuccessful,
|
|
372
|
+
toBeError,
|
|
373
|
+
toHaveTextContent,
|
|
374
|
+
toHaveImageContent,
|
|
375
|
+
toHaveResourceContent,
|
|
376
|
+
// Resource matchers
|
|
377
|
+
toContainResource,
|
|
378
|
+
toContainResourceTemplate,
|
|
379
|
+
toHaveMimeType,
|
|
380
|
+
// Prompt matchers
|
|
381
|
+
toContainPrompt,
|
|
382
|
+
toHaveMessages,
|
|
383
|
+
// Protocol matchers
|
|
384
|
+
toBeValidJsonRpc,
|
|
385
|
+
toHaveResult,
|
|
386
|
+
toHaveError,
|
|
387
|
+
toHaveErrorCode,
|
|
388
|
+
// UI matchers (for testing tool UI responses)
|
|
389
|
+
...ui_matchers_1.uiMatchers,
|
|
390
|
+
};
|
|
391
|
+
//# sourceMappingURL=mcp-matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-matchers.js","sourceRoot":"","sources":["../../../src/matchers/mcp-matchers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file mcp-matchers.ts\n * @description Custom Jest matchers for MCP testing\n *\n * @example\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test('tools work', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n *\n * const result = await mcp.tools.call('my-tool', {});\n * expect(result).toBeSuccessful();\n * expect(result).toHaveTextContent();\n * });\n * ```\n */\n\nimport type { MatcherFunction } from 'expect';\nimport type { Tool, Resource, ResourceTemplate, Prompt } from '@modelcontextprotocol/sdk/types.js';\nimport type { ToolResultWrapper, ResourceContentWrapper, PromptResultWrapper } from '../client/mcp-test-client.types';\nimport { uiMatchers } from '../ui/ui-matchers';\n\n// ═══════════════════════════════════════════════════════════════════\n// HELPER TYPES\n// ═══════════════════════════════════════════════════════════════════\n\ntype ResultWrapper = ToolResultWrapper | ResourceContentWrapper;\n\n// ═══════════════════════════════════════════════════════════════════\n// TOOL MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if tools array contains a tool with the given name\n */\nconst toContainTool: MatcherFunction<[toolName: string]> = function (received, toolName) {\n const tools = received as Tool[];\n\n if (!Array.isArray(tools)) {\n return {\n pass: false,\n message: () => `Expected an array of tools, but received ${typeof received}`,\n };\n }\n\n const pass = tools.some((t) => t.name === toolName);\n const availableTools = tools.map((t) => t.name).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected tools not to contain \"${toolName}\"`\n : `Expected tools to contain \"${toolName}\", but got: [${availableTools}]`,\n };\n};\n\n/**\n * Check if result is successful (not an error)\n */\nconst toBeSuccessful: MatcherFunction<[]> = function (received) {\n const result = received as ResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('isSuccess' in result)) {\n return {\n pass: false,\n message: () => `Expected a result wrapper object with isSuccess property`,\n };\n }\n\n const pass = result.isSuccess;\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected result not to be successful'\n : `Expected result to be successful, but got error: ${result.error?.message ?? 'unknown error'}`,\n };\n};\n\n/**\n * Check if result is an error, optionally with a specific error code\n */\nconst toBeError: MatcherFunction<[expectedCode?: number]> = function (received, expectedCode) {\n const result = received as ResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('isError' in result)) {\n return {\n pass: false,\n message: () => `Expected a result wrapper object with isError property`,\n };\n }\n\n let pass = result.isError;\n\n if (pass && expectedCode !== undefined) {\n pass = result.error?.code === expectedCode;\n }\n\n return {\n pass,\n message: () => {\n if (!result.isError) {\n return 'Expected result to be an error, but it was successful';\n }\n if (expectedCode !== undefined && result.error?.code !== expectedCode) {\n return `Expected error code ${expectedCode}, but got ${result.error?.code}`;\n }\n return 'Expected result not to be an error';\n },\n };\n};\n\n/**\n * Check if tool result has text content, optionally containing specific text\n */\nconst toHaveTextContent: MatcherFunction<[expectedText?: string]> = function (received, expectedText) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasTextContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasTextContent method`,\n };\n }\n\n const hasText = result.hasTextContent();\n const text = result.text();\n let pass = hasText;\n\n if (pass && expectedText !== undefined) {\n pass = text?.includes(expectedText) ?? false;\n }\n\n return {\n pass,\n message: () => {\n if (!hasText) {\n return 'Expected result to have text content';\n }\n if (expectedText !== undefined && !text?.includes(expectedText)) {\n return `Expected text to contain \"${expectedText}\", but got: \"${text}\"`;\n }\n return 'Expected result not to have text content';\n },\n };\n};\n\n/**\n * Check if tool result has image content\n */\nconst toHaveImageContent: MatcherFunction<[]> = function (received) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasImageContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasImageContent method`,\n };\n }\n\n const pass = result.hasImageContent();\n\n return {\n pass,\n message: () => (pass ? 'Expected result not to have image content' : 'Expected result to have image content'),\n };\n};\n\n/**\n * Check if tool result has resource content\n */\nconst toHaveResourceContent: MatcherFunction<[]> = function (received) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasResourceContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasResourceContent method`,\n };\n }\n\n const pass = result.hasResourceContent();\n\n return {\n pass,\n message: () => (pass ? 'Expected result not to have resource content' : 'Expected result to have resource content'),\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// RESOURCE MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if resources array contains a resource with the given URI\n */\nconst toContainResource: MatcherFunction<[uri: string]> = function (received, uri) {\n const resources = received as Resource[];\n\n if (!Array.isArray(resources)) {\n return {\n pass: false,\n message: () => `Expected an array of resources, but received ${typeof received}`,\n };\n }\n\n const pass = resources.some((r) => r.uri === uri);\n const availableUris = resources.map((r) => r.uri).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected resources not to contain \"${uri}\"`\n : `Expected resources to contain \"${uri}\", but got: [${availableUris}]`,\n };\n};\n\n/**\n * Check if resource templates array contains a template with the given URI template\n */\nconst toContainResourceTemplate: MatcherFunction<[uriTemplate: string]> = function (received, uriTemplate) {\n const templates = received as ResourceTemplate[];\n\n if (!Array.isArray(templates)) {\n return {\n pass: false,\n message: () => `Expected an array of resource templates, but received ${typeof received}`,\n };\n }\n\n const pass = templates.some((t) => t.uriTemplate === uriTemplate);\n const availableTemplates = templates.map((t) => t.uriTemplate).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected templates not to contain \"${uriTemplate}\"`\n : `Expected templates to contain \"${uriTemplate}\", but got: [${availableTemplates}]`,\n };\n};\n\n/**\n * Check if resource content has a specific MIME type\n */\nconst toHaveMimeType: MatcherFunction<[mimeType: string]> = function (received, mimeType) {\n const result = received as ResourceContentWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasMimeType' in result)) {\n return {\n pass: false,\n message: () => `Expected a ResourceContentWrapper object with hasMimeType method`,\n };\n }\n\n const pass = result.hasMimeType(mimeType);\n const actualMimeType = result.mimeType();\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected content not to have MIME type \"${mimeType}\"`\n : `Expected MIME type \"${mimeType}\", but got \"${actualMimeType}\"`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// PROMPT MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if prompts array contains a prompt with the given name\n */\nconst toContainPrompt: MatcherFunction<[name: string]> = function (received, name) {\n const prompts = received as Prompt[];\n\n if (!Array.isArray(prompts)) {\n return {\n pass: false,\n message: () => `Expected an array of prompts, but received ${typeof received}`,\n };\n }\n\n const pass = prompts.some((p) => p.name === name);\n const availablePrompts = prompts.map((p) => p.name).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected prompts not to contain \"${name}\"`\n : `Expected prompts to contain \"${name}\", but got: [${availablePrompts}]`,\n };\n};\n\n/**\n * Check if prompt result has a specific number of messages\n */\nconst toHaveMessages: MatcherFunction<[count: number]> = function (received, count) {\n const result = received as PromptResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('messages' in result)) {\n return {\n pass: false,\n message: () => `Expected a PromptResultWrapper object with messages property`,\n };\n }\n\n const actualCount = result.messages?.length ?? 0;\n const pass = actualCount === count;\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected prompt not to have ${count} messages`\n : `Expected prompt to have ${count} messages, but got ${actualCount}`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// PROTOCOL MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if response is valid JSON-RPC 2.0\n * A valid JSON-RPC 2.0 response must have:\n * - jsonrpc: \"2.0\"\n * - id (matching the request, can be null for notifications)\n * - Either result OR error (but not both)\n */\nconst toBeValidJsonRpc: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const hasJsonRpc = response['jsonrpc'] === '2.0';\n const hasId = 'id' in response;\n const hasResult = 'result' in response;\n const hasError = 'error' in response;\n const hasExactlyOneResultOrError = (hasResult || hasError) && !(hasResult && hasError);\n\n const pass = hasJsonRpc && hasId && hasExactlyOneResultOrError;\n\n return {\n pass,\n message: () => {\n if (pass) {\n return 'Expected response not to be valid JSON-RPC';\n }\n const issues: string[] = [];\n if (!hasJsonRpc) issues.push('missing or invalid \"jsonrpc\": \"2.0\"');\n if (!hasId) issues.push('missing \"id\" field');\n if (!hasExactlyOneResultOrError) {\n if (!hasResult && !hasError) issues.push('missing \"result\" or \"error\"');\n else issues.push('cannot have both \"result\" and \"error\"');\n }\n return `Expected valid JSON-RPC 2.0 response: ${issues.join(', ')}`;\n },\n };\n};\n\n/**\n * Check if JSON-RPC response has a result\n */\nconst toHaveResult: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const pass = 'result' in response;\n\n return {\n pass,\n message: () => (pass ? 'Expected response not to have result' : 'Expected response to have result'),\n };\n};\n\n/**\n * Check if JSON-RPC response has an error\n */\nconst toHaveError: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const pass = 'error' in response;\n\n return {\n pass,\n message: () => (pass ? 'Expected response not to have error' : 'Expected response to have error'),\n };\n};\n\n/**\n * Check if JSON-RPC response has a specific error code\n */\nconst toHaveErrorCode: MatcherFunction<[code: number]> = function (received, code) {\n const response = received as { error?: { code: number } };\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const actualCode = response.error?.code;\n const pass = actualCode === code;\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected response not to have error code ${code}`\n : `Expected error code ${code}, but got ${actualCode ?? 'no error'}`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * All MCP matchers as an object for expect.extend()\n */\nexport const mcpMatchers = {\n // Tool matchers\n toContainTool,\n toBeSuccessful,\n toBeError,\n toHaveTextContent,\n toHaveImageContent,\n toHaveResourceContent,\n\n // Resource matchers\n toContainResource,\n toContainResourceTemplate,\n toHaveMimeType,\n\n // Prompt matchers\n toContainPrompt,\n toHaveMessages,\n\n // Protocol matchers\n toBeValidJsonRpc,\n toHaveResult,\n toHaveError,\n toHaveErrorCode,\n\n // UI matchers (for testing tool UI responses)\n ...uiMatchers,\n};\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file playwright/index.ts
|
|
3
|
+
* @description Playwright integration for OAuth flow testing (Future Phase)
|
|
4
|
+
*
|
|
5
|
+
* This module will provide Playwright-based testing utilities for:
|
|
6
|
+
* - OAuth consent flow testing
|
|
7
|
+
* - Login page interactions
|
|
8
|
+
* - Browser-based authentication flows
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { test, expect } from '@frontmcp/testing/playwright';
|
|
13
|
+
* import MyServer from './src/main';
|
|
14
|
+
*
|
|
15
|
+
* test.describe('OAuth Flow', () => {
|
|
16
|
+
* test.use({
|
|
17
|
+
* server: MyServer,
|
|
18
|
+
* auth: { mode: 'orchestrated' }
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* test('completes OAuth flow', async ({ page, oauth }) => {
|
|
22
|
+
* const { authorizeUrl } = await oauth.startFlow({ ... });
|
|
23
|
+
* await page.goto(authorizeUrl);
|
|
24
|
+
* // Test login and consent pages
|
|
25
|
+
* });
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const playwrightIntegration: {
|
|
30
|
+
version: string;
|
|
31
|
+
status: string;
|
|
32
|
+
description: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function test(_name: string, _fn: () => Promise<void>): void;
|
|
35
|
+
export declare const expect: {
|
|
36
|
+
notImplemented: boolean;
|
|
37
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file playwright/index.ts
|
|
4
|
+
* @description Playwright integration for OAuth flow testing (Future Phase)
|
|
5
|
+
*
|
|
6
|
+
* This module will provide Playwright-based testing utilities for:
|
|
7
|
+
* - OAuth consent flow testing
|
|
8
|
+
* - Login page interactions
|
|
9
|
+
* - Browser-based authentication flows
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { test, expect } from '@frontmcp/testing/playwright';
|
|
14
|
+
* import MyServer from './src/main';
|
|
15
|
+
*
|
|
16
|
+
* test.describe('OAuth Flow', () => {
|
|
17
|
+
* test.use({
|
|
18
|
+
* server: MyServer,
|
|
19
|
+
* auth: { mode: 'orchestrated' }
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* test('completes OAuth flow', async ({ page, oauth }) => {
|
|
23
|
+
* const { authorizeUrl } = await oauth.startFlow({ ... });
|
|
24
|
+
* await page.goto(authorizeUrl);
|
|
25
|
+
* // Test login and consent pages
|
|
26
|
+
* });
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.expect = exports.playwrightIntegration = void 0;
|
|
32
|
+
exports.test = test;
|
|
33
|
+
// Placeholder exports - will be implemented in future phase
|
|
34
|
+
exports.playwrightIntegration = {
|
|
35
|
+
version: '0.4.0',
|
|
36
|
+
status: 'planned',
|
|
37
|
+
description: 'Playwright integration for OAuth flow testing - coming in a future release',
|
|
38
|
+
};
|
|
39
|
+
// Export placeholder test function
|
|
40
|
+
function test(_name, _fn) {
|
|
41
|
+
throw new Error('Playwright integration not yet implemented. ' +
|
|
42
|
+
'Use @frontmcp/testing for non-browser tests, ' +
|
|
43
|
+
'or wait for a future release that includes playwright support.');
|
|
44
|
+
}
|
|
45
|
+
// Export placeholder expect
|
|
46
|
+
exports.expect = {
|
|
47
|
+
notImplemented: true,
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/playwright/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;;;AAUH,oBAMC;AAdD,4DAA4D;AAC/C,QAAA,qBAAqB,GAAG;IACnC,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,SAAS;IACjB,WAAW,EAAE,4EAA4E;CAC1F,CAAC;AAEF,mCAAmC;AACnC,SAAgB,IAAI,CAAC,KAAa,EAAE,GAAwB;IAC1D,MAAM,IAAI,KAAK,CACb,8CAA8C;QAC5C,+CAA+C;QAC/C,gEAAgE,CACnE,CAAC;AACJ,CAAC;AAED,4BAA4B;AACf,QAAA,MAAM,GAAG;IACpB,cAAc,EAAE,IAAI;CACrB,CAAC","sourcesContent":["/**\n * @file playwright/index.ts\n * @description Playwright integration for OAuth flow testing (Future Phase)\n *\n * This module will provide Playwright-based testing utilities for:\n * - OAuth consent flow testing\n * - Login page interactions\n * - Browser-based authentication flows\n *\n * @example\n * ```typescript\n * import { test, expect } from '@frontmcp/testing/playwright';\n * import MyServer from './src/main';\n *\n * test.describe('OAuth Flow', () => {\n * test.use({\n * server: MyServer,\n * auth: { mode: 'orchestrated' }\n * });\n *\n * test('completes OAuth flow', async ({ page, oauth }) => {\n * const { authorizeUrl } = await oauth.startFlow({ ... });\n * await page.goto(authorizeUrl);\n * // Test login and consent pages\n * });\n * });\n * ```\n */\n\n// Placeholder exports - will be implemented in future phase\nexport const playwrightIntegration = {\n version: '0.4.0',\n status: 'planned',\n description: 'Playwright integration for OAuth flow testing - coming in a future release',\n};\n\n// Export placeholder test function\nexport function test(_name: string, _fn: () => Promise<void>): void {\n throw new Error(\n 'Playwright integration not yet implemented. ' +\n 'Use @frontmcp/testing for non-browser tests, ' +\n 'or wait for a future release that includes playwright support.',\n );\n}\n\n// Export placeholder expect\nexport const expect = {\n notImplemented: true,\n};\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file server/index.ts
|
|
4
|
+
* @description Test server management exports
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.TestServer = void 0;
|
|
8
|
+
var test_server_1 = require("./test-server");
|
|
9
|
+
Object.defineProperty(exports, "TestServer", { enumerable: true, get: function () { return test_server_1.TestServer; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6CAA2C;AAAlC,yGAAA,UAAU,OAAA","sourcesContent":["/**\n * @file server/index.ts\n * @description Test server management exports\n */\n\nexport { TestServer } from './test-server';\nexport type { TestServerOptions, TestServerInfo } from './test-server';\n"]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file test-server.ts
|
|
3
|
+
* @description Test server management for E2E testing
|
|
4
|
+
*/
|
|
5
|
+
export interface TestServerOptions {
|
|
6
|
+
/** Port to run the server on (default: random available port) */
|
|
7
|
+
port?: number;
|
|
8
|
+
/** Command to start the server */
|
|
9
|
+
command?: string;
|
|
10
|
+
/** Working directory */
|
|
11
|
+
cwd?: string;
|
|
12
|
+
/** Environment variables */
|
|
13
|
+
env?: Record<string, string>;
|
|
14
|
+
/** Timeout for server startup in milliseconds (default: 30000) */
|
|
15
|
+
startupTimeout?: number;
|
|
16
|
+
/** Path to check for server readiness (default: /health) */
|
|
17
|
+
healthCheckPath?: string;
|
|
18
|
+
/** Enable debug logging */
|
|
19
|
+
debug?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface TestServerInfo {
|
|
22
|
+
/** Base URL of the server */
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
/** Port the server is running on */
|
|
25
|
+
port: number;
|
|
26
|
+
/** Process ID (if available) */
|
|
27
|
+
pid?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Manages test server lifecycle for E2E testing
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Start a server with custom command
|
|
35
|
+
* const server = await TestServer.start({
|
|
36
|
+
* command: 'node dist/main.js',
|
|
37
|
+
* port: 3003,
|
|
38
|
+
* cwd: './apps/my-server',
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Or start an Nx project
|
|
42
|
+
* const server = await TestServer.startNx('demo-public', { port: 3003 });
|
|
43
|
+
*
|
|
44
|
+
* // Use the server
|
|
45
|
+
* console.log(server.info.baseUrl); // http://localhost:3003
|
|
46
|
+
*
|
|
47
|
+
* // Stop when done
|
|
48
|
+
* await server.stop();
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare class TestServer {
|
|
52
|
+
private process;
|
|
53
|
+
private readonly options;
|
|
54
|
+
private _info;
|
|
55
|
+
private logs;
|
|
56
|
+
private constructor();
|
|
57
|
+
/**
|
|
58
|
+
* Start a test server with custom command
|
|
59
|
+
*/
|
|
60
|
+
static start(options: TestServerOptions): Promise<TestServer>;
|
|
61
|
+
/**
|
|
62
|
+
* Start an Nx project as test server
|
|
63
|
+
*/
|
|
64
|
+
static startNx(project: string, options?: Partial<TestServerOptions>): Promise<TestServer>;
|
|
65
|
+
/**
|
|
66
|
+
* Create a test server connected to an already running server
|
|
67
|
+
*/
|
|
68
|
+
static connect(baseUrl: string): TestServer;
|
|
69
|
+
/**
|
|
70
|
+
* Get server information
|
|
71
|
+
*/
|
|
72
|
+
get info(): TestServerInfo;
|
|
73
|
+
/**
|
|
74
|
+
* Stop the test server
|
|
75
|
+
*/
|
|
76
|
+
stop(): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Wait for server to be ready
|
|
79
|
+
*/
|
|
80
|
+
waitForReady(timeout?: number): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Restart the server
|
|
83
|
+
*/
|
|
84
|
+
restart(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Get captured server logs
|
|
87
|
+
*/
|
|
88
|
+
getLogs(): string[];
|
|
89
|
+
/**
|
|
90
|
+
* Clear captured logs
|
|
91
|
+
*/
|
|
92
|
+
clearLogs(): void;
|
|
93
|
+
private startProcess;
|
|
94
|
+
private log;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Find an available port
|
|
98
|
+
*/
|
|
99
|
+
export declare function findAvailablePort(): Promise<number>;
|