mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* feat(agents): surface actionable CLI diagnostics Detect unavailable CLI agents with structured diagnostics, stream agent discovery results to the settings UI, and render actionable fix controls for common setup failures. Also harden the streamed agents client so server error events and dropped streams surface as failures instead of replacing state with a truncated agent list. * fix(agents): gate streamed rescan loading Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(agents): distinguish non-executable CLI diagnostics Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(agents): defer OAuth diagnostics without producer Generated-By: looper 0.9.2 (runner=fixer, agent=codex) --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> Co-authored-by: Looper <looper@noreply.github.com> Co-authored-by: Looper Fixer <looper-fixer@noreply.github.com> Co-authored-by: looper-fixer <looper-fixer@users.noreply.github.com>
53 lines
1.9 KiB
TypeScript
53 lines
1.9 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { AgentDiagnosticRow } from '../../src/components/AgentDiagnosticRow';
|
|
import type { AgentDiagnostic } from '../../src/types';
|
|
import { en } from '../../src/i18n/locales/en';
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
const notOnPath: AgentDiagnostic = {
|
|
reason: 'not-on-path',
|
|
severity: 'error',
|
|
message: 'Gemini (`gemini`) was not found on your PATH.',
|
|
searchedDirs: ['/usr/bin', '/opt/homebrew/bin'],
|
|
fixActions: [{ kind: 'openInstall' }, { kind: 'rescan' }],
|
|
};
|
|
|
|
describe('AgentDiagnosticRow', () => {
|
|
it('renders the daemon message and tags the reason', () => {
|
|
render(<AgentDiagnosticRow diagnostic={notOnPath} />);
|
|
const group = screen.getByRole('group');
|
|
expect(group.getAttribute('data-reason')).toBe('not-on-path');
|
|
expect(screen.getByText(notOnPath.message)).toBeTruthy();
|
|
});
|
|
|
|
it('exposes searched dirs via the message tooltip', () => {
|
|
render(<AgentDiagnosticRow diagnostic={notOnPath} />);
|
|
const title = screen.getByText(notOnPath.message).getAttribute('title') ?? '';
|
|
expect(title).toContain('/usr/bin');
|
|
expect(title).toContain('/opt/homebrew/bin');
|
|
});
|
|
|
|
it('only renders buttons for intents that have a wired handler', () => {
|
|
const onRescan = vi.fn();
|
|
// openInstall is in the diagnostic but no onOpenInstall handler is given,
|
|
// so only Rescan should render. Actions are icon-only buttons whose
|
|
// accessible name comes from the (tooltip) aria-label.
|
|
render(<AgentDiagnosticRow diagnostic={notOnPath} handlers={{ onRescan }} />);
|
|
expect(
|
|
screen.queryByRole('button', { name: en['settings.agentInstall.install'] }),
|
|
).toBeNull();
|
|
const rescan = screen.getByRole('button', { name: en['settings.rescan'] });
|
|
fireEvent.click(rescan);
|
|
expect(onRescan).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
});
|