fix: better handling of deletes and links

This commit is contained in:
2026-02-13 10:19:43 +01:00
parent b61dfd7b61
commit f904f42f88
13 changed files with 728 additions and 45 deletions

View File

@@ -0,0 +1,264 @@
/**
* ConfirmDeleteModal Component Tests
*
* Tests the REAL ConfirmDeleteModal component with mocked callbacks.
* Following TDD best practices: verify component behavior.
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { ConfirmDeleteModal, type ConfirmDeleteDetails } from '../../../src/renderer/components/ConfirmDeleteModal';
describe('ConfirmDeleteModal', () => {
const mockOnClose = vi.fn();
const mockOnConfirm = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
describe('Rendering', () => {
it('should not render when details is null', () => {
const { container } = render(
<ConfirmDeleteModal details={null} onClose={mockOnClose} />
);
expect(container.firstChild).toBeNull();
});
it('should render modal when details is provided', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText('Confirm Deletion')).toBeInTheDocument();
expect(screen.getByText(/Test Post/)).toBeInTheDocument();
});
it('should display post deletion message for post type', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'My Blog Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText(/delete the post/)).toBeInTheDocument();
expect(screen.getByText('My Blog Post')).toBeInTheDocument();
});
it('should display media deletion message for media type', () => {
const details: ConfirmDeleteDetails = {
itemType: 'media',
itemTitle: 'image.jpg',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText(/delete the media file/)).toBeInTheDocument();
expect(screen.getByText('image.jpg')).toBeInTheDocument();
});
});
describe('References Warning', () => {
it('should not show warning when no references exist', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.queryByText(/Warning/)).not.toBeInTheDocument();
});
it('should show warning when references exist', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [
{ id: '1', title: 'Linked Post', type: 'link' },
],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText(/Warning/)).toBeInTheDocument();
expect(screen.getByText('Linked Post')).toBeInTheDocument();
});
it('should list all reference types correctly', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [
{ id: '1', title: 'Some Post', type: 'post' },
{ id: '2', title: 'Some Image', type: 'media' },
{ id: '3', title: 'Some Link', type: 'link' },
],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText('Some Post')).toBeInTheDocument();
expect(screen.getByText('Some Image')).toBeInTheDocument();
expect(screen.getByText('Some Link')).toBeInTheDocument();
});
it('should show correct warning note', () => {
const details: ConfirmDeleteDetails = {
itemType: 'media',
itemTitle: 'photo.jpg',
references: [
{ id: '1', title: 'Blog Post', type: 'post' },
],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText(/Deleting this media will remove all these references/)).toBeInTheDocument();
});
});
describe('User Actions', () => {
it('should call onClose when Cancel button is clicked', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
fireEvent.click(screen.getByText('Cancel'));
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('should call onClose when close button is clicked', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
fireEvent.click(screen.getByTitle('Close'));
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('should call onClose when backdrop is clicked', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
const backdrop = document.querySelector('.confirm-delete-modal-backdrop');
fireEvent.click(backdrop!);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('should not call onClose when modal content is clicked', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
const modal = document.querySelector('.confirm-delete-modal');
fireEvent.click(modal!);
expect(mockOnClose).not.toHaveBeenCalled();
});
it('should call onConfirm and onClose when Delete button is clicked', async () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
fireEvent.click(screen.getByText('Delete Post'));
// Wait for the async handler to complete
await vi.waitFor(() => {
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
});
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('should show correct delete button text for post', () => {
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText('Delete Post')).toBeInTheDocument();
});
it('should show correct delete button text for media', () => {
const details: ConfirmDeleteDetails = {
itemType: 'media',
itemTitle: 'image.jpg',
references: [],
onConfirm: mockOnConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
expect(screen.getByText('Delete Media')).toBeInTheDocument();
});
it('should handle async onConfirm correctly', async () => {
const asyncConfirm = vi.fn().mockResolvedValue(undefined);
const details: ConfirmDeleteDetails = {
itemType: 'post',
itemTitle: 'Test Post',
references: [],
onConfirm: asyncConfirm,
};
render(<ConfirmDeleteModal details={details} onClose={mockOnClose} />);
fireEvent.click(screen.getByText('Delete Post'));
// Wait for the async handler to complete
await vi.waitFor(() => {
expect(asyncConfirm).toHaveBeenCalledTimes(1);
});
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
});
});