diff --git a/admin-v2/tests/pages/Accounts.test.tsx b/admin-v2/tests/pages/Accounts.test.tsx new file mode 100644 index 0000000..8575efc --- /dev/null +++ b/admin-v2/tests/pages/Accounts.test.tsx @@ -0,0 +1,114 @@ +// ============================================================ +// Accounts 页面冒烟测试 +// ============================================================ + +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { render, screen, waitFor } from '@testing-library/react' +import { http, HttpResponse } from 'msw' +import { setupServer } from 'msw/node' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +import Accounts from '@/pages/Accounts' + +// ── Mock data ──────────────────────────────────────────────── + +const mockAccounts = { + items: [ + { + id: 'acc-001', + username: 'zclaw_admin', + display_name: 'Admin', + email: 'admin@zclaw.ai', + role: 'super_admin' as const, + status: 'active' as const, + totp_enabled: true, + last_login_at: '2026-03-30T10:00:00Z', + created_at: '2026-01-01T00:00:00Z', + llm_routing: 'relay' as const, + }, + { + id: 'acc-002', + username: 'test_user', + display_name: 'Test', + email: 'test@zclaw.ai', + role: 'user' as const, + status: 'active' as const, + totp_enabled: false, + last_login_at: null, + created_at: '2026-02-15T00:00:00Z', + llm_routing: 'local' as const, + }, + ], + total: 2, + page: 1, + page_size: 20, +} + +// ── MSW server ─────────────────────────────────────────────── + +const server = setupServer() + +beforeEach(() => { + server.listen({ onUnhandledRequest: 'bypass' }) +}) + +afterEach(() => { + server.close() +}) + +// ── Helper: render with QueryClient ────────────────────────── + +function renderWithProviders(ui: React.ReactElement) { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + }, + }) + return render( + + {ui} + , + ) +} + +// ── Tests ──────────────────────────────────────────────────── + +describe('Accounts page', () => { + it('renders account usernames in the table', async () => { + server.use( + http.get('*/api/v1/accounts', () => { + return HttpResponse.json(mockAccounts) + }), + ) + + renderWithProviders() + + // Wait for data to load and usernames to appear + await waitFor(() => { + expect(screen.getByText('zclaw_admin')).toBeInTheDocument() + }) + expect(screen.getByText('test_user')).toBeInTheDocument() + }) + + it('shows loading state before data arrives', async () => { + // Use a delayed response to observe loading state + server.use( + http.get('*/api/v1/accounts', async () => { + await new Promise((resolve) => setTimeout(resolve, 500)) + return HttpResponse.json(mockAccounts) + }), + ) + + renderWithProviders() + + // Ant Design ProTable renders a spinner while loading + // Check that a .ant-spin element exists + const spinner = document.querySelector('.ant-spin') + expect(spinner).toBeTruthy() + + // Wait for loading to complete so afterEach cleanup is clean + await waitFor(() => { + expect(screen.getByText('zclaw_admin')).toBeInTheDocument() + }) + }) +}) diff --git a/admin-v2/tests/pages/AgentTemplates.test.tsx b/admin-v2/tests/pages/AgentTemplates.test.tsx new file mode 100644 index 0000000..1480525 --- /dev/null +++ b/admin-v2/tests/pages/AgentTemplates.test.tsx @@ -0,0 +1,137 @@ +// ============================================================ +// AgentTemplates 页面冒烟测试 +// ============================================================ + +import { describe, it, expect, beforeEach, afterEach } from 'vitest' +import { render, screen, waitFor } from '@testing-library/react' +import { http, HttpResponse } from 'msw' +import { setupServer } from 'msw/node' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +import AgentTemplates from '@/pages/AgentTemplates' + +// ── Mock data ──────────────────────────────────────────────── + +const mockTemplates = { + items: [ + { + id: 'tmpl-001', + name: 'Medical Assistant', + description: 'AI health assistant', + category: 'assistant', + source: 'builtin' as const, + model: 'gpt-4o', + system_prompt: 'You are a medical assistant.', + tools: ['web_search'], + capabilities: ['conversation'], + temperature: 0.7, + max_tokens: 4096, + visibility: 'public' as const, + status: 'active' as const, + current_version: 2, + created_at: '2026-01-10T00:00:00Z', + updated_at: '2026-03-20T00:00:00Z', + soul_content: null, + scenarios: ['healthcare'], + welcome_message: 'Hello!', + quick_commands: [], + personality: 'professional', + communication_style: null, + emoji: 'hospital', + version: 2, + source_id: 'medical-v1', + }, + { + id: 'tmpl-002', + name: 'Code Helper', + description: 'Programming assistant', + category: 'tool', + source: 'custom' as const, + model: null, + system_prompt: null, + tools: [], + capabilities: [], + temperature: null, + max_tokens: null, + visibility: 'team' as const, + status: 'active' as const, + current_version: 1, + created_at: '2026-02-01T00:00:00Z', + updated_at: '2026-02-01T00:00:00Z', + soul_content: null, + scenarios: [], + welcome_message: null, + quick_commands: [], + personality: null, + communication_style: null, + emoji: null, + version: 1, + source_id: null, + }, + ], + total: 2, + page: 1, + page_size: 20, +} + +// ── MSW server ─────────────────────────────────────────────── + +const server = setupServer() + +beforeEach(() => { + server.listen({ onUnhandledRequest: 'bypass' }) +}) + +afterEach(() => { + server.close() +}) + +// ── Helper: render with QueryClient ────────────────────────── + +function renderWithProviders(ui: React.ReactElement) { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + }, + }) + return render( + + {ui} + , + ) +} + +// ── Tests ──────────────────────────────────────────────────── + +describe('AgentTemplates page', () => { + it('renders template names in the table', async () => { + server.use( + http.get('*/api/v1/agent-templates', () => { + return HttpResponse.json(mockTemplates) + }), + ) + + renderWithProviders() + + // Wait for data to load and template names to appear + await waitFor(() => { + expect(screen.getByText('Medical Assistant')).toBeInTheDocument() + }) + expect(screen.getByText('Code Helper')).toBeInTheDocument() + }) + + it('renders template categories', async () => { + server.use( + http.get('*/api/v1/agent-templates', () => { + return HttpResponse.json(mockTemplates) + }), + ) + + renderWithProviders() + + await waitFor(() => { + expect(screen.getByText('assistant')).toBeInTheDocument() + }) + expect(screen.getByText('tool')).toBeInTheDocument() + }) +})