Skip to content

Commit c3ed50d

Browse files
MugemaneBertin2001teerenzo
authored andcommitted
User: this pr enables user to logout from one's session
1 parent 968b444 commit c3ed50d

18 files changed

+757
-247
lines changed

docker-compose.yml

+47-20
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
version: '3.1'
22

33
services:
4-
web:
5-
build:
6-
context: .
7-
dockerfile: Dockerfile
8-
container_name: eagle-ec-fe-container
9-
image: mugemanebertin/eagle-ec-fe
10-
ports:
11-
- "5173:5173"
12-
env_file:
13-
- .env
14-
4+
# Backend Service
155
backend:
166
image: mugemanebertin/eagle_ec_be:latest
17-
container_name: eagle-ec-be-container
7+
container_name: express-server-container
188
ports:
19-
- "499:499"
20-
command: sh -c "npm run migrate && (npm run seed || true) && npm run dev"
9+
- "${PORT}:${PORT}"
10+
volumes:
11+
- ./backend:/usr/src/app
12+
- /usr/src/app/node_modules
13+
command: sh -c "npm run migrate && npm run seed || true && npm run dev"
2114
depends_on:
22-
- db
15+
- postgres_db
2316
- redis
17+
env_file:
18+
- ./.env
2419
environment:
2520
- DB_CONNECTION=${DOCKER_DB_CONNECTION}
2621
- JWT_SECRET=${JWT_SECRET}
@@ -35,12 +30,14 @@ services:
3530
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
3631
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
3732
- GOOGLE_CALLBACK_URL=${GOOGLE_CALLBACK_URL}
38-
- REDIS_HOST=redis
39-
- REDIS_PORT=6379
33+
- FE_URL=${FE_URL}
34+
networks:
35+
- eagle-ec
4036

41-
db:
37+
# PostgreSQL Database Service
38+
postgres_db:
4239
image: postgres:latest
43-
container_name: eagle-ec-db-container
40+
container_name: postgres-db-container
4441
ports:
4542
- "5433:5432"
4643
environment:
@@ -49,11 +46,41 @@ services:
4946
- POSTGRES_DB=${POSTGRES_DB}
5047
volumes:
5148
- postgres_data:/var/lib/postgresql/data
49+
networks:
50+
- eagle-ec
5251

52+
# Redis Service
5353
redis:
5454
image: redis:latest
55+
container_name: redis-container
5556
ports:
5657
- "6379:6379"
58+
networks:
59+
- eagle-ec
60+
61+
# Web Frontend Service
62+
frontend:
63+
build:
64+
context: .
65+
dockerfile: Dockerfile
66+
container_name: eagle-ec-fe-container
67+
image: mugemanebertin/eagle-ec-fe
68+
ports:
69+
- "5173:5173"
70+
env_file:
71+
- ./.env
72+
volumes:
73+
- ./frontend:/app
74+
- /app/node_modules
75+
networks:
76+
- eagle-ec
77+
command: npm run dev -- --host
78+
depends_on:
79+
- backend
5780

5881
volumes:
59-
postgres_data:
82+
postgres_data:
83+
84+
networks:
85+
eagle-ec:
86+
driver: bridge

src/__test__/LogoutContext.test.tsx

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import "@testing-library/jest-dom";
2+
import React from "react";
3+
import {
4+
render, screen, fireEvent, waitFor,
5+
} from "@testing-library/react";
6+
7+
import {
8+
LogoutProvider,
9+
useLogout,
10+
} from "../components/dashboard/admin/LogoutContext";
11+
import api from "../redux/api/api";
12+
13+
// Mock the API and localStorage
14+
jest.mock("../redux/api/api");
15+
const mockPost = api.post as jest.MockedFunction<typeof api.post>;
16+
17+
// Mock LogoutModal component
18+
jest.mock("../components/dashboard/admin/LogoutModal", () => ({
19+
__esModule: true,
20+
default: ({ isOpen, onClose, onConfirm }: any) =>
21+
(isOpen ? (
22+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
23+
<div className="p-6 bg-white rounded shadow-lg">
24+
<h2 className="mb-4 text-xl">Confirm Logout</h2>
25+
<p className="mb-4">Are you sure you want to logout?</p>
26+
<div className="flex justify-end gap-4">
27+
<button
28+
className="px-4 py-2 text-white bg-gray-500 rounded"
29+
onClick={onClose}
30+
>
31+
Cancel
32+
</button>
33+
<button
34+
className="px-4 py-2 text-white bg-red-500 rounded"
35+
onClick={onConfirm}
36+
>
37+
Logout
38+
</button>
39+
</div>
40+
</div>
41+
</div>
42+
) : null),
43+
}));
44+
45+
const TestComponent: React.FC = () => {
46+
const { openLogoutModal } = useLogout();
47+
return <button onClick={openLogoutModal}>Open Logout Modal</button>;
48+
};
49+
50+
describe("LogoutProvider Component", () => {
51+
beforeAll(() => {
52+
jest.spyOn(console, "error").mockImplementation(() => {});
53+
});
54+
55+
afterAll(() => {
56+
(console.error as jest.Mock).mockRestore();
57+
});
58+
59+
it("should render LogoutProvider and trigger logout modal", () => {
60+
render(
61+
<LogoutProvider>
62+
<TestComponent />
63+
</LogoutProvider>,
64+
);
65+
66+
const openModalButton = screen.getByText("Open Logout Modal");
67+
fireEvent.click(openModalButton);
68+
69+
// Verify that the modal is rendered
70+
expect(screen.getByText("Confirm Logout")).toBeInTheDocument();
71+
expect(
72+
screen.getByText("Are you sure you want to logout?"),
73+
).toBeInTheDocument();
74+
expect(screen.getByText("Cancel")).toBeInTheDocument();
75+
expect(screen.getByText("Logout")).toBeInTheDocument();
76+
});
77+
78+
it("should call confirmLogout and perform logout", async () => {
79+
localStorage.setItem("accessToken", "mockToken");
80+
mockPost.mockResolvedValue({});
81+
82+
render(
83+
<LogoutProvider>
84+
<TestComponent />
85+
</LogoutProvider>,
86+
);
87+
88+
fireEvent.click(screen.getByText("Open Logout Modal"));
89+
fireEvent.click(screen.getByText("Logout"));
90+
91+
await waitFor(() => {
92+
expect(mockPost).toHaveBeenCalledWith(
93+
"/users/logout",
94+
{},
95+
{
96+
headers: {
97+
Authorization: `Bearer mockToken`,
98+
Accept: "*/*",
99+
},
100+
},
101+
);
102+
expect(localStorage.getItem("accessToken")).toBeNull();
103+
});
104+
});
105+
106+
it("should handle logout failure", async () => {
107+
localStorage.setItem("accessToken", "mockToken");
108+
mockPost.mockRejectedValue(new Error("Network error"));
109+
110+
render(
111+
<LogoutProvider>
112+
<TestComponent />
113+
</LogoutProvider>,
114+
);
115+
116+
fireEvent.click(screen.getByText("Open Logout Modal"));
117+
fireEvent.click(screen.getByText("Logout"));
118+
119+
await waitFor(() => {
120+
expect(console.error).toHaveBeenCalledWith(
121+
"Failed to logout:",
122+
expect.any(Error),
123+
);
124+
});
125+
});
126+
});

0 commit comments

Comments
 (0)