Add tests for AppearanceSettings, DangerZoneSettings, EditorSettings, GeneralSettings, GitSettings, and WorkspaceSettings components

This commit is contained in:
2025-07-03 21:58:14 +02:00
parent f4ec3af80c
commit 5c7edf40a8
7 changed files with 849 additions and 1 deletions

View File

@@ -0,0 +1,80 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render as rtlRender, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { MantineProvider } from '@mantine/core';
import AppearanceSettings from './AppearanceSettings';
import { Theme } from '@/types/models';
const mockUpdateColorScheme = vi.fn();
vi.mock('../../../contexts/ThemeContext', () => ({
useTheme: vi.fn(),
}));
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider defaultColorScheme="light">{children}</MantineProvider>
);
const render = (ui: React.ReactElement) => {
return rtlRender(ui, { wrapper: TestWrapper });
};
describe('AppearanceSettings', () => {
const mockOnThemeChange = vi.fn();
beforeEach(async () => {
vi.clearAllMocks();
const { useTheme } = await import('../../../contexts/ThemeContext');
vi.mocked(useTheme).mockReturnValue({
colorScheme: 'light',
updateColorScheme: mockUpdateColorScheme,
});
});
it('renders dark mode toggle with correct state', () => {
render(<AppearanceSettings onThemeChange={mockOnThemeChange} />);
expect(screen.getByText('Dark Mode')).toBeInTheDocument();
const toggle = screen.getByRole('switch');
expect(toggle).not.toBeChecked();
});
it('shows toggle as checked when in dark mode', async () => {
const { useTheme } = await import('../../../contexts/ThemeContext');
vi.mocked(useTheme).mockReturnValue({
colorScheme: 'dark',
updateColorScheme: mockUpdateColorScheme,
});
render(<AppearanceSettings onThemeChange={mockOnThemeChange} />);
const toggle = screen.getByRole('switch');
expect(toggle).toBeChecked();
});
it('toggles theme from light to dark', () => {
render(<AppearanceSettings onThemeChange={mockOnThemeChange} />);
const toggle = screen.getByRole('switch');
fireEvent.click(toggle);
expect(mockUpdateColorScheme).toHaveBeenCalledWith(Theme.Dark);
expect(mockOnThemeChange).toHaveBeenCalledWith(Theme.Dark);
});
it('toggles theme from dark to light', async () => {
const { useTheme } = await import('../../../contexts/ThemeContext');
vi.mocked(useTheme).mockReturnValue({
colorScheme: 'dark',
updateColorScheme: mockUpdateColorScheme,
});
render(<AppearanceSettings onThemeChange={mockOnThemeChange} />);
const toggle = screen.getByRole('switch');
fireEvent.click(toggle);
expect(mockUpdateColorScheme).toHaveBeenCalledWith(Theme.Light);
expect(mockOnThemeChange).toHaveBeenCalledWith(Theme.Light);
});
});

View File

@@ -0,0 +1,283 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
render as rtlRender,
screen,
fireEvent,
waitFor,
} from '@testing-library/react';
import React from 'react';
import { MantineProvider } from '@mantine/core';
import DangerZoneSettings from './DangerZoneSettings';
import { Theme } from '@/types/models';
const mockDeleteCurrentWorkspace = vi.fn();
vi.mock('../../../hooks/useWorkspace', () => ({
useWorkspace: vi.fn(),
}));
const mockSetSettingsModalVisible = vi.fn();
vi.mock('../../../contexts/ModalContext', () => ({
useModalContext: () => ({
setSettingsModalVisible: mockSetSettingsModalVisible,
}),
}));
vi.mock('../../modals/workspace/DeleteWorkspaceModal', () => ({
default: ({
opened,
onClose,
onConfirm,
workspaceName,
}: {
opened: boolean;
onClose: () => void;
onConfirm: () => Promise<void>;
workspaceName: string | undefined;
}) =>
opened ? (
<div data-testid="delete-workspace-modal">
<span data-testid="workspace-name">{workspaceName}</span>
<button onClick={onClose} data-testid="modal-close">
Close
</button>
<button onClick={() => void onConfirm()} data-testid="modal-confirm">
Confirm Delete
</button>
</div>
) : null,
}));
// Helper wrapper component for testing
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider defaultColorScheme="light">{children}</MantineProvider>
);
// Custom render function
const render = (ui: React.ReactElement) => {
return rtlRender(ui, { wrapper: TestWrapper });
};
describe('DangerZoneSettings (Workspace)', () => {
beforeEach(async () => {
vi.clearAllMocks();
mockDeleteCurrentWorkspace.mockResolvedValue(undefined);
const { useWorkspace } = await import('../../../hooks/useWorkspace');
vi.mocked(useWorkspace).mockReturnValue({
currentWorkspace: {
id: 1,
userId: 1,
name: 'Test Workspace',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
workspaces: [
{
id: 1,
userId: 1,
name: 'Workspace 1',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
{
id: 2,
userId: 1,
name: 'Workspace 2',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
],
settings: {
id: 1,
userId: 1,
name: 'Test Workspace',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
updateSettings: vi.fn(),
loading: false,
colorScheme: 'light',
updateColorScheme: vi.fn(),
switchWorkspace: vi.fn(),
deleteCurrentWorkspace: mockDeleteCurrentWorkspace,
});
});
it('renders delete button when multiple workspaces exist', () => {
render(<DangerZoneSettings />);
const deleteButton = screen.getByRole('button', {
name: 'Delete Workspace',
});
expect(deleteButton).toBeInTheDocument();
expect(deleteButton).not.toBeDisabled();
});
it('disables delete button when only one workspace exists', async () => {
const { useWorkspace } = await import('../../../hooks/useWorkspace');
vi.mocked(useWorkspace).mockReturnValue({
currentWorkspace: {
id: 1,
userId: 1,
name: 'Last Workspace',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
workspaces: [
{
id: 1,
userId: 1,
name: 'Last Workspace',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
],
settings: {
id: 1,
userId: 1,
name: 'Last Workspace',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
updateSettings: vi.fn(),
loading: false,
colorScheme: 'light',
updateColorScheme: vi.fn(),
switchWorkspace: vi.fn(),
deleteCurrentWorkspace: mockDeleteCurrentWorkspace,
});
render(<DangerZoneSettings />);
const deleteButton = screen.getByRole('button', {
name: 'Delete Workspace',
});
expect(deleteButton).toBeDisabled();
expect(deleteButton).toHaveAttribute(
'title',
'Cannot delete the last workspace'
);
});
it('opens and closes delete modal', () => {
render(<DangerZoneSettings />);
const deleteButton = screen.getByRole('button', {
name: 'Delete Workspace',
});
fireEvent.click(deleteButton);
expect(screen.getByTestId('delete-workspace-modal')).toBeInTheDocument();
expect(screen.getByTestId('workspace-name')).toHaveTextContent(
'Test Workspace'
);
fireEvent.click(screen.getByTestId('modal-close'));
expect(
screen.queryByTestId('delete-workspace-modal')
).not.toBeInTheDocument();
});
it('completes workspace deletion flow', async () => {
render(<DangerZoneSettings />);
fireEvent.click(screen.getByRole('button', { name: 'Delete Workspace' }));
fireEvent.click(screen.getByTestId('modal-confirm'));
await waitFor(() => {
expect(mockDeleteCurrentWorkspace).toHaveBeenCalled();
});
await waitFor(() => {
expect(mockSetSettingsModalVisible).toHaveBeenCalledWith(false);
});
expect(
screen.queryByTestId('delete-workspace-modal')
).not.toBeInTheDocument();
});
it('allows cancellation of deletion process', () => {
render(<DangerZoneSettings />);
fireEvent.click(screen.getByRole('button', { name: 'Delete Workspace' }));
fireEvent.click(screen.getByTestId('modal-close'));
expect(
screen.queryByTestId('delete-workspace-modal')
).not.toBeInTheDocument();
expect(mockDeleteCurrentWorkspace).not.toHaveBeenCalled();
expect(mockSetSettingsModalVisible).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,78 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render as rtlRender, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { MantineProvider } from '@mantine/core';
import EditorSettings from './EditorSettings';
// Helper wrapper component for testing
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider defaultColorScheme="light">{children}</MantineProvider>
);
// Custom render function
const render = (ui: React.ReactElement) => {
return rtlRender(ui, { wrapper: TestWrapper });
};
describe('EditorSettings', () => {
const mockOnAutoSaveChange = vi.fn();
const mockOnShowHiddenFilesChange = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('renders both toggle switches with labels', () => {
render(
<EditorSettings
autoSave={false}
showHiddenFiles={false}
onAutoSaveChange={mockOnAutoSaveChange}
onShowHiddenFilesChange={mockOnShowHiddenFilesChange}
/>
);
expect(screen.getByText('Auto Save')).toBeInTheDocument();
expect(screen.getByText('Show Hidden Files')).toBeInTheDocument();
});
it('shows correct toggle states', () => {
render(
<EditorSettings
autoSave={true}
showHiddenFiles={false}
onAutoSaveChange={mockOnAutoSaveChange}
onShowHiddenFilesChange={mockOnShowHiddenFilesChange}
/>
);
const toggles = screen.getAllByRole('switch');
const autoSaveToggle = toggles[0];
const hiddenFilesToggle = toggles[1];
expect(autoSaveToggle).toBeChecked();
expect(hiddenFilesToggle).not.toBeChecked();
});
it('calls onShowHiddenFilesChange when toggle is clicked', () => {
render(
<EditorSettings
autoSave={false}
showHiddenFiles={false}
onAutoSaveChange={mockOnAutoSaveChange}
onShowHiddenFilesChange={mockOnShowHiddenFilesChange}
/>
);
// Get the show hidden files toggle by finding the one that's not disabled
const toggles = screen.getAllByRole('switch');
const hiddenFilesToggle = toggles.find(
(toggle) => !toggle.hasAttribute('disabled')
);
expect(hiddenFilesToggle).toBeDefined();
fireEvent.click(hiddenFilesToggle!);
expect(mockOnShowHiddenFilesChange).toHaveBeenCalledWith(true);
});
});

View File

@@ -0,0 +1,61 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render as rtlRender, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { MantineProvider } from '@mantine/core';
import GeneralSettings from './GeneralSettings';
// Helper wrapper component for testing
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider defaultColorScheme="light">{children}</MantineProvider>
);
// Custom render function
const render = (ui: React.ReactElement) => {
return rtlRender(ui, { wrapper: TestWrapper });
};
describe('GeneralSettings', () => {
const mockOnInputChange = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('renders workspace name input with current value', () => {
render(
<GeneralSettings name="My Workspace" onInputChange={mockOnInputChange} />
);
const nameInput = screen.getByDisplayValue('My Workspace');
expect(nameInput).toBeInTheDocument();
expect(screen.getByText('Workspace Name')).toBeInTheDocument();
});
it('renders with empty name', () => {
render(<GeneralSettings name="" onInputChange={mockOnInputChange} />);
const nameInput = screen.getByPlaceholderText('Enter workspace name');
expect(nameInput).toHaveValue('');
});
it('calls onInputChange when name is modified', () => {
render(
<GeneralSettings name="Old Name" onInputChange={mockOnInputChange} />
);
const nameInput = screen.getByDisplayValue('Old Name');
fireEvent.change(nameInput, { target: { value: 'New Workspace Name' } });
expect(mockOnInputChange).toHaveBeenCalledWith(
'name',
'New Workspace Name'
);
});
it('has required attribute on input', () => {
render(<GeneralSettings name="Test" onInputChange={mockOnInputChange} />);
const nameInput = screen.getByDisplayValue('Test');
expect(nameInput).toHaveAttribute('required');
});
});

View File

@@ -0,0 +1,134 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render as rtlRender, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { MantineProvider } from '@mantine/core';
import GitSettings from './GitSettings';
// Helper wrapper component for testing
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider defaultColorScheme="light">{children}</MantineProvider>
);
// Custom render function
const render = (ui: React.ReactElement) => {
return rtlRender(ui, { wrapper: TestWrapper });
};
describe('GitSettings', () => {
const mockOnInputChange = vi.fn();
const defaultProps = {
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
onInputChange: mockOnInputChange,
};
beforeEach(() => {
vi.clearAllMocks();
});
it('renders all git settings fields', () => {
render(<GitSettings {...defaultProps} />);
expect(screen.getByText('Enable Git Repository')).toBeInTheDocument();
expect(screen.getByText('Git URL')).toBeInTheDocument();
expect(screen.getByText('Username')).toBeInTheDocument();
expect(screen.getByText('Access Token')).toBeInTheDocument();
expect(screen.getByText('Commit on Save')).toBeInTheDocument();
expect(screen.getByText('Commit Message Template')).toBeInTheDocument();
expect(screen.getByText('Commit Author')).toBeInTheDocument();
expect(screen.getByText('Commit Author Email')).toBeInTheDocument();
});
it('disables all inputs when git is not enabled', () => {
render(<GitSettings {...defaultProps} gitEnabled={false} />);
expect(screen.getByPlaceholderText('Enter Git URL')).toBeDisabled();
expect(screen.getByPlaceholderText('Enter Git username')).toBeDisabled();
expect(screen.getByPlaceholderText('Enter Git token')).toBeDisabled();
const switches = screen.getAllByRole('switch');
const commitOnSaveSwitch = switches[1]; // Second switch is commit on save
expect(commitOnSaveSwitch).toBeDisabled();
});
it('enables all inputs when git is enabled', () => {
render(<GitSettings {...defaultProps} gitEnabled={true} />);
expect(screen.getByPlaceholderText('Enter Git URL')).not.toBeDisabled();
expect(
screen.getByPlaceholderText('Enter Git username')
).not.toBeDisabled();
expect(screen.getByPlaceholderText('Enter Git token')).not.toBeDisabled();
const switches = screen.getAllByRole('switch');
const commitOnSaveSwitch = switches[1];
expect(commitOnSaveSwitch).not.toBeDisabled();
});
it('calls onInputChange when git enabled toggle is changed', () => {
render(<GitSettings {...defaultProps} />);
const switches = screen.getAllByRole('switch');
const gitEnabledSwitch = switches[0];
expect(gitEnabledSwitch).toBeDefined();
fireEvent.click(gitEnabledSwitch!);
expect(mockOnInputChange).toHaveBeenCalledWith('gitEnabled', true);
});
it('calls onInputChange when git URL is changed', () => {
render(<GitSettings {...defaultProps} gitEnabled={true} />);
const urlInput = screen.getByPlaceholderText('Enter Git URL');
fireEvent.change(urlInput, {
target: { value: 'https://github.com/user/repo.git' },
});
expect(mockOnInputChange).toHaveBeenCalledWith(
'gitUrl',
'https://github.com/user/repo.git'
);
});
it('calls onInputChange when commit template is changed', () => {
render(<GitSettings {...defaultProps} gitEnabled={true} />);
const templateInput = screen.getByPlaceholderText(
'Enter commit message template'
);
fireEvent.change(templateInput, {
target: { value: '${action}: ${filename}' },
});
expect(mockOnInputChange).toHaveBeenCalledWith(
'gitCommitMsgTemplate',
'${action}: ${filename}'
);
});
it('shows current values in form fields', () => {
const propsWithValues = {
...defaultProps,
gitEnabled: true,
gitUrl: 'https://github.com/test/repo.git',
gitUser: 'testuser',
gitCommitMsgTemplate: 'Update ${filename}',
};
render(<GitSettings {...propsWithValues} />);
expect(
screen.getByDisplayValue('https://github.com/test/repo.git')
).toBeInTheDocument();
expect(screen.getByDisplayValue('testuser')).toBeInTheDocument();
expect(screen.getByDisplayValue('Update ${filename}')).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,212 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
render as rtlRender,
screen,
fireEvent,
waitFor,
} from '@testing-library/react';
import React from 'react';
import { MantineProvider } from '@mantine/core';
import WorkspaceSettings from './WorkspaceSettings';
import { Theme } from '@/types/models';
const mockUpdateSettings = vi.fn();
vi.mock('../../../hooks/useWorkspace', () => ({
useWorkspace: vi.fn(),
}));
const mockSetSettingsModalVisible = vi.fn();
vi.mock('../../../contexts/ModalContext', () => ({
useModalContext: () => ({
settingsModalVisible: true,
setSettingsModalVisible: mockSetSettingsModalVisible,
}),
}));
vi.mock('@mantine/notifications', () => ({
notifications: {
show: vi.fn(),
},
}));
vi.mock('./GeneralSettings', () => ({
default: ({
name,
onInputChange,
}: {
name: string;
onInputChange: (key: string, value: string) => void;
}) => (
<div data-testid="general-settings">
<input
data-testid="workspace-name-input"
value={name}
onChange={(e) => onInputChange('name', e.target.value)}
/>
</div>
),
}));
vi.mock('./AppearanceSettings', () => ({
default: ({ onThemeChange }: { onThemeChange: (theme: string) => void }) => (
<div data-testid="appearance-settings">
<button onClick={() => onThemeChange('dark')} data-testid="theme-toggle">
Toggle Theme
</button>
</div>
),
}));
vi.mock('./EditorSettings', () => ({
default: () => <div data-testid="editor-settings">Editor Settings</div>,
}));
vi.mock('./GitSettings', () => ({
default: () => <div data-testid="git-settings">Git Settings</div>,
}));
vi.mock('./DangerZoneSettings', () => ({
default: () => <div data-testid="danger-zone-settings">Danger Zone</div>,
}));
// Helper wrapper component for testing
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider defaultColorScheme="light">{children}</MantineProvider>
);
// Custom render function
const render = (ui: React.ReactElement) => {
return rtlRender(ui, { wrapper: TestWrapper });
};
describe('WorkspaceSettings', () => {
beforeEach(async () => {
vi.clearAllMocks();
mockUpdateSettings.mockResolvedValue(undefined);
const { useWorkspace } = await import('../../../hooks/useWorkspace');
vi.mocked(useWorkspace).mockReturnValue({
currentWorkspace: {
name: 'Test Workspace',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
workspaces: [],
settings: {
id: 1,
userId: 1,
name: 'Test Workspace',
createdAt: '2024-01-01T00:00:00Z',
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '',
gitCommitName: '',
gitCommitEmail: '',
},
updateSettings: mockUpdateSettings,
loading: false,
colorScheme: 'light',
updateColorScheme: vi.fn(),
switchWorkspace: vi.fn(),
deleteCurrentWorkspace: vi.fn(),
});
});
it('renders modal with all setting sections', () => {
render(<WorkspaceSettings />);
expect(screen.getByText('Workspace Settings')).toBeInTheDocument();
expect(screen.getByTestId('general-settings')).toBeInTheDocument();
expect(screen.getByTestId('appearance-settings')).toBeInTheDocument();
expect(screen.getByTestId('editor-settings')).toBeInTheDocument();
expect(screen.getByTestId('git-settings')).toBeInTheDocument();
expect(screen.getByTestId('danger-zone-settings')).toBeInTheDocument();
});
it('shows unsaved changes badge when settings are modified', () => {
render(<WorkspaceSettings />);
const nameInput = screen.getByTestId('workspace-name-input');
fireEvent.change(nameInput, { target: { value: 'Updated Workspace' } });
expect(screen.getByText('Unsaved Changes')).toBeInTheDocument();
});
it('saves settings successfully', async () => {
render(<WorkspaceSettings />);
const nameInput = screen.getByTestId('workspace-name-input');
fireEvent.change(nameInput, { target: { value: 'Updated Workspace' } });
const saveButton = screen.getByRole('button', { name: 'Save Changes' });
expect(saveButton).toBeDefined();
fireEvent.click(saveButton);
await waitFor(() => {
expect(mockUpdateSettings).toHaveBeenCalledWith(
expect.objectContaining({ name: 'Updated Workspace' })
);
});
await waitFor(() => {
expect(mockSetSettingsModalVisible).toHaveBeenCalledWith(false);
});
});
it('handles theme changes', () => {
render(<WorkspaceSettings />);
const themeToggle = screen.getByTestId('theme-toggle');
fireEvent.click(themeToggle);
expect(screen.getByText('Unsaved Changes')).toBeInTheDocument();
});
it('closes modal when cancel is clicked', () => {
render(<WorkspaceSettings />);
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
fireEvent.click(cancelButton);
expect(mockSetSettingsModalVisible).toHaveBeenCalledWith(false);
});
it('prevents saving with empty workspace name', async () => {
const { notifications } = await import('@mantine/notifications');
render(<WorkspaceSettings />);
const nameInput = screen.getByTestId('workspace-name-input');
fireEvent.change(nameInput, { target: { value: ' ' } }); // Empty/whitespace
const saveButton = screen.getByRole('button', { name: 'Save Changes' });
fireEvent.click(saveButton);
await waitFor(() => {
expect(notifications.show).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Workspace name cannot be empty',
color: 'red',
})
);
});
expect(mockUpdateSettings).not.toHaveBeenCalled();
});
});

View File

@@ -235,7 +235,7 @@ const WorkspaceSettings: React.FC = () => {
<Button variant="default" onClick={handleClose}>
Cancel
</Button>
<Button onClick={() => void handleSubmit}>Save Changes</Button>
<Button onClick={() => void handleSubmit()}>Save Changes</Button>
</Group>
</Stack>
</Modal>