diff --git a/package.json b/package.json
index fccb0aebe9..ffc6372b55 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,10 @@
"build": "vite build",
"lint": "eslint",
"serve": "vite preview",
- "start": "vite"
+ "start": "vite",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:coverage": "vitest run --coverage"
},
"dependencies": {
"@coreui/chartjs": "^4.1.0",
@@ -39,6 +42,9 @@
"simplebar-react": "^3.3.2"
},
"devDependencies": {
+ "@testing-library/dom": "^10.0.0",
+ "@testing-library/jest-dom": "^6.4.2",
+ "@testing-library/react": "^16.0.0",
"@vitejs/plugin-react": "^5.1.2",
"autoprefixer": "^10.4.23",
"eslint": "^9.39.2",
@@ -47,9 +53,11 @@
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"globals": "^16.5.0",
+ "jsdom": "^24.0.0",
"postcss": "^8.5.6",
"prettier": "3.7.4",
"sass": "^1.97.0",
- "vite": "^7.3.0"
+ "vite": "^7.3.0",
+ "vitest": "^1.3.1"
}
}
diff --git a/src/components/AppFooter.test.jsx b/src/components/AppFooter.test.jsx
new file mode 100644
index 0000000000..c846a8c076
--- /dev/null
+++ b/src/components/AppFooter.test.jsx
@@ -0,0 +1,33 @@
+import { describe, it, expect } from 'vitest'
+import { render, screen } from '@testing-library/react'
+import AppFooter from './AppFooter'
+
+describe('AppFooter', () => {
+ it('renders without crashing', () => {
+ render()
+ expect(screen.getByText('CoreUI')).toBeInTheDocument()
+ })
+
+ it('displays the copyright text', () => {
+ render()
+ expect(screen.getByText(/2025 creativeLabs/)).toBeInTheDocument()
+ })
+
+ it('contains CoreUI link with correct href', () => {
+ render()
+ const coreUILink = screen.getByRole('link', { name: 'CoreUI' })
+ expect(coreUILink).toHaveAttribute('href', 'https://coreui.io')
+ expect(coreUILink).toHaveAttribute('target', '_blank')
+ })
+
+ it('contains CoreUI React Admin link', () => {
+ render()
+ const adminLink = screen.getByRole('link', { name: /CoreUI React Admin/ })
+ expect(adminLink).toHaveAttribute('href', 'https://coreui.io/react')
+ })
+
+ it('displays "Powered by" text', () => {
+ render()
+ expect(screen.getByText('Powered by')).toBeInTheDocument()
+ })
+})
diff --git a/src/store.test.js b/src/store.test.js
new file mode 100644
index 0000000000..116b3f22ce
--- /dev/null
+++ b/src/store.test.js
@@ -0,0 +1,38 @@
+import { describe, it, expect } from 'vitest'
+import store from './store'
+
+describe('Redux Store', () => {
+ it('has correct initial state', () => {
+ const state = store.getState()
+ expect(state).toEqual({
+ sidebarShow: true,
+ theme: 'light',
+ })
+ })
+
+ it('handles set action to update sidebarShow', () => {
+ store.dispatch({ type: 'set', sidebarShow: false })
+ const state = store.getState()
+ expect(state.sidebarShow).toBe(false)
+ })
+
+ it('handles set action to update theme', () => {
+ store.dispatch({ type: 'set', theme: 'dark' })
+ const state = store.getState()
+ expect(state.theme).toBe('dark')
+ })
+
+ it('handles set action with multiple properties', () => {
+ store.dispatch({ type: 'set', sidebarShow: true, theme: 'light' })
+ const state = store.getState()
+ expect(state.sidebarShow).toBe(true)
+ expect(state.theme).toBe('light')
+ })
+
+ it('returns current state for unknown action types', () => {
+ const stateBefore = store.getState()
+ store.dispatch({ type: 'UNKNOWN_ACTION' })
+ const stateAfter = store.getState()
+ expect(stateAfter).toEqual(stateBefore)
+ })
+})
diff --git a/src/test/setup.js b/src/test/setup.js
new file mode 100644
index 0000000000..c44951a680
--- /dev/null
+++ b/src/test/setup.js
@@ -0,0 +1 @@
+import '@testing-library/jest-dom'
diff --git a/vitest.config.mjs b/vitest.config.mjs
new file mode 100644
index 0000000000..ab0a8c7234
--- /dev/null
+++ b/vitest.config.mjs
@@ -0,0 +1,32 @@
+import { defineConfig } from 'vitest/config'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+ esbuild: {
+ loader: 'jsx',
+ include: /src\/.*\.jsx?$/,
+ exclude: [],
+ },
+ optimizeDeps: {
+ esbuildOptions: {
+ loader: {
+ '.js': 'jsx',
+ },
+ },
+ },
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ setupFiles: ['./src/test/setup.js'],
+ include: ['src/**/*.{test,spec}.{js,jsx}'],
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html'],
+ exclude: [
+ 'node_modules/',
+ 'src/test/',
+ ],
+ },
+ },
+})