summaryrefslogtreecommitdiff
path: root/src/components/ui
diff options
context:
space:
mode:
authorBertrand Yuan <189593334+bertyuan@users.noreply.github.com>2026-03-26 00:19:31 +0800
committerGitHub <noreply@github.com>2026-03-26 00:19:31 +0800
commitf247a8c4a863ec430f4a705b5c493d652c8429bd (patch)
tree71d0985970984c105582f6e3c370b254f38e9bbe /src/components/ui
parentf7a02fe0e112cf108fc5f22872f1efc077e99fe8 (diff)
parentcd3c4bc89c169616b38bdb7443bb4eb7571b020c (diff)
Merge pull request #12 from bertyuan/fix-vitestv1.1
Fix vitest
Diffstat (limited to 'src/components/ui')
-rw-r--r--src/components/ui/alert.test.tsx30
-rw-r--r--src/components/ui/button-variants.test.ts28
-rw-r--r--src/components/ui/button.test.tsx15
-rw-r--r--src/components/ui/card.test.tsx40
-rw-r--r--src/components/ui/input.test.tsx22
-rw-r--r--src/components/ui/label.test.tsx15
-rw-r--r--src/components/ui/pagination.test.tsx61
-rw-r--r--src/components/ui/skeleton.test.tsx16
8 files changed, 223 insertions, 4 deletions
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(
+ <Alert>
+ <AlertTitle>Heads up</AlertTitle>
+ <AlertDescription>Something happened.</AlertDescription>
+ </Alert>,
+ );
+
+ 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(<Alert variant='destructive'>Danger</Alert>);
+
+ 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/button.test.tsx b/src/components/ui/button.test.tsx
index 314e9bf..f8bd3a9 100644
--- a/src/components/ui/button.test.tsx
+++ b/src/components/ui/button.test.tsx
@@ -14,9 +14,16 @@ describe('Button', () => {
// Test buttons with different variants
test('renders button with different variants', () => {
- const variants = ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'];
+ const variants = [
+ 'default',
+ 'destructive',
+ 'outline',
+ 'secondary',
+ 'ghost',
+ 'link',
+ ] as const;
variants.forEach((variant) => {
- render(<Button variant={variant as any}>{variant} Variant</Button>);
+ render(<Button variant={variant}>{variant} Variant</Button>);
const button = screen.getByText(`${variant} Variant`);
expect(button).toBeInTheDocument();
});
@@ -24,9 +31,9 @@ describe('Button', () => {
// Test buttons with different sizes
test('renders button with different sizes', () => {
- const sizes = ['default', 'sm', 'lg', 'icon'];
+ const sizes = ['default', 'sm', 'lg', 'icon'] as const;
sizes.forEach((size) => {
- render(<Button size={size as any}>{size} Size</Button>);
+ render(<Button size={size}>{size} Size</Button>);
const button = screen.getByText(`${size} Size`);
expect(button).toBeInTheDocument();
});
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>
+ <CardHeader>
+ <CardTitle>Card Title</CardTitle>
+ <CardDescription>Card Description</CardDescription>
+ <CardAction>Action</CardAction>
+ </CardHeader>
+ <CardContent>Content</CardContent>
+ <CardFooter>Footer</CardFooter>
+ </Card>,
+ );
+
+ 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(<Input aria-label='Email' />);
+
+ const input = screen.getByLabelText('Email');
+ expect(input).toBeInTheDocument();
+ expect(input).toHaveAttribute('data-slot', 'input');
+ });
+
+ test('supports input type and disabled props', () => {
+ render(<Input aria-label='Password' type='password' disabled />);
+
+ 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(<Label htmlFor='email'>Email</Label>);
+
+ 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(
+ <Pagination>
+ <PaginationContent>
+ <PaginationItem>
+ <PaginationLink href='#'>1</PaginationLink>
+ </PaginationItem>
+ </PaginationContent>
+ </Pagination>,
+ );
+
+ 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(
+ <PaginationLink href='#' isActive>
+ 2
+ </PaginationLink>,
+ );
+
+ 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(
+ <div>
+ <PaginationPrevious href='#' />
+ <PaginationNext href='#' />
+ <PaginationEllipsis />
+ </div>,
+ );
+
+ 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(<Skeleton data-testid='skeleton' className='h-4 w-20' />);
+
+ 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');
+ });
+});
+