mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-28 20:01:17 +08:00
Add end-to-end tests for Graph Explorer, History, Orchestrator, and Project features
- Implemented E2E tests for code relationship visualization in Graph Explorer. - Added tests for archived session management in History, including search, filter, restore, and delete functionalities. - Created tests for workflow orchestration in Orchestrator, covering node creation, connection, deletion, and workflow management. - Developed tests for project statistics and timeline visualization in Project, including error handling and internationalization checks.
This commit is contained in:
480
ccw/frontend/tests/e2e/api-settings.spec.ts
Normal file
480
ccw/frontend/tests/e2e/api-settings.spec.ts
Normal file
@@ -0,0 +1,480 @@
|
||||
// ========================================
|
||||
// E2E Tests: API Settings - CLI Provider Configuration
|
||||
// ========================================
|
||||
// End-to-end tests for API provider management and testing
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
|
||||
|
||||
test.describe('[API Settings] - CLI Provider Configuration Tests', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/api-settings', { waitUntil: 'networkidle' as const });
|
||||
});
|
||||
|
||||
test('L3.21 - Page loads and displays current configuration', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API response for current settings
|
||||
await page.route('**/api/settings/cli', (route) => {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
providers: [
|
||||
{
|
||||
id: 'provider-1',
|
||||
name: 'Gemini',
|
||||
endpoint: 'https://api.example.com',
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// Reload to trigger API
|
||||
await page.reload({ waitUntil: 'networkidle' as const });
|
||||
|
||||
// Look for API settings form
|
||||
const settingsForm = page.getByTestId('api-settings-form').or(
|
||||
page.locator('form').filter({ hasText: /api|settings|provider/i })
|
||||
);
|
||||
|
||||
const isFormVisible = await settingsForm.isVisible().catch(() => false);
|
||||
|
||||
if (isFormVisible) {
|
||||
// Verify provider list is displayed
|
||||
const providerList = page.getByTestId('provider-list').or(
|
||||
settingsForm.locator('*').filter({ hasText: /provider|gemini|cli/i })
|
||||
);
|
||||
|
||||
const hasProviders = await providerList.isVisible().catch(() => false);
|
||||
expect(hasProviders).toBe(true);
|
||||
}
|
||||
|
||||
monitoring.assertClean({ allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.22 - Add new CLI provider', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API for adding provider
|
||||
await page.route('**/api/settings/cli', (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
provider: {
|
||||
id: 'new-provider',
|
||||
name: 'Test Provider',
|
||||
endpoint: 'https://test.api.com'
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
// Look for add provider button
|
||||
const addButton = page.getByRole('button', { name: /add|create|new provider/i }).or(
|
||||
page.getByTestId('provider-add-button')
|
||||
);
|
||||
|
||||
const hasAddButton = await addButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasAddButton) {
|
||||
await addButton.click();
|
||||
|
||||
// Look for provider form
|
||||
const form = page.getByTestId('provider-form').or(
|
||||
page.getByRole('dialog').locator('form')
|
||||
);
|
||||
|
||||
const hasForm = await form.isVisible().catch(() => false);
|
||||
|
||||
if (hasForm) {
|
||||
// Fill in provider details
|
||||
const nameInput = form.getByRole('textbox', { name: /name/i }).or(
|
||||
form.getByLabel(/name/i)
|
||||
);
|
||||
|
||||
const hasNameInput = await nameInput.isVisible().catch(() => false);
|
||||
|
||||
if (hasNameInput) {
|
||||
await nameInput.fill('E2E Test Provider');
|
||||
|
||||
const endpointInput = form.getByRole('textbox', { name: /endpoint|url|api/i });
|
||||
const hasEndpointInput = await endpointInput.isVisible().catch(() => false);
|
||||
|
||||
if (hasEndpointInput) {
|
||||
await endpointInput.fill('https://e2e-test.api.com');
|
||||
|
||||
// Submit form
|
||||
const saveButton = form.getByRole('button', { name: /save|create|submit/i });
|
||||
await saveButton.click();
|
||||
|
||||
// Verify success
|
||||
const successMessage = page.getByText(/added|created|success/i);
|
||||
const hasSuccess = await successMessage.isVisible().catch(() => false);
|
||||
expect(hasSuccess).toBe(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monitoring.assertClean({ allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.23 - Edit existing provider', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API for editing provider
|
||||
await page.route('**/api/settings/cli', (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ success: true })
|
||||
});
|
||||
} else {
|
||||
route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
// Look for existing provider
|
||||
const providerItems = page.getByTestId(/provider-item|provider-card/).or(
|
||||
page.locator('.provider-item')
|
||||
);
|
||||
|
||||
const providerCount = await providerItems.count();
|
||||
|
||||
if (providerCount > 0) {
|
||||
const firstProvider = providerItems.first();
|
||||
|
||||
// Look for edit button
|
||||
const editButton = firstProvider.getByRole('button', { name: /edit|modify/i }).or(
|
||||
firstProvider.getByTestId('edit-button')
|
||||
);
|
||||
|
||||
const hasEditButton = await editButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasEditButton) {
|
||||
await editButton.click();
|
||||
|
||||
// Edit provider details
|
||||
const nameInput = page.getByRole('textbox', { name: /name/i });
|
||||
const hasNameInput = await nameInput.isVisible().catch(() => false);
|
||||
|
||||
if (hasNameInput) {
|
||||
await nameInput.fill('Updated Provider Name');
|
||||
|
||||
const saveButton = page.getByRole('button', { name: /save|update/i });
|
||||
await saveButton.click();
|
||||
|
||||
// Verify success
|
||||
const successMessage = page.getByText(/updated|saved|success/i);
|
||||
const hasSuccess = await successMessage.isVisible().catch(() => false);
|
||||
expect(hasSuccess).toBe(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monitoring.assertClean({ allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.24 - Delete provider', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API for deleting provider
|
||||
await page.route('**/api/settings/cli/*', (route) => {
|
||||
if (route.request().method() === 'DELETE') {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ success: true })
|
||||
});
|
||||
} else {
|
||||
route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
// Look for existing provider
|
||||
const providerItems = page.getByTestId(/provider-item|provider-card/).or(
|
||||
page.locator('.provider-item')
|
||||
);
|
||||
|
||||
const providerCount = await providerItems.count();
|
||||
|
||||
if (providerCount > 0) {
|
||||
const firstProvider = providerItems.first();
|
||||
|
||||
// Look for delete button
|
||||
const deleteButton = firstProvider.getByRole('button', { name: /delete|remove/i }).or(
|
||||
firstProvider.getByTestId('delete-button')
|
||||
);
|
||||
|
||||
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasDeleteButton) {
|
||||
await deleteButton.click();
|
||||
|
||||
// Confirm deletion if dialog appears
|
||||
const confirmDialog = page.getByRole('dialog').filter({ hasText: /delete|confirm/i });
|
||||
const hasDialog = await confirmDialog.isVisible().catch(() => false);
|
||||
|
||||
if (hasDialog) {
|
||||
const confirmButton = confirmDialog.getByRole('button', { name: /delete|confirm|yes/i });
|
||||
await confirmButton.click();
|
||||
}
|
||||
|
||||
// Verify success
|
||||
const successMessage = page.getByText(/deleted|removed|success/i);
|
||||
const hasSuccess = await successMessage.isVisible().catch(() => false);
|
||||
expect(hasSuccess).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
monitoring.assertClean({ allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.25 - Test API connection', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API for connection test
|
||||
await page.route('**/api/settings/test', (route) => {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
latency: 45,
|
||||
status: 'connected'
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// Look for test connection button
|
||||
const testButton = page.getByRole('button', { name: /test|check connection/i }).or(
|
||||
page.getByTestId('connection-test-button')
|
||||
);
|
||||
|
||||
const hasTestButton = await testButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasTestButton) {
|
||||
await testButton.click();
|
||||
|
||||
// Wait for test to complete
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify connection result
|
||||
const successMessage = page.getByText(/connected|success|available/i);
|
||||
const hasSuccess = await successMessage.isVisible().catch(() => false);
|
||||
expect(hasSuccess).toBe(true);
|
||||
}
|
||||
|
||||
monitoring.assertClean({ allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.26 - Save configuration persists', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API for saving
|
||||
await page.route('**/api/settings/cli', (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ success: true, persisted: true })
|
||||
});
|
||||
} else {
|
||||
route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
// Look for save button
|
||||
const saveButton = page.getByRole('button', { name: /save|apply/i }).or(
|
||||
page.getByTestId('provider-save-button')
|
||||
);
|
||||
|
||||
const hasSaveButton = await saveButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasSaveButton) {
|
||||
await saveButton.click();
|
||||
|
||||
// Verify persistence via localStorage
|
||||
const configStore = await page.evaluate(() => {
|
||||
const storage = localStorage.getItem('ccw-config-store');
|
||||
return storage ? JSON.parse(storage) : null;
|
||||
});
|
||||
|
||||
const hasPersisted = configStore !== null;
|
||||
expect(hasPersisted).toBe(true);
|
||||
}
|
||||
|
||||
monitoring.assertClean({ allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.27 - i18n - Form labels in EN/ZH', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Get language switcher
|
||||
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
|
||||
|
||||
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
|
||||
|
||||
if (hasLanguageSwitcher) {
|
||||
// Switch to Chinese
|
||||
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
|
||||
|
||||
// Verify form is visible in Chinese
|
||||
const form = page.getByTestId('api-settings-form').or(
|
||||
page.locator('form')
|
||||
);
|
||||
|
||||
const isFormVisible = await form.isVisible().catch(() => false);
|
||||
expect(isFormVisible).toBe(true);
|
||||
|
||||
// Check for Chinese text
|
||||
const pageContent = await page.content();
|
||||
const hasChineseText = /[\u4e00-\u9fa5]/.test(pageContent);
|
||||
expect(hasChineseText).toBe(true);
|
||||
|
||||
// Switch back to English
|
||||
await switchLanguageAndVerify(page, 'en', languageSwitcher);
|
||||
}
|
||||
|
||||
monitoring.assertClean({ allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.28 - Error - Invalid API endpoint', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API error for invalid endpoint
|
||||
await page.route('**/api/settings/test', (route) => {
|
||||
route.fulfill({
|
||||
status: 400,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
error: 'Invalid endpoint URL',
|
||||
details: 'Endpoint must be a valid HTTPS URL'
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// Look for endpoint input
|
||||
const endpointInput = page.getByRole('textbox', { name: /endpoint|url|api/i }).or(
|
||||
page.getByLabel(/endpoint|url/i)
|
||||
);
|
||||
|
||||
const hasEndpointInput = await endpointInput.isVisible().catch(() => false);
|
||||
|
||||
if (hasEndpointInput) {
|
||||
await endpointInput.fill('not-a-valid-url');
|
||||
|
||||
const testButton = page.getByRole('button', { name: /test|validate/i });
|
||||
const hasTestButton = await testButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasTestButton) {
|
||||
await testButton.click();
|
||||
|
||||
// Verify error message
|
||||
const errorMessage = page.getByText(/invalid|error|url/i);
|
||||
const hasError = await errorMessage.isVisible().catch(() => false);
|
||||
expect(hasError).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/test'], allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.29 - Error - Connection timeout', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock timeout
|
||||
await page.route('**/api/settings/test', (route) => {
|
||||
// Simulate timeout by never responding
|
||||
setTimeout(() => {
|
||||
route.abort('timedout');
|
||||
}, 35000);
|
||||
});
|
||||
|
||||
// Look for test connection button
|
||||
const testButton = page.getByRole('button', { name: /test|check/i });
|
||||
|
||||
const hasTestButton = await testButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasTestButton) {
|
||||
await testButton.click();
|
||||
|
||||
// Wait for timeout message
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Verify timeout error
|
||||
const timeoutMessage = page.getByText(/timeout|timed out|unavailable/i);
|
||||
const hasTimeout = await timeoutMessage.isVisible().catch(() => false);
|
||||
// Timeout message may or may not appear
|
||||
}
|
||||
|
||||
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/test'], allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
|
||||
test('L3.30 - Edge - Maximum providers limit', async ({ page }) => {
|
||||
const monitoring = setupEnhancedMonitoring(page);
|
||||
|
||||
// Mock API to enforce limit
|
||||
await page.route('**/api/settings/cli', (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
route.fulfill({
|
||||
status: 409,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
error: 'Maximum provider limit reached',
|
||||
limit: 10
|
||||
})
|
||||
});
|
||||
} else {
|
||||
route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
// Look for add provider button
|
||||
const addButton = page.getByRole('button', { name: /add|create/i });
|
||||
|
||||
const hasAddButton = await addButton.isVisible().catch(() => false);
|
||||
|
||||
if (hasAddButton) {
|
||||
await addButton.click();
|
||||
|
||||
// Try to add provider
|
||||
const nameInput = page.getByRole('textbox', { name: /name/i });
|
||||
const hasNameInput = await nameInput.isVisible().catch(() => false);
|
||||
|
||||
if (hasNameInput) {
|
||||
await nameInput.fill('Test Provider');
|
||||
|
||||
const saveButton = page.getByRole('button', { name: /save|create/i });
|
||||
await saveButton.click();
|
||||
|
||||
// Look for limit error
|
||||
const limitMessage = page.getByText(/limit|maximum|too many/i);
|
||||
const hasLimitMessage = await limitMessage.isVisible().catch(() => false);
|
||||
// Limit message may or may not appear
|
||||
}
|
||||
}
|
||||
|
||||
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/cli'], allowWarnings: true });
|
||||
monitoring.stop();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user