diff --git a/package.json b/package.json
index 89d9182..68ee688 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,10 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@testing-library/user-event": "^14.6.1",
+ "@types/jsdom": "^28.0.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"eslint": "^10.2.1",
@@ -40,6 +44,7 @@
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.5.0",
"husky": "^9.1.7",
+ "jsdom": "^29.1.1",
"lint-staged": "^16.4.0",
"prettier": "^3.8.3",
"typescript-eslint": "^8.58.2",
diff --git a/src/components/ui/Button.test.tsx b/src/components/ui/Button.test.tsx
new file mode 100644
index 0000000..f536318
--- /dev/null
+++ b/src/components/ui/Button.test.tsx
@@ -0,0 +1,16 @@
+import { render, screen } from '@testing-library/react';
+import { Button } from './Button';
+import { describe, it, expect } from 'vitest';
+
+describe('Button component', () => {
+ it('renders children correctly', () => {
+ render();
+ expect(screen.getByText('Click Me')).toBeInTheDocument();
+ });
+
+ it('shows loading state when loading prop is true', () => {
+ render();
+ expect(screen.getByText('⏳')).toBeInTheDocument();
+ expect(screen.getByRole('button')).toBeDisabled();
+ });
+});
diff --git a/src/test/setup.ts b/src/test/setup.ts
new file mode 100644
index 0000000..7b0828b
--- /dev/null
+++ b/src/test/setup.ts
@@ -0,0 +1 @@
+import '@testing-library/jest-dom';
diff --git a/vite.config.ts b/vite.config.ts
index 0e77097..80a6bac 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,4 +1,4 @@
-import { defineConfig } from 'vite';
+import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import path from 'path';
@@ -14,4 +14,9 @@ export default defineConfig({
port: 5173,
open: true,
},
+ test: {
+ environment: 'jsdom',
+ setupFiles: ['./src/test/setup.ts'],
+ globals: true,
+ },
});