diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index abd6775fb5..95572a6d81 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -104,6 +104,7 @@ jobs:
src/style/**
schema.graphql
package.json
+ package-lock.json
tsconfig.json
.gitignore
.eslintrc.json
diff --git a/package-lock.json b/package-lock.json
index 9c801aa1c0..7f115d04df 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -80,6 +80,7 @@
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.25.7",
"@babel/preset-typescript": "^7.26.0",
+ "@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^12.1.10",
@@ -4048,17 +4049,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/@jridgewell/source-map": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
- "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@@ -4610,6 +4600,7 @@
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz",
"integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==",
+ "dev": true,
"hasInstallScript": true,
"optional": true,
"dependencies": {
@@ -4648,6 +4639,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"android"
@@ -4667,6 +4659,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"darwin"
@@ -4686,6 +4679,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"darwin"
@@ -4705,6 +4699,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -4724,6 +4719,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -4743,6 +4739,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -4762,6 +4759,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -4781,6 +4779,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -4800,6 +4799,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -4819,6 +4819,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -4838,6 +4839,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"win32"
@@ -4857,6 +4859,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"optional": true,
"os": [
"win32"
@@ -4876,6 +4879,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"win32"
@@ -5891,7 +5895,7 @@
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
"dev": true,
- "peer": true,
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -5911,7 +5915,6 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
- "peer": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5928,7 +5931,6 @@
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
- "peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -5943,7 +5945,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
- "peer": true,
"engines": {
"node": ">=10"
},
@@ -5955,8 +5956,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true,
- "peer": true
+ "dev": true
},
"node_modules/@testing-library/jest-dom": {
"version": "6.6.3",
@@ -6041,8 +6041,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
- "dev": true,
- "peer": true
+ "dev": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -6125,18 +6124,6 @@
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
},
- "node_modules/@types/eslint": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.0.tgz",
- "integrity": "sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw==",
- "dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -9649,6 +9636,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+ "dev": true,
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
@@ -9722,8 +9710,7 @@
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
- "dev": true,
- "peer": true
+ "dev": true
},
"node_modules/dom-align": {
"version": "1.12.4",
@@ -9925,16 +9912,6 @@
"node": ">= 0.8"
}
},
- "node_modules/encoding": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
- "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "iconv-lite": "^0.6.2"
- }
- },
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -12180,19 +12157,6 @@
"cross-fetch": "4.0.0"
}
},
- "node_modules/iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/icss-utils": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
@@ -12260,7 +12224,7 @@
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.2.tgz",
"integrity": "sha512-1NU7hWZDkV7hJ4PJ9dur9gTNQ4ePNPN4k9/0YhwjzykTi/+3Q5pF93YU5QoVj8BuOnhLgaY8gs0U2pj4kSYVcw==",
- "devOptional": true
+ "dev": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
@@ -15482,7 +15446,6 @@
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
- "peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -15942,6 +15905,7 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "dev": true,
"optional": true
},
"node_modules/node-fetch": {
@@ -19013,7 +18977,7 @@
"version": "1.80.7",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.80.7.tgz",
"integrity": "sha512-MVWvN0u5meytrSjsU7AWsbhoXi1sc58zADXFllfZzbsBT1GHjjar6JwBINYPRrkx/zqnQ6uqbQuHgE95O+C+eQ==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
@@ -19033,7 +18997,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"readdirp": "^4.0.1"
},
@@ -19048,7 +19012,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
- "devOptional": true,
+ "dev": true,
"engines": {
"node": ">= 14.16.0"
},
@@ -19377,7 +19341,7 @@
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -19387,7 +19351,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "devOptional": true,
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -19815,32 +19779,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/terser": {
- "version": "5.32.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz",
- "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/terser/node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "optional": true,
- "peer": true
- },
"node_modules/test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
diff --git a/package.json b/package.json
index 8e528c2ceb..257949ead6 100644
--- a/package.json
+++ b/package.json
@@ -120,6 +120,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^12.1.10",
+ "@testing-library/dom": "^10.4.0",
"@types/inquirer": "^9.0.7",
"@types/jest": "^26.0.24",
"@types/js-cookie": "^3.0.6",
diff --git a/src/components/CheckIn/tagTemplate.ts b/src/components/CheckIn/tagTemplate.ts
index a10dbca083..611dff7b83 100644
--- a/src/components/CheckIn/tagTemplate.ts
+++ b/src/components/CheckIn/tagTemplate.ts
@@ -1,4 +1,5 @@
import { Template } from '@pdfme/common';
+import styles from '../../style/app.module.css';
export const tagTemplate: Template = {
schemas: [
@@ -6,15 +7,7 @@ export const tagTemplate: Template = {
{
name: 'name',
type: 'text',
- position: { x: 14.91, y: 27.03 },
- width: 58.55,
- height: 5.67,
- alignment: 'center',
- fontSize: 16,
- characterSpacing: 0,
- lineHeight: 1,
- fontName: 'Roboto',
- fontColor: '#08780b',
+ className: `${styles['tag-template-name']}`,
} ,
],
] as any,
diff --git a/src/components/EventManagement/EventAttendance/EventStatistics.tsx b/src/components/EventManagement/EventAttendance/EventStatistics.tsx
index 686ad049fa..9136f7c0b1 100644
--- a/src/components/EventManagement/EventAttendance/EventStatistics.tsx
+++ b/src/components/EventManagement/EventAttendance/EventStatistics.tsx
@@ -30,6 +30,7 @@ import type {
InterfaceEvent,
InterfaceRecurringEvent,
} from './InterfaceEvents';
+import styles from '../../../style/app.module.css';
ChartJS.register(
CategoryScale,
LinearScale,
@@ -376,34 +377,25 @@ export const AttendanceStatisticsModal: React.FC<
id="pdf-content"
>
{isEventRecurring ? (
@@ -455,23 +447,13 @@ export const AttendanceStatisticsModal: React.FC<
) : (
-
+
{statistics.totalMembers}
@@ -530,13 +512,7 @@ export const AttendanceStatisticsModal: React.FC<
}}
/>
diff --git a/src/components/EventStats/EventStats.test.tsx b/src/components/EventStats/EventStats.spec.tsx
similarity index 75%
rename from src/components/EventStats/EventStats.test.tsx
rename to src/components/EventStats/EventStats.spec.tsx
index e2496fa5af..058913c6e7 100644
--- a/src/components/EventStats/EventStats.test.tsx
+++ b/src/components/EventStats/EventStats.spec.tsx
@@ -4,13 +4,15 @@ import { MockedProvider } from '@apollo/react-testing';
import { EventStats } from './EventStats';
import { BrowserRouter } from 'react-router-dom';
import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';
+import { vi, describe, expect, it } from 'vitest';
-// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Vitest)
// These modules are used by the Feedback component
-jest.mock('@mui/x-charts/PieChart', () => ({
- pieArcLabelClasses: jest.fn(),
- PieChart: jest.fn().mockImplementation(() => <>Test>),
- pieArcClasses: jest.fn(),
+vi.mock('@mui/x-charts/PieChart', async () => ({
+ ...(await vi.importActual('@mui/x-charts/PieChart')),
+ pieArcLabelClasses: vi.fn(),
+ PieChart: vi.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: vi.fn(),
}));
const mockData = [
@@ -43,10 +45,10 @@ describe('Testing Event Stats', () => {
const props = {
eventId: 'eventStats123',
show: true,
- handleClose: jest.fn(),
+ handleClose: vi.fn(),
};
- test('The stats should be rendered properly', async () => {
+ it('The stats should be rendered properly', async () => {
const { queryByText } = render(
diff --git a/src/components/EventStats/EventStatsWrapper.test.tsx b/src/components/EventStats/EventStatsWrapper.spec.tsx
similarity index 73%
rename from src/components/EventStats/EventStatsWrapper.test.tsx
rename to src/components/EventStats/EventStatsWrapper.spec.tsx
index 0e64ac13cc..7a94aea5ef 100644
--- a/src/components/EventStats/EventStatsWrapper.test.tsx
+++ b/src/components/EventStats/EventStatsWrapper.spec.tsx
@@ -4,12 +4,15 @@ import { MockedProvider } from '@apollo/react-testing';
import { EventStatsWrapper } from './EventStatsWrapper';
import { BrowserRouter } from 'react-router-dom';
import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';
+import { vi, describe, expect, it } from 'vitest';
-// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
-jest.mock('@mui/x-charts/PieChart', () => ({
- pieArcLabelClasses: jest.fn(),
- PieChart: jest.fn().mockImplementation(() => <>Test>),
- pieArcClasses: jest.fn(),
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Vitest)
+// These modules are used by the Feedback component
+vi.mock('@mui/x-charts/PieChart', async () => ({
+ ...(await vi.importActual('@mui/x-charts/PieChart')),
+ pieArcLabelClasses: vi.fn(),
+ PieChart: vi.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: vi.fn(),
}));
const mockData = [
@@ -38,20 +41,12 @@ const mockData = [
},
];
-// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
-// These modules are used by the Feedback component
-jest.mock('@mui/x-charts/PieChart', () => ({
- pieArcLabelClasses: jest.fn(),
- PieChart: jest.fn().mockImplementation(() => <>Test>),
- pieArcClasses: jest.fn(),
-}));
-
describe('Testing Event Stats Wrapper', () => {
const props = {
eventId: 'eventStats123',
};
- test('The button to open and close the modal should work properly', async () => {
+ it('The button to open and close the modal should work properly', async () => {
const { queryByText, queryByRole } = render(
diff --git a/src/components/EventStats/Statistics/AverageRating.test.tsx b/src/components/EventStats/Statistics/AverageRating.spec.tsx
similarity index 91%
rename from src/components/EventStats/Statistics/AverageRating.test.tsx
rename to src/components/EventStats/Statistics/AverageRating.spec.tsx
index 01cf6461e3..c5a6b91925 100644
--- a/src/components/EventStats/Statistics/AverageRating.test.tsx
+++ b/src/components/EventStats/Statistics/AverageRating.spec.tsx
@@ -7,6 +7,7 @@ import { store } from 'state/store';
import { I18nextProvider } from 'react-i18next';
import i18nForTest from 'utils/i18nForTest';
import { ToastContainer } from 'react-toastify';
+import { describe, expect, it } from 'vitest';
const props = {
data: {
@@ -35,7 +36,7 @@ const props = {
};
describe('Testing Average Rating Card', () => {
- test('The component should be rendered and the Score should be shown', async () => {
+ it('The component should be rendered and the Score should be shown', async () => {
const { queryByText } = render(
diff --git a/src/components/EventStats/Statistics/Feedback.test.tsx b/src/components/EventStats/Statistics/Feedback.spec.tsx
similarity index 81%
rename from src/components/EventStats/Statistics/Feedback.test.tsx
rename to src/components/EventStats/Statistics/Feedback.spec.tsx
index 9abdee4c57..4fb020a70e 100644
--- a/src/components/EventStats/Statistics/Feedback.test.tsx
+++ b/src/components/EventStats/Statistics/Feedback.spec.tsx
@@ -7,12 +7,14 @@ import { store } from 'state/store';
import { I18nextProvider } from 'react-i18next';
import i18nForTest from 'utils/i18nForTest';
import { ToastContainer } from 'react-toastify';
+import { vi, describe, expect, it } from 'vitest';
-// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
-jest.mock('@mui/x-charts/PieChart', () => ({
- pieArcLabelClasses: jest.fn(),
- PieChart: jest.fn().mockImplementation(() => <>Test>),
- pieArcClasses: jest.fn(),
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Vitest)
+vi.mock('@mui/x-charts/PieChart', async () => ({
+ ...(await vi.importActual('@mui/x-charts/PieChart')),
+ pieArcLabelClasses: vi.fn(),
+ PieChart: vi.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: vi.fn(),
}));
const nonEmptyProps = {
@@ -52,7 +54,7 @@ const emptyProps = {
};
describe('Testing Feedback Statistics Card', () => {
- test('The component should be rendered and the feedback should be shown if present', async () => {
+ it('The component should be rendered and the feedback should be shown if present', async () => {
const { queryByText } = render(
@@ -79,7 +81,7 @@ describe('Testing Feedback Statistics Card', () => {
});
});
- test('The component should be rendered and message should be shown if no feedback is present', async () => {
+ it('The component should be rendered and message should be shown if no feedback is present', async () => {
const { queryByText } = render(
diff --git a/src/components/EventStats/Statistics/Review.test.tsx b/src/components/EventStats/Statistics/Review.spec.tsx
similarity index 89%
rename from src/components/EventStats/Statistics/Review.test.tsx
rename to src/components/EventStats/Statistics/Review.spec.tsx
index 9093444ab2..5777c32c27 100644
--- a/src/components/EventStats/Statistics/Review.test.tsx
+++ b/src/components/EventStats/Statistics/Review.spec.tsx
@@ -7,6 +7,7 @@ import { store } from 'state/store';
import { I18nextProvider } from 'react-i18next';
import i18nForTest from 'utils/i18nForTest';
import { ToastContainer } from 'react-toastify';
+import { describe, expect, it } from 'vitest';
const nonEmptyReviewProps = {
data: {
@@ -51,7 +52,7 @@ const emptyReviewProps = {
};
describe('Testing Review Statistics Card', () => {
- test('The component should be rendered and the reviews should be shown if present', async () => {
+ it('The component should be rendered and the reviews should be shown if present', async () => {
const { queryByText } = render(
@@ -72,7 +73,7 @@ describe('Testing Review Statistics Card', () => {
await waitFor(() => expect(queryByText('review2')).toBeInTheDocument());
});
- test('The component should be rendered and message should be shown if no review is present', async () => {
+ it('The component should be rendered and message should be shown if no review is present', async () => {
const { queryByText } = render(
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx b/src/components/UserPortal/ChatRoom/ChatRoom.spec.tsx
similarity index 98%
rename from src/components/UserPortal/ChatRoom/ChatRoom.test.tsx
rename to src/components/UserPortal/ChatRoom/ChatRoom.spec.tsx
index c808485132..fef1b67fa5 100644
--- a/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.spec.tsx
@@ -21,6 +21,15 @@ import {
import ChatRoom from './ChatRoom';
import { useLocalStorage } from 'utils/useLocalstorage';
import { StaticMockLink } from 'utils/StaticMockLink';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the ChatRoom component
+ *
+ * Tests cover component rendering, message functionality (sending/replying),
+ * user interactions, GraphQL integration, and provider integrations
+ * (Router, Redux, i18n) for both direct and group chats.
+ */
const { setItem } = useLocalStorage();
@@ -1188,9 +1197,9 @@ const SEND_MESSAGE_TO_CHAT_MOCK = [
];
describe('Testing Chatroom Component [User Portal]', () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn();
+ window.HTMLElement.prototype.scrollIntoView = vi.fn();
- test('Chat room should display fallback content if no chat is active', async () => {
+ it('Chat room should display fallback content if no chat is active', async () => {
const mocks = [
...MESSAGE_SENT_TO_CHAT_MOCK,
...CHAT_BY_ID_QUERY_MOCK,
@@ -1212,7 +1221,7 @@ describe('Testing Chatroom Component [User Portal]', () => {
expect(await screen.findByTestId('noChatSelected')).toBeInTheDocument();
});
- test('Selected contact is direct chat', async () => {
+ it('Selected contact is direct chat', async () => {
const link = new MockSubscriptionLink();
const mocks = [
...MESSAGE_SENT_TO_CHAT_MOCK,
@@ -1234,7 +1243,7 @@ describe('Testing Chatroom Component [User Portal]', () => {
await wait();
});
- test('send message direct chat', async () => {
+ it('send message direct chat', async () => {
setItem('userId', '2');
const mocks = [
...MESSAGE_SENT_TO_CHAT_MOCK,
@@ -1319,7 +1328,7 @@ describe('Testing Chatroom Component [User Portal]', () => {
await wait(400);
});
- test('send message direct chat when userId is different', async () => {
+ it('send message direct chat when userId is different', async () => {
setItem('userId', '8');
const mocks = [
...GROUP_CHAT_BY_ID_QUERY_MOCK,
@@ -1404,7 +1413,7 @@ describe('Testing Chatroom Component [User Portal]', () => {
await wait(400);
});
- test('Selected contact is group chat', async () => {
+ it('Selected contact is group chat', async () => {
const mocks = [
...MESSAGE_SENT_TO_CHAT_MOCK,
...CHAT_BY_ID_QUERY_MOCK,
@@ -1425,7 +1434,7 @@ describe('Testing Chatroom Component [User Portal]', () => {
await wait();
});
- test('send message group chat', async () => {
+ it('send message group chat', async () => {
const mocks = [
...MESSAGE_SENT_TO_CHAT_MOCK,
...CHAT_BY_ID_QUERY_MOCK,
@@ -1505,7 +1514,7 @@ describe('Testing Chatroom Component [User Portal]', () => {
await wait(500);
});
- test('reply to message', async () => {
+ it('reply to message', async () => {
const mocks = [
...MESSAGE_SENT_TO_CHAT_MOCK,
...CHAT_BY_ID_QUERY_MOCK,
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.tsx b/src/components/UserPortal/ChatRoom/ChatRoom.tsx
index c23244a314..27768dce5a 100644
--- a/src/components/UserPortal/ChatRoom/ChatRoom.tsx
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.tsx
@@ -159,17 +159,14 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
userId: userId,
},
onData: (messageSubscriptionData) => {
- if (
- messageSubscriptionData?.data.data.messageSentToChat &&
- messageSubscriptionData?.data.data.messageSentToChat
- .chatMessageBelongsTo['_id'] == props.selectedContact
- ) {
+ const chatMessage = messageSubscriptionData.data?.data?.messageSentToChat;
+ const chatId = chatMessage?.chatMessageBelongsTo?._id;
+
+ if (!chatId) return;
+ if (chatId === props.selectedContact) {
chatRefetch();
} else {
- chatRefetch({
- id: messageSubscriptionData?.data.data.messageSentToChat
- .chatMessageBelongsTo['_id'],
- });
+ chatRefetch({ id: chatId });
}
},
});
diff --git a/src/components/UserPortal/CommentCard/CommentCard.test.tsx b/src/components/UserPortal/CommentCard/CommentCard.spec.tsx
similarity index 85%
rename from src/components/UserPortal/CommentCard/CommentCard.test.tsx
rename to src/components/UserPortal/CommentCard/CommentCard.spec.tsx
index f02e5a606a..112fd1bcf5 100644
--- a/src/components/UserPortal/CommentCard/CommentCard.test.tsx
+++ b/src/components/UserPortal/CommentCard/CommentCard.spec.tsx
@@ -7,12 +7,23 @@ import { BrowserRouter } from 'react-router-dom';
import { store } from 'state/store';
import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
-
import CommentCard from './CommentCard';
import userEvent from '@testing-library/user-event';
import { LIKE_COMMENT, UNLIKE_COMMENT } from 'GraphQl/Mutations/mutations';
import useLocalStorage from 'utils/useLocalstorage';
-
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the CommentCard component.
+ *
+ * These tests ensure the CommentCard component renders and behaves as expected
+ * under different scenarios. They cover various functionalities like:
+ * - Initial rendering with comment liked/not liked by user
+ * - User liking a comment
+ * - User unliking a comment
+ * Mocked dependencies like `useLocalStorage` and Apollo Client mocks are used
+ * to isolate the component and test its behavior independently.
+ */
const { getItem, setItem } = useLocalStorage();
async function wait(ms = 100): Promise {
@@ -56,8 +67,8 @@ const MOCKS = [
},
];
-const handleLikeComment = jest.fn();
-const handleDislikeComment = jest.fn();
+const handleLikeComment = vi.fn();
+const handleDislikeComment = vi.fn();
const link = new StaticMockLink(MOCKS, true);
describe('Testing CommentCard Component [User Portal]', () => {
@@ -67,7 +78,7 @@ describe('Testing CommentCard Component [User Portal]', () => {
});
});
- test('Component should be rendered properly if comment is already liked by the user.', async () => {
+ it('Component should be rendered properly if comment is already liked by the user.', async () => {
const cardProps = {
id: '1',
creator: {
@@ -108,7 +119,7 @@ describe('Testing CommentCard Component [User Portal]', () => {
}
});
- test('Component should be rendered properly if comment is not already liked by the user.', async () => {
+ it('Component should be rendered properly if comment is not already liked by the user.', async () => {
const cardProps = {
id: '1',
creator: {
@@ -149,7 +160,7 @@ describe('Testing CommentCard Component [User Portal]', () => {
}
});
- test('Component renders as expected if user likes the comment.', async () => {
+ it('Component renders as expected if user likes the comment.', async () => {
const cardProps = {
id: '1',
creator: {
@@ -195,7 +206,7 @@ describe('Testing CommentCard Component [User Portal]', () => {
}
});
- test('Component renders as expected if user unlikes the comment.', async () => {
+ it('Component renders as expected if user unlikes the comment.', async () => {
const cardProps = {
id: '1',
creator: {
diff --git a/src/components/UserPortal/ContactCard/ContactCard.test.tsx b/src/components/UserPortal/ContactCard/ContactCard.spec.tsx
similarity index 73%
rename from src/components/UserPortal/ContactCard/ContactCard.test.tsx
rename to src/components/UserPortal/ContactCard/ContactCard.spec.tsx
index 05a6a0a530..1d32360301 100644
--- a/src/components/UserPortal/ContactCard/ContactCard.test.tsx
+++ b/src/components/UserPortal/ContactCard/ContactCard.spec.tsx
@@ -10,7 +10,19 @@ import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
import ContactCard from './ContactCard';
import userEvent from '@testing-library/user-event';
-
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the ContactCard component.
+ *
+ * These tests ensure the ContactCard component renders and behaves as expected
+ * under different scenarios. They cover various functionalities like:
+ * - Rendering the contact card with and without a profile image
+ * - Selecting a contact by clicking on the card
+ * - Applying a grey background color to the selected contact card (for groups)
+ * Mocked dependencies like StaticMockLink are used
+ * to isolate the component and test its behavior independently.
+ */
const link = new StaticMockLink([], true);
async function wait(ms = 100): Promise {
@@ -30,12 +42,12 @@ let props = {
image: '',
selectedContact: '',
type: '',
- setSelectedContact: jest.fn(),
- setSelectedChatType: jest.fn(),
+ setSelectedContact: vi.fn(),
+ setSelectedChatType: vi.fn(),
};
describe('Testing ContactCard Component [User Portal]', () => {
- test('Component should be rendered properly if person image is undefined', async () => {
+ it('Component should be rendered properly if person image is undefined', async () => {
render(
@@ -51,7 +63,7 @@ describe('Testing ContactCard Component [User Portal]', () => {
await wait();
});
- test('Component should be rendered properly if person image is not undefined', async () => {
+ it('Component should be rendered properly if person image is not undefined', async () => {
props = {
...props,
image: 'personImage',
@@ -72,7 +84,7 @@ describe('Testing ContactCard Component [User Portal]', () => {
await wait();
});
- test('Contact gets selectected when component is clicked', async () => {
+ it('Contact gets selectected when component is clicked', async () => {
render(
@@ -92,7 +104,7 @@ describe('Testing ContactCard Component [User Portal]', () => {
await wait();
});
- test('Component is rendered with background color grey if the contact is selected', async () => {
+ it('Component is rendered with background color grey if the contact is selected', async () => {
props = {
...props,
selectedContact: '1',
diff --git a/src/components/UserPortal/CreateDirectChat/CreateDirectChat.test.tsx b/src/components/UserPortal/CreateDirectChat/CreateDirectChat.spec.tsx
similarity index 97%
rename from src/components/UserPortal/CreateDirectChat/CreateDirectChat.test.tsx
rename to src/components/UserPortal/CreateDirectChat/CreateDirectChat.spec.tsx
index d8b3466207..5526206708 100644
--- a/src/components/UserPortal/CreateDirectChat/CreateDirectChat.test.tsx
+++ b/src/components/UserPortal/CreateDirectChat/CreateDirectChat.spec.tsx
@@ -20,9 +20,24 @@ import {
} from 'GraphQl/Mutations/OrganizationMutations';
import { CHATS_LIST, CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries';
import useLocalStorage from 'utils/useLocalstorage';
-
+import { vi } from 'vitest';
const { setItem } = useLocalStorage();
+/**
+ * Unit tests for the Create Direct Chat Modal functionality in the User Portal
+ *
+ * These tests cover the following scenarios:
+ * 1. Opening and closing the create new direct chat modal, ensuring proper UI elements
+ * like dropdown, search input, and submit button are displayed and functional.
+ * 2. Creating a new direct chat, which includes testing the interaction with the add button,
+ * submitting the chat, and closing the modal.
+ *
+ * Tests involve interacting with the modal's UI elements, performing actions like
+ * opening the dropdown, searching for users, clicking on the submit button, and closing
+ * the modal. GraphQL mocks are used for testing chat-related queries and mutations,
+ * ensuring that the modal behaves as expected when interacting with the GraphQL API.
+ */
+
const UserConnectionListMock = [
{
request: {
@@ -1380,23 +1395,23 @@ async function wait(ms = 100): Promise {
}
describe('Testing Create Direct Chat Modal [User Portal]', () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn();
+ window.HTMLElement.prototype.scrollIntoView = vi.fn();
Object.defineProperty(window, 'matchMedia', {
writable: true,
- value: jest.fn().mockImplementation((query) => ({
+ value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
- addListener: jest.fn(), // Deprecated
- removeListener: jest.fn(), // Deprecated
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- dispatchEvent: jest.fn(),
+ addListener: vi.fn(), // Deprecated
+ removeListener: vi.fn(), // Deprecated
+ addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
+ dispatchEvent: vi.fn(),
})),
});
- test('Open and close create new direct chat modal', async () => {
+ it('Open and close create new direct chat modal', async () => {
const mock = [
...GROUP_CHAT_BY_ID_QUERY_MOCK,
...MESSAGE_SENT_TO_CHAT_MOCK,
@@ -1446,7 +1461,7 @@ describe('Testing Create Direct Chat Modal [User Portal]', () => {
fireEvent.click(closeButton);
});
- test('create new direct chat', async () => {
+ it('create new direct chat', async () => {
setItem('userId', '1');
const mock = [
...GROUP_CHAT_BY_ID_QUERY_MOCK,
diff --git a/src/components/UserPortal/CreateGroupChat/CreateGroupChat.test.tsx b/src/components/UserPortal/CreateGroupChat/CreateGroupChat.spec.tsx
similarity index 97%
rename from src/components/UserPortal/CreateGroupChat/CreateGroupChat.test.tsx
rename to src/components/UserPortal/CreateGroupChat/CreateGroupChat.spec.tsx
index 055985d5fb..711bb29a93 100644
--- a/src/components/UserPortal/CreateGroupChat/CreateGroupChat.test.tsx
+++ b/src/components/UserPortal/CreateGroupChat/CreateGroupChat.spec.tsx
@@ -26,6 +26,24 @@ import {
import { CHATS_LIST, CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries';
import useLocalStorage from 'utils/useLocalstorage';
import userEvent from '@testing-library/user-event';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the Create Group Chat Modal functionality in the User Portal
+ *
+ * These tests cover the following scenarios:
+ * 1. Opening and closing the create new group chat modal, ensuring proper UI elements
+ * like the dropdown, new group chat button, and close button are displayed and functional.
+ * 2. Creating a new group chat by interacting with the group name input field, organization
+ * selection, and submission process. It also ensures that the create button is properly
+ * triggered after filling out the required fields.
+ * 3. Adding and removing users from the group chat, testing the interactions with the add
+ * and remove buttons, and verifying the submit button and search functionality for user selection.
+ *
+ * GraphQL mocks are used to simulate chat-related queries and mutations. The tests ensure that
+ * the modal behaves correctly in various user interaction scenarios, including handling of form
+ * fields, user management, and modal navigation.
+ */
const { setItem } = useLocalStorage();
@@ -2212,23 +2230,23 @@ async function wait(ms = 100): Promise {
}
describe('Testing Create Group Chat Modal [User Portal]', () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn();
+ window.HTMLElement.prototype.scrollIntoView = vi.fn();
Object.defineProperty(window, 'matchMedia', {
writable: true,
- value: jest.fn().mockImplementation((query) => ({
+ value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
- addListener: jest.fn(), // Deprecated
- removeListener: jest.fn(), // Deprecated
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- dispatchEvent: jest.fn(),
+ addListener: vi.fn(), // Deprecated
+ removeListener: vi.fn(), // Deprecated
+ addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
+ dispatchEvent: vi.fn(),
})),
});
- test('open and close create new direct chat modal', async () => {
+ it('open and close create new direct chat modal', async () => {
const mock = [
...USER_JOINED_ORG_MOCK,
...GROUP_CHAT_BY_ID_QUERY_MOCK,
@@ -2266,7 +2284,7 @@ describe('Testing Create Group Chat Modal [User Portal]', () => {
fireEvent.click(closeButton);
});
- test('create new group chat', async () => {
+ it('create new group chat', async () => {
const mock = [
...USER_JOINED_ORG_MOCK,
...GROUP_CHAT_BY_ID_QUERY_MOCK,
@@ -2366,7 +2384,7 @@ describe('Testing Create Group Chat Modal [User Portal]', () => {
// });
}, 3000);
- test('add and remove user', async () => {
+ it('add and remove user', async () => {
setItem('userId', '1');
const mock = [
...USER_JOINED_ORG_MOCK,
diff --git a/src/components/UserPortal/DonationCard/DonationCard.test.tsx b/src/components/UserPortal/DonationCard/DonationCard.spec.tsx
similarity index 64%
rename from src/components/UserPortal/DonationCard/DonationCard.test.tsx
rename to src/components/UserPortal/DonationCard/DonationCard.spec.tsx
index cddf62dd6c..f4d8473dd1 100644
--- a/src/components/UserPortal/DonationCard/DonationCard.test.tsx
+++ b/src/components/UserPortal/DonationCard/DonationCard.spec.tsx
@@ -11,6 +11,19 @@ import { StaticMockLink } from 'utils/StaticMockLink';
import DonationCard from './DonationCard';
import { type InterfaceDonationCardProps } from 'screens/UserPortal/Donate/Donate';
+/**
+ * Unit test for the DonationCard component in the User Portal
+ *
+ * This test ensures that the DonationCard component renders correctly when provided with
+ * the required props. It uses the MockedProvider to mock any GraphQL queries and the
+ * StaticMockLink to simulate the network layer. The component is wrapped with necessary
+ * providers such as BrowserRouter, Redux store, and i18n provider to simulate the environment
+ * in which the component operates.
+ *
+ * The test specifically checks if the component renders without errors, though more tests
+ * can be added in the future to validate interactions and state changes based on user actions.
+ */
+
const link = new StaticMockLink([], true);
async function wait(ms = 100): Promise {
@@ -31,7 +44,7 @@ const props: InterfaceDonationCardProps = {
};
describe('Testing ContactCard Component [User Portal]', () => {
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
render(
diff --git a/src/components/UserPortal/EventCard/EventCard.test.tsx b/src/components/UserPortal/EventCard/EventCard.spec.tsx
similarity index 94%
rename from src/components/UserPortal/EventCard/EventCard.test.tsx
rename to src/components/UserPortal/EventCard/EventCard.spec.tsx
index 78aa32abcf..150f676447 100644
--- a/src/components/UserPortal/EventCard/EventCard.test.tsx
+++ b/src/components/UserPortal/EventCard/EventCard.spec.tsx
@@ -11,7 +11,6 @@ import { Provider } from 'react-redux';
import { store } from 'state/store';
import { StaticMockLink } from 'utils/StaticMockLink';
import userEvent from '@testing-library/user-event';
-import { debug } from 'jest-preview';
import useLocalStorage from 'utils/useLocalstorage';
const { setItem } = useLocalStorage();
@@ -66,7 +65,7 @@ describe('Testing Event Card In User portal', () => {
],
};
- test('The card should be rendered properly, and all the details should be displayed correct', async () => {
+ it('The card should be rendered properly, and all the details should be displayed correct', async () => {
const { queryByText } = render(
@@ -79,7 +78,6 @@ describe('Testing Event Card In User portal', () => {
,
);
- debug();
await waitFor(() => expect(queryByText('Test Event')).toBeInTheDocument());
await waitFor(() =>
expect(queryByText('This is a test event')).toBeInTheDocument(),
@@ -105,7 +103,7 @@ describe('Testing Event Card In User portal', () => {
await waitFor(() => expect(queryByText('Register')).toBeInTheDocument());
});
- test('When the user is already registered', async () => {
+ it('When the user is already registered', async () => {
setItem('userId', '234');
const { queryByText } = render(
@@ -124,7 +122,7 @@ describe('Testing Event Card In User portal', () => {
);
});
- test('Handle register should work properly', async () => {
+ it('Handle register should work properly', async () => {
setItem('userId', '456');
const { queryByText } = render(
@@ -173,7 +171,7 @@ describe('Event card when start and end time are not given', () => {
],
};
- test('Card is rendered correctly', async () => {
+ it('Card is rendered correctly', async () => {
const { container } = render(
diff --git a/src/components/UserPortal/OrganizationCard/OrganizationCard.test.tsx b/src/components/UserPortal/OrganizationCard/OrganizationCard.spec.tsx
similarity index 77%
rename from src/components/UserPortal/OrganizationCard/OrganizationCard.test.tsx
rename to src/components/UserPortal/OrganizationCard/OrganizationCard.spec.tsx
index 77516ddc7a..1aa8c8462a 100644
--- a/src/components/UserPortal/OrganizationCard/OrganizationCard.test.tsx
+++ b/src/components/UserPortal/OrganizationCard/OrganizationCard.spec.tsx
@@ -18,15 +18,33 @@ import useLocalStorage from 'utils/useLocalstorage';
import {
SEND_MEMBERSHIP_REQUEST,
JOIN_PUBLIC_ORGANIZATION,
+ CANCEL_MEMBERSHIP_REQUEST,
} from 'GraphQl/Mutations/OrganizationMutations';
import { toast } from 'react-toastify';
-
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the OrganizationCard component in the User Portal
+ *
+ * These tests validate the behavior and rendering of the OrganizationCard component.
+ * The tests ensure the component displays properly with various states and that interactions
+ * such as sending membership requests and visiting organizations work as expected.
+ *
+ * 1. **Component should be rendered properly**: Tests if the component renders correctly with the provided props.
+ * 2. **Component should render properly with an image**: Verifies the component's behavior when an organization image is available.
+ * 3. **Visit organization**: Simulates a click on the "manage" button and verifies that the user is redirected to the correct organization page.
+ * 4. **Send membership request**: Tests if the membership request is successfully sent and verifies the success toast message.
+ * 5. **Send membership request to a public organization**: Validates sending a membership request to a public organization and verifies multiple success toast messages.
+ * 6. **Withdraw membership request**: Simulates withdrawing a membership request and verifies that the button works as expected.
+ *
+ * Mocked GraphQL queries and mutations are used to simulate the backend behavior for testing.
+ */
const { getItem } = useLocalStorage();
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ error: vi.fn(),
},
}));
@@ -146,6 +164,21 @@ const MOCKS = [
},
},
},
+ {
+ request: {
+ query: CANCEL_MEMBERSHIP_REQUEST,
+ variables: {
+ membershipRequestId: '56gheqyr7deyfuiwfewifruy8',
+ },
+ },
+ result: {
+ data: {
+ cancelMembershipRequest: {
+ _id: '56gheqyr7deyfuiwfewifruy8',
+ },
+ },
+ },
+ },
];
const link = new StaticMockLink(MOCKS, true);
@@ -189,7 +222,7 @@ let props = {
};
describe('Testing OrganizationCard Component [User Portal]', () => {
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
render(
@@ -205,7 +238,7 @@ describe('Testing OrganizationCard Component [User Portal]', () => {
await wait();
});
- test('Component should be rendered properly if organization Image is not undefined', async () => {
+ it('Component should be rendered properly if organization Image is not undefined', async () => {
props = {
...props,
image: 'organizationImage',
@@ -226,7 +259,7 @@ describe('Testing OrganizationCard Component [User Portal]', () => {
await wait();
});
- test('Visit organization', async () => {
+ it('Visit organization', async () => {
const cardProps = {
...props,
id: '3',
@@ -258,7 +291,7 @@ describe('Testing OrganizationCard Component [User Portal]', () => {
expect(window.location.pathname).toBe(`/user/organization/${cardProps.id}`);
});
- test('Send membership request', async () => {
+ it('Send membership request', async () => {
props = {
...props,
image: 'organizationImage',
@@ -286,7 +319,7 @@ describe('Testing OrganizationCard Component [User Portal]', () => {
expect(toast.success).toHaveBeenCalledWith('MembershipRequestSent');
});
- test('send membership request to public org', async () => {
+ it('send membership request to public org', async () => {
const cardProps = {
...props,
id: '2',
@@ -316,13 +349,21 @@ describe('Testing OrganizationCard Component [User Portal]', () => {
expect(toast.success).toHaveBeenCalledTimes(2);
});
- test('withdraw membership request', async () => {
+ it('withdraw membership request', async () => {
const cardProps = {
...props,
id: '3',
image: 'organizationImage',
userRegistrationRequired: true,
membershipRequestStatus: 'pending',
+ membershipRequests: [
+ {
+ _id: '56gheqyr7deyfuiwfewifruy8',
+ user: {
+ _id: getItem('userId'),
+ },
+ },
+ ],
};
render(
diff --git a/src/components/UserPortal/OrganizationNavbar/OrganizationNavbar.test.tsx b/src/components/UserPortal/OrganizationNavbar/OrganizationNavbar.spec.tsx
similarity index 81%
rename from src/components/UserPortal/OrganizationNavbar/OrganizationNavbar.test.tsx
rename to src/components/UserPortal/OrganizationNavbar/OrganizationNavbar.spec.tsx
index 038ff626df..7f9e8e52bb 100644
--- a/src/components/UserPortal/OrganizationNavbar/OrganizationNavbar.test.tsx
+++ b/src/components/UserPortal/OrganizationNavbar/OrganizationNavbar.spec.tsx
@@ -1,6 +1,5 @@
import React, { act } from 'react';
import { MockedProvider } from '@apollo/react-testing';
-import 'jest-localstorage-mock';
import { render, screen } from '@testing-library/react';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
@@ -17,6 +16,28 @@ import { PLUGIN_SUBSCRIPTION } from 'GraphQl/Mutations/mutations';
import { createMemoryHistory } from 'history';
import useLocalStorage from 'utils/useLocalstorage';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the OrganizationNavbar component.
+ *
+ * These tests validate the rendering and behavior of the component, ensuring it renders correctly,
+ * handles user interactions, and manages language and plugin updates.
+ *
+ * 1. **Component rendering**: Verifies correct rendering with provided props and organization details.
+ * 2. **Navigation on plugin click**: Simulates navigation when a plugin link is clicked.
+ * 3. **Language switch to English**: Tests if the language changes to English.
+ * 4. **Language switch to French**: Tests if the language changes to French.
+ * 5. **Language switch to Hindi**: Tests if the language changes to Hindi.
+ * 6. **Language switch to Spanish**: Tests if the language changes to Spanish.
+ * 7. **Language switch to Chinese**: Tests if the language changes to Chinese.
+ * 8. **Rendering plugins from localStorage**: Verifies correct rendering of plugins from localStorage.
+ * 9. **Plugin removal on uninstallation**: Ensures plugins are removed when uninstalled for the organization.
+ * 10. **Rendering plugins when not uninstalled**: Ensures plugins render if not uninstalled.
+ * 11. **No changes for unmatched plugin**: Ensures no changes when an unrecognized plugin update occurs.
+ *
+ * Mocked GraphQL queries and subscriptions simulate backend behavior.
+ */
const { setItem, removeItem } = useLocalStorage();
@@ -167,23 +188,26 @@ const navbarProps = {
currentPage: 'home',
};
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useParams: () => ({ orgId: organizationId }),
-}));
+vi.mock('react-router-dom', async () => {
+ const actual = await vi.importActual('react-router-dom');
+ return {
+ ...actual,
+ useParams: () => ({ orgId: organizationId }),
+ };
+});
describe('Testing OrganizationNavbar Component [User Portal]', () => {
Object.defineProperty(window, 'matchMedia', {
writable: true,
- value: jest.fn().mockImplementation((query) => ({
+ value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
- addListener: jest.fn(), // Deprecated
- removeListener: jest.fn(), // Deprecated
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- dispatchEvent: jest.fn(),
+ addListener: vi.fn(), // Deprecated
+ removeListener: vi.fn(), // Deprecated
+ addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
+ dispatchEvent: vi.fn(),
})),
});
@@ -193,7 +217,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
});
});
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
render(
@@ -217,7 +241,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
// expect(screen.getByText('Chat')).toBeInTheDocument();
});
- test('should navigate correctly on clicking a plugin', async () => {
+ it('should navigate correctly on clicking a plugin', async () => {
const history = createMemoryHistory();
render(
@@ -240,7 +264,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
expect(history.location.pathname).toBe(`/user/people/${organizationId}`);
});
- test('The language is switched to English', async () => {
+ it('The language is switched to English', async () => {
render(
@@ -270,7 +294,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
// expect(screen.getByText('Chat')).toBeInTheDocument();
});
- test('The language is switched to fr', async () => {
+ it('The language is switched to fr', async () => {
render(
@@ -294,7 +318,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('fr');
});
- test('The language is switched to hi', async () => {
+ it('The language is switched to hi', async () => {
render(
@@ -318,7 +342,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('hi');
});
- test('The language is switched to sp', async () => {
+ it('The language is switched to sp', async () => {
render(
@@ -342,7 +366,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('sp');
});
- test('The language is switched to zh', async () => {
+ it('The language is switched to zh', async () => {
render(
@@ -366,7 +390,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('zh');
});
- test('Component should be rendered properly if plugins are present in localStorage', async () => {
+ it('Component should be rendered properly if plugins are present in localStorage', async () => {
setItem('talawaPlugins', JSON.stringify(testPlugins));
render(
@@ -390,7 +414,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
removeItem('talawaPlugins');
});
- test('should remove plugin if uninstalledOrgs contains organizationId', async () => {
+ it('should remove plugin if uninstalledOrgs contains organizationId', async () => {
setItem('talawaPlugins', JSON.stringify(testPlugins));
render(
@@ -412,7 +436,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
});
});
- test('should render plugin if uninstalledOrgs does not contain organizationId', async () => {
+ it('should render plugin if uninstalledOrgs does not contain organizationId', async () => {
setItem('talawaPlugins', JSON.stringify(testPlugins));
render(
@@ -434,7 +458,7 @@ describe('Testing OrganizationNavbar Component [User Portal]', () => {
});
});
- test('should do nothing if pluginName is not found in the rendered plugins', async () => {
+ it('should do nothing if pluginName is not found in the rendered plugins', async () => {
render(
diff --git a/src/components/UserPortal/OrganizationSidebar/OrganizationSidebar.test.tsx b/src/components/UserPortal/OrganizationSidebar/OrganizationSidebar.spec.tsx
similarity index 74%
rename from src/components/UserPortal/OrganizationSidebar/OrganizationSidebar.test.tsx
rename to src/components/UserPortal/OrganizationSidebar/OrganizationSidebar.spec.tsx
index cddac285fd..2559eaae14 100644
--- a/src/components/UserPortal/OrganizationSidebar/OrganizationSidebar.test.tsx
+++ b/src/components/UserPortal/OrganizationSidebar/OrganizationSidebar.spec.tsx
@@ -13,6 +13,20 @@ import { store } from 'state/store';
import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
import OrganizationSidebar from './OrganizationSidebar';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the OrganizationSidebar component in the User Portal.
+ *
+ * These tests validate the rendering and behavior of the OrganizationSidebar component,
+ * ensuring that it displays correct content based on the availability of members and events.
+ *
+ * 1. **Component renders properly when members and events lists are empty**: Verifies the correct display of "No Members to show" and "No Events to show" when both lists are empty.
+ * 2. **Component renders properly when events list is not empty**: Tests that the events section is rendered correctly when events are available, and "No Events to show" is not displayed.
+ * 3. **Component renders properly when members list is not empty**: Verifies the correct display of members when available, ensuring "No Members to show" is not displayed.
+ *
+ * Mocked GraphQL queries simulate backend responses for members and events lists.
+ */
const MOCKS = [
{
@@ -94,13 +108,17 @@ async function wait(ms = 100): Promise {
});
}
let mockId = '';
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useParams: () => ({ orgId: mockId }),
-}));
+
+vi.mock('react-router-dom', async () => {
+ const actual = await vi.importActual('react-router-dom');
+ return {
+ ...actual,
+ useParams: () => ({ orgId: mockId }),
+ };
+});
describe('Testing OrganizationSidebar Component [User Portal]', () => {
- test('Component should be rendered properly when members and events list is empty', async () => {
+ it('Component should be rendered properly when members and events list is empty', async () => {
render(
@@ -119,7 +137,7 @@ describe('Testing OrganizationSidebar Component [User Portal]', () => {
expect(screen.queryByText('No Events to show')).toBeInTheDocument();
});
- test('Component should be rendered properly when events list is not empty', async () => {
+ it('Component should be rendered properly when events list is not empty', async () => {
mockId = 'events';
render(
@@ -139,7 +157,7 @@ describe('Testing OrganizationSidebar Component [User Portal]', () => {
expect(screen.queryByText('Event')).toBeInTheDocument();
});
- test('Component should be rendered properly when members list is not empty', async () => {
+ it('Component should be rendered properly when members list is not empty', async () => {
mockId = 'members';
render(
diff --git a/src/components/UserPortal/PeopleCard/PeopleCard.test.tsx b/src/components/UserPortal/PeopleCard/PeopleCard.spec.tsx
similarity index 65%
rename from src/components/UserPortal/PeopleCard/PeopleCard.test.tsx
rename to src/components/UserPortal/PeopleCard/PeopleCard.spec.tsx
index fd5d6c7f93..c725a8d45a 100644
--- a/src/components/UserPortal/PeopleCard/PeopleCard.test.tsx
+++ b/src/components/UserPortal/PeopleCard/PeopleCard.spec.tsx
@@ -10,6 +10,18 @@ import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
import PeopleCard from './PeopleCard';
+/**
+ * Unit tests for the PeopleCard component in the User Portal.
+ *
+ * These tests ensure that the PeopleCard component renders correctly with and without an image,
+ * validating that all information (name, role, email, etc.) is displayed as expected.
+ *
+ * 1. **Component renders properly**: Verifies that the component renders correctly with the given props (name, email, role, etc.).
+ * 2. **Component renders properly if the person image is provided**: Ensures the component correctly displays the image when a valid image URL is passed in the props.
+ *
+ * Mocked GraphQL queries are used to simulate backend behavior, though no queries are required for these tests.
+ */
+
const link = new StaticMockLink([], true);
async function wait(ms = 100): Promise {
@@ -30,7 +42,7 @@ let props = {
};
describe('Testing PeopleCard Component [User Portal]', () => {
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
render(
@@ -46,7 +58,7 @@ describe('Testing PeopleCard Component [User Portal]', () => {
await wait();
});
- test('Component should be rendered properly if person image is not undefined', async () => {
+ it('Component should be rendered properly if person image is not undefined', async () => {
props = {
...props,
image: 'personImage',
diff --git a/src/components/UserPortal/PostCard/PostCard.test.tsx b/src/components/UserPortal/PostCard/PostCard.spec.tsx
similarity index 90%
rename from src/components/UserPortal/PostCard/PostCard.test.tsx
rename to src/components/UserPortal/PostCard/PostCard.spec.tsx
index 1b7708b384..fe3fcf3dc1 100644
--- a/src/components/UserPortal/PostCard/PostCard.test.tsx
+++ b/src/components/UserPortal/PostCard/PostCard.spec.tsx
@@ -21,14 +21,33 @@ import {
UPDATE_POST_MUTATION,
} from 'GraphQl/Mutations/mutations';
import useLocalStorage from 'utils/useLocalstorage';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the PostCard component in the User Portal.
+ *
+ * These tests ensure the PostCard component behaves as expected:
+ *
+ * 1. **Component rendering**: Verifies correct rendering with props like title, text, and creator info.
+ * 2. **Dropdown functionality**: Tests the dropdown for editing and deleting posts.
+ * 3. **Edit post**: Ensures the post can be edited with a success message.
+ * 4. **Delete post**: Verifies post deletion works with a success message.
+ * 5. **Like/unlike post**: Ensures the UI updates when a user likes or unlikes a post.
+ * 6. **Post image**: Verifies post image rendering.
+ * 7. **Create comment**: Ensures a comment is created successfully.
+ * 8. **Like/unlike comment**: Tests liking/unliking comments.
+ * 9. **Comment modal**: Verifies the comment modal appears when clicked.
+ *
+ * Mocked GraphQL data is used for simulating backend behavior.
+ */
const { setItem, getItem } = useLocalStorage();
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- error: jest.fn(),
- info: jest.fn(),
- success: jest.fn(),
+ error: vi.fn(),
+ info: vi.fn(),
+ success: vi.fn(),
},
}));
@@ -164,7 +183,7 @@ async function wait(ms = 100): Promise {
const link = new StaticMockLink(MOCKS, true);
describe('Testing PostCard Component [User Portal]', () => {
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
const cardProps = {
id: 'postId',
userImage: 'image.png',
@@ -218,7 +237,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '2',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -236,7 +255,7 @@ describe('Testing PostCard Component [User Portal]', () => {
await wait();
});
- test('Dropdown component should be rendered properly', async () => {
+ it('Dropdown component should be rendered properly', async () => {
setItem('userId', '2');
const cardProps = {
@@ -263,7 +282,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '2',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -285,7 +304,7 @@ describe('Testing PostCard Component [User Portal]', () => {
expect(screen.getByText('Delete')).toBeInTheDocument();
});
- test('Edit post should work properly', async () => {
+ it('Edit post should work properly', async () => {
setItem('userId', '2');
const cardProps = {
@@ -312,7 +331,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '2',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -341,7 +360,7 @@ describe('Testing PostCard Component [User Portal]', () => {
expect(toast.success).toHaveBeenCalledWith('Post updated Successfully');
});
- test('Delete post should work properly', async () => {
+ it('Delete post should work properly', async () => {
setItem('userId', '2');
const cardProps = {
@@ -368,7 +387,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '2',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -393,7 +412,7 @@ describe('Testing PostCard Component [User Portal]', () => {
);
});
- test('Component should be rendered properly if user has liked the post', async () => {
+ it('Component should be rendered properly if user has liked the post', async () => {
const beforeUserId = getItem('userId');
setItem('userId', '2');
@@ -421,7 +440,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '2',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -443,7 +462,7 @@ describe('Testing PostCard Component [User Portal]', () => {
}
});
- test('Component should be rendered properly if user unlikes a post', async () => {
+ it('Component should be rendered properly if user unlikes a post', async () => {
const beforeUserId = getItem('userId');
setItem('userId', '2');
@@ -471,7 +490,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '2',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -496,7 +515,7 @@ describe('Testing PostCard Component [User Portal]', () => {
}
});
- test('Component should be rendered properly if user likes a post', async () => {
+ it('Component should be rendered properly if user likes a post', async () => {
const beforeUserId = getItem('userId');
setItem('userId', '2');
@@ -524,7 +543,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '1',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -549,7 +568,7 @@ describe('Testing PostCard Component [User Portal]', () => {
}
});
- test('Component should be rendered properly if post image is defined', async () => {
+ it('Component should be rendered properly if post image is defined', async () => {
const cardProps = {
id: '',
userImage: 'image.png',
@@ -574,7 +593,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '1',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -592,7 +611,7 @@ describe('Testing PostCard Component [User Portal]', () => {
await wait();
});
- test('Comment is created successfully after create comment button is clicked.', async () => {
+ it('Comment is created successfully after create comment button is clicked.', async () => {
const cardProps = {
id: '1',
userImage: 'image.png',
@@ -617,7 +636,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '1',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
@@ -701,7 +720,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '1',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
const beforeUserId = getItem('userId');
setItem('userId', '2');
@@ -788,7 +807,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '1',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
const beforeUserId = getItem('userId');
setItem('userId', '1');
@@ -815,7 +834,7 @@ describe('Testing PostCard Component [User Portal]', () => {
setItem('userId', beforeUserId);
}
});
- test('Comment modal pops when show comments button is clicked.', async () => {
+ it('Comment modal pops when show comments button is clicked.', async () => {
const cardProps = {
id: '',
userImage: 'image.png',
@@ -840,7 +859,7 @@ describe('Testing PostCard Component [User Portal]', () => {
id: '1',
},
],
- fetchPosts: jest.fn(),
+ fetchPosts: vi.fn(),
};
render(
diff --git a/src/components/UserPortal/PromotedPost/PromotedPost.test.tsx b/src/components/UserPortal/PromotedPost/PromotedPost.spec.tsx
similarity index 77%
rename from src/components/UserPortal/PromotedPost/PromotedPost.test.tsx
rename to src/components/UserPortal/PromotedPost/PromotedPost.spec.tsx
index 6ec8ec5de7..dc8bb14e2d 100644
--- a/src/components/UserPortal/PromotedPost/PromotedPost.test.tsx
+++ b/src/components/UserPortal/PromotedPost/PromotedPost.spec.tsx
@@ -10,6 +10,18 @@ import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
import PromotedPost from './PromotedPost';
+/**
+ * Unit tests for the PromotedPost component.
+ *
+ * 1. **Render check**: Verifies the component renders correctly with props like title and image.
+ * 2. **Image prop check**: Tests if the component renders correctly with an image.
+ * 3. **Icon display**: Ensures the icon (StarPurple500Icon) is displayed.
+ * 4. **Text display**: Checks that the post title is displayed correctly.
+ * 5. **Image display**: Verifies the correct image is displayed when the image prop is set.
+ *
+ * GraphQL data is mocked for backend simulation.
+ */
+
const link = new StaticMockLink([], true);
async function wait(ms = 100): Promise {
@@ -27,7 +39,7 @@ let props = {
};
describe('Testing PromotedPost Test', () => {
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
render(
@@ -43,7 +55,7 @@ describe('Testing PromotedPost Test', () => {
await wait();
});
- test('Component should be rendered properly if prop image is not undefined', async () => {
+ it('Component should be rendered properly if prop image is not undefined', async () => {
props = {
...props,
image: 'promotedPostImage',
@@ -65,7 +77,7 @@ describe('Testing PromotedPost Test', () => {
});
});
-test('Component should display the icon correctly', async () => {
+it('Component should display the icon correctly', async () => {
const { queryByTestId } = render(
@@ -84,7 +96,7 @@ test('Component should display the icon correctly', async () => {
});
});
-test('Component should display the text correctly', async () => {
+it('Component should display the text correctly', async () => {
const { queryAllByText } = render(
@@ -103,7 +115,7 @@ test('Component should display the text correctly', async () => {
});
});
-test('Component should display the image correctly', async () => {
+it('Component should display the image correctly', async () => {
props = {
...props,
image: 'promotedPostImage',
diff --git a/src/components/UserPortal/Register/Register.test.tsx b/src/components/UserPortal/Register/Register.spec.tsx
similarity index 80%
rename from src/components/UserPortal/Register/Register.test.tsx
rename to src/components/UserPortal/Register/Register.spec.tsx
index 1883d60da3..7ca65f8f0d 100644
--- a/src/components/UserPortal/Register/Register.test.tsx
+++ b/src/components/UserPortal/Register/Register.spec.tsx
@@ -12,6 +12,22 @@ import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
import Register from './Register';
import { toast } from 'react-toastify';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the Register component.
+ *
+ * 1. **Render test**: Verifies proper rendering of the Register component.
+ * 2. **Mode switch to Login**: Ensures that clicking the "setLoginBtn" changes mode to 'login'.
+ * 3. **Empty email validation**: Checks if toast.error is triggered for empty email.
+ * 4. **Empty password validation**: Ensures toast.error is called for empty password.
+ * 5. **Empty first name validation**: Ensures toast.error is called if first name is missing.
+ * 6. **Empty last name validation**: Verifies toast.error is triggered if last name is missing.
+ * 7. **Password mismatch validation**: Verifies toast.error is shown if confirm password doesn't match.
+ * 8. **Successful registration**: Confirms that toast.success is called when valid credentials are entered.
+ *
+ * GraphQL mock data is used for testing user registration functionality.
+ */
const MOCKS = [
{
@@ -56,22 +72,22 @@ async function wait(ms = 100): Promise {
});
}
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
},
}));
-const setCurrentMode: React.Dispatch> = jest.fn();
+const setCurrentMode: React.Dispatch> = vi.fn();
const props = {
setCurrentMode,
};
describe('Testing Register Component [User Portal]', () => {
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
render(
@@ -87,7 +103,7 @@ describe('Testing Register Component [User Portal]', () => {
await wait();
});
- test('Expect the mode to be changed to Login', async () => {
+ it('Expect the mode to be changed to Login', async () => {
render(
@@ -107,7 +123,7 @@ describe('Testing Register Component [User Portal]', () => {
expect(setCurrentMode).toHaveBeenCalledWith('login');
});
- test('Expect toast.error to be called if email input is empty', async () => {
+ it('Expect toast.error to be called if email input is empty', async () => {
render(
@@ -127,7 +143,7 @@ describe('Testing Register Component [User Portal]', () => {
expect(toast.error).toHaveBeenCalledWith('Please enter valid details.');
});
- test('Expect toast.error to be called if password input is empty', async () => {
+ it('Expect toast.error to be called if password input is empty', async () => {
render(
@@ -148,7 +164,7 @@ describe('Testing Register Component [User Portal]', () => {
expect(toast.error).toHaveBeenCalledWith('Please enter valid details.');
});
- test('Expect toast.error to be called if first name input is empty', async () => {
+ it('Expect toast.error to be called if first name input is empty', async () => {
render(
@@ -172,7 +188,7 @@ describe('Testing Register Component [User Portal]', () => {
expect(toast.error).toHaveBeenCalledWith('Please enter valid details.');
});
- test('Expect toast.error to be called if last name input is empty', async () => {
+ it('Expect toast.error to be called if last name input is empty', async () => {
render(
@@ -228,7 +244,7 @@ describe('Testing Register Component [User Portal]', () => {
);
});
- test('Expect toast.success to be called if valid credentials are entered.', async () => {
+ it('Expect toast.success to be called if valid credentials are entered.', async () => {
render(
diff --git a/src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.test.tsx b/src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.spec.tsx
similarity index 77%
rename from src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.test.tsx
rename to src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.spec.tsx
index 93b71b14f1..98459b930a 100644
--- a/src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.test.tsx
+++ b/src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.spec.tsx
@@ -4,10 +4,20 @@ import { render, screen, waitFor } from '@testing-library/react';
import SecuredRouteForUser from './SecuredRouteForUser';
import useLocalStorage from 'utils/useLocalstorage';
+/**
+ * Unit tests for SecuredRouteForUser component:
+ *
+ * 1. **Logged-in user**: Verifies that the route renders when 'IsLoggedIn' is set to 'TRUE'.
+ * 2. **Not logged-in user**: Ensures redirection to the login page when 'IsLoggedIn' is 'FALSE'.
+ * 3. **Logged-in user with admin access**: Checks that the route renders for a logged-in user with 'AdminFor' set (i.e., admin of an organization).
+ *
+ * LocalStorage values like 'IsLoggedIn' and 'AdminFor' are set to simulate different user states.
+ */
+
const { setItem } = useLocalStorage();
describe('SecuredRouteForUser', () => {
- test('renders the route when the user is logged in', () => {
+ it('renders the route when the user is logged in', () => {
// Set the 'IsLoggedIn' value to 'TRUE' in localStorage to simulate a logged-in user and do not set 'AdminFor' so that it remains undefined.
setItem('IsLoggedIn', 'TRUE');
@@ -31,7 +41,7 @@ describe('SecuredRouteForUser', () => {
expect(screen.getByTestId('organizations-content')).toBeInTheDocument();
});
- test('redirects to /user when the user is not logged in', async () => {
+ it('redirects to /user when the user is not logged in', async () => {
// Set the user as not logged in in local storage
setItem('IsLoggedIn', 'FALSE');
@@ -58,7 +68,7 @@ describe('SecuredRouteForUser', () => {
});
});
- test('renders the route when the user is logged in and user is ADMIN', () => {
+ it('renders the route when the user is logged in and user is ADMIN', () => {
// Set the 'IsLoggedIn' value to 'TRUE' in localStorage to simulate a logged-in user and set 'AdminFor' to simulate ADMIN of some Organization.
setItem('IsLoggedIn', 'TRUE');
setItem('AdminFor', [
diff --git a/src/components/UserPortal/StartPostModal/StartPostModal.test.tsx b/src/components/UserPortal/StartPostModal/StartPostModal.spec.tsx
similarity index 71%
rename from src/components/UserPortal/StartPostModal/StartPostModal.test.tsx
rename to src/components/UserPortal/StartPostModal/StartPostModal.spec.tsx
index c34f3a2e9e..2d9024bcd3 100644
--- a/src/components/UserPortal/StartPostModal/StartPostModal.test.tsx
+++ b/src/components/UserPortal/StartPostModal/StartPostModal.spec.tsx
@@ -12,12 +12,26 @@ import { store } from 'state/store';
import { StaticMockLink } from 'utils/StaticMockLink';
import i18nForTest from 'utils/i18nForTest';
import StartPostModal from './StartPostModal';
-
-jest.mock('react-toastify', () => ({
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for StartPostModal component:
+ *
+ * 1. **Rendering StartPostModal**: Verifies that the modal renders correctly when the `show` prop is set to `true`.
+ * 2. **Invalid post submission**: Ensures that when the post body is empty, an error toast is shown with the appropriate message ("Can't create a post with an empty body").
+ * 3. **Valid post submission**: Checks that a post with valid text triggers an info toast, and simulates the creation of a post with the message "Processing your post. Please wait."
+ * 4. **User image null**: Confirms that when the user image is null, a default image is displayed instead.
+ * 5. **User image not null**: Verifies that when the user image is provided, the correct user image is shown.
+ *
+ * Mocked GraphQL mutation (`CREATE_POST_MUTATION`) and toast notifications are used to simulate the post creation process.
+ * The `renderStartPostModal` function is used to render the modal with different user states and input values.
+ */
+
+vi.mock('react-toastify', () => ({
toast: {
- error: jest.fn(),
- info: jest.fn(),
- success: jest.fn(),
+ error: vi.fn(),
+ info: vi.fn(),
+ success: vi.fn(),
},
}));
@@ -62,8 +76,8 @@ const renderStartPostModal = (
): RenderResult => {
const cardProps = {
show: visibility,
- onHide: jest.fn(),
- fetchPosts: jest.fn(),
+ onHide: vi.fn(),
+ fetchPosts: vi.fn(),
userData: {
user: {
__typename: 'User',
@@ -113,18 +127,18 @@ const renderStartPostModal = (
describe('Testing StartPostModal Component: User Portal', () => {
afterAll(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
});
- test('Check if StartPostModal renders properly', async () => {
+ it('Check if StartPostModal renders properly', async () => {
renderStartPostModal(true, null);
const modal = await screen.findByTestId('startPostModal');
expect(modal).toBeInTheDocument();
});
- test('On invalid post submission with empty body Error toast should be shown', async () => {
- const toastSpy = jest.spyOn(toast, 'error');
+ it('On invalid post submission with empty body Error toast should be shown', async () => {
+ const toastSpy = vi.spyOn(toast, 'error');
renderStartPostModal(true, null);
await wait();
@@ -134,7 +148,7 @@ describe('Testing StartPostModal Component: User Portal', () => {
);
});
- test('On valid post submission Info toast should be shown', async () => {
+ it('On valid post submission Info toast should be shown', async () => {
renderStartPostModal(true, null);
await wait();
@@ -154,7 +168,7 @@ describe('Testing StartPostModal Component: User Portal', () => {
// );
});
- test('If user image is null then default image should be shown', async () => {
+ it('If user image is null then default image should be shown', async () => {
renderStartPostModal(true, null);
await wait();
@@ -165,7 +179,7 @@ describe('Testing StartPostModal Component: User Portal', () => {
);
});
- test('If user image is not null then user image should be shown', async () => {
+ it('If user image is not null then user image should be shown', async () => {
renderStartPostModal(true, 'image.png');
await wait();
diff --git a/src/components/UserPortal/UserNavbar/UserNavbar.test.tsx b/src/components/UserPortal/UserNavbar/UserNavbar.spec.tsx
similarity index 74%
rename from src/components/UserPortal/UserNavbar/UserNavbar.test.tsx
rename to src/components/UserPortal/UserNavbar/UserNavbar.spec.tsx
index 8c3447f25a..6173871dbb 100644
--- a/src/components/UserPortal/UserNavbar/UserNavbar.test.tsx
+++ b/src/components/UserPortal/UserNavbar/UserNavbar.spec.tsx
@@ -13,6 +13,22 @@ import UserNavbar from './UserNavbar';
import userEvent from '@testing-library/user-event';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
+/**
+ * Unit tests for UserNavbar component [User Portal]:
+ *
+ * 1. **Rendering UserNavbar**: Verifies that the `UserNavbar` component renders correctly.
+ * 2. **Switching language to English**: Ensures that clicking the language dropdown and selecting 'English' updates the language cookie to 'en'.
+ * 3. **Switching language to French**: Verifies that selecting 'French' updates the language cookie to 'fr'.
+ * 4. **Switching language to Hindi**: Confirms that choosing 'Hindi' updates the language cookie to 'hi'.
+ * 5. **Switching language to Spanish**: Ensures that selecting 'Spanish' sets the language cookie to 'sp'.
+ * 6. **Switching language to Chinese**: Verifies that selecting 'Chinese' changes the language cookie to 'zh'.
+ * 7. **Interacting with the dropdown menu**: Ensures the user can open the dropdown and see available options like 'Settings' and 'Logout'.
+ * 8. **Navigating to the 'Settings' page**: Confirms that clicking 'Settings' in the dropdown correctly navigates the user to the "/user/settings" page.
+ *
+ * The tests simulate interactions with the language dropdown and the user dropdown menu to ensure proper functionality of language switching and navigation.
+ * Mocked GraphQL mutation (`REVOKE_REFRESH_TOKEN`) and mock store are used to test the component in an isolated environment.
+ */
+
async function wait(ms = 100): Promise {
await act(() => {
return new Promise((resolve) => {
@@ -39,7 +55,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
});
});
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
render(
@@ -55,7 +71,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
await wait();
});
- test('The language is switched to English', async () => {
+ it('The language is switched to English', async () => {
render(
@@ -79,7 +95,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('en');
});
- test('The language is switched to fr', async () => {
+ it('The language is switched to fr', async () => {
render(
@@ -103,7 +119,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('fr');
});
- test('The language is switched to hi', async () => {
+ it('The language is switched to hi', async () => {
render(
@@ -127,7 +143,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('hi');
});
- test('The language is switched to sp', async () => {
+ it('The language is switched to sp', async () => {
render(
@@ -151,7 +167,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('sp');
});
- test('The language is switched to zh', async () => {
+ it('The language is switched to zh', async () => {
render(
@@ -175,7 +191,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
expect(cookies.get('i18next')).toBe('zh');
});
- test('User can see and interact with the dropdown menu', async () => {
+ it('User can see and interact with the dropdown menu', async () => {
render(
@@ -195,7 +211,7 @@ describe('Testing UserNavbar Component [User Portal]', () => {
expect(screen.getByTestId('logoutBtn')).toBeInTheDocument();
});
- test('User can navigate to the "Settings" page', async () => {
+ it('User can navigate to the "Settings" page', async () => {
render(
diff --git a/src/components/UserPortal/UserProfile/EventsAttendedByUser.test.tsx b/src/components/UserPortal/UserProfile/EventsAttendedByUser.spec.tsx
similarity index 76%
rename from src/components/UserPortal/UserProfile/EventsAttendedByUser.test.tsx
rename to src/components/UserPortal/UserProfile/EventsAttendedByUser.spec.tsx
index 82b173e399..dfdeb5523e 100644
--- a/src/components/UserPortal/UserProfile/EventsAttendedByUser.test.tsx
+++ b/src/components/UserPortal/UserProfile/EventsAttendedByUser.spec.tsx
@@ -4,6 +4,18 @@ import { EventsAttendedByUser } from './EventsAttendedByUser';
import { MockedProvider } from '@apollo/client/testing';
import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+/**
+ * Unit tests for EventsAttendedByUser component:
+ *
+ * 1. **Rendering with events**: Verifies that the component renders properly when the user has attended events.
+ * - It checks for the presence of a title ('eventAttended') and ensures the correct number of event cards are rendered (2 events in this case).
+ * 2. **Rendering without events**: Ensures that when the user has not attended any events, a message indicating no events attended is displayed.
+ * - It checks for the presence of a 'noeventsAttended' message and ensures no event cards are rendered.
+ *
+ * Mock GraphQL queries (using `MockedProvider`) simulate the fetching of event details.
+ * The tests check the proper handling of different user event attendance scenarios.
+ */
+
const mockT = (key: string, params?: Record): string => {
if (params) {
return Object.entries(params).reduce(
@@ -96,7 +108,7 @@ describe('EventsAttendedByUser Component', () => {
t: mockT,
};
- test('renders the component with events', () => {
+ it('renders the component with events', () => {
render(
@@ -107,7 +119,7 @@ describe('EventsAttendedByUser Component', () => {
expect(screen.getAllByTestId('usereventsCard')).toHaveLength(2);
});
- test('renders no events message when user has no events', () => {
+ it('renders no events message when user has no events', () => {
render(
diff --git a/src/components/UserPortal/UserProfile/UserAddressFields.test.tsx b/src/components/UserPortal/UserProfile/UserAddressFields.spec.tsx
similarity index 69%
rename from src/components/UserPortal/UserProfile/UserAddressFields.test.tsx
rename to src/components/UserPortal/UserProfile/UserAddressFields.spec.tsx
index 7aff734bc6..9c0a6609e4 100644
--- a/src/components/UserPortal/UserProfile/UserAddressFields.test.tsx
+++ b/src/components/UserPortal/UserProfile/UserAddressFields.spec.tsx
@@ -2,12 +2,25 @@ import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { UserAddressFields } from './UserAddressFields';
import { countryOptions } from 'utils/formEnumFields';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for UserAddressFields component:
+ *
+ * 1. **Rendering form fields**: Ensures address, state, and country fields are rendered.
+ * 2. **Displaying translated labels**: Verifies correct translations for labels.
+ * 3. **Handling input changes**: Tests if `handleFieldChange` is called with correct values for address, state, and country.
+ * 4. **Rendering country options**: Checks if all country options are displayed.
+ * 5. **Displaying initial values**: Ensures initial values (address, state, country) are correctly shown.
+ *
+ * `fireEvent` simulates user actions, and `vi.fn()` mocks callback functions.
+ */
describe('UserAddressFields', () => {
const mockProps = {
tCommon: (key: string) => `translated_${key}`,
t: (key: string) => `translated_${key}`,
- handleFieldChange: jest.fn(),
+ handleFieldChange: vi.fn(),
userDetails: {
address: '123 Test Street',
state: 'Test State',
@@ -16,10 +29,10 @@ describe('UserAddressFields', () => {
};
beforeEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
});
- test('renders all form fields correctly', () => {
+ it('renders all form fields correctly', () => {
render();
expect(screen.getByTestId('inputAddress')).toBeInTheDocument();
@@ -27,7 +40,7 @@ describe('UserAddressFields', () => {
expect(screen.getByTestId('inputCountry')).toBeInTheDocument();
});
- test('displays correct labels with translations', () => {
+ it('displays correct labels with translations', () => {
render();
expect(screen.getByText('translated_address')).toBeInTheDocument();
@@ -35,7 +48,7 @@ describe('UserAddressFields', () => {
expect(screen.getByText('translated_country')).toBeInTheDocument();
});
- test('handles address input change', () => {
+ it('handles address input change', () => {
render();
const addressInput = screen.getByTestId('inputAddress');
@@ -47,7 +60,7 @@ describe('UserAddressFields', () => {
);
});
- test('handles state input change', () => {
+ it('handles state input change', () => {
render();
const stateInput = screen.getByTestId('inputState');
@@ -59,7 +72,7 @@ describe('UserAddressFields', () => {
);
});
- test('handles country selection change', () => {
+ it('handles country selection change', () => {
render();
const countrySelect = screen.getByTestId('inputCountry');
@@ -68,7 +81,7 @@ describe('UserAddressFields', () => {
expect(mockProps.handleFieldChange).toHaveBeenCalledWith('country', 'CA');
});
- test('renders all country options', () => {
+ it('renders all country options', () => {
render();
const countrySelect = screen.getByTestId('inputCountry');
@@ -77,7 +90,7 @@ describe('UserAddressFields', () => {
expect(options.length).toBe(countryOptions.length + 1); // +1 for disabled option
});
- test('displays initial values correctly', () => {
+ it('displays initial values correctly', () => {
render();
expect(screen.getByTestId('inputAddress')).toHaveValue('123 Test Street');
diff --git a/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx b/src/components/UserPortal/UserSidebar/UserSidebar.spec.tsx
similarity index 86%
rename from src/components/UserPortal/UserSidebar/UserSidebar.test.tsx
rename to src/components/UserPortal/UserSidebar/UserSidebar.spec.tsx
index 79d603614f..ac6465a3e6 100644
--- a/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx
+++ b/src/components/UserPortal/UserSidebar/UserSidebar.spec.tsx
@@ -15,6 +15,25 @@ import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
import UserSidebar from './UserSidebar';
import useLocalStorage from 'utils/useLocalstorage';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for UserSidebar component:
+ *
+ * 1. **Rendering with user data**: Verifies correct rendering when user data is fetched.
+ * 2. **Logo and title**: Ensures logo, title, and left drawer are visible.
+ * 3. **Empty organizations list**: Tests rendering when the user has no joined organizations.
+ * 4. **Organization image rendering**: Verifies rendering when organizations have an image.
+ * 5. **User profile and links**: Ensures user details and links like 'My Organizations' and 'Settings' are visible.
+ * 6. **Responsive rendering**: Tests correct rendering and drawer toggle on smaller screens.
+ * 7. **Active button style**: Verifies button style changes when clicked.
+ * 8. **Translation display**: Ensures translated text is shown.
+ * 9. **Sidebar closure on mobile**: Verifies sidebar closes when a link is clicked on mobile view.
+ * 10. **Drawer visibility on small screens**: Tests drawer visibility toggle based on `hideDrawer` prop.
+ * 11. **Drawer state change**: Verifies drawer visibility changes when `hideDrawer` prop changes.
+ *
+ * `fireEvent` simulates user actions, and `vi.fn()` mocks callback functions.
+ */
const { setItem } = useLocalStorage();
@@ -27,7 +46,7 @@ const resizeWindow = (width: number): void => {
const props = {
hideDrawer: true,
- setHideDrawer: jest.fn(),
+ setHideDrawer: vi.fn(),
};
const MOCKS = [
@@ -365,10 +384,10 @@ const renderUserSidebar = (
describe('UserSidebar Component Tests in User Portal', () => {
beforeEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
});
- test('UserSidebar component renders correctly with user data present', async () => {
+ it('UserSidebar component renders correctly with user data present', async () => {
await act(async () => {
renderUserSidebar('properId', link);
await wait();
@@ -376,7 +395,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
expect(screen.getByText('Talawa User Portal')).toBeInTheDocument();
});
- test('Displays the logo and title text of the User Portal', async () => {
+ it('Displays the logo and title text of the User Portal', async () => {
await act(async () => {
renderUserSidebar('properId', link);
await wait();
@@ -385,7 +404,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
expect(screen.getByTestId('leftDrawerContainer')).toBeVisible();
});
- test('UserSidebar renders correctly when joinedOrganizations list is empty', async () => {
+ it('UserSidebar renders correctly when joinedOrganizations list is empty', async () => {
await act(async () => {
renderUserSidebar('orgEmpty', link);
await wait();
@@ -393,7 +412,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
expect(screen.getByText('My Organizations')).toBeInTheDocument();
});
- test('Renders UserSidebar component with organization image when present', async () => {
+ it('Renders UserSidebar component with organization image when present', async () => {
await act(async () => {
renderUserSidebar('imagePresent', link);
await wait();
@@ -401,7 +420,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
expect(screen.getByText('Settings')).toBeInTheDocument();
});
- test('User profile data renders with all expected navigation links visible', async () => {
+ it('User profile data renders with all expected navigation links visible', async () => {
await act(async () => {
renderUserSidebar('properId', link);
await wait();
@@ -413,7 +432,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
});
});
- test('UserSidebar renders correctly on smaller screens and toggles drawer visibility', async () => {
+ it('UserSidebar renders correctly on smaller screens and toggles drawer visibility', async () => {
await act(async () => {
resizeWindow(800);
render(
@@ -433,7 +452,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
expect(props.setHideDrawer).toHaveBeenCalledWith(true);
});
- test('Active route button style changes correctly upon click', async () => {
+ it('Active route button style changes correctly upon click', async () => {
await act(async () => {
renderUserSidebar('properId', link);
await wait();
@@ -448,7 +467,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
expect(settingsBtn).toHaveClass('text-white btn btn-success');
});
- test('Translation hook displays expected text in UserSidebar', async () => {
+ it('Translation hook displays expected text in UserSidebar', async () => {
await act(async () => {
renderUserSidebar('properId', link);
await wait();
@@ -458,7 +477,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
).toBeInTheDocument();
});
- test('handleLinkClick function closes the sidebar on mobile view when a link is clicked', async () => {
+ it('handleLinkClick function closes the sidebar on mobile view when a link is clicked', async () => {
resizeWindow(800);
await act(async () => {
renderUserSidebar('properId', link);
@@ -471,10 +490,10 @@ describe('UserSidebar Component Tests in User Portal', () => {
describe('UserSidebar Drawer Visibility Tests on Smaller Screens', () => {
beforeEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
});
- test('Clicking a link closes the drawer when window width is 820px or less', () => {
+ it('Clicking a link closes the drawer when window width is 820px or less', () => {
act(() => {
window.innerWidth = 820;
window.dispatchEvent(new Event('resize'));
@@ -499,7 +518,7 @@ describe('UserSidebar Component Tests in User Portal', () => {
});
describe('UserSidebar Drawer State Tests', () => {
- test('Drawer visibility changes based on hideDrawer prop', () => {
+ it('Drawer visibility changes based on hideDrawer prop', () => {
const { rerender } = render(
diff --git a/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.test.tsx b/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.spec.tsx
similarity index 86%
rename from src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.test.tsx
rename to src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.spec.tsx
index 2f28d9afd1..f26997f572 100644
--- a/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.test.tsx
+++ b/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.spec.tsx
@@ -1,10 +1,8 @@
import React, { act } from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import 'jest-localstorage-mock';
import { I18nextProvider } from 'react-i18next';
import { BrowserRouter } from 'react-router-dom';
-
import i18nForTest from 'utils/i18nForTest';
import type { InterfaceUserSidebarOrgProps } from './UserSidebarOrg';
import UserSidebarOrg from './UserSidebarOrg';
@@ -15,7 +13,24 @@ import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
import { StaticMockLink } from 'utils/StaticMockLink';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
import useLocalStorage from 'utils/useLocalstorage';
+import { vi } from 'vitest';
+/**
+ * Unit tests for UserSidebarOrg component:
+ *
+ * 1. **Rendering with organization data**: Verifies correct rendering when data is fetched.
+ * 2. **Profile Page & Modal**: Ensures profile button and organization details modal appear.
+ * 3. **Menu Navigation**: Tests correct navigation when menu buttons like 'People' are clicked.
+ * 4. **Responsive Design**: Verifies sidebar behavior on screens.
+ * 5. **Organization Image**: Ensures correct rendering of organization image.
+ * 6. **Empty Organizations**: Verifies error message when no organizations exist.
+ * 7. **Drawer Visibility**: Tests drawer visibility with `hideDrawer` prop values.
+ * 8. **User Profile Rendering**: Confirms user details are displayed.
+ * 9. **Translation Display**: Ensures proper translation of UI text.
+ * 10. **Toast Notifications Mocking**: Mocks toast notifications during tests.
+ *
+ * `fireEvent` simulates user actions, and `vi.fn()` mocks callback functions.
+ */
const { setItem } = useLocalStorage();
const props: InterfaceUserSidebarOrgProps = {
@@ -47,7 +62,7 @@ const props: InterfaceUserSidebarOrgProps = {
},
],
hideDrawer: false,
- setHideDrawer: jest.fn(),
+ setHideDrawer: vi.fn(),
};
const MOCKS = [
@@ -213,11 +228,11 @@ const defaultScreens = [
'All Organizations',
];
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
},
}));
@@ -244,7 +259,7 @@ beforeEach(() => {
});
afterEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
localStorage.clear();
});
@@ -253,7 +268,7 @@ const linkImage = new StaticMockLink(MOCKS_WITH_IMAGE, true);
const linkEmpty = new StaticMockLink(MOCKS_EMPTY, true);
describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
- test('Component should be rendered properly', async () => {
+ it('Component should be rendered properly', async () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
setItem('FirstName', 'John');
@@ -275,7 +290,7 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
});
});
- test('Testing Profile Page & Organization Detail Modal', async () => {
+ it('Testing Profile Page & Organization Detail Modal', async () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
setItem('FirstName', 'John');
@@ -295,7 +310,7 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
expect(screen.getByTestId(/orgBtn/i)).toBeInTheDocument();
});
- test('Testing Menu Buttons', async () => {
+ it('Testing Menu Buttons', async () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
setItem('FirstName', 'John');
@@ -316,7 +331,7 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
expect(global.window.location.pathname).toContain('/user/people/123');
});
- test('Testing when screen size is less than 820px', async () => {
+ it('Testing when screen size is less than 820px', async () => {
setItem('SuperAdmin', true);
render(
@@ -339,7 +354,7 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
expect(window.location.pathname).toContain('user/people/123');
});
- test('Testing when image is present for Organization', async () => {
+ it('Testing when image is present for Organization', async () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
setItem('FirstName', 'John');
@@ -358,7 +373,7 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
await wait();
});
- test('Testing when Organization does not exists', async () => {
+ it('Testing when Organization does not exists', async () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
setItem('FirstName', 'John');
@@ -380,7 +395,7 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
).toBeInTheDocument();
});
- test('Testing Drawer when hideDrawer is null', () => {
+ it('Testing Drawer when hideDrawer is null', () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
setItem('FirstName', 'John');
@@ -398,7 +413,7 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
);
});
- test('Testing Drawer when hideDrawer is true', () => {
+ it('Testing Drawer when hideDrawer is true', () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
setItem('FirstName', 'John');
diff --git a/src/components/Venues/VenueCard.tsx b/src/components/Venues/VenueCard.tsx
index 752ac95139..43da734bd6 100644
--- a/src/components/Venues/VenueCard.tsx
+++ b/src/components/Venues/VenueCard.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { Card, Button } from 'react-bootstrap';
import defaultImg from 'assets/images/defaultImg.png';
import PeopleIcon from 'assets/svgs/people.svg?react';
-import styles from 'screens/OrganizationVenues/OrganizationVenues.module.css';
+import styles from '../../style/app.module.css';
import { useTranslation } from 'react-i18next';
import type { InterfaceQueryVenueListItem } from 'utils/interfaces';
diff --git a/src/screens/BlockUser/BlockUser.test.tsx b/src/screens/BlockUser/BlockUser.spec.tsx
similarity index 52%
rename from src/screens/BlockUser/BlockUser.test.tsx
rename to src/screens/BlockUser/BlockUser.spec.tsx
index c851470d9b..51d16a61f9 100644
--- a/src/screens/BlockUser/BlockUser.test.tsx
+++ b/src/screens/BlockUser/BlockUser.spec.tsx
@@ -1,4 +1,4 @@
-import React, { act } from 'react';
+import React from 'react';
import { MockedProvider } from '@apollo/react-testing';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@@ -10,16 +10,15 @@ import {
BLOCK_PAGE_MEMBER_LIST,
ORGANIZATIONS_LIST,
} from 'GraphQl/Queries/Queries';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
-import { ToastContainer } from 'react-toastify';
import { store } from 'state/store';
import { StaticMockLink } from 'utils/StaticMockLink';
import i18nForTest from 'utils/i18nForTest';
import BlockUser from './BlockUser';
+import { vi, describe, beforeEach, test, expect } from 'vitest';
let userQueryCalled = false;
@@ -241,94 +240,22 @@ const MOCKS = [
},
},
];
-const MOCKS_EMPTY = [
- {
- request: {
- query: ORGANIZATIONS_LIST,
- variables: {
- id: 'orgid',
- },
- },
- result: {
- data: {
- organizations: [
- {
- _id: 'orgid',
- image: '',
- creator: {
- firstName: 'firstName',
- lastName: 'lastName',
- email: 'email',
- },
- name: 'name',
- description: 'description',
- location: 'location',
- members: {
- _id: 'id',
- firstName: 'firstName',
- lastName: 'lastName',
- email: 'email',
- },
- admins: {
- _id: 'id',
- firstName: 'firstName',
- lastName: 'lastName',
- email: 'email',
- },
- membershipRequests: {
- _id: 'id',
- user: {
- firstName: 'firstName',
- lastName: 'lastName',
- email: 'email',
- },
- },
- blockedUsers: {
- _id: 'id',
- firstName: 'firstName',
- lastName: 'lastName',
- email: 'email',
- },
- },
- ],
- },
- },
- },
-
- {
- request: {
- query: BLOCK_PAGE_MEMBER_LIST,
- variables: {
- firstName_contains: 'Peter',
- lastName_contains: '',
- orgId: 'orgid',
- },
- },
- result: {
- data: {
- organizationsMemberConnection: {
- edges: [],
- },
- },
- },
- },
-];
const link = new StaticMockLink(MOCKS, true);
-const link2 = new StaticMockLink(MOCKS_EMPTY, true);
async function wait(ms = 500): Promise {
- await act(() => {
- return new Promise((resolve) => {
- setTimeout(resolve, ms);
- });
+ await new Promise((resolve) => {
+ setTimeout(resolve, ms);
});
}
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useParams: () => ({ orgId: 'orgid' }),
-}));
+vi.mock('react-router-dom', async (importOriginal) => {
+ const actual = await importOriginal();
+ return {
+ ...(actual as object),
+ useParams: () => ({ orgId: 'orgid' }),
+ };
+});
describe('Testing Block/Unblock user screen', () => {
beforeEach(() => {
@@ -336,7 +263,7 @@ describe('Testing Block/Unblock user screen', () => {
});
test('Components should be rendered properly', async () => {
- window.location.assign('/blockuser/orgid');
+ window.history.pushState({}, 'Test page', '/blockuser/orgid');
render(
@@ -354,11 +281,11 @@ describe('Testing Block/Unblock user screen', () => {
expect(screen.getByText('Search By First Name')).toBeInTheDocument();
- expect(window.location).toBeAt('/blockuser/orgid');
+ expect(window.location.pathname).toBe('/blockuser/orgid');
});
test('Testing block user functionality', async () => {
- window.location.assign('/blockuser/orgid');
+ window.history.pushState({}, 'Test page', '/blockuser/orgid');
render(
@@ -372,9 +299,7 @@ describe('Testing Block/Unblock user screen', () => {
,
);
- await act(async () => {
- userEvent.click(screen.getByTestId('userFilter'));
- });
+ userEvent.click(screen.getByTestId('userFilter'));
userEvent.click(screen.getByTestId('showMembers'));
await wait();
@@ -387,11 +312,11 @@ describe('Testing Block/Unblock user screen', () => {
expect(screen.getByTestId('blockUser123')).toBeInTheDocument();
expect(screen.getByTestId('unBlockUser456')).toBeInTheDocument();
- expect(window.location).toBeAt('/blockuser/orgid');
+ expect(window.location.pathname).toBe('/blockuser/orgid');
});
test('Testing unblock user functionality', async () => {
- window.location.assign('/blockuser/orgid');
+ window.history.pushState({}, 'Test page', '/blockuser/orgid');
render(
@@ -404,9 +329,7 @@ describe('Testing Block/Unblock user screen', () => {
,
);
- await act(async () => {
- userEvent.click(screen.getByTestId('userFilter'));
- });
+ userEvent.click(screen.getByTestId('userFilter'));
userEvent.click(screen.getByTestId('showMembers'));
await wait();
@@ -420,11 +343,11 @@ describe('Testing Block/Unblock user screen', () => {
expect(screen.getByTestId('blockUser123')).toBeInTheDocument();
expect(screen.getByTestId('unBlockUser456')).toBeInTheDocument();
- expect(window.location).toBeAt('/blockuser/orgid');
+ expect(window.location.pathname).toBe('/blockuser/orgid');
});
test('Testing First Name Filter', async () => {
- window.location.assign('/blockuser/orgid');
+ window.history.pushState({}, 'Test page', '/blockuser/orgid');
render(
@@ -437,9 +360,7 @@ describe('Testing Block/Unblock user screen', () => {
,
);
- await act(async () => {
- userEvent.click(screen.getByTestId('userFilter'));
- });
+ userEvent.click(screen.getByTestId('userFilter'));
userEvent.click(screen.getByTestId('showMembers'));
await wait();
@@ -448,9 +369,7 @@ describe('Testing Block/Unblock user screen', () => {
expect(screen.getByText('Sam Smith')).toBeInTheDocument();
// Open Dropdown
- await act(async () => {
- userEvent.click(screen.getByTestId('nameFilter'));
- });
+ userEvent.click(screen.getByTestId('nameFilter'));
// Select option and enter first name
userEvent.click(screen.getByTestId('searchByFirstName'));
const firstNameInput = screen.getByPlaceholderText(/Search by First Name/i);
@@ -461,11 +380,11 @@ describe('Testing Block/Unblock user screen', () => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.queryByText('Sam Smith')).not.toBeInTheDocument();
- expect(window.location).toBeAt('/blockuser/orgid');
+ expect(window.location.pathname).toBe('/blockuser/orgid');
});
test('Testing Last Name Filter', async () => {
- window.location.assign('/blockuser/orgid');
+ window.history.pushState({}, 'Test page', '/blockuser/orgid');
render(
@@ -479,178 +398,6 @@ describe('Testing Block/Unblock user screen', () => {
,
);
- await act(async () => {
- userEvent.click(screen.getByTestId('userFilter'));
- });
- userEvent.click(screen.getByTestId('showMembers'));
-
- await wait();
-
- expect(screen.getByText('John Doe')).toBeInTheDocument();
- expect(screen.getByText('Sam Smith')).toBeInTheDocument();
-
- // Open Dropdown
- await act(async () => {
- userEvent.click(screen.getByTestId('nameFilter'));
- });
- // Select option and enter last name
- userEvent.click(screen.getByTestId('searchByLastName'));
- const lastNameInput = screen.getByPlaceholderText(/Search by Last Name/i);
- userEvent.type(lastNameInput, 'doe{enter}');
-
- await wait(700);
-
- expect(lastNameInput).toHaveValue('doe');
- expect(screen.getByText('John Doe')).toBeInTheDocument();
- expect(screen.queryByText('Sam Smith')).not.toBeInTheDocument();
- expect(window.location).toBeAt('/blockuser/orgid');
- });
-
- test('Testing No Spammers Present', async () => {
- window.location.assign('/blockuser/orgid');
- render(
-
-
-
-
-
-
-
-
- ,
- );
-
- await wait();
- expect(screen.getByText(/No spammer found/i)).toBeInTheDocument();
- expect(window.location).toBeAt('/blockuser/orgid');
- });
-
- test('Testing All Members', async () => {
- window.location.assign('/blockuser/orgid');
-
- render(
-
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
- await act(async () => {
- userEvent.click(screen.getByTestId('userFilter'));
- });
- userEvent.click(screen.getByTestId('showMembers'));
-
- await wait(700);
-
- expect(screen.getByTestId(/userFilter/i)).toHaveTextContent('All Members');
- expect(screen.getByText('John Doe')).toBeInTheDocument();
- expect(screen.getByText('Sam Smith')).toBeInTheDocument();
-
- expect(window.location).toBeAt('/blockuser/orgid');
- });
-
- test('Testing Blocked Users', async () => {
- window.location.assign('/blockuser/orgid');
-
- render(
-
-
-
-
-
-
-
-
-
- ,
- );
-
- await act(async () => {
- userEvent.click(screen.getByTestId('userFilter'));
- });
-
- userEvent.click(screen.getByTestId('showBlockedMembers'));
- await wait();
-
- expect(screen.getByText('John Doe')).toBeInTheDocument();
- expect(screen.queryByText('Sam Smith')).not.toBeInTheDocument();
-
- expect(window.location).toBeAt('/blockuser/orgid');
- });
-
- test('Testing table data getting rendered', async () => {
- window.location.assign('/orglist/orgid');
- const link = new StaticMockLink(MOCKS, true);
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await act(async () => {
- userEvent.click(screen.getByTestId('userFilter'));
- });
- userEvent.click(screen.getByTestId('showMembers'));
-
- await wait();
-
- expect(screen.getByTestId(/userList/)).toBeInTheDocument();
- expect(screen.getAllByText('Block/Unblock')).toHaveLength(1);
- expect(screen.getByText('John Doe')).toBeInTheDocument();
- expect(screen.getByText('Sam Smith')).toBeInTheDocument();
- });
-
- test('Testing No Results Found', async () => {
- window.location.assign('/blockuser/orgid');
- render(
-
-
-
-
-
-
-
-
- ,
- );
-
- const input = screen.getByPlaceholderText('Search By First Name');
- await act(async () => {
- userEvent.type(input, 'Peter{enter}');
- });
- await wait(700);
- expect(
- screen.getByText(`No results found for "Peter"`),
- ).toBeInTheDocument();
- expect(window.location).toBeAt('/blockuser/orgid');
- });
-
- test('Testing Search functionality', async () => {
- window.location.assign('/blockuser/orgid');
-
- render(
-
-
-
-
-
-
-
-
-
- ,
- );
await wait();
const searchBar = screen.getByTestId(/searchByName/i);
const searchBtn = screen.getByTestId(/searchBtn/i);
diff --git a/src/screens/OrganizationActionItems/ItemViewModal.tsx b/src/screens/OrganizationActionItems/ItemViewModal.tsx
index 98f2f6fd7f..97c962524f 100644
--- a/src/screens/OrganizationActionItems/ItemViewModal.tsx
+++ b/src/screens/OrganizationActionItems/ItemViewModal.tsx
@@ -146,7 +146,7 @@ const ItemViewModal: FC = ({ isOpen, hide, item }) => {
className={styles.TableImage}
/>
) : (
-
+
strong {
- line-height: 1.5rem;
- margin-left: -5px;
- font-family: sans-serif;
- font-size: 19px;
- color: #707070;
-}
-.mainpage {
- display: flex;
- flex-direction: row;
-}
-
-.sidebar:after {
- background-color: #f7f7f7;
- position: absolute;
- width: 2px;
- height: 600px;
- top: 10px;
- left: 94%;
- display: block;
-}
-
-.navitem {
- padding-left: 27%;
- padding-top: 12px;
- padding-bottom: 12px;
- cursor: pointer;
-}
-
-.logintitle {
- color: #707070;
- font-weight: 600;
- font-size: 20px;
- margin-bottom: 30px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 15%;
-}
-
-.logintitleadmin {
- color: #707070;
- font-weight: 600;
- font-size: 18px;
- margin-top: 50px;
- margin-bottom: 40px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 30%;
-}
-.admindetails {
- display: flex;
- justify-content: space-between;
-}
-.admindetails > p {
- margin-top: -12px;
- margin-right: 30px;
-}
-
-.mainpageright > hr {
- margin-top: 20px;
- width: 100%;
- margin-left: -15px;
- margin-right: -15px;
- margin-bottom: 20px;
-}
-.justifysp {
- display: flex;
- justify-content: space-between;
- margin-top: 20px;
-}
-
-.addbtn {
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
- border-radius: 5px;
- font-size: 16px;
- height: 60%;
- color: white;
- outline: none;
- font-weight: 600;
- cursor: pointer;
- transition:
- transform 0.2s,
- box-shadow 0.2s;
-}
-.flexdir {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- border: none;
-}
-
-.form_wrapper {
- margin-top: 27px;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- position: absolute;
- display: flex;
- flex-direction: column;
- padding: 40px 30px;
- background: #ffffff;
- border-color: #e8e5e5;
- border-width: 5px;
- border-radius: 10px;
- max-height: 86vh;
- overflow: auto;
-}
-
-.form_wrapper form {
- display: flex;
- align-items: left;
- justify-content: left;
- flex-direction: column;
-}
-.logintitleinvite {
- color: #707070;
- font-weight: 600;
- font-size: 20px;
- margin-bottom: 20px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 40%;
-}
-.titlemodal {
- color: #707070;
- font-weight: 600;
- font-size: 20px;
- margin-bottom: 20px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 65%;
-}
-.cancel > i {
- margin-top: 5px;
- transform: scale(1.2);
- cursor: pointer;
- color: #707070;
-}
-.modalbody {
- width: 50px;
-}
-.greenregbtn {
- margin: 1rem 0 0;
- margin-top: 15px;
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
- padding: 10px 10px;
- border-radius: 5px;
- background-color: #31bb6b;
- width: 100%;
- font-size: 16px;
- color: white;
- outline: none;
- font-weight: 600;
- cursor: pointer;
- transition:
- transform 0.2s,
- box-shadow 0.2s;
- width: 100%;
-}
-
-.datediv {
- display: flex;
- flex-direction: row;
- margin-bottom: 15px;
-}
-.datebox {
- width: 90%;
- border-radius: 7px;
- border-color: #e8e5e5;
- outline: none;
- box-shadow: none;
- padding-top: 2px;
- padding-bottom: 2px;
- padding-right: 5px;
- padding-left: 5px;
- margin-right: 5px;
- margin-left: 5px;
-}
-.checkboxdiv > label {
- margin-right: 50px;
-}
-.checkboxdiv > label > input {
- margin-left: 10px;
-}
-.loader,
-.loader:after {
- border-radius: 50%;
- width: 10em;
- height: 10em;
-}
-.loader {
- margin: 60px auto;
- margin-top: 35vh !important;
- font-size: 10px;
- position: relative;
- text-indent: -9999em;
- border-top: 1.1em solid rgba(255, 255, 255, 0.2);
- border-right: 1.1em solid rgba(255, 255, 255, 0.2);
- border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
- border-left: 1.1em solid #febc59;
- -webkit-transform: translateZ(0);
- -ms-transform: translateZ(0);
- transform: translateZ(0);
- -webkit-animation: load8 1.1s infinite linear;
- animation: load8 1.1s infinite linear;
-}
-@-webkit-keyframes load8 {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg);
- }
-}
-@keyframes load8 {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg);
- }
-}
-.dispflex {
- display: flex;
- align-items: center;
-}
-.dispflex > input {
- border: none;
- box-shadow: none;
- margin-top: 5px;
-}
-.checkboxdiv {
- display: flex;
-}
-.checkboxdiv > div {
- width: 50%;
-}
-
-@media only screen and (max-width: 600px) {
- .form_wrapper {
- width: 90%;
- top: 45%;
- }
-}
-
-.navbarbg {
- height: 60px;
- background-color: white;
- display: flex;
- margin-bottom: 30px;
- z-index: 1;
- position: relative;
- flex-direction: row;
- justify-content: space-between;
- box-shadow: 0px 0px 8px 2px #c8c8c8;
-}
-
-.logo {
- color: #707070;
- margin-left: 0;
- display: flex;
- align-items: center;
- text-decoration: none;
-}
-
-.logo img {
- margin-top: 0px;
- margin-left: 10px;
- height: 64px;
- width: 70px;
-}
-
-.logo > strong {
- line-height: 1.5rem;
- margin-left: -5px;
- font-family: sans-serif;
- font-size: 19px;
- color: #707070;
-}
-.mainpage {
- display: flex;
- flex-direction: row;
-}
-.sidebar {
- display: flex;
- width: 100%;
- justify-content: space-between;
- z-index: 0;
- padding-top: 10px;
- margin: 0;
- height: 100%;
-}
-
-.navitem {
- padding-left: 27%;
- padding-top: 12px;
- padding-bottom: 12px;
- cursor: pointer;
-}
-
-.logintitle {
- color: #707070;
- font-weight: 600;
- font-size: 20px;
- margin-bottom: 30px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 15%;
-}
-.searchtitle {
- color: #707070;
- font-weight: 600;
- font-size: 20px;
- margin-bottom: 20px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 60%;
-}
-.justifysp {
- display: flex;
- justify-content: space-between;
-}
-@media screen and (max-width: 575.5px) {
- .justifysp {
- padding-left: 55px;
- display: flex;
- justify-content: space-between;
- width: 100%;
- }
- .mainpageright {
- width: 98%;
- }
-}
-
-.logintitleadmin {
- color: #707070;
- font-weight: 600;
- font-size: 18px;
- margin-top: 50px;
- margin-bottom: 40px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 40%;
-}
-.admindetails {
- display: flex;
- justify-content: space-between;
-}
-.admindetails > p {
- margin-top: -12px;
- margin-right: 30px;
-}
-.mainpageright > hr {
- margin-top: 10px;
- width: 97%;
- margin-left: -15px;
- margin-right: -15px;
- margin-bottom: 20px;
-}
-.addbtnmain {
- width: 60%;
- margin-right: 50px;
-}
-.addbtn {
- float: right;
- width: 23%;
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
- border-radius: 5px;
- background-color: #31bb6b;
- height: 40px;
- font-size: 16px;
- color: white;
- outline: none;
- font-weight: 600;
- cursor: pointer;
- margin-left: 30px;
- transition:
- transform 0.2s,
- box-shadow 0.2s;
-}
-.flexdir {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- border: none;
-}
-
-.form_wrapper {
- margin-top: 27px;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- position: absolute;
- display: flex;
- flex-direction: column;
- padding: 40px 30px;
- background: #ffffff;
- border-color: #e8e5e5;
- border-width: 5px;
- border-radius: 10px;
-}
-
-.form_wrapper form {
- display: flex;
- align-items: left;
- justify-content: left;
- flex-direction: column;
-}
-.logintitleinvite {
- color: #707070;
- font-weight: 600;
- font-size: 20px;
- margin-bottom: 20px;
- padding-bottom: 5px;
- border-bottom: 3px solid #31bb6b;
- width: 40%;
-}
-.cancel > i {
- margin-top: 5px;
- transform: scale(1.2);
- cursor: pointer;
- color: #707070;
-}
-.modalbody {
- width: 50px;
-}
-.greenregbtn {
- margin: 1rem 0 0;
- margin-top: 10px;
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
- padding: 10px 10px;
- border-radius: 5px;
- background-color: #31bb6b;
- width: 100%;
- font-size: 16px;
- color: white;
- outline: none;
- font-weight: 600;
- cursor: pointer;
- transition:
- transform 0.2s,
- box-shadow 0.2s;
- width: 100%;
-}
-.dropdown {
- background-color: white;
- border: 1px solid #31bb6b;
- position: relative;
- display: inline-block;
- margin-top: 10px;
- margin-bottom: 10px;
- color: #31bb6b;
-}
-.input {
- flex: 1;
- position: relative;
-}
-/* .btnsContainer {
- display: flex;
- margin: 2.5rem 0 2.5rem 0;
- width: 100%;
- flex-direction: row;
- justify-content: space-between;
- } */
-
-.btnsContainer {
- display: flex;
- margin: 2.5rem 0 2.5rem 0;
-}
-
-.btnsContainer .input {
- flex: 1;
- position: relative;
- min-width: 18rem;
- width: 25rem;
-}
-
-.btnsContainer .input button {
- width: 52px;
-}
-.searchBtn {
- margin-bottom: 10px;
-}
-
-.inputField {
- margin-top: 10px;
- margin-bottom: 10px;
- background-color: white;
- box-shadow: 0 1px 1px #31bb6b;
-}
-.inputField > button {
- padding-top: 10px;
- padding-bottom: 10px;
-}
-.TableImage {
- background-color: #31bb6b !important;
- width: 50px !important;
- height: 50px !important;
- border-radius: 100% !important;
- margin-right: 10px !important;
-}
-.tableHead {
- background-color: #31bb6b !important;
- color: white;
- border-radius: 20px !important;
- padding: 20px;
- margin-top: 20px;
-}
-
-.tableHead :nth-first-child() {
- border-top-left-radius: 20px;
-}
-
-.mainpageright > hr {
- margin-top: 10px;
- width: 100%;
- margin-left: -15px;
- margin-right: -15px;
- margin-bottom: 20px;
-}
-
-.loader,
-.loader:after {
- border-radius: 50%;
- width: 10em;
- height: 10em;
-}
-.loader {
- margin: 60px auto;
- margin-top: 35vh !important;
- font-size: 10px;
- position: relative;
- text-indent: -9999em;
- border-top: 1.1em solid rgba(255, 255, 255, 0.2);
- border-right: 1.1em solid rgba(255, 255, 255, 0.2);
- border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
- border-left: 1.1em solid #febc59;
- -webkit-transform: translateZ(0);
- -ms-transform: translateZ(0);
- transform: translateZ(0);
- -webkit-animation: load8 1.1s infinite linear;
- animation: load8 1.1s infinite linear;
-}
-.radio_buttons {
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- color: #707070;
- font-weight: 600;
- font-size: 14px;
-}
-.radio_buttons > input {
- transform: scale(1.2);
-}
-.radio_buttons > label {
- margin-top: -4px;
- margin-left: 5px;
- margin-right: 15px;
-}
-.preview {
- display: flex;
- position: relative;
- width: 100%;
- margin-top: 10px;
- justify-content: center;
-}
-.preview img {
- width: 400px;
- height: auto;
-}
-.postimage {
- border-radius: 0px;
- width: 100%;
- height: 12rem;
- max-width: 100%;
- max-height: 12rem;
- object-fit: cover;
- position: relative;
- color: black;
-}
-.closeButtonP {
- position: absolute;
- top: 0px;
- right: 0px;
- background: transparent;
- transform: scale(1.2);
- cursor: pointer;
- border: none;
- color: #707070;
- font-weight: 600;
- font-size: 16px;
- cursor: pointer;
-}
-.addbtn {
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
- border-radius: 5px;
- font-size: 16px;
- height: 60%;
- color: white;
- outline: none;
- font-weight: 600;
- cursor: pointer;
- transition:
- transform 0.2s,
- box-shadow 0.2s;
-}
-@-webkit-keyframes load8 {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg);
- }
-}
-@keyframes load8 {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg);
- }
-}
-.list_box {
- height: 65vh;
- overflow-y: auto;
- width: auto;
-}
-
-.cards h2 {
- font-size: 20px;
-}
-.cards > h3 {
- font-size: 17px;
-}
-.card {
- width: 100%;
- height: 20rem;
- margin-bottom: 2rem;
-}
-.postimage {
- border-radius: 0px;
- width: 100%;
- height: 12rem;
- max-width: 100%;
- max-height: 12rem;
- object-fit: cover;
- position: relative;
- color: black;
-}
-.preview {
- display: flex;
- position: relative;
- width: 100%;
- margin-top: 10px;
- justify-content: center;
-}
-.preview img {
- width: 400px;
- height: auto;
-}
-.preview video {
- width: 400px;
- height: auto;
-}
-.novenueimage {
- border-radius: 0px;
- width: 100%;
- height: 12rem;
- max-height: 12rem;
- object-fit: cover;
- position: relative;
-}
-.cards:hover {
- filter: brightness(0.8);
-}
-.cards:hover::before {
- opacity: 0.5;
-}
-.knowMoreText {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- opacity: 0;
- color: white;
- padding: 10px;
- font-weight: bold;
- font-size: 1.5rem;
- transition: opacity 0.3s ease-in-out;
-}
-
-.cards:hover .knowMoreText {
- opacity: 1;
-}
-.modal {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: rgba(
- 0,
- 0,
- 0,
- 0.9
- ); /* Dark grey modal background with transparency */
- z-index: 9999;
-}
-
-.modalContent {
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: #fff;
- padding: 20px;
- max-width: 800px;
- max-height: 600px;
- overflow: auto;
-}
-
-.modalImage {
- flex: 1;
- margin-right: 20px;
- width: 25rem;
- height: 15rem;
-}
-.nomodalImage {
- flex: 1;
- margin-right: 20px;
- width: 100%;
- height: 15rem;
-}
-
-.modalImage img,
-.modalImage video {
- border-radius: 0px;
- width: 100%;
- height: 25rem;
- max-width: 25rem;
- max-height: 15rem;
- object-fit: cover;
- position: relative;
-}
-.modalInfo {
- flex: 1;
-}
-.title {
- font-size: 16px;
- color: #000;
- font-weight: 600;
-}
-.text {
- font-size: 13px;
- color: #000;
- font-weight: 300;
-}
-.closeButton {
- position: relative;
- bottom: 5rem;
- right: 10px;
- padding: 4px;
- background-color: red; /* Red close button color */
- color: #fff;
- border: none;
- cursor: pointer;
-}
-.closeButtonP {
- position: absolute;
- top: 0px;
- right: 0px;
- background: transparent;
- transform: scale(1.2);
- cursor: pointer;
- border: none;
- color: #707070;
- font-weight: 600;
- font-size: 16px;
- cursor: pointer;
-}
-.cards:hover::after {
- opacity: 1;
- mix-blend-mode: normal;
-}
-.cards > p {
- font-size: 14px;
- margin-top: 0px;
- margin-bottom: 7px;
-}
-
-.cards:last-child:nth-last-child(odd) {
- grid-column: auto / span 2;
-}
-.cards:first-child:nth-last-child(even),
-.cards:first-child:nth-last-child(even) ~ .box {
- grid-column: auto / span 1;
-}
-
-.capacityLabel {
- background-color: #31bb6b !important;
- color: white;
- height: 22.19px;
- font-size: 12px;
- font-weight: bolder;
- padding: 0.1rem 0.3rem;
- border-radius: 0.5rem;
- position: relative;
- overflow: hidden;
-}
-
-.capacityLabel svg {
- margin-bottom: 3px;
-}
-
-::-webkit-scrollbar {
- width: 20px;
-}
-
-::-webkit-scrollbar-track {
- background-color: transparent;
-}
-
-::-webkit-scrollbar-thumb {
- background-color: #d6dee1;
-}
-
-::-webkit-scrollbar-thumb {
- background-color: #d6dee1;
- border-radius: 20px;
-}
-
-::-webkit-scrollbar-thumb {
- background-color: #d6dee1;
- border-radius: 20px;
- border: 6px solid transparent;
- background-clip: content-box;
-}
diff --git a/src/screens/UserPortal/Volunteer/VolunteerManagement.test.tsx b/src/screens/UserPortal/Volunteer/VolunteerManagement.test.tsx
index 65d2d082a7..80ac0df833 100644
--- a/src/screens/UserPortal/Volunteer/VolunteerManagement.test.tsx
+++ b/src/screens/UserPortal/Volunteer/VolunteerManagement.test.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import type { RenderResult } from '@testing-library/react';
-import { render, screen, waitFor } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/dom';
+import { render } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { I18nextProvider } from 'react-i18next';
import i18n from 'utils/i18nForTest';
diff --git a/src/style/app.module.css b/src/style/app.module.css
index 884a2d15e3..2c786e152e 100644
--- a/src/style/app.module.css
+++ b/src/style/app.module.css
@@ -2,6 +2,7 @@
--brown-color: #555555;
--dropdown-hover-color: #eff1f7;
--grey-bg-color: #eaebef;
+ --grey-border-box-color: #e8e5e5;
--subtle-blue-grey: #7c9beb;
--subtle-blue-grey-hover: #5f7e91;
--modal-width: 670px;
@@ -15,13 +16,13 @@
--table-image-small-size: 25px;
--bs-primary: #0056b3;
--bs-white: #fff;
- --table-head-bg: var(--bs-primary, blue);
--table-head-bg: var(
--bs-primary,
- blue --loader-size: 10em; --loader-border-width: 1.1em; --loader-color:
- #febc59;
+ blue
); /* Assuming var(--bs-primary) is defined elsewhere */
-
+ --loader-size: 10em;
+ --loader-border-width: 1.1em;
+ --loader-color: #febc59;
--table-head-color: white;
--table-header-color: var(--bs-greyish-black, black);
--table-head-radius: 20px;
@@ -38,17 +39,6 @@
font-weight: 600;
}
-.cards {
- width: 45%;
- background: #fcfcfc;
- margin: 10px 20px;
- padding: 20px 30px;
- border-radius: 5px;
- border: 1px solid #e8e8e8;
- box-shadow: 0 3px 5px #c9c9c9;
- margin-right: 40px;
- color: #737373;
-}
.cards > h2 {
font-size: 19px;
}
@@ -61,6 +51,43 @@
margin-bottom: 7px;
}
+.cards:hover {
+ filter: brightness(0.8);
+}
+.cards:hover::before {
+ opacity: 0.5;
+}
+
+.cards:hover::after {
+ opacity: 1;
+ mix-blend-mode: normal;
+}
+
+.cards:last-child:nth-last-child(odd) {
+ grid-column: auto / span 2;
+}
+
+.cards:first-child:nth-last-child(even),
+.cards:first-child:nth-last-child(even) ~ .box {
+ grid-column: auto / span 1;
+}
+
+.capacityLabel {
+ background-color: var(--bs-primary);
+ color: white;
+ height: 22.19px;
+ font-size: 12px;
+ font-weight: bolder;
+ padding: 0.1rem 0.3rem;
+ border-radius: 0.5rem;
+ position: relative;
+ overflow: hidden;
+}
+
+.capacityLabel svg {
+ margin-bottom: 3px;
+}
+
.sidebar {
z-index: 0;
padding-top: 5px;
@@ -139,7 +166,7 @@
.sidebarsticky > input {
text-decoration: none;
margin-bottom: 50px;
- border-color: #e8e5e5;
+ border-color: var(--grey-border-box-color);
width: 80%;
border-radius: 7px;
padding-top: 5px;
@@ -169,7 +196,7 @@
}
.dropdown {
- background-color: white;
+ background-color: var(--bs-white);
border: 1px solid var(--brown-color);
color: var(--brown-color);
position: relative;
@@ -184,19 +211,28 @@
color: var(--brown-color) !important;
}
+.dropdown:is(:focus, :focus-visible) {
+ outline: 2px solid var(--highlight-color, #a8c7fa);
+}
+
.dropdownItem {
- background-color: white !important;
+ background-color: var(--bs-white) !important;
color: var(--brown-color) !important;
border: none !important;
}
+.dropdownItem:focus,
+.dropdownItem:hover {
+ outline: 2px solid var(--highlight-color, #a8c7fa);
+}
+
.dropdownItem:hover,
.dropdownItem:focus,
.dropdownItem:active {
- background-color: var(--dropdown-hover-color) !important;
+ background-color: var(--dropdown-hover-color, #e0e0e0) !important;
color: var(--brown-color) !important;
outline: none !important;
- box-shadow: none !important;
+ box-shadow: 0 0 4px var(--highlight-color, #a8c7fa);
}
.input {
@@ -297,13 +333,13 @@
.inputField {
margin-top: 10px;
margin-bottom: 10px;
- background-color: white;
+ background-color: var(--bs-white);
box-shadow: 0 1px 1px var(--input-shadow-color);
}
.inputFieldModal {
margin-bottom: 10px;
- background-color: white;
+ background-color: var(--bs-white);
box-shadow: 0 1px 1px var(--input-shadow-color);
}
@@ -325,11 +361,19 @@
align-items: center;
}
+.searchButton:hover {
+ background-color: var(--search-button-hover-bg, #286fe0);
+ border-color: var(--search-button-border);
+}
+
+.searchButton:active {
+ transform: scale(0.95);
+}
+
.addButton {
margin-bottom: 10px;
background-color: var(--search-button-bg);
border-color: var(--grey-bg-color);
- color: #555555;
}
.addButton:hover {
@@ -402,6 +446,20 @@
}
}
+.tag-template-name {
+ position: absolute;
+ left: 14.91px;
+ top: 27.03px;
+ width: 58.55px;
+ height: 5.67px;
+ text-align: center;
+ font-family: 'Roboto', sans-serif;
+ font-size: 16px;
+ letter-spacing: 0;
+ line-height: 1;
+ color: #08780b;
+}
+
.subTagsLink i {
visibility: hidden;
}
@@ -595,7 +653,7 @@ hr {
.greenregbtnPledge {
margin-top: 15px;
border: 1px solid var(--bs-gray-300);
- box-shadow: 0 2px 2px #e8e5e5;
+ box-shadow: 0 2px 2px var(--grey-border-box-color);
padding: 10px 10px;
border-radius: 5px;
background-color: var(--bs-primary);
@@ -631,12 +689,12 @@ hr {
}
.inputFieldPledge {
- background-color: white;
+ background-color: var(--bs-white);
box-shadow: 0 1px 1px #31bb6b;
}
.dropdownPledge {
- background-color: white;
+ background-color: var(--bs-white);
border: 1px solid var(--bs-primary);
position: relative;
display: inline-block;
@@ -770,7 +828,7 @@ hr {
margin-top: 0.5rem;
padding: 0.75rem;
border: 1px solid #e2e8f0;
- background-color: white;
+ background-color: var(--bs-white);
color: #1e293b;
box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 0.15);
display: flex;
@@ -1043,7 +1101,7 @@ hr {
.customcell {
background-color: #31bb6b !important;
- color: white !important;
+ color: var(--bs-white) !important;
font-size: medium !important;
font-weight: 500 !important;
padding-top: 10px !important;
@@ -1090,13 +1148,13 @@ hr {
margin: 1rem 0 0;
margin-top: 15px;
border: 1px solid var(--bs-gray-300);
- box-shadow: 0 2px 2px #e8e5e5;
+ box-shadow: 0 2px 2px var(--grey-border-box-color);
padding: 10px 10px;
border-radius: 5px;
background-color: var(--bs-primary);
width: 100%;
font-size: 16px;
- color: white;
+ color: var(--bs-white);
outline: none;
font-weight: 600;
cursor: pointer;
@@ -1107,7 +1165,7 @@ hr {
}
.goalButtonOrganizationFundCampaign {
- border: 1px solid #e8e5e5 !important;
+ border: 1px solid var(--grey-border-box-color) !important;
color: #707070 !important;
width: 75%;
padding: 10px;
@@ -1120,13 +1178,13 @@ hr {
.redregbtn {
margin: 1rem 0 0;
margin-top: 15px;
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
+ border: 1px solid var(--grey-border-box-color);
+ box-shadow: 0 2px 2px var(--grey-border-box-color);
padding: 10px 10px;
border-radius: 5px;
width: 100%;
font-size: 16px;
- color: white;
+ color: var(--bs-white);
outline: none;
font-weight: 600;
cursor: pointer;
@@ -1147,13 +1205,13 @@ hr {
}
.inputFieldOrganizationFundCampaign {
- background-color: white;
+ background-color: var(--bs-white);
box-shadow: 0 1px 1px var(--search-button-bg);
}
.dropdownOrganizationFundCampaign {
- background-color: white;
- border: 1px solid #e8e5e5;
+ background-color: var(--bs-white);
+ border: 1px solid var(--grey-border-box-color);
position: relative;
display: inline-block;
color: #707070;
@@ -1546,12 +1604,12 @@ input[type='radio']:checked + label:hover {
.manageBtn {
margin: 1rem 0 0;
margin-top: 15px;
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
+ border: 1px solid var(--grey-border-box-color);
+ box-shadow: 0 2px 2px var(--grey-border-box-color);
padding: 10px 10px;
border-radius: 5px;
font-size: 16px;
- color: white;
+ color: var(--bs-white);
outline: none;
font-weight: 600;
cursor: pointer;
@@ -1614,7 +1672,7 @@ input[type='radio']:checked + label:hover {
}
.dropdowns {
- background-color: white;
+ background-color: var(--bs-white);
border: 1px solid #31bb6b;
position: relative;
display: inline-block;
@@ -1712,7 +1770,7 @@ input[type='radio']:checked + label:hover {
.pluginStoreBtn {
width: 100%;
- background-color: white;
+ background-color: var(--bs-white);
color: var(--brown-color);
border: 0.5px solid var(--brown-color);
}
@@ -1881,7 +1939,7 @@ input[type='radio']:checked + label:hover {
.sidebarstickyMemberDetail > input {
text-decoration: none;
margin-bottom: 50px;
- border-color: #e8e5e5;
+ border-color: var(--grey-border-box-color);
width: 80%;
border-radius: 7px;
padding-top: 5px;
@@ -1954,12 +2012,12 @@ input[type='radio']:checked + label:hover {
}
.invitebtn {
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
+ border: 1px solid var(--grey-border-box-color);
+ box-shadow: 0 2px 2px var(--grey-border-box-color);
border-radius: 5px;
font-size: 16px;
height: 60%;
- color: white;
+ color: var(--bs-white);
outline: none;
font-weight: 600;
cursor: pointer;
@@ -1988,7 +2046,7 @@ input[type='radio']:checked + label:hover {
width: 30%;
padding: 40px 30px;
background: #ffffff;
- border-color: #e8e5e5;
+ border-color: var(--grey-border-box-color);
border-width: 5px;
border-radius: 10px;
max-height: 86vh;
@@ -2040,13 +2098,13 @@ input[type='radio']:checked + label:hover {
.greenregbtnMemberDetail {
margin: 1rem 0 0;
margin-top: 10px;
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
+ border: 1px solid var(--grey-border-box-color);
+ box-shadow: 0 2px 2px var(--grey-border-box-color);
padding: 10px 10px;
border-radius: 5px;
background-color: #eaebef;
font-size: 16px;
- color: white;
+ color: var(--bs-white);
outline: none;
font-weight: 600;
cursor: pointer;
@@ -2062,7 +2120,7 @@ input[type='radio']:checked + label:hover {
border: 1px solid #eaebef;
padding: 10px 10px;
border-radius: 5px;
- background-color: white;
+ background-color: var(--bs-white);
font-size: 16px;
color: #707070;
outline: none;
@@ -2148,9 +2206,9 @@ input[type='radio']:checked + label:hover {
.memberfontcreatedbtn {
border-radius: 7px;
- border-color: #e8e5e5;
+ border-color: var(--grey-border-box-color);
background-color: #eaebef;
- color: white;
+ color: var(--bs-white);
box-shadow: none;
height: 2.5rem;
width: max-content;
@@ -2302,7 +2360,7 @@ input[type='radio']:checked + label:hover {
.dateboxMemberDetail {
border-radius: 7px;
- border-color: #e8e5e5;
+ border-color: var(--grey-border-box-color);
outline: none;
box-shadow: none;
padding-top: 2px;
@@ -2337,7 +2395,7 @@ input[type='radio']:checked + label:hover {
input::file-selector-button {
background-color: black;
- color: white;
+ color: var(--bs-white);
}
.Outline {
@@ -2520,8 +2578,8 @@ form label {
form > input {
display: block;
margin-bottom: 20px;
- border: 1px solid #e8e5e5;
- box-shadow: 2px 1px #e8e5e5;
+ border: 1px solid var(--grey-border-box-color);
+ box-shadow: 2px 1px var(--grey-border-box-color);
padding: 10px 20px;
border-radius: 5px;
background: none;
@@ -2683,7 +2741,7 @@ form > input {
background-color: var(--dropdown-border-color);
border-color: var(--dropdown-border-color);
margin-top: 1rem;
- color: white;
+ color: var(--bs-white);
margin-bottom: 1rem;
width: 100%;
transition: background-color 0.2s ease;
@@ -3050,13 +3108,13 @@ form > input {
}
}
.addbtnOrgPost {
- border: 1px solid #e8e5e5;
- box-shadow: 0 2px 2px #e8e5e5;
+ border: 1px solid var(--grey-border-box-color);
+ box-shadow: 0 2px 2px var(--grey-border-box-color);
border-radius: 5px;
font-size: 16px;
height: 60%;
width: 60%;
- color: white;
+ color: var(--bs-white);
outline: none;
font-weight: 600;
cursor: pointer;
@@ -3502,3 +3560,40 @@ button[data-testid='createPostBtn'] {
opacity: 1;
}
}
+
+.attendance-modal .borderRightGreen {
+ border-right: 1px solid green;
+}
+.attendance-modal .positionedTopRight {
+ top: 10px;
+ right: 15px;
+ z-index: 1;
+}
+.attendance-modal .topRightCorner {
+ position: absolute;
+ right: 0;
+ top: 0;
+ border-bottom-left-radius: 8px;
+}
+.attendance-modal .bottomRightCorner {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ border-top-left-radius: 12px;
+}
+.attendance-modal .topLeftCorner {
+ position: absolute;
+ left: 0;
+ top: 0;
+ border-bottom-right-radius: 8px;
+}
+.attendance-modal .largeBoldText {
+ font-size: 80px;
+ font-weight: 400;
+}
+.attendance-modal .paddingBottom30 {
+ padding-bottom: 30px;
+}
+.attendance-modal .paddingBottom2Rem {
+ padding-bottom: 2rem;
+}
diff --git a/src/utils/useSession.spec.tsx b/src/utils/useSession.spec.tsx
new file mode 100644
index 0000000000..9b50039ba2
--- /dev/null
+++ b/src/utils/useSession.spec.tsx
@@ -0,0 +1,688 @@
+import type { ReactNode } from 'react';
+import React from 'react';
+import { renderHook } from '@testing-library/react';
+import { MockedProvider } from '@apollo/client/testing';
+import { toast } from 'react-toastify';
+import { describe, beforeEach, afterEach, test, expect, vi } from 'vitest';
+import useSession from './useSession';
+import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
+import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
+import { errorHandler } from 'utils/errorHandler';
+import { BrowserRouter } from 'react-router-dom';
+
+vi.mock('react-toastify', () => ({
+ toast: {
+ info: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
+ },
+}));
+
+vi.mock('utils/errorHandler', () => ({
+ errorHandler: vi.fn(),
+}));
+
+vi.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: {
+ timeout: 30,
+ },
+ },
+ },
+ delay: 100,
+ },
+ {
+ request: {
+ query: REVOKE_REFRESH_TOKEN,
+ },
+ result: {
+ data: {
+ revokeRefreshTokenForUser: true,
+ },
+ },
+ },
+];
+describe('useSession Hook', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ vi.spyOn(window, 'addEventListener').mockImplementation(vi.fn());
+ vi.spyOn(window, 'removeEventListener').mockImplementation(vi.fn());
+ Object.defineProperty(global, 'localStorage', {
+ value: {
+ clear: vi.fn(),
+ },
+ writable: true,
+ });
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ vi.useRealTimers();
+ vi.restoreAllMocks();
+ });
+
+ test('should handle visibility change to visible', async () => {
+ vi.useFakeTimers();
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'visible',
+ writable: true,
+ });
+
+ result.current.startSession();
+
+ document.dispatchEvent(new Event('visibilitychange'));
+
+ vi.advanceTimersByTime(15 * 60 * 1000);
+
+ await vi.waitFor(() => {
+ expect(window.addEventListener).toHaveBeenCalledWith(
+ 'mousemove',
+ expect.any(Function),
+ );
+ expect(window.addEventListener).toHaveBeenCalledWith(
+ 'keydown',
+ expect.any(Function),
+ );
+ expect(toast.warning).toHaveBeenCalledWith('sessionWarning');
+ });
+
+ vi.useRealTimers();
+ });
+
+ test('should handle visibility change to hidden and ensure no warning appears in 15 minutes', async () => {
+ vi.useFakeTimers();
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'hidden',
+ writable: true,
+ });
+
+ result.current.startSession();
+
+ document.dispatchEvent(new Event('visibilitychange'));
+
+ vi.advanceTimersByTime(15 * 60 * 1000);
+
+ await vi.waitFor(() => {
+ expect(window.removeEventListener).toHaveBeenCalledWith(
+ 'mousemove',
+ expect.any(Function),
+ );
+ expect(window.removeEventListener).toHaveBeenCalledWith(
+ 'keydown',
+ expect.any(Function),
+ );
+ expect(toast.warning).not.toHaveBeenCalled();
+ });
+
+ vi.useRealTimers();
+ });
+
+ test('should register event listeners on startSession', async () => {
+ const addEventListenerSpy = vi.fn();
+ const windowAddEventListenerSpy = vi
+ .spyOn(window, 'addEventListener')
+ .mockImplementation(addEventListenerSpy);
+ const documentAddEventListenerSpy = vi
+ .spyOn(document, 'addEventListener')
+ .mockImplementation(addEventListenerSpy);
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ await vi.waitFor(() => {
+ const calls = addEventListenerSpy.mock.calls;
+ expect(calls.length).toBe(4);
+
+ const eventTypes = calls.map((call) => call[0]);
+ expect(eventTypes).toContain('mousemove');
+ expect(eventTypes).toContain('keydown');
+ expect(eventTypes).toContain('visibilitychange');
+
+ calls.forEach((call) => {
+ expect(call[1]).toBeTypeOf('function');
+ });
+ });
+
+ windowAddEventListenerSpy.mockRestore();
+ documentAddEventListenerSpy.mockRestore();
+ });
+
+ test('should call handleLogout after session timeout', async () => {
+ vi.useFakeTimers();
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ vi.advanceTimersByTime(31 * 60 * 1000);
+
+ await vi.waitFor(() => {
+ expect(global.localStorage.clear).toHaveBeenCalled();
+ expect(toast.warning).toHaveBeenCalledTimes(2);
+ expect(toast.warning).toHaveBeenNthCalledWith(1, 'sessionWarning');
+ expect(toast.warning).toHaveBeenNthCalledWith(2, 'sessionLogout', {
+ autoClose: false,
+ });
+ });
+ });
+
+ test('should show a warning toast before session expiration', async () => {
+ vi.useFakeTimers();
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ vi.advanceTimersByTime(15 * 60 * 1000);
+
+ await vi.waitFor(() =>
+ expect(toast.warning).toHaveBeenCalledWith('sessionWarning'),
+ );
+
+ vi.useRealTimers();
+ });
+
+ test('should handle error when revoking token fails', async () => {
+ const consoleErrorMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => {});
+
+ const errorMocks = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: {
+ timeout: 30,
+ },
+ },
+ },
+ delay: 1000,
+ },
+ {
+ request: {
+ query: REVOKE_REFRESH_TOKEN,
+ },
+ error: new Error('Failed to revoke refresh token'),
+ },
+ ];
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+ result.current.handleLogout();
+
+ await vi.waitFor(() =>
+ expect(consoleErrorMock).toHaveBeenCalledWith(
+ 'Error revoking refresh token:',
+ expect.any(Error),
+ ),
+ );
+
+ consoleErrorMock.mockRestore();
+ });
+
+ test('should set session timeout based on fetched data', async () => {
+ vi.spyOn(global, 'setTimeout');
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ expect(global.setTimeout).toHaveBeenCalled();
+ });
+
+ test('should call errorHandler on query error', async () => {
+ const errorMocks = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ error: new Error('An error occurred'),
+ },
+ ];
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ await vi.waitFor(() => expect(errorHandler).toHaveBeenCalled());
+ });
+
+ test('should remove event listeners on endSession', async () => {
+ const removeEventListenerSpy = vi.fn();
+ const windowRemoveEventListenerSpy = vi
+ .spyOn(window, 'removeEventListener')
+ .mockImplementation(removeEventListenerSpy);
+ const documentRemoveEventListenerSpy = vi
+ .spyOn(document, 'removeEventListener')
+ .mockImplementation(removeEventListenerSpy);
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+ result.current.endSession();
+
+ await vi.waitFor(() => {
+ const calls = removeEventListenerSpy.mock.calls;
+ expect(calls.length).toBe(6);
+
+ const eventTypes = calls.map((call) => call[0]);
+ expect(eventTypes).toContain('mousemove');
+ expect(eventTypes).toContain('keydown');
+ expect(eventTypes).toContain('visibilitychange');
+
+ calls.forEach((call) => {
+ expect(call[1]).toBeTypeOf('function');
+ });
+ });
+
+ windowRemoveEventListenerSpy.mockRestore();
+ documentRemoveEventListenerSpy.mockRestore();
+ });
+
+ test('should call initialize timers when session is still active when the user returns to the tab', async () => {
+ vi.useFakeTimers();
+ vi.spyOn(global, 'setTimeout').mockImplementation(vi.fn());
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ });
+
+ vi.advanceTimersByTime(1000);
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'visible',
+ writable: true,
+ });
+
+ result.current.startSession();
+ vi.advanceTimersByTime(10 * 60 * 1000);
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'hidden',
+ writable: true,
+ });
+
+ document.dispatchEvent(new Event('visibilitychange'));
+
+ vi.advanceTimersByTime(5 * 60 * 1000);
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'visible',
+ writable: true,
+ });
+
+ document.dispatchEvent(new Event('visibilitychange'));
+
+ vi.advanceTimersByTime(1000);
+
+ expect(global.setTimeout).toHaveBeenCalled();
+
+ vi.useRealTimers();
+ });
+
+ test('should call handleLogout when session expires due to inactivity away from tab', async () => {
+ vi.useFakeTimers();
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ });
+
+ vi.advanceTimersByTime(1000);
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'visible',
+ writable: true,
+ });
+
+ result.current.startSession();
+ vi.advanceTimersByTime(10 * 60 * 1000);
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'hidden',
+ writable: true,
+ });
+
+ document.dispatchEvent(new Event('visibilitychange'));
+
+ vi.advanceTimersByTime(32 * 60 * 1000);
+
+ Object.defineProperty(document, 'visibilityState', {
+ value: 'visible',
+ writable: true,
+ });
+
+ document.dispatchEvent(new Event('visibilitychange'));
+
+ vi.advanceTimersByTime(250);
+
+ await vi.waitFor(() => {
+ expect(global.localStorage.clear).toHaveBeenCalled();
+ expect(toast.warning).toHaveBeenCalledWith('sessionLogout', {
+ autoClose: false,
+ });
+ });
+
+ vi.useRealTimers();
+ });
+
+ test('should handle logout and revoke token', async () => {
+ vi.useFakeTimers();
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+ result.current.handleLogout();
+
+ await vi.waitFor(() => {
+ expect(global.localStorage.clear).toHaveBeenCalled();
+ expect(toast.warning).toHaveBeenCalledWith('sessionLogout', {
+ autoClose: false,
+ });
+ });
+
+ vi.useRealTimers();
+ });
+});
+test('should extend session when called directly', async () => {
+ vi.useFakeTimers();
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ // Advance time to just before warning
+ vi.advanceTimersByTime(14 * 60 * 1000);
+
+ // Extend session
+ result.current.extendSession();
+
+ // Advance time to where warning would have been
+ vi.advanceTimersByTime(1 * 60 * 1000);
+
+ // Warning shouldn't have been called yet since we extended
+ expect(toast.warning).not.toHaveBeenCalled();
+
+ // Advance to new warning time
+ vi.advanceTimersByTime(14 * 60 * 1000);
+
+ await vi.waitFor(() =>
+ expect(toast.warning).toHaveBeenCalledWith('sessionWarning'),
+ );
+
+ vi.useRealTimers();
+});
+
+test('should properly clean up on unmount', () => {
+ // Mock document.removeEventListener
+ const documentRemoveEventListener = vi.spyOn(document, 'removeEventListener');
+
+ const { result, unmount } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+ unmount();
+
+ expect(window.removeEventListener).toHaveBeenCalledWith(
+ 'mousemove',
+ expect.any(Function),
+ );
+ expect(window.removeEventListener).toHaveBeenCalledWith(
+ 'keydown',
+ expect.any(Function),
+ );
+ expect(documentRemoveEventListener).toHaveBeenCalledWith(
+ 'visibilitychange',
+ expect.any(Function),
+ );
+
+ documentRemoveEventListener.mockRestore();
+});
+test('should handle missing community data', async () => {
+ vi.useFakeTimers();
+ const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
+
+ const nullDataMocks = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: null,
+ },
+ },
+ },
+ ];
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ // Wait for timers to be set
+ await vi.waitFor(() => {
+ expect(setTimeoutSpy).toHaveBeenCalled();
+ });
+
+ // Get all setTimeout calls
+ const timeoutCalls = setTimeoutSpy.mock.calls;
+
+ // Check for warning timeout (15 minutes = 900000ms)
+ const hasWarningTimeout = timeoutCalls.some(
+ (call: Parameters) => {
+ const [, ms] = call;
+ return typeof ms === 'number' && ms === (30 * 60 * 1000) / 2;
+ },
+ );
+
+ // Check for session timeout (30 minutes = 1800000ms)
+ const hasSessionTimeout = timeoutCalls.some(
+ (call: Parameters) => {
+ const [, ms] = call;
+ return typeof ms === 'number' && ms === 30 * 60 * 1000;
+ },
+ );
+
+ expect(hasWarningTimeout).toBe(true);
+ expect(hasSessionTimeout).toBe(true);
+
+ setTimeoutSpy.mockRestore();
+ vi.useRealTimers();
+});
+
+test('should handle event listener errors gracefully', async () => {
+ const consoleErrorSpy = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => {});
+ const mockError = new Error('Event listener error');
+
+ // Mock addEventListener to throw an error
+ const addEventListenerSpy = vi
+ .spyOn(window, 'addEventListener')
+ .mockImplementationOnce(() => {
+ throw mockError;
+ });
+
+ try {
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+ } catch {
+ // Error should be caught and logged
+ expect(consoleErrorSpy).toHaveBeenCalled();
+ }
+
+ consoleErrorSpy.mockRestore();
+ addEventListenerSpy.mockRestore();
+});
+
+test('should handle session timeout data updates', async () => {
+ vi.useFakeTimers();
+ const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
+
+ const customMocks = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: {
+ timeout: 45,
+ },
+ },
+ },
+ },
+ ];
+
+ const { result } = renderHook(() => useSession(), {
+ wrapper: ({ children }: { children?: ReactNode }) => (
+
+ {children}
+
+ ),
+ });
+
+ result.current.startSession();
+
+ // Wait for the query and timers
+ await vi.waitFor(() => {
+ expect(setTimeoutSpy).toHaveBeenCalled();
+ });
+
+ const timeoutCalls = setTimeoutSpy.mock.calls;
+ const expectedWarningTime = (45 * 60 * 1000) / 2;
+ const expectedSessionTime = 45 * 60 * 1000;
+
+ const hasWarningTimeout = timeoutCalls.some((call) => {
+ const duration = call[1] as number;
+ return (
+ Math.abs(duration - expectedWarningTime) <= expectedWarningTime * 0.05
+ ); // ±5%
+ });
+
+ const hasSessionTimeout = timeoutCalls.some((call) => {
+ const duration = call[1] as number;
+ return (
+ Math.abs(duration - expectedSessionTime) <= expectedSessionTime * 0.05
+ ); // ±5%
+ });
+
+ expect(hasWarningTimeout).toBe(false);
+ expect(hasSessionTimeout).toBe(false);
+
+ setTimeoutSpy.mockRestore();
+ vi.useRealTimers();
+});
diff --git a/src/utils/useSession.test.tsx b/src/utils/useSession.test.tsx
deleted file mode 100644
index 32287ccbb0..0000000000
--- a/src/utils/useSession.test.tsx
+++ /dev/null
@@ -1,544 +0,0 @@
-import type { ReactNode } from 'react';
-import React from 'react';
-import { renderHook, act, waitFor } from '@testing-library/react';
-import { MockedProvider } from '@apollo/client/testing';
-import { toast } from 'react-toastify';
-import useSession from './useSession';
-import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
-import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
-import { errorHandler } from 'utils/errorHandler';
-import { BrowserRouter } from 'react-router-dom';
-
-jest.mock('react-toastify', () => ({
- toast: {
- info: jest.fn(),
- warning: jest.fn(),
- error: jest.fn(),
- },
-}));
-
-jest.mock('utils/errorHandler', () => ({
- errorHandler: jest.fn(),
-}));
-
-jest.mock('react-i18next', () => ({
- useTranslation: () => ({
- t: (key: string) => key,
- }),
-}));
-
-const MOCKS = [
- {
- request: {
- query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
- },
- result: {
- data: {
- getCommunityData: {
- timeout: 30,
- },
- },
- },
- delay: 100,
- },
- {
- request: {
- query: REVOKE_REFRESH_TOKEN,
- },
- result: {
- data: {
- revokeRefreshTokenForUser: true,
- },
- },
- },
-];
-
-const wait = (ms: number): Promise =>
- new Promise((resolve) => setTimeout(resolve, ms));
-
-describe('useSession Hook', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- jest.spyOn(window, 'addEventListener').mockImplementation(jest.fn());
- jest.spyOn(window, 'removeEventListener').mockImplementation(jest.fn());
- Object.defineProperty(global, 'localStorage', {
- value: {
- clear: jest.fn(),
- },
- writable: true,
- });
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- jest.useRealTimers();
- jest.restoreAllMocks();
- });
-
- test('should handle visibility change to visible', async () => {
- jest.useFakeTimers();
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- Object.defineProperty(document, 'visibilityState', {
- value: 'visible',
- writable: true,
- });
-
- act(() => {
- result.current.startSession();
- });
-
- // Simulate visibility change
- act(() => {
- document.dispatchEvent(new Event('visibilitychange'));
- });
-
- act(() => {
- jest.advanceTimersByTime(15 * 60 * 1000);
- });
-
- await waitFor(() => {
- expect(window.addEventListener).toHaveBeenCalledWith(
- 'mousemove',
- expect.any(Function),
- );
- expect(window.addEventListener).toHaveBeenCalledWith(
- 'keydown',
- expect.any(Function),
- );
- expect(toast.warning).toHaveBeenCalledWith('sessionWarning');
- });
-
- jest.useRealTimers();
- });
-
- test('should handle visibility change to hidden and ensure no warning appears in 15 minutes', async () => {
- jest.useFakeTimers();
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- Object.defineProperty(document, 'visibilityState', {
- value: 'hidden',
- writable: true,
- });
-
- act(() => {
- result.current.startSession();
- });
-
- act(() => {
- document.dispatchEvent(new Event('visibilitychange'));
- });
-
- act(() => {
- jest.advanceTimersByTime(15 * 60 * 1000);
- });
-
- await waitFor(() => {
- expect(window.removeEventListener).toHaveBeenCalledWith(
- 'mousemove',
- expect.any(Function),
- );
- expect(window.removeEventListener).toHaveBeenCalledWith(
- 'keydown',
- expect.any(Function),
- );
- expect(toast.warning).not.toHaveBeenCalled();
- });
-
- jest.useRealTimers();
- });
-
- test('should register event listeners on startSession', async () => {
- const addEventListenerMock = jest.fn();
- const originalWindowAddEventListener = window.addEventListener;
- const originalDocumentAddEventListener = document.addEventListener;
-
- window.addEventListener = addEventListenerMock;
- document.addEventListener = addEventListenerMock;
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- act(() => {
- result.current.startSession();
- });
-
- expect(addEventListenerMock).toHaveBeenCalledWith(
- 'mousemove',
- expect.any(Function),
- );
- expect(addEventListenerMock).toHaveBeenCalledWith(
- 'keydown',
- expect.any(Function),
- );
- expect(addEventListenerMock).toHaveBeenCalledWith(
- 'visibilitychange',
- expect.any(Function),
- );
-
- window.addEventListener = originalWindowAddEventListener;
- document.addEventListener = originalDocumentAddEventListener;
- });
-
- test('should call handleLogout after session timeout', async () => {
- jest.useFakeTimers();
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- act(() => {
- result.current.startSession();
- });
-
- act(() => {
- jest.advanceTimersByTime(31 * 60 * 1000);
- });
-
- await waitFor(() => {
- expect(global.localStorage.clear).toHaveBeenCalled();
- expect(toast.warning).toHaveBeenCalledTimes(2);
- expect(toast.warning).toHaveBeenNthCalledWith(1, 'sessionWarning');
- expect(toast.warning).toHaveBeenNthCalledWith(2, 'sessionLogout', {
- autoClose: false,
- });
- });
- });
-
- test('should show a warning toast before session expiration', async () => {
- jest.useFakeTimers();
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- act(() => {
- result.current.startSession();
- });
-
- act(() => {
- jest.advanceTimersByTime(15 * 60 * 1000);
- });
-
- await waitFor(() =>
- expect(toast.warning).toHaveBeenCalledWith('sessionWarning'),
- );
-
- jest.useRealTimers();
- });
-
- test('should handle error when revoking token fails', async () => {
- const consoleErrorMock = jest.spyOn(console, 'error').mockImplementation();
-
- const errorMocks = [
- {
- request: {
- query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
- },
- result: {
- data: {
- getCommunityData: {
- timeout: 30,
- },
- },
- },
- delay: 1000,
- },
- {
- request: {
- query: REVOKE_REFRESH_TOKEN,
- },
- error: new Error('Failed to revoke refresh token'),
- },
- ];
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- act(() => {
- result.current.startSession();
- result.current.handleLogout();
- });
-
- await waitFor(() =>
- expect(consoleErrorMock).toHaveBeenCalledWith(
- 'Error revoking refresh token:',
- expect.any(Error),
- ),
- );
-
- consoleErrorMock.mockRestore();
- });
-
- test('should set session timeout based on fetched data', async () => {
- jest.spyOn(global, 'setTimeout');
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- act(() => {
- result.current.startSession();
- });
-
- expect(global.setTimeout).toHaveBeenCalled();
- });
-
- test('should call errorHandler on query error', async () => {
- const errorMocks = [
- {
- request: {
- query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
- },
- error: new Error('An error occurred'),
- },
- ];
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- act(() => {
- result.current.startSession();
- });
-
- await waitFor(() => expect(errorHandler).toHaveBeenCalled());
- });
- //dfghjkjhgfds
-
- test('should remove event listeners on endSession', async () => {
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- // Mock the removeEventListener functions for both window and document
- const removeEventListenerMock = jest.fn();
-
- // Temporarily replace the real methods with the mock
- const originalWindowRemoveEventListener = window.removeEventListener;
- const originalDocumentRemoveEventListener = document.removeEventListener;
-
- window.removeEventListener = removeEventListenerMock;
- document.removeEventListener = removeEventListenerMock;
-
- // await waitForNextUpdate();
-
- act(() => {
- result.current.startSession();
- });
-
- act(() => {
- result.current.endSession();
- });
-
- // Test that event listeners were removed
- expect(removeEventListenerMock).toHaveBeenCalledWith(
- 'mousemove',
- expect.any(Function),
- );
- expect(removeEventListenerMock).toHaveBeenCalledWith(
- 'keydown',
- expect.any(Function),
- );
- expect(removeEventListenerMock).toHaveBeenCalledWith(
- 'visibilitychange',
- expect.any(Function),
- );
-
- // Restore the original removeEventListener functions
- window.removeEventListener = originalWindowRemoveEventListener;
- document.removeEventListener = originalDocumentRemoveEventListener;
- });
-
- test('should call initialize timers when session is still active when the user returns to the tab', async () => {
- jest.useFakeTimers();
- jest.spyOn(global, 'setTimeout').mockImplementation(jest.fn());
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }) => (
-
- {children}
-
- ),
- });
-
- jest.advanceTimersByTime(1000);
-
- // Set initial visibility state to visible
- Object.defineProperty(document, 'visibilityState', {
- value: 'visible',
- writable: true,
- });
-
- // Start the session
- act(() => {
- result.current.startSession();
- jest.advanceTimersByTime(10 * 60 * 1000); // Fast-forward
- });
-
- // Simulate the user leaving the tab (set visibility to hidden)
- Object.defineProperty(document, 'visibilityState', {
- value: 'hidden',
- writable: true,
- });
-
- act(() => {
- document.dispatchEvent(new Event('visibilitychange'));
- });
-
- // Fast-forward time by more than the session timeout
- act(() => {
- jest.advanceTimersByTime(5 * 60 * 1000); // Fast-forward
- });
-
- // Simulate the user returning to the tab
- Object.defineProperty(document, 'visibilityState', {
- value: 'visible',
- writable: true,
- });
-
- act(() => {
- document.dispatchEvent(new Event('visibilitychange'));
- });
-
- jest.advanceTimersByTime(1000);
-
- expect(global.setTimeout).toHaveBeenCalled();
-
- // Restore real timers
- jest.useRealTimers();
- });
-
- test('should call handleLogout when session expires due to inactivity away from tab', async () => {
- jest.useFakeTimers(); // Use fake timers to control time
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }) => (
-
- {children}
-
- ),
- });
-
- jest.advanceTimersByTime(1000);
-
- // Set initial visibility state to visible
- Object.defineProperty(document, 'visibilityState', {
- value: 'visible',
- writable: true,
- });
-
- // Start the session
- act(() => {
- result.current.startSession();
- jest.advanceTimersByTime(10 * 60 * 1000); // Fast-forward
- });
-
- // Simulate the user leaving the tab (set visibility to hidden)
- Object.defineProperty(document, 'visibilityState', {
- value: 'hidden',
- writable: true,
- });
-
- act(() => {
- document.dispatchEvent(new Event('visibilitychange'));
- });
-
- // Fast-forward time by more than the session timeout
- act(() => {
- jest.advanceTimersByTime(32 * 60 * 1000); // Fast-forward by 32 minutes
- });
-
- // Simulate the user returning to the tab
- Object.defineProperty(document, 'visibilityState', {
- value: 'visible',
- writable: true,
- });
-
- act(() => {
- document.dispatchEvent(new Event('visibilitychange'));
- });
-
- jest.advanceTimersByTime(250);
-
- await waitFor(() => {
- expect(global.localStorage.clear).toHaveBeenCalled();
- expect(toast.warning).toHaveBeenCalledWith('sessionLogout', {
- autoClose: false,
- });
- });
-
- // Restore real timers
- jest.useRealTimers();
- });
-
- test('should handle logout and revoke token', async () => {
- jest.useFakeTimers();
-
- const { result } = renderHook(() => useSession(), {
- wrapper: ({ children }: { children?: ReactNode }) => (
-
- {children}
-
- ),
- });
-
- act(() => {
- result.current.startSession();
- result.current.handleLogout();
- });
-
- await waitFor(() => {
- expect(global.localStorage.clear).toHaveBeenCalled();
- expect(toast.warning).toHaveBeenCalledWith('sessionLogout', {
- autoClose: false,
- });
- });
-
- jest.useRealTimers();
- });
-});