From 36c156df092bd3b3b13d4d2e305dca836046f208 Mon Sep 17 00:00:00 2001
From: Anvesh <127006149+Xploit-Ghost@users.noreply.github.com>
Date: Fri, 29 May 2026 23:03:32 +0530
Subject: [PATCH 1/5] Add test:client script to package.json
---
package.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/package.json b/package.json
index 43ad31cc..96087c28 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"build": "vite build",
"lint": "eslint .",
"test": "vitest",
+ "test:client": "vitest",
"test:backend": "jasmine spec/**/*.spec.cjs",
"preview": "vite preview",
"docker:dev": "docker compose --profile dev up --build",
From 38c16f973280e8572b9c20e0d976e3265ccad218 Mon Sep 17 00:00:00 2001
From: Anvesh <127006149+Xploit-Ghost@users.noreply.github.com>
Date: Fri, 29 May 2026 23:05:02 +0530
Subject: [PATCH 2/5] Add tests for Navbar component functionality
---
src/components/Navbar.test.tsx | 54 ++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
create mode 100644 src/components/Navbar.test.tsx
diff --git a/src/components/Navbar.test.tsx b/src/components/Navbar.test.tsx
new file mode 100644
index 00000000..573c6c87
--- /dev/null
+++ b/src/components/Navbar.test.tsx
@@ -0,0 +1,54 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
+import { vi } from 'vitest';
+import Navbar from './Navbar';
+import { ThemeContext } from '../context/ThemeContext';
+
+// Helper function to render components with required providers
+const renderWithProviders = (ui: React.ReactElement, themeMode: 'light' | 'dark' = 'light') => {
+ const mockToggleTheme = vi.fn();
+
+ return render(
+
+
+ {ui}
+
+
+ );
+};
+
+describe('Navbar Component', () => {
+ it('renders the brand name and logo', () => {
+ renderWithProviders();
+ expect(screen.getByText('GitHub Tracker')).toBeInTheDocument();
+ expect(screen.getByAltText('CRL Icon')).toBeInTheDocument();
+ });
+
+ it('renders desktop navigation links', () => {
+ renderWithProviders();
+ expect(screen.getByText('Home')).toBeInTheDocument();
+ expect(screen.getByText('Tracker')).toBeInTheDocument();
+ expect(screen.getByText('Contributors')).toBeInTheDocument();
+ expect(screen.getByText('Login')).toBeInTheDocument();
+ });
+
+ it('renders the correct theme toggle icon based on context', () => {
+ // Render with dark mode
+ renderWithProviders(, 'dark');
+ const toggleButtons = screen.getAllByLabelText('Toggle Theme');
+
+ // Check that the toggle buttons are present (one for desktop, one for mobile)
+ expect(toggleButtons.length).toBeGreaterThan(0);
+ });
+
+ it('opens mobile menu when hamburger icon is clicked', () => {
+ renderWithProviders();
+
+ const menuButton = screen.getByLabelText('Toggle Menu');
+ fireEvent.click(menuButton);
+
+ // The mobile menu should now be visible with the links
+ const mobileLinks = screen.getAllByText('Home');
+ expect(mobileLinks.length).toBe(2); // One desktop, one mobile
+ });
+});
From 0fca14027312b2f597d3373a219fa0caf6c43317 Mon Sep 17 00:00:00 2001
From: Anvesh <127006149+Xploit-Ghost@users.noreply.github.com>
Date: Fri, 29 May 2026 23:12:48 +0530
Subject: [PATCH 3/5] Update README with frontend testing instructions
Added instructions for running frontend tests using Vitest.
---
README.md | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a747b53a..eadd0129 100644
--- a/README.md
+++ b/README.md
@@ -50,13 +50,20 @@ $ git clone https://github.com/yourusername/github-tracker.git
```bash
$ cd github-tracker
```
-
3. Run the frontend
```bash
$ npm i
$ npm run dev
```
+This project utilizes [Vitest](https://vitest.dev/) and React Testing Library to ensure UI reliability.
+
+To run the frontend test suite, use the following command:
+```bash
+npm run test:client
+```
+
+
4. Run the backend
```bash
$ npm i
From 746ba66b8d2390918083d6ebf03a66e9ea9a864a Mon Sep 17 00:00:00 2001
From: Anvesh <127006149+Xploit-Ghost@users.noreply.github.com>
Date: Fri, 29 May 2026 23:13:56 +0530
Subject: [PATCH 4/5] Add tests for ActivityFeed component
---
src/components/ActivityFeed.test.tsx | 55 ++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 src/components/ActivityFeed.test.tsx
diff --git a/src/components/ActivityFeed.test.tsx b/src/components/ActivityFeed.test.tsx
new file mode 100644
index 00000000..68a4f378
--- /dev/null
+++ b/src/components/ActivityFeed.test.tsx
@@ -0,0 +1,55 @@
+import { render, screen, waitFor } from '@testing-library/react';
+import { vi } from 'vitest';
+import ActivityFeed from './ActivityFeed';
+
+global.fetch = vi.fn();
+
+const mockEvents = [
+ {
+ id: '12345',
+ type: 'PushEvent',
+ created_at: new Date().toISOString(),
+ repo: { name: 'GitMetricsLab/github_tracker' }
+ }
+];
+
+describe('ActivityFeed Component', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('displays the loading state initially', () => {
+ (global.fetch as any).mockResolvedValueOnce({ json: async () => [] });
+
+ render();
+
+ expect(screen.getByText('Loading...')).toBeInTheDocument();
+ });
+
+ it('renders activity events after successful fetch', async () => {
+ (global.fetch as any).mockResolvedValueOnce({
+ json: async () => mockEvents,
+ });
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
+ });
+
+ expect(screen.getByText('🚀 Commit pushed')).toBeInTheDocument();
+ expect(screen.getByText(/GitMetricsLab\/github_tracker/)).toBeInTheDocument();
+ });
+
+ it('displays a fallback message when no activity is found', async () => {
+ (global.fetch as any).mockResolvedValueOnce({
+ json: async () => [],
+ });
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByText('No activity found')).toBeInTheDocument();
+ });
+ });
+});
From a5ce1ec748bf4c72df7381849b66a933ace25b70 Mon Sep 17 00:00:00 2001
From: Anvesh <127006149+Xploit-Ghost@users.noreply.github.com>
Date: Fri, 29 May 2026 23:49:31 +0530
Subject: [PATCH 5/5] Improve fetch mocking in ActivityFeed tests
Refactor fetch mocking in ActivityFeed tests to use a helper function for creating mock responses and ensure original fetch is restored after tests.
---
src/components/ActivityFeed.test.tsx | 34 ++++++++++++++++++++--------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/src/components/ActivityFeed.test.tsx b/src/components/ActivityFeed.test.tsx
index 68a4f378..077e179d 100644
--- a/src/components/ActivityFeed.test.tsx
+++ b/src/components/ActivityFeed.test.tsx
@@ -2,7 +2,8 @@ import { render, screen, waitFor } from '@testing-library/react';
import { vi } from 'vitest';
import ActivityFeed from './ActivityFeed';
-global.fetch = vi.fn();
+// 1. Capture the original global fetch to prevent side effects
+const originalFetch = global.fetch;
const mockEvents = [
{
@@ -13,13 +14,32 @@ const mockEvents = [
}
];
+// Helper to generate a full Response-like object to satisfy TypeScript
+const createMockResponse = (data: any): Partial => ({
+ ok: true,
+ status: 200,
+ statusText: 'OK',
+ json: async () => data,
+});
+
describe('ActivityFeed Component', () => {
- beforeEach(() => {
+ beforeAll(() => {
+ // Mock fetch before the suite runs
+ global.fetch = vi.fn();
+ });
+
+ afterEach(() => {
+ // Clear mock history between individual tests
vi.clearAllMocks();
});
+ afterAll(() => {
+ // 2. Restore original fetch after the suite finishes to prevent leaks
+ global.fetch = originalFetch;
+ });
+
it('displays the loading state initially', () => {
- (global.fetch as any).mockResolvedValueOnce({ json: async () => [] });
+ vi.mocked(global.fetch).mockResolvedValueOnce(createMockResponse([]) as Response);
render();
@@ -27,9 +47,7 @@ describe('ActivityFeed Component', () => {
});
it('renders activity events after successful fetch', async () => {
- (global.fetch as any).mockResolvedValueOnce({
- json: async () => mockEvents,
- });
+ vi.mocked(global.fetch).mockResolvedValueOnce(createMockResponse(mockEvents) as Response);
render();
@@ -42,9 +60,7 @@ describe('ActivityFeed Component', () => {
});
it('displays a fallback message when no activity is found', async () => {
- (global.fetch as any).mockResolvedValueOnce({
- json: async () => [],
- });
+ vi.mocked(global.fetch).mockResolvedValueOnce(createMockResponse([]) as Response);
render();