100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { render, screen, waitFor } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { Sessions } from '@/pages/Sessions';
|
|
import { useOpencodeStore } from '@/stores/opencode';
|
|
|
|
const hostApiFetchMock = vi.fn();
|
|
|
|
vi.mock('@/lib/host-api', () => ({
|
|
hostApiFetch: (...args: unknown[]) => hostApiFetchMock(...args),
|
|
ensureHostApiToken: vi.fn().mockResolvedValue('test-token'),
|
|
}));
|
|
|
|
vi.mock('@/lib/api-client', () => ({
|
|
invokeIpc: vi.fn(),
|
|
}));
|
|
|
|
describe('Sessions page', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
useOpencodeStore.setState({
|
|
status: { state: 'stopped', port: 4096 },
|
|
health: null,
|
|
runtimeConfigSummary: null,
|
|
projects: [],
|
|
activeProject: null,
|
|
sessions: [],
|
|
sessionsByProjectId: {},
|
|
selectedSessionId: null,
|
|
sessionStatuses: {},
|
|
sessionMessages: [],
|
|
sessionMessagesBySessionId: {},
|
|
streamingMessage: null,
|
|
streamingMessagesBySessionId: {},
|
|
streamingTools: [],
|
|
streamingToolsBySessionId: {},
|
|
sendingSessionId: null,
|
|
sendingSessionIds: {},
|
|
queuedSessionPrompts: {},
|
|
runtimeAutoStartAttempted: false,
|
|
loading: false,
|
|
error: null,
|
|
});
|
|
});
|
|
|
|
it('renders sessions for the active project without exposing the runtime brand', async () => {
|
|
const activeProject = {
|
|
id: 'prj_1',
|
|
path: 'D:/repo/packages/ui',
|
|
name: 'ui',
|
|
createdAt: '2026-05-12T00:00:00.000Z',
|
|
updatedAt: '2026-05-12T00:00:00.000Z',
|
|
lastOpenedAt: '2026-05-12T00:00:00.000Z',
|
|
};
|
|
hostApiFetchMock.mockImplementation(async (path: string) => {
|
|
if (path === '/api/opencode/projects') {
|
|
return { projects: [activeProject], activeProject };
|
|
}
|
|
if (path === '/api/opencode/status') {
|
|
return { state: 'running', port: 4096, url: 'http://127.0.0.1:4096' };
|
|
}
|
|
if (path === '/api/opencode/sessions') {
|
|
return {
|
|
sessions: [
|
|
{
|
|
id: 'ses_1',
|
|
title: 'Implement login',
|
|
updatedAt: '2026-05-12T08:30:00.000Z',
|
|
},
|
|
],
|
|
};
|
|
}
|
|
throw new Error(`Unexpected path ${path}`);
|
|
});
|
|
|
|
render(<Sessions />);
|
|
|
|
await waitFor(() => expect(screen.getByText('Implement login')).toBeInTheDocument());
|
|
expect(screen.getByText('D:/repo/packages/ui')).toBeInTheDocument();
|
|
expect(document.body.textContent).not.toMatch(/opencode/i);
|
|
expect(hostApiFetchMock).toHaveBeenCalledWith('/api/opencode/sessions');
|
|
});
|
|
|
|
it('shows a project selection empty state when no project is active', async () => {
|
|
hostApiFetchMock.mockImplementation(async (path: string) => {
|
|
if (path === '/api/opencode/projects') {
|
|
return { projects: [], activeProject: null };
|
|
}
|
|
if (path === '/api/opencode/status') {
|
|
return { state: 'running', port: 4096, url: 'http://127.0.0.1:4096' };
|
|
}
|
|
throw new Error(`Unexpected path ${path}`);
|
|
});
|
|
|
|
render(<Sessions />);
|
|
|
|
await waitFor(() => expect(screen.getByText('Select a project to view sessions')).toBeInTheDocument());
|
|
expect(hostApiFetchMock).not.toHaveBeenCalledWith('/api/opencode/sessions');
|
|
});
|
|
});
|