feat: improve Caddy import with directive detection and warnings
Add backend detection for import directives with actionable error message Display warning banner for unsupported features (file_server, redirects) Ensure multi-file import button always visible in upload form Add accessibility attributes (role, aria-labelledby) to multi-site modal Fix 12 frontend unit tests with outdated hook mock interfaces Add data-testid attributes for E2E test reliability Fix JSON syntax in 4 translation files (missing commas) Create 6 diagnostic E2E tests covering import edge cases Addresses Reddit feedback on Caddy import UX confusion
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import ImportReviewTable from '../ImportReviewTable'
|
||||
|
||||
describe('ImportReviewTable - Status Display', () => {
|
||||
const mockOnCommit = vi.fn()
|
||||
const mockOnCancel = vi.fn()
|
||||
|
||||
it('displays New badge for hosts without conflicts', () => {
|
||||
const hosts = [
|
||||
{
|
||||
domain_names: 'app.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 8080,
|
||||
},
|
||||
]
|
||||
|
||||
render(
|
||||
<ImportReviewTable
|
||||
hosts={hosts}
|
||||
conflicts={[]}
|
||||
errors={[]}
|
||||
onCommit={mockOnCommit}
|
||||
onCancel={mockOnCancel}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(screen.getByText('New')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('displays Conflict badge for hosts in conflicts array', () => {
|
||||
const hosts = [
|
||||
{
|
||||
domain_names: 'conflict.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
},
|
||||
]
|
||||
|
||||
render(
|
||||
<ImportReviewTable
|
||||
hosts={hosts}
|
||||
conflicts={['conflict.example.com']}
|
||||
errors={[]}
|
||||
onCommit={mockOnCommit}
|
||||
onCancel={mockOnCancel}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(screen.getByText('Conflict')).toBeInTheDocument()
|
||||
expect(screen.queryByText('New')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows expand button only for hosts with conflicts', () => {
|
||||
const hosts = [
|
||||
{
|
||||
domain_names: 'conflict.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
},
|
||||
{
|
||||
domain_names: 'new.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 8080,
|
||||
},
|
||||
]
|
||||
|
||||
const conflictDetails = {
|
||||
'conflict.example.com': {
|
||||
existing: {
|
||||
forward_scheme: 'http',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
ssl_forced: false,
|
||||
websocket: false,
|
||||
enabled: true,
|
||||
},
|
||||
imported: {
|
||||
forward_scheme: 'http',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 8080,
|
||||
ssl_forced: false,
|
||||
websocket: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
render(
|
||||
<ImportReviewTable
|
||||
hosts={hosts}
|
||||
conflicts={['conflict.example.com']}
|
||||
conflictDetails={conflictDetails}
|
||||
errors={[]}
|
||||
onCommit={mockOnCommit}
|
||||
onCancel={mockOnCancel}
|
||||
/>
|
||||
)
|
||||
|
||||
// Expand button shows as triangle character
|
||||
const expandButtons = screen.getAllByRole('button', { name: /▶/ })
|
||||
expect(expandButtons).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('expands to show conflict details when clicked', async () => {
|
||||
const hosts = [
|
||||
{
|
||||
domain_names: 'conflict.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
},
|
||||
]
|
||||
|
||||
const conflictDetails = {
|
||||
'conflict.example.com': {
|
||||
existing: {
|
||||
forward_scheme: 'http',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
ssl_forced: false,
|
||||
websocket: false,
|
||||
enabled: true,
|
||||
},
|
||||
imported: {
|
||||
forward_scheme: 'http',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 8080,
|
||||
ssl_forced: false,
|
||||
websocket: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
render(
|
||||
<ImportReviewTable
|
||||
hosts={hosts}
|
||||
conflicts={['conflict.example.com']}
|
||||
conflictDetails={conflictDetails}
|
||||
errors={[]}
|
||||
onCommit={mockOnCommit}
|
||||
onCancel={mockOnCancel}
|
||||
/>
|
||||
)
|
||||
|
||||
const expandButton = screen.getByRole('button', { name: /▶/ })
|
||||
fireEvent.click(expandButton)
|
||||
|
||||
expect(screen.getByText('Current Configuration')).toBeInTheDocument()
|
||||
expect(screen.getByText('Imported Configuration')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('collapses conflict details when clicked again', () => {
|
||||
const hosts = [
|
||||
{
|
||||
domain_names: 'conflict.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
},
|
||||
]
|
||||
|
||||
const conflictDetails = {
|
||||
'conflict.example.com': {
|
||||
existing: {
|
||||
forward_scheme: 'http',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
ssl_forced: false,
|
||||
websocket: false,
|
||||
enabled: true,
|
||||
},
|
||||
imported: {
|
||||
forward_scheme: 'http',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 8080,
|
||||
ssl_forced: false,
|
||||
websocket: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
render(
|
||||
<ImportReviewTable
|
||||
hosts={hosts}
|
||||
conflicts={['conflict.example.com']}
|
||||
conflictDetails={conflictDetails}
|
||||
errors={[]}
|
||||
onCommit={mockOnCommit}
|
||||
onCancel={mockOnCancel}
|
||||
/>
|
||||
)
|
||||
|
||||
const expandButton = screen.getByRole('button', { name: /▶/ })
|
||||
|
||||
// Expand
|
||||
fireEvent.click(expandButton)
|
||||
expect(screen.getByText('Current Configuration')).toBeInTheDocument()
|
||||
|
||||
// Collapse (now button shows ▼)
|
||||
const collapseButton = screen.getByRole('button', { name: /▼/ })
|
||||
fireEvent.click(collapseButton)
|
||||
expect(screen.queryByText('Current Configuration')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows conflict resolution dropdown for conflicting hosts', () => {
|
||||
const hosts = [
|
||||
{
|
||||
domain_names: 'conflict.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 80,
|
||||
},
|
||||
]
|
||||
|
||||
render(
|
||||
<ImportReviewTable
|
||||
hosts={hosts}
|
||||
conflicts={['conflict.example.com']}
|
||||
errors={[]}
|
||||
onCommit={mockOnCommit}
|
||||
onCancel={mockOnCancel}
|
||||
/>
|
||||
)
|
||||
|
||||
const select = screen.getByRole('combobox')
|
||||
expect(select).toBeInTheDocument()
|
||||
expect(screen.getByText('Keep Existing (Skip Import)')).toBeInTheDocument()
|
||||
expect(screen.getByText('Replace with Imported')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows "Will be imported" text for non-conflicting hosts', () => {
|
||||
const hosts = [
|
||||
{
|
||||
domain_names: 'new.example.com',
|
||||
forward_host: 'localhost',
|
||||
forward_port: 8080,
|
||||
},
|
||||
]
|
||||
|
||||
render(
|
||||
<ImportReviewTable
|
||||
hosts={hosts}
|
||||
conflicts={[]}
|
||||
errors={[]}
|
||||
onCommit={mockOnCommit}
|
||||
onCancel={mockOnCancel}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(screen.getByText('Will be imported')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
85
frontend/src/pages/__tests__/ImportCaddy-imports.test.tsx
Normal file
85
frontend/src/pages/__tests__/ImportCaddy-imports.test.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import ImportCaddy from '../ImportCaddy'
|
||||
|
||||
// Create a simple mock for useImport that returns the error state
|
||||
const mockUseImport = vi.fn()
|
||||
|
||||
// Mock the hooks
|
||||
vi.mock('../../hooks/useImport', () => ({
|
||||
useImport: () => mockUseImport(),
|
||||
}))
|
||||
|
||||
// Mock translation
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}))
|
||||
|
||||
const createWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: false },
|
||||
mutations: { retry: false },
|
||||
},
|
||||
})
|
||||
|
||||
return ({ children }: { children: React.ReactNode }) => (
|
||||
<BrowserRouter>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{children}
|
||||
</QueryClientProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
|
||||
describe('ImportCaddy - Import Detection Error Display', () => {
|
||||
it('displays error message when import directives detected', () => {
|
||||
// Mock the hook to return error state with imports
|
||||
mockUseImport.mockReturnValue({
|
||||
session: null,
|
||||
preview: null,
|
||||
loading: false,
|
||||
error: 'This Caddyfile contains import directives. Please use the multi-file import flow to upload all referenced files together.',
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Check main error message is displayed
|
||||
expect(screen.getByText(/this caddyfile contains import directives/i)).toBeInTheDocument()
|
||||
|
||||
// Check multi-site import button is available as alternative
|
||||
const multiSiteButton = screen.getByTestId('multi-file-import-button')
|
||||
expect(multiSiteButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('displays plain error when no imports detected', () => {
|
||||
// Mock the hook to return error without imports
|
||||
mockUseImport.mockReturnValue({
|
||||
session: null,
|
||||
preview: null,
|
||||
loading: false,
|
||||
error: 'no sites found in uploaded Caddyfile',
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Should show error message
|
||||
expect(screen.getByText('no sites found in uploaded Caddyfile')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,204 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import ImportCaddy from '../ImportCaddy'
|
||||
import { useImport } from '../../hooks/useImport'
|
||||
|
||||
// Mock the hooks and API calls
|
||||
vi.mock('../../hooks/useImport')
|
||||
vi.mock('../../api/backups', () => ({
|
||||
createBackup: vi.fn().mockResolvedValue({}),
|
||||
}))
|
||||
|
||||
const mockUseImport = vi.mocked(useImport)
|
||||
|
||||
describe('ImportCaddy - Multi-File Modal', () => {
|
||||
const defaultMockReturn = {
|
||||
session: null,
|
||||
preview: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockUseImport.mockReturnValue(defaultMockReturn)
|
||||
})
|
||||
|
||||
it('renders multi-file button when no session exists', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
const button = screen.getByTestId('multi-file-import-button')
|
||||
expect(button).toBeInTheDocument()
|
||||
expect(button).toHaveTextContent(/multi.*site.*import/i)
|
||||
})
|
||||
|
||||
it('shows import banner when session exists (multi-file hidden during active session)', () => {
|
||||
mockUseImport.mockReturnValueOnce({
|
||||
...defaultMockReturn,
|
||||
session: { id: 'test-session-id', state: 'reviewing', created_at: '', updated_at: '' },
|
||||
})
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
// When a session exists, the import banner is shown instead of the upload form
|
||||
expect(screen.getByTestId('import-banner')).toBeInTheDocument()
|
||||
// Multi-file button is part of upload form, which is hidden during active session
|
||||
expect(screen.queryByTestId('multi-file-import-button')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('opens modal when multi-file button is clicked', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
const button = screen.getByTestId('multi-file-import-button')
|
||||
await user.click(button)
|
||||
|
||||
await waitFor(() => {
|
||||
const modal = screen.getByTestId('multi-site-modal')
|
||||
expect(modal).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('modal has correct accessibility attributes', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
const button = screen.getByTestId('multi-file-import-button')
|
||||
await user.click(button)
|
||||
|
||||
await waitFor(() => {
|
||||
const modal = screen.getByRole('dialog')
|
||||
expect(modal).toBeInTheDocument()
|
||||
expect(modal).toHaveAttribute('aria-modal', 'true')
|
||||
expect(modal).toHaveAttribute('aria-labelledby', 'multi-site-modal-title')
|
||||
expect(modal).toHaveAttribute('data-testid', 'multi-site-modal')
|
||||
})
|
||||
})
|
||||
|
||||
it('modal contains correct title for screen readers', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
const button = screen.getByTestId('multi-file-import-button')
|
||||
await user.click(button)
|
||||
|
||||
await waitFor(() => {
|
||||
// Use heading role to specifically target the modal title, not the button
|
||||
const title = screen.getByRole('heading', { name: 'Multi-site Import' })
|
||||
expect(title).toBeInTheDocument()
|
||||
expect(title).toHaveAttribute('id', 'multi-site-modal-title')
|
||||
})
|
||||
})
|
||||
|
||||
it('closes modal when clicking outside overlay', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
// Open modal
|
||||
const button = screen.getByTestId('multi-file-import-button')
|
||||
await user.click(button)
|
||||
|
||||
// Wait for modal to appear
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Click overlay (the semi-transparent background)
|
||||
const overlay = screen.getByRole('dialog').querySelector('.bg-black\\/60')
|
||||
expect(overlay).toBeInTheDocument()
|
||||
|
||||
if (overlay) {
|
||||
await user.click(overlay)
|
||||
|
||||
// Modal should close
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('opens modal and shows it correctly', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
const button = screen.getByTestId('multi-file-import-button')
|
||||
await user.click(button)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Verify modal is displayed
|
||||
const modal = screen.getByRole('dialog')
|
||||
expect(modal).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('modal button text matches E2E test selector', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
// E2E test uses: page.getByRole('button', { name: /multi.*file|multi.*site/i })
|
||||
const button = screen.getByRole('button', { name: /multi.*file|multi.*site/i })
|
||||
expect(button).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('handles error state from import', async () => {
|
||||
mockUseImport.mockReturnValueOnce({
|
||||
...defaultMockReturn,
|
||||
error: 'Import directives detected',
|
||||
})
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ImportCaddy />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
// Error message should display
|
||||
expect(screen.getByText(/Import directives detected/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
188
frontend/src/pages/__tests__/ImportCaddy-warnings.test.tsx
Normal file
188
frontend/src/pages/__tests__/ImportCaddy-warnings.test.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import ImportCaddy from '../ImportCaddy'
|
||||
|
||||
// Create a simple mock for useImport that returns the preview state
|
||||
const mockUseImport = vi.fn()
|
||||
|
||||
// Mock the hooks
|
||||
vi.mock('../../hooks/useImport', () => ({
|
||||
useImport: () => mockUseImport(),
|
||||
}))
|
||||
|
||||
// Mock translation
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}))
|
||||
|
||||
const createWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: false },
|
||||
mutations: { retry: false },
|
||||
},
|
||||
})
|
||||
|
||||
return ({ children }: { children: React.ReactNode }) => (
|
||||
<BrowserRouter>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{children}
|
||||
</QueryClientProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
|
||||
describe('ImportCaddy - Warning Display', () => {
|
||||
it('displays empty file warning when session exists but no hosts found', () => {
|
||||
// Mock the hook to return session with empty hosts
|
||||
mockUseImport.mockReturnValue({
|
||||
session: { id: 'test-session', state: 'reviewing', created_at: '', updated_at: '' },
|
||||
preview: {
|
||||
session: { id: 'test-session', state: 'reviewing' },
|
||||
preview: {
|
||||
hosts: [],
|
||||
conflicts: [],
|
||||
errors: [],
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Check empty file warning is displayed
|
||||
expect(screen.getByText('importCaddy.noDomainsFound')).toBeInTheDocument()
|
||||
expect(screen.getByText('importCaddy.emptyFileWarning')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('displays import banner when session exists', () => {
|
||||
// Mock the hook to return session with hosts
|
||||
mockUseImport.mockReturnValue({
|
||||
session: { id: 'test-session', state: 'reviewing', created_at: '', updated_at: '' },
|
||||
preview: {
|
||||
session: { id: 'test-session', state: 'reviewing' },
|
||||
preview: {
|
||||
hosts: [{ domain_names: 'example.com' }],
|
||||
conflicts: [],
|
||||
errors: [],
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Check import banner is visible
|
||||
expect(screen.getByTestId('import-banner')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not display empty file warning when hosts exist', () => {
|
||||
// Mock the hook to return session with hosts
|
||||
mockUseImport.mockReturnValue({
|
||||
session: { id: 'test-session', state: 'reviewing', created_at: '', updated_at: '' },
|
||||
preview: {
|
||||
session: { id: 'test-session', state: 'reviewing' },
|
||||
preview: {
|
||||
hosts: [{ domain_names: 'example.com' }],
|
||||
conflicts: [],
|
||||
errors: [],
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Check empty file warning is NOT visible
|
||||
expect(screen.queryByText('importCaddy.noDomainsFound')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not display import banner when no session exists', () => {
|
||||
// Mock the hook to return null session
|
||||
mockUseImport.mockReturnValue({
|
||||
session: null,
|
||||
preview: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Check import banner is NOT visible
|
||||
expect(screen.queryByTestId('import-banner')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('displays error message when error exists', () => {
|
||||
// Mock the hook to return error state
|
||||
mockUseImport.mockReturnValue({
|
||||
session: null,
|
||||
preview: null,
|
||||
loading: false,
|
||||
error: 'Failed to parse Caddyfile',
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Check error message is displayed
|
||||
expect(screen.getByText('Failed to parse Caddyfile')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows upload form when no session exists', () => {
|
||||
// Mock the hook to return null session
|
||||
mockUseImport.mockReturnValue({
|
||||
session: null,
|
||||
preview: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
commitSuccess: false,
|
||||
commitResult: null,
|
||||
clearCommitResult: vi.fn(),
|
||||
upload: vi.fn(),
|
||||
commit: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
})
|
||||
|
||||
render(<ImportCaddy />, { wrapper: createWrapper() })
|
||||
|
||||
// Check upload form elements are visible
|
||||
expect(screen.getByTestId('import-dropzone')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('multi-file-import-button')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user