import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { DraggableEvent } from '../DraggableEvent';
// Mock DnD Kit
vi.mock('@dnd-kit/core', () => ({
DndContext: ({ children }: { children: React.ReactNode }) =>
{children}
,
useDraggable: vi.fn(() => ({
attributes: {},
listeners: {},
setNodeRef: vi.fn(),
transform: null,
isDragging: false,
})),
}));
vi.mock('@dnd-kit/utilities', () => ({
CSS: {
Translate: {
toString: (transform: any) => transform ? `translate3d(${transform.x}px, ${transform.y}px, 0)` : undefined,
},
},
}));
describe('DraggableEvent', () => {
const defaultProps = {
id: 1,
title: 'Test Event',
serviceName: 'Test Service',
status: 'CONFIRMED' as const,
isPaid: false,
start: new Date('2024-01-01T10:00:00'),
end: new Date('2024-01-01T11:00:00'),
laneIndex: 0,
height: 80,
left: 100,
width: 200,
top: 10,
onResizeStart: vi.fn(),
};
it('renders the event title', () => {
render();
expect(screen.getByText('Test Event')).toBeInTheDocument();
});
it('renders the service name when provided', () => {
render();
expect(screen.getByText('Test Service')).toBeInTheDocument();
});
it('does not render service name when not provided', () => {
render();
expect(screen.queryByText('Test Service')).not.toBeInTheDocument();
});
it('displays the start time formatted correctly', () => {
render();
expect(screen.getByText('10:00 AM')).toBeInTheDocument();
});
it('applies correct position styles', () => {
const { container } = render();
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveStyle({
left: '100px',
width: '200px',
top: '10px',
height: '80px',
});
});
it('applies confirmed status border color', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass('border-blue-500');
});
it('applies completed status border color', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass('border-green-500');
});
it('applies cancelled status border color', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass('border-red-500');
});
it('applies no-show status border color', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass('border-gray-500');
});
it('applies green border when paid', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass('border-green-500');
});
it('applies default brand border color for scheduled status', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass('border-brand-500');
});
it('calls onResizeStart when top resize handle is clicked', () => {
const onResizeStart = vi.fn();
const { container } = render(
);
const topHandle = container.querySelector('.cursor-ns-resize');
if (topHandle) {
fireEvent.mouseDown(topHandle);
expect(onResizeStart).toHaveBeenCalledWith(
expect.any(Object),
'left',
1
);
}
});
it('calls onResizeStart when bottom resize handle is clicked', () => {
const onResizeStart = vi.fn();
const { container } = render(
);
const handles = container.querySelectorAll('.cursor-ns-resize');
const bottomHandle = handles[handles.length - 1]; // Get the last one (bottom)
if (bottomHandle) {
fireEvent.mouseDown(bottomHandle);
expect(onResizeStart).toHaveBeenCalledWith(
expect.any(Object),
'right',
1
);
}
});
it('renders grip icon', () => {
const { container } = render();
const gripIcon = container.querySelector('svg');
expect(gripIcon).toBeInTheDocument();
});
it('applies hover styles', () => {
const { container } = render();
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass('group', 'hover:shadow-md');
});
it('renders with correct base styling classes', () => {
const { container } = render();
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveClass(
'absolute',
'rounded-b',
'overflow-hidden',
'group',
'bg-brand-100'
);
});
it('has two resize handles', () => {
const { container } = render();
const handles = container.querySelectorAll('.cursor-ns-resize');
expect(handles).toHaveLength(2);
});
it('stops propagation when resize handle is clicked', () => {
const onResizeStart = vi.fn();
const { container } = render(
);
const topHandle = container.querySelector('.cursor-ns-resize');
const mockEvent = {
stopPropagation: vi.fn(),
} as any;
if (topHandle) {
fireEvent.mouseDown(topHandle, mockEvent);
// The event handler should call stopPropagation to prevent drag
expect(onResizeStart).toHaveBeenCalled();
}
});
it('renders content area with cursor-move', () => {
const { container } = render();
const contentArea = container.querySelector('.cursor-move');
expect(contentArea).toBeInTheDocument();
expect(contentArea).toHaveClass('select-none');
});
it('applies different heights correctly', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveStyle({ height: '100px' });
});
it('applies different widths correctly', () => {
const { container } = render(
);
const eventElement = container.querySelector('.absolute.rounded-b');
expect(eventElement).toHaveStyle({ width: '300px' });
});
});