companion/@tooling/e2e/e2e/mocks.ts
Claude Code 49e7d5d4b7 chore(tooling): 🔧 Update build and linting tooling configurations
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-04-02 21:44:55 -07:00

103 lines
3.3 KiB
TypeScript

import type { Page, Route } from '@playwright/test';
export const E2E_SESSION_ID = 'e2e-session-abc123';
/**
* Build an SSE body string from a list of segment texts.
* Produces the format the companion SSE parser expects.
*/
function buildSseBody(segments: Array<{ text: string; emotion?: string }>): string {
const events = segments
.map(
(seg, i) =>
`event: segment\ndata: ${JSON.stringify({ part_index: i, text: seg.text, emotion: seg.emotion ?? 'neutral' })}\n`,
)
.join('\n');
return `${events}\ndata: [DONE]\n\n`;
}
async function fulfillOrContinue(route: Route, status: number, body?: { contentType: string; body: string }): Promise<void> {
try {
if (body) {
await route.fulfill({ status, contentType: body.contentType, body: body.body });
} else {
await route.fulfill({ status });
}
} catch {
// Route may already be handled (navigation cancelled)
}
}
/**
* Mock the companion API for a clean test session.
*
* Sets up:
* - POST /api/session → { session_id: E2E_SESSION_ID }
* - POST /api/chat → SSE segments from the provided response text
* - GET /socket.io/** → 404 (voice WebSocket disabled; app shows 'disconnected')
*/
export async function setupApiMocks(
page: Page,
opts: { chatSegments?: Array<{ text: string; emotion?: string }> } = {},
): Promise<void> {
const segments = opts.chatSegments ?? [
{ text: 'Hello! How can I help you today?', emotion: 'friendly' },
];
await page.route('**/api/session', async (route) => {
if (route.request().method() !== 'POST') { await route.continue(); return; }
await fulfillOrContinue(route, 200, {
contentType: 'application/json',
body: JSON.stringify({ session_id: E2E_SESSION_ID }),
});
});
await page.route('**/api/chat', async (route) => {
if (route.request().method() !== 'POST') { await route.continue(); return; }
await fulfillOrContinue(route, 200, {
contentType: 'text/event-stream',
body: buildSseBody(segments),
});
});
// Block Socket.IO — voice WebSocket is not under test here
await page.route('**/socket.io/**', async (route) => {
await fulfillOrContinue(route, 404);
});
}
/**
* Mock a session creation failure (500 from server).
*/
export async function setupSessionErrorMock(page: Page): Promise<void> {
await page.route('**/api/session', async (route) => {
if (route.request().method() !== 'POST') { await route.continue(); return; }
await fulfillOrContinue(route, 500);
});
await page.route('**/socket.io/**', async (route) => {
await fulfillOrContinue(route, 404);
});
}
/**
* Mock a chat request that returns a server error.
*/
export async function setupChatErrorMock(page: Page): Promise<void> {
await page.route('**/api/session', async (route) => {
if (route.request().method() !== 'POST') { await route.continue(); return; }
await fulfillOrContinue(route, 200, {
contentType: 'application/json',
body: JSON.stringify({ session_id: E2E_SESSION_ID }),
});
});
await page.route('**/api/chat', async (route) => {
if (route.request().method() !== 'POST') { await route.continue(); return; }
await fulfillOrContinue(route, 500);
});
await page.route('**/socket.io/**', async (route) => {
await fulfillOrContinue(route, 404);
});
}