import React from 'react'; import { describe, expect, test } from 'vitest'; import { render, screen, within, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { DataTableDemo } from '../src/demos/DataTableDemo'; import { DialogDemo } from '../src/demos/DialogDemo'; import { ConfirmDialogDemo } from '../src/demos/ConfirmDialogDemo'; import { SpinnerDemo } from '../src/demos/SpinnerDemo'; import { LoadingIndicatorDemo } from '../src/demos/LoadingIndicatorDemo'; import { TooltipDemo } from '../src/demos/TooltipDemo'; import { ProgressDemo } from '../src/demos/ProgressDemo'; import { SkeletonDemo } from '../src/demos/SkeletonDemo'; describe('DataTable demo', () => { test('table renders with column headers', () => { render(); expect(screen.getByText('Company')).toBeInTheDocument(); expect(screen.getByText('Plan')).toBeInTheDocument(); expect(screen.getByText('Status')).toBeInTheDocument(); }); test('clicking sortable header sorts data', async () => { const user = userEvent.setup(); render(); const rows = screen.getAllByRole('row'); const firstCell = rows[1].querySelectorAll('td')[0].textContent; await user.click(screen.getByRole('button', { name: /Company/ })); const updatedRows = screen.getAllByRole('row'); const updatedFirstCell = updatedRows[1].querySelectorAll('td')[0].textContent; expect(updatedFirstCell).not.toBe(firstCell); }); test('pagination controls are visible', () => { render(); expect(screen.getByText(/of .* items/)).toBeInTheDocument(); }); test('page size selector changes rows per page', async () => { const user = userEvent.setup(); render(); const initialRows = screen.getAllByRole('row').length; const selector = screen.getByRole('combobox'); await user.click(selector); await user.click(await screen.findByRole('option', { name: '10 per page' })); const updatedRows = screen.getAllByRole('row').length; expect(updatedRows).toBeGreaterThan(initialRows); }); test('responsive mode hides columns when narrow', async () => { render(); const table = screen.getByRole('table'); const container = table.parentElement?.parentElement as HTMLElement; Object.defineProperty(container, 'clientWidth', { value: 200, configurable: true }); window.dispatchEvent(new Event('resize')); await waitFor(() => { expect(screen.queryByText('MRR')).not.toBeInTheDocument(); }); }); test('custom cell render displays Badge', () => { render(); expect(screen.getAllByText('Active')[0].tagName).toBe('SPAN'); }); }); describe('Dialog demo', () => { test('dialog opens when trigger is clicked', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Open Dialog' })); expect(screen.getByRole('dialog')).toBeInTheDocument(); }); test('dialog closes when close button is clicked', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Open Dialog' })); await user.click(screen.getByRole('button', { name: 'Cancel' })); expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); }); test('dialog displays title in header', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Open Dialog' })); expect(screen.getByText('Invite teammates')).toBeInTheDocument(); }); }); describe('ConfirmDialog demo', () => { test('confirm button triggers onConfirm callback', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Open Confirm' })); await user.click(screen.getByRole('button', { name: 'Confirm' })); expect(screen.getByText(/Last action: Confirmed/)).toBeInTheDocument(); }); test('cancel button triggers onCancel callback', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Open Confirm' })); await user.click(screen.getByRole('button', { name: 'Cancel' })); expect(screen.getByText(/Last action: Canceled/)).toBeInTheDocument(); }); test('danger variant shows destructive confirm button', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Open Danger Confirm' })); const deleteButton = screen.getByRole('button', { name: 'Delete' }); expect(deleteButton.style.background).toContain('var(--alga-accent)'); }); }); describe('Spinner demo', () => { test('spinner animates', () => { render(); const spinner = screen.getAllByRole('status')[0] as HTMLElement; expect(spinner.style.animation).toContain('alga-spinner-spin'); }); test('different sizes render correctly', () => { render(); const spinners = screen.getAllByRole('status'); expect(spinners.length).toBeGreaterThan(3); }); }); describe('LoadingIndicator demo', () => { test('text is displayed alongside spinner', () => { render(); expect(screen.getByText('Syncing updates')).toBeInTheDocument(); }); }); describe('Tooltip demo', () => { test('tooltip appears on hover', async () => { const user = userEvent.setup(); render(); await user.hover(screen.getByRole('button', { name: 'Top' })); expect(await screen.findByText('Tooltip on top')).toBeInTheDocument(); }); test('tooltip positions correctly for each placement', async () => { const user = userEvent.setup(); render(); await user.hover(screen.getByRole('button', { name: 'Left' })); expect(await screen.findByText('Tooltip on left')).toBeInTheDocument(); await user.unhover(screen.getByRole('button', { name: 'Left' })); await user.hover(screen.getByRole('button', { name: 'Right' })); expect(await screen.findByText('Tooltip on right')).toBeInTheDocument(); }); }); describe('Progress demo', () => { test('progress bar fills to specified value', () => { render(); const progress = screen.getAllByRole('progressbar').find((el) => el.getAttribute('aria-valuenow') === '50'); expect(progress).toBeTruthy(); const track = progress?.querySelector('div') as HTMLElement | null; const bar = track?.querySelector('div') as HTMLElement | null; expect(bar?.style.width).toBe('50%'); }); test('striped variant shows stripe pattern', () => { render(); const striped = document.querySelector('[aria-hidden="true"] div') as HTMLElement | null; expect(striped?.style.backgroundImage).toContain('repeating-linear-gradient'); }); test('animated variant has moving animation', () => { render(); const progress = screen.getAllByRole('progressbar').find((el) => el.getAttribute('aria-valuenow') === '70'); const track = progress?.querySelector('div') as HTMLElement | null; const bar = track?.querySelector('div') as HTMLElement | null; expect(bar?.style.transition).toContain('width'); }); test('size variants change bar height', () => { render(); const sizeBars = screen.getAllByRole('progressbar').filter((el) => el.getAttribute('aria-valuenow') === '35'); const smTrack = sizeBars[0].querySelector('div') as HTMLElement; const lgTrack = sizeBars[2].querySelector('div') as HTMLElement; expect(Number(smTrack.style.height.replace('px', ''))).toBeLessThan(Number(lgTrack.style.height.replace('px', ''))); }); test('indeterminate mode shows continuous animation', () => { render(); const indeterminate = screen.getAllByRole('progressbar').find((el) => el.getAttribute('aria-valuenow') === '25'); const track = indeterminate?.querySelector('div') as HTMLElement | null; const bar = track?.querySelector('div') as HTMLElement | null; expect(bar?.style.animation).toContain('progress-indeterminate'); }); }); describe('Skeleton demo', () => { test('basic skeleton renders with pulse animation', () => { render(); const label = screen.getByText('Basic'); const skeleton = label.nextElementSibling?.querySelector('span') as HTMLElement; expect(skeleton?.style.animation).toContain('skeleton-pulse'); }); test('SkeletonText renders multiple lines', () => { render(); const container = screen.getByText('Text Lines').parentElement as HTMLElement; const lines = container.querySelectorAll('span'); expect(lines.length).toBeGreaterThan(1); }); test('SkeletonCircle renders as circle shape', () => { render(); const label = screen.getByText('Circle'); const circle = label.nextElementSibling?.querySelector('span') as HTMLElement; expect(circle?.style.borderRadius).toBe('50%'); }); test('SkeletonRectangle renders with specified dimensions', () => { render(); const label = screen.getByText('Rectangle'); const rect = label.nextElementSibling?.querySelector('span') as HTMLElement; expect(rect?.style.height).toBe('120px'); }); });