chore: update gitignore and restore backend files

- Add backend/node_modules to .gitignore
- Untrack backend/node_modules from git
- Restore backend source files (go.mod, main.go, etc.) that were missing in HEAD
- Include frontend test updates
This commit is contained in:
Wikid82
2025-11-19 21:45:21 -05:00
parent f7ce1df847
commit d6bfb0c3c9
42 changed files with 2056 additions and 3416 deletions
+4 -2
View File
@@ -88,10 +88,11 @@ export default function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFor
{/* Remote Server Quick Select */}
{remoteServers.length > 0 && (
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
<label htmlFor="quick-select" className="block text-sm font-medium text-gray-300 mb-2">
Quick Select from Remote Servers
</label>
<select
id="quick-select"
onChange={e => handleServerSelect(e.target.value)}
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"
>
@@ -212,10 +213,11 @@ export default function ProxyHostForm({ host, onSubmit, onCancel }: ProxyHostFor
{/* Advanced Config */}
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
<label htmlFor="advanced-config" className="block text-sm font-medium text-gray-300 mb-2">
Advanced Caddy Config (Optional)
</label>
<textarea
id="advanced-config"
value={formData.advanced_config}
onChange={e => setFormData({ ...formData, advanced_config: e.target.value })}
placeholder="Additional Caddy directives..."
@@ -149,4 +149,54 @@ describe('ProxyHostForm', () => {
expect(sslCheckbox).toBeChecked()
expect(wsCheckbox).toBeChecked()
})
it('populates fields when remote server is selected', async () => {
renderWithClient(
<ProxyHostForm onSubmit={mockOnSubmit} onCancel={mockOnCancel} />
)
await waitFor(() => {
expect(screen.getByText(/Local Docker Registry/)).toBeInTheDocument()
})
const select = screen.getByRole('combobox', { name: /quick select/i })
fireEvent.change(select, { target: { value: mockRemoteServers[0].uuid } })
expect(screen.getByDisplayValue(mockRemoteServers[0].host)).toBeInTheDocument()
expect(screen.getByDisplayValue(mockRemoteServers[0].port)).toBeInTheDocument()
})
it('displays error message on submission failure', async () => {
const mockErrorSubmit = vi.fn(() => Promise.reject(new Error('Submission failed')))
renderWithClient(
<ProxyHostForm onSubmit={mockErrorSubmit} onCancel={mockOnCancel} />
)
fireEvent.change(screen.getByPlaceholderText('example.com, www.example.com'), {
target: { value: 'test.com' },
})
fireEvent.change(screen.getByPlaceholderText('192.168.1.100'), {
target: { value: 'localhost' },
})
fireEvent.change(screen.getByDisplayValue('80'), {
target: { value: '8080' },
})
fireEvent.click(screen.getByText('Create'))
await waitFor(() => {
expect(screen.getByText('Submission failed')).toBeInTheDocument()
})
})
it('handles advanced config input', async () => {
renderWithClient(
<ProxyHostForm onSubmit={mockOnSubmit} onCancel={mockOnCancel} />
)
const advancedInput = screen.getByLabelText(/Advanced Caddy Config/i)
fireEvent.change(advancedInput, { target: { value: 'header_up X-Test "True"' } })
expect(advancedInput).toHaveValue('header_up X-Test "True"')
})
})
+67 -1
View File
@@ -1,10 +1,20 @@
import { render, screen, waitFor } from '@testing-library/react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import Setup from '../Setup';
import * as setupApi from '../../api/setup';
// Mock react-router-dom
const mockNavigate = vi.fn();
vi.mock('react-router-dom', async () => {
const actual = await vi.importActual('react-router-dom');
return {
...actual,
useNavigate: () => mockNavigate,
};
});
// Mock the API module
vi.mock('../../api/setup', () => ({
getSetupStatus: vi.fn(),
@@ -57,5 +67,61 @@ describe('Setup Page', () => {
await waitFor(() => {
expect(screen.queryByText('Welcome to CPM+')).toBeNull();
});
await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith('/login');
});
});
it('submits form successfully', async () => {
vi.mocked(setupApi.getSetupStatus).mockResolvedValue({ setupRequired: true });
vi.mocked(setupApi.performSetup).mockResolvedValue();
renderWithProviders(<Setup />);
await waitFor(() => {
expect(screen.getByText('Welcome to CPM+')).toBeTruthy();
});
fireEvent.change(screen.getByLabelText('Name'), { target: { value: 'Admin' } });
fireEvent.change(screen.getByLabelText('Email Address'), { target: { value: 'admin@example.com' } });
fireEvent.change(screen.getByLabelText('Password'), { target: { value: 'password123' } });
fireEvent.click(screen.getByRole('button', { name: 'Create Admin Account' }));
await waitFor(() => {
expect(setupApi.performSetup).toHaveBeenCalledWith({
name: 'Admin',
email: 'admin@example.com',
password: 'password123',
}, expect.anything());
});
await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith('/login');
});
});
it('displays error on submission failure', async () => {
vi.mocked(setupApi.getSetupStatus).mockResolvedValue({ setupRequired: true });
vi.mocked(setupApi.performSetup).mockRejectedValue({
response: { data: { error: 'Setup failed' } }
});
renderWithProviders(<Setup />);
await waitFor(() => {
expect(screen.getByText('Welcome to CPM+')).toBeTruthy();
});
fireEvent.change(screen.getByLabelText('Name'), { target: { value: 'Admin' } });
fireEvent.change(screen.getByLabelText('Email Address'), { target: { value: 'admin@example.com' } });
fireEvent.change(screen.getByLabelText('Password'), { target: { value: 'password123' } });
fireEvent.click(screen.getByRole('button', { name: 'Create Admin Account' }));
await waitFor(() => {
expect(screen.getByText('Setup failed')).toBeTruthy();
});
});
});