feat(tests): add comprehensive tests for ProxyHosts and Uptime components

- Introduced isolated coverage tests for ProxyHosts with various scenarios including rendering, bulk apply, and link behavior.
- Enhanced existing ProxyHosts coverage tests to include additional assertions and error handling.
- Added tests for Uptime component to verify rendering and monitoring toggling functionality.
- Created utility functions for setting labels and help texts related to proxy host settings.
- Implemented bulk settings application logic with progress tracking and error handling.
- Added toast utility tests to ensure callback functionality and ID incrementing.
- Improved type safety in test files by using appropriate TypeScript types.
This commit is contained in:
GitHub Actions
2025-11-30 15:17:38 +00:00
parent d80f545a6e
commit 224a53975d
38 changed files with 1821 additions and 233 deletions

View File

@@ -1,7 +1,9 @@
import { useState, useEffect } from 'react'
import { CircleHelp, AlertCircle, Check, X, Loader2, Copy, Info } from 'lucide-react'
import { toast } from 'react-hot-toast'
import type { ProxyHost, ApplicationPreset } from '../api/proxyHosts'
import { testProxyHostConnection } from '../api/proxyHosts'
import { syncMonitors } from '../api/uptime'
import { useRemoteServers } from '../hooks/useRemoteServers'
import { useDomains } from '../hooks/useDomains'
import { useCertificates } from '../hooks/useCertificates'
@@ -85,7 +87,8 @@ interface ProxyHostFormProps {
}
export default function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFormProps) {
const [formData, setFormData] = useState({
type ProxyHostFormState = Partial<ProxyHost> & { addUptime?: boolean; uptimeInterval?: number; uptimeMaxRetries?: number }
const [formData, setFormData] = useState<ProxyHostFormState>({
name: host?.name || '',
domain_names: host?.domain_names || '',
forward_scheme: host?.forward_scheme || 'http',
@@ -144,7 +147,7 @@ export default function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFor
// Get the external URL for this proxy host
const getExternalUrl = () => {
const domain = formData.domain_names.split(',')[0]?.trim()
const domain = (formData.domain_names ?? '').split(',')[0]?.trim()
if (!domain) return ''
return `https://${domain}:443`
}
@@ -269,28 +272,38 @@ export default function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFor
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [nameError, setNameError] = useState<string | null>(null)
const [addUptime, setAddUptime] = useState(false)
const [uptimeInterval, setUptimeInterval] = useState(60)
const [uptimeMaxRetries, setUptimeMaxRetries] = useState(3)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError(null)
setNameError(null)
// Validate name is required
if (!formData.name.trim()) {
setNameError('Name is required')
setLoading(false)
return
}
try {
await onSubmit(formData)
} catch (err: unknown) {
console.error("Submit error:", err)
// Extract error message from axios response if available
const errorObj = err as { response?: { data?: { error?: string } }; message?: string }
const message = errorObj.response?.data?.error || errorObj.message || 'Failed to save proxy host'
const payload = { ...formData }
// strip temporary uptime-only flags from payload by destructuring
const { addUptime: _addUptime, uptimeInterval: _uptimeInterval, uptimeMaxRetries: _uptimeMaxRetries, ...payloadWithoutUptime } = payload as ProxyHostFormState
void _addUptime; void _uptimeInterval; void _uptimeMaxRetries;
const res = await onSubmit(payloadWithoutUptime)
// if user asked to add uptime, request server to sync monitors
if (addUptime) {
try {
await syncMonitors({ interval: uptimeInterval, max_retries: uptimeMaxRetries })
toast.success('Requested uptime monitor creation')
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err)
toast.error(msg || 'Failed to request uptime creation')
}
}
onCancel()
return res
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to save host'
setError(message)
toast.error(message)
throw err
} finally {
setLoading(false)
}
@@ -555,7 +568,10 @@ export default function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFor
min="1"
max="65535"
value={formData.forward_port}
onChange={e => setFormData({ ...formData, forward_port: parseInt(e.target.value) })}
onChange={e => {
const v = parseInt(e.target.value)
setFormData({ ...formData, forward_port: Number.isNaN(v) ? 0 : v })
}}
className="w-full bg-gray-900 border border-gray-700 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
@@ -875,6 +891,44 @@ export default function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFor
</label>
</div>
{/* Uptime option */}
<div className="border-t border-gray-800 pt-4">
<label className="flex items-center gap-3">
<input
type="checkbox"
checked={addUptime}
onChange={e => setAddUptime(e.target.checked)}
className="w-4 h-4 text-blue-600 bg-gray-900 border-gray-700 rounded focus:ring-blue-500"
/>
<span className="text-sm text-gray-300">Add Uptime monitoring for this host</span>
</label>
{addUptime && (
<div className="grid grid-cols-2 gap-4 mt-3">
<div>
<label className="block text-sm text-gray-400 mb-1">Check Interval (seconds)</label>
<input
type="number"
min={10}
value={uptimeInterval}
onChange={e => setUptimeInterval(parseInt(e.target.value || '60'))}
className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white"
/>
</div>
<div>
<label className="block text-sm text-gray-400 mb-1">Max Retries</label>
<input
type="number"
min={1}
value={uptimeMaxRetries}
onChange={e => setUptimeMaxRetries(parseInt(e.target.value || '3'))}
className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white"
/>
</div>
</div>
)}
</div>
{/* Actions */}
<div className="flex gap-3 justify-end pt-4 border-t border-gray-800">
<button