205 lines
7.0 KiB
JavaScript
205 lines
7.0 KiB
JavaScript
import React from 'react';
|
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
import {
|
|
DEFAULT_SKILLS,
|
|
DEFAULT_TOOLS,
|
|
AGENTS,
|
|
loadStoredSkills,
|
|
saveStoredSkills,
|
|
loadStoredTools,
|
|
loadActiveAgentId,
|
|
saveActiveAgentId,
|
|
} from '@/pages/doormile/settings/agentStudio/agentRegistryData';
|
|
// Stub lucide-react ESM exports for Jest
|
|
jest.mock('lucide-react', () => {
|
|
return new Proxy(
|
|
{},
|
|
{
|
|
get: (target, prop) => {
|
|
if (prop === '__esModule') return true;
|
|
return (props) => <span data-testid={`icon-${String(prop)}`} {...props} />;
|
|
},
|
|
}
|
|
);
|
|
});
|
|
|
|
// Stub framer-motion layout animations for jsdom
|
|
jest.mock('framer-motion', () => ({
|
|
motion: {
|
|
div: ({ children, ...rest }) => <div {...rest}>{children}</div>,
|
|
span: ({ children, ...rest }) => <span {...rest}>{children}</span>,
|
|
},
|
|
AnimatePresence: ({ children }) => <>{children}</>,
|
|
}));
|
|
|
|
import SkillCard from '@/pages/doormile/settings/agentStudio/SkillCard';
|
|
import SkillCardsGrid from '@/pages/doormile/settings/agentStudio/SkillCardsGrid';
|
|
import ToolRegistryView from '@/pages/doormile/settings/agentStudio/ToolRegistryView';
|
|
|
|
describe('Agent Studio — Data Layer & Persistence', () => {
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
});
|
|
|
|
it('pre-seeds default agents and surfaces correctly', () => {
|
|
expect(AGENTS.length).toBeGreaterThanOrEqual(2);
|
|
expect(AGENTS.some((a) => a.name === 'MileTruth AI Agent')).toBe(true);
|
|
expect(AGENTS.some((a) => a.name === 'MileTruth AI Dispatcher')).toBe(true);
|
|
});
|
|
|
|
it('pre-seeds default skills matching reference image', () => {
|
|
expect(DEFAULT_SKILLS.some((s) => s.title === 'MileTruth Skill Management')).toBe(true);
|
|
expect(DEFAULT_SKILLS.some((s) => s.title === 'Learning Analysis')).toBe(true);
|
|
const learningSkill = DEFAULT_SKILLS.find((s) => s.title === 'Learning Analysis');
|
|
expect(learningSkill.prompt).toBe('How is training progressing?');
|
|
expect(learningSkill.category).toBe('WORKFORCE');
|
|
});
|
|
|
|
it('persists modified skills to localStorage and restores them', () => {
|
|
const customSkill = {
|
|
id: 'custom-skill-1',
|
|
title: 'Custom Automation Skill',
|
|
category: 'LOGISTICS',
|
|
scope: 'agent',
|
|
enabled: true,
|
|
description: 'Test custom capability',
|
|
tools: ['create_single_order'],
|
|
};
|
|
|
|
saveStoredSkills([customSkill]);
|
|
const restored = loadStoredSkills();
|
|
expect(restored.length).toBe(1);
|
|
expect(restored[0].title).toBe('Custom Automation Skill');
|
|
});
|
|
|
|
it('saves and loads active agent id', () => {
|
|
saveActiveAgentId('miletruth-agent');
|
|
expect(loadActiveAgentId()).toBe('miletruth-agent');
|
|
});
|
|
});
|
|
|
|
describe('Agent Studio — SkillCard Component', () => {
|
|
const sampleSkill = {
|
|
id: 'test-skill-1',
|
|
title: 'MileTruth Skill Management',
|
|
category: 'AUTHORING',
|
|
scope: 'agent',
|
|
integration: 'MileTruth AI',
|
|
enabled: true,
|
|
description: 'Create workforce skills, training and verification in MileTruth AI.',
|
|
prompt: 'How is training progressing?',
|
|
tools: ['open_add_training'],
|
|
};
|
|
|
|
it('renders title, description, category, and prompt trigger bubble', () => {
|
|
render(<SkillCard skill={sampleSkill} onTest={() => {}} onDetails={() => {}} />);
|
|
|
|
expect(screen.getByText('MileTruth Skill Management')).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText('Create workforce skills, training and verification in MileTruth AI.')
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText('AUTHORING')).toBeInTheDocument();
|
|
expect(screen.getByText('"How is training progressing?"')).toBeInTheDocument();
|
|
expect(screen.getByText('MileTruth AI')).toBeInTheDocument();
|
|
});
|
|
|
|
it('fires onTest when Test in Playground is clicked', () => {
|
|
const handleTest = jest.fn();
|
|
render(<SkillCard skill={sampleSkill} onTest={handleTest} onDetails={() => {}} />);
|
|
|
|
const testBtn = screen.getByText('Test in Playground');
|
|
fireEvent.click(testBtn);
|
|
expect(handleTest).toHaveBeenCalledWith(sampleSkill);
|
|
});
|
|
|
|
it('fires onDetails when Details button is clicked', () => {
|
|
const handleDetails = jest.fn();
|
|
render(<SkillCard skill={sampleSkill} onTest={() => {}} onDetails={handleDetails} />);
|
|
|
|
const detailsBtn = screen.getByText('Details');
|
|
fireEvent.click(detailsBtn);
|
|
expect(handleDetails).toHaveBeenCalledWith(sampleSkill);
|
|
});
|
|
});
|
|
|
|
describe('Agent Studio — SkillCardsGrid Component', () => {
|
|
it('renders skill cards and filters by search query', () => {
|
|
render(
|
|
<SkillCardsGrid
|
|
skills={DEFAULT_SKILLS}
|
|
activeAgent={AGENTS[0]}
|
|
onTestSkill={() => {}}
|
|
onOpenDetails={() => {}}
|
|
onNewSkill={() => {}}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('MileTruth Skill Management')).toBeInTheDocument();
|
|
expect(screen.getByText('Learning Analysis')).toBeInTheDocument();
|
|
|
|
const searchInput = screen.getByPlaceholderText('Search skills, capabilities or pages...');
|
|
fireEvent.change(searchInput, { target: { value: 'Learning' } });
|
|
|
|
expect(screen.queryByText('MileTruth Skill Management')).not.toBeInTheDocument();
|
|
expect(screen.getByText('Learning Analysis')).toBeInTheDocument();
|
|
});
|
|
|
|
it('filters by category pill selection', () => {
|
|
render(
|
|
<SkillCardsGrid
|
|
skills={DEFAULT_SKILLS}
|
|
activeAgent={AGENTS[0]}
|
|
onTestSkill={() => {}}
|
|
onOpenDetails={() => {}}
|
|
onNewSkill={() => {}}
|
|
/>
|
|
);
|
|
|
|
// Click 'Authoring' category pill
|
|
const authoringPill = screen.getByText('Authoring');
|
|
fireEvent.click(authoringPill);
|
|
|
|
expect(screen.getByText('MileTruth Skill Management')).toBeInTheDocument();
|
|
expect(screen.queryByText('Learning Analysis')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('triggers onNewSkill when clicking New skill button', () => {
|
|
const handleNewSkill = jest.fn();
|
|
render(
|
|
<SkillCardsGrid
|
|
skills={DEFAULT_SKILLS}
|
|
activeAgent={AGENTS[0]}
|
|
onTestSkill={() => {}}
|
|
onOpenDetails={() => {}}
|
|
onNewSkill={handleNewSkill}
|
|
/>
|
|
);
|
|
|
|
const newSkillBtn = screen.getByText('New skill');
|
|
fireEvent.click(newSkillBtn);
|
|
expect(handleNewSkill).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('Agent Studio — ToolRegistryView Component', () => {
|
|
it('renders tool registry table and displays argument schemas', () => {
|
|
render(<ToolRegistryView tools={DEFAULT_TOOLS} />);
|
|
|
|
expect(screen.getByText('open_add_skill_training')).toBeInTheDocument();
|
|
expect(screen.getByText('create_single_order')).toBeInTheDocument();
|
|
expect(screen.getByText('rebalance_riders')).toBeInTheDocument();
|
|
});
|
|
|
|
it('filters tool list by search input', () => {
|
|
render(<ToolRegistryView tools={DEFAULT_TOOLS} />);
|
|
|
|
const searchInput = screen.getByPlaceholderText(
|
|
'Filter tools by function name, category or description...'
|
|
);
|
|
fireEvent.change(searchInput, { target: { value: 'rebalance' } });
|
|
|
|
expect(screen.getByText('rebalance_riders')).toBeInTheDocument();
|
|
expect(screen.queryByText('open_add_skill_training')).not.toBeInTheDocument();
|
|
});
|
|
});
|