From 8a6a6712e7554f110b5ef951f270d88fd010e040 Mon Sep 17 00:00:00 2001 From: Bertrand Yuan Date: Thu, 26 Mar 2026 00:02:16 +0800 Subject: add more tests --- src/components/ui/alert.test.tsx | 30 +++++++++++++++ src/components/ui/button-variants.test.ts | 28 ++++++++++++++ src/components/ui/card.test.tsx | 40 ++++++++++++++++++++ src/components/ui/input.test.tsx | 22 +++++++++++ src/components/ui/label.test.tsx | 15 ++++++++ src/components/ui/pagination.test.tsx | 61 +++++++++++++++++++++++++++++++ src/components/ui/skeleton.test.tsx | 16 ++++++++ 7 files changed, 212 insertions(+) create mode 100644 src/components/ui/alert.test.tsx create mode 100644 src/components/ui/button-variants.test.ts create mode 100644 src/components/ui/card.test.tsx create mode 100644 src/components/ui/input.test.tsx create mode 100644 src/components/ui/label.test.tsx create mode 100644 src/components/ui/pagination.test.tsx create mode 100644 src/components/ui/skeleton.test.tsx (limited to 'src/components/ui') diff --git a/src/components/ui/alert.test.tsx b/src/components/ui/alert.test.tsx new file mode 100644 index 0000000..ded0356 --- /dev/null +++ b/src/components/ui/alert.test.tsx @@ -0,0 +1,30 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { Alert, AlertDescription, AlertTitle } from './alert'; + +describe('Alert', () => { + test('renders with role and slot attributes', () => { + render( + + Heads up + Something happened. + , + ); + + const alert = screen.getByRole('alert'); + expect(alert).toHaveAttribute('data-slot', 'alert'); + expect(screen.getByText('Heads up')).toHaveAttribute('data-slot', 'alert-title'); + expect(screen.getByText('Something happened.')).toHaveAttribute( + 'data-slot', + 'alert-description', + ); + }); + + test('applies destructive variant classes', () => { + render(Danger); + + const alert = screen.getByRole('alert'); + expect(alert.className).toContain('text-destructive'); + }); +}); + diff --git a/src/components/ui/button-variants.test.ts b/src/components/ui/button-variants.test.ts new file mode 100644 index 0000000..5090703 --- /dev/null +++ b/src/components/ui/button-variants.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from 'vitest'; +import { buttonVariants } from './button'; + +describe('buttonVariants', () => { + test('returns default variant and size classes when no args are provided', () => { + const classes = buttonVariants(); + + expect(classes).toContain('bg-primary'); + expect(classes).toContain('h-9'); + }); + + test('returns destructive and icon classes for explicit options', () => { + const classes = buttonVariants({ + variant: 'destructive', + size: 'icon', + }); + + expect(classes).toContain('bg-destructive'); + expect(classes).toContain('size-9'); + }); + + test('includes caller-provided className', () => { + const classes = buttonVariants({ className: 'w-full' }); + + expect(classes).toContain('w-full'); + }); +}); + diff --git a/src/components/ui/card.test.tsx b/src/components/ui/card.test.tsx new file mode 100644 index 0000000..aba3d00 --- /dev/null +++ b/src/components/ui/card.test.tsx @@ -0,0 +1,40 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { + Card, + CardAction, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from './card'; + +describe('Card', () => { + test('renders all card slots', () => { + render( + + + Card Title + Card Description + Action + + Content + Footer + , + ); + + expect(screen.getByText('Card Title')).toHaveAttribute( + 'data-slot', + 'card-title', + ); + expect(screen.getByText('Card Description')).toHaveAttribute( + 'data-slot', + 'card-description', + ); + expect(screen.getByText('Action')).toHaveAttribute('data-slot', 'card-action'); + expect(screen.getByText('Content')).toHaveAttribute('data-slot', 'card-content'); + expect(screen.getByText('Footer')).toHaveAttribute('data-slot', 'card-footer'); + }); +}); + diff --git a/src/components/ui/input.test.tsx b/src/components/ui/input.test.tsx new file mode 100644 index 0000000..aabe76f --- /dev/null +++ b/src/components/ui/input.test.tsx @@ -0,0 +1,22 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { Input } from './input'; + +describe('Input', () => { + test('renders an input with the expected slot attribute', () => { + render(); + + const input = screen.getByLabelText('Email'); + expect(input).toBeInTheDocument(); + expect(input).toHaveAttribute('data-slot', 'input'); + }); + + test('supports input type and disabled props', () => { + render(); + + const input = screen.getByLabelText('Password'); + expect(input).toHaveAttribute('type', 'password'); + expect(input).toBeDisabled(); + }); +}); + diff --git a/src/components/ui/label.test.tsx b/src/components/ui/label.test.tsx new file mode 100644 index 0000000..08031b6 --- /dev/null +++ b/src/components/ui/label.test.tsx @@ -0,0 +1,15 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { Label } from './label'; + +describe('Label', () => { + test('renders label text and exposes data-slot', () => { + render(); + + const label = screen.getByText('Email'); + expect(label).toBeInTheDocument(); + expect(label).toHaveAttribute('for', 'email'); + expect(label).toHaveAttribute('data-slot', 'label'); + }); +}); + diff --git a/src/components/ui/pagination.test.tsx b/src/components/ui/pagination.test.tsx new file mode 100644 index 0000000..77309b8 --- /dev/null +++ b/src/components/ui/pagination.test.tsx @@ -0,0 +1,61 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { + Pagination, + PaginationContent, + PaginationEllipsis, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from './pagination'; + +describe('Pagination UI', () => { + test('renders pagination container and list slots', () => { + render( + + + + 1 + + + , + ); + + const nav = screen.getByLabelText('pagination'); + expect(nav).toHaveAttribute('data-slot', 'pagination'); + expect(screen.getByRole('link', { name: '1' })).toHaveAttribute( + 'data-slot', + 'pagination-link', + ); + }); + + test('sets active page attributes', () => { + render( + + 2 + , + ); + + const link = screen.getByRole('link', { name: '2' }); + expect(link).toHaveAttribute('aria-current', 'page'); + expect(link).toHaveAttribute('data-active', 'true'); + }); + + test('renders previous, next, and ellipsis affordances', () => { + render( +
+ + + +
, + ); + + expect( + screen.getByRole('link', { name: 'Go to previous page' }), + ).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'Go to next page' })).toBeInTheDocument(); + expect(screen.getByText('More pages')).toBeInTheDocument(); + }); +}); + diff --git a/src/components/ui/skeleton.test.tsx b/src/components/ui/skeleton.test.tsx new file mode 100644 index 0000000..4459d77 --- /dev/null +++ b/src/components/ui/skeleton.test.tsx @@ -0,0 +1,16 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; +import { Skeleton } from './skeleton'; + +describe('Skeleton', () => { + test('renders with default and custom classes', () => { + render(); + + const skeleton = screen.getByTestId('skeleton'); + expect(skeleton).toHaveAttribute('data-slot', 'skeleton'); + expect(skeleton.className).toContain('animate-pulse'); + expect(skeleton.className).toContain('h-4'); + expect(skeleton.className).toContain('w-20'); + }); +}); + -- cgit v1.2.3