@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,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file mcp-test-client.ts
|
|
3
|
+
* @description Main MCP Test Client implementation for E2E testing
|
|
4
|
+
*/
|
|
5
|
+
import type { McpTestClientConfig, TestTransportType, ToolResultWrapper, ResourceContentWrapper, PromptResultWrapper, LogEntry, RequestTrace, NotificationEntry, ProgressUpdate, SessionInfo, AuthState, InitializeResult, Tool, Resource, ResourceTemplate, Prompt, JSONRPCResponse } from './mcp-test-client.types';
|
|
6
|
+
import { McpTestClientBuilder } from './mcp-test-client.builder';
|
|
7
|
+
import type { MockDefinition, MockHandle, RequestInterceptor, ResponseInterceptor } from '../interceptor';
|
|
8
|
+
export declare class McpTestClient {
|
|
9
|
+
private readonly config;
|
|
10
|
+
private transport;
|
|
11
|
+
private initResult;
|
|
12
|
+
private requestIdCounter;
|
|
13
|
+
private _lastRequestId;
|
|
14
|
+
private _sessionId;
|
|
15
|
+
private _sessionInfo;
|
|
16
|
+
private _authState;
|
|
17
|
+
private _logs;
|
|
18
|
+
private _traces;
|
|
19
|
+
private _notifications;
|
|
20
|
+
private _progressUpdates;
|
|
21
|
+
private _interceptors;
|
|
22
|
+
constructor(config: McpTestClientConfig);
|
|
23
|
+
/**
|
|
24
|
+
* Create a new McpTestClientBuilder for fluent configuration
|
|
25
|
+
*/
|
|
26
|
+
static create(config: McpTestClientConfig): McpTestClientBuilder;
|
|
27
|
+
/**
|
|
28
|
+
* Connect to the MCP server and perform initialization
|
|
29
|
+
*/
|
|
30
|
+
connect(): Promise<InitializeResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Disconnect from the MCP server
|
|
33
|
+
*/
|
|
34
|
+
disconnect(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Reconnect to the server, optionally with an existing session ID
|
|
37
|
+
*/
|
|
38
|
+
reconnect(options?: {
|
|
39
|
+
sessionId?: string;
|
|
40
|
+
}): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Check if the client is currently connected
|
|
43
|
+
*/
|
|
44
|
+
isConnected(): boolean;
|
|
45
|
+
get sessionId(): string;
|
|
46
|
+
get session(): SessionInfo & {
|
|
47
|
+
expire: () => Promise<void>;
|
|
48
|
+
};
|
|
49
|
+
get auth(): AuthState;
|
|
50
|
+
/**
|
|
51
|
+
* Authenticate with a token
|
|
52
|
+
*/
|
|
53
|
+
authenticate(token: string): Promise<void>;
|
|
54
|
+
get serverInfo(): {
|
|
55
|
+
name: string;
|
|
56
|
+
version: string;
|
|
57
|
+
title?: string;
|
|
58
|
+
};
|
|
59
|
+
get protocolVersion(): string;
|
|
60
|
+
get instructions(): string;
|
|
61
|
+
get capabilities(): InitializeResult['capabilities'];
|
|
62
|
+
/**
|
|
63
|
+
* Check if server has a specific capability
|
|
64
|
+
*/
|
|
65
|
+
hasCapability(name: 'tools' | 'resources' | 'prompts' | 'logging' | 'sampling'): boolean;
|
|
66
|
+
readonly tools: {
|
|
67
|
+
/**
|
|
68
|
+
* List all available tools
|
|
69
|
+
*/
|
|
70
|
+
list: () => Promise<Tool[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Call a tool by name with arguments
|
|
73
|
+
*/
|
|
74
|
+
call: (name: string, args?: Record<string, unknown>) => Promise<ToolResultWrapper>;
|
|
75
|
+
};
|
|
76
|
+
readonly resources: {
|
|
77
|
+
/**
|
|
78
|
+
* List all static resources
|
|
79
|
+
*/
|
|
80
|
+
list: () => Promise<Resource[]>;
|
|
81
|
+
/**
|
|
82
|
+
* List all resource templates
|
|
83
|
+
*/
|
|
84
|
+
listTemplates: () => Promise<ResourceTemplate[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Read a resource by URI
|
|
87
|
+
*/
|
|
88
|
+
read: (uri: string) => Promise<ResourceContentWrapper>;
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe to resource changes (placeholder for future implementation)
|
|
91
|
+
*/
|
|
92
|
+
subscribe: (_uri: string) => Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Unsubscribe from resource changes (placeholder for future implementation)
|
|
95
|
+
*/
|
|
96
|
+
unsubscribe: (_uri: string) => Promise<void>;
|
|
97
|
+
};
|
|
98
|
+
readonly prompts: {
|
|
99
|
+
/**
|
|
100
|
+
* List all available prompts
|
|
101
|
+
*/
|
|
102
|
+
list: () => Promise<Prompt[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Get a prompt with arguments
|
|
105
|
+
*/
|
|
106
|
+
get: (name: string, args?: Record<string, string>) => Promise<PromptResultWrapper>;
|
|
107
|
+
};
|
|
108
|
+
readonly raw: {
|
|
109
|
+
/**
|
|
110
|
+
* Send any JSON-RPC request
|
|
111
|
+
*/
|
|
112
|
+
request: (message: {
|
|
113
|
+
jsonrpc: "2.0";
|
|
114
|
+
id: string | number;
|
|
115
|
+
method: string;
|
|
116
|
+
params?: Record<string, unknown>;
|
|
117
|
+
}) => Promise<JSONRPCResponse>;
|
|
118
|
+
/**
|
|
119
|
+
* Send a notification (no response expected)
|
|
120
|
+
*/
|
|
121
|
+
notify: (message: {
|
|
122
|
+
jsonrpc: "2.0";
|
|
123
|
+
method: string;
|
|
124
|
+
params?: Record<string, unknown>;
|
|
125
|
+
}) => Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Send raw string data (for error testing)
|
|
128
|
+
*/
|
|
129
|
+
sendRaw: (data: string) => Promise<JSONRPCResponse>;
|
|
130
|
+
};
|
|
131
|
+
get lastRequestId(): string | number;
|
|
132
|
+
/**
|
|
133
|
+
* Get transport information and utilities
|
|
134
|
+
*/
|
|
135
|
+
get transport_info(): {
|
|
136
|
+
type: TestTransportType;
|
|
137
|
+
isConnected: () => boolean;
|
|
138
|
+
messageEndpoint: string | undefined;
|
|
139
|
+
connectionCount: number;
|
|
140
|
+
reconnectCount: number;
|
|
141
|
+
lastRequestHeaders: Record<string, string>;
|
|
142
|
+
simulateDisconnect: () => Promise<void>;
|
|
143
|
+
waitForReconnect: (timeoutMs: number) => Promise<void>;
|
|
144
|
+
};
|
|
145
|
+
get transport_(): {
|
|
146
|
+
type: TestTransportType;
|
|
147
|
+
isConnected: () => boolean;
|
|
148
|
+
messageEndpoint: string | undefined;
|
|
149
|
+
connectionCount: number;
|
|
150
|
+
reconnectCount: number;
|
|
151
|
+
lastRequestHeaders: Record<string, string>;
|
|
152
|
+
simulateDisconnect: () => Promise<void>;
|
|
153
|
+
waitForReconnect: (timeoutMs: number) => Promise<void>;
|
|
154
|
+
};
|
|
155
|
+
readonly notifications: {
|
|
156
|
+
/**
|
|
157
|
+
* Start collecting server notifications
|
|
158
|
+
*/
|
|
159
|
+
collect: () => NotificationCollector;
|
|
160
|
+
/**
|
|
161
|
+
* Collect progress notifications specifically
|
|
162
|
+
*/
|
|
163
|
+
collectProgress: () => ProgressCollector;
|
|
164
|
+
/**
|
|
165
|
+
* Send a notification to the server
|
|
166
|
+
*/
|
|
167
|
+
send: (method: string, params?: Record<string, unknown>) => Promise<void>;
|
|
168
|
+
};
|
|
169
|
+
readonly logs: {
|
|
170
|
+
all: () => LogEntry[];
|
|
171
|
+
filter: (level: "debug" | "info" | "warn" | "error") => LogEntry[];
|
|
172
|
+
search: (text: string) => LogEntry[];
|
|
173
|
+
last: () => LogEntry | undefined;
|
|
174
|
+
clear: () => void;
|
|
175
|
+
};
|
|
176
|
+
readonly trace: {
|
|
177
|
+
all: () => RequestTrace[];
|
|
178
|
+
last: () => RequestTrace | undefined;
|
|
179
|
+
clear: () => void;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* API for mocking MCP requests
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* // Mock a specific tool call
|
|
187
|
+
* const handle = mcp.mock.tool('my-tool', { result: 'mocked!' });
|
|
188
|
+
*
|
|
189
|
+
* // Mock with params matching
|
|
190
|
+
* mcp.mock.add({
|
|
191
|
+
* method: 'tools/call',
|
|
192
|
+
* params: { name: 'my-tool' },
|
|
193
|
+
* response: mockResponse.toolResult([{ type: 'text', text: 'mocked' }]),
|
|
194
|
+
* });
|
|
195
|
+
*
|
|
196
|
+
* // Clear all mocks after test
|
|
197
|
+
* mcp.mock.clear();
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
readonly mock: {
|
|
201
|
+
/**
|
|
202
|
+
* Add a mock definition
|
|
203
|
+
*/
|
|
204
|
+
add: (mock: MockDefinition) => MockHandle;
|
|
205
|
+
/**
|
|
206
|
+
* Mock a tools/call request for a specific tool
|
|
207
|
+
*/
|
|
208
|
+
tool: (name: string, result: unknown, options?: {
|
|
209
|
+
times?: number;
|
|
210
|
+
delay?: number;
|
|
211
|
+
}) => MockHandle;
|
|
212
|
+
/**
|
|
213
|
+
* Mock a tools/call request to return an error
|
|
214
|
+
*/
|
|
215
|
+
toolError: (name: string, code: number, message: string, options?: {
|
|
216
|
+
times?: number;
|
|
217
|
+
}) => MockHandle;
|
|
218
|
+
/**
|
|
219
|
+
* Mock a resources/read request
|
|
220
|
+
*/
|
|
221
|
+
resource: (uri: string, content: string | {
|
|
222
|
+
text?: string;
|
|
223
|
+
blob?: string;
|
|
224
|
+
mimeType?: string;
|
|
225
|
+
}, options?: {
|
|
226
|
+
times?: number;
|
|
227
|
+
delay?: number;
|
|
228
|
+
}) => MockHandle;
|
|
229
|
+
/**
|
|
230
|
+
* Mock a resources/read request to return an error
|
|
231
|
+
*/
|
|
232
|
+
resourceError: (uri: string, options?: {
|
|
233
|
+
times?: number;
|
|
234
|
+
}) => MockHandle;
|
|
235
|
+
/**
|
|
236
|
+
* Mock the tools/list response
|
|
237
|
+
*/
|
|
238
|
+
toolsList: (tools: Array<{
|
|
239
|
+
name: string;
|
|
240
|
+
description?: string;
|
|
241
|
+
inputSchema?: Record<string, unknown>;
|
|
242
|
+
}>, options?: {
|
|
243
|
+
times?: number;
|
|
244
|
+
}) => MockHandle;
|
|
245
|
+
/**
|
|
246
|
+
* Mock the resources/list response
|
|
247
|
+
*/
|
|
248
|
+
resourcesList: (resources: Array<{
|
|
249
|
+
uri: string;
|
|
250
|
+
name?: string;
|
|
251
|
+
description?: string;
|
|
252
|
+
mimeType?: string;
|
|
253
|
+
}>, options?: {
|
|
254
|
+
times?: number;
|
|
255
|
+
}) => MockHandle;
|
|
256
|
+
/**
|
|
257
|
+
* Clear all mocks
|
|
258
|
+
*/
|
|
259
|
+
clear: () => void;
|
|
260
|
+
/**
|
|
261
|
+
* Get all active mocks
|
|
262
|
+
*/
|
|
263
|
+
all: () => MockDefinition[];
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* API for intercepting requests and responses
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```typescript
|
|
270
|
+
* // Log all requests
|
|
271
|
+
* const remove = mcp.intercept.request((ctx) => {
|
|
272
|
+
* console.log('Request:', ctx.request.method);
|
|
273
|
+
* return { action: 'passthrough' };
|
|
274
|
+
* });
|
|
275
|
+
*
|
|
276
|
+
* // Modify requests
|
|
277
|
+
* mcp.intercept.request((ctx) => {
|
|
278
|
+
* if (ctx.request.method === 'tools/call') {
|
|
279
|
+
* return {
|
|
280
|
+
* action: 'modify',
|
|
281
|
+
* request: { ...ctx.request, params: { ...ctx.request.params, extra: true } },
|
|
282
|
+
* };
|
|
283
|
+
* }
|
|
284
|
+
* return { action: 'passthrough' };
|
|
285
|
+
* });
|
|
286
|
+
*
|
|
287
|
+
* // Add latency to all requests
|
|
288
|
+
* mcp.intercept.delay(100);
|
|
289
|
+
*
|
|
290
|
+
* // Clean up
|
|
291
|
+
* remove();
|
|
292
|
+
* mcp.intercept.clear();
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
readonly intercept: {
|
|
296
|
+
/**
|
|
297
|
+
* Add a request interceptor
|
|
298
|
+
* @returns Function to remove the interceptor
|
|
299
|
+
*/
|
|
300
|
+
request: (interceptor: RequestInterceptor) => (() => void);
|
|
301
|
+
/**
|
|
302
|
+
* Add a response interceptor
|
|
303
|
+
* @returns Function to remove the interceptor
|
|
304
|
+
*/
|
|
305
|
+
response: (interceptor: ResponseInterceptor) => (() => void);
|
|
306
|
+
/**
|
|
307
|
+
* Add latency to all requests
|
|
308
|
+
* @returns Function to remove the interceptor
|
|
309
|
+
*/
|
|
310
|
+
delay: (ms: number) => (() => void);
|
|
311
|
+
/**
|
|
312
|
+
* Fail requests matching a method
|
|
313
|
+
* @returns Function to remove the interceptor
|
|
314
|
+
*/
|
|
315
|
+
failMethod: (method: string, error?: string) => (() => void);
|
|
316
|
+
/**
|
|
317
|
+
* Clear all interceptors (but not mocks)
|
|
318
|
+
*/
|
|
319
|
+
clear: () => void;
|
|
320
|
+
/**
|
|
321
|
+
* Clear everything (interceptors and mocks)
|
|
322
|
+
*/
|
|
323
|
+
clearAll: () => void;
|
|
324
|
+
};
|
|
325
|
+
setTimeout(ms: number): void;
|
|
326
|
+
private initialize;
|
|
327
|
+
private listTools;
|
|
328
|
+
private callTool;
|
|
329
|
+
private listResources;
|
|
330
|
+
private listResourceTemplates;
|
|
331
|
+
private readResource;
|
|
332
|
+
private listPrompts;
|
|
333
|
+
private getPrompt;
|
|
334
|
+
private createTransport;
|
|
335
|
+
private request;
|
|
336
|
+
private ensureConnected;
|
|
337
|
+
private updateSessionActivity;
|
|
338
|
+
private wrapToolResult;
|
|
339
|
+
private wrapResourceContent;
|
|
340
|
+
private wrapPromptResult;
|
|
341
|
+
private log;
|
|
342
|
+
private traceRequest;
|
|
343
|
+
private parseScopesFromToken;
|
|
344
|
+
private parseUserFromToken;
|
|
345
|
+
private decodeJwtPayload;
|
|
346
|
+
}
|
|
347
|
+
declare class NotificationCollector {
|
|
348
|
+
private readonly notifications;
|
|
349
|
+
constructor(notifications: NotificationEntry[]);
|
|
350
|
+
get received(): NotificationEntry[];
|
|
351
|
+
has(method: string): boolean;
|
|
352
|
+
waitFor(method: string, timeoutMs: number): Promise<NotificationEntry>;
|
|
353
|
+
}
|
|
354
|
+
declare class ProgressCollector {
|
|
355
|
+
private readonly updates;
|
|
356
|
+
constructor(updates: ProgressUpdate[]);
|
|
357
|
+
get all(): ProgressUpdate[];
|
|
358
|
+
waitForComplete(timeoutMs: number): Promise<void>;
|
|
359
|
+
}
|
|
360
|
+
export {};
|