11import { describe , test , expect , beforeAll , afterAll , beforeEach , afterEach } from 'vitest'
22import { chromium , Browser , Page } from 'playwright'
3- import { readFileSync } from 'fs'
4- import { join , dirname } from 'path'
5- import { fileURLToPath } from 'url'
6-
7- const __dirname = dirname ( fileURLToPath ( import . meta. url ) )
8- const testDir = join ( __dirname , '..' )
9- const testStateFile = join ( testDir , 'node_modules/.cache/use-mcp-tests/test-state.json' )
10-
11- // Get MCP servers to test (ports determined at runtime)
12- function getMCPServers ( ) {
13- try {
14- const stateData = readFileSync ( testStateFile , 'utf-8' )
15- const state = JSON . parse ( stateData )
16-
17- if ( ! state . honoPort ) {
18- throw new Error ( 'hono-mcp port not found in test state' )
19- }
20-
21- if ( ! state . cfAgentsPort ) {
22- throw new Error ( 'cf-agents port not found in test state' )
23- }
24-
25- return [
26- {
27- name : 'hono-mcp' ,
28- url : `http://localhost:${ state . honoPort } /mcp` ,
29- expectedTools : 1 , // Minimum expected tools count
30- } ,
31- {
32- name : 'cf-agents' ,
33- url : `http://localhost:${ state . cfAgentsPort } /public/mcp` ,
34- expectedTools : 1 , // Minimum expected tools count
35- } ,
36- {
37- name : 'cf-agents-sse' ,
38- url : `http://localhost:${ state . cfAgentsPort } /public/sse` ,
39- expectedTools : 1 , // Minimum expected tools count
40- } ,
41- {
42- name : 'cf-agents-auth' ,
43- url : `http://localhost:${ state . cfAgentsPort } /mcp` ,
44- expectedTools : 1 , // Minimum expected tools count
45- } ,
46- {
47- name : 'cf-agents-auth-sse' ,
48- url : `http://localhost:${ state . cfAgentsPort } /sse` ,
49- expectedTools : 1 , // Minimum expected tools count
50- } ,
51- ]
52- } catch ( error ) {
53- throw new Error ( `Test environment not properly initialized: ${ error } ` )
54- }
55- }
56-
57- async function connectToMCPServer (
58- page : Page ,
59- serverUrl : string ,
60- transportType : 'auto' | 'http' | 'sse' = 'auto' ,
61- ) : Promise < { success : boolean ; tools : string [ ] ; debugLog : string } > {
62- // Navigate to the inspector
63- const stateData = readFileSync ( testStateFile , 'utf-8' )
64- const state = JSON . parse ( stateData )
65-
66- if ( ! state . staticPort ) {
67- throw new Error ( 'Static server port not available - state: ' + JSON . stringify ( state ) )
68- }
69- const staticPort = state . staticPort
70-
71- await page . goto ( `http://localhost:${ staticPort } ` )
72-
73- // Wait for the page to load
74- await page . waitForSelector ( 'input[placeholder="Enter MCP server URL"]' , { timeout : 10000 } )
75-
76- // Enter the server URL
77- const urlInput = page . locator ( 'input[placeholder="Enter MCP server URL"]' )
78- await urlInput . fill ( serverUrl )
79-
80- // Set transport type
81- const transportSelect = page . locator ( 'select' )
82- await transportSelect . selectOption ( transportType )
83-
84- // Click connect button
85- const connectButton = page . locator ( 'button:has-text("Connect")' )
86- await connectButton . click ( )
87-
88- // Wait for connection attempt to complete (max 10 seconds)
89- await page . waitForTimeout ( 1000 ) // Initial wait
90-
91- // Check for connection status
92- let attempts = 0
93- const maxAttempts = 20 // 10 seconds total (500ms * 20)
94- let isConnected = false
95-
96- while ( attempts < maxAttempts && ! isConnected ) {
97- try {
98- // Check if status badge shows "Connected"
99- const statusBadge = page . locator ( '.px-2.py-1.rounded-full' )
100- if ( ( await statusBadge . count ( ) ) > 0 ) {
101- const statusText = await statusBadge . textContent ( { timeout : 500 } )
102- if ( statusText ?. toLowerCase ( ) . includes ( 'connected' ) ) {
103- isConnected = true
104- break
105- }
106- }
107-
108- // Also check if tools count is > 0
109- const toolsHeader = page . locator ( 'h3:has-text("Available Tools")' )
110- if ( ( await toolsHeader . count ( ) ) > 0 ) {
111- const toolsText = await toolsHeader . textContent ( )
112- if ( toolsText && / \d + / . test ( toolsText ) ) {
113- const toolsCount = parseInt ( toolsText . match ( / \d + / ) ?. [ 0 ] || '0' )
114- if ( toolsCount > 0 ) {
115- isConnected = true
116- break
117- }
118- }
119- }
120- } catch ( e ) {
121- // Continue waiting
122- }
123-
124- await page . waitForTimeout ( 500 )
125- attempts ++
126- }
127-
128- // Extract available tools
129- const tools : string [ ] = [ ]
130- try {
131- // Look for tool cards in the tools container
132- const toolCards = page . locator ( '.bg-white.rounded.border' )
133- const toolCount = await toolCards . count ( )
134-
135- for ( let i = 0 ; i < toolCount ; i ++ ) {
136- const toolNameElement = toolCards . nth ( i ) . locator ( 'h4.font-bold.text-base.text-black' )
137- const toolName = await toolNameElement . textContent ( )
138- if ( toolName ?. trim ( ) ) {
139- tools . push ( toolName . trim ( ) )
140- }
141- }
142- } catch ( e ) {
143- console . warn ( 'Could not extract tools list:' , e )
144- }
145-
146- // Extract debug log
147- let debugLog = ''
148- try {
149- const debugContainer = page . locator ( '.h-32.overflow-y-auto.font-mono.text-xs' )
150- if ( ( await debugContainer . count ( ) ) > 0 ) {
151- debugLog = ( await debugContainer . first ( ) . textContent ( ) ) || ''
152- }
153- } catch ( e ) {
154- console . warn ( 'Could not extract debug log:' , e )
155- }
156-
157- return {
158- success : isConnected ,
159- tools,
160- debugLog,
161- }
162- }
3+ import { SERVER_CONFIGS } from './server-configs.js'
4+ import { getTestState , connectToMCPServer , cleanupProcess } from './test-utils.js'
1635
1646describe ( 'MCP Connection Integration Tests' , ( ) => {
1657 let browser : Browser
@@ -176,33 +18,21 @@ describe('MCP Connection Integration Tests', () => {
17618 await browser . close ( )
17719 }
17820
179- // Force cleanup before Vitest exits - don't throw errors
21+ // Force cleanup before Vitest exits
18022 const state = globalThis . __INTEGRATION_TEST_STATE__
181- try {
182- if ( state ?. honoServer && ! state . honoServer . killed ) {
183- console . log ( '🔥 Force cleanup before test exit...' )
184- state . honoServer . kill ( 'SIGKILL' )
185- }
186- } catch ( e ) {
187- // Ignore errors - process might already be dead
23+ if ( state ?. honoServer ) {
24+ cleanupProcess ( state . honoServer , 'hono-mcp' )
18825 }
189-
190- try {
191- if ( state ?. cfAgentsServer && ! state . cfAgentsServer . killed ) {
192- console . log ( '🔥 Force cleanup cf-agents server...' )
193- state . cfAgentsServer . kill ( 'SIGKILL' )
194- }
195- } catch ( e ) {
196- // Ignore errors - process might already be dead
26+ if ( state ?. cfAgentsServer ) {
27+ cleanupProcess ( state . cfAgentsServer , 'cf-agents' )
19728 }
198-
199- try {
200- if ( state ?. staticServer ) {
29+ if ( state ?. staticServer ) {
30+ try {
20131 state . staticServer . close ( )
20232 state . staticServer . closeAllConnections ?.( )
33+ } catch ( e ) {
34+ // Ignore errors
20335 }
204- } catch ( e ) {
205- // Ignore errors
20636 }
20737 } )
20838
@@ -225,59 +55,59 @@ describe('MCP Connection Integration Tests', () => {
22555 }
22656 } )
22757
228- const testScenarios = [
229- // Hono examples (MCP only)
230- { serverName : 'hono-mcp' , transportType : 'auto' as const } ,
231- { serverName : 'hono-mcp' , transportType : 'http' as const } ,
232-
233- // Agents, no auth
234- { serverName : 'cf-agents' , transportType : 'auto' as const } ,
235- { serverName : 'cf-agents' , transportType : 'http' as const } ,
236- { serverName : 'cf-agents-sse' , transportType : 'sse' as const } ,
237- { serverName : 'cf-agents-sse' , transportType : 'auto' as const } ,
238-
239- // Agents, with auth
240- { serverName : 'cf-agents-auth' , transportType : 'auto' as const } ,
241- { serverName : 'cf-agents-auth' , transportType : 'http' as const } ,
242- { serverName : 'cf-agents-auth-sse' , transportType : 'sse' as const } ,
243- { serverName : 'cf-agents-auth-sse' , transportType : 'auto' as const } ,
244- ]
245-
246- test . each ( testScenarios ) (
247- 'should connect to $serverName with $transportType transport' ,
248- async ( { serverName, transportType } ) => {
249- const servers = getMCPServers ( )
250- const server = servers . find ( ( s ) => s . name === serverName )
251-
252- if ( ! server ) {
253- throw new Error ( `Server ${ serverName } not found. Available servers: ${ servers . map ( ( s ) => s . name ) . join ( ', ' ) } ` )
254- }
255-
256- console . log ( `\n🔗 Testing connection to ${ server . name } at ${ server . url } with ${ transportType } transport` )
257-
258- const result = await connectToMCPServer ( page , server . url , transportType )
259-
260- if ( result . success ) {
261- console . log ( `✅ Successfully connected to ${ server . name } ` )
262- console . log ( `📋 Available tools (${ result . tools . length } ):` )
263- result . tools . forEach ( ( tool , index ) => {
264- console . log ( ` ${ index + 1 } . ${ tool } ` )
265- } )
266-
267- // Verify connection success
268- expect ( result . success ) . toBe ( true )
269- expect ( result . tools . length ) . toBeGreaterThanOrEqual ( server . expectedTools )
270- } else {
271- console . log ( `❌ Failed to connect to ${ server . name } ` )
272- if ( result . debugLog ) {
273- console . log ( `🐛 Debug log:` )
274- console . log ( result . debugLog )
58+ // Test each server configuration
59+ for ( const serverConfig of SERVER_CONFIGS ) {
60+ describe ( `${ serverConfig . name } server` , ( ) => {
61+ // Test each endpoint for this server
62+ for ( const endpoint of serverConfig . endpoints ) {
63+ // Test each transport type for this endpoint
64+ for ( const transportType of endpoint . transportTypes ) {
65+ test ( `should connect to ${ endpoint . path } with ${ transportType } transport` , async ( ) => {
66+ const testState = getTestState ( )
67+ const port = testState [ serverConfig . portKey ]
68+
69+ if ( ! port ) {
70+ throw new Error ( `Port not found for ${ serverConfig . name } (${ serverConfig . portKey } )` )
71+ }
72+
73+ const serverUrl = `http://localhost:${ port } ${ endpoint . path } `
74+ console . log ( `\n🔗 Testing connection to ${ serverConfig . name } at ${ serverUrl } with ${ transportType } transport` )
75+
76+ const result = await connectToMCPServer ( page , serverUrl , transportType )
77+
78+ if ( result . success ) {
79+ console . log ( `✅ Successfully connected to ${ serverConfig . name } ` )
80+ console . log ( `📋 Available tools (${ result . tools . length } ):` )
81+ result . tools . forEach ( ( tool , index ) => {
82+ console . log ( ` ${ index + 1 } . ${ tool } ` )
83+ } )
84+
85+ // Verify connection success
86+ expect ( result . success ) . toBe ( true )
87+ expect ( result . tools . length ) . toBeGreaterThanOrEqual ( serverConfig . expectedTools )
88+ } else {
89+ console . log ( `❌ Failed to connect to ${ serverConfig . name } ` )
90+ if ( result . debugLog ) {
91+ console . log ( `🐛 Debug log:` )
92+ console . log ( result . debugLog )
93+ }
94+
95+ // Check if this is an expected failure case
96+ const isExpectedFailure = endpoint . path . endsWith ( '/sse' ) && transportType === 'auto'
97+
98+ if ( isExpectedFailure ) {
99+ console . log ( `ℹ️ Expected failure: SSE endpoint with auto transport` )
100+ expect ( result . success ) . toBe ( false )
101+ } else {
102+ // Fail the test with detailed information
103+ throw new Error (
104+ `Expected to connect to ${ serverConfig . name } with ${ transportType } transport but failed. Debug log: ${ result . debugLog } ` ,
105+ )
106+ }
107+ }
108+ } , 45000 )
275109 }
276-
277- // Fail the test with detailed information
278- throw new Error ( `Expected to connect to ${ server . name } with ${ transportType } transport but failed. Debug log: ${ result . debugLog } ` )
279110 }
280- } ,
281- 45000 ,
282- )
111+ } )
112+ }
283113} )
0 commit comments