Files
gridpilot.gg/apps/website/components/actions/ActionList.test.tsx
Marc Mintel fb1221701d
Some checks failed
Contract Testing / contract-tests (push) Failing after 6m7s
Contract Testing / contract-snapshot (push) Failing after 4m46s
add tests
2026-01-22 11:52:42 +01:00

247 lines
8.3 KiB
TypeScript

import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { ActionList } from './ActionList';
import { ActionItem } from '@/lib/queries/ActionsPageQuery';
describe('ActionList', () => {
const mockActions: ActionItem[] = [
{
id: 'action-1',
timestamp: '2024-01-15T10:30:00Z',
type: 'USER_UPDATE',
initiator: 'John Doe',
status: 'COMPLETED',
details: 'Updated profile settings',
},
{
id: 'action-2',
timestamp: '2024-01-15T11:45:00Z',
type: 'ONBOARDING',
initiator: 'Jane Smith',
status: 'PENDING',
details: 'Started onboarding process',
},
{
id: 'action-3',
timestamp: '2024-01-15T12:00:00Z',
type: 'USER_UPDATE',
initiator: 'Bob Johnson',
status: 'FAILED',
details: 'Failed to update email',
},
{
id: 'action-4',
timestamp: '2024-01-15T13:15:00Z',
type: 'ONBOARDING',
initiator: 'Alice Brown',
status: 'IN_PROGRESS',
details: 'Completing verification',
},
];
describe('Rendering states', () => {
it('renders table headers', () => {
render(<ActionList actions={mockActions} />);
expect(screen.getByText('Timestamp')).toBeDefined();
expect(screen.getByText('Type')).toBeDefined();
expect(screen.getByText('Initiator')).toBeDefined();
expect(screen.getByText('Status')).toBeDefined();
expect(screen.getByText('Details')).toBeDefined();
});
it('renders all action rows', () => {
render(<ActionList actions={mockActions} />);
mockActions.forEach((action) => {
expect(screen.getByText(action.timestamp)).toBeDefined();
expect(screen.getAllByText(action.type).length).toBeGreaterThan(0);
expect(screen.getByText(action.initiator)).toBeDefined();
expect(screen.getByText(action.details)).toBeDefined();
});
});
it('renders action status badges', () => {
render(<ActionList actions={mockActions} />);
// Check that status badges are rendered for each action
expect(screen.getByText('COMPLETED')).toBeDefined();
expect(screen.getByText('PENDING')).toBeDefined();
expect(screen.getByText('FAILED')).toBeDefined();
expect(screen.getByText('IN PROGRESS')).toBeDefined();
});
it('renders empty table when no actions provided', () => {
render(<ActionList actions={[]} />);
// Table headers should still be visible
expect(screen.getByText('Timestamp')).toBeDefined();
expect(screen.getByText('Type')).toBeDefined();
expect(screen.getByText('Initiator')).toBeDefined();
expect(screen.getByText('Status')).toBeDefined();
expect(screen.getByText('Details')).toBeDefined();
});
});
describe('Interaction behavior', () => {
it('renders clickable rows', () => {
render(<ActionList actions={mockActions} />);
// Check that rows have clickable attribute
const rows = screen.getAllByRole('row');
// Skip the header row
const dataRows = rows.slice(1);
dataRows.forEach((row) => {
expect(row).toBeDefined();
});
});
it('renders row with key based on action id', () => {
const { container } = render(<ActionList actions={mockActions} />);
// Verify that each row has a unique key
const rows = container.querySelectorAll('tbody tr');
expect(rows.length).toBe(mockActions.length);
mockActions.forEach((action, index) => {
const row = rows[index];
expect(row).toBeDefined();
});
});
});
describe('Visual presentation', () => {
it('renders table structure correctly', () => {
const { container } = render(<ActionList actions={mockActions} />);
// Verify table structure
const table = container.querySelector('table');
expect(table).toBeDefined();
const thead = container.querySelector('thead');
expect(thead).toBeDefined();
const tbody = container.querySelector('tbody');
expect(tbody).toBeDefined();
});
it('renders timestamp in monospace font', () => {
render(<ActionList actions={mockActions} />);
// The timestamp should be rendered with monospace font
const timestamp = screen.getByText('2024-01-15T10:30:00Z');
expect(timestamp).toBeDefined();
});
it('renders type with medium weight', () => {
render(<ActionList actions={mockActions} />);
// The type should be rendered with medium weight
const types = screen.getAllByText('USER_UPDATE');
expect(types.length).toBeGreaterThan(0);
});
it('renders initiator with low variant', () => {
render(<ActionList actions={mockActions} />);
// The initiator should be rendered with low variant
const initiator = screen.getByText('John Doe');
expect(initiator).toBeDefined();
});
it('renders details with low variant', () => {
render(<ActionList actions={mockActions} />);
// The details should be rendered with low variant
const details = screen.getByText('Updated profile settings');
expect(details).toBeDefined();
});
});
describe('Edge cases', () => {
it('handles single action', () => {
const singleAction = [mockActions[0]];
render(<ActionList actions={singleAction} />);
expect(screen.getByText(singleAction[0].timestamp)).toBeDefined();
expect(screen.getByText(singleAction[0].type)).toBeDefined();
expect(screen.getByText(singleAction[0].initiator)).toBeDefined();
expect(screen.getByText(singleAction[0].details)).toBeDefined();
});
it('handles actions with long details', () => {
const longDetailsAction: ActionItem = {
id: 'action-long',
timestamp: '2024-01-15T14:00:00Z',
type: 'USER_UPDATE',
initiator: 'Long Name User',
status: 'COMPLETED',
details: 'This is a very long details text that might wrap to multiple lines and should still be displayed correctly in the table',
};
render(<ActionList actions={[longDetailsAction]} />);
expect(screen.getByText(longDetailsAction.details)).toBeDefined();
});
it('handles actions with special characters in details', () => {
const specialDetailsAction: ActionItem = {
id: 'action-special',
timestamp: '2024-01-15T15:00:00Z',
type: 'USER_UPDATE',
initiator: 'Special User',
status: 'COMPLETED',
details: 'Updated settings & preferences (admin)',
};
render(<ActionList actions={[specialDetailsAction]} />);
expect(screen.getByText(specialDetailsAction.details)).toBeDefined();
});
it('handles actions with unicode characters', () => {
const unicodeAction: ActionItem = {
id: 'action-unicode',
timestamp: '2024-01-15T16:00:00Z',
type: 'USER_UPDATE',
initiator: 'Über User',
status: 'COMPLETED',
details: 'Updated profile with emoji 🚀',
};
render(<ActionList actions={[unicodeAction]} />);
expect(screen.getByText(unicodeAction.details)).toBeDefined();
});
});
describe('Status badge integration', () => {
it('renders ActionStatusBadge for each action', () => {
render(<ActionList actions={mockActions} />);
// Each action should have a status badge
const completedBadge = screen.getByText('COMPLETED');
const pendingBadge = screen.getByText('PENDING');
const failedBadge = screen.getByText('FAILED');
const inProgressBadge = screen.getByText('IN PROGRESS');
expect(completedBadge).toBeDefined();
expect(pendingBadge).toBeDefined();
expect(failedBadge).toBeDefined();
expect(inProgressBadge).toBeDefined();
});
it('renders correct badge variant for each status', () => {
render(<ActionList actions={mockActions} />);
// Verify that badges are rendered with correct variants
// This is verified by the ActionStatusBadge component tests
expect(screen.getByText('COMPLETED')).toBeDefined();
expect(screen.getByText('PENDING')).toBeDefined();
expect(screen.getByText('FAILED')).toBeDefined();
expect(screen.getByText('IN PROGRESS')).toBeDefined();
});
});
});