Refactor modals to dialogs and update related tests for consistency

This commit is contained in:
2025-07-31 19:03:43 +02:00
parent c038cabaf6
commit f94a150b07
8 changed files with 62 additions and 62 deletions

View File

@@ -1,9 +1,9 @@
import { useState } from "react"; import { useState } from "react";
import Header from "@/components/Header"; import Header from "@/components/Header";
import InstanceList from "@/components/InstanceList"; import InstanceList from "@/components/InstanceList";
import InstanceModal from "@/components/InstanceModal"; import InstanceDialog from "@/components/InstanceDialog";
import LoginDialog from "@/components/LoginDialog"; import LoginDialog from "@/components/LoginDialog";
import SystemInfoModal from "./components/SystemInfoModal"; import SystemInfoDialog from "./components/SystemInfoDialog";
import { type CreateInstanceOptions, type Instance } from "@/types/instance"; import { type CreateInstanceOptions, type Instance } from "@/types/instance";
import { useInstances } from "@/contexts/InstancesContext"; import { useInstances } from "@/contexts/InstancesContext";
import { useAuth } from "@/contexts/AuthContext"; import { useAuth } from "@/contexts/AuthContext";
@@ -68,14 +68,14 @@ function App() {
<InstanceList editInstance={handleEditInstance} /> <InstanceList editInstance={handleEditInstance} />
</main> </main>
<InstanceModal <InstanceDialog
open={isInstanceModalOpen} open={isInstanceModalOpen}
onOpenChange={setIsInstanceModalOpen} onOpenChange={setIsInstanceModalOpen}
onSave={handleSaveInstance} onSave={handleSaveInstance}
instance={editingInstance} instance={editingInstance}
/> />
<SystemInfoModal <SystemInfoDialog
open={isSystemInfoModalOpen} open={isSystemInfoModalOpen}
onOpenChange={setIsSystemInfoModalOpen} onOpenChange={setIsSystemInfoModalOpen}
/> />

View File

@@ -75,7 +75,7 @@ describe('App Component - Critical Business Logic Only', () => {
const nameInput = screen.getByLabelText(/Instance Name/) const nameInput = screen.getByLabelText(/Instance Name/)
await user.type(nameInput, 'new-test-instance') await user.type(nameInput, 'new-test-instance')
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
// Verify correct API call // Verify correct API call
await waitFor(() => { await waitFor(() => {
@@ -109,7 +109,7 @@ describe('App Component - Critical Business Logic Only', () => {
const editButtons = screen.getAllByTitle('Edit instance') const editButtons = screen.getAllByTitle('Edit instance')
await user.click(editButtons[0]) await user.click(editButtons[0])
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
// Verify correct API call with existing instance data // Verify correct API call with existing instance data
await waitFor(() => { await waitFor(() => {

View File

@@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import type { Instance } from "@/types/instance"; import type { Instance } from "@/types/instance";
import { Edit, FileText, Play, Square, Trash2 } from "lucide-react"; import { Edit, FileText, Play, Square, Trash2 } from "lucide-react";
import LogsModal from "@/components/LogModal"; import LogsDialog from "@/components/LogDialog";
import HealthBadge from "@/components/HealthBadge"; import HealthBadge from "@/components/HealthBadge";
import { useState } from "react"; import { useState } from "react";
import { useInstanceHealth } from "@/hooks/useInstanceHealth"; import { useInstanceHealth } from "@/hooks/useInstanceHealth";
@@ -118,7 +118,7 @@ function InstanceCard({
</CardContent> </CardContent>
</Card> </Card>
<LogsModal <LogsDialog
open={isLogsOpen} open={isLogsOpen}
onOpenChange={setIsLogsOpen} onOpenChange={setIsLogsOpen}
instanceName={instance.name} instanceName={instance.name}

View File

@@ -15,14 +15,14 @@ import { getBasicFields, getAdvancedFields } from "@/lib/zodFormUtils";
import { ChevronDown, ChevronRight } from "lucide-react"; import { ChevronDown, ChevronRight } from "lucide-react";
import ZodFormField from "@/components/ZodFormField"; import ZodFormField from "@/components/ZodFormField";
interface InstanceModalProps { interface InstanceDialogProps {
open: boolean; open: boolean;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
onSave: (name: string, options: CreateInstanceOptions) => void; onSave: (name: string, options: CreateInstanceOptions) => void;
instance?: Instance; // For editing existing instance instance?: Instance; // For editing existing instance
} }
const InstanceModal: React.FC<InstanceModalProps> = ({ const InstanceDialog: React.FC<InstanceDialogProps> = ({
open, open,
onOpenChange, onOpenChange,
onSave, onSave,
@@ -40,7 +40,7 @@ const InstanceModal: React.FC<InstanceModalProps> = ({
const basicFields = getBasicFields(); const basicFields = getBasicFields();
const advancedFields = getAdvancedFields(); const advancedFields = getAdvancedFields();
// Reset form when modal opens/closes or when instance changes // Reset form when dialog opens/closes or when instance changes
useEffect(() => { useEffect(() => {
if (open) { if (open) {
if (instance) { if (instance) {
@@ -255,14 +255,14 @@ const InstanceModal: React.FC<InstanceModalProps> = ({
<Button <Button
variant="outline" variant="outline"
onClick={handleCancel} onClick={handleCancel}
data-testid="modal-cancel-button" data-testid="dialog-cancel-button"
> >
Cancel Cancel
</Button> </Button>
<Button <Button
onClick={handleSave} onClick={handleSave}
disabled={!instanceName.trim() || !!nameError} disabled={!instanceName.trim() || !!nameError}
data-testid="modal-save-button" data-testid="dialog-save-button"
> >
{isEditing {isEditing
? isRunning ? isRunning
@@ -276,4 +276,4 @@ const InstanceModal: React.FC<InstanceModalProps> = ({
); );
}; };
export default InstanceModal; export default InstanceDialog;

View File

@@ -21,14 +21,14 @@ import {
Settings Settings
} from 'lucide-react' } from 'lucide-react'
interface LogsModalProps { interface LogsDialogProps {
open: boolean open: boolean
onOpenChange: (open: boolean) => void onOpenChange: (open: boolean) => void
instanceName: string instanceName: string
isRunning: boolean isRunning: boolean
} }
const LogsModal: React.FC<LogsModalProps> = ({ const LogsDialog: React.FC<LogsDialogProps> = ({
open, open,
onOpenChange, onOpenChange,
instanceName, instanceName,
@@ -76,7 +76,7 @@ const LogsModal: React.FC<LogsModalProps> = ({
} }
} }
// Initial load when modal opens // Initial load when dialog opens
useEffect(() => { useEffect(() => {
if (open && instanceName) { if (open && instanceName) {
fetchLogs(lineCount) fetchLogs(lineCount)
@@ -327,4 +327,4 @@ const LogsModal: React.FC<LogsModalProps> = ({
) )
} }
export default LogsModal export default LogsDialog

View File

@@ -30,7 +30,7 @@ interface SystemInfo {
help: string help: string
} }
const SystemInfoModal: React.FC<SystemInfoModalProps> = ({ const SystemInfoDialog: React.FC<SystemInfoModalProps> = ({
open, open,
onOpenChange onOpenChange
}) => { }) => {
@@ -59,7 +59,7 @@ const SystemInfoModal: React.FC<SystemInfoModalProps> = ({
} }
} }
// Load data when modal opens // Load data when dialog opens
useEffect(() => { useEffect(() => {
if (open) { if (open) {
fetchSystemInfo() fetchSystemInfo()
@@ -180,4 +180,4 @@ const SystemInfoModal: React.FC<SystemInfoModalProps> = ({
) )
} }
export default SystemInfoModal export default SystemInfoDialog

View File

@@ -93,7 +93,7 @@ describe('InstanceCard - Instance Actions and State', () => {
expect(mockEditInstance).toHaveBeenCalledWith(stoppedInstance) expect(mockEditInstance).toHaveBeenCalledWith(stoppedInstance)
}) })
it('opens logs modal when logs button clicked', async () => { it('opens logs dialog when logs button clicked', async () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
@@ -109,7 +109,7 @@ describe('InstanceCard - Instance Actions and State', () => {
const logsButton = screen.getByTitle('View logs') const logsButton = screen.getByTitle('View logs')
await user.click(logsButton) await user.click(logsButton)
// Should open logs modal (we can verify this by checking if modal title appears) // Should open logs dialog (we can verify this by checking if dialog title appears)
expect(screen.getByText(`Logs: ${stoppedInstance.name}`)).toBeInTheDocument() expect(screen.getByText(`Logs: ${stoppedInstance.name}`)).toBeInTheDocument()
}) })
}) })
@@ -272,19 +272,19 @@ describe('InstanceCard - Instance Actions and State', () => {
/> />
) )
// Open logs modal // Open logs dialog
await user.click(screen.getByTitle('View logs')) await user.click(screen.getByTitle('View logs'))
// Verify modal opened with correct instance data // Verify dialog opened with correct instance data
expect(screen.getByText('Logs: running-instance')).toBeInTheDocument() expect(screen.getByText('Logs: running-instance')).toBeInTheDocument()
// Close modal to test close functionality // Close dialog to test close functionality
const closeButtons = screen.getAllByText('Close') const closeButtons = screen.getAllByText('Close')
const modalCloseButton = closeButtons.find(button => const dialogCloseButton = closeButtons.find(button =>
button.closest('[data-slot="dialog-content"]') button.closest('[data-slot="dialog-content"]')
) )
expect(modalCloseButton).toBeTruthy() expect(dialogCloseButton).toBeTruthy()
await user.click(modalCloseButton!) await user.click(dialogCloseButton!)
// Modal should close // Modal should close
expect(screen.queryByText('Logs: running-instance')).not.toBeInTheDocument() expect(screen.queryByText('Logs: running-instance')).not.toBeInTheDocument()

View File

@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest' import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react' import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event' import userEvent from '@testing-library/user-event'
import InstanceModal from '@/components/InstanceModal' import InstanceDialog from '@/components/InstanceDialog'
import type { Instance } from '@/types/instance' import type { Instance } from '@/types/instance'
describe('InstanceModal - Form Logic and Validation', () => { describe('InstanceModal - Form Logic and Validation', () => {
@@ -17,7 +17,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -25,7 +25,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
) )
// Try to submit without name // Try to submit without name
const saveButton = screen.getByTestId('modal-save-button') const saveButton = screen.getByTestId('dialog-save-button')
expect(saveButton).toBeDisabled() expect(saveButton).toBeDisabled()
// Add name, button should be enabled // Add name, button should be enabled
@@ -41,7 +41,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -54,7 +54,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
await user.type(nameInput, 'test instance!') await user.type(nameInput, 'test instance!')
expect(screen.getByText(/can only contain letters, numbers, hyphens, and underscores/)).toBeInTheDocument() expect(screen.getByText(/can only contain letters, numbers, hyphens, and underscores/)).toBeInTheDocument()
expect(screen.getByTestId('modal-save-button')).toBeDisabled() expect(screen.getByTestId('dialog-save-button')).toBeDisabled()
// Clear and test valid name // Clear and test valid name
await user.clear(nameInput) await user.clear(nameInput)
@@ -62,7 +62,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
await waitFor(() => { await waitFor(() => {
expect(screen.queryByText(/can only contain letters, numbers, hyphens, and underscores/)).not.toBeInTheDocument() expect(screen.queryByText(/can only contain letters, numbers, hyphens, and underscores/)).not.toBeInTheDocument()
expect(screen.getByTestId('modal-save-button')).not.toBeDisabled() expect(screen.getByTestId('dialog-save-button')).not.toBeDisabled()
}) })
}) })
@@ -70,7 +70,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -81,16 +81,16 @@ describe('InstanceModal - Form Logic and Validation', () => {
await user.type(screen.getByLabelText(/Instance Name/), 'my-instance') await user.type(screen.getByLabelText(/Instance Name/), 'my-instance')
// Submit form // Submit form
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
expect(mockOnSave).toHaveBeenCalledWith('my-instance', { expect(mockOnSave).toHaveBeenCalledWith('my-instance', {
auto_restart: true, // Default value auto_restart: true, // Default value
}) })
}) })
it('form resets when modal reopens', async () => { it('form resets when dialog reopens', async () => {
const { rerender } = render( const { rerender } = render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -101,18 +101,18 @@ describe('InstanceModal - Form Logic and Validation', () => {
const nameInput = screen.getByLabelText(/Instance Name/) const nameInput = screen.getByLabelText(/Instance Name/)
await userEvent.setup().type(nameInput, 'temp-name') await userEvent.setup().type(nameInput, 'temp-name')
// Close modal // Close dialog
rerender( rerender(
<InstanceModal <InstanceDialog
open={false} open={false}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
/> />
) )
// Reopen modal // Reopen dialog
rerender( rerender(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -138,7 +138,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
it('pre-fills form with existing instance data', () => { it('pre-fills form with existing instance data', () => {
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -159,7 +159,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -168,7 +168,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
) )
// Submit without changes // Submit without changes
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
expect(mockOnSave).toHaveBeenCalledWith('existing-instance', { expect(mockOnSave).toHaveBeenCalledWith('existing-instance', {
model: 'test-model.gguf', model: 'test-model.gguf',
@@ -181,7 +181,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const runningInstance: Instance = { ...mockInstance, running: true } const runningInstance: Instance = { ...mockInstance, running: true }
const { rerender } = render( const { rerender } = render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -189,10 +189,10 @@ describe('InstanceModal - Form Logic and Validation', () => {
/> />
) )
expect(screen.getByTestId('modal-save-button')).toBeInTheDocument() expect(screen.getByTestId('dialog-save-button')).toBeInTheDocument()
rerender( rerender(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -207,7 +207,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
describe('Auto Restart Configuration', () => { describe('Auto Restart Configuration', () => {
it('shows restart options when auto restart is enabled', () => { it('shows restart options when auto restart is enabled', () => {
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -227,7 +227,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -247,7 +247,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -261,7 +261,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
await user.type(screen.getByLabelText(/Max Restarts/), '5') await user.type(screen.getByLabelText(/Max Restarts/), '5')
await user.type(screen.getByLabelText(/Restart Delay/), '10') await user.type(screen.getByLabelText(/Restart Delay/), '10')
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
expect(mockOnSave).toHaveBeenCalledWith('test-instance', { expect(mockOnSave).toHaveBeenCalledWith('test-instance', {
auto_restart: true, auto_restart: true,
@@ -276,7 +276,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -300,7 +300,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -310,7 +310,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
// Fill only required field // Fill only required field
await user.type(screen.getByLabelText(/Instance Name/), 'clean-instance') await user.type(screen.getByLabelText(/Instance Name/), 'clean-instance')
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
// Should only include non-empty values // Should only include non-empty values
expect(mockOnSave).toHaveBeenCalledWith('clean-instance', { expect(mockOnSave).toHaveBeenCalledWith('clean-instance', {
@@ -322,7 +322,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -335,7 +335,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const gpuLayersInput = screen.getByLabelText(/GPU Layers/) const gpuLayersInput = screen.getByLabelText(/GPU Layers/)
await user.type(gpuLayersInput, '15') await user.type(gpuLayersInput, '15')
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
expect(mockOnSave).toHaveBeenCalledWith('numeric-test', { expect(mockOnSave).toHaveBeenCalledWith('numeric-test', {
auto_restart: true, auto_restart: true,
@@ -349,14 +349,14 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
/> />
) )
await user.click(screen.getByTestId('modal-cancel-button')) await user.click(screen.getByTestId('dialog-cancel-button'))
expect(mockOnOpenChange).toHaveBeenCalledWith(false) expect(mockOnOpenChange).toHaveBeenCalledWith(false)
}) })
@@ -365,7 +365,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
const user = userEvent.setup() const user = userEvent.setup()
render( render(
<InstanceModal <InstanceDialog
open={true} open={true}
onOpenChange={mockOnOpenChange} onOpenChange={mockOnOpenChange}
onSave={mockOnSave} onSave={mockOnSave}
@@ -373,7 +373,7 @@ describe('InstanceModal - Form Logic and Validation', () => {
) )
await user.type(screen.getByLabelText(/Instance Name/), 'test') await user.type(screen.getByLabelText(/Instance Name/), 'test')
await user.click(screen.getByTestId('modal-save-button')) await user.click(screen.getByTestId('dialog-save-button'))
expect(mockOnSave).toHaveBeenCalled() expect(mockOnSave).toHaveBeenCalled()
expect(mockOnOpenChange).toHaveBeenCalledWith(false) expect(mockOnOpenChange).toHaveBeenCalledWith(false)