mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-13 02:41:50 +08:00
feat: add tests and implementation for issue discovery and queue pages
- Implemented `DiscoveryPage` with session management and findings display. - Added tests for `DiscoveryPage` to ensure proper rendering and functionality. - Created `QueuePage` for managing issue execution queues with stats and actions. - Added tests for `QueuePage` to verify UI elements and translations. - Introduced `useIssues` hooks for fetching and managing issue data. - Added loading skeletons and error handling for better user experience. - Created `vite-env.d.ts` for TypeScript support in Vite environment.
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
// ========================================
|
||||
// DiscoveryCard Component Tests
|
||||
// ========================================
|
||||
// Tests for the discovery card component with i18n
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { render, screen } from '@/test/i18n';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { DiscoveryCard } from './DiscoveryCard';
|
||||
import type { DiscoverySession } from '@/lib/api';
|
||||
|
||||
describe('DiscoveryCard', () => {
|
||||
const mockSession: DiscoverySession = {
|
||||
id: '1',
|
||||
name: 'Test Session',
|
||||
status: 'running',
|
||||
progress: 50,
|
||||
findings_count: 5,
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
session: mockSession,
|
||||
isActive: false,
|
||||
onClick: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('with en locale', () => {
|
||||
it('should render session name', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
expect(screen.getByText('Test Session')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show running status badge', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
expect(screen.getByText(/Running/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show completed status badge', () => {
|
||||
const completedSession: DiscoverySession = {
|
||||
...mockSession,
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={completedSession} />, { locale: 'en' });
|
||||
expect(screen.getByText(/Completed/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show failed status badge', () => {
|
||||
const failedSession: DiscoverySession = {
|
||||
...mockSession,
|
||||
status: 'failed',
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={failedSession} />, { locale: 'en' });
|
||||
expect(screen.getByText(/Failed/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show progress bar for running sessions', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
expect(screen.getByText(/Progress/i)).toBeInTheDocument();
|
||||
expect(screen.getByText('50%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show progress bar for completed sessions', () => {
|
||||
const completedSession: DiscoverySession = {
|
||||
...mockSession,
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={completedSession} />, { locale: 'en' });
|
||||
expect(screen.queryByText(/Progress/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show findings count', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
expect(screen.getByText(/Findings/i)).toBeInTheDocument();
|
||||
expect(screen.getByText('5')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show formatted date', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
const dateText = new Date(mockSession.created_at).toLocaleString();
|
||||
expect(screen.getByText(new RegExp(dateText.replace(/[\/:]/g, '[/:]'), 'i'))).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with zh locale', () => {
|
||||
it('should render session name', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'zh' });
|
||||
expect(screen.getByText('Test Session')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show translated running status badge', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'zh' });
|
||||
expect(screen.getByText(/运行中/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show translated completed status badge', () => {
|
||||
const completedSession: DiscoverySession = {
|
||||
...mockSession,
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={completedSession} />, { locale: 'zh' });
|
||||
expect(screen.getByText(/已完成/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show translated failed status badge', () => {
|
||||
const failedSession: DiscoverySession = {
|
||||
...mockSession,
|
||||
status: 'failed',
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={failedSession} />, { locale: 'zh' });
|
||||
expect(screen.getByText(/失败/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show translated progress text', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'zh' });
|
||||
expect(screen.getByText(/进度/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show translated findings count', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'zh' });
|
||||
expect(screen.getByText(/发现/i)).toBeInTheDocument();
|
||||
expect(screen.getByText('5')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('interaction', () => {
|
||||
it('should call onClick when clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onClick = vi.fn();
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} onClick={onClick} />, { locale: 'en' });
|
||||
|
||||
const card = screen.getByText('Test Session').closest('.cursor-pointer');
|
||||
if (card) {
|
||||
await user.click(card);
|
||||
}
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('visual states', () => {
|
||||
it('should apply active styles when isActive', () => {
|
||||
const { container } = render(
|
||||
<DiscoveryCard {...defaultProps} isActive={true} />,
|
||||
{ locale: 'en' }
|
||||
);
|
||||
|
||||
const card = container.firstChild as HTMLElement;
|
||||
expect(card.className).toContain('ring-2');
|
||||
expect(card.className).toContain('ring-primary');
|
||||
});
|
||||
|
||||
it('should not apply active styles when not active', () => {
|
||||
const { container } = render(
|
||||
<DiscoveryCard {...defaultProps} isActive={false} />,
|
||||
{ locale: 'en' }
|
||||
);
|
||||
|
||||
const card = container.firstChild as HTMLElement;
|
||||
expect(card.className).not.toContain('ring-2');
|
||||
});
|
||||
|
||||
it('should have hover effect', () => {
|
||||
const { container } = render(
|
||||
<DiscoveryCard {...defaultProps} />,
|
||||
{ locale: 'en' }
|
||||
);
|
||||
|
||||
const card = container.firstChild as HTMLElement;
|
||||
expect(card.className).toContain('hover:shadow-md');
|
||||
});
|
||||
});
|
||||
|
||||
describe('progress bar', () => {
|
||||
it('should render progress element for running sessions', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
|
||||
const progressBar = document.querySelector('[role="progressbar"]');
|
||||
expect(progressBar).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render progress element for completed sessions', () => {
|
||||
const completedSession: DiscoverySession = {
|
||||
...mockSession,
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={completedSession} />, { locale: 'en' });
|
||||
|
||||
const progressBar = document.querySelector('[role="progressbar"]');
|
||||
expect(progressBar).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display correct progress percentage', () => {
|
||||
const sessionWithDifferentProgress: DiscoverySession = {
|
||||
...mockSession,
|
||||
progress: 75,
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={sessionWithDifferentProgress} />, { locale: 'en' });
|
||||
expect(screen.getByText('75%')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('accessibility', () => {
|
||||
it('should have clickable card with proper cursor', () => {
|
||||
const { container } = render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
const card = container.firstChild as HTMLElement;
|
||||
expect(card.className).toContain('cursor-pointer');
|
||||
});
|
||||
|
||||
it('should have proper heading structure', () => {
|
||||
render(<DiscoveryCard {...defaultProps} />, { locale: 'en' });
|
||||
const heading = screen.getByRole('heading', { level: 3, name: 'Test Session' });
|
||||
expect(heading).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle zero findings', () => {
|
||||
const sessionWithNoFindings: DiscoverySession = {
|
||||
...mockSession,
|
||||
findings_count: 0,
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={sessionWithNoFindings} />, { locale: 'en' });
|
||||
expect(screen.getByText('0')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle zero progress', () => {
|
||||
const sessionWithNoProgress: DiscoverySession = {
|
||||
...mockSession,
|
||||
progress: 0,
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={sessionWithNoProgress} />, { locale: 'en' });
|
||||
expect(screen.getByText('0%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle 100% progress', () => {
|
||||
const sessionWithFullProgress: DiscoverySession = {
|
||||
...mockSession,
|
||||
progress: 100,
|
||||
};
|
||||
|
||||
render(<DiscoveryCard {...defaultProps} session={sessionWithFullProgress} />, { locale: 'en' });
|
||||
expect(screen.getByText('100%')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user