diff --git a/backend/models/User.js b/backend/models/User.js
index 779294f..aeb0951 100644
--- a/backend/models/User.js
+++ b/backend/models/User.js
@@ -18,18 +18,12 @@ const UserSchema = new mongoose.Schema({
},
});
-UserSchema.pre('save', async function (next) {
-
+UserSchema.pre('save', async function () {
if (!this.isModified('password'))
- return next();
+ return;
- try {
- const salt = await bcrypt.genSalt(10);
- this.password = await bcrypt.hash(this.password, salt);
- next();
- } catch (err) {
- return next(err);
- }
+ const salt = await bcrypt.genSalt(10);
+ this.password = await bcrypt.hash(this.password, salt);
});
// Compare passwords during login
diff --git a/backend/package.json b/backend/package.json
index 9891c08..8c1035f 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -5,7 +5,8 @@
"scripts": {
"dev": "nodemon server.js",
"start": "node server.js",
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "jasmine spec/**/*.spec.cjs"
+
},
"keywords": [],
"author": "",
diff --git a/package.json b/package.json
index f2d89f5..3b7652d 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,8 @@
"dev": "vite --host",
"build": "vite build",
"lint": "eslint .",
+ "test": "vitest",
+ "test:backend": "jasmine spec/**/*.spec.cjs",
"preview": "vite preview",
"docker:dev": "docker compose --profile dev up --build",
"docker:prod": "docker compose --profile prod up -d --build"
@@ -32,6 +34,9 @@
},
"devDependencies": {
"@eslint/js": "^9.13.0",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@testing-library/user-event": "^14.6.1",
"@types/jasmine": "^5.1.8",
"@types/node": "^22.10.1",
"@types/react": "^18.3.23",
@@ -47,10 +52,13 @@
"eslint-plugin-react-refresh": "^0.4.14",
"express-session": "^1.18.2",
"globals": "^15.11.0",
- "jasmine": "^5.9.0",
+ "jasmine": "^5.13.0",
+ "jasmine-spec-reporter": "^7.0.0",
+ "jsdom": "^29.1.1",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
- "supertest": "^7.1.4",
- "vite": "^5.4.10"
+ "supertest": "^7.2.2",
+ "vite": "^5.4.10",
+ "vitest": "^4.1.6"
}
}
diff --git a/spec/auth.routes.spec.cjs b/spec/auth.routes.spec.cjs
index c5ac003..293c55e 100644
--- a/spec/auth.routes.spec.cjs
+++ b/spec/auth.routes.spec.cjs
@@ -22,10 +22,7 @@ describe('Auth Routes', () => {
let app;
beforeAll(async () => {
- await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test', {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- });
+ await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test');
app = createTestApp();
});
diff --git a/spec/user.model.spec.cjs b/spec/user.model.spec.cjs
index 236d9bd..79932bb 100644
--- a/spec/user.model.spec.cjs
+++ b/spec/user.model.spec.cjs
@@ -4,10 +4,7 @@ const User = require('../backend/models/User');
describe('User Model', () => {
beforeAll(async () => {
- await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test', {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- });
+ await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test');
});
afterAll(async () => {
diff --git a/src/components/__test__/Navbar.test.tsx b/src/components/__test__/Navbar.test.tsx
new file mode 100644
index 0000000..780b9bb
--- /dev/null
+++ b/src/components/__test__/Navbar.test.tsx
@@ -0,0 +1,91 @@
+// src/components/__tests__/Navbar.test.tsx
+import { render, screen, fireEvent } from '@testing-library/react'
+import { describe, it, expect, vi, beforeEach } from 'vitest'
+import { MemoryRouter } from 'react-router-dom'
+import { ThemeContext } from "../../context/ThemeContext";
+import Navbar from '../Navbar.tsx'
+
+// Helper to render Navbar with a mock ThemeContext
+const renderNavbar = (mode: 'light' | 'dark' = 'light') => {
+ const toggleTheme = vi.fn()
+ render(
+
+
+
+
+
+ )
+ return { toggleTheme }
+}
+
+describe('Navbar', () => {
+ // --- Rendering ---
+ it('renders the GitHub Tracker logo link', () => {
+ renderNavbar()
+ expect(screen.getByText('GitHub Tracker')).toBeInTheDocument()
+ })
+
+ it('renders all desktop nav links', () => {
+ renderNavbar()
+ expect(screen.getByRole('link', { name: /home/i })).toBeInTheDocument()
+ expect(screen.getByRole('link', { name: /^tracker$/i })).toBeInTheDocument()
+ expect(screen.getByRole('link', { name: /contributors/i })).toBeInTheDocument()
+ expect(screen.getByRole('link', { name: /login/i })).toBeInTheDocument()
+ })
+
+ // --- Theme toggle ---
+ it('shows Moon icon in light mode', () => {
+ renderNavbar('light')
+ // Lucide renders an