agent-ecosystem/test/renderer/features/agent-graph/GraphMemberLogPreviewHud.test.tsx
2026-05-07 15:18:21 +03:00

221 lines
6.1 KiB
TypeScript

import React, { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { GraphMemberLogPreviewHud } from '@features/agent-graph/renderer/ui/GraphMemberLogPreviewHud';
import type { GraphNode } from '@claude-teams/agent-graph';
const previewsByMember = new Map([
[
'team-lead',
{
memberName: 'team-lead',
items: [
{
id: 'lead-preview-1',
kind: 'text' as const,
provider: 'claude_transcript' as const,
timestamp: '2026-04-03T00:00:00.000Z',
title: 'Assistant',
preview: 'lead log preview',
tone: 'neutral' as const,
},
],
coverage: [{ provider: 'claude_transcript' as const, status: 'included' as const }],
warnings: [],
truncated: false,
overflowCount: 0,
generatedAt: '2026-04-03T00:00:00.000Z',
},
],
[
'alice',
{
memberName: 'alice',
items: [
{
id: 'preview-1',
kind: 'tool_use' as const,
provider: 'claude_transcript' as const,
timestamp: '2026-04-03T00:00:00.000Z',
title: 'Bash',
preview: 'pnpm test',
tone: 'warning' as const,
},
],
coverage: [{ provider: 'claude_transcript' as const, status: 'included' as const }],
warnings: [],
truncated: true,
overflowCount: 2,
generatedAt: '2026-04-03T00:00:00.000Z',
},
],
]);
vi.mock('@features/agent-graph/renderer/hooks/useGraphMemberLogPreviews', () => ({
buildGraphLogPreviewLaneIdsByMember: () => ({ alice: 'secondary:opencode:alice' }),
useGraphMemberLogPreviews: () => ({
previewsByMember,
loading: false,
error: null,
reload: vi.fn(),
}),
}));
vi.mock('@features/agent-graph/renderer/hooks/useGraphActivityContext', () => ({
useGraphActivityContext: () => ({
teamData: {
members: [
{
name: 'alice',
status: 'active',
currentTaskId: null,
taskCount: 0,
lastActiveAt: null,
messageCount: 0,
providerId: 'opencode',
laneOwnerProviderId: 'opencode',
laneId: 'secondary:opencode:alice',
},
],
},
}),
}));
describe('GraphMemberLogPreviewHud', () => {
beforeEach(() => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
vi.stubGlobal(
'requestAnimationFrame',
vi.fn(() => 1)
);
vi.stubGlobal('cancelAnimationFrame', vi.fn());
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-04-03T00:01:00.000Z'));
});
afterEach(() => {
document.body.innerHTML = '';
vi.useRealTimers();
vi.unstubAllGlobals();
});
it('opens the member profile on the logs tab when a preview row or overflow is clicked', async () => {
const node: GraphNode = {
id: 'member:alpha-team:alice',
kind: 'member',
label: 'alice',
state: 'active',
domainRef: { kind: 'member', teamName: 'alpha-team', memberName: 'alice' },
};
const onOpenMemberProfile = vi.fn();
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
await act(async () => {
root.render(
<GraphMemberLogPreviewHud
teamName="alpha-team"
nodes={[node]}
getLogWorldRect={() => ({
left: 40,
top: 80,
right: 300,
bottom: 372,
width: 260,
height: 292,
})}
getCameraZoom={() => 1}
worldToScreen={(x, y) => ({ x, y })}
getViewportSize={() => ({ width: 1200, height: 800 })}
focusNodeIds={null}
onOpenMemberProfile={onOpenMemberProfile}
/>
);
await Promise.resolve();
});
const row = Array.from(host.querySelectorAll('button')).find((button) =>
button.textContent?.includes('pnpm test')
);
expect(row).not.toBeUndefined();
await act(async () => {
row?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onOpenMemberProfile).toHaveBeenCalledWith('alice', { initialTab: 'logs' });
const moreButton = Array.from(host.querySelectorAll('button')).find((button) =>
button.textContent?.includes('+2 more')
);
expect(moreButton).not.toBeUndefined();
await act(async () => {
moreButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onOpenMemberProfile).toHaveBeenCalledTimes(2);
act(() => {
root.unmount();
});
});
it('renders lead log previews and opens the lead profile logs tab', async () => {
const leadNode: GraphNode = {
id: 'lead:alpha-team',
kind: 'lead',
label: 'alpha-team',
state: 'active',
domainRef: { kind: 'lead', teamName: 'alpha-team', memberName: 'team-lead' },
};
const onOpenMemberProfile = vi.fn();
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
await act(async () => {
root.render(
<GraphMemberLogPreviewHud
teamName="alpha-team"
nodes={[leadNode]}
getLogWorldRect={() => ({
left: 40,
top: 80,
right: 300,
bottom: 372,
width: 260,
height: 292,
})}
getCameraZoom={() => 1}
worldToScreen={(x, y) => ({ x, y })}
getViewportSize={() => ({ width: 1200, height: 800 })}
focusNodeIds={null}
onOpenMemberProfile={onOpenMemberProfile}
/>
);
await Promise.resolve();
});
const row = Array.from(host.querySelectorAll('button')).find((button) =>
button.textContent?.includes('lead log preview')
);
expect(row).not.toBeUndefined();
await act(async () => {
row?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onOpenMemberProfile).toHaveBeenCalledWith('team-lead', { initialTab: 'logs' });
act(() => {
root.unmount();
});
});
});