From c2630ae47b94ddfd787fbd5fb024b57dbbdf8e7c Mon Sep 17 00:00:00 2001 From: adithya <144007420+adithyanotfound@users.noreply.github.com> Date: Sun, 1 Dec 2024 11:55:40 +0530 Subject: [PATCH 01/24] Refactored src/components/AddOn/support/components/Action/Action.test.tsx from Jest to Vitest (#2482) * Migrated to vitest * Added ts-doc comment --------- Co-authored-by: Vamshi Maskuri <117595548+varshith257@users.noreply.github.com> --- .../support/components/Action/Action.spec.tsx | 31 +++++++++++++++++++ vitest.config.ts | 3 +- vitest.setup.ts | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/components/AddOn/support/components/Action/Action.spec.tsx create mode 100644 vitest.setup.ts diff --git a/src/components/AddOn/support/components/Action/Action.spec.tsx b/src/components/AddOn/support/components/Action/Action.spec.tsx new file mode 100644 index 0000000000..e0682a5645 --- /dev/null +++ b/src/components/AddOn/support/components/Action/Action.spec.tsx @@ -0,0 +1,31 @@ +/** + * Unit tests for the Action component. + * + * This file contains tests for the Action component to ensure it behaves as expected + * under various scenarios. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { describe, test, expect } from 'vitest'; + +import { store } from 'state/store'; +import Action from './Action'; + +describe('Testing Action Component', () => { + const props = { + children: 'dummy children', + label: 'dummy label', + }; + + test('should render props and text elements for the page component', () => { + const { getByText } = render( + + + , + ); + + expect(getByText(props.label)).toBeInTheDocument(); + expect(getByText(props.children)).toBeInTheDocument(); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index cd08488b3c..ad85276111 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vite'; +import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; import tsconfigPaths from 'vite-tsconfig-paths'; @@ -15,6 +15,7 @@ export default defineConfig({ include: ['src/**/*.spec.{js,jsx,ts,tsx}'], globals: true, environment: 'jsdom', + setupFiles: 'vitest.setup.ts', coverage: { enabled: true, provider: 'istanbul', diff --git a/vitest.setup.ts b/vitest.setup.ts new file mode 100644 index 0000000000..7b0828bfa8 --- /dev/null +++ b/vitest.setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; From 90ae8b8b6ce5c9442d60afdba000d514b35b945f Mon Sep 17 00:00:00 2001 From: Shekhar Patel <90516956+duplixx@users.noreply.github.com> Date: Sat, 7 Dec 2024 22:30:46 +0530 Subject: [PATCH 02/24] Plugin and advertisement screen revamp (#2006) * advertisement and plugin screen * added revamped design * fixes added * fixed testcases * chore(deps): bump sass from 1.80.6 to 1.80.7 (#2433) Bumps [sass](https://github.com/sass/dart-sass) from 1.80.6 to 1.80.7. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.80.6...1.80.7) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump eslint-plugin-import from 2.30.0 to 2.31.0 (#2434) Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.30.0 to 2.31.0. - [Release notes](https://github.com/import-js/eslint-plugin-import/releases) - [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md) - [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.30.0...v2.31.0) --- updated-dependencies: - dependency-name: eslint-plugin-import dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @mui/x-charts from 7.22.1 to 7.22.2 (#2435) Bumps [@mui/x-charts](https://github.com/mui/mui-x/tree/HEAD/packages/x-charts) from 7.22.1 to 7.22.2. - [Release notes](https://github.com/mui/mui-x/releases) - [Changelog](https://github.com/mui/mui-x/blob/v7.22.2/CHANGELOG.md) - [Commits](https://github.com/mui/mui-x/commits/v7.22.2/packages/x-charts) --- updated-dependencies: - dependency-name: "@mui/x-charts" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @types/react from 18.3.3 to 18.3.12 (#2436) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.3.3 to 18.3.12. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update pull-request.yml * Update dependabot.yaml * added docker check to workflow (#2414) * added docker check to workflow * made recommended changes to docker check in workflow * added changes to docker check inn workflow as recommended * added changes * updated indentation in pull-request.yml file * updated indentation in pull-request.yml file * added Dockerfile and Docker-compose.yml file * added Dockerfile and Docker-compose.yml file * updated .docker-ignore file * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * properly formatted code * trying to make docker check pass * trying to make docker check pass * updated INSTALLATION.md * updated INSTALLATION.md * added recommended changes to INSTALLATION.md * added recommended changes to INSTALLATION.md * added recommended changes to INSTALLATION.md * updated docker workflow * updated INSTALLATION.md * eslint-rule-no_unused_vars (#2428) * Updated pr template with checklist (#2454) * Updated pr template with checklist * Additional changes for the PR template * Changed the url for the PR template * refactored addOnStore * refactored addOnEntry v1 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Co-authored-by: Vanshika Sabharwal <143436704+VanshikaSabharwal@users.noreply.github.com> Co-authored-by: prayansh_chhablani <135210710+prayanshchh@users.noreply.github.com> Co-authored-by: Dhiraj Udhani --- .github/pull_request_template.md | 2 +- public/locales/en/translation.json | 4 +- .../core/AddOnEntry/AddOnEntry.module.css | 6 +- .../AddOn/core/AddOnEntry/AddOnEntry.tsx | 14 +- .../core/AddOnStore/AddOnStore.module.css | 45 +- .../AddOn/core/AddOnStore/AddOnStore.test.tsx | 99 +---- .../AddOn/core/AddOnStore/AddOnStore.tsx | 398 ++++++++---------- .../support/services/Plugin.helper.test.ts | 13 +- .../Advertisements/Advertisements.module.css | 26 +- .../Advertisements/Advertisements.test.tsx | 2 +- .../Advertisements/Advertisements.tsx | 80 ++-- .../AdvertisementEntry.module.css | 6 +- .../AdvertisementEntry/AdvertisementEntry.tsx | 9 +- .../AdvertisementRegister.module.css | 3 +- 14 files changed, 346 insertions(+), 361 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e1cfbfd8ea..0748858133 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -61,4 +61,4 @@ Fixes # **Have you read the [contributing guide](https://github.com/PalisadoesFoundation/talawa-admin/blob/master/CONTRIBUTING.md)?** - + \ No newline at end of file diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 5da5ef49ee..57b4c5de98 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -923,7 +923,7 @@ "register": "register" }, "addOnStore": { - "title": "Add On Store", + "title": "Plugin Store", "searchName": "Ex: Donations", "search": "Search", "enable": "Enabled", @@ -1227,7 +1227,7 @@ "RstartDate": "Select Start Date", "RendDate": "Select End Date", "RClose": "Close the window", - "addNew": "Create new advertisement", + "addNew": "Create", "EXname": "Ex. Cookie Shop", "EXlink": "Ex. http://yourwebsite.com/photo", "createAdvertisement": "Create Advertisement", diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntry.module.css b/src/components/AddOn/core/AddOnEntry/AddOnEntry.module.css index 1f1ea89996..c5dd86c8d4 100644 --- a/src/components/AddOn/core/AddOnEntry/AddOnEntry.module.css +++ b/src/components/AddOn/core/AddOnEntry/AddOnEntry.module.css @@ -7,8 +7,12 @@ margin-left: auto; display: flex !important; align-items: center; + background-color: transparent; + color: #31bb6b; +} +.card { + border: 4px solid green; } - .entryaction i { margin-right: 8px; } diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx b/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx index 257917e2c2..12805568f6 100644 --- a/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx +++ b/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx @@ -17,9 +17,9 @@ interface InterfaceAddOnEntryProps { description?: string; // Optional props createdBy: string; component?: string; // Optional props - modified?: any; // Optional props + modified?: boolean; // Optional props uninstalledOrgs: string[]; - getInstalledPlugins: () => any; + getInstalledPlugins: () => void; } /** @@ -59,6 +59,7 @@ function addOnEntry({ // Getting orgId from URL parameters const { orgId: currentOrg } = useParams(); + // console.log(currentOrg); if (!currentOrg) { // If orgId is not present in the URL, navigate to the org list page return ; @@ -101,7 +102,10 @@ function addOnEntry({ return ( <> - + {/* {uninstalledOrgs.includes(currentOrg) && ( )} */} - {title} + {title} {createdBy} @@ -134,7 +138,7 @@ function addOnEntry({ ) : ( )} {/* {installed ? 'Remove' : configurable ? 'Installed' : 'Install'} */} diff --git a/src/components/AddOn/core/AddOnStore/AddOnStore.module.css b/src/components/AddOn/core/AddOnStore/AddOnStore.module.css index 8a34c03be5..9f5bb6c868 100644 --- a/src/components/AddOn/core/AddOnStore/AddOnStore.module.css +++ b/src/components/AddOn/core/AddOnStore/AddOnStore.module.css @@ -11,8 +11,12 @@ border-bottom: 3px solid #31bb6b; width: 15%; } - -.actioninput { +.input { + display: flex; + position: relative; + width: 560px; +} +/* .actioninput { text-decoration: none; margin-bottom: 50px; border-color: #e8e5e5; @@ -23,9 +27,46 @@ padding-right: 10px; padding-left: 10px; box-shadow: none; +} */ +.actioninput { + margin-top: 10px; + margin-bottom: 10px; + background-color: white; + box-shadow: 0 1px 1px #31bb6b; +} +.inputField > button { + padding-top: 10px; + padding-bottom: 10px; } .actionradio input { width: fit-content; margin: inherit; } +.cardGridItem { + width: 38vw; +} +.justifysp { + display: grid; + width: 100%; + justify-content: space-between; + align-items: baseline; + grid-template-rows: auto; + grid-template-columns: repeat(2, 1fr); + grid-gap: 0.8rem 0.4rem; +} + +@media screen and (max-width: 600px) { + .cardGridItem { + width: 100%; + } + .justifysp { + display: grid; + width: 100%; + justify-content: center; + align-items: start; + grid-template-rows: auto; + grid-template-columns: 1fr; + grid-gap: 0.8rem 0.4rem; + } +} diff --git a/src/components/AddOn/core/AddOnStore/AddOnStore.test.tsx b/src/components/AddOn/core/AddOnStore/AddOnStore.test.tsx index e76e2a7b73..abb4a80ce8 100644 --- a/src/components/AddOn/core/AddOnStore/AddOnStore.test.tsx +++ b/src/components/AddOn/core/AddOnStore/AddOnStore.test.tsx @@ -22,7 +22,11 @@ import useLocalStorage from 'utils/useLocalstorage'; import { MockedProvider } from '@apollo/react-testing'; const { getItem } = useLocalStorage(); - +interface InterfacePlugin { + enabled: boolean; + pluginName: string; + component: string; +} jest.mock('components/AddOn/support/services/Plugin.helper', () => ({ __esModule: true, default: jest.fn().mockImplementation(() => ({ @@ -60,16 +64,18 @@ jest.mock('components/AddOn/support/services/Plugin.helper', () => ({ }, // Add more mock data as needed ]), - generateLinks: jest.fn().mockImplementation((plugins) => { - return plugins - .filter((plugin: { enabled: any }) => plugin.enabled) - .map((installedPlugin: { pluginName: any; component: string }) => { - return { - name: installedPlugin.pluginName, - url: `/plugin/${installedPlugin.component.toLowerCase()}`, - }; - }); - }), + generateLinks: jest + .fn() + .mockImplementation((plugins: InterfacePlugin[]) => { + return plugins + .filter((plugin) => plugin.enabled) + .map((installedPlugin) => { + return { + name: installedPlugin.pluginName, + url: `/plugin/${installedPlugin.component.toLowerCase()}`, + }; + }); + }), })), })); @@ -301,77 +307,6 @@ describe('Testing AddOnStore Component', () => { expect(message.length).toBeGreaterThanOrEqual(1); }); - test('check filters enabled and disabled under Installed tab', async () => { - const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_GET_MOCK]; - render( - - - - - - - - - - - , - ); - - await wait(); - userEvent.click(screen.getByText('Installed')); - - expect(screen.getByText('Filters')).toBeInTheDocument(); - expect(screen.getByLabelText('Enabled')).toBeInTheDocument(); - expect(screen.getByLabelText('Disabled')).toBeInTheDocument(); - - fireEvent.click(screen.getByLabelText('Enabled')); - expect(screen.getByLabelText('Enabled')).toBeChecked(); - fireEvent.click(screen.getByLabelText('Disabled')); - expect(screen.getByLabelText('Disabled')).toBeChecked(); - }); - - test('check the working search bar when on Installed tab', async () => { - const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_GET_MOCK]; - - const { container } = render( - - - - - - - - - - - , - ); - await wait(); - userEvent.click(screen.getByText('Installed')); - - await wait(); - let searchText = ''; - fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), { - target: { value: searchText }, - }); - expect(container).toHaveTextContent('Plugin 1'); - expect(container).toHaveTextContent('Plugin 3'); - - searchText = 'Plugin 1'; - fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), { - target: { value: searchText }, - }); - const plugin1Elements = screen.queryAllByText('Plugin 1'); - expect(plugin1Elements.length).toBeGreaterThan(1); - - searchText = 'Test Plugin'; - fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), { - target: { value: searchText }, - }); - const message = screen.getAllByText('Plugin does not exists'); - expect(message.length).toBeGreaterThanOrEqual(1); - }); - test('AddOnStore loading test', async () => { expect(true).toBe(true); const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_LOADING_MOCK]; diff --git a/src/components/AddOn/core/AddOnStore/AddOnStore.tsx b/src/components/AddOn/core/AddOnStore/AddOnStore.tsx index 878ad64e31..90a32d9bb3 100644 --- a/src/components/AddOn/core/AddOnStore/AddOnStore.tsx +++ b/src/components/AddOn/core/AddOnStore/AddOnStore.tsx @@ -1,16 +1,27 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import React, { useState } from 'react'; -// import PropTypes from 'react'; import styles from './AddOnStore.module.css'; import AddOnEntry from '../AddOnEntry/AddOnEntry'; -import Action from '../../support/components/Action/Action'; import { useQuery } from '@apollo/client'; -import { PLUGIN_GET } from 'GraphQl/Queries/Queries'; // GraphQL query for fetching plugins -import { Col, Form, Row, Tab, Tabs } from 'react-bootstrap'; +import { PLUGIN_GET } from 'GraphQl/Queries/Queries'; // PLUGIN_LIST +import { Col, Dropdown, Form, Row, Tab, Tabs, Button } from 'react-bootstrap'; import PluginHelper from 'components/AddOn/support/services/Plugin.helper'; import { store } from './../../../../state/store'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; +import { Search } from '@mui/icons-material'; + +interface InterfacePluginHelper { + _id: string; + pluginName?: string; + pluginDesc?: string; + pluginCreatedBy: string; + pluginInstallStatus?: boolean; + uninstalledOrgs: string[]; + installed: boolean; + enabled: boolean; + name: string; + component: string; +} /** * Component for managing and displaying plugins in the store. @@ -30,12 +41,13 @@ function addOnStore(): JSX.Element { const [isStore, setIsStore] = useState(true); const [showEnabled, setShowEnabled] = useState(true); const [searchText, setSearchText] = useState(''); - const [, setDataList] = useState([]); + const [, setDataList] = useState([]); - // type plugData = { pluginName: String, plug }; - const { data, loading } = useQuery(PLUGIN_GET); + const { data, loading } = useQuery<{ getPlugins: InterfacePluginHelper[] }>( + PLUGIN_GET, + ); - const { orgId } = useParams(); + const { orgId } = useParams<{ orgId: string }>(); /** * Fetches store plugins and updates the Redux store with the plugin data. @@ -44,10 +56,10 @@ function addOnStore(): JSX.Element { const getStorePlugins = async (): Promise => { let plugins = await new PluginHelper().fetchStore(); const installIds = (await new PluginHelper().fetchInstalled()).map( - (plugin: any) => plugin.id, + (plugin: InterfacePluginHelper) => plugin._id, ); - plugins = plugins.map((plugin: any) => { - plugin.installed = installIds.includes(plugin.id); + plugins = plugins.map((plugin: InterfacePluginHelper) => { + plugin.installed = installIds.includes(plugin._id); return plugin; }); store.dispatch({ type: 'UPDATE_STORE', payload: plugins }); @@ -57,8 +69,8 @@ function addOnStore(): JSX.Element { * Sets the list of installed plugins in the component's state. */ /* istanbul ignore next */ - const getInstalledPlugins: () => any = () => { - setDataList(data); + const getInstalledPlugins: () => void = () => { + setDataList(data?.getPlugins ?? []); }; /** @@ -66,10 +78,14 @@ function addOnStore(): JSX.Element { * * @param tab - The key of the selected tab (either 'available' or 'installed'). */ - const updateSelectedTab = (tab: any): void => { + const updateSelectedTab = (tab: string): void => { setIsStore(tab === 'available'); /* istanbul ignore next */ - isStore ? getStorePlugins() : getInstalledPlugins(); + if (tab === 'available') { + getStorePlugins(); + } else { + getInstalledPlugins(); + } }; /** @@ -77,10 +93,23 @@ function addOnStore(): JSX.Element { * * @param ev - The event object from the filter change. */ - const filterChange = (ev: any): void => { + const filterChange = (ev: React.ChangeEvent): void => { setShowEnabled(ev.target.value === 'enabled'); }; + const filterPlugins = ( + plugins: InterfacePluginHelper[], + searchTerm: string, + ): InterfacePluginHelper[] => { + if (!searchTerm) { + return plugins; + } + + return plugins.filter((plugin) => + plugin.pluginName?.toLowerCase().includes(searchTerm.toLowerCase()), + ); + }; + // Show a loader while the data is being fetched /* istanbul ignore next */ if (loading) { @@ -93,9 +122,23 @@ function addOnStore(): JSX.Element { return ( <> - - - + + +
setSearchText(e.target.value)} /> - + +
{!isStore && ( - -
-
- - -
-
-
+ + filterChange( + e as unknown as React.ChangeEvent, + ) + } + > + + {showEnabled ? t('enable') : t('disable')} + + + + {t('enable')} + + + {t('disable')} + + + )} - -
-

{t('pHeading')}

- {searchText ? ( -

- Search results for {searchText} -

- ) : null} +
+ { + if (eventKey) { + updateSelectedTab(eventKey); + } + }} + > + +
+ {(() => { + const filteredPlugins = filterPlugins( + data?.getPlugins || [], + searchText, + ); - {t('pMessage')}; + } + + return ( +
+ {filteredPlugins.map((plug, i) => ( +
+ +
+ ))} +
+ ); + })()} +
+
+ - - {data.getPlugins.filter( - (val: { - _id: string; - pluginName: string | undefined; - pluginDesc: string | undefined; - pluginCreatedBy: string; - pluginInstallStatus: boolean | undefined; - getInstalledPlugins: () => any; - }) => { - if (searchText == '') { - return val; - } else if ( - val.pluginName - ?.toLowerCase() - .includes(searchText.toLowerCase()) - ) { - return val; - } - }, - ).length === 0 ? ( -

{t('pMessage')}

- ) : ( - data.getPlugins - .filter( - (val: { - _id: string; - pluginName: string | undefined; - pluginDesc: string | undefined; - pluginCreatedBy: string; - pluginInstallStatus: boolean | undefined; - getInstalledPlugins: () => any; - }) => { - if (searchText == '') { - return val; - } else if ( - val.pluginName - ?.toLowerCase() - .includes(searchText.toLowerCase()) - ) { - return val; - } - }, - ) - .map( - ( - plug: { - _id: string; - pluginName: string | undefined; - pluginDesc: string | undefined; - pluginCreatedBy: string; - uninstalledOrgs: string[]; - getInstalledPlugins: () => any; - }, - i: React.Key | null | undefined, - ): JSX.Element => ( - - ), - ) - )} -
- - {data.getPlugins - .filter( - (plugin: any) => !plugin.uninstalledOrgs.includes(orgId), - ) - .filter( - (val: { - _id: string; - pluginName: string | undefined; - pluginDesc: string | undefined; - pluginCreatedBy: string; - pluginInstallStatus: boolean | undefined; - getInstalledPlugins: () => any; - }) => { - if (searchText == '') { - return val; - } else if ( - val.pluginName - ?.toLowerCase() - .includes(searchText.toLowerCase()) - ) { - return val; - } - }, - ).length === 0 ? ( -

{t('pMessage')}

- ) : ( - data.getPlugins - .filter( - (plugin: any) => !plugin.uninstalledOrgs.includes(orgId), - ) - .filter( - (val: { - _id: string; - pluginName: string | undefined; - pluginDesc: string | undefined; - pluginCreatedBy: string; - pluginInstallStatus: boolean | undefined; - getInstalledPlugins: () => any; - }) => { - if (searchText == '') { - return val; - } else if ( - val.pluginName - ?.toLowerCase() - .includes(searchText.toLowerCase()) - ) { - return val; - } - }, - ) - .map( - ( - plug: { - _id: string; - pluginName: string | undefined; - pluginDesc: string | undefined; - pluginCreatedBy: string; - uninstalledOrgs: string[]; - pluginInstallStatus: boolean | undefined; - getInstalledPlugins: () => any; - }, - i: React.Key | null | undefined, - ): JSX.Element => ( - - ), - ) - )} -
-
-
- +
+ {(() => { + const installedPlugins = (data?.getPlugins || []).filter( + (plugin) => !plugin.uninstalledOrgs.includes(orgId ?? ''), + ); + const filteredPlugins = filterPlugins( + installedPlugins, + searchText, + ); + + if (filteredPlugins.length === 0) { + return

{t('pMessage')}

; + } + + return filteredPlugins.map((plug, i) => ( +
+ +
+ )); + })()} +
+ + +
); diff --git a/src/components/AddOn/support/services/Plugin.helper.test.ts b/src/components/AddOn/support/services/Plugin.helper.test.ts index e024734247..39f0a5d12c 100644 --- a/src/components/AddOn/support/services/Plugin.helper.test.ts +++ b/src/components/AddOn/support/services/Plugin.helper.test.ts @@ -9,7 +9,18 @@ describe('Testing src/components/AddOn/support/services/Plugin.helper.ts', () => expect(pluginHelper).toHaveProperty('generateLinks'); }); test('generateLinks should return proper objects', () => { - const obj = { enabled: true, name: 'demo', component: 'samplecomponent' }; + const obj = { + enabled: true, + name: 'demo', + component: 'samplecomponent', + _id: 'someId', + pluginName: 'pluginName', + pluginDesc: 'pluginDesc', + pluginCreatedBy: 'creator', + pluginInstallStatus: true, + uninstalledOrgs: ['org1', 'org2'], + installed: true, + }; const objToMatch = { name: 'demo', url: '/plugin/samplecomponent' }; const pluginHelper = new PluginHelper(); const val = pluginHelper.generateLinks([obj]); diff --git a/src/components/Advertisements/Advertisements.module.css b/src/components/Advertisements/Advertisements.module.css index 8a34c03be5..6d9eb7f612 100644 --- a/src/components/Advertisements/Advertisements.module.css +++ b/src/components/Advertisements/Advertisements.module.css @@ -1,6 +1,13 @@ .container { display: flex; } +.listBox { + display: grid; + width: 100%; + grid-template-rows: auto; + grid-template-columns: repeat(6, 1fr); + grid-gap: 0.8rem 0.4rem; +} .logintitle { color: #707070; @@ -11,15 +18,24 @@ border-bottom: 3px solid #31bb6b; width: 15%; } - +.input { + display: flex; + position: relative; + width: 560px; +} +.justifysp { + display: grid; + width: 100%; + margin-top: 30px; +} .actioninput { text-decoration: none; - margin-bottom: 50px; + /* margin-bottom: 50px; */ border-color: #e8e5e5; - width: 80%; + background-color: white; border-radius: 7px; - padding-top: 5px; - padding-bottom: 5px; + padding-top: 10px; + padding-bottom: 10px; padding-right: 10px; padding-left: 10px; box-shadow: none; diff --git a/src/components/Advertisements/Advertisements.test.tsx b/src/components/Advertisements/Advertisements.test.tsx index c0992a1012..88bbb1255c 100644 --- a/src/components/Advertisements/Advertisements.test.tsx +++ b/src/components/Advertisements/Advertisements.test.tsx @@ -461,7 +461,7 @@ describe('Testing Advertisement Component', () => { await wait(); const date = await screen.findAllByTestId('Ad_end_date'); - const dateString = date[1].innerHTML; + const dateString = date[0].innerHTML; const dateMatch = dateString.match( /\b(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(\d{1,2})\s+(\d{4})\b/, ); diff --git a/src/components/Advertisements/Advertisements.tsx b/src/components/Advertisements/Advertisements.tsx index f20c2a7d8e..5f0e2b2033 100644 --- a/src/components/Advertisements/Advertisements.tsx +++ b/src/components/Advertisements/Advertisements.tsx @@ -2,30 +2,16 @@ import React, { useEffect, useState } from 'react'; import styles from './Advertisements.module.css'; import { useQuery } from '@apollo/client'; import { ORGANIZATION_ADVERTISEMENT_LIST } from 'GraphQl/Queries/Queries'; -import { Col, Row, Tab, Tabs } from 'react-bootstrap'; +import { Button, Col, Form, Row, Tab, Tabs } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import AdvertisementEntry from './core/AdvertisementEntry/AdvertisementEntry'; import AdvertisementRegister from './core/AdvertisementRegister/AdvertisementRegister'; import { useParams } from 'react-router-dom'; import type { InterfaceQueryOrganizationAdvertisementListItem } from 'utils/interfaces'; import InfiniteScroll from 'react-infinite-scroll-component'; +import { Search } from '@mui/icons-material'; -/** - * The `Advertisements` component displays a list of advertisements for a specific organization. - * It uses a tab-based interface to toggle between active and archived advertisements. - * - * The component utilizes the `useQuery` hook from Apollo Client to fetch advertisements data - * and implements infinite scrolling to load more advertisements as the user scrolls. - * - * @example - * return ( - * - * ) - * - */ - -export default function Advertisements(): JSX.Element { - // Retrieve the organization ID from URL parameters +export default function advertisements(): JSX.Element { const { orgId: currentOrgId } = useParams(); // Translation hook for internationalization const { t } = useTranslation('translation', { keyPrefix: 'advertisement' }); @@ -43,20 +29,14 @@ export default function Advertisements(): JSX.Element { name: string; type: 'BANNER' | 'MENU' | 'POPUP'; mediaUrl: string; - endDate: string; // Assuming it's a string in the format 'yyyy-MM-dd' - startDate: string; // Assuming it's a string in the format 'yyyy-MM-dd' + endDate: string; + startDate: string; }; // GraphQL query to fetch the list of advertisements - const { - data: orgAdvertisementListData, - refetch, - }: { - data?: { - organizations: InterfaceQueryOrganizationAdvertisementListItem[]; - }; - refetch: () => void; - } = useQuery(ORGANIZATION_ADVERTISEMENT_LIST, { + const { data: orgAdvertisementListData, refetch } = useQuery<{ + organizations: InterfaceQueryOrganizationAdvertisementListItem[]; + }>(ORGANIZATION_ADVERTISEMENT_LIST, { variables: { id: currentOrgId, after: after, @@ -99,19 +79,45 @@ export default function Advertisements(): JSX.Element { return ( <> - +
- {/* Component for registering a new advertisement */} - + +
+ setSearchText("search")} + /> + +
+ + - {/* Tabs for active and archived advertisements */} - {/* Tab for active advertisements */} - + - - {/* Tab for archived advertisements */} new Date(ad.endDate) < new Date(), ).length !== 0 && (
-
{tCommon('endOfResults')}
+
{t('endOfResults')}
) } diff --git a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.module.css b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.module.css index 879d96a0a0..e4f244807f 100644 --- a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.module.css +++ b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.module.css @@ -20,7 +20,7 @@ .admedia { object-fit: cover; - height: 20rem; + height: 16rem; } .buttons { @@ -28,6 +28,10 @@ justify-content: flex-end; } +.card { + width: 28rem; +} + .dropdownButton { background-color: transparent; color: #000; diff --git a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.tsx b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.tsx index 7368ded68e..7656f1f0cf 100644 --- a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.tsx +++ b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.tsx @@ -38,6 +38,7 @@ function AdvertisementEntry({ startDate = new Date(), setAfter, }: InterfaceAddOnEntryProps): JSX.Element { + console.log(id, type); const { t } = useTranslation('translation', { keyPrefix: 'advertisement' }); const { t: tCommon } = useTranslation('common'); @@ -98,7 +99,7 @@ function AdvertisementEntry({ {Array.from({ length: 1 }).map((_, idx) => ( - +
} + element={ +
Redirected to Home
+ } /> @@ -42,28 +54,37 @@ const renderOrganisationSettings = (link: ApolloLink): RenderResult => { }; describe('Organisation Settings Page', () => { - beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId' }), - })); + afterEach(() => { + vi.unmock('react-router-dom'); }); - afterAll(() => { - jest.clearAllMocks(); - }); + const SetupRedirectTest = async (): Promise => { + const useParamsMock = vi.fn(() => ({ orgId: undefined })); + vi.doMock('react-router-dom', async () => { + const actual = await vi.importActual('react-router-dom'); + return { + ...actual, + useParams: useParamsMock, + }; + }); + const orgSettingsModule = await import('./OrgSettings'); + return ; + }; it('should redirect to fallback URL if URL params are undefined', async () => { + const OrgSettings = await SetupRedirectTest(); render( - + - } /> +
} + element={ +
Redirected to Home
+ } /> @@ -71,13 +92,16 @@ describe('Organisation Settings Page', () => { , ); + await waitFor(() => { - expect(screen.getByTestId('paramsError')).toBeInTheDocument(); + const paramsErrorElement = screen.getByTestId('paramsError'); + expect(paramsErrorElement).toBeInTheDocument(); + expect(paramsErrorElement.textContent).toBe('Redirected to Home'); }); }); - test('should render the organisation settings page', async () => { - renderOrganisationSettings(link1); + it('should render the organisation settings page', async () => { + renderOrganisationSettings(); await waitFor(() => { expect(screen.getByTestId('generalSettings')).toBeInTheDocument(); @@ -88,10 +112,11 @@ describe('Organisation Settings Page', () => { screen.getByTestId('agendaItemCategoriesSettings'), ).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('generalSettings')); + userEvent.click(screen.getByTestId('generalSettings')); await waitFor(() => { expect(screen.getByTestId('generalTab')).toBeInTheDocument(); + expect(screen.getByTestId('generalTab')).toBeVisible(); }); userEvent.click(screen.getByTestId('actionItemCategoriesSettings')); @@ -104,4 +129,19 @@ describe('Organisation Settings Page', () => { expect(screen.getByTestId('agendaItemCategoriesTab')).toBeInTheDocument(); }); }); + + it('should render dropdown for settings tabs', async () => { + renderOrganisationSettings(); + + await waitFor(() => { + expect(screen.getByTestId('settingsDropdownToggle')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByTestId('settingsDropdownToggle')); + + const dropdownItems = screen.getAllByRole('button', { + name: /general|actionItemCategories|agendaItemCategories/i, + }); + expect(dropdownItems).toHaveLength(3); + }); }); From a16118e5f3bafa95b701293525941049342a9d01 Mon Sep 17 00:00:00 2001 From: Shiva <148421597+shivasankaran18@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:26:27 +0530 Subject: [PATCH 12/24] Refactor: Added Vitest To Requests Screen (#2655) * refactor:request screen to vitest * refactor:vitest to Requests Screen * implement codreabbit suggestions --- .../{Requests.test.tsx => Requests.spec.tsx} | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) rename src/screens/Requests/{Requests.test.tsx => Requests.spec.tsx} (91%) diff --git a/src/screens/Requests/Requests.test.tsx b/src/screens/Requests/Requests.spec.tsx similarity index 91% rename from src/screens/Requests/Requests.test.tsx rename to src/screens/Requests/Requests.spec.tsx index 4606fdae08..820b24c40d 100644 --- a/src/screens/Requests/Requests.test.tsx +++ b/src/screens/Requests/Requests.spec.tsx @@ -1,8 +1,6 @@ import React, { act } from 'react'; import { MockedProvider } from '@apollo/react-testing'; import { render, screen } from '@testing-library/react'; -import 'jest-localstorage-mock'; -import 'jest-location-mock'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; @@ -22,6 +20,34 @@ import { MOCKS4, } from './RequestsMocks'; import useLocalStorage from 'utils/useLocalstorage'; +import { vi } from 'vitest'; + +/** + * Set up `localStorage` stubs for testing. + */ + +vi.stubGlobal('localStorage', { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + removeItem: vi.fn(), +}); + +/** + * Mock `window.location` for testing redirection behavior. + */ + +Object.defineProperty(window, 'location', { + value: { + href: 'http://localhost/', + assign: vi.fn(), + reload: vi.fn(), + pathname: '/', + search: '', + hash: '', + origin: 'http://localhost', + }, +}); const { setItem, removeItem } = useLocalStorage(); @@ -33,6 +59,14 @@ const link5 = new StaticMockLink(MOCKS_WITH_ERROR, true); const link6 = new StaticMockLink(MOCKS3, true); const link7 = new StaticMockLink(MOCKS4, true); +/** + * Utility function to wait for a specified amount of time. + * Wraps `setTimeout` in an `act` block for testing purposes. + * + * @param ms - The duration to wait in milliseconds. Default is 100ms. + * @returns A promise that resolves after the specified time. + */ + async function wait(ms = 100): Promise { await act(() => { return new Promise((resolve) => { @@ -53,7 +87,6 @@ afterEach(() => { describe('Testing Requests screen', () => { test('Component should be rendered properly', async () => { - const loadMoreRequests = jest.fn(); render( From debe6727b767d57873318b8629767b1bce41bb46 Mon Sep 17 00:00:00 2001 From: Antriksh Dangi <132576984+antriksh-9@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:28:58 +0530 Subject: [PATCH 13/24] refactored CSS files in src/screens/Users (#2600) * refactor: Users-css * Update Users.tsx * Update app.module.css * Updated app.module.css * Fixed formatting isuess * Fixed formatting isuess * fixed issues * Fix linting issues --- src/screens/Users/Users.module.css | 95 ------------------------------ src/screens/Users/Users.tsx | 42 +++++++++---- src/style/app.module.css | 74 ++++++++++++----------- 3 files changed, 70 insertions(+), 141 deletions(-) delete mode 100644 src/screens/Users/Users.module.css diff --git a/src/screens/Users/Users.module.css b/src/screens/Users/Users.module.css deleted file mode 100644 index 0750dba108..0000000000 --- a/src/screens/Users/Users.module.css +++ /dev/null @@ -1,95 +0,0 @@ -.btnsContainer { - display: flex; - margin: 2.5rem 0 2.5rem 0; -} - -.btnsContainer .btnsBlock { - display: flex; -} - -.btnsContainer .btnsBlock button { - margin-left: 1rem; - display: flex; - justify-content: center; - align-items: center; -} - -.btnsContainer .inputContainer { - flex: 1; - position: relative; -} -.btnsContainer .input { - width: 70%; - position: relative; -} - -.btnsContainer input { - outline: 1px solid var(--bs-gray-400); -} - -.btnsContainer .inputContainer button { - width: 52px; -} - -.listBox { - width: 100%; - flex: 1; -} - -.notFound { - flex: 1; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -@media (max-width: 1020px) { - .btnsContainer { - flex-direction: column; - margin: 1.5rem 0; - } - .btnsContainer .input { - width: 100%; - } - .btnsContainer .btnsBlock { - margin: 1.5rem 0 0 0; - justify-content: space-between; - } - - .btnsContainer .btnsBlock button { - margin: 0; - } - - .btnsContainer .btnsBlock div button { - margin-right: 1.5rem; - } -} - -/* For mobile devices */ - -@media (max-width: 520px) { - .btnsContainer { - margin-bottom: 0; - } - - .btnsContainer .btnsBlock { - display: block; - margin-top: 1rem; - margin-right: 0; - } - - .btnsContainer .btnsBlock div { - flex: 1; - } - - .btnsContainer .btnsBlock div[title='Sort organizations'] { - margin-right: 0.5rem; - } - - .btnsContainer .btnsBlock button { - margin-bottom: 1rem; - margin-right: 0; - width: 100%; - } -} diff --git a/src/screens/Users/Users.tsx b/src/screens/Users/Users.tsx index 72acba5b5c..3ea165d7b0 100644 --- a/src/screens/Users/Users.tsx +++ b/src/screens/Users/Users.tsx @@ -16,7 +16,7 @@ import TableLoader from 'components/TableLoader/TableLoader'; import UsersTableItem from 'components/UsersTableItem/UsersTableItem'; import InfiniteScroll from 'react-infinite-scroll-component'; import type { InterfaceQueryUserListItem } from 'utils/interfaces'; -import styles from './Users.module.css'; +import styles from '../../style/app.module.css'; import useLocalStorage from 'utils/useLocalstorage'; import type { ApolloError } from '@apollo/client'; /** @@ -91,8 +91,16 @@ const Users = (): JSX.Element => { }: { data?: { users: InterfaceQueryUserListItem[] }; loading: boolean; - fetchMore: any; - refetch: any; + fetchMore: (options: { + variables: Record; + updateQuery: ( + previousQueryResult: { users: InterfaceQueryUserListItem[] }, + options: { + fetchMoreResult?: { users: InterfaceQueryUserListItem[] }; + }, + ) => { users: InterfaceQueryUserListItem[] }; + }) => void; + refetch: (variables?: Record) => void; error?: ApolloError; } = useQuery(USER_LIST, { variables: { @@ -171,9 +179,11 @@ const Users = (): JSX.Element => { setHasMore(true); }; - const handleSearchByEnter = (e: any): void => { + const handleSearchByEnter = ( + e: React.KeyboardEvent, + ): void => { if (e.key === 'Enter') { - const { value } = e.target; + const { value } = e.target as HTMLInputElement; handleSearch(value); } }; @@ -211,16 +221,16 @@ const Users = (): JSX.Element => { { fetchMoreResult, }: { - fetchMoreResult: { users: InterfaceQueryUserListItem[] } | undefined; + fetchMoreResult?: { users: InterfaceQueryUserListItem[] }; }, - ): { users: InterfaceQueryUserListItem[] } | undefined => { + ) => { setIsLoadingMore(false); - if (!fetchMoreResult) return prev; + if (!fetchMoreResult) return prev || { users: [] }; if (fetchMoreResult.users.length < perPageResult) { setHasMore(false); } return { - users: [...(prev?.users || []), ...(fetchMoreResult.users || [])], + users: [...(prev?.users || []), ...fetchMoreResult.users], }; }, }); @@ -401,15 +411,23 @@ const Users = (): JSX.Element => { usersData && displayedUsers.length === 0 && searchByName.length > 0 ? ( -
+

{tCommon('noResultsFoundFor')} "{searchByName}"

-
+ ) : isLoading == false && usersData === undefined && displayedUsers.length === 0 ? ( -
+

{t('noUserFound')}

) : ( diff --git a/src/style/app.module.css b/src/style/app.module.css index fc8a389145..461e2cc260 100644 --- a/src/style/app.module.css +++ b/src/style/app.module.css @@ -81,7 +81,6 @@ align-items: center; gap: 10px; /* Adjust spacing between items */ - margin: 2.5rem 0; } .btnsContainer .btnsBlock { @@ -95,15 +94,28 @@ align-items: center; } -.btnsContainer .input { +.btnsContainer .inputContainer { flex: 1; position: relative; } -.btnsContainer .input button { +.btnsContainer .input { + width: 70%; +} + +.btnsContainer input { + outline: 1px solid var(--bs-gray-400); +} + +.btnsContainer .inputContainer button { width: 52px; } +.listBox { + width: 100%; + flex: 1; +} + .inputField { margin-top: 10px; margin-bottom: 10px; @@ -499,37 +511,6 @@ hr { outline-offset: -2px; } -@media (max-width: 520px) { - .btnsContainer { - margin-bottom: 0; - } - - .btnsContainer .btnsBlock { - display: block; - margin-top: 1rem; - margin-right: 0; - } - - .btnsContainer .btnsBlock div { - flex: 1; - } - - .btnsContainer .btnsBlock div[title='Sort organizations'] { - margin-right: 0.5rem; - } - - .btnsContainer .btnsBlock button { - margin-bottom: 1rem; - margin-right: 0; - width: 100%; - } -} - -.listBox { - width: 100%; - flex: 1; -} - .listTable { width: 100%; box-sizing: border-box; @@ -636,6 +617,31 @@ hr { } } +@media (max-width: 520px) { + .btnsContainer { + margin-bottom: 0; + } + + .btnsContainer .btnsBlock { + display: block; + margin-top: 1rem; + } + + .btnsContainer .btnsBlock div { + flex: 1; + } + + .btnsContainer .btnsBlock div[title='Sort organizations'] { + margin-right: 0.5rem; + } + + .btnsContainer .btnsBlock button { + margin-bottom: 1rem; + margin-right: 0; + width: 100%; + } +} + @media (max-width: 1120px) { .contract { padding-left: calc(250px + 2rem + 1.5rem); From 880f6f3140fc500829f2c8eb545fa602f742ee27 Mon Sep 17 00:00:00 2001 From: Devender singh shekhawat <50149675+devender18@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:30:49 +0530 Subject: [PATCH 14/24] refactored OrganizationDashboard css/ closes #2513 (#2593) * refactored OrganizationDashboard css/ closes #2513 * refactor:OrganizationDashboard_css(fixes #2513) * Plugin and advertisement screen revamp (#2006) * advertisement and plugin screen * added revamped design * fixes added * fixed testcases * chore(deps): bump sass from 1.80.6 to 1.80.7 (#2433) Bumps [sass](https://github.com/sass/dart-sass) from 1.80.6 to 1.80.7. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.80.6...1.80.7) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump eslint-plugin-import from 2.30.0 to 2.31.0 (#2434) Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.30.0 to 2.31.0. - [Release notes](https://github.com/import-js/eslint-plugin-import/releases) - [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md) - [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.30.0...v2.31.0) --- updated-dependencies: - dependency-name: eslint-plugin-import dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @mui/x-charts from 7.22.1 to 7.22.2 (#2435) Bumps [@mui/x-charts](https://github.com/mui/mui-x/tree/HEAD/packages/x-charts) from 7.22.1 to 7.22.2. - [Release notes](https://github.com/mui/mui-x/releases) - [Changelog](https://github.com/mui/mui-x/blob/v7.22.2/CHANGELOG.md) - [Commits](https://github.com/mui/mui-x/commits/v7.22.2/packages/x-charts) --- updated-dependencies: - dependency-name: "@mui/x-charts" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @types/react from 18.3.3 to 18.3.12 (#2436) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.3.3 to 18.3.12. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update pull-request.yml * Update dependabot.yaml * added docker check to workflow (#2414) * added docker check to workflow * made recommended changes to docker check in workflow * added changes to docker check inn workflow as recommended * added changes * updated indentation in pull-request.yml file * updated indentation in pull-request.yml file * added Dockerfile and Docker-compose.yml file * added Dockerfile and Docker-compose.yml file * updated .docker-ignore file * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * added recommended changes by code rabbit * properly formatted code * trying to make docker check pass * trying to make docker check pass * updated INSTALLATION.md * updated INSTALLATION.md * added recommended changes to INSTALLATION.md * added recommended changes to INSTALLATION.md * added recommended changes to INSTALLATION.md * updated docker workflow * updated INSTALLATION.md * eslint-rule-no_unused_vars (#2428) * Updated pr template with checklist (#2454) * Updated pr template with checklist * Additional changes for the PR template * Changed the url for the PR template * refactored addOnStore * refactored addOnEntry v1 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Co-authored-by: Vanshika Sabharwal <143436704+VanshikaSabharwal@users.noreply.github.com> Co-authored-by: prayansh_chhablani <135210710+prayanshchh@users.noreply.github.com> Co-authored-by: Dhiraj Udhani * refactored: CSS files in src/screens/OrgSettings(fixes: #2523) (#2610) * refactored: CSS files in src/screens/OrgSettings(fixes: #2523) * refactored: CSS files in src/screens/OrgSettings(fixes: #2523) * refactored: CSS files in src/screens/OrgSettings(fixes: #2523) * refactored: CSS files in src/screens/OrgSettings(fixes: #2523) * Update pull-request.yml * refactored: CSS files in src/screens/OrganizationDashboard(fixes: #2513) * refactored: CSS files in src/screens/OrganizationDashboard(fixes: #2513) * improve import for global css --------- Signed-off-by: dependabot[bot] Co-authored-by: Shekhar Patel <90516956+duplixx@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Co-authored-by: Vanshika Sabharwal <143436704+VanshikaSabharwal@users.noreply.github.com> Co-authored-by: prayansh_chhablani <135210710+prayanshchh@users.noreply.github.com> Co-authored-by: Dhiraj Udhani --- .../OrganizationDashboard.module.css | 35 ---------------- .../OrganizationDashboard.tsx | 17 ++++---- src/style/app.module.css | 40 ++++++++++++++++++- 3 files changed, 49 insertions(+), 43 deletions(-) delete mode 100644 src/screens/OrganizationDashboard/OrganizationDashboard.module.css diff --git a/src/screens/OrganizationDashboard/OrganizationDashboard.module.css b/src/screens/OrganizationDashboard/OrganizationDashboard.module.css deleted file mode 100644 index 3ffe274196..0000000000 --- a/src/screens/OrganizationDashboard/OrganizationDashboard.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.cardHeader { - padding: 1.25rem 1rem 1rem 1rem; - border-bottom: 1px solid var(--bs-gray-200); - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; -} - -.cardHeader .cardTitle { - font-size: 1.2rem; - font-weight: 600; -} - -.cardBody { - min-height: 180px; - padding-top: 0; - max-height: 570px; - overflow-y: scroll; - width: 100%; - max-width: 400px; -} - -.cardBody .emptyContainer { - display: flex; - height: 180px; - justify-content: center; - align-items: center; -} - -.rankings { - aspect-ratio: 1; - border-radius: 50%; - width: 35px; -} diff --git a/src/screens/OrganizationDashboard/OrganizationDashboard.tsx b/src/screens/OrganizationDashboard/OrganizationDashboard.tsx index ebea874d2e..abc712289c 100644 --- a/src/screens/OrganizationDashboard/OrganizationDashboard.tsx +++ b/src/screens/OrganizationDashboard/OrganizationDashboard.tsx @@ -31,7 +31,7 @@ import type { InterfaceQueryOrganizationsListObject, InterfaceVolunteerRank, } from 'utils/interfaces'; -import styles from './OrganizationDashboard.module.css'; +import styles from 'style/app.module.css'; import { VOLUNTEER_RANKING } from 'GraphQl/Queries/EventVolunteerQueries'; /** @@ -41,7 +41,7 @@ import { VOLUNTEER_RANKING } from 'GraphQl/Queries/EventVolunteerQueries'; * * @returns The rendered component. */ -function organizationDashboard(): JSX.Element { +function OrganizationDashboard(): JSX.Element { const { t } = useTranslation('translation', { keyPrefix: 'dashboard' }); const { t: tCommon } = useTranslation('common'); const { t: tErrors } = useTranslation('errors'); @@ -299,7 +299,7 @@ function organizationDashboard(): JSX.Element { {t('viewAll')}
- + {loadingEvent ? ( [...Array(4)].map((_, index) => { return ; @@ -341,7 +341,7 @@ function organizationDashboard(): JSX.Element { {t('viewAll')} - + {loadingPost ? ( [...Array(4)].map((_, index) => { return ; @@ -392,7 +392,7 @@ function organizationDashboard(): JSX.Element { {loadingOrgData ? ( @@ -435,7 +435,10 @@ function organizationDashboard(): JSX.Element { {t('viewAll')} - + {rankingsLoading ? ( [...Array(3)].map((_, index) => { return ; @@ -483,4 +486,4 @@ function organizationDashboard(): JSX.Element { ); } -export default organizationDashboard; +export default OrganizationDashboard; diff --git a/src/style/app.module.css b/src/style/app.module.css index 461e2cc260..3bc2e1fd6c 100644 --- a/src/style/app.module.css +++ b/src/style/app.module.css @@ -1,5 +1,7 @@ :root { + /* Color contrast ratio: 7.5:1 (exceeds WCAG AAA) */ --high-contrast-text: #494949; + /* Color contrast ratio: 9:1 (exceeds WCAG AAA) */ --high-contrast-border: #2c2c2c; } @@ -561,13 +563,49 @@ hr { display: flex; justify-content: space-between; align-items: center; + margin-bottom: 10px; } - .cardHeader .cardTitle { font-size: 1.2rem; font-weight: 600; } +.containerBody { + min-height: 180px; + padding-top: 0; + max-height: 570px; + overflow-y: auto; + width: 100%; + max-width: min(400px, 90vw); + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.3) transparent; + + &::-webkit-scrollbar { + width: thin; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.3); + } +} + +.containerBody .emptyContainer { + display: flex; + min-height: 180px; + justify-content: center; + align-items: center; +} + +.containerBody .rankings { + aspect-ratio: 1; + border-radius: 50%; + width: 35px; +} + .cardBody { min-height: 180px; } From f4aafd76e3c2ef3018aeb3159ee36ab38b3bc472 Mon Sep 17 00:00:00 2001 From: Abhishek Raj <113784630+abbi4code@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:32:58 +0530 Subject: [PATCH 15/24] Refactor: src/screens/UserPortal/Volunteer/UpcomingEvents from Jest to Vitest (#2623) * migrated UpcomingEvents tests from Jest to Vitest * Rename UpcomingEvents.test.tsx to UpcomingEvents.spec.tsx * add header comments for UpcomingEvents component tests --- ...vents.test.tsx => UpcomingEvents.spec.tsx} | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) rename src/screens/UserPortal/Volunteer/UpcomingEvents/{UpcomingEvents.test.tsx => UpcomingEvents.spec.tsx} (90%) diff --git a/src/screens/UserPortal/Volunteer/UpcomingEvents/UpcomingEvents.test.tsx b/src/screens/UserPortal/Volunteer/UpcomingEvents/UpcomingEvents.spec.tsx similarity index 90% rename from src/screens/UserPortal/Volunteer/UpcomingEvents/UpcomingEvents.test.tsx rename to src/screens/UserPortal/Volunteer/UpcomingEvents/UpcomingEvents.spec.tsx index 43e0b15cdb..f636d8d8d4 100644 --- a/src/screens/UserPortal/Volunteer/UpcomingEvents/UpcomingEvents.test.tsx +++ b/src/screens/UserPortal/Volunteer/UpcomingEvents/UpcomingEvents.spec.tsx @@ -21,11 +21,20 @@ import { } from './UpcomingEvents.mocks'; import { toast } from 'react-toastify'; import useLocalStorage from 'utils/useLocalstorage'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +/** + * Unit tests for the UpcomingEvents component. + * + * This file contains tests to verify the functionality and behavior of the UpcomingEvents component + * under various scenarios, including successful data fetching, error handling, and user interactions. + * Mocked dependencies are used to ensure isolated testing of the component. + */ + +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -81,10 +90,13 @@ const renderUpcomingEvents = (link: ApolloLink): RenderResult => { describe('Testing Upcoming Events Screen', () => { beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId' }), - })); + vi.mock('react-router-dom', async () => { + const actual = await vi.importActual('react-router-dom'); + return { + ...actual, + useParams: () => ({ orgId: 'orgId' }), + }; + }); }); beforeEach(() => { @@ -92,7 +104,7 @@ describe('Testing Upcoming Events Screen', () => { }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('should redirect to fallback URL if URL params are undefined', async () => { From 4aaa8cf2c0dd00ca1542e6ba8eede1f66859e2a0 Mon Sep 17 00:00:00 2001 From: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com> Date: Sun, 15 Dec 2024 20:34:17 +0530 Subject: [PATCH 16/24] fix: simplified error messages for invalid inputs at profile settings (#2500) --- public/locales/en/errors.json | 7 +++- public/locales/fr/errors.json | 7 +++- public/locales/hi/errors.json | 7 +++- public/locales/sp/errors.json | 7 +++- public/locales/zh/errors.json | 7 +++- src/utils/errorHandler.test.tsx | 74 ++++++++++++++++++++++++++++++--- src/utils/errorHandler.tsx | 24 +++++++---- 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/public/locales/en/errors.json b/public/locales/en/errors.json index 752c0db750..ffb0e6f146 100644 --- a/public/locales/en/errors.json +++ b/public/locales/en/errors.json @@ -7,5 +7,10 @@ "emailNotRegistered": "Email not registered", "notFoundMsg": "Oops! The Page you requested was not found!", "errorOccurredCouldntCreate": "An error occurred. Couldn't create {{entity}}", - "errorLoading": "Error occured while loading {{entity}} data" + "errorLoading": "Error occured while loading {{entity}} data", + "invalidPhoneNumber": "Please enter a valid phone number", + "invalidEducationGrade": "Please select a valid education grade", + "invalidEmploymentStatus": "Please select a valid employment status", + "invalidMaritalStatus": "Please select a valid marital status", + "error400": "The submitted information is invalid. Please check your inputs and try again" } diff --git a/public/locales/fr/errors.json b/public/locales/fr/errors.json index e9a7cf4fd9..ae53237404 100644 --- a/public/locales/fr/errors.json +++ b/public/locales/fr/errors.json @@ -7,5 +7,10 @@ "emailNotRegistered": "Email non enregistré", "notFoundMsg": "Oops! ", "errorOccurredCouldntCreate": "Une erreur s'est produite. Impossible de créer {{entity}}", - "errorLoading": "Une erreur s'est produite lors du chargement des données {{entity}}" + "errorLoading": "Une erreur s'est produite lors du chargement des données {{entity}}", + "invalidPhoneNumber": "Veuillez entrer un numéro de téléphone valide", + "invalidEducationGrade": "Veuillez sélectionner un niveau d'études valide", + "invalidEmploymentStatus": "Veuillez sélectionner un statut d'emploi valide", + "invalidMaritalStatus": "Veuillez sélectionner un état matrimonial valide", + "error400": "Réponse non réussie. Code d'état 400 reçu du serveur" } diff --git a/public/locales/hi/errors.json b/public/locales/hi/errors.json index 63b6c3f5d3..64f9523180 100644 --- a/public/locales/hi/errors.json +++ b/public/locales/hi/errors.json @@ -7,5 +7,10 @@ "emailNotRegistered": "ईमेल पंजीकृत नहीं है", "notFoundMsg": "उफ़! ", "errorOccurredCouldntCreate": "एक त्रुटि हुई। {{entity}} नहीं बना सके", - "errorLoading": "{{entity}} डेटा लोड करते समय त्रुटि हुई" + "errorLoading": "{{entity}} डेटा लोड करते समय त्रुटि हुई", + "invalidPhoneNumber": "कृपया एक मान्य फोन-नंबर दर्ज करे", + "invalidEducationGrade": "कृपया एक शिक्षा ग्रेड चुनें", + "invalidEmploymentStatus": "कृपया वैध रोजगार स्थिति चुनें", + "invalidMaritalStatus": "कृपया वैध वैवाहिक स्थिति चुनें", + "error400": "आपकी जानकारी सहेजी नहीं जा सकी। कृपया अपनी प्रविष्टियों की जांच करें और पुनः प्रयास करें।" } diff --git a/public/locales/sp/errors.json b/public/locales/sp/errors.json index 39b579abac..7489356b5e 100644 --- a/public/locales/sp/errors.json +++ b/public/locales/sp/errors.json @@ -7,5 +7,10 @@ "emailNotRegistered": "Email not registered", "notFoundMsg": "Oops! The Page you requested was not found!", "errorOccurredCouldntCreate": "Ocurrió un error. No se pudo crear {{entity}}", - "errorLoading": "Ocurrió un error al cargar los datos de {{entity}}" + "errorLoading": "Ocurrió un error al cargar los datos de {{entity}}", + "invalidPhoneNumber": "Por favor, introduzca un número de teléfono válido", + "invalidEducationGrade": "Por favor seleccione un grado de educación válido", + "invalidEmploymentStatus": "Por favor seleccione un estado de empleo válido", + "invalidMaritalStatus": "Por favor seleccione un estado civil válido", + "error400": "Respuesta no exitosa. Se recibió el código de estado 400 del servidor" } diff --git a/public/locales/zh/errors.json b/public/locales/zh/errors.json index c872f367a5..c289d67aa1 100644 --- a/public/locales/zh/errors.json +++ b/public/locales/zh/errors.json @@ -7,5 +7,10 @@ "emailNotRegistered": "邮箱未注册", "notFoundMsg": "哎呀!", "errorOccurredCouldntCreate": "发生错误。 无法创建{{entity}}", - "errorLoading": "加载{{entity}}数据时出错" + "errorLoading": "加载{{entity}}数据时出错", + "invalidPhoneNumber": "请选择一个有效的电话号码", + "invalidEducationGrade": "请选择教育年级", + "invalidEmploymentStatus": "请选择有效的就业状况", + "invalidMaritalStatus": "请选择有效的婚姻状况", + "error400": "响应不成功. 从服务器收到状态代码 400" } diff --git a/src/utils/errorHandler.test.tsx b/src/utils/errorHandler.test.tsx index 45f46e6389..f229e8d5fa 100644 --- a/src/utils/errorHandler.test.tsx +++ b/src/utils/errorHandler.test.tsx @@ -11,23 +11,87 @@ jest.mock('react-toastify', () => ({ describe('Test if errorHandler is working properly', () => { const t: TFunction = (key: string) => key; - const tErrors: TFunction = (key: string, options?: Record) => - key; + const tErrors: TFunction = ( + key: string, + options?: Record, + ) => { + if (options) { + console.log(`options are passed, but the function returns only ${key}`); + } + return key; + }; - it('should call toast.error with the correct message if error message is "Failed to fetch"', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call toast.error with the correct message if error message is "Failed to fetch"', async () => { const error = new Error('Failed to fetch'); errorHandler(t, error); expect(toast.error).toHaveBeenCalledWith(tErrors('talawaApiUnavailable')); }); - it('should call toast.error with the error message if it is not "Failed to fetch"', () => { - const error = new Error('Some other error message'); + it('should call toast.error with the correct message if error message contains this substring "Value is not a valid phone number"', () => { + const error = new Error('This value is not a valid phone number'); errorHandler(t, error); + expect(toast.error).toHaveBeenCalledWith(tErrors('invalidPhoneNumber')); + }); + + test.each([ + ['EducationGrade', 'invalidEducationGrade'], + ['EmploymentStatus', 'invalidEmploymentStatus'], + ['MaritalStatus', 'invalidMaritalStatus'], + ])('should handle invalid %s error', (field, expectedKey) => { + const error = new Error(`This value does not exist in "${field}"`); + errorHandler(t, error); + expect(toast.error).toHaveBeenCalledWith(tErrors(expectedKey)); + }); + + it('should call toast.error with the correct message if error message contains this substring "status code 400"', () => { + const error = new Error('Server responded with status code 400'); + errorHandler(t, error); + + expect(toast.error).toHaveBeenCalledWith(tErrors('error400')); + }); + + it('should handle error messages with different cases', () => { + errorHandler(t, new Error('VALUE IS NOT A VALID PHONE NUMBER')); + expect(toast.error).toHaveBeenCalledWith(tErrors('invalidPhoneNumber')); + + errorHandler(t, new Error('This Value Does Not Exist in "EducationGrade"')); + expect(toast.error).toHaveBeenCalledWith(tErrors('invalidEducationGrade')); + }); + it('should call toast.error with the error message if it is an instance of error but have not matched any error message patterns', () => { + const error = new Error('Bandhan sent an error message'); + errorHandler(t, error); expect(toast.error).toHaveBeenCalledWith(error.message); }); + it('should handle different types for the first parameter while still showing error messages', () => { + errorHandler(undefined, new Error('Some error')); + expect(toast.error).toHaveBeenCalled(); + + errorHandler(null, new Error('Some error')); + expect(toast.error).toHaveBeenCalled(); + + errorHandler({}, new Error('Some error')); + expect(toast.error).toHaveBeenCalled(); + }); + + it('should handle non-null but non-Error objects for the error parameter', () => { + errorHandler(t, { message: 'Error message in object' }); + expect(toast.error).toHaveBeenCalledWith( + tErrors('unknownError', { msg: { message: 'Error message in object' } }), + ); + + errorHandler(t, 'Direct error message'); + expect(toast.error).toHaveBeenCalledWith( + tErrors('unknownError', { msg: 'Direct error message' }), + ); + }); + it('should call toast.error with the error message if error object is falsy', () => { const error = null; errorHandler(t, error); diff --git a/src/utils/errorHandler.tsx b/src/utils/errorHandler.tsx index b7a22210a8..e4e543e940 100644 --- a/src/utils/errorHandler.tsx +++ b/src/utils/errorHandler.tsx @@ -5,18 +5,26 @@ import i18n from './i18n'; /** This function is used to handle api errors in the application. It takes in the error object and displays the error message to the user. - If the error is due to the Talawa API being unavailable, it displays a custom message. + If the error is due to the Talawa API being unavailable, it displays a custom message. And for other error cases, it is using regular expression (case-insensitive) to match and show valid messages */ export const errorHandler = (a: unknown, error: unknown): void => { const tErrors: TFunction = i18n.getFixedT(null, 'errors'); if (error instanceof Error) { - switch (error.message) { - case 'Failed to fetch': - toast.error(tErrors('talawaApiUnavailable') as string); - break; - // Add more cases as needed - default: - toast.error(error.message); + const errorMessage = error.message; + if (errorMessage === 'Failed to fetch') { + toast.error(tErrors('talawaApiUnavailable')); + } else if (errorMessage.match(/value is not a valid phone number/i)) { + toast.error(tErrors('invalidPhoneNumber')); + } else if (errorMessage.match(/does not exist in "EducationGrade"/i)) { + toast.error(tErrors('invalidEducationGrade')); + } else if (errorMessage.match(/does not exist in "EmploymentStatus"/i)) { + toast.error(tErrors('invalidEmploymentStatus')); + } else if (errorMessage.match(/does not exist in "MaritalStatus"/i)) { + toast.error(tErrors('invalidMaritalStatus')); + } else if (errorMessage.match(/status code 400/i)) { + toast.error(tErrors('error400')); + } else { + toast.error(errorMessage); } } else { toast.error(tErrors('unknownError', { msg: error }) as string); From c849b44624fc434eb246f88892ff4f4bcd513344 Mon Sep 17 00:00:00 2001 From: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Date: Sun, 15 Dec 2024 08:25:04 -0800 Subject: [PATCH 17/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cbade9e407..a27feca60b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Talawa Admin -[💬 Join the community on Slack](https://github.com/PalisadoesFoundation/) +💬 Join the community on Slack. The link can be found in our [Palisadoes Foundation GitHub Home Page](https://github.com/PalisadoesFoundation) ![talawa-logo-lite-200x200](https://github.com/PalisadoesFoundation/talawa-admin/assets/16875803/26291ec5-d3c1-4135-8bc7-80885dff613d) From 9723078f3522b576427e722a043a7caebcff7834 Mon Sep 17 00:00:00 2001 From: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Date: Sun, 15 Dec 2024 11:13:54 -0800 Subject: [PATCH 18/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a27feca60b..911ba11453 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Talawa Admin -💬 Join the community on Slack. The link can be found in our [Palisadoes Foundation GitHub Home Page](https://github.com/PalisadoesFoundation) +💬 Join the community on Slack from our [Palisadoes Foundation GitHub Home Page](https://github.com/PalisadoesFoundation) ![talawa-logo-lite-200x200](https://github.com/PalisadoesFoundation/talawa-admin/assets/16875803/26291ec5-d3c1-4135-8bc7-80885dff613d) From 4f050523f1e5a9f980a59fea1494bd6b10af9bff Mon Sep 17 00:00:00 2001 From: Shiva <148421597+shivasankaran18@users.noreply.github.com> Date: Thu, 19 Dec 2024 23:10:33 +0530 Subject: [PATCH 19/24] refactor:vitest to MemberDetailScreen (#2662) --- jest.config.js | 4 ++- package-lock.json | 1 + .../OrganizationScreen.test.tsx | 8 ++--- ...rDetail.test.tsx => MemberDetail.spec.tsx} | 32 ++++++++++++------- 4 files changed, 26 insertions(+), 19 deletions(-) rename src/screens/MemberDetail/{MemberDetail.test.tsx => MemberDetail.spec.tsx} (95%) diff --git a/jest.config.js b/jest.config.js index 3083bcda4f..a3c6bbecbf 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,7 +10,8 @@ export default { '!**/index.{js,ts}', '!**/*.d.ts', '!src/test/**', - '!vitest.config.ts',], + '!vitest.config.ts', + ], // setupFiles: ['react-app-polyfill/jsdom'], setupFiles: ['whatwg-fetch'], setupFilesAfterEnv: ['/src/setupTests.ts'], @@ -42,6 +43,7 @@ export default { '\\.svg\\?react$': '/scripts/__mocks__/fileMock.js', '\\.svg$': '/scripts/__mocks__/fileMock.js', '^@pdfme/generator$': '/scripts/__mocks__/@pdfme/generator.ts', + '\\.(css|less|scss|sass)$': 'identity-obj-proxy', }, moduleFileExtensions: [ 'web.js', diff --git a/package-lock.json b/package-lock.json index 2404c03835..f07c9e0e22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11375,6 +11375,7 @@ "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", "dev": true, + "license": "MIT", "dependencies": { "harmony-reflect": "^1.4.6" }, diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.test.tsx index cd039cc3ca..6293562a5a 100644 --- a/src/components/OrganizationScreen/OrganizationScreen.test.tsx +++ b/src/components/OrganizationScreen/OrganizationScreen.test.tsx @@ -79,17 +79,13 @@ describe('Testing OrganizationScreen', () => { const closeButton = screen.getByTestId('closeMenu'); fireEvent.click(closeButton); - - // Check for contract class after closing - expect(screen.getByTestId('mainpageright')).toHaveClass('_expand_ccl5z_8'); + expect(screen.getByTestId('mainpageright')).toHaveClass(styles.expand); const openButton = screen.getByTestId('openMenu'); fireEvent.click(openButton); // Check for expand class after opening - expect(screen.getByTestId('mainpageright')).toHaveClass( - '_contract_ccl5z_61', - ); + expect(screen.getByTestId('mainpageright')).toHaveClass(styles.contract); }); test('handles window resize', () => { diff --git a/src/screens/MemberDetail/MemberDetail.test.tsx b/src/screens/MemberDetail/MemberDetail.spec.tsx similarity index 95% rename from src/screens/MemberDetail/MemberDetail.test.tsx rename to src/screens/MemberDetail/MemberDetail.spec.tsx index b0514701f5..4c1a68369f 100644 --- a/src/screens/MemberDetail/MemberDetail.test.tsx +++ b/src/screens/MemberDetail/MemberDetail.spec.tsx @@ -21,6 +21,7 @@ import MemberDetail, { getLanguageName, prettyDate } from './MemberDetail'; import { MOCKS1, MOCKS2, MOCKS3 } from './MemberDetailMocks'; import type { ApolloLink } from '@apollo/client'; import { toast } from 'react-toastify'; +import { vi } from 'vitest'; const link1 = new StaticMockLink(MOCKS1, true); const link2 = new StaticMockLink(MOCKS2, true); @@ -44,21 +45,28 @@ const translations = { ), }; -jest.mock('@mui/x-date-pickers/DateTimePicker', () => { +vi.mock('@mui/x-date-pickers/DateTimePicker', async () => { + const actual = await vi.importActual( + '@mui/x-date-pickers/DesktopDateTimePicker', + ); return { - DateTimePicker: jest.requireActual( - '@mui/x-date-pickers/DesktopDateTimePicker', - ).DesktopDateTimePicker, + DateTimePicker: actual.DesktopDateTimePicker, }; }); -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); +vi.mock('@dicebear/core', () => ({ + createAvatar: vi.fn(() => ({ + toDataUri: vi.fn(() => 'mocked-data-uri'), + })), +})); + const props = { id: 'rishav-jha-mech', }; @@ -87,10 +95,10 @@ const renderMemberDetailScreen = (link: ApolloLink): RenderResult => { }; describe('MemberDetail', () => { - global.alert = jest.fn(); + global.alert = vi.fn(); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); cleanup(); }); @@ -115,7 +123,7 @@ describe('MemberDetail', () => { test('prettyDate function should work properly', () => { // If the date is provided - const datePretty = jest.fn(prettyDate); + const datePretty = vi.fn(prettyDate); expect(datePretty('2023-02-18T09:22:27.969Z')).toBe( prettyDate('2023-02-18T09:22:27.969Z'), ); @@ -124,7 +132,7 @@ describe('MemberDetail', () => { }); test('getLanguageName function should work properly', () => { - const getLangName = jest.fn(getLanguageName); + const getLangName = vi.fn(getLanguageName); // If the language code is provided expect(getLangName('en')).toBe('English'); // If the language code is not provided @@ -229,7 +237,7 @@ describe('MemberDetail', () => { expect(screen.queryByText('Loading data...')).not.toBeInTheDocument(); - const dicebearUrl = `mocked-data-uri`; + const dicebearUrl = 'mocked-data-uri'; const userImage = await screen.findByTestId('userImageAbsent'); expect(userImage).toBeInTheDocument(); From 8169150d979931e26b8c0daff3e66eb0547a6551 Mon Sep 17 00:00:00 2001 From: Shiva <148421597+shivasankaran18@users.noreply.github.com> Date: Thu, 19 Dec 2024 23:11:56 +0530 Subject: [PATCH 20/24] Refactor:Vitest to OrgPeople Screen (#2663) --- ...e.test.tsx => OrganizationPeople.spec.tsx} | 62 ++++++++++++++----- 1 file changed, 47 insertions(+), 15 deletions(-) rename src/screens/OrganizationPeople/{OrganizationPeople.test.tsx => OrganizationPeople.spec.tsx} (95%) diff --git a/src/screens/OrganizationPeople/OrganizationPeople.test.tsx b/src/screens/OrganizationPeople/OrganizationPeople.spec.tsx similarity index 95% rename from src/screens/OrganizationPeople/OrganizationPeople.test.tsx rename to src/screens/OrganizationPeople/OrganizationPeople.spec.tsx index a840f1e1f0..1477c646f6 100644 --- a/src/screens/OrganizationPeople/OrganizationPeople.test.tsx +++ b/src/screens/OrganizationPeople/OrganizationPeople.spec.tsx @@ -2,6 +2,7 @@ import React, { act } from 'react'; import { MockedProvider } from '@apollo/react-testing'; import { fireEvent, render, screen } from '@testing-library/react'; import { Provider } from 'react-redux'; +import type { Params } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom'; import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; @@ -13,7 +14,7 @@ import { USERS_CONNECTION_LIST, USER_LIST_FOR_TABLE, } from 'GraphQl/Queries/Queries'; -import 'jest-location-mock'; +// import 'jest-location-mock'; import i18nForTest from 'utils/i18nForTest'; import { StaticMockLink } from 'utils/StaticMockLink'; import { @@ -21,6 +22,29 @@ import { SIGNUP_MUTATION, } from 'GraphQl/Mutations/mutations'; import type { TestMock } from './MockDataTypes'; +import { vi } from 'vitest'; + +/** + * Mock window.location for testing redirection behavior. + */ + +Object.defineProperty(window, 'location', { + value: { + href: 'http://localhost/', + assign: vi.fn((url) => { + const urlObj = new URL(url, 'http://localhost'); + window.location.href = urlObj.href; + window.location.pathname = urlObj.pathname; + window.location.search = urlObj.search; + window.location.hash = urlObj.hash; + }), + reload: vi.fn(), + pathname: '/', + search: '', + hash: '', + origin: 'http://localhost', + }, +}); const createMemberMock = ( orgId = '', @@ -596,14 +620,18 @@ async function wait(ms = 2): Promise { }); } const linkURL = 'orgid'; -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: linkURL }), -})); + +vi.mock('react-router-dom', async () => { + const actualDom = await vi.importActual('react-router-dom'); + return { + ...actualDom, + useParams: (): Readonly> => ({ orgId: linkURL }), + }; +}); // TODO - REMOVE THE NEXT LINE IT IS TO SUPPRESS THE ERROR // FOR THE FIRST TEST WHICH CAME OUT OF NOWHERE -console.error = jest.fn(); +console.error = vi.fn(); describe('Organization People Page', () => { const searchData = { @@ -683,7 +711,7 @@ describe('Organization People Page', () => { }, ]); - expect(window.location).toBeAt('/orgpeople/orgid'); + expect(window.location.href).toBe('http://localhost/orgpeople/orgid'); }); test('It is necessary to query the correct mock data.', async () => { @@ -705,7 +733,7 @@ describe('Organization People Page', () => { await wait(); - expect(window.location).toBeAt('/orgpeople/orgid'); + expect(window.location.href).toBe('http://localhost/orgpeople/orgid'); }); test('Testing MEMBERS list', async () => { @@ -753,7 +781,7 @@ describe('Organization People Page', () => { ); await wait(); - expect(window.location).toBeAt('/orgpeople/orgid'); + expect(window.location.href).toBe('http://localhost/orgpeople/orgid'); }); test('Testing MEMBERS list with filters', async () => { @@ -792,7 +820,7 @@ describe('Organization People Page', () => { await wait(); expect(findtext).toBeInTheDocument(); await wait(); - expect(window.location).toBeAt('/orgpeople/orgid'); + expect(window.location.href).toBe('http://localhost/orgpeople/orgid'); }); test('Testing ADMIN LIST', async () => { @@ -855,7 +883,7 @@ describe('Organization People Page', () => { // Wait for any asynchronous operations to complete await wait(); - expect(window.location).toBeAt('/orgpeople/orgid'); + expect(window.location.href).toBe('http://localhost/orgpeople/orgid'); }); test('Testing ADMIN list with filters', async () => { @@ -905,7 +933,7 @@ describe('Organization People Page', () => { // Ensure that the name is still present after filtering await wait(); - expect(window.location).toBeAt('/orgpeople/orgid'); + expect(window.location.href).toBe('http://localhost/orgpeople/orgid'); }); test('Testing add existing user modal', async () => { @@ -1256,7 +1284,9 @@ describe('Organization People Page', () => { const btn = screen.getByTestId('searchbtn'); userEvent.click(btn); await wait(); - expect(window.location).toBeAt('/orgpeople/6401ff65ce8e8406b8f07af1'); + expect(window.location.href).toBe( + 'http://localhost/orgpeople/6401ff65ce8e8406b8f07af1', + ); }); test('Testing USERS list with filters', async () => { @@ -1289,7 +1319,9 @@ describe('Organization People Page', () => { const btn = screen.getByTestId('searchbtn'); userEvent.click(btn); await wait(); - expect(window.location).toBeAt('/orgpeople/6401ff65ce8e8406b8f07af2'); + expect(window.location.href).toBe( + 'http://localhost/orgpeople/6401ff65ce8e8406b8f07af2', + ); }); test('Add Member component renders', async () => { @@ -1378,7 +1410,7 @@ describe('Organization People Page', () => { ); await wait(); - expect(window.location).toBeAt('/orgpeople/orgid'); + expect(window.location.href).toBe('http://localhost/orgpeople/orgid'); expect(screen.queryByText(/Nothing Found !!/i)).toBeInTheDocument(); }); }); From 4b45d851023a2efd841526f55bb1dcbb54feff22 Mon Sep 17 00:00:00 2001 From: raggettii <155475892+raggettii@users.noreply.github.com> Date: Thu, 19 Dec 2024 23:24:36 +0530 Subject: [PATCH 21/24] fix : Search retains previous input after closing the modal instead of resetting to default data --- src/screens/OrganizationPeople/AddMember.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/screens/OrganizationPeople/AddMember.tsx b/src/screens/OrganizationPeople/AddMember.tsx index 750d831abf..c7987b738d 100644 --- a/src/screens/OrganizationPeople/AddMember.tsx +++ b/src/screens/OrganizationPeople/AddMember.tsx @@ -76,6 +76,9 @@ function AddMember(): JSX.Element { function openAddUserModal(): void { setAddUserModalIsOpen(true); } + useEffect(() => { + setUserName(''); + }, [addUserModalisOpen]); const toggleDialogModal = (): void => setAddUserModalIsOpen(!addUserModalisOpen); From 395fbc02c74032c80160ad30b16f53f454d309d1 Mon Sep 17 00:00:00 2001 From: Amaan ali Date: Fri, 20 Dec 2024 06:43:31 +0530 Subject: [PATCH 22/24] Refactor CSS files in src/screens/OrgContribution (Fixs : #2520) (#2674) * merged OrgContribution.tsx and it's related components file's corresponding css file code into global css file (app.module.css) * fix failed test 1 and 2 * tried to resolve error 'Runs Introspection on the GitHub talawa-api repo on the schema.graphql file' --- package-lock.json | 112 ++-- .../Mutations/ActionItemCategoryMutations.ts | 2 +- .../ContriStats/ContriStats.module.css | 7 - src/components/ContriStats/ContriStats.tsx | 19 +- .../OrgContriCards/OrgContriCards.module.css | 22 - .../OrgContriCards/OrgContriCards.tsx | 2 +- .../OrgContribution.module.css | 261 ---------- .../OrgContribution/OrgContribution.tsx | 7 +- src/style/app.module.css | 485 +++++++----------- 9 files changed, 273 insertions(+), 644 deletions(-) delete mode 100644 src/components/ContriStats/ContriStats.module.css delete mode 100644 src/components/OrgContriCards/OrgContriCards.module.css delete mode 100644 src/screens/OrgContribution/OrgContribution.module.css diff --git a/package-lock.json b/package-lock.json index f07c9e0e22..d227ca252c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6378,13 +6378,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.5.tgz", - "integrity": "sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.5", - "@vitest/utils": "2.1.5", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -6393,12 +6394,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.5.tgz", - "integrity": "sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.5", + "@vitest/spy": "2.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, @@ -6419,10 +6421,11 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.5.tgz", - "integrity": "sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, + "license": "MIT", "dependencies": { "tinyrainbow": "^1.2.0" }, @@ -6431,12 +6434,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.5.tgz", - "integrity": "sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", + "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.5", + "@vitest/utils": "2.1.8", "pathe": "^1.1.2" }, "funding": { @@ -6444,12 +6448,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.5.tgz", - "integrity": "sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", + "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.5", + "@vitest/pretty-format": "2.1.8", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -6458,10 +6463,11 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.5.tgz", - "integrity": "sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", + "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", "dev": true, + "license": "MIT", "dependencies": { "tinyspy": "^3.0.2" }, @@ -6470,12 +6476,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz", - "integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.5", + "@vitest/pretty-format": "2.1.8", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -6896,6 +6903,7 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" } @@ -7694,6 +7702,7 @@ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -7817,6 +7826,7 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", "dev": true, + "license": "MIT", "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", @@ -7890,6 +7900,7 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 16" } @@ -8787,6 +8798,7 @@ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -9354,7 +9366,8 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.0.0", @@ -10129,6 +10142,7 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } @@ -14690,7 +14704,8 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lower-case": { "version": "2.0.2", @@ -15925,13 +15940,15 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/pathval": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14.16" } @@ -18451,6 +18468,7 @@ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -19720,10 +19738,11 @@ } }, "node_modules/vite-node": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.5.tgz", - "integrity": "sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", "dev": true, + "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", @@ -20053,18 +20072,19 @@ } }, "node_modules/vitest": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.5.tgz", - "integrity": "sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.5", - "@vitest/mocker": "2.1.5", - "@vitest/pretty-format": "^2.1.5", - "@vitest/runner": "2.1.5", - "@vitest/snapshot": "2.1.5", - "@vitest/spy": "2.1.5", - "@vitest/utils": "2.1.5", + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", @@ -20076,7 +20096,7 @@ "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.5", + "vite-node": "2.1.8", "why-is-node-running": "^2.3.0" }, "bin": { @@ -20091,8 +20111,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.5", - "@vitest/ui": "2.1.5", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", "happy-dom": "*", "jsdom": "*" }, diff --git a/src/GraphQl/Mutations/ActionItemCategoryMutations.ts b/src/GraphQl/Mutations/ActionItemCategoryMutations.ts index 92e7b0968c..4b4c51bc78 100644 --- a/src/GraphQl/Mutations/ActionItemCategoryMutations.ts +++ b/src/GraphQl/Mutations/ActionItemCategoryMutations.ts @@ -29,7 +29,7 @@ export const CREATE_ACTION_ITEM_CATEGORY_MUTATION = gql` * * @param id - The id of the ActionItemCategory to be updated. * @param name - Updated name of the ActionItemCategory. - * @param isDisabled - Updated disabled status of the ActionItemCategory. + * @param isDisabled - Updated disabled status of the ActionItemCategory. */ export const UPDATE_ACTION_ITEM_CATEGORY_MUTATION = gql` diff --git a/src/components/ContriStats/ContriStats.module.css b/src/components/ContriStats/ContriStats.module.css deleted file mode 100644 index 7d6c83ea8e..0000000000 --- a/src/components/ContriStats/ContriStats.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.fonts { - color: #707070; -} - -.fonts > span { - font-weight: 600; -} diff --git a/src/components/ContriStats/ContriStats.tsx b/src/components/ContriStats/ContriStats.tsx index a2db307d91..91e6b9872f 100644 --- a/src/components/ContriStats/ContriStats.tsx +++ b/src/components/ContriStats/ContriStats.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import styles from './ContriStats.module.css'; +import styles from '../../style/app.module.css'; interface InterfaceContriStatsProps { id: string; @@ -13,11 +13,17 @@ interface InterfaceContriStatsProps { /** * A component that displays contribution statistics. * - * @param props - The properties passed to the component, including `recentAmount`, `highestAmount`, and `totalAmount`. + * @param recentAmount - The most recent contribution amount. + * @param highestAmount - The highest contribution amount. + * @param totalAmount - The total contribution amount. * * @returns JSX.Element - The rendered component displaying the contribution stats. */ -function ContriStats(props: InterfaceContriStatsProps): JSX.Element { +function ContriStats({ + recentAmount, + highestAmount, + totalAmount, +}: InterfaceContriStatsProps): JSX.Element { const { t } = useTranslation('translation', { keyPrefix: 'contriStats', }); @@ -25,15 +31,16 @@ function ContriStats(props: InterfaceContriStatsProps): JSX.Element { return ( <>

- {t('recentContribution')}: $ {props.recentAmount} + {t('recentContribution')}: $ {recentAmount}

- {t('highestContribution')}: $ {props.highestAmount} + {t('highestContribution')}: $ {highestAmount}

- {t('totalContribution')}: $ {props.totalAmount} + {t('totalContribution')}: $ {totalAmount}

); } + export default ContriStats; diff --git a/src/components/OrgContriCards/OrgContriCards.module.css b/src/components/OrgContriCards/OrgContriCards.module.css deleted file mode 100644 index d20b696621..0000000000 --- a/src/components/OrgContriCards/OrgContriCards.module.css +++ /dev/null @@ -1,22 +0,0 @@ -.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; -} -.cards > h3 { - font-size: 17px; -} -.cards > p { - font-size: 14px; - margin-top: -5px; - margin-bottom: 7px; -} diff --git a/src/components/OrgContriCards/OrgContriCards.tsx b/src/components/OrgContriCards/OrgContriCards.tsx index 6635be09b8..84237013c8 100644 --- a/src/components/OrgContriCards/OrgContriCards.tsx +++ b/src/components/OrgContriCards/OrgContriCards.tsx @@ -3,7 +3,7 @@ import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; import { useTranslation } from 'react-i18next'; -import styles from './OrgContriCards.module.css'; +import styles from '../../style/app.module.css'; /** * Props for the OrgContriCards component diff --git a/src/screens/OrgContribution/OrgContribution.module.css b/src/screens/OrgContribution/OrgContribution.module.css deleted file mode 100644 index 7ca9333bf7..0000000000 --- a/src/screens/OrgContribution/OrgContribution.module.css +++ /dev/null @@ -1,261 +0,0 @@ -.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 { - z-index: 0; - padding-top: 5px; - margin: 0; - height: 100%; -} -.sidebar:after { - background-color: #f7f7f7; - position: absolute; - width: 2px; - height: 600px; - top: 10px; - left: 94%; - display: block; -} -.sidebarsticky { - padding-left: 45px; - margin-top: 7px; -} -.sidebarsticky > p { - margin-top: -10px; -} - -.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: 18px; - margin-bottom: 20px; - padding-bottom: 5px; - border-bottom: 3px solid #31bb6b; - width: 60%; -} -.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; -} -@media screen and (max-width: 575.5px) { - .justifysp { - padding-left: 55px; - display: flex; - justify-content: space-between; - width: 100%; - } -} -.addbtn { - border: 1px solid #e8e5e5; - box-shadow: 0 2px 2px #e8e5e5; - border-radius: 5px; - background-color: #31bb6b; - width: 15%; - height: 40px; - font-size: 16px; - 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; - width: 30%; - 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%; -} -.sidebarsticky > input { - text-decoration: none; - margin-bottom: 50px; - border-color: #e8e5e5; - width: 80%; - border-radius: 7px; - padding-top: 5px; - padding-bottom: 5px; - padding-right: 10px; - padding-left: 10px; - box-shadow: none; -} - -.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); - } -} diff --git a/src/screens/OrgContribution/OrgContribution.tsx b/src/screens/OrgContribution/OrgContribution.tsx index db155cdb47..e2061f5587 100644 --- a/src/screens/OrgContribution/OrgContribution.tsx +++ b/src/screens/OrgContribution/OrgContribution.tsx @@ -2,11 +2,10 @@ import React from 'react'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import { useTranslation } from 'react-i18next'; - import ContriStats from 'components/ContriStats/ContriStats'; import OrgContriCards from 'components/OrgContriCards/OrgContriCards'; import { Form } from 'react-bootstrap'; -import styles from './OrgContribution.module.css'; +import styles from '../../style/app.module.css'; /** * The `orgContribution` component displays the contributions to an organization. @@ -14,7 +13,7 @@ import styles from './OrgContribution.module.css'; * Additionally, it shows recent contribution statistics and a list of contribution cards. * */ -function orgContribution(): JSX.Element { +function OrgContribution(): JSX.Element { // Hook to get translation functions and translation text const { t } = useTranslation('translation', { keyPrefix: 'orgContribution', @@ -83,4 +82,4 @@ function orgContribution(): JSX.Element { ); } -export default orgContribution; +export default OrgContribution; diff --git a/src/style/app.module.css b/src/style/app.module.css index 3bc2e1fd6c..e2ad209d3b 100644 --- a/src/style/app.module.css +++ b/src/style/app.module.css @@ -1,12 +1,142 @@ :root { - /* Color contrast ratio: 7.5:1 (exceeds WCAG AAA) */ - --high-contrast-text: #494949; - /* Color contrast ratio: 9:1 (exceeds WCAG AAA) */ - --high-contrast-border: #2c2c2c; + --brown-color: #555555; + --dropdown-hover-color: #eff1f7; + --grey-bg-color: #eaebef; + --subtle-blue-grey: #7c9beb; + --subtle-blue-grey-hover: #5f7e91; + --modal-width: 670px; + --modal-max-width: 680px; + --input-shadow-color: #dddddd; + --delete-button-bg: #f8d6dc; + --delete-button-color: #ff4d4f; + --search-button-bg: #a8c7fa; + --search-button-border: #555555; + --table-image-size: 50px; + --table-head-bg: var( + --bs-primary, + blue + ); /* Assuming var(--bs-primary) is defined elsewhere */ + --table-head-color: white; + --table-header-color: var(--bs-greyish-black, black); + --table-head-radius: 20px; + --table-bg-color: #eaebef; + --tablerow-bg-color: #eff1f7; + --row-background: var(--bs-white, white); + --font-size-header: 16px; +} +.fonts { + color: #707070; +} + +.fonts > span { + font-weight: 600; } -.noOutline input { - outline: none; +.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; +} +.cards > h3 { + font-size: 17px; +} +.cards > p { + font-size: 14px; + margin-top: -5px; + margin-bottom: 7px; +} + +.sidebar { + z-index: 0; + padding-top: 5px; + margin: 0; + height: 100%; +} +.sidebar:after { + background-color: #f7f7f7; + position: absolute; + width: 2px; + height: 600px; + top: 10px; + left: 94%; + display: block; +} +.sidebarsticky { + padding-left: 45px; + margin-top: 7px; +} +.sidebarsticky > p { + margin-top: -10px; +} + +.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: 18px; + margin-bottom: 20px; + padding-bottom: 5px; + border-bottom: 3px solid #31bb6b; + width: 60%; +} + +.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; +} +@media screen and (max-width: 575.5px) { + .justifysp { + padding-left: 55px; + display: flex; + justify-content: space-between; + width: 100%; + } +} + +.sidebarsticky > input { + text-decoration: none; + margin-bottom: 50px; + border-color: #e8e5e5; + width: 80%; + border-radius: 7px; + padding-top: 5px; + padding-bottom: 5px; + padding-right: 10px; + padding-left: 10px; + box-shadow: none; } .noOutline:is(:hover, :focus, :active, :focus-visible, .show) { @@ -43,8 +173,8 @@ .dropdown { background-color: white; - border: 1px solid var(--dropdown-border-color); - color: var(--dropdown-text-color); + border: 1px solid var(--brown-color); + color: var(--brown-color); position: relative; display: inline-block; margin-top: 10px; @@ -53,13 +183,13 @@ .dropdown:is(:hover, :focus, :active, :focus-visible, .show) { background-color: transparent !important; - border: 1px solid var(--dropdown-border-color); - color: var(--dropdown-text-color) !important; + border: 1px solid var(--brown-color); + color: var(--brown-color) !important; } .dropdownItem { background-color: white !important; - color: var(--dropdown-text-color) !important; + color: var(--brown-color) !important; border: none !important; } @@ -67,22 +197,20 @@ .dropdownItem:focus, .dropdownItem:active { background-color: var(--dropdown-hover-color) !important; - color: var(--dropdown-text-color) !important; + color: var(--brown-color) !important; outline: none !important; box-shadow: none !important; } .input { - flex: 3; + flex: 1; + position: relative; } .btnsContainer { display: flex; margin: 2.5rem 0; - align-items: center; - gap: 10px; - /* Adjust spacing between items */ } .btnsContainer .btnsBlock { @@ -96,74 +224,15 @@ align-items: center; } -.btnsContainer .inputContainer { - flex: 1; - position: relative; -} - .btnsContainer .input { - width: 70%; -} - -.btnsContainer input { - outline: 1px solid var(--bs-gray-400); -} - -.btnsContainer .inputContainer button { - width: 52px; -} - -.listBox { - width: 100%; flex: 1; -} - -.inputField { - margin-top: 10px; - margin-bottom: 10px; - - background-color: white; - box-shadow: 0 1px 1px var(--input-shadow-color); -} - -.btnsContainerBlockAndUnblock { - display: flex; - margin: 2.5rem 0 2.5rem 0; -} - -.btnsContainerBlockAndUnblock .btnsBlockBlockAndUnblock { - display: flex; -} - -.btnsContainerBlockAndUnblock .btnsBlockBlockAndUnblock button { - margin-left: 1rem; - display: flex; - justify-content: center; - align-items: center; -} - -.btnsContainerBlockAndUnblock .inputContainerBlockAndUnblock { - flex: 1; - position: relative; -} - -.btnsContainerBlockAndUnblock .inputBlockAndUnblock { - width: 70%; position: relative; } -.btnsContainerBlockAndUnblock input { - outline: 1px solid var(--bs-gray-400); -} - -.btnsContainerBlockAndUnblock .inputContainerBlockAndUnblock button { +.btnsContainer .input button { width: 52px; } -.largeBtnsWrapper { - display: flex; -} - .deleteButton { background-color: var(--delete-button-bg); color: var(--delete-button-color); @@ -188,13 +257,13 @@ color: black !important; margin-top: 10px; margin-left: 5px; - border: 1px solid var(--dropdown-border-color); + border: 1px solid var(--brown-color); } .createButton:hover { background-color: var(--grey-bg-color) !important; color: black !important; - border: 1px solid var(--dropdown-border-color) !important; + border: 1px solid var(--brown-color) !important; } .visuallyHidden { @@ -208,6 +277,13 @@ border: 0; } +.inputField { + margin-top: 10px; + margin-bottom: 10px; + background-color: white; + box-shadow: 0 1px 1px var(--input-shadow-color); +} + .inputFieldModal { margin-bottom: 10px; background-color: white; @@ -232,52 +308,16 @@ align-items: center; } -.searchButton:hover { - background-color: var(--search-button-bg); - border-color: var(--search-button-border); -} - -.search { - position: absolute; - z-index: 10; - background-color: var(--search-button-bg); - border-color: var(--search-button-border); - bottom: 0; - right: 0; - height: 100%; - display: flex; - justify-content: center; - align-items: center; -} - -.editButton { - background-color: var(--search-button-bg); - border-color: var(--search-button-border); - color: var(--high-contrast-text); - margin-left: 2; -} - .addButton { margin-bottom: 10px; background-color: var(--search-button-bg); border-color: var(--grey-bg-color); - color: var(--high-contrast-text); + color: #555555; } .addButton:hover { background-color: #286fe0; border-color: var(--search-button-border); - /* color: #555555; */ -} - -.modalbtn { - margin-top: 1rem; - display: flex !important; - margin-left: auto; - align-items: center; - background-color: var(--grey-bg-color) !important; - color: black !important; - border: 1px solid var(--dropdown-border-color) !important; } .yesButton { @@ -285,14 +325,14 @@ border-color: var(--search-button-border); } -.mainpageright { - color: var(--dropdown-text-color); +.searchIcon { + color: var(--brown-color); } .infoButton { background-color: var(--search-button-bg); border-color: var(--search-button-border); - color: var(--dropdown-text-color); + color: var(--brown-color); margin-right: 0.5rem; border-radius: 0.25rem; } @@ -318,14 +358,6 @@ margin-top: 20px; } -.mainpageright > hr { - margin-top: 10px; - width: 100%; - margin-left: -15px; - margin-right: -15px; - margin-bottom: 20px; -} - .rowBackground { background-color: var(--row-background); } @@ -397,6 +429,15 @@ position: sticky; } +/* .checkboxButton{ + background-color: transparent; +} + +.checkboxButton:checked{ + background-color: var(--subtle-blue-grey); + color:white +} */ + input[type='checkbox']:checked + label { background-color: var(--subtle-blue-grey) !important; } @@ -444,7 +485,6 @@ hr { display: flex; justify-content: flex-end; } - .icon { margin: 1px; } @@ -462,7 +502,7 @@ hr { flex-direction: row; font-weight: 900; font-size: 16px; - color: var(--high-contrast-text); + color: rgb(80, 80, 80); } .rankings { @@ -489,172 +529,6 @@ hr { color: var(--bs-primary) !important; } -.custom_table { - border-radius: 20px; - background-color: var(--grey-bg-color); -} - -.custom_table tbody tr { - background-color: var(--dropdown-hover-color); -} - -.custom_table tbody tr:hover { - background-color: var(--grey-bg-color); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1); -} - -.custom_table tbody tr:focus-within { - outline: 2px solid #000; - outline-offset: -2px; -} - -.custom_table tbody td:focus { - outline: 2px solid #000; - outline-offset: -2px; -} - -.listTable { - width: 100%; - box-sizing: border-box; - background: #ffffff; - border: 1px solid #0000001f; - border-radius: 24px; -} - -.listBox .customTable { - margin-bottom: 0%; -} - -.requestsTable thead th { - font-size: 20px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0em; - text-align: left; - color: #000000; - border-bottom: 1px solid #dddddd; - padding: 1.5rem; -} - -.notFound { - flex: 1; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.headerBtn { - box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 2px; -} - -.settingsContainer { - min-height: 100vh; -} - -.settingsBody { - min-height: 100vh; - margin: 2.5rem 1rem; -} - -.cardHeader { - padding: 1.25rem 1rem 1rem 1rem; - border-bottom: 1px solid var(--bs-gray-200); - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; -} -.cardHeader .cardTitle { - font-size: 1.2rem; - font-weight: 600; -} - -.containerBody { - min-height: 180px; - padding-top: 0; - max-height: 570px; - overflow-y: auto; - width: 100%; - max-width: min(400px, 90vw); - scrollbar-width: thin; - scrollbar-color: rgba(0, 0, 0, 0.3) transparent; - - &::-webkit-scrollbar { - width: thin; - } - - &::-webkit-scrollbar-track { - background: transparent; - } - - &::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.3); - } -} - -.containerBody .emptyContainer { - display: flex; - min-height: 180px; - justify-content: center; - align-items: center; -} - -.containerBody .rankings { - aspect-ratio: 1; - border-radius: 50%; - width: 35px; -} - -.cardBody { - min-height: 180px; -} - -.cardBody .textBox { - margin: 0 0 3rem 0; - color: var(--high-contrast-text); -} - -.settingsTabs { - display: none; -} - -@media (min-width: 576px) { - .settingsDropdown { - display: none; - } -} - -@media (min-width: 576px) { - .settingsTabs { - display: block; - } -} - -@media (max-width: 1020px) { - .btnsContainer { - flex-direction: column; - margin: 1.5rem 0; - } - - .btnsContainer .input { - width: 100%; - } - - .btnsContainer .btnsBlock { - margin: 1.5rem 0 0 0; - justify-content: space-between; - } - - .btnsContainer .btnsBlock button { - margin: 0; - } - - .btnsContainer .btnsBlock div button { - margin-right: 1.5rem; - } -} - @media (max-width: 520px) { .btnsContainer { margin-bottom: 0; @@ -663,6 +537,7 @@ hr { .btnsContainer .btnsBlock { display: block; margin-top: 1rem; + margin-right: 0; } .btnsContainer .btnsBlock div { @@ -690,12 +565,31 @@ hr { } } +@media (max-width: 1020px) { + .btnsContainer { + flex-direction: column; + margin: 1.5rem 0; + } + + .btnsContainer .btnsBlock { + margin: 1.5rem 0 0 0; + justify-content: space-between; + } + + .btnsContainer .btnsBlock button { + margin: 0; + } + + .btnsContainer .btnsBlock div button { + margin-right: 1.5rem; + } +} + @-webkit-keyframes load8 { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } - 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); @@ -707,7 +601,6 @@ hr { -webkit-transform: rotate(0deg); transform: rotate(0deg); } - 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); From d9dc57d62aa6fa46bf2a703bc271a27aa7575e64 Mon Sep 17 00:00:00 2001 From: NISHANT SINGH <151461374+NishantSinghhhhh@users.noreply.github.com> Date: Fri, 20 Dec 2024 06:48:34 +0530 Subject: [PATCH 23/24] refactor l: chat.jsx to vitest (#2649) --- jest.config.js | 1 + .../EventCalendar/EventCalendar.tsx | 42 ++-- .../EventCalendar/YearlyEventCalender.tsx | 29 ++- .../OrgActionItemCategories.test.tsx | 12 +- .../OrganizationScreen.test.tsx | 1 + src/screens/LoginPage/LoginPage.tsx | 15 +- src/screens/OrgList/OrgList.tsx | 12 +- .../Chat/{Chat.test.tsx => Chat.spec.tsx} | 184 ++++++++---------- src/utils/getRefreshToken.ts | 2 +- 9 files changed, 120 insertions(+), 178 deletions(-) rename src/screens/UserPortal/Chat/{Chat.test.tsx => Chat.spec.tsx} (94%) diff --git a/jest.config.js b/jest.config.js index a3c6bbecbf..75e0cc5b4d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -36,6 +36,7 @@ export default { '/src', ], moduleNameMapper: { + '\\.(css|scss|sass|less)$': 'identity-obj-proxy', '^react-native$': 'react-native-web', '^@dicebear/core$': '/scripts/__mocks__/@dicebear/core.ts', '^@dicebear/collection$': diff --git a/src/components/EventCalendar/EventCalendar.tsx b/src/components/EventCalendar/EventCalendar.tsx index 592456f295..575bda391e 100644 --- a/src/components/EventCalendar/EventCalendar.tsx +++ b/src/components/EventCalendar/EventCalendar.tsx @@ -93,7 +93,7 @@ const Calendar: React.FC = ({ const data: InterfaceEventListCardProps[] = []; if (userRole === Role.SUPERADMIN) return eventData; // Hard to test all the cases - /* istanbul ignore next */ + if (userRole === Role.ADMIN) { eventData?.forEach((event) => { if (event.isPublic) data.push(event); @@ -130,7 +130,6 @@ const Calendar: React.FC = ({ * Moves the calendar view to the previous month. */ const handlePrevMonth = (): void => { - /*istanbul ignore next*/ if (currentMonth === 0) { setCurrentMonth(11); setCurrentYear(currentYear - 1); @@ -143,7 +142,6 @@ const Calendar: React.FC = ({ * Moves the calendar view to the next month. */ const handleNextMonth = (): void => { - /*istanbul ignore next*/ if (currentMonth === 11) { setCurrentMonth(0); setCurrentYear(currentYear + 1); @@ -156,7 +154,6 @@ const Calendar: React.FC = ({ * Moves the calendar view to the previous date. */ const handlePrevDate = (): void => { - /*istanbul ignore next*/ if (currentDate > 1) { setCurrentDate(currentDate - 1); } else { @@ -175,15 +172,13 @@ const Calendar: React.FC = ({ } } }; - /*istanbul ignore next*/ + const handleNextDate = (): void => { - /*istanbul ignore next*/ const lastDayOfCurrentMonth = new Date( currentYear, currentMonth - 1, 0, ).getDate(); - /*istanbul ignore next*/ if (currentDate < lastDayOfCurrentMonth) { setCurrentDate(currentDate + 1); } else { @@ -202,7 +197,6 @@ const Calendar: React.FC = ({ * Moves the calendar view to today's date. */ const handleTodayButton = (): void => { - /*istanbul ignore next*/ setCurrentYear(today.getFullYear()); setCurrentMonth(today.getMonth()); setCurrentDate(today.getDate()); @@ -215,7 +209,6 @@ const Calendar: React.FC = ({ '0', )}:${String(Math.abs(new Date().getTimezoneOffset()) % 60).padStart(2, '0')}`; - /*istanbul ignore next*/ const renderHours = (): JSX.Element => { const toggleExpand = (index: number): void => { if (expanded === index) { @@ -225,11 +218,9 @@ const Calendar: React.FC = ({ } }; - /*istanbul ignore next*/ const allDayEventsList: JSX.Element[] = events ?.filter((datas) => { - /*istanbul ignore next*/ const currDate = new Date(currentYear, currentMonth, currentDate); if ( datas.startTime == undefined && @@ -372,7 +363,7 @@ const Calendar: React.FC = ({ /> ); }) || []; - /*istanbul ignore next*/ + return (
@@ -405,7 +396,7 @@ const Calendar: React.FC = ({ : styles.event_list } > - {/*istanbul ignore next*/} + {} {expanded === index ? timeEventsList : timeEventsList?.slice(0, 1)} @@ -465,14 +456,13 @@ const Calendar: React.FC = ({ styles.day, ].join(' '); const toggleExpand = (index: number): void => { - /*istanbul ignore next*/ if (expanded === index) { setExpanded(-1); } else { setExpanded(index); } }; - /*istanbul ignore next*/ + const allEventsList: JSX.Element[] = events ?.filter((datas) => { @@ -536,38 +526,28 @@ const Calendar: React.FC = ({ >
{holidayList}
- { - /*istanbul ignore next*/ - expanded === index - ? allEventsList - : holidayList?.length > 0 - ? /*istanbul ignore next*/ - allEventsList?.slice(0, 1) - : allEventsList?.slice(0, 2) - } + {expanded === index + ? allEventsList + : holidayList?.length > 0 + ? allEventsList?.slice(0, 1) + : allEventsList?.slice(0, 2)}
{(allEventsList?.length > 2 || (windowWidth <= 700 && allEventsList?.length > 0)) && ( - /*istanbul ignore next*/ )}
diff --git a/src/components/EventCalendar/YearlyEventCalender.tsx b/src/components/EventCalendar/YearlyEventCalender.tsx index 63870ded3c..facf75038c 100644 --- a/src/components/EventCalendar/YearlyEventCalender.tsx +++ b/src/components/EventCalendar/YearlyEventCalender.tsx @@ -46,11 +46,11 @@ interface InterfaceCalendarProps { viewType?: ViewType; } -enum Status { - ACTIVE = 'ACTIVE', - BLOCKED = 'BLOCKED', - DELETED = 'DELETED', -} +// enum Status { +// ACTIVE = 'ACTIVE', +// BLOCKED = 'BLOCKED', +// DELETED = 'DELETED', +// } /** * Enum for different user roles. @@ -63,13 +63,13 @@ enum Role { /** * Interface for event attendees. - */ -interface InterfaceIEventAttendees { - userId: string; - user?: string; - status?: Status; - createdAt?: Date; -} +// */ +// interface InterfaceIEventAttendees { +// userId: string; +// user?: string; +// status?: Status; +// createdAt?: Date; +// } /** * Interface for organization list. @@ -177,7 +177,6 @@ const Calendar: React.FC = ({ * Navigates to the previous year. */ const handlePrevYear = (): void => { - /*istanbul ignore next*/ setCurrentYear(currentYear - 1); }; @@ -185,7 +184,6 @@ const Calendar: React.FC = ({ * Navigates to the next year. */ const handleNextYear = (): void => { - /*istanbul ignore next*/ setCurrentYear(currentYear + 1); }; @@ -239,7 +237,6 @@ const Calendar: React.FC = ({ return dayjs(event.startDate).isSame(date, 'day'); }); - /*istanbul ignore next*/ const renderedEvents = eventsForCurrentDate?.map((datas: InterfaceEventListCardProps) => { const attendees: { _id: string }[] = []; @@ -276,7 +273,6 @@ const Calendar: React.FC = ({ ); }) || []; - /*istanbul ignore next*/ const toggleExpand = (index: string): void => { if (expandedY === index) { setExpandedY(null); @@ -285,7 +281,6 @@ const Calendar: React.FC = ({ } }; - /*istanbul ignore next*/ return (
{ const searchInput = await screen.findByTestId('searchByName'); expect(searchInput).toBeInTheDocument(); - userEvent.type(searchInput, 'Category 1'); - userEvent.type(searchInput, '{enter}'); + // Simulate typing and pressing ENTER + userEvent.type(searchInput, 'Category 1{enter}'); + + // Wait for the filtering to complete await waitFor(() => { - expect(screen.getByText('Category 1')).toBeInTheDocument(); - expect(screen.queryByText('Category 2')).toBeNull(); + // Assert only "Category 1" is visible + const categories = screen.getAllByTestId('categoryName'); + expect(categories).toHaveLength(1); + expect(categories[0]).toHaveTextContent('Category 1'); }); }); diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.test.tsx index 6293562a5a..0dfb6d7a14 100644 --- a/src/components/OrganizationScreen/OrganizationScreen.test.tsx +++ b/src/components/OrganizationScreen/OrganizationScreen.test.tsx @@ -79,6 +79,7 @@ describe('Testing OrganizationScreen', () => { const closeButton = screen.getByTestId('closeMenu'); fireEvent.click(closeButton); + expect(screen.getByTestId('mainpageright')).toHaveClass(styles.expand); const openButton = screen.getByTestId('openMenu'); diff --git a/src/screens/LoginPage/LoginPage.tsx b/src/screens/LoginPage/LoginPage.tsx index c68ecaceb0..180009926c 100644 --- a/src/screens/LoginPage/LoginPage.tsx +++ b/src/screens/LoginPage/LoginPage.tsx @@ -153,7 +153,6 @@ const loginPage = (): JSX.Element => { try { await fetch(BACKEND_URL as string); } catch (error) { - /* istanbul ignore next */ errorHandler(t, error); } } @@ -165,7 +164,6 @@ const loginPage = (): JSX.Element => { recaptchaToken: string | null, ): Promise => { try { - /* istanbul ignore next */ if (REACT_APP_USE_RECAPTCHA !== 'yes') { return true; } @@ -177,7 +175,6 @@ const loginPage = (): JSX.Element => { return data.recaptcha; } catch { - /* istanbul ignore next */ toast.error(t('captchaError') as string); } }; @@ -199,7 +196,7 @@ const loginPage = (): JSX.Element => { } = signformState; const isVerified = await verifyRecaptcha(recaptchaToken); - /* istanbul ignore next */ + if (!isVerified) { toast.error(t('Please_check_the_captcha') as string); return; @@ -242,7 +239,6 @@ const loginPage = (): JSX.Element => { }, }); - /* istanbul ignore next */ if (signUpData) { toast.success( t( @@ -260,7 +256,6 @@ const loginPage = (): JSX.Element => { }); } } catch (error) { - /* istanbul ignore next */ errorHandler(t, error); } } else { @@ -285,7 +280,7 @@ const loginPage = (): JSX.Element => { const loginLink = async (e: ChangeEvent): Promise => { e.preventDefault(); const isVerified = await verifyRecaptcha(recaptchaToken); - /* istanbul ignore next */ + if (!isVerified) { toast.error(t('Please_check_the_captcha') as string); return; @@ -299,7 +294,6 @@ const loginPage = (): JSX.Element => { }, }); - /* istanbul ignore next */ if (loginData) { i18n.changeLanguage(loginData.login.appUserProfile.appLanguageCode); const { login } = loginData; @@ -336,7 +330,6 @@ const loginPage = (): JSX.Element => { toast.warn(tErrors('notFound') as string); } } catch (error) { - /* istanbul ignore next */ errorHandler(t, error); } }; @@ -494,14 +487,12 @@ const loginPage = (): JSX.Element => {
) : ( - /* istanbul ignore next */ <> )}
) : !isLoading && orgsData?.organizationsConnection.length == 0 && - /* istanbul ignore next */ searchByName.length > 0 ? ( - /* istanbul ignore next */

{tCommon('noResultsFoundFor')} "{searchByName}" diff --git a/src/screens/UserPortal/Chat/Chat.test.tsx b/src/screens/UserPortal/Chat/Chat.spec.tsx similarity index 94% rename from src/screens/UserPortal/Chat/Chat.test.tsx rename to src/screens/UserPortal/Chat/Chat.spec.tsx index 660eeaa236..ec8243c823 100644 --- a/src/screens/UserPortal/Chat/Chat.test.tsx +++ b/src/screens/UserPortal/Chat/Chat.spec.tsx @@ -1,27 +1,55 @@ import React from 'react'; import { - act, - fireEvent, render, + fireEvent, screen, waitFor, + act, } from '@testing-library/react'; import { MockedProvider } from '@apollo/react-testing'; -import { I18nextProvider } from 'react-i18next'; - -import { - USERS_CONNECTION_LIST, - USER_JOINED_ORGANIZATIONS, -} from 'GraphQl/Queries/Queries'; import { BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; +import { expect, describe, test, vi } from 'vitest'; import { store } from 'state/store'; import i18nForTest from 'utils/i18nForTest'; +import { I18nextProvider } from 'react-i18next'; import Chat from './Chat'; -import useLocalStorage from 'utils/useLocalstorage'; +import { + USERS_CONNECTION_LIST, + USER_JOINED_ORGANIZATIONS, +} from 'GraphQl/Queries/Queries'; import { MESSAGE_SENT_TO_CHAT } from 'GraphQl/Mutations/OrganizationMutations'; import { CHATS_LIST, CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries'; +import useLocalStorage from 'utils/useLocalstorage'; +/** + * Unit tests for the ChatScreen component. + * These tests covers all areas + * Covers: + * - Rendering of UI elements. + * - Contact selection functionality. + * - Direct and group chat creation workflows. + * - Sidebar behavior in desktop and mobile views. + * + * Uses Vitest for testing and Apollo MockedProvider for mocking GraphQL queries. + */ + +vi.mock('../../../components/UserPortal/ChatRoom/ChatRoom', () => ({ + default: () =>
Mocked ChatRoom
, +})); + +const resizeWindow = (width: number): void => { + window.innerWidth = width; + fireEvent(window, new Event('resize')); +}; + +async function wait(ms = 100): Promise { + await act(() => { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + }); +} const { setItem } = useLocalStorage(); const USER_JOINED_ORG_MOCK = [ @@ -1457,47 +1485,45 @@ const GROUP_CHAT_BY_ID_QUERY_MOCK = [ }, ]; -const resizeWindow = (width: number): void => { - window.innerWidth = width; - fireEvent(window, new Event('resize')); -}; - -async function wait(ms = 100): Promise { - await act(() => { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); - }); -} - describe('Testing Chat Screen [User Portal]', () => { - window.HTMLElement.prototype.scrollIntoView = jest.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(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), })), }); - test('Screen should be rendered properly', async () => { - const mock = [ - ...USER_JOINED_ORG_MOCK, - ...GROUP_CHAT_BY_ID_QUERY_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...UserConnectionListMock, - ...CHAT_BY_ID_QUERY_MOCK, - ...CHATS_LIST_MOCK, - ...UserConnectionListMock, - ]; + // Define mock data outside of tests to reuse + const mock = [ + ...USER_JOINED_ORG_MOCK, + ...GROUP_CHAT_BY_ID_QUERY_MOCK, + ...MESSAGE_SENT_TO_CHAT_MOCK, + ...MESSAGE_SENT_TO_CHAT_MOCK, + ...UserConnectionListMock, + ...CHAT_BY_ID_QUERY_MOCK, + ...CHATS_LIST_MOCK, + ...UserConnectionListMock, + ]; + + beforeEach(() => { + setItem('userId', '1'); + vi.clearAllMocks(); + localStorage.clear(); + vi.resetModules(); + }); + afterEach(() => { + localStorage.clear(); + }); + + test('Screen should be rendered properly', async () => { render( @@ -1512,18 +1538,7 @@ describe('Testing Chat Screen [User Portal]', () => { await wait(); }); - test('User is able to select a contact', async () => { - const mock = [ - ...USER_JOINED_ORG_MOCK, - ...GROUP_CHAT_BY_ID_QUERY_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...UserConnectionListMock, - ...CHAT_BY_ID_QUERY_MOCK, - ...CHATS_LIST_MOCK, - ...UserConnectionListMock, - ]; - + it('User is able to select a contact', async () => { render( @@ -1537,24 +1552,13 @@ describe('Testing Chat Screen [User Portal]', () => { ); await wait(); - expect(await screen.findByText('Messages')).toBeInTheDocument(); - expect( await screen.findByTestId('contactCardContainer'), ).toBeInTheDocument(); }); test('create new direct chat', async () => { - const mock = [ - ...USER_JOINED_ORG_MOCK, - ...GROUP_CHAT_BY_ID_QUERY_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...CHAT_BY_ID_QUERY_MOCK, - ...CHATS_LIST_MOCK, - ...UserConnectionListMock, - ]; render( @@ -1572,6 +1576,7 @@ describe('Testing Chat Screen [User Portal]', () => { const dropdown = await screen.findByTestId('dropdown'); expect(dropdown).toBeInTheDocument(); fireEvent.click(dropdown); + const newDirectChatBtn = await screen.findByTestId('newDirectChat'); expect(newDirectChatBtn).toBeInTheDocument(); fireEvent.click(newDirectChatBtn); @@ -1586,16 +1591,6 @@ describe('Testing Chat Screen [User Portal]', () => { }); test('create new group chat', async () => { - const mock = [ - ...USER_JOINED_ORG_MOCK, - ...GROUP_CHAT_BY_ID_QUERY_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...UserConnectionListMock, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...CHAT_BY_ID_QUERY_MOCK, - ...CHATS_LIST_MOCK, - ...UserConnectionListMock, - ]; render( @@ -1618,6 +1613,7 @@ describe('Testing Chat Screen [User Portal]', () => { expect(newGroupChatBtn).toBeInTheDocument(); fireEvent.click(newGroupChatBtn); + const closeButton = screen.getByRole('button', { name: /close/i }); expect(closeButton).toBeInTheDocument(); @@ -1625,18 +1621,6 @@ describe('Testing Chat Screen [User Portal]', () => { }); test('sidebar', async () => { - const mock = [ - ...USER_JOINED_ORG_MOCK, - ...GROUP_CHAT_BY_ID_QUERY_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...UserConnectionListMock, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...CHAT_BY_ID_QUERY_MOCK, - ...CHATS_LIST_MOCK, - ...UserConnectionListMock, - ]; - setItem('userId', '1'); - render( @@ -1661,18 +1645,9 @@ describe('Testing Chat Screen [User Portal]', () => { }); test('Testing sidebar when the screen size is less than or equal to 820px', async () => { - setItem('userId', '1'); - const mock = [ - ...USER_JOINED_ORG_MOCK, - ...GROUP_CHAT_BY_ID_QUERY_MOCK, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...UserConnectionListMock, - ...MESSAGE_SENT_TO_CHAT_MOCK, - ...CHAT_BY_ID_QUERY_MOCK, - ...CHATS_LIST_MOCK, - ...UserConnectionListMock, - ]; + // Resize window for mobile view (<= 820px) resizeWindow(800); + render( @@ -1684,12 +1659,17 @@ describe('Testing Chat Screen [User Portal]', () => { , ); - await wait(); - expect(screen.getByText('My Organizations')).toBeInTheDocument(); - expect(screen.getByText('Talawa User Portal')).toBeInTheDocument(); - expect(await screen.findByTestId('openMenu')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('openMenu')); - expect(await screen.findByTestId('closeMenu')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('My Organizations')).toBeInTheDocument(); + expect(screen.getByText('Talawa User Portal')).toBeInTheDocument(); + }); + + const openMenuBtn = await screen.findByTestId('openMenu'); + expect(openMenuBtn).toBeInTheDocument(); + fireEvent.click(openMenuBtn); + + const closeMenuBtn = await screen.findByTestId('closeMenu'); + expect(closeMenuBtn).toBeInTheDocument(); }); }); diff --git a/src/utils/getRefreshToken.ts b/src/utils/getRefreshToken.ts index 5d6f8aa2ce..90b1b55ef1 100644 --- a/src/utils/getRefreshToken.ts +++ b/src/utils/getRefreshToken.ts @@ -14,7 +14,7 @@ export async function refreshToken(): Promise { const { getItem, setItem } = useLocalStorage(); const refreshToken = getItem('refreshToken'); - /* istanbul ignore next */ + try { const { data } = await client.mutate({ mutation: REFRESH_TOKEN_MUTATION, From 371327e1e2340e4b12879044a9f0c780b0d34bac Mon Sep 17 00:00:00 2001 From: Abhishek Raj <113784630+abbi4code@users.noreply.github.com> Date: Fri, 20 Dec 2024 06:52:30 +0530 Subject: [PATCH 24/24] Refactor:src/screens/UserPortal/Events from Jest to Vitest (#2639) * migrated UpcomingEvents tests from Jest to Vitest * Rename UpcomingEvents.test.tsx to UpcomingEvents.spec.tsx * add header comments for UpcomingEvents component tests * migrated Events tests from jest to vitest * Using hardcoded dates instead of dynamic dates to ensure consistent behavior * Fix: Make dates dynamic for Vitest compatibility with DatePicker --- .../{Events.test.tsx => Events.spec.tsx} | 106 +++++++++++------- 1 file changed, 65 insertions(+), 41 deletions(-) rename src/screens/UserPortal/Events/{Events.test.tsx => Events.spec.tsx} (83%) diff --git a/src/screens/UserPortal/Events/Events.test.tsx b/src/screens/UserPortal/Events/Events.spec.tsx similarity index 83% rename from src/screens/UserPortal/Events/Events.test.tsx rename to src/screens/UserPortal/Events/Events.spec.tsx index 8c0b7c6912..646f5052f0 100644 --- a/src/screens/UserPortal/Events/Events.test.tsx +++ b/src/screens/UserPortal/Events/Events.spec.tsx @@ -1,5 +1,5 @@ import React, { act } from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { MockedProvider } from '@apollo/react-testing'; import { I18nextProvider } from 'react-i18next'; @@ -19,35 +19,51 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { ThemeProvider } from 'react-bootstrap'; import { createTheme } from '@mui/material'; import useLocalStorage from 'utils/useLocalstorage'; +import { vi } from 'vitest'; -const { setItem, getItem } = useLocalStorage(); +/** + * Unit tests for the Events component. + * + * This file contains tests to verify the functionality and behavior of the Events component + * under various scenarios, including successful event creation, date/time picker handling, + * calendar view toggling, and error handling. Mocked dependencies are used to ensure isolated testing. + */ -jest.mock('react-toastify', () => ({ +const { setItem } = useLocalStorage(); + +vi.mock('react-toastify', () => ({ toast: { - error: jest.fn(), - info: jest.fn(), - success: jest.fn(), + error: vi.fn(), + info: vi.fn(), + success: vi.fn(), }, })); -jest.mock('@mui/x-date-pickers/DatePicker', () => { +vi.mock('@mui/x-date-pickers/DatePicker', async () => { + const desktopDatePickerModule = await vi.importActual( + '@mui/x-date-pickers/DesktopDatePicker', + ); return { - DatePicker: jest.requireActual('@mui/x-date-pickers/DesktopDatePicker') - .DesktopDatePicker, + DatePicker: desktopDatePickerModule.DesktopDatePicker, }; }); -jest.mock('@mui/x-date-pickers/TimePicker', () => { +vi.mock('@mui/x-date-pickers/TimePicker', async () => { + const timePickerModule = await vi.importActual( + '@mui/x-date-pickers/DesktopTimePicker', + ); return { - TimePicker: jest.requireActual('@mui/x-date-pickers/DesktopTimePicker') - .DesktopTimePicker, + TimePicker: timePickerModule.DesktopTimePicker, }; }); -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: '' }), -})); +vi.mock('react-router-dom', async () => { + const actual = await vi.importActual('react-router-dom'); + return { + ...actual, + useParams: () => ({ orgId: '' }), + }; +}); const theme = createTheme({ palette: { @@ -252,19 +268,19 @@ async function wait(ms = 100): Promise { describe('Testing Events Screen [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(), })), }); - test('Screen should be rendered properly', async () => { + it('Screen should be rendered properly', async () => { render( @@ -283,7 +299,7 @@ describe('Testing Events Screen [User Portal]', () => { await wait(); }); - test('Create event works as expected when event is not an all day event.', async () => { + it('Create event works as expected when event is not an all day event.', async () => { render( @@ -341,7 +357,7 @@ describe('Testing Events Screen [User Portal]', () => { ); }); - test('Create event works as expected when event is an all day event.', async () => { + it('Create event works as expected when event is an all day event.', async () => { render( @@ -384,7 +400,7 @@ describe('Testing Events Screen [User Portal]', () => { ); }); - test('Switch to calendar view works as expected.', async () => { + it('Switch to calendar view works as expected.', async () => { render( @@ -413,7 +429,7 @@ describe('Testing Events Screen [User Portal]', () => { expect(screen.getByText('Sun')).toBeInTheDocument(); }); - test('Testing DatePicker and TimePicker', async () => { + it('Testing DatePicker and TimePicker', async () => { render( @@ -430,31 +446,39 @@ describe('Testing Events Screen [User Portal]', () => { , ); - await wait(); - - const startDate = '03/23/2024'; - const endDate = '04/23/2024'; - const startTime = '02:00 PM'; - const endTime = '06:00 PM'; - userEvent.click(screen.getByTestId('createEventModalBtn')); + // MM/DD/YYYY + const startDate = new Date(); + const endDate = new Date(); + const startTime = '08:00 AM'; + const endTime = '10:00 AM'; + + await waitFor(() => { + expect(screen.getByLabelText('Start Date')).toBeInTheDocument(); + expect(screen.getByLabelText('End Date')).toBeInTheDocument(); + }); expect(endDate).not.toBeNull(); const endDateDatePicker = screen.getByLabelText('End Date'); expect(startDate).not.toBeNull(); const startDateDatePicker = screen.getByLabelText('Start Date'); + const startDateDayjs = dayjs(startDate); + const endDateDayjs = dayjs(endDate); + fireEvent.change(startDateDatePicker, { - target: { value: startDate }, + target: { value: startDateDayjs.format('MM/DD/YYYY') }, }); fireEvent.change(endDateDatePicker, { - target: { value: endDate }, + target: { value: endDateDayjs.format('MM/DD/YYYY') }, }); - await wait(); - - expect(endDateDatePicker).toHaveValue(endDate); - expect(startDateDatePicker).toHaveValue(startDate); + await waitFor(() => { + expect(startDateDatePicker).toHaveValue( + startDateDayjs.format('MM/DD/YYYY'), + ); + expect(endDateDatePicker).toHaveValue(endDateDayjs.format('MM/DD/YYYY')); + }); userEvent.click(screen.getByTestId('allDayEventCheck')); @@ -475,7 +499,7 @@ describe('Testing Events Screen [User Portal]', () => { expect(startTimePicker).toHaveValue(startTime); }); - test('EndDate null', async () => { + it('EndDate null', async () => { render(