Enhance UX and Coordinator Role Constraints in Skills Documentation

- Added detailed constraints for the Coordinator role in the team UX improvement skill, emphasizing orchestration responsibilities and workflow management.
- Updated test cases in DashboardToolbar, useIssues, and useWebSocket to improve reliability and clarity.
- Introduced new tests for configStore and ignore patterns in Codex Lens to ensure proper functionality and configuration handling.
- Enhanced smart search functionality with improved embedding selection logic and added tests for various scenarios.
- Updated installation and usage documentation to reflect changes in directory structure and role specifications.
This commit is contained in:
catlog22
2026-03-08 23:43:44 +08:00
parent f3ae78f95e
commit 61ea9d47a6
110 changed files with 1516 additions and 218 deletions

View File

@@ -105,7 +105,7 @@ describe('DashboardToolbar', () => {
/>
);
fireEvent.click(screen.getByTitle('Click to configure and launch a CLI session'));
fireEvent.click(screen.getByRole('button', { name: 'New Session' }));
expect(screen.getByTestId('cli-config-modal')).toBeInTheDocument();
mockState.currentProjectPath = 'D:/workspace-b';

View File

@@ -4,7 +4,7 @@
// Tests for issue-related hooks with queue and discovery
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import { act, renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
useIssueQueue,
@@ -203,14 +203,18 @@ describe('useIssueDiscovery', () => {
});
// Select a session to load findings
result.current.selectSession('1');
act(() => {
result.current.selectSession('1');
});
await waitFor(() => {
expect(result.current.findings).toHaveLength(2);
});
// Apply severity filter
result.current.setFilters({ severity: 'critical' as const });
act(() => {
result.current.setFilters({ severity: 'critical' as const });
});
await waitFor(() => {
expect(result.current.filteredFindings).toHaveLength(1);
@@ -239,14 +243,18 @@ describe('useIssueDiscovery', () => {
});
// Select a session to load findings
result.current.selectSession('1');
act(() => {
result.current.selectSession('1');
});
await waitFor(() => {
expect(result.current.findings).toHaveLength(2);
});
// Apply type filter
result.current.setFilters({ type: 'bug' });
act(() => {
result.current.setFilters({ type: 'bug' });
});
await waitFor(() => {
expect(result.current.filteredFindings).toHaveLength(1);
@@ -275,14 +283,18 @@ describe('useIssueDiscovery', () => {
});
// Select a session to load findings
result.current.selectSession('1');
act(() => {
result.current.selectSession('1');
});
await waitFor(() => {
expect(result.current.findings).toHaveLength(2);
});
// Apply search filter
result.current.setFilters({ search: 'authentication' });
act(() => {
result.current.setFilters({ search: 'authentication' });
});
await waitFor(() => {
expect(result.current.filteredFindings).toHaveLength(1);
@@ -310,7 +322,9 @@ describe('useIssueDiscovery', () => {
});
// Select a session to load findings
result.current.selectSession('1');
act(() => {
result.current.selectSession('1');
});
await waitFor(() => {
expect(result.current.findings).toHaveLength(1);

View File

@@ -77,6 +77,7 @@ describe('useWebSocket workspace scoping', () => {
useSessionManagerStore.getState().resetState();
useWorkflowStore.setState({ projectPath: 'D:\\workspace-a' });
vi.spyOn(console, 'log').mockImplementation(() => {});
vi.stubGlobal('WebSocket', MockWebSocket as unknown as typeof WebSocket);
});
@@ -84,6 +85,7 @@ describe('useWebSocket workspace scoping', () => {
useCliSessionStore.getState().resetState();
useExecutionMonitorStore.getState().resetState();
useSessionManagerStore.getState().resetState();
vi.restoreAllMocks();
vi.unstubAllGlobals();
});

View File

@@ -137,6 +137,7 @@ describe('OrchestrationPlanBuilder', () => {
});
it('should detect cycles and throw an error', () => {
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
const flow: Flow = {
id: 'flow-cycle',
name: 'Cyclic Flow',
@@ -156,7 +157,11 @@ describe('OrchestrationPlanBuilder', () => {
metadata: {},
};
expect(() => OrchestrationPlanBuilder.fromFlow(flow)).toThrow('Cycle detected in flow graph. Cannot build orchestration plan from cyclic flow.');
try {
expect(() => OrchestrationPlanBuilder.fromFlow(flow)).toThrow('Cycle detected in flow graph. Cannot build orchestration plan from cyclic flow.');
} finally {
consoleErrorSpy.mockRestore();
}
});
it('should correctly map sessionStrategy and executionType from node data', () => {

View File

@@ -0,0 +1,67 @@
// ========================================
// Config Store Tests
// ========================================
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const CONFIG_STORE_MODULE_PATH = './configStore';
describe('configStore backend sync', () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
localStorage.clear();
window.history.replaceState({}, '', '/');
});
afterEach(() => {
vi.unstubAllGlobals();
});
it('does not fetch backend config during module import', async () => {
const fetchMock = vi.fn();
vi.stubGlobal('fetch', fetchMock);
await import(CONFIG_STORE_MODULE_PATH);
expect(fetchMock).not.toHaveBeenCalled();
});
it('syncs backend config explicitly with an absolute URL', async () => {
const fetchMock = vi.fn().mockResolvedValue({
json: vi.fn().mockResolvedValue({
config: {
tools: {
codex: {
enabled: true,
primaryModel: 'gpt-5',
secondaryModel: 'gpt-5-mini',
tags: ['analysis', 'debug'],
type: 'builtin',
envFile: '.env.codex',
settingsFile: 'codex.settings.json',
availableModels: ['gpt-5', 'gpt-5-mini'],
},
},
},
}),
});
vi.stubGlobal('fetch', fetchMock);
const { syncConfigStoreFromBackend, useConfigStore } = await import(CONFIG_STORE_MODULE_PATH);
await syncConfigStoreFromBackend(true);
expect(fetchMock).toHaveBeenCalledWith(`${window.location.origin}/api/cli/config`);
expect(useConfigStore.getState().cliTools.codex).toMatchObject({
enabled: true,
primaryModel: 'gpt-5',
secondaryModel: 'gpt-5-mini',
tags: ['analysis', 'debug'],
type: 'builtin',
envFile: '.env.codex',
settingsFile: 'codex.settings.json',
availableModels: ['gpt-5', 'gpt-5-mini'],
});
});
});

View File

@@ -10,6 +10,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { vi } from 'vitest';
import type { Locale } from '../types/store';
import enMessages from '../locales/en/index';
import zhMessages from '../locales/zh/index';
// Mock translation messages for testing
const mockMessages: Record<Locale, Record<string, string>> = {
@@ -677,13 +679,36 @@ interface I18nWrapperProps {
locale?: Locale;
}
const testMessages: Record<Locale, Record<string, string>> = {
en: { ...enMessages, ...mockMessages.en },
zh: { ...zhMessages, ...mockMessages.zh },
};
function handleIntlTestError(error: unknown): void {
const intlError = error as { code?: string } | undefined;
if (intlError?.code === 'MISSING_TRANSLATION') {
return;
}
console.error(error);
}
const testRouterFutureConfig = {
v7_startTransition: true,
v7_relativeSplatPath: true,
} as const;
function I18nWrapper({ children, locale = 'en' }: I18nWrapperProps) {
const queryClient = createTestQueryClient();
return (
<MemoryRouter>
<MemoryRouter future={testRouterFutureConfig}>
<QueryClientProvider client={queryClient}>
<IntlProvider locale={locale} messages={mockMessages[locale]}>
<IntlProvider
locale={locale}
messages={testMessages[locale]}
onError={handleIntlTestError}
>
{children}
</IntlProvider>
</QueryClientProvider>
@@ -726,9 +751,9 @@ export const mockLocaleUtils = {
export function mockI18nContext(locale: Locale = 'en') {
return {
locale,
messages: mockMessages[locale],
messages: testMessages[locale],
formatMessage: (id: string, values?: Record<string, unknown>) => {
const message = mockMessages[locale][id];
const message = testMessages[locale][id];
if (!message) return id;
if (!values) return message;