111 lines
4.3 KiB
TypeScript
111 lines
4.3 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { supabase } from '../services/supabaseClient';
|
|
import { BeakerIcon } from './icons/BeakerIcon';
|
|
import { LoadingSpinner } from './LoadingSpinner';
|
|
import { CheckCircleIcon } from './icons/CheckCircleIcon';
|
|
import { XCircleIcon } from './icons/XCircleIcon';
|
|
|
|
type TestStatus = 'idle' | 'running' | 'pass' | 'fail';
|
|
|
|
interface TestResult {
|
|
name: string;
|
|
status: TestStatus;
|
|
message: string;
|
|
}
|
|
|
|
const initialTests: TestResult[] = [
|
|
{ name: 'Test Admin Login', status: 'idle', message: 'Verifies the seeded admin user can log in.' },
|
|
];
|
|
|
|
export const DevTestRunner: React.FC = () => {
|
|
const [testResults, setTestResults] = useState<TestResult[]>(initialTests);
|
|
const [isRunning, setIsRunning] = useState(false);
|
|
|
|
const runTests = async () => {
|
|
setIsRunning(true);
|
|
// Reset statuses to running
|
|
setTestResults(prev => prev.map(t => ({ ...t, status: 'running' })));
|
|
|
|
// --- Test Case 1: Admin Login ---
|
|
try {
|
|
const { error } = await supabase.auth.signInWithPassword({
|
|
email: 'admin@example.com',
|
|
password: 'password123',
|
|
});
|
|
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
// IMPORTANT: Sign out immediately so the test doesn't affect the app's state
|
|
await supabase.auth.signOut();
|
|
|
|
setTestResults(prev => prev.map(t => t.name === 'Test Admin Login'
|
|
? { ...t, status: 'pass', message: 'Successfully logged in and out.' }
|
|
: t
|
|
));
|
|
} catch (e: any) {
|
|
setTestResults(prev => prev.map(t => t.name === 'Test Admin Login'
|
|
? { ...t, status: 'fail', message: `Failed: ${e.message}` }
|
|
: t
|
|
));
|
|
}
|
|
|
|
setIsRunning(false);
|
|
};
|
|
|
|
const getStatusIndicator = (status: TestStatus) => {
|
|
switch (status) {
|
|
case 'running': return <div className="w-5 h-5 text-blue-500"><LoadingSpinner /></div>;
|
|
case 'pass': return <CheckCircleIcon className="w-5 h-5 text-green-500" />;
|
|
case 'fail': return <XCircleIcon className="w-5 h-5 text-red-500" />;
|
|
case 'idle': return <div className="w-5 h-5 rounded-full border-2 border-gray-400 dark:border-gray-600"></div>;
|
|
}
|
|
};
|
|
|
|
if (!supabase) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
|
|
<h3 className="text-lg font-bold text-gray-800 dark:text-white flex items-center mb-3">
|
|
<BeakerIcon className="w-6 h-6 mr-2 text-brand-primary" />
|
|
Development Test Runner
|
|
</h3>
|
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
|
Run integration tests to verify your backend setup.
|
|
</p>
|
|
|
|
<ul className="space-y-3 mb-4">
|
|
{testResults.map(test => (
|
|
<li key={test.name} className="flex items-start space-x-3">
|
|
<div className="flex-shrink-0 pt-0.5">{getStatusIndicator(test.status)}</div>
|
|
<div>
|
|
<p className="text-sm font-semibold text-gray-800 dark:text-gray-200">{test.name}</p>
|
|
<p className={`text-xs ${test.status === 'fail' ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`}>
|
|
{test.message}
|
|
</p>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<button
|
|
onClick={runTests}
|
|
disabled={isRunning}
|
|
className="w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-wait text-gray-800 dark:text-white font-bold py-2 px-4 rounded-lg transition-colors duration-300 flex items-center justify-center"
|
|
>
|
|
{isRunning ? (
|
|
<>
|
|
<div className="w-5 h-5 mr-2"><LoadingSpinner /></div>
|
|
Running Tests...
|
|
</>
|
|
) : (
|
|
'Run Tests'
|
|
)}
|
|
</button>
|
|
</div>
|
|
);
|
|
};
|