Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b957905
fix(frontend): Correct API URL construction for dev environment
google-labs-jules[bot] Aug 15, 2025
1af781d
fix(frontend): Replace crypto.randomUUID with uuid
google-labs-jules[bot] Aug 15, 2025
d5a7649
Merge pull request #1 from rnarciso/fix-frontend-api-url
rnarciso Aug 15, 2025
881401a
Merge branch 'coleam00:main' into main
rnarciso Aug 18, 2025
279f2ae
Fix several bugs in the Archon Unit Tests UI.
google-labs-jules[bot] Aug 18, 2025
ab2ddb4
Merge pull request #12 from rnarciso/fix-unit-test-bugs
rnarciso Aug 18, 2025
7a8b07b
Fix several bugs in the Archon Unit Tests UI.
google-labs-jules[bot] Aug 18, 2025
8efca65
Merge pull request #13 from rnarciso/fix-unit-test-bugs
rnarciso Aug 18, 2025
1861980
Merge branch 'coleam00:main' into main
rnarciso Aug 18, 2025
33e3a18
Fix React UI test assertion error
google-labs-jules[bot] Aug 18, 2025
6571319
Fix flaky React UI test for alert
google-labs-jules[bot] Aug 18, 2025
78d7084
Merge pull request #15 from rnarciso/fix-react-ui-test-assertion-error
rnarciso Aug 18, 2025
300c15e
Fix missing `act` import in React UI test
google-labs-jules[bot] Aug 18, 2025
998d46b
Merge branch 'fix-react-ui-test-assertion-error' of https://github.co…
Aug 18, 2025
46ca613
Fixed UI test by excluding erros.test.tsx
Aug 18, 2025
e640268
Fix `npm test` script to generate coverage
google-labs-jules[bot] Aug 18, 2025
1e064ac
Merge branch 'fix-react-ui-test-assertion-error' of https://github.co…
Aug 18, 2025
b726e3b
Fix test dashboard to use local report files
google-labs-jules[bot] Aug 18, 2025
20fd264
Merge branch 'fix-react-ui-test-assertion-error' of https://github.co…
Aug 18, 2025
92441eb
Fix test dashboard summary display
google-labs-jules[bot] Aug 18, 2025
33cd82d
Merge branch 'fix-react-ui-test-assertion-error' of https://github.co…
Aug 18, 2025
9911d1c
Merge branch 'main' into fix-react-ui-test-assertion-error
rnarciso Aug 18, 2025
e80a464
Merge branch 'fix-react-ui-test-assertion-error' of https://github.co…
Aug 18, 2025
26f0acc
Vitest Bail
rnarciso Aug 18, 2025
4313024
Fix failing React UI tests in errors.test.tsx and api.test.ts
google-labs-jules[bot] Aug 18, 2025
9b878fe
Fix failing React UI tests in errors.test.tsx and api.test.ts
google-labs-jules[bot] Aug 19, 2025
2a4e1f7
Merge pull request #19 from rnarciso/fix-failing-react-tests
rnarciso Aug 19, 2025
42c7eac
Merge pull request #18 from rnarciso/fix/failing-react-tests
rnarciso Aug 19, 2025
b8cc251
Merge branch 'coleam00:main' into main
rnarciso Aug 19, 2025
98ca9cc
Merge branch 'coleam00:main' into main
rnarciso Aug 19, 2025
62f4408
Merge branch 'coleam00:main' into main
rnarciso Aug 20, 2025
cd9258f
Merge branch 'coleam00:main' into main
rnarciso Aug 21, 2025
b9de0f0
Merge branch 'coleam00:main' into main
rnarciso Aug 22, 2025
7df4a34
Merge branch 'main' into fix_attempt
rnarciso Aug 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions archon-ui-main/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion archon-ui-main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"build": "npx vite build",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"preview": "npx vite preview",
"test": "vitest",
"test": "vitest run --coverage",
"test:ui": "vitest --ui",
"test:coverage": "npm run test:coverage:run && npm run test:coverage:summary",
"test:coverage:run": "vitest run --coverage --reporter=dot --reporter=json",
Expand Down Expand Up @@ -36,6 +36,7 @@
"react-router-dom": "^6.26.2",
"socket.io-client": "^4.8.1",
"tailwind-merge": "latest",
"uuid": "^11.1.0",
"zod": "^3.25.46"
},
"devDependencies": {
Expand All @@ -45,6 +46,7 @@
"@types/node": "^20.19.0",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.1",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"@vitejs/plugin-react": "^4.2.1",
Expand Down
44 changes: 28 additions & 16 deletions archon-ui-main/src/components/ui/TestResultDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
);
}

if (!results) {
const summary = results?.summary;

if (!summary) {
return (
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 border border-gray-200 dark:border-gray-700 shadow-sm">
<div className="flex flex-col items-center justify-center py-8 text-center">
Expand All @@ -88,11 +90,18 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
);
}

const { summary } = results;
const successRate = summary.total > 0 ? (summary.passed / summary.total) * 100 : 0;
const safeSummary = {
total: summary.total || 0,
passed: summary.passed || 0,
failed: summary.failed || 0,
skipped: summary.skipped || 0,
duration: summary.duration || 0,
};

const successRate = safeSummary.total > 0 ? (safeSummary.passed / safeSummary.total) * 100 : 0;

const getHealthStatus = () => {
if (summary.failed === 0 && summary.passed > 0) return { text: 'All Tests Passing', color: 'text-green-600 dark:text-green-400', bg: 'bg-green-50 dark:bg-green-900/20' };
if (safeSummary.failed === 0 && safeSummary.passed > 0) return { text: 'All Tests Passing', color: 'text-green-600 dark:text-green-400', bg: 'bg-green-50 dark:bg-green-900/20' };
if (successRate >= 80) return { text: 'Mostly Passing', color: 'text-yellow-600 dark:text-yellow-400', bg: 'bg-yellow-50 dark:bg-yellow-900/20' };
return { text: 'Tests Failing', color: 'text-red-600 dark:text-red-400', bg: 'bg-red-50 dark:bg-red-900/20' };
};
Expand Down Expand Up @@ -127,7 +136,7 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
className="text-center p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg"
>
<div className="text-2xl font-bold text-gray-800 dark:text-white mb-1">
{summary.total}
{safeSummary.total}
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Total Tests</div>
</motion.div>
Expand All @@ -140,7 +149,7 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
>
<div className="text-2xl font-bold text-green-600 dark:text-green-400 mb-1 flex items-center justify-center gap-1">
<CheckCircle className="w-5 h-5" />
{summary.passed}
{safeSummary.passed}
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Passed</div>
</motion.div>
Expand All @@ -153,7 +162,7 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
>
<div className="text-2xl font-bold text-red-600 dark:text-red-400 mb-1 flex items-center justify-center gap-1">
<XCircle className="w-5 h-5" />
{summary.failed}
{safeSummary.failed}
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Failed</div>
</motion.div>
Expand All @@ -166,7 +175,7 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
>
<div className="text-2xl font-bold text-yellow-600 dark:text-yellow-400 mb-1 flex items-center justify-center gap-1">
<Clock className="w-5 h-5" />
{summary.skipped}
{safeSummary.skipped}
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Skipped</div>
</motion.div>
Expand Down Expand Up @@ -195,7 +204,7 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
<div className="flex items-center justify-between text-sm text-gray-600 dark:text-gray-400">
<div className="flex items-center gap-2">
<Zap className="w-4 h-4" />
<span>Duration: {(summary.duration / 1000).toFixed(2)}s</span>
<span>Duration: {(safeSummary.duration / 1000).toFixed(2)}s</span>
</div>
{results.timestamp && (
<div className="flex items-center gap-2">
Expand All @@ -206,7 +215,7 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
</div>

{/* Failed Tests Alert */}
{summary.failed > 0 && (
{safeSummary.failed > 0 && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
Expand All @@ -215,7 +224,7 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
<div className="flex items-center gap-2 text-red-700 dark:text-red-400">
<AlertTriangle className="w-4 h-4" />
<span className="text-sm font-medium">
{summary.failed} test{summary.failed > 1 ? 's' : ''} failing - review errors below
{safeSummary.failed} test{safeSummary.failed > 1 ? 's' : ''} failing - review errors below
</span>
</div>
</motion.div>
Expand All @@ -225,7 +234,10 @@ const TestSummaryCard: React.FC<TestSummaryCardProps> = ({ results, isLoading })
};

const FailedTestsList: React.FC<{ results: TestResults }> = ({ results }) => {
const failedSuites = results.suites.filter(suite => suite.failed > 0);
if (!results || !Array.isArray(results.suites) || !results.summary) {
return null;
}
const failedSuites = results.suites.filter(suite => suite && typeof suite.failed === 'number' && suite.failed > 0);

if (failedSuites.length === 0) {
return null;
Expand All @@ -240,7 +252,7 @@ const FailedTestsList: React.FC<{ results: TestResults }> = ({ results }) => {
<div className="flex items-center gap-3 mb-4">
<XCircle className="w-5 h-5 text-red-500" />
<h3 className="text-lg font-semibold text-gray-800 dark:text-white">
Failed Tests ({results.summary.failed})
Failed Tests ({results?.summary?.failed || 0})
</h3>
</div>

Expand Down Expand Up @@ -381,9 +393,9 @@ export const TestResultDashboard: React.FC<TestResultDashboardProps> = ({
)}

{/* Main content */}
<div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'grid-cols-1 xl:grid-cols-2'}`}>
<div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'grid-cols-1'}`}>
{/* Test Summary */}
<div>
<div className="flex-1">
<TestSummaryCard results={results} isLoading={loading && !results} />
</div>

Comment on lines +396 to 401
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Grid layout condition is a no-op; compact has no effect

Both branches yield 'grid-cols-1', so the layout never expands. Likely a regression from the earlier xl breakpoint removal.

Fix the condition to restore non-compact layout:

-      <div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'grid-cols-1'}`}>
+      <div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'md:grid-cols-2'}`}>

Alternatively, if you intentionally want single-column below xl, preserve a wider layout at larger breakpoints:

-      <div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'grid-cols-1'}`}>
+      <div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'grid-cols-1 xl:grid-cols-2'}`}>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'grid-cols-1'}`}>
{/* Test Summary */}
<div>
<div className="flex-1">
<TestSummaryCard results={results} isLoading={loading && !results} />
</div>
<div className={`grid gap-6 ${compact ? 'grid-cols-1' : 'md:grid-cols-2'}`}>
{/* Test Summary */}
<div className="flex-1">
<TestSummaryCard results={results} isLoading={loading && !results} />
</div>
🤖 Prompt for AI Agents
In archon-ui-main/src/components/ui/TestResultDashboard.tsx around lines 396 to
401, the grid class conditional is a no-op because both branches use
'grid-cols-1'; change the else branch to a wider column layout so compact
actually changes layout (for example use `${compact ? 'grid-cols-1' :
'grid-cols-2'}` or restore breakpoint-based classes like `grid-cols-1
md:grid-cols-2`/`grid-cols-1 xl:grid-cols-2` depending on desired responsive
behavior); update the JSX className string accordingly to reflect the
non-compact layout.

Expand All @@ -400,7 +412,7 @@ export const TestResultDashboard: React.FC<TestResultDashboardProps> = ({
</div>

{/* Failed Tests */}
{results && results.summary.failed > 0 && (
{results?.summary?.failed > 0 && results?.suites && (
<FailedTestsList results={results} />
)}
</div>
Expand Down
31 changes: 16 additions & 15 deletions archon-ui-main/src/config/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,28 @@

// Get the API URL from environment or construct it
export function getApiUrl(): string {
// Check if VITE_API_URL is provided (set by docker-compose)
// 1. Priority: VITE_API_URL from environment (e.g., Docker)
if (import.meta.env.VITE_API_URL) {
return import.meta.env.VITE_API_URL;
return import.meta.env.VITE_API_URL
}

// For relative URLs in production (goes through proxy)
// 2. Production mode: Use relative path
if (import.meta.env.PROD) {
return '';
return ''
}

// For development, construct from window location
const protocol = window.location.protocol;
const host = window.location.hostname;
// Use configured port or default to 8181
const port = import.meta.env.VITE_ARCHON_SERVER_PORT || '8181';

if (!import.meta.env.VITE_ARCHON_SERVER_PORT) {
console.info('[Archon] Using default ARCHON_SERVER_PORT: 8181');
}

return `${protocol}//${host}:${port}`;
// 3. Development mode: Construct URL from port
const port = import.meta.env.ARCHON_SERVER_PORT
if (!port) {
throw new Error(
'ARCHON_SERVER_PORT environment variable is required. ' +
'Please set it in your .env file. ' +
'Default value: 8181',
)

const protocol = window.location.protocol
const hostname = window.location.hostname
return `${protocol}//${hostname}:${port}`
}

// Get the base path for API endpoints
Expand Down
4 changes: 1 addition & 3 deletions archon-ui-main/src/services/mcpClientService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,11 @@ const MCPToolSchema = z.object({
export type MCPTool = z.infer<typeof MCPToolSchema>;
export type MCPParameter = z.infer<typeof MCPParameterSchema>;

import { getApiUrl } from '../config/api';

/**
* MCP Client Service - Universal MCP client that connects to any MCP servers
* This service communicates with the standalone Python MCP client service
*/
class MCPClientService {
export class MCPClientService {
private baseUrl = getApiUrl();

// ========================================
Expand Down
66 changes: 35 additions & 31 deletions archon-ui-main/src/services/testService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface TestStatus {
}

import { getApiUrl, getWebSocketUrl } from '../config/api';
import { v4 as uuidv4 } from 'uuid';

// Use unified API configuration
const API_BASE_URL = getApiUrl();
Expand Down Expand Up @@ -159,7 +160,7 @@ class TestService {
onError?: (error: Error) => void,
onComplete?: () => void
): Promise<string> {
const execution_id = crypto.randomUUID();
const execution_id = uuidv4();

try {
// Send initial status
Expand Down Expand Up @@ -247,44 +248,47 @@ class TestService {
* Get coverage data for Test Results Modal from new API endpoints with fallback
*/
async getCoverageData(): Promise<any> {
try {
// Try new API endpoint first
const response = await callAPI<any>('/api/coverage/combined-summary');
return response;
} catch (apiError) {
// Fallback to static files for backward compatibility
try {
const response = await fetch('/test-results/coverage/coverage-summary.json');
if (!response.ok) {
throw new Error('Coverage data not available');
}
return await response.json();
} catch (staticError) {
throw new Error(`Failed to load coverage data: ${apiError instanceof Error ? apiError.message : 'API and static files unavailable'}`);
}
const response = await fetch('/test-results/coverage/coverage-summary.json');
if (!response.ok) {
throw new Error('Coverage data not available');
}
return await response.json();
}

/**
* Get test results for Test Results Modal from new API endpoints with fallback
*/
async getTestResults(): Promise<any> {
try {
// Try new API endpoint first
const response = await callAPI<any>('/api/tests/latest-results');
return response;
} catch (apiError) {
// Fallback to static files for backward compatibility
try {
const response = await fetch('/test-results/test-results.json');
if (!response.ok) {
throw new Error('Test results not available');
}
return await response.json();
} catch (staticError) {
throw new Error(`Failed to load test results: ${apiError instanceof Error ? apiError.message : 'API and static files unavailable'}`);
}
const response = await fetch('/test-results/test-results.json');
if (!response.ok) {
throw new Error('Test results not available');
}
const data = await response.json();

// Transform the data to match the expected TestResults interface
const transformedData = {
summary: {
total: data.numTotalTests || 0,
passed: data.numPassedTests || 0,
failed: data.numFailedTests || 0,
skipped: data.numPendingTests || 0,
duration: data.testResults.reduce((acc: number, suite: any) => acc + (suite.endTime - suite.startTime), 0),
},
suites: data.testResults.map((suite: any) => ({
name: suite.name,
tests: suite.assertionResults.length,
passed: suite.assertionResults.filter((r: any) => r.status === 'passed').length,
failed: suite.assertionResults.filter((r: any) => r.status === 'failed').length,
skipped: suite.assertionResults.filter((r: any) => r.status === 'pending' || r.status === 'todo').length,
duration: suite.endTime - suite.startTime,
failedTests: suite.assertionResults
.filter((r: any) => r.status === 'failed')
.map((r: any) => ({ name: r.title, error: r.failureMessages.join('\\n') }))
})),
timestamp: new Date(data.startTime).toISOString()
};

return transformedData;
}

/**
Expand Down
9 changes: 5 additions & 4 deletions archon-ui-main/src/utils/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ export function isLmConfigured(
// Helper function to check if a credential has a valid value
const hasValidCredential = (cred: NormalizedCredential | undefined): boolean => {
if (!cred) return false;
return !!(
(cred.value && cred.value !== 'null' && cred.value !== null && cred.value.trim() !== '') ||
(cred.is_encrypted && cred.encrypted_value && cred.encrypted_value !== 'null' && cred.encrypted_value !== null)
);
// If is_encrypted is true, we consider it valid even without a value,
// as the value is stored on the server.
if (cred.is_encrypted) return true;

return !!(cred.value && cred.value !== 'null' && cred.value !== null && cred.value.trim() !== '');
};

// Find API keys
Expand Down
Loading