@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.
Files changed (112) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1358 -0
  3. package/jest-preset.js +61 -0
  4. package/package.json +94 -0
  5. package/src/assertions/index.d.ts +5 -0
  6. package/src/assertions/index.js +18 -0
  7. package/src/assertions/index.js.map +1 -0
  8. package/src/assertions/mcp-assertions.d.ts +81 -0
  9. package/src/assertions/mcp-assertions.js +220 -0
  10. package/src/assertions/mcp-assertions.js.map +1 -0
  11. package/src/auth/auth-headers.d.ts +29 -0
  12. package/src/auth/auth-headers.js +62 -0
  13. package/src/auth/auth-headers.js.map +1 -0
  14. package/src/auth/index.d.ts +9 -0
  15. package/src/auth/index.js +15 -0
  16. package/src/auth/index.js.map +1 -0
  17. package/src/auth/token-factory.d.ts +94 -0
  18. package/src/auth/token-factory.js +181 -0
  19. package/src/auth/token-factory.js.map +1 -0
  20. package/src/auth/user-fixtures.d.ts +26 -0
  21. package/src/auth/user-fixtures.js +92 -0
  22. package/src/auth/user-fixtures.js.map +1 -0
  23. package/src/client/index.d.ts +7 -0
  24. package/src/client/index.js +12 -0
  25. package/src/client/index.js.map +1 -0
  26. package/src/client/mcp-test-client.builder.d.ts +72 -0
  27. package/src/client/mcp-test-client.builder.js +111 -0
  28. package/src/client/mcp-test-client.builder.js.map +1 -0
  29. package/src/client/mcp-test-client.d.ts +360 -0
  30. package/src/client/mcp-test-client.js +929 -0
  31. package/src/client/mcp-test-client.js.map +1 -0
  32. package/src/client/mcp-test-client.types.d.ts +216 -0
  33. package/src/client/mcp-test-client.types.js +7 -0
  34. package/src/client/mcp-test-client.types.js.map +1 -0
  35. package/src/errors/index.d.ts +45 -0
  36. package/src/errors/index.js +85 -0
  37. package/src/errors/index.js.map +1 -0
  38. package/src/expect.d.ts +67 -0
  39. package/src/expect.js +31 -0
  40. package/src/expect.js.map +1 -0
  41. package/src/fixtures/fixture-types.d.ts +166 -0
  42. package/src/fixtures/fixture-types.js +7 -0
  43. package/src/fixtures/fixture-types.js.map +1 -0
  44. package/src/fixtures/index.d.ts +7 -0
  45. package/src/fixtures/index.js +16 -0
  46. package/src/fixtures/index.js.map +1 -0
  47. package/src/fixtures/test-fixture.d.ts +41 -0
  48. package/src/fixtures/test-fixture.js +280 -0
  49. package/src/fixtures/test-fixture.js.map +1 -0
  50. package/src/http-mock/http-mock.d.ts +84 -0
  51. package/src/http-mock/http-mock.js +544 -0
  52. package/src/http-mock/http-mock.js.map +1 -0
  53. package/src/http-mock/http-mock.types.d.ts +124 -0
  54. package/src/http-mock/http-mock.types.js +10 -0
  55. package/src/http-mock/http-mock.types.js.map +1 -0
  56. package/src/http-mock/index.d.ts +6 -0
  57. package/src/http-mock/index.js +11 -0
  58. package/src/http-mock/index.js.map +1 -0
  59. package/src/index.d.ts +65 -0
  60. package/src/index.js +128 -0
  61. package/src/index.js.map +1 -0
  62. package/src/interceptor/index.d.ts +7 -0
  63. package/src/interceptor/index.js +15 -0
  64. package/src/interceptor/index.js.map +1 -0
  65. package/src/interceptor/interceptor-chain.d.ts +77 -0
  66. package/src/interceptor/interceptor-chain.js +207 -0
  67. package/src/interceptor/interceptor-chain.js.map +1 -0
  68. package/src/interceptor/interceptor.types.d.ts +131 -0
  69. package/src/interceptor/interceptor.types.js +7 -0
  70. package/src/interceptor/interceptor.types.js.map +1 -0
  71. package/src/interceptor/mock-registry.d.ts +82 -0
  72. package/src/interceptor/mock-registry.js +189 -0
  73. package/src/interceptor/mock-registry.js.map +1 -0
  74. package/src/matchers/index.d.ts +7 -0
  75. package/src/matchers/index.js +12 -0
  76. package/src/matchers/index.js.map +1 -0
  77. package/src/matchers/matcher-types.d.ts +266 -0
  78. package/src/matchers/matcher-types.js +10 -0
  79. package/src/matchers/matcher-types.js.map +1 -0
  80. package/src/matchers/mcp-matchers.d.ts +47 -0
  81. package/src/matchers/mcp-matchers.js +391 -0
  82. package/src/matchers/mcp-matchers.js.map +1 -0
  83. package/src/playwright/index.d.ts +37 -0
  84. package/src/playwright/index.js +49 -0
  85. package/src/playwright/index.js.map +1 -0
  86. package/src/server/index.d.ts +6 -0
  87. package/src/server/index.js +10 -0
  88. package/src/server/index.js.map +1 -0
  89. package/src/server/test-server.d.ts +99 -0
  90. package/src/server/test-server.js +286 -0
  91. package/src/server/test-server.js.map +1 -0
  92. package/src/setup.d.ts +22 -0
  93. package/src/setup.js +30 -0
  94. package/src/setup.js.map +1 -0
  95. package/src/transport/index.d.ts +6 -0
  96. package/src/transport/index.js +10 -0
  97. package/src/transport/index.js.map +1 -0
  98. package/src/transport/streamable-http.transport.d.ts +65 -0
  99. package/src/transport/streamable-http.transport.js +432 -0
  100. package/src/transport/streamable-http.transport.js.map +1 -0
  101. package/src/transport/transport.interface.d.ts +124 -0
  102. package/src/transport/transport.interface.js +7 -0
  103. package/src/transport/transport.interface.js.map +1 -0
  104. package/src/ui/index.d.ts +17 -0
  105. package/src/ui/index.js +23 -0
  106. package/src/ui/index.js.map +1 -0
  107. package/src/ui/ui-assertions.d.ts +94 -0
  108. package/src/ui/ui-assertions.js +215 -0
  109. package/src/ui/ui-assertions.js.map +1 -0
  110. package/src/ui/ui-matchers.d.ts +39 -0
  111. package/src/ui/ui-matchers.js +275 -0
  112. 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 {};