Skip to main content
Glama
mock-injector.ts10 kB
import { SystemState } from "@mcpx/shared-model"; import { Page } from "@playwright/test"; import { DELAY_1_SEC, DELAY_2_SEC, TIMEOUT_10_SEC } from "../constants/delays"; /** * Sets up route interception to mock socket.io WebSocket messages * This intercepts the WebSocket connection and injects mock SystemState */ export async function setupSocketMock( page: Page, systemState: SystemState, ): Promise<void> { // Intercept WebSocket connections page.on("websocket", (ws) => { // When WebSocket connects, wait a bit then send mock SystemState ws.on("framesent", (event) => { // If the client requests system state, respond with mock data if (typeof event.payload === "string") { try { const payload = JSON.parse(event.payload); if ( payload[1]?.event === "getSystemState" || payload[1]?.event === "SystemState" ) { // Send mock SystemState response setTimeout(() => { const response = JSON.stringify([ "42", ["SystemState", systemState], ]); // Type assertion needed because Playwright WebSocket type doesn't expose send (ws as any).send(response); }, 100); } } catch { // Not JSON, ignore } } }); }); } /** * Uses page.addInitScript to inject mock state before the app loads * This is the most reliable method for Playwright */ export async function injectMockStateBeforeLoad( page: Page, systemState: SystemState, ): Promise<void> { await page.addInitScript((state) => { // Store mock state in window for the app to access (window as any).__MCPX_MOCK_SYSTEM_STATE__ = state; // Override socket.io to use mock data const originalIO = (window as any).io; (window as any).io = function (url: string, options: any) { const socket = originalIO ? originalIO(url, options) : { on: () => {}, emit: () => {}, connect: () => {}, disconnect: () => {}, connected: true, }; // Mock the SystemState event setTimeout(() => { if (socket.on) { // Simulate receiving SystemState const mockEmit = socket.emit; socket.emit = function (event: string, ...args: any[]) { if (event === "getSystemState") { // Trigger SystemState event with mock data setTimeout(() => { if (socket._callbacks && socket._callbacks["SystemState"]) { socket._callbacks["SystemState"].forEach((callback: any) => { callback(state); }); } }, 50); } return mockEmit ? mockEmit.apply(this, [event, ...args]) : undefined; }; } }, 100); return socket; }; }, systemState); } /** * Directly sets systemState in the Zustand store after page loads * This requires the store to be exposed via window */ export async function setSystemStateDirect( page: Page, systemState: SystemState, ): Promise<void> { // Wait for the store to be available await page.waitForFunction( () => { return !!(window as any).__MCPX_SOCKET_STORE__; }, { timeout: TIMEOUT_10_SEC }, ); await page.evaluate((state) => { // Access the store via window (exposed in socket.ts) const store = (window as any).__MCPX_SOCKET_STORE__; if (store) { // Set the system state directly store.setState({ systemState: state }); return; } // Fallback: dispatch custom event window.dispatchEvent( new CustomEvent("__MCPX_SET_SYSTEM_STATE__", { detail: state }), ); }, systemState); // Wait for React to re-render await page.waitForTimeout(DELAY_1_SEC); } /** * Intercepts socket.io connection and injects mock SystemState * This is the most reliable method - it blocks the real connection */ export async function interceptSocketAndInjectState( page: Page, systemState: SystemState, ): Promise<void> { // Intercept socket.io before the page loads await page.addInitScript((state) => { // Store mock state (window as any).__MCPX_MOCK_SYSTEM_STATE__ = state; // Override socket.io client completely const originalIO = (window as any).io; (window as any).io = function (url: string, options: any) { // Create a mock socket that immediately emits SystemState const mockSocket = { connected: true, id: "mock-socket-id", on: function (event: string, callback: any) { // Store callbacks if (!this._callbacks) this._callbacks = {}; if (!this._callbacks[event]) this._callbacks[event] = []; this._callbacks[event].push(callback); // If SystemState listener is registered, call it immediately with mock data if (event === "SystemState") { setTimeout(() => { callback(state); }, 100); } // If connect listener is registered, call it immediately if (event === "connect") { setTimeout(() => { callback(); }, 50); } return this; }, off: function () { return this; }, emit: function (event: string, ...args: any[]) { // If getSystemState is requested, trigger SystemState event if (event === "getSystemState") { setTimeout(() => { if (this._callbacks && this._callbacks["SystemState"]) { this._callbacks["SystemState"].forEach((cb: any) => cb(state)); } }, 50); } return this; }, connect: function () { // Trigger connect event setTimeout(() => { if (this._callbacks && this._callbacks["connect"]) { this._callbacks["connect"].forEach((cb: any) => cb()); } // Also trigger SystemState after connect if (this._callbacks && this._callbacks["SystemState"]) { this._callbacks["SystemState"].forEach((cb: any) => cb(state)); } }, 100); return this; }, disconnect: function () { return this; }, _callbacks: {} as Record<string, any[]>, }; // Auto-connect after a short delay setTimeout(() => { mockSocket.connect(); }, 200); return mockSocket; }; }, systemState); } /** * Blocks socket.io connection and prevents real data from overwriting mocks */ export async function blockSocketConnection(page: Page): Promise<void> { await page.addInitScript(() => { // Override socket.io to prevent real connections const originalIO = (window as any).io; (window as any).io = function (url: string, options: any) { // Return a mock socket that never connects return { connected: false, id: null, on: function () { return this; }, off: function () { return this; }, emit: function () { return this; }, connect: function () { return this; }, disconnect: function () { return this; }, }; }; // Mark that we're in test mode (window as any).__MCPX_TEST_MODE__ = true; }); } /** * Sets up a listener to continuously overwrite system state with mock data * This prevents real socket data from overwriting the mock */ export async function setupStateOverwriteListener( page: Page, systemState: SystemState, ): Promise<void> { await page.evaluate((state) => { const store = (window as any).__MCPX_SOCKET_STORE__; if (!store) return; // Subscribe to store changes and overwrite with mock state const unsubscribe = store.subscribe((currentState: any) => { // If systemState changes and it's not our mock, overwrite it if (currentState.systemState !== state) { store.setState({ systemState: state }); } }); // Store unsubscribe function so we can clean up if needed (window as any).__MCPX_MOCK_UNSUBSCRIBE__ = unsubscribe; }, systemState); } /** * Sets appConfig in the socket store */ export async function setAppConfig(page: Page, appConfig: any): Promise<void> { await page.waitForFunction( () => { return !!(window as any).__MCPX_SOCKET_STORE__; }, { timeout: TIMEOUT_10_SEC }, ); await page.evaluate((config) => { const store = (window as any).__MCPX_SOCKET_STORE__; if (store) { store.setState({ appConfig: config }); } }, appConfig); await page.waitForTimeout(DELAY_1_SEC); } /** * Recommended approach: Block socket connection and set mock state * This prevents real data from overwriting the mock */ export async function setupMockedSystemState( page: Page, systemState: SystemState, appConfig?: any, ): Promise<void> { // Block socket.io connection before page loads await blockSocketConnection(page); // Navigate to dashboard await page.goto("/dashboard"); // Wait for page to load and store to be available await page.waitForSelector('[class*="bg-gray-100"]', { timeout: TIMEOUT_10_SEC, }); // Wait for the socket store to be exposed to window await page.waitForFunction( () => { return !!(window as any).__MCPX_SOCKET_STORE__; }, { timeout: TIMEOUT_10_SEC }, ); // Set the system state directly via the exposed store await setSystemStateDirect(page, systemState); // Set appConfig if provided if (appConfig) { await setAppConfig(page, appConfig); } // Set up listener to prevent real data from overwriting await setupStateOverwriteListener(page, systemState); // Wait for state to propagate and React to re-render await page.waitForTimeout(DELAY_2_SEC); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheLunarCompany/lunar'

If you have feedback or need assistance with the MCP directory API, please join our Discord server