React Native Debugger MCP

by twodoorsdev
MIT License
1
import { describe, expect, it, beforeEach, mock } from 'bun:test'; import { getConnectedAppsTool } from './tool'; import type { InspectorApp } from '@/types'; const mockApps: InspectorApp[] = [ { id: 'test-app-1', description: 'Test App 1', deviceName: 'Test Device 1', webSocketDebuggerUrl: 'ws://localhost:8081/debugger-proxy?role=debugger&targetId=test-app-1', title: 'Test App 1', type: 'app', devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/inspector.html?ws=localhost:8081/debugger-proxy&targetId=test-app-1', reactNative: { logicalDeviceId: 'device-1', capabilities: { prefersFuseboxFrontend: true, nativeSourceCodeFetching: false, nativePageReloads: true, }, }, }, { id: 'test-app-2', description: 'Test App 2', deviceName: 'Test Device 2', webSocketDebuggerUrl: 'ws://localhost:8081/debugger-proxy?role=debugger&targetId=test-app-2', title: 'Test App 2', type: 'app', devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/inspector.html?ws=localhost:8081/debugger-proxy&targetId=test-app-2', reactNative: { logicalDeviceId: 'device-2', capabilities: { prefersFuseboxFrontend: true, nativeSourceCodeFetching: false, nativePageReloads: true, }, }, }, ]; describe('getConnectedAppsTool', () => { beforeEach(() => { // Reset all mocks before each test mock.restore(); }); it('should return connected apps when Metro server is running', async () => { // Mock the module to return our mock data mock.module( '@expo/cli/build/src/start/server/middleware/inspector/JsInspector', () => ({ queryAllInspectorAppsAsync: async () => mockApps, }), ); const result = await getConnectedAppsTool.handler({ metroServerPort: 8081, }); expect(result.content).toHaveLength(2); // Expect two messages, one for each app expect(result.content[0].type).toBe('text'); expect(result.content[1].type).toBe('text'); const firstApp = JSON.parse(result.content[0].text as string); const secondApp = JSON.parse(result.content[1].text as string); expect(firstApp).toEqual({ id: mockApps[0].id, description: mockApps[0].description, deviceName: mockApps[0].deviceName, webSocketDebuggerUrl: mockApps[0].webSocketDebuggerUrl, title: mockApps[0].title, type: mockApps[0].type, devtoolsFrontendUrl: mockApps[0].devtoolsFrontendUrl, reactNative: mockApps[0].reactNative, }); expect(secondApp).toEqual({ id: mockApps[1].id, description: mockApps[1].description, deviceName: mockApps[1].deviceName, webSocketDebuggerUrl: mockApps[1].webSocketDebuggerUrl, title: mockApps[1].title, type: mockApps[1].type, devtoolsFrontendUrl: mockApps[1].devtoolsFrontendUrl, reactNative: mockApps[1].reactNative, }); }); it('should handle case when no apps are connected', async () => { // Mock the module to return an empty array mock.module( '@expo/cli/build/src/start/server/middleware/inspector/JsInspector', () => ({ queryAllInspectorAppsAsync: async () => [], }), ); const result = await getConnectedAppsTool.handler({ metroServerPort: 8081, }); expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('No connected apps found'); expect(result.isError).toBe(true); }); it('should handle errors when Metro server is not running', async () => { const error = new Error('Connection refused'); // Mock the module to throw an error mock.module( '@expo/cli/build/src/start/server/middleware/inspector/JsInspector', () => ({ queryAllInspectorAppsAsync: async () => { throw error; }, }), ); const result = await getConnectedAppsTool.handler({ metroServerPort: 8081, }); expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Error: Connection refused'); expect(result.isError).toBe(true); }); it('should validate input schema', () => { const schema = getConnectedAppsTool.inputSchema; expect(schema).toBeDefined(); const properties = schema.properties as { metroServerPort: { type: string }; }; expect(properties).toHaveProperty('metroServerPort'); expect(properties.metroServerPort.type).toBe('number'); }); });
ID: ax2kpj513o