@frontmcp/testing 0.6.1 → 0.6.2

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 (142) hide show
  1. package/esm/fixtures/index.mjs +2377 -0
  2. package/esm/index.mjs +4768 -0
  3. package/esm/matchers/index.mjs +646 -0
  4. package/esm/package.json +127 -0
  5. package/esm/playwright/index.mjs +19 -0
  6. package/esm/setup.mjs +680 -0
  7. package/fixtures/index.js +2418 -0
  8. package/index.js +4866 -0
  9. package/jest-preset.js +3 -3
  10. package/matchers/index.js +673 -0
  11. package/package.json +51 -23
  12. package/playwright/index.js +46 -0
  13. package/setup.js +651 -0
  14. package/src/assertions/index.js +0 -18
  15. package/src/assertions/index.js.map +0 -1
  16. package/src/assertions/mcp-assertions.js +0 -220
  17. package/src/assertions/mcp-assertions.js.map +0 -1
  18. package/src/auth/auth-headers.js +0 -62
  19. package/src/auth/auth-headers.js.map +0 -1
  20. package/src/auth/index.js +0 -15
  21. package/src/auth/index.js.map +0 -1
  22. package/src/auth/mock-api-server.js +0 -200
  23. package/src/auth/mock-api-server.js.map +0 -1
  24. package/src/auth/mock-oauth-server.js +0 -253
  25. package/src/auth/mock-oauth-server.js.map +0 -1
  26. package/src/auth/token-factory.js +0 -181
  27. package/src/auth/token-factory.js.map +0 -1
  28. package/src/auth/user-fixtures.js +0 -92
  29. package/src/auth/user-fixtures.js.map +0 -1
  30. package/src/client/index.js +0 -12
  31. package/src/client/index.js.map +0 -1
  32. package/src/client/mcp-test-client.builder.js +0 -163
  33. package/src/client/mcp-test-client.builder.js.map +0 -1
  34. package/src/client/mcp-test-client.js +0 -937
  35. package/src/client/mcp-test-client.js.map +0 -1
  36. package/src/client/mcp-test-client.types.js +0 -16
  37. package/src/client/mcp-test-client.types.js.map +0 -1
  38. package/src/errors/index.js +0 -85
  39. package/src/errors/index.js.map +0 -1
  40. package/src/example-tools/index.js +0 -40
  41. package/src/example-tools/index.js.map +0 -1
  42. package/src/example-tools/tool-configs.js +0 -222
  43. package/src/example-tools/tool-configs.js.map +0 -1
  44. package/src/expect.js +0 -31
  45. package/src/expect.js.map +0 -1
  46. package/src/fixtures/fixture-types.js +0 -7
  47. package/src/fixtures/fixture-types.js.map +0 -1
  48. package/src/fixtures/index.js +0 -16
  49. package/src/fixtures/index.js.map +0 -1
  50. package/src/fixtures/test-fixture.js +0 -311
  51. package/src/fixtures/test-fixture.js.map +0 -1
  52. package/src/http-mock/http-mock.js +0 -544
  53. package/src/http-mock/http-mock.js.map +0 -1
  54. package/src/http-mock/http-mock.types.js +0 -10
  55. package/src/http-mock/http-mock.types.js.map +0 -1
  56. package/src/http-mock/index.js +0 -11
  57. package/src/http-mock/index.js.map +0 -1
  58. package/src/index.js +0 -167
  59. package/src/index.js.map +0 -1
  60. package/src/interceptor/index.js +0 -15
  61. package/src/interceptor/index.js.map +0 -1
  62. package/src/interceptor/interceptor-chain.js +0 -207
  63. package/src/interceptor/interceptor-chain.js.map +0 -1
  64. package/src/interceptor/interceptor.types.js +0 -7
  65. package/src/interceptor/interceptor.types.js.map +0 -1
  66. package/src/interceptor/mock-registry.js +0 -189
  67. package/src/interceptor/mock-registry.js.map +0 -1
  68. package/src/matchers/index.js +0 -12
  69. package/src/matchers/index.js.map +0 -1
  70. package/src/matchers/matcher-types.js +0 -10
  71. package/src/matchers/matcher-types.js.map +0 -1
  72. package/src/matchers/mcp-matchers.js +0 -395
  73. package/src/matchers/mcp-matchers.js.map +0 -1
  74. package/src/platform/index.js +0 -47
  75. package/src/platform/index.js.map +0 -1
  76. package/src/platform/platform-client-info.js +0 -155
  77. package/src/platform/platform-client-info.js.map +0 -1
  78. package/src/platform/platform-types.js +0 -110
  79. package/src/platform/platform-types.js.map +0 -1
  80. package/src/playwright/index.js +0 -49
  81. package/src/playwright/index.js.map +0 -1
  82. package/src/server/index.js +0 -10
  83. package/src/server/index.js.map +0 -1
  84. package/src/server/test-server.js +0 -341
  85. package/src/server/test-server.js.map +0 -1
  86. package/src/setup.js +0 -30
  87. package/src/setup.js.map +0 -1
  88. package/src/transport/index.js +0 -10
  89. package/src/transport/index.js.map +0 -1
  90. package/src/transport/streamable-http.transport.js +0 -438
  91. package/src/transport/streamable-http.transport.js.map +0 -1
  92. package/src/transport/transport.interface.js +0 -7
  93. package/src/transport/transport.interface.js.map +0 -1
  94. package/src/ui/index.js +0 -23
  95. package/src/ui/index.js.map +0 -1
  96. package/src/ui/ui-assertions.js +0 -367
  97. package/src/ui/ui-assertions.js.map +0 -1
  98. package/src/ui/ui-matchers.js +0 -493
  99. package/src/ui/ui-matchers.js.map +0 -1
  100. /package/{src/assertions → assertions}/index.d.ts +0 -0
  101. /package/{src/assertions → assertions}/mcp-assertions.d.ts +0 -0
  102. /package/{src/auth → auth}/auth-headers.d.ts +0 -0
  103. /package/{src/auth → auth}/index.d.ts +0 -0
  104. /package/{src/auth → auth}/mock-api-server.d.ts +0 -0
  105. /package/{src/auth → auth}/mock-oauth-server.d.ts +0 -0
  106. /package/{src/auth → auth}/token-factory.d.ts +0 -0
  107. /package/{src/auth → auth}/user-fixtures.d.ts +0 -0
  108. /package/{src/client → client}/index.d.ts +0 -0
  109. /package/{src/client → client}/mcp-test-client.builder.d.ts +0 -0
  110. /package/{src/client → client}/mcp-test-client.d.ts +0 -0
  111. /package/{src/client → client}/mcp-test-client.types.d.ts +0 -0
  112. /package/{src/errors → errors}/index.d.ts +0 -0
  113. /package/{src/example-tools → example-tools}/index.d.ts +0 -0
  114. /package/{src/example-tools → example-tools}/tool-configs.d.ts +0 -0
  115. /package/{src/expect.d.ts → expect.d.ts} +0 -0
  116. /package/{src/fixtures → fixtures}/fixture-types.d.ts +0 -0
  117. /package/{src/fixtures → fixtures}/index.d.ts +0 -0
  118. /package/{src/fixtures → fixtures}/test-fixture.d.ts +0 -0
  119. /package/{src/http-mock → http-mock}/http-mock.d.ts +0 -0
  120. /package/{src/http-mock → http-mock}/http-mock.types.d.ts +0 -0
  121. /package/{src/http-mock → http-mock}/index.d.ts +0 -0
  122. /package/{src/index.d.ts → index.d.ts} +0 -0
  123. /package/{src/interceptor → interceptor}/index.d.ts +0 -0
  124. /package/{src/interceptor → interceptor}/interceptor-chain.d.ts +0 -0
  125. /package/{src/interceptor → interceptor}/interceptor.types.d.ts +0 -0
  126. /package/{src/interceptor → interceptor}/mock-registry.d.ts +0 -0
  127. /package/{src/matchers → matchers}/index.d.ts +0 -0
  128. /package/{src/matchers → matchers}/matcher-types.d.ts +0 -0
  129. /package/{src/matchers → matchers}/mcp-matchers.d.ts +0 -0
  130. /package/{src/platform → platform}/index.d.ts +0 -0
  131. /package/{src/platform → platform}/platform-client-info.d.ts +0 -0
  132. /package/{src/platform → platform}/platform-types.d.ts +0 -0
  133. /package/{src/playwright → playwright}/index.d.ts +0 -0
  134. /package/{src/server → server}/index.d.ts +0 -0
  135. /package/{src/server → server}/test-server.d.ts +0 -0
  136. /package/{src/setup.d.ts → setup.d.ts} +0 -0
  137. /package/{src/transport → transport}/index.d.ts +0 -0
  138. /package/{src/transport → transport}/streamable-http.transport.d.ts +0 -0
  139. /package/{src/transport → transport}/transport.interface.d.ts +0 -0
  140. /package/{src/ui → ui}/index.d.ts +0 -0
  141. /package/{src/ui → ui}/ui-assertions.d.ts +0 -0
  142. /package/{src/ui → ui}/ui-matchers.d.ts +0 -0
@@ -1,341 +0,0 @@
1
- "use strict";
2
- /**
3
- * @file test-server.ts
4
- * @description Test server management for E2E testing
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.TestServer = void 0;
8
- exports.findAvailablePort = findAvailablePort;
9
- const child_process_1 = require("child_process");
10
- // ═══════════════════════════════════════════════════════════════════
11
- // TEST SERVER CLASS
12
- // ═══════════════════════════════════════════════════════════════════
13
- /**
14
- * Manages test server lifecycle for E2E testing
15
- *
16
- * @example
17
- * ```typescript
18
- * // Start a server with custom command
19
- * const server = await TestServer.start({
20
- * command: 'node dist/main.js',
21
- * port: 3003,
22
- * cwd: './apps/my-server',
23
- * });
24
- *
25
- * // Or start an Nx project
26
- * const server = await TestServer.startNx('demo-public', { port: 3003 });
27
- *
28
- * // Use the server
29
- * console.log(server.info.baseUrl); // http://localhost:3003
30
- *
31
- * // Stop when done
32
- * await server.stop();
33
- * ```
34
- */
35
- class TestServer {
36
- process = null;
37
- options;
38
- _info;
39
- logs = [];
40
- constructor(options, port) {
41
- this.options = {
42
- port,
43
- command: options.command ?? '',
44
- cwd: options.cwd ?? process.cwd(),
45
- env: options.env ?? {},
46
- startupTimeout: options.startupTimeout ?? 30000,
47
- healthCheckPath: options.healthCheckPath ?? '/health',
48
- debug: options.debug ?? false,
49
- };
50
- this._info = {
51
- baseUrl: `http://localhost:${port}`,
52
- port,
53
- };
54
- }
55
- /**
56
- * Start a test server with custom command
57
- */
58
- static async start(options) {
59
- const port = options.port ?? (await findAvailablePort());
60
- const server = new TestServer(options, port);
61
- try {
62
- await server.startProcess();
63
- }
64
- catch (error) {
65
- await server.stop(); // Clean up spawned process to prevent leaks
66
- throw error;
67
- }
68
- return server;
69
- }
70
- /**
71
- * Start an Nx project as test server
72
- */
73
- static async startNx(project, options = {}) {
74
- // Validate project name contains only safe characters to prevent shell injection
75
- if (!/^[\w-]+$/.test(project)) {
76
- throw new Error(`Invalid project name: ${project}. Must contain only alphanumeric, underscore, and hyphen characters.`);
77
- }
78
- const port = options.port ?? (await findAvailablePort());
79
- const serverOptions = {
80
- ...options,
81
- port,
82
- command: `npx nx serve ${project} --port ${port}`,
83
- cwd: options.cwd ?? process.cwd(),
84
- };
85
- const server = new TestServer(serverOptions, port);
86
- try {
87
- await server.startProcess();
88
- }
89
- catch (error) {
90
- await server.stop(); // Clean up spawned process to prevent leaks
91
- throw error;
92
- }
93
- return server;
94
- }
95
- /**
96
- * Create a test server connected to an already running server
97
- */
98
- static connect(baseUrl) {
99
- const url = new URL(baseUrl);
100
- const port = parseInt(url.port, 10) || (url.protocol === 'https:' ? 443 : 80);
101
- const server = new TestServer({
102
- command: '',
103
- port,
104
- }, port);
105
- server._info = {
106
- baseUrl: baseUrl.replace(/\/$/, ''),
107
- port,
108
- };
109
- return server;
110
- }
111
- /**
112
- * Get server information
113
- */
114
- get info() {
115
- return { ...this._info };
116
- }
117
- /**
118
- * Stop the test server
119
- */
120
- async stop() {
121
- if (this.process) {
122
- this.log('Stopping server...');
123
- // Try graceful shutdown first
124
- this.process.kill('SIGTERM');
125
- // Wait for process to exit
126
- const exitPromise = new Promise((resolve) => {
127
- if (this.process) {
128
- this.process.once('exit', () => resolve());
129
- }
130
- else {
131
- resolve();
132
- }
133
- });
134
- // Force kill after timeout (but still wait for actual exit)
135
- const killTimeout = setTimeout(() => {
136
- if (this.process) {
137
- this.log('Force killing server after timeout...');
138
- this.process.kill('SIGKILL');
139
- }
140
- }, 5000);
141
- await exitPromise;
142
- clearTimeout(killTimeout);
143
- this.process = null;
144
- this.log('Server stopped');
145
- }
146
- }
147
- /**
148
- * Wait for server to be ready
149
- */
150
- async waitForReady(timeout) {
151
- const timeoutMs = timeout ?? this.options.startupTimeout;
152
- const deadline = Date.now() + timeoutMs;
153
- const checkInterval = 100;
154
- while (Date.now() < deadline) {
155
- try {
156
- const response = await fetch(`${this._info.baseUrl}${this.options.healthCheckPath}`, {
157
- method: 'GET',
158
- signal: AbortSignal.timeout(1000),
159
- });
160
- if (response.ok || response.status === 404) {
161
- // 404 is okay - it means the server is running but might not have a health endpoint
162
- this.log('Server is ready');
163
- return;
164
- }
165
- }
166
- catch {
167
- // Server not ready yet
168
- }
169
- await sleep(checkInterval);
170
- }
171
- throw new Error(`Server did not become ready within ${timeoutMs}ms`);
172
- }
173
- /**
174
- * Restart the server
175
- */
176
- async restart() {
177
- await this.stop();
178
- await this.startProcess();
179
- }
180
- /**
181
- * Get captured server logs
182
- */
183
- getLogs() {
184
- return [...this.logs];
185
- }
186
- /**
187
- * Clear captured logs
188
- */
189
- clearLogs() {
190
- this.logs = [];
191
- }
192
- // ═══════════════════════════════════════════════════════════════════
193
- // PRIVATE METHODS
194
- // ═══════════════════════════════════════════════════════════════════
195
- async startProcess() {
196
- if (!this.options.command) {
197
- // No command means we're connecting to an existing server
198
- await this.waitForReady();
199
- return;
200
- }
201
- this.log(`Starting server: ${this.options.command}`);
202
- const env = {
203
- ...process.env,
204
- ...this.options.env,
205
- PORT: String(this.options.port),
206
- };
207
- // Use shell: true to handle complex commands with quoted arguments
208
- // This avoids fragile command parsing with split(' ')
209
- this.process = (0, child_process_1.spawn)(this.options.command, [], {
210
- cwd: this.options.cwd,
211
- env,
212
- shell: true,
213
- stdio: ['pipe', 'pipe', 'pipe'],
214
- });
215
- // pid can be undefined if spawn fails
216
- if (this.process.pid !== undefined) {
217
- this._info.pid = this.process.pid;
218
- }
219
- // Track process exit for early failure detection
220
- let processExited = false;
221
- let exitCode = null;
222
- let exitError = null;
223
- // Capture stdout
224
- this.process.stdout?.on('data', (data) => {
225
- const text = data.toString();
226
- this.logs.push(text);
227
- if (this.options.debug) {
228
- console.log('[SERVER]', text);
229
- }
230
- });
231
- // Capture stderr
232
- this.process.stderr?.on('data', (data) => {
233
- const text = data.toString();
234
- this.logs.push(`[ERROR] ${text}`);
235
- if (this.options.debug) {
236
- console.error('[SERVER ERROR]', text);
237
- }
238
- });
239
- // Handle spawn errors to prevent unhandled error events
240
- this.process.on('error', (err) => {
241
- this.logs.push(`[SPAWN ERROR] ${err.message}`);
242
- exitError = err;
243
- if (this.options.debug) {
244
- console.error('[SERVER SPAWN ERROR]', err);
245
- }
246
- });
247
- // Handle process exit - track early failures
248
- this.process.once('exit', (code) => {
249
- processExited = true;
250
- exitCode = code;
251
- this.log(`Server process exited with code ${code}`);
252
- });
253
- // Wait for server to be ready, but detect early process exit
254
- await this.waitForReadyWithExitDetection(() => {
255
- if (exitError) {
256
- return { exited: true, error: exitError };
257
- }
258
- if (processExited) {
259
- const recentLogs = this.logs.slice(-10).join('\n');
260
- return {
261
- exited: true,
262
- error: new Error(`Server process exited unexpectedly with code ${exitCode}.\n\nRecent logs:\n${recentLogs}`),
263
- };
264
- }
265
- return { exited: false };
266
- });
267
- }
268
- /**
269
- * Wait for server to be ready, but also detect early process exit
270
- */
271
- async waitForReadyWithExitDetection(checkExit) {
272
- const timeoutMs = this.options.startupTimeout;
273
- const deadline = Date.now() + timeoutMs;
274
- const checkInterval = 100;
275
- while (Date.now() < deadline) {
276
- // Check if process has exited before continuing to poll
277
- const exitStatus = checkExit();
278
- if (exitStatus.exited) {
279
- throw exitStatus.error ?? new Error('Server process exited unexpectedly');
280
- }
281
- try {
282
- const response = await fetch(`${this._info.baseUrl}${this.options.healthCheckPath}`, {
283
- method: 'GET',
284
- signal: AbortSignal.timeout(1000),
285
- });
286
- if (response.ok || response.status === 404) {
287
- // 404 is okay - it means the server is running but might not have a health endpoint
288
- this.log('Server is ready');
289
- return;
290
- }
291
- }
292
- catch {
293
- // Server not ready yet
294
- }
295
- await sleep(checkInterval);
296
- }
297
- // Final check before throwing timeout error
298
- const finalExitStatus = checkExit();
299
- if (finalExitStatus.exited) {
300
- throw finalExitStatus.error ?? new Error('Server process exited unexpectedly');
301
- }
302
- throw new Error(`Server did not become ready within ${timeoutMs}ms`);
303
- }
304
- log(message) {
305
- if (this.options.debug) {
306
- console.log(`[TestServer] ${message}`);
307
- }
308
- }
309
- }
310
- exports.TestServer = TestServer;
311
- // ═══════════════════════════════════════════════════════════════════
312
- // UTILITIES
313
- // ═══════════════════════════════════════════════════════════════════
314
- /**
315
- * Find an available port
316
- */
317
- async function findAvailablePort() {
318
- // Use a simple approach: try to create a server on port 0 to get an available port
319
- const { createServer } = await import('net');
320
- return new Promise((resolve, reject) => {
321
- const server = createServer();
322
- server.listen(0, () => {
323
- const address = server.address();
324
- if (address && typeof address !== 'string') {
325
- const port = address.port;
326
- server.close(() => resolve(port));
327
- }
328
- else {
329
- reject(new Error('Could not get port'));
330
- }
331
- });
332
- server.on('error', reject);
333
- });
334
- }
335
- /**
336
- * Sleep for specified milliseconds
337
- */
338
- function sleep(ms) {
339
- return new Promise((resolve) => setTimeout(resolve, ms));
340
- }
341
- //# sourceMappingURL=test-server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-server.js","sourceRoot":"","sources":["../../../src/server/test-server.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file test-server.ts\n * @description Test server management for E2E testing\n */\n\nimport { spawn, ChildProcess } from 'child_process';\n\n// ═══════════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════════\n\nexport interface TestServerOptions {\n /** Port to run the server on (default: random available port) */\n port?: number;\n /** Command to start the server */\n command?: string;\n /** Working directory */\n cwd?: string;\n /** Environment variables */\n env?: Record<string, string>;\n /** Timeout for server startup in milliseconds (default: 30000) */\n startupTimeout?: number;\n /** Path to check for server readiness (default: /health) */\n healthCheckPath?: string;\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport interface TestServerInfo {\n /** Base URL of the server */\n baseUrl: string;\n /** Port the server is running on */\n port: number;\n /** Process ID (if available) */\n pid?: number;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// TEST SERVER CLASS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Manages test server lifecycle for E2E testing\n *\n * @example\n * ```typescript\n * // Start a server with custom command\n * const server = await TestServer.start({\n * command: 'node dist/main.js',\n * port: 3003,\n * cwd: './apps/my-server',\n * });\n *\n * // Or start an Nx project\n * const server = await TestServer.startNx('demo-public', { port: 3003 });\n *\n * // Use the server\n * console.log(server.info.baseUrl); // http://localhost:3003\n *\n * // Stop when done\n * await server.stop();\n * ```\n */\nexport class TestServer {\n private process: ChildProcess | null = null;\n private readonly options: Required<TestServerOptions>;\n private _info: TestServerInfo;\n private logs: string[] = [];\n\n private constructor(options: TestServerOptions, port: number) {\n this.options = {\n port,\n command: options.command ?? '',\n cwd: options.cwd ?? process.cwd(),\n env: options.env ?? {},\n startupTimeout: options.startupTimeout ?? 30000,\n healthCheckPath: options.healthCheckPath ?? '/health',\n debug: options.debug ?? false,\n };\n\n this._info = {\n baseUrl: `http://localhost:${port}`,\n port,\n };\n }\n\n /**\n * Start a test server with custom command\n */\n static async start(options: TestServerOptions): Promise<TestServer> {\n const port = options.port ?? (await findAvailablePort());\n const server = new TestServer(options, port);\n try {\n await server.startProcess();\n } catch (error) {\n await server.stop(); // Clean up spawned process to prevent leaks\n throw error;\n }\n return server;\n }\n\n /**\n * Start an Nx project as test server\n */\n static async startNx(project: string, options: Partial<TestServerOptions> = {}): Promise<TestServer> {\n // Validate project name contains only safe characters to prevent shell injection\n if (!/^[\\w-]+$/.test(project)) {\n throw new Error(\n `Invalid project name: ${project}. Must contain only alphanumeric, underscore, and hyphen characters.`,\n );\n }\n\n const port = options.port ?? (await findAvailablePort());\n\n const serverOptions: TestServerOptions = {\n ...options,\n port,\n command: `npx nx serve ${project} --port ${port}`,\n cwd: options.cwd ?? process.cwd(),\n };\n\n const server = new TestServer(serverOptions, port);\n try {\n await server.startProcess();\n } catch (error) {\n await server.stop(); // Clean up spawned process to prevent leaks\n throw error;\n }\n return server;\n }\n\n /**\n * Create a test server connected to an already running server\n */\n static connect(baseUrl: string): TestServer {\n const url = new URL(baseUrl);\n const port = parseInt(url.port, 10) || (url.protocol === 'https:' ? 443 : 80);\n\n const server = new TestServer(\n {\n command: '',\n port,\n },\n port,\n );\n\n server._info = {\n baseUrl: baseUrl.replace(/\\/$/, ''),\n port,\n };\n\n return server;\n }\n\n /**\n * Get server information\n */\n get info(): TestServerInfo {\n return { ...this._info };\n }\n\n /**\n * Stop the test server\n */\n async stop(): Promise<void> {\n if (this.process) {\n this.log('Stopping server...');\n\n // Try graceful shutdown first\n this.process.kill('SIGTERM');\n\n // Wait for process to exit\n const exitPromise = new Promise<void>((resolve) => {\n if (this.process) {\n this.process.once('exit', () => resolve());\n } else {\n resolve();\n }\n });\n\n // Force kill after timeout (but still wait for actual exit)\n const killTimeout = setTimeout(() => {\n if (this.process) {\n this.log('Force killing server after timeout...');\n this.process.kill('SIGKILL');\n }\n }, 5000);\n\n await exitPromise;\n clearTimeout(killTimeout);\n this.process = null;\n this.log('Server stopped');\n }\n }\n\n /**\n * Wait for server to be ready\n */\n async waitForReady(timeout?: number): Promise<void> {\n const timeoutMs = timeout ?? this.options.startupTimeout;\n const deadline = Date.now() + timeoutMs;\n const checkInterval = 100;\n\n while (Date.now() < deadline) {\n try {\n const response = await fetch(`${this._info.baseUrl}${this.options.healthCheckPath}`, {\n method: 'GET',\n signal: AbortSignal.timeout(1000),\n });\n\n if (response.ok || response.status === 404) {\n // 404 is okay - it means the server is running but might not have a health endpoint\n this.log('Server is ready');\n return;\n }\n } catch {\n // Server not ready yet\n }\n\n await sleep(checkInterval);\n }\n\n throw new Error(`Server did not become ready within ${timeoutMs}ms`);\n }\n\n /**\n * Restart the server\n */\n async restart(): Promise<void> {\n await this.stop();\n await this.startProcess();\n }\n\n /**\n * Get captured server logs\n */\n getLogs(): string[] {\n return [...this.logs];\n }\n\n /**\n * Clear captured logs\n */\n clearLogs(): void {\n this.logs = [];\n }\n\n // ═══════════════════════════════════════════════════════════════════\n // PRIVATE METHODS\n // ═══════════════════════════════════════════════════════════════════\n\n private async startProcess(): Promise<void> {\n if (!this.options.command) {\n // No command means we're connecting to an existing server\n await this.waitForReady();\n return;\n }\n\n this.log(`Starting server: ${this.options.command}`);\n\n const env = {\n ...process.env,\n ...this.options.env,\n PORT: String(this.options.port),\n };\n\n // Use shell: true to handle complex commands with quoted arguments\n // This avoids fragile command parsing with split(' ')\n this.process = spawn(this.options.command, [], {\n cwd: this.options.cwd,\n env,\n shell: true,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n // pid can be undefined if spawn fails\n if (this.process.pid !== undefined) {\n this._info.pid = this.process.pid;\n }\n\n // Track process exit for early failure detection\n let processExited = false;\n let exitCode: number | null = null;\n let exitError: Error | null = null;\n\n // Capture stdout\n this.process.stdout?.on('data', (data: Buffer) => {\n const text = data.toString();\n this.logs.push(text);\n if (this.options.debug) {\n console.log('[SERVER]', text);\n }\n });\n\n // Capture stderr\n this.process.stderr?.on('data', (data: Buffer) => {\n const text = data.toString();\n this.logs.push(`[ERROR] ${text}`);\n if (this.options.debug) {\n console.error('[SERVER ERROR]', text);\n }\n });\n\n // Handle spawn errors to prevent unhandled error events\n this.process.on('error', (err) => {\n this.logs.push(`[SPAWN ERROR] ${err.message}`);\n exitError = err;\n if (this.options.debug) {\n console.error('[SERVER SPAWN ERROR]', err);\n }\n });\n\n // Handle process exit - track early failures\n this.process.once('exit', (code) => {\n processExited = true;\n exitCode = code;\n this.log(`Server process exited with code ${code}`);\n });\n\n // Wait for server to be ready, but detect early process exit\n await this.waitForReadyWithExitDetection(() => {\n if (exitError) {\n return { exited: true, error: exitError };\n }\n if (processExited) {\n const recentLogs = this.logs.slice(-10).join('\\n');\n return {\n exited: true,\n error: new Error(`Server process exited unexpectedly with code ${exitCode}.\\n\\nRecent logs:\\n${recentLogs}`),\n };\n }\n return { exited: false };\n });\n }\n\n /**\n * Wait for server to be ready, but also detect early process exit\n */\n private async waitForReadyWithExitDetection(checkExit: () => { exited: boolean; error?: Error }): Promise<void> {\n const timeoutMs = this.options.startupTimeout;\n const deadline = Date.now() + timeoutMs;\n const checkInterval = 100;\n\n while (Date.now() < deadline) {\n // Check if process has exited before continuing to poll\n const exitStatus = checkExit();\n if (exitStatus.exited) {\n throw exitStatus.error ?? new Error('Server process exited unexpectedly');\n }\n\n try {\n const response = await fetch(`${this._info.baseUrl}${this.options.healthCheckPath}`, {\n method: 'GET',\n signal: AbortSignal.timeout(1000),\n });\n\n if (response.ok || response.status === 404) {\n // 404 is okay - it means the server is running but might not have a health endpoint\n this.log('Server is ready');\n return;\n }\n } catch {\n // Server not ready yet\n }\n\n await sleep(checkInterval);\n }\n\n // Final check before throwing timeout error\n const finalExitStatus = checkExit();\n if (finalExitStatus.exited) {\n throw finalExitStatus.error ?? new Error('Server process exited unexpectedly');\n }\n\n throw new Error(`Server did not become ready within ${timeoutMs}ms`);\n }\n\n private log(message: string): void {\n if (this.options.debug) {\n console.log(`[TestServer] ${message}`);\n }\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// UTILITIES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Find an available port\n */\nexport async function findAvailablePort(): Promise<number> {\n // Use a simple approach: try to create a server on port 0 to get an available port\n const { createServer } = await import('net');\n\n return new Promise((resolve, reject) => {\n const server = createServer();\n\n server.listen(0, () => {\n const address = server.address();\n if (address && typeof address !== 'string') {\n const port = address.port;\n server.close(() => resolve(port));\n } else {\n reject(new Error('Could not get port'));\n }\n });\n\n server.on('error', reject);\n });\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"]}
package/src/setup.js DELETED
@@ -1,30 +0,0 @@
1
- "use strict";
2
- /**
3
- * @file setup.ts
4
- * @description Jest setup file for @frontmcp/testing
5
- *
6
- * This file registers custom MCP matchers with Jest.
7
- * Include it in your Jest config's setupFilesAfterEnv:
8
- *
9
- * @example jest.config.ts
10
- * ```typescript
11
- * export default {
12
- * setupFilesAfterEnv: ['@frontmcp/testing/setup'],
13
- * };
14
- * ```
15
- *
16
- * Or use the preset:
17
- * ```typescript
18
- * export default {
19
- * preset: '@frontmcp/testing/jest-preset',
20
- * };
21
- * ```
22
- */
23
- Object.defineProperty(exports, "__esModule", { value: true });
24
- const globals_1 = require("@jest/globals");
25
- const mcp_matchers_1 = require("./matchers/mcp-matchers");
26
- // Register custom matchers with Jest
27
- globals_1.expect.extend(mcp_matchers_1.mcpMatchers);
28
- // Import type augmentation
29
- require("./matchers/matcher-types");
30
- //# sourceMappingURL=setup.js.map
package/src/setup.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/setup.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;AAEH,2CAAuC;AACvC,0DAAsD;AAEtD,qCAAqC;AACrC,gBAAM,CAAC,MAAM,CAAC,0BAAW,CAAC,CAAC;AAE3B,2BAA2B;AAC3B,oCAAkC","sourcesContent":["/**\n * @file setup.ts\n * @description Jest setup file for @frontmcp/testing\n *\n * This file registers custom MCP matchers with Jest.\n * Include it in your Jest config's setupFilesAfterEnv:\n *\n * @example jest.config.ts\n * ```typescript\n * export default {\n * setupFilesAfterEnv: ['@frontmcp/testing/setup'],\n * };\n * ```\n *\n * Or use the preset:\n * ```typescript\n * export default {\n * preset: '@frontmcp/testing/jest-preset',\n * };\n * ```\n */\n\nimport { expect } from '@jest/globals';\nimport { mcpMatchers } from './matchers/mcp-matchers';\n\n// Register custom matchers with Jest\nexpect.extend(mcpMatchers);\n\n// Import type augmentation\nimport './matchers/matcher-types';\n"]}
@@ -1,10 +0,0 @@
1
- "use strict";
2
- /**
3
- * @file transport/index.ts
4
- * @description Transport layer exports
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.StreamableHttpTransport = void 0;
8
- var streamable_http_transport_1 = require("./streamable-http.transport");
9
- Object.defineProperty(exports, "StreamableHttpTransport", { enumerable: true, get: function () { return streamable_http_transport_1.StreamableHttpTransport; } });
10
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transport/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AASH,yEAAsE;AAA7D,oIAAA,uBAAuB,OAAA","sourcesContent":["/**\n * @file transport/index.ts\n * @description Transport layer exports\n */\n\nexport type {\n McpTransport,\n TransportConfig,\n TransportState,\n JsonRpcRequest,\n JsonRpcResponse,\n} from './transport.interface';\nexport { StreamableHttpTransport } from './streamable-http.transport';\n"]}