agent-ecosystem/test/renderer/features/agent-graph/GraphControls.test.ts
2026-05-07 18:07:00 +03:00

284 lines
8.1 KiB
TypeScript

import React, { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('@radix-ui/react-tooltip', () => ({
Root: ({ children }: { children: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
Trigger: ({ children }: { children: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
Portal: ({ children }: { children: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
Content: ({ children }: { children: React.ReactNode }) =>
React.createElement('div', { 'data-testid': 'tooltip-content' }, children),
Arrow: () => null,
}));
import { GraphControls } from '../../../../packages/agent-graph/src/ui/GraphControls';
describe('GraphControls', () => {
beforeEach(() => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
});
afterEach(() => {
document.body.innerHTML = '';
vi.unstubAllGlobals();
});
it('renders the sidebar toggle before the team and task buttons and triggers the callback', async () => {
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
const onToggleSidebar = vi.fn();
await act(async () => {
root.render(
React.createElement(GraphControls, {
filters: {
showActivity: true,
showLogs: true,
showTasks: true,
showProcesses: true,
showEdges: true,
paused: false,
},
onFiltersChange: vi.fn(),
onZoomIn: vi.fn(),
onZoomOut: vi.fn(),
onZoomToFit: vi.fn(),
onToggleSidebar,
isSidebarVisible: true,
onOpenTeamPage: vi.fn(),
onCreateTask: vi.fn(),
teamName: 'demo-team',
})
);
await Promise.resolve();
});
const labels = Array.from(host.querySelectorAll('button[aria-label]')).map((button) =>
button.getAttribute('aria-label')
);
expect(labels.indexOf('Hide sidebar')).toBeGreaterThanOrEqual(0);
expect(labels.indexOf('Open team page')).toBeGreaterThan(labels.indexOf('Hide sidebar'));
expect(labels.indexOf('Create task')).toBeGreaterThan(labels.indexOf('Open team page'));
const toggleButton = host.querySelector('button[aria-label="Hide sidebar"]');
expect(toggleButton).not.toBeNull();
await act(async () => {
toggleButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onToggleSidebar).toHaveBeenCalledTimes(1);
await act(async () => {
root.unmount();
await Promise.resolve();
});
});
it('shows the open-sidebar label when the sidebar is hidden', async () => {
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
await act(async () => {
root.render(
React.createElement(GraphControls, {
filters: {
showActivity: true,
showLogs: true,
showTasks: true,
showProcesses: true,
showEdges: true,
paused: false,
},
onFiltersChange: vi.fn(),
onZoomIn: vi.fn(),
onZoomOut: vi.fn(),
onZoomToFit: vi.fn(),
onToggleSidebar: vi.fn(),
isSidebarVisible: false,
teamName: 'demo-team',
})
);
await Promise.resolve();
});
expect(host.querySelector('button[aria-label="Show sidebar"]')).not.toBeNull();
await act(async () => {
root.unmount();
await Promise.resolve();
});
});
it('toggles activity visibility from graph settings', async () => {
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
const onFiltersChange = vi.fn();
await act(async () => {
root.render(
React.createElement(GraphControls, {
filters: {
showActivity: true,
showLogs: true,
showTasks: true,
showProcesses: true,
showEdges: true,
paused: false,
},
onFiltersChange,
onZoomIn: vi.fn(),
onZoomOut: vi.fn(),
onZoomToFit: vi.fn(),
teamName: 'demo-team',
})
);
await Promise.resolve();
});
const settingsButton = host.querySelector('button[aria-label="Graph settings"]');
expect(settingsButton).not.toBeNull();
await act(async () => {
settingsButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
const activityButton = Array.from(host.querySelectorAll('button')).find((button) =>
button.textContent?.includes('Activity')
);
expect(activityButton).not.toBeUndefined();
await act(async () => {
activityButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onFiltersChange).toHaveBeenCalledWith({
showActivity: false,
showLogs: true,
showTasks: true,
showProcesses: true,
showEdges: true,
paused: false,
});
await act(async () => {
root.unmount();
await Promise.resolve();
});
});
it('toggles log preview visibility from graph settings independently of activity', async () => {
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
const onFiltersChange = vi.fn();
await act(async () => {
root.render(
React.createElement(GraphControls, {
filters: {
showActivity: true,
showLogs: true,
showTasks: true,
showProcesses: true,
showEdges: true,
paused: false,
},
onFiltersChange,
onZoomIn: vi.fn(),
onZoomOut: vi.fn(),
onZoomToFit: vi.fn(),
teamName: 'demo-team',
})
);
await Promise.resolve();
});
const settingsButton = host.querySelector('button[aria-label="Graph settings"]');
expect(settingsButton).not.toBeNull();
await act(async () => {
settingsButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
const logsButton = Array.from(host.querySelectorAll('button')).find((button) =>
button.textContent?.includes('Logs')
);
expect(logsButton).not.toBeUndefined();
await act(async () => {
logsButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onFiltersChange).toHaveBeenCalledWith({
showActivity: true,
showLogs: false,
showTasks: true,
showProcesses: true,
showEdges: true,
paused: false,
});
await act(async () => {
root.unmount();
await Promise.resolve();
});
});
it('switches layout mode from the top toolbar', async () => {
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
const onLayoutModeChange = vi.fn();
await act(async () => {
root.render(
React.createElement(GraphControls, {
filters: {
showActivity: true,
showLogs: true,
showTasks: true,
showProcesses: true,
showEdges: true,
paused: false,
},
onFiltersChange: vi.fn(),
onZoomIn: vi.fn(),
onZoomOut: vi.fn(),
onZoomToFit: vi.fn(),
layoutMode: 'radial',
onLayoutModeChange,
teamName: 'demo-team',
})
);
await Promise.resolve();
});
const rowsButton = host.querySelector('button[aria-label="Switch to rows layout"]');
expect(rowsButton).not.toBeNull();
await act(async () => {
rowsButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onLayoutModeChange).toHaveBeenCalledWith('grid-under-lead');
await act(async () => {
root.unmount();
await Promise.resolve();
});
});
});