-
+
{
}
describe('ProxyHostForm', () => {
- const mockOnSubmit = vi.fn(() => Promise.resolve())
+ const mockOnSubmit = vi.fn((_data: any) => Promise.resolve())
const mockOnCancel = vi.fn()
afterEach(() => {
@@ -251,4 +251,76 @@ describe('ProxyHostForm', () => {
fireEvent.change(input, { target: { value: 'tcp://remote:2375' } })
expect(input).toHaveValue('tcp://remote:2375')
})
+
+ it('toggles all checkboxes', async () => {
+ renderWithClient(
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('Add Proxy Host')).toBeInTheDocument()
+ })
+
+ // Fill required fields
+ fireEvent.change(screen.getByPlaceholderText('example.com, www.example.com'), { target: { value: 'test.com' } })
+ fireEvent.change(screen.getByPlaceholderText('192.168.1.100'), { target: { value: '10.0.0.1' } })
+
+ const checkboxes = [
+ 'Force SSL',
+ 'HTTP/2 Support',
+ 'HSTS Enabled',
+ 'HSTS Subdomains',
+ 'Block Common Exploits',
+ 'WebSocket Support',
+ 'Enabled'
+ ]
+
+ for (const label of checkboxes) {
+ const checkbox = screen.getByLabelText(label)
+ fireEvent.click(checkbox)
+ }
+
+ // Verify state change by submitting
+ fireEvent.click(screen.getByText('Create'))
+
+ await waitFor(() => {
+ expect(mockOnSubmit).toHaveBeenCalled()
+ })
+
+ // Check that the submitted data reflects the toggles
+ // Default for block_exploits is true, others false (except enabled)
+ // We toggled them, so block_exploits should be false, others true (enabled false)
+ // Wait, enabled default is true. So enabled -> false.
+ // block_exploits default true -> false.
+ // others default false -> true.
+
+ const submittedData = mockOnSubmit.mock.calls[0]?.[0] as any
+ expect(submittedData).toBeDefined()
+ if (submittedData) {
+ expect(submittedData.ssl_forced).toBe(true)
+ expect(submittedData.http2_support).toBe(true)
+ expect(submittedData.hsts_enabled).toBe(true)
+ expect(submittedData.hsts_subdomains).toBe(true)
+ expect(submittedData.block_exploits).toBe(false)
+ expect(submittedData.websocket_support).toBe(true)
+ expect(submittedData.enabled).toBe(false)
+ }
+ })
+
+ it('handles scheme selection', async () => {
+ renderWithClient(
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('Add Proxy Host')).toBeInTheDocument()
+ })
+
+ // Find scheme select - it defaults to HTTP
+ // We can find it by label "Scheme"
+ const schemeSelect = screen.getByLabelText('Scheme')
+ fireEvent.change(schemeSelect, { target: { value: 'https' } })
+
+ expect(schemeSelect).toHaveValue('https')
+ })
})
diff --git a/frontend/src/components/__tests__/RemoteServerForm.test.tsx b/frontend/src/components/__tests__/RemoteServerForm.test.tsx
index 0ce0c319..a1550350 100644
--- a/frontend/src/components/__tests__/RemoteServerForm.test.tsx
+++ b/frontend/src/components/__tests__/RemoteServerForm.test.tsx
@@ -1,12 +1,11 @@
import { describe, it, expect, vi, afterEach } from 'vitest'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import RemoteServerForm from '../RemoteServerForm'
+import * as remoteServersApi from '../../api/remoteServers'
// Mock the API
-vi.mock('../../services/api', () => ({
- remoteServersAPI: {
- test: vi.fn(() => Promise.resolve({ reachable: true, address: 'localhost:8080' })),
- },
+vi.mock('../../api/remoteServers', () => ({
+ testRemoteServerConnection: vi.fn(() => Promise.resolve({ address: 'localhost:8080' })),
}))
describe('RemoteServerForm', () => {
@@ -121,4 +120,74 @@ describe('RemoteServerForm', () => {
expect(providerSelect).toHaveValue('docker')
})
+
+ it('handles submission error', async () => {
+ const mockErrorSubmit = vi.fn(() => Promise.reject(new Error('Submission failed')))
+ render(
+
+ )
+
+ // Fill required fields
+ fireEvent.change(screen.getByPlaceholderText('My Production Server'), { target: { value: 'Test Server' } })
+ fireEvent.change(screen.getByPlaceholderText('192.168.1.100'), { target: { value: '10.0.0.1' } })
+
+ fireEvent.click(screen.getByText('Create'))
+
+ await waitFor(() => {
+ expect(screen.getByText('Submission failed')).toBeInTheDocument()
+ })
+ })
+
+ it('handles test connection success', async () => {
+ const mockAlert = vi.spyOn(window, 'alert').mockImplementation(() => {})
+ const mockServer = {
+ uuid: '123',
+ name: 'Test Server',
+ provider: 'docker',
+ host: 'localhost',
+ port: 5000,
+ enabled: true,
+ reachable: true,
+ created_at: '2025-11-18T10:00:00Z',
+ updated_at: '2025-11-18T10:00:00Z',
+ }
+
+ render(
+
+ )
+
+ fireEvent.click(screen.getByText('Test Connection'))
+
+ await waitFor(() => {
+ expect(mockAlert).toHaveBeenCalledWith('Connection successful: localhost:8080')
+ })
+ mockAlert.mockRestore()
+ })
+
+ it('handles test connection failure', async () => {
+ // Override mock for this test
+ vi.mocked(remoteServersApi.testRemoteServerConnection).mockRejectedValueOnce(new Error('Connection failed'))
+
+ const mockServer = {
+ uuid: '123',
+ name: 'Test Server',
+ provider: 'docker',
+ host: 'localhost',
+ port: 5000,
+ enabled: true,
+ reachable: true,
+ created_at: '2025-11-18T10:00:00Z',
+ updated_at: '2025-11-18T10:00:00Z',
+ }
+
+ render(
+
+ )
+
+ fireEvent.click(screen.getByText('Test Connection'))
+
+ await waitFor(() => {
+ expect(screen.getByText('Connection failed')).toBeInTheDocument()
+ })
+ })
})
diff --git a/frontend/src/hooks/__tests__/useTheme.test.tsx b/frontend/src/hooks/__tests__/useTheme.test.tsx
new file mode 100644
index 00000000..a47f2159
--- /dev/null
+++ b/frontend/src/hooks/__tests__/useTheme.test.tsx
@@ -0,0 +1,17 @@
+import { describe, it, expect } from 'vitest'
+import { renderHook } from '@testing-library/react'
+import { useTheme } from '../useTheme'
+
+describe('useTheme', () => {
+ it('throws error when used outside ThemeProvider', () => {
+ // Suppress console.error for this test as React logs the error
+ const consoleSpy = vi.spyOn(console, 'error')
+ consoleSpy.mockImplementation(() => {})
+
+ expect(() => {
+ renderHook(() => useTheme())
+ }).toThrow('useTheme must be used within a ThemeProvider')
+
+ consoleSpy.mockRestore()
+ })
+})