284 lines
8.1 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|