agent-ecosystem/test/renderer/components/team/taskLogs/TaskLogStreamSection.opencode-fixture-e2e.test.tsx

141 lines
4.7 KiB
TypeScript

import { readFile } from 'fs/promises';
import path from 'path';
import React, { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { OpenCodeTaskLogStreamSource } from '../../../../../src/main/services/team/taskLogs/stream/OpenCodeTaskLogStreamSource';
import { BoardTaskExactLogChunkBuilder } from '../../../../../src/main/services/team/taskLogs/exact/BoardTaskExactLogChunkBuilder';
import { TooltipProvider } from '../../../../../src/renderer/components/ui/tooltip';
import type { OpenCodeRuntimeTranscriptResponse } from '../../../../../src/main/services/runtime/ClaudeMultimodelBridgeService';
import type { BoardTaskLogStreamResponse, TeamTask } from '../../../../../src/shared/types';
const TEAM_NAME = 'relay-works-10';
const TASK_ID = '0b3a0624-5d66-4067-848e-5a74a1720c0d';
const FIXTURE_PATH = path.resolve(
process.cwd(),
'test/fixtures/team/opencode/relay-works-10-jack-projection-transcript.json'
);
const apiState = {
getTaskLogStream: vi.fn<
(teamName: string, taskId: string) => Promise<BoardTaskLogStreamResponse>
>(),
onTeamChange: vi.fn<(callback: (event: unknown, data: unknown) => void) => () => void>(),
};
vi.mock('@renderer/api', () => ({
api: {
teams: {
getTaskLogStream: (...args: Parameters<typeof apiState.getTaskLogStream>) =>
apiState.getTaskLogStream(...args),
onTeamChange: (...args: Parameters<typeof apiState.onTeamChange>) =>
apiState.onTeamChange(...args),
},
},
}));
import { TaskLogStreamSection } from '@renderer/components/team/taskLogs/TaskLogStreamSection';
const RELAY_WORKS_10_TASK: TeamTask = {
id: TASK_ID,
displayId: '0b3a0624',
subject: 'Define calculator arithmetic behavior',
owner: 'jack',
status: 'completed',
createdAt: '2026-04-24T20:29:03.133Z',
updatedAt: '2026-04-24T20:29:34.157Z',
workIntervals: [
{
startedAt: '2026-04-24T20:29:03.133Z',
completedAt: '2026-04-24T20:29:34.157Z',
},
],
};
async function loadFixtureTranscript(): Promise<
NonNullable<OpenCodeRuntimeTranscriptResponse['transcript']>
> {
const parsed = JSON.parse(await readFile(FIXTURE_PATH, 'utf8')) as OpenCodeRuntimeTranscriptResponse;
if (parsed.providerId !== 'opencode' || !parsed.transcript) {
throw new Error('Invalid OpenCode transcript fixture');
}
return parsed.transcript;
}
async function buildFixtureStream(): Promise<BoardTaskLogStreamResponse> {
const transcript = await loadFixtureTranscript();
const source = new OpenCodeTaskLogStreamSource(
{
getOpenCodeTranscript: vi.fn(async () => transcript),
} as never,
{ resolve: async () => '/tmp/agent_teams_orchestrator' },
{
getTasks: vi.fn(async () => [RELAY_WORKS_10_TASK]),
getDeletedTasks: vi.fn(async () => []),
} as never,
new BoardTaskExactLogChunkBuilder(),
{ readTaskRecords: vi.fn(async () => []) }
);
const stream = await source.getTaskLogStream(TEAM_NAME, TASK_ID);
if (!stream) {
throw new Error('Expected OpenCode fixture stream');
}
return stream;
}
function flushMicrotasks(): Promise<void> {
return Promise.resolve();
}
describe('TaskLogStreamSection OpenCode real fixture e2e', () => {
afterEach(() => {
document.body.innerHTML = '';
apiState.getTaskLogStream.mockReset();
apiState.onTeamChange.mockReset();
vi.unstubAllGlobals();
});
it('renders real OpenCode task activity through the UI log stream', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
apiState.onTeamChange.mockImplementation(() => () => undefined);
apiState.getTaskLogStream.mockResolvedValueOnce(await buildFixtureStream());
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
await act(async () => {
root.render(
React.createElement(
TooltipProvider,
null,
React.createElement(TaskLogStreamSection, {
teamName: TEAM_NAME,
taskId: TASK_ID,
liveEnabled: false,
})
)
);
await flushMicrotasks();
await flushMicrotasks();
});
const text = host.textContent ?? '';
expect(text).toContain('Task Log Stream');
expect(text).toContain('matched task tool markers');
expect(text).toContain('Agent');
expect(text).toContain('Calculator behavior');
expect(text).toContain('Задача #0b3a0624 завершена');
expect(text).not.toContain('Keyboard handlers added');
expect(text).not.toContain('Logic smoke check');
expect(text).not.toContain('#00000000');
expect(text).not.toContain('SendMessage');
await act(async () => {
root.unmount();
await flushMicrotasks();
});
});
});