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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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( From b1f4903d50aaea775b89b7ace361b348295b5279 Mon Sep 17 00:00:00 2001 From: Amit Bora Date: Sat, 21 Dec 2024 05:45:08 +0530 Subject: [PATCH 25/57] Refactor styles: move SubTags.module.css styles to app.module.css (#2538) * Refactor styles: move SubTags.module.css styles to app.module.css * Enhance keyboard navigation accessibility * Color contrast issue * Refactor styles: move SubTags.module.css styles to app.module.css * Enhance keyboard navigation accessibility * Color contrast issue --- src/screens/SubTags/SubTags.module.css | 145 ----------------------- src/style/app.module.css | 156 ++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 147 deletions(-) delete mode 100644 src/screens/SubTags/SubTags.module.css diff --git a/src/screens/SubTags/SubTags.module.css b/src/screens/SubTags/SubTags.module.css deleted file mode 100644 index 0a210bdfa4..0000000000 --- a/src/screens/SubTags/SubTags.module.css +++ /dev/null @@ -1,145 +0,0 @@ -.btnsContainer { - display: flex; - margin: 2rem 0; -} - -.btnsContainer .btnsBlock { - display: flex; - width: max-content; -} - -.btnsContainer .btnsBlock button { - margin-left: 1rem; - display: flex; - justify-content: center; - align-items: center; -} - -.btnsContainer .input { - flex: 1; - position: relative; - max-width: 60%; - justify-content: space-between; -} - -.btnsContainer input { - outline: 1px solid var(--bs-gray-400); -} - -.btnsContainer .input button { - width: 52px; -} - -@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; - } -} - -/* 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%; - } -} - -.errorContainer { - min-height: 100vh; -} - -.errorMessage { - margin-top: 25%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.errorIcon { - transform: scale(1.5); - color: var(--bs-danger); - margin-bottom: 1rem; -} - -.tableHeader { - background-color: var(--bs-primary); - color: var(--bs-white); - font-size: 1rem; -} -.rowBackground { - background-color: var(--bs-white); - max-height: 120px; -} - -.subTagsLink { - color: var(--bs-blue); - font-weight: 500; - cursor: pointer; -} - -.subTagsLink i { - visibility: hidden; -} - -.subTagsLink:hover { - font-weight: 600; - text-decoration: underline; -} - -.subTagsLink:hover i { - visibility: visible; -} - -.tagsBreadCrumbs { - color: var(--bs-gray); - cursor: pointer; -} - -.tagsBreadCrumbs:hover { - color: var(--bs-blue); - font-weight: 600; - text-decoration: underline; -} - -.subTagsScrollableDiv { - scrollbar-width: auto; - scrollbar-color: var(--bs-gray-400) var(--bs-white); - - max-height: calc(100vh - 18rem); - overflow: auto; -} diff --git a/src/style/app.module.css b/src/style/app.module.css index e2ad209d3b..982cec9672 100644 --- a/src/style/app.module.css +++ b/src/style/app.module.css @@ -210,7 +210,10 @@ .btnsContainer { display: flex; + /* Adjust spacing between items */ margin: 2.5rem 0; + align-items: center; + gap: 10px; } .btnsContainer .btnsBlock { @@ -393,6 +396,11 @@ transform: scale(1.5); color: var(--bs-danger); margin-bottom: 1rem; + /* Add error icon for non-color indication */ + &::before { + content: '⚠️'; + margin-right: 0.5rem; + } } .subTagsLink { @@ -405,13 +413,15 @@ visibility: hidden; } -.subTagsLink:hover { +.subTagsLink:hover, +.subTagsLink:focus { color: var(--subtle-blue-grey-hover); font-weight: 600; text-decoration: underline; } -.subTagsLink:hover i { +.subTagsLink:hover i, +.subTagsLink:focus i { visibility: visible; } @@ -606,3 +616,145 @@ hr { transform: rotate(360deg); } } + +.btnsContainer .btnsBlock { + display: flex; + width: max-content; +} + +.btnsContainer .btnsBlock button { + margin-left: 1rem; + display: flex; + justify-content: center; + align-items: center; +} + +.btnsContainer .input { + flex: 1; + position: relative; + max-width: 60%; + justify-content: space-between; +} + +.btnsContainer input { + outline: 1px solid var(--bs-gray-400); +} + +.btnsContainer .input button { + width: 52px; +} + +@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; + } +} + +/* 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%; + } +} + +.errorMessage { + margin-top: 25%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.tableHeader { + background-color: var(--bs-primary-text-emphasis); + color: var(--bs-white); + font-size: 1rem; +} +.rowBackground { + background-color: var(--bs-white); + max-height: 120px; + overflow-y: auto; +} + +.subTagsLink { + color: var(--bs-blue); + font-weight: 500; + cursor: pointer; + /* Prevent layout shift */ + &::after { + display: block; + content: attr(data-text); + font-weight: 600; + height: 0; + overflow: hidden; + visibility: hidden; + } +} + +.subTagsLink i { + visibility: hidden; +} + +.tagsBreadCrumbs { + color: var(--bs-gray); + cursor: pointer; + /* Prevent layout shift */ + &::after { + display: block; + content: attr(data-text); + font-weight: 600; + height: 0; + overflow: hidden; + visibility: hidden; + } +} + +.tagsBreadCrumbs:hover, +.tagsBreadCrumbs:focus { + color: var(--bs-blue); + font-weight: 600; + text-decoration: underline; +} + +.subTagsScrollableDiv { + scrollbar-width: auto; + scrollbar-color: var(--bs-gray-400) var(--bs-white); + + max-height: calc(100vh - 18rem); + overflow: auto; +} From c843926aa6f9b7cac5a4c43ddb889c07e03168be Mon Sep 17 00:00:00 2001 From: Shiva <148421597+shivasankaran18@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:27:31 +0530 Subject: [PATCH 26/57] refactor:vitest to EventVolunteers/Volunteers (#2695) * refactor:vitest to EventVolunteers/Volunteers * fix:coderabbit sugg --- ...test.tsx => VolunteerCreateModal.spec.tsx} | 17 +++-- ...test.tsx => VolunteerDeleteModal.spec.tsx} | 17 +++-- ...l.test.tsx => VolunteerViewModal.spec.tsx} | 5 +- ...olunteers.test.tsx => Volunteers.spec.tsx} | 64 +++++++++++++++++-- 4 files changed, 85 insertions(+), 18 deletions(-) rename src/screens/EventVolunteers/Volunteers/{VolunteerCreateModal.test.tsx => VolunteerCreateModal.spec.tsx} (91%) rename src/screens/EventVolunteers/Volunteers/{VolunteerDeleteModal.test.tsx => VolunteerDeleteModal.spec.tsx} (91%) rename src/screens/EventVolunteers/Volunteers/{VolunteerViewModal.test.tsx => VolunteerViewModal.spec.tsx} (97%) rename src/screens/EventVolunteers/Volunteers/{Volunteers.test.tsx => Volunteers.spec.tsx} (83%) diff --git a/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.test.tsx b/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.spec.tsx similarity index 91% rename from src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.test.tsx rename to src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.spec.tsx index cac8fe94f0..77fe028655 100644 --- a/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.test.tsx +++ b/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.spec.tsx @@ -22,11 +22,18 @@ import { toast } from 'react-toastify'; import type { InterfaceVolunteerCreateModal } from './VolunteerCreateModal'; import VolunteerCreateModal from './VolunteerCreateModal'; import userEvent from '@testing-library/user-event'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +/** + * Mock implementation of the `react-toastify` module. + * Mocks the `toast` object with `success` and `error` methods to allow testing + * without triggering actual toast notifications. + */ + +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -45,10 +52,10 @@ const t = { const itemProps: InterfaceVolunteerCreateModal[] = [ { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), eventId: 'eventId', orgId: 'orgId', - refetchVolunteers: jest.fn(), + refetchVolunteers: vi.fn(), }, ]; diff --git a/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.test.tsx b/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.spec.tsx similarity index 91% rename from src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.test.tsx rename to src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.spec.tsx index dd9d6d5985..575670a887 100644 --- a/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.test.tsx +++ b/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.spec.tsx @@ -15,11 +15,18 @@ import { toast } from 'react-toastify'; import type { InterfaceDeleteVolunteerModal } from './VolunteerDeleteModal'; import VolunteerDeleteModal from './VolunteerDeleteModal'; import userEvent from '@testing-library/user-event'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +/** + * Mock implementation of the `react-toastify` module. + * Mocks the `toast` object with `success` and `error` methods to allow testing + * without triggering actual toast notifications. + */ + +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -38,8 +45,8 @@ const t = { const itemProps: InterfaceDeleteVolunteerModal[] = [ { isOpen: true, - hide: jest.fn(), - refetchVolunteers: jest.fn(), + hide: vi.fn(), + refetchVolunteers: vi.fn(), volunteer: { _id: 'volunteerId1', hasAccepted: true, diff --git a/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.test.tsx b/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.spec.tsx similarity index 97% rename from src/screens/EventVolunteers/Volunteers/VolunteerViewModal.test.tsx rename to src/screens/EventVolunteers/Volunteers/VolunteerViewModal.spec.tsx index 155dba8464..e99fb47d20 100644 --- a/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.test.tsx +++ b/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.spec.tsx @@ -10,6 +10,7 @@ import { store } from 'state/store'; import i18n from 'utils/i18nForTest'; import type { InterfaceVolunteerViewModal } from './VolunteerViewModal'; import VolunteerViewModal from './VolunteerViewModal'; +import { vi } from 'vitest'; const t = { ...JSON.parse( @@ -24,7 +25,7 @@ const t = { const itemProps: InterfaceVolunteerViewModal[] = [ { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), volunteer: { _id: 'volunteerId1', hasAccepted: true, @@ -51,7 +52,7 @@ const itemProps: InterfaceVolunteerViewModal[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), volunteer: { _id: 'volunteerId2', hasAccepted: false, diff --git a/src/screens/EventVolunteers/Volunteers/Volunteers.test.tsx b/src/screens/EventVolunteers/Volunteers/Volunteers.spec.tsx similarity index 83% rename from src/screens/EventVolunteers/Volunteers/Volunteers.test.tsx rename to src/screens/EventVolunteers/Volunteers/Volunteers.spec.tsx index 2af25b0b84..be08ec61fb 100644 --- a/src/screens/EventVolunteers/Volunteers/Volunteers.test.tsx +++ b/src/screens/EventVolunteers/Volunteers/Volunteers.spec.tsx @@ -7,13 +7,14 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; -import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { MemoryRouter, Route, Routes, useParams } from 'react-router-dom'; import { store } from 'state/store'; import { StaticMockLink } from 'utils/StaticMockLink'; import i18n from 'utils/i18nForTest'; import Volunteers from './Volunteers'; import type { ApolloLink } from '@apollo/client'; import { MOCKS, MOCKS_EMPTY, MOCKS_ERROR } from './Volunteers.mocks'; +import { vi } from 'vitest'; const link1 = new StaticMockLink(MOCKS); const link2 = new StaticMockLink(MOCKS_ERROR); @@ -58,19 +59,25 @@ const renderVolunteers = (link: ApolloLink): RenderResult => { ); }; +/** Mock useParams to provide consistent test data */ + describe('Testing Volunteers Screen', () => { beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId', eventId: 'eventId' }), - })); + vi.mock('react-router-dom', async () => { + const actualDom = await vi.importActual('react-router-dom'); + return { + ...actualDom, + useParams: vi.fn(), + }; + }); }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('should redirect to fallback URL if URL params are undefined', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: '', eventId: '' }); render( @@ -95,12 +102,21 @@ describe('Testing Volunteers Screen', () => { }); it('should render Volunteers screen', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); + renderVolunteers(link1); const searchInput = await screen.findByTestId('searchBy'); expect(searchInput).toBeInTheDocument(); }); it('Check Sorting Functionality', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const searchInput = await screen.findByTestId('searchBy'); expect(searchInput).toBeInTheDocument(); @@ -134,6 +150,10 @@ describe('Testing Volunteers Screen', () => { }); it('Filter Volunteers by status (All)', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const filterBtn = await screen.findByTestId('filter'); @@ -151,6 +171,10 @@ describe('Testing Volunteers Screen', () => { }); it('Filter Volunteers by status (Pending)', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const filterBtn = await screen.findByTestId('filter'); @@ -168,6 +192,10 @@ describe('Testing Volunteers Screen', () => { }); it('Filter Volunteers by status (Accepted)', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const filterBtn = await screen.findByTestId('filter'); @@ -185,6 +213,10 @@ describe('Testing Volunteers Screen', () => { }); it('Search', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const searchInput = await screen.findByTestId('searchBy'); expect(searchInput).toBeInTheDocument(); @@ -197,6 +229,10 @@ describe('Testing Volunteers Screen', () => { }); it('should render screen with No Volunteers', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link3); await waitFor(() => { @@ -206,6 +242,10 @@ describe('Testing Volunteers Screen', () => { }); it('Error while fetching volunteers data', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link2); await waitFor(() => { @@ -214,6 +254,10 @@ describe('Testing Volunteers Screen', () => { }); it('Open and close Volunteer Modal (View)', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const viewItemBtn = await screen.findAllByTestId('viewItemBtn'); @@ -224,6 +268,10 @@ describe('Testing Volunteers Screen', () => { }); it('Open and Close Volunteer Modal (Delete)', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const deleteItemBtn = await screen.findAllByTestId('deleteItemBtn'); @@ -234,6 +282,10 @@ describe('Testing Volunteers Screen', () => { }); it('Open and close Volunteer Modal (Create)', async () => { + vi.mocked(useParams).mockReturnValue({ + orgId: 'orgId', + eventId: 'eventId', + }); renderVolunteers(link1); const addVolunteerBtn = await screen.findByTestId('addVolunteerBtn'); From 651aa71c9f8973ad7dd860a2264d3f183e136d03 Mon Sep 17 00:00:00 2001 From: Abhishek Raj <113784630+abbi4code@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:30:48 +0530 Subject: [PATCH 27/57] Refactor: src/screens/Leaderboard from Jest to Vitest (#2694) * chore: migrated leaderboard tests jest to vitest * docs: Added comments for leaderboard tests --- ...derboard.test.tsx => Leaderboard.spec.tsx} | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) rename src/screens/Leaderboard/{Leaderboard.test.tsx => Leaderboard.spec.tsx} (81%) diff --git a/src/screens/Leaderboard/Leaderboard.test.tsx b/src/screens/Leaderboard/Leaderboard.spec.tsx similarity index 81% rename from src/screens/Leaderboard/Leaderboard.test.tsx rename to src/screens/Leaderboard/Leaderboard.spec.tsx index d2f12a9052..b4c73bef5c 100644 --- a/src/screens/Leaderboard/Leaderboard.test.tsx +++ b/src/screens/Leaderboard/Leaderboard.spec.tsx @@ -7,13 +7,34 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; -import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { MemoryRouter, Route, Routes, useParams } from 'react-router-dom'; import { store } from 'state/store'; import { StaticMockLink } from 'utils/StaticMockLink'; import i18n from 'utils/i18nForTest'; import Leaderboard from './Leaderboard'; import type { ApolloLink } from '@apollo/client'; import { MOCKS, EMPTY_MOCKS, ERROR_MOCKS } from './Leaderboard.mocks'; +import { vi } from 'vitest'; + +/** + * Unit tests for the Leaderboard component. + * + * This file verifies the Leaderboard's functionality in scenarios like URL handling, sorting, filtering, + * user interactions, and error states. Mocked dependencies and Apollo links ensure isolated testing + * of Redux, React Router, and internationalization integration. + * + * Key tests include: + * - Redirecting when parameters are missing. + * - Rendering with mock data, empty states, and errors. + * - Sorting and filtering for various timeframes. + * - Searching volunteers and navigating to the Member screen. + * - Handling errors during data fetching. + * + * Mock setups: + * - StaticMockLink for GraphQL responses. + * - Mocked `useParams` for route parameters. + * - Redux store and i18n for consistent state and translations. + */ const link1 = new StaticMockLink(MOCKS); const link2 = new StaticMockLink(ERROR_MOCKS); @@ -62,17 +83,21 @@ const renderLeaderboard = (link: ApolloLink): RenderResult => { describe('Testing Leaderboard Screen', () => { beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId' }), - })); + vi.mock('react-router-dom', async () => { + const originalModule = await vi.importActual('react-router-dom'); + return { + ...originalModule, + useParams: vi.fn(), + }; + }); }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('should redirect to fallback URL if URL params are undefined', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: '' }); render( @@ -96,6 +121,7 @@ describe('Testing Leaderboard Screen', () => { }); it('should render Leaderboard screen', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); await waitFor(() => { @@ -104,6 +130,7 @@ describe('Testing Leaderboard Screen', () => { }); it('Check Sorting Functionality', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); await waitFor(() => { @@ -134,6 +161,7 @@ describe('Testing Leaderboard Screen', () => { }); it('Check Timeframe filter Functionality (All Time)', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); await waitFor(() => { @@ -154,6 +182,7 @@ describe('Testing Leaderboard Screen', () => { }); it('Check Timeframe filter Functionality (Weekly)', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); await waitFor(() => { @@ -176,6 +205,7 @@ describe('Testing Leaderboard Screen', () => { }); it('Check Timeframe filter Functionality (Monthly)', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); await waitFor(() => { @@ -196,6 +226,7 @@ describe('Testing Leaderboard Screen', () => { }); it('Check Timeframe filter Functionality (Yearly)', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); await waitFor(() => { @@ -216,6 +247,7 @@ describe('Testing Leaderboard Screen', () => { }); it('Search Volunteers', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); const searchInput = await screen.findByTestId('searchBy'); @@ -232,6 +264,7 @@ describe('Testing Leaderboard Screen', () => { }); it('OnClick of Member navigate to Member Screen', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link1); const searchInput = await screen.findByTestId('searchBy'); @@ -246,6 +279,7 @@ describe('Testing Leaderboard Screen', () => { }); it('should render Leaderboard screen with No Volunteers', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link3); await waitFor(() => { @@ -255,6 +289,7 @@ describe('Testing Leaderboard Screen', () => { }); it('Error while fetching volunteer data', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderLeaderboard(link2); await waitFor(() => { From 4144fe7c29a1a69f2adc6cde8ae91d60c38721d9 Mon Sep 17 00:00:00 2001 From: Mayank Jha <132004139+MayankJha014@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:52:27 +0530 Subject: [PATCH 28/57] Refactor: src/screens/UserPortal/Volunteer/Actions from Jest to Vitest (#2619) * migrate jest to vitetst * migrate jest to vitetst * migrate jest to vitetst ignore * migrate jest to vitetst eslint fix * ignore action.tsx * migrate jest to vitetst vite config change * coderabbitai asked changes * revert vitest.config.ts * fixing merge issue * tsdoc issue fix * Enhance the useNavigate mock implementation. * trying to inc code range * test failing fix * test fix * test fail fix trying * fix organization issue ts * action.spec waitfor issue fix * excluded uncessary file * fix test failed issue * yearlyEvent calender coverage increase * inc code cov * setting revert back * setting revert * organisation covergae * unwanted file * ignoring covergae for setup.ts * ignoring setup.ts * revert back changes * removing ignore comment * schema fix * schema fix * test to spec * revert test to spec * revert pull req change * codeai to resole * codeai to resole * removing spec file from jest * code coverage fix * code coverage fix * code coverage fix * code patch inc * code patch inc * inc path coverage for eventstatiscs --------- Co-authored-by: Vamshi Maskuri <117595548+varshith257@users.noreply.github.com> Co-authored-by: Mayankpulse333 --- package-lock.json | 220 ++++++++++++++++++ .../EventAttendance/EventStatistics.tsx | 7 +- .../OrganizationScreen.test.tsx | 1 - src/screens/UserPortal/Settings/Settings.tsx | 1 + .../{Actions.test.tsx => Actions.spec.tsx} | 137 +++++++---- vitest.config.ts | 1 + 6 files changed, 314 insertions(+), 53 deletions(-) rename src/screens/UserPortal/Volunteer/Actions/{Actions.test.tsx => Actions.spec.tsx} (56%) diff --git a/package-lock.json b/package-lock.json index d227ca252c..24478f4f69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,6 +99,7 @@ "@typescript-eslint/eslint-plugin": "^8.11.0", "@typescript-eslint/parser": "^8.5.0", "@vitest/coverage-istanbul": "^2.1.5", + "@vitest/eslint-plugin": "^1.1.14", "babel-jest": "^29.7.0", "cross-env": "^7.0.3", "eslint": "^8.49.0", @@ -108,6 +109,7 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.1", "eslint-plugin-tsdoc": "^0.3.0", + "eslint-plugin-vitest": "^0.5.4", "husky": "^9.1.6", "identity-obj-proxy": "^3.0.0", "jest": "^27.4.5", @@ -6377,6 +6379,27 @@ "node": ">=18" } }, + "node_modules/@vitest/eslint-plugin": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.14.tgz", + "integrity": "sha512-ej0cT5rUt7uvwxuu7Qxkm7fI+eaOq8vD34qGpuRoXCdvOybOlE5GDqtgvVCYbxLANkcRJfm5VDU1TnJmQRHi9g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/utils": ">= 8.0", + "eslint": ">= 8.57.0", + "typescript": ">= 5.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, "node_modules/@vitest/expect": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", @@ -6757,6 +6780,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -8996,6 +9029,19 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==" }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -9869,6 +9915,159 @@ "@microsoft/tsdoc-config": "0.17.0" } }, + "node_modules/eslint-plugin-vitest": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", + "integrity": "sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^7.7.1" + }, + "engines": { + "node": "^18.0.0 || >= 20.0.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", @@ -10863,6 +11062,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globrex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", diff --git a/src/components/EventManagement/EventAttendance/EventStatistics.tsx b/src/components/EventManagement/EventAttendance/EventStatistics.tsx index 5dda9e88a8..686ad049fa 100644 --- a/src/components/EventManagement/EventAttendance/EventStatistics.tsx +++ b/src/components/EventManagement/EventAttendance/EventStatistics.tsx @@ -119,9 +119,8 @@ export const AttendanceStatisticsModal: React.FC< try { const eventDate = new Date(event.startDate); if (Number.isNaN(eventDate.getTime())) { - /*istanbul ignore next*/ console.error(`Invalid date for event: ${event._id}`); - /*istanbul ignore next*/ + return 'Invalid date'; } return eventDate.toLocaleDateString('en-US', { @@ -129,12 +128,11 @@ export const AttendanceStatisticsModal: React.FC< day: 'numeric', }); } catch (error) { - /*istanbul ignore next*/ console.error( `Error formatting date for event: ${event._id}`, error, ); - /*istanbul ignore next*/ + return 'Invalid date'; } })(); @@ -325,7 +323,6 @@ export const AttendanceStatisticsModal: React.FC< exportToCSV(data, `${selectedCategory.toLowerCase()}_demographics.csv`); }, [selectedCategory, categoryLabels, categoryData]); - /*istanbul ignore next*/ const handleExport = (eventKey: string | null): void => { switch (eventKey) { case 'trends': diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.test.tsx index 0dfb6d7a14..913c038ab9 100644 --- a/src/components/OrganizationScreen/OrganizationScreen.test.tsx +++ b/src/components/OrganizationScreen/OrganizationScreen.test.tsx @@ -85,7 +85,6 @@ describe('Testing OrganizationScreen', () => { const openButton = screen.getByTestId('openMenu'); fireEvent.click(openButton); - // Check for expand class after opening expect(screen.getByTestId('mainpageright')).toHaveClass(styles.contract); }); diff --git a/src/screens/UserPortal/Settings/Settings.tsx b/src/screens/UserPortal/Settings/Settings.tsx index 6038879b7f..cc222468fc 100644 --- a/src/screens/UserPortal/Settings/Settings.tsx +++ b/src/screens/UserPortal/Settings/Settings.tsx @@ -91,6 +91,7 @@ export default function settings(): JSX.Element { * This function sends a mutation request to update the user details * and reloads the page on success. */ + /*istanbul ignore next*/ const handleUpdateUserDetails = async (): Promise => { try { diff --git a/src/screens/UserPortal/Volunteer/Actions/Actions.test.tsx b/src/screens/UserPortal/Volunteer/Actions/Actions.spec.tsx similarity index 56% rename from src/screens/UserPortal/Volunteer/Actions/Actions.test.tsx rename to src/screens/UserPortal/Volunteer/Actions/Actions.spec.tsx index ce64d98adf..dea26c5fe1 100644 --- a/src/screens/UserPortal/Volunteer/Actions/Actions.test.tsx +++ b/src/screens/UserPortal/Volunteer/Actions/Actions.spec.tsx @@ -1,3 +1,10 @@ +/** + * Unit tests for the Actions component. + * + * This file contains tests for the Actions component to ensure it behaves as expected + * under various scenarios. + */ + import React, { act } from 'react'; import { MockedProvider } from '@apollo/react-testing'; import { LocalizationProvider } from '@mui/x-date-pickers'; @@ -15,6 +22,7 @@ import Actions from './Actions'; import type { ApolloLink } from '@apollo/client'; import { MOCKS, EMPTY_MOCKS, ERROR_MOCKS } from './Actions.mocks'; import useLocalStorage from 'utils/useLocalstorage'; +import { describe, it, beforeAll, beforeEach, afterAll, vi } from 'vitest'; const { setItem } = useLocalStorage(); @@ -39,6 +47,18 @@ const debounceWait = async (ms = 300): Promise => { }); }); }; +const mockNavigate = vi.fn(); + +const expectVitestToBeInTheDocument = (element: HTMLElement): void => { + expect(element).toBeInTheDocument(); +}; + +const expectElementToHaveTextContent = ( + element: HTMLElement, + text: string, +): void => { + expect(element).toHaveTextContent(text); +}; const renderActions = (link: ApolloLink): RenderResult => { return render( @@ -64,10 +84,13 @@ const renderActions = (link: ApolloLink): RenderResult => { describe('Testing Actions 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'); // Import the actual implementation + return { + ...actual, + useNavigate: () => mockNavigate, // Replace useNavigate with the mock + }; + }); }); beforeEach(() => { @@ -75,7 +98,7 @@ describe('Testing Actions Screen', () => { }); afterAll(() => { - jest.clearAllMocks(); + vi.restoreAllMocks(); }); it('should redirect to fallback URL if URL params are undefined', async () => { @@ -99,95 +122,109 @@ describe('Testing Actions Screen', () => { ); await waitFor(() => { - expect(screen.getByTestId('paramsError')).toBeInTheDocument(); + expectVitestToBeInTheDocument(screen.getByTestId('paramsError')); }); }); it('should render Actions screen', async () => { renderActions(link1); - const searchInput = await screen.findByTestId('searchBy'); - expect(searchInput).toBeInTheDocument(); + await waitFor(async () => { + const searchInput = await screen.findByTestId('searchBy'); + expectVitestToBeInTheDocument(searchInput); - const assigneeName = await screen.findAllByTestId('assigneeName'); - expect(assigneeName[0]).toHaveTextContent('Teresa Bradley'); + const assigneeName = await screen.findAllByTestId('assigneeName'); + expectElementToHaveTextContent(assigneeName[0], 'Teresa Bradley'); + }); }); it('Check Sorting Functionality', async () => { renderActions(link1); + const searchInput = await screen.findByTestId('searchBy'); - expect(searchInput).toBeInTheDocument(); + expectVitestToBeInTheDocument(searchInput); let sortBtn = await screen.findByTestId('sort'); - expect(sortBtn).toBeInTheDocument(); + expectVitestToBeInTheDocument(sortBtn); // Sort by dueDate_DESC fireEvent.click(sortBtn); const dueDateDESC = await screen.findByTestId('dueDate_DESC'); - expect(dueDateDESC).toBeInTheDocument(); + expectVitestToBeInTheDocument(dueDateDESC); fireEvent.click(dueDateDESC); - let assigneeName = await screen.findAllByTestId('assigneeName'); - expect(assigneeName[0]).toHaveTextContent('Group 1'); + await waitFor(() => { + const assigneeName = screen.getAllByTestId('assigneeName'); + expectElementToHaveTextContent(assigneeName[0], 'Group 1'); + }); // Sort by dueDate_ASC sortBtn = await screen.findByTestId('sort'); - expect(sortBtn).toBeInTheDocument(); + expectVitestToBeInTheDocument(sortBtn); fireEvent.click(sortBtn); const dueDateASC = await screen.findByTestId('dueDate_ASC'); - expect(dueDateASC).toBeInTheDocument(); + expectVitestToBeInTheDocument(dueDateASC); fireEvent.click(dueDateASC); - assigneeName = await screen.findAllByTestId('assigneeName'); - expect(assigneeName[0]).toHaveTextContent('Teresa Bradley'); + await waitFor(() => { + const assigneeName = screen.getAllByTestId('assigneeName'); + expectElementToHaveTextContent(assigneeName[0], 'Teresa Bradley'); + }); }); it('Search by Assignee name', async () => { renderActions(link1); - const searchInput = await screen.findByTestId('searchBy'); - expect(searchInput).toBeInTheDocument(); + await waitFor(async () => { + const searchInput = await screen.findByTestId('searchBy'); + expectVitestToBeInTheDocument(searchInput); - const searchToggle = await screen.findByTestId('searchByToggle'); - expect(searchToggle).toBeInTheDocument(); - userEvent.click(searchToggle); + const searchToggle = await screen.findByTestId('searchByToggle'); + expectVitestToBeInTheDocument(searchToggle); + userEvent.click(searchToggle); - const searchByAssignee = await screen.findByTestId('assignee'); - expect(searchByAssignee).toBeInTheDocument(); - userEvent.click(searchByAssignee); + const searchByAssignee = await screen.findByTestId('assignee'); + expectVitestToBeInTheDocument(searchByAssignee); + userEvent.click(searchByAssignee); - userEvent.type(searchInput, '1'); + userEvent.type(searchInput, '1'); + }); await debounceWait(); - const assigneeName = await screen.findAllByTestId('assigneeName'); - expect(assigneeName[0]).toHaveTextContent('Group 1'); + await waitFor(async () => { + const assigneeName = await screen.findAllByTestId('assigneeName'); + expectElementToHaveTextContent(assigneeName[0], 'Group 1'); + }); }); it('Search by Category name', async () => { renderActions(link1); - const searchInput = await screen.findByTestId('searchBy'); - expect(searchInput).toBeInTheDocument(); + await waitFor(async () => { + const searchInput = await screen.findByTestId('searchBy'); + expectVitestToBeInTheDocument(searchInput); - const searchToggle = await screen.findByTestId('searchByToggle'); - expect(searchToggle).toBeInTheDocument(); - userEvent.click(searchToggle); + const searchToggle = await screen.findByTestId('searchByToggle'); + expectVitestToBeInTheDocument(searchToggle); + userEvent.click(searchToggle); - const searchByCategory = await screen.findByTestId('category'); - expect(searchByCategory).toBeInTheDocument(); - userEvent.click(searchByCategory); + const searchByCategory = await screen.findByTestId('category'); + expectVitestToBeInTheDocument(searchByCategory); + userEvent.click(searchByCategory); - // Search by name on press of ENTER - userEvent.type(searchInput, '1'); + userEvent.type(searchInput, '1'); + }); await debounceWait(); - const assigneeName = await screen.findAllByTestId('assigneeName'); - expect(assigneeName[0]).toHaveTextContent('Teresa Bradley'); + await waitFor(() => { + const assigneeName = screen.getAllByTestId('assigneeName'); + expectElementToHaveTextContent(assigneeName[0], 'Teresa Bradley'); + }); }); it('should render screen with No Actions', async () => { renderActions(link3); await waitFor(() => { - expect(screen.getByTestId('searchBy')).toBeInTheDocument(); - expect(screen.getByText(t.noActionItems)).toBeInTheDocument(); + expectVitestToBeInTheDocument(screen.getByTestId('searchBy')); + expectVitestToBeInTheDocument(screen.getByText(t.noActionItems)); }); }); @@ -195,7 +232,7 @@ describe('Testing Actions Screen', () => { renderActions(link2); await waitFor(() => { - expect(screen.getByTestId('errorMsg')).toBeInTheDocument(); + expectVitestToBeInTheDocument(screen.getByTestId('errorMsg')); }); }); @@ -205,7 +242,10 @@ describe('Testing Actions Screen', () => { const checkbox = await screen.findAllByTestId('statusCheckbox'); userEvent.click(checkbox[0]); - expect(await screen.findByText(t.actionItemStatus)).toBeInTheDocument(); + await waitFor(async () => { + const element = await screen.findByText(t.actionItemStatus); // Resolve the promise + expectVitestToBeInTheDocument(element); // Now assert the resolved element + }); userEvent.click(await screen.findByTestId('modalCloseBtn')); }); @@ -215,7 +255,10 @@ describe('Testing Actions Screen', () => { const viewItemBtn = await screen.findAllByTestId('viewItemBtn'); userEvent.click(viewItemBtn[0]); - expect(await screen.findByText(t.actionItemDetails)).toBeInTheDocument(); + await waitFor(() => { + expectVitestToBeInTheDocument(screen.getByText(t.actionItemDetails)); + }); + userEvent.click(await screen.findByTestId('modalCloseBtn')); }); }); diff --git a/vitest.config.ts b/vitest.config.ts index c158cf9c2a..3d071e7534 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -31,6 +31,7 @@ export default defineConfig({ '**/*.d.ts', 'src/test/**', 'vitest.config.ts', + 'vitest.setup.ts', // Exclude from coverage if necessary ], reporter: ['text', 'html', 'text-summary', 'lcov'], }, From 4831b84174e7bb669fa00cba80f066bcf151c250 Mon Sep 17 00:00:00 2001 From: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Date: Sat, 21 Dec 2024 10:23:25 -0800 Subject: [PATCH 29/57] Update pull-request.yml --- .github/workflows/pull-request.yml | 49 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 5096758ea5..fb6505324e 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -253,33 +253,34 @@ jobs: path: "./coverage/lcov.info" min_coverage: 0.0 - Graphql-Inspector: - if: ${{ github.actor != 'dependabot[bot]' }} - name: Runs Introspection on the GitHub talawa-api repo on the schema.graphql file - runs-on: ubuntu-latest - steps: - - name: Checkout the Repository - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '22.x' - - - name: resolve dependency - run: npm install -g @graphql-inspector/cli + # Graphql-Inspector: + # if: ${{ github.actor != 'dependabot[bot]' }} + # name: Runs Introspection on the GitHub talawa-api repo on the schema.graphql file + # runs-on: ubuntu-latest + # steps: + # - name: Checkout the Repository + # uses: actions/checkout@v4 + + # - name: Set up Node.js + # uses: actions/setup-node@v4 + # with: + # node-version: '22.x' + + # - name: resolve dependency + # run: npm install -g @graphql-inspector/cli - - name: Clone API Repository - run: | - # Retrieve the complete branch name directly from the GitHub context - FULL_BRANCH_NAME=${{ github.base_ref }} - echo "FULL_Branch_NAME: $FULL_BRANCH_NAME" + # - name: Clone API Repository + # run: | + # # Retrieve the complete branch name directly from the GitHub context + # FULL_BRANCH_NAME=${{ github.base_ref }} + # echo "FULL_Branch_NAME: $FULL_BRANCH_NAME" - # Clone the specified repository using the extracted branch name - git clone --branch $FULL_BRANCH_NAME https://github.com/PalisadoesFoundation/talawa-api && ls -a + # # Clone the specified repository using the extracted branch name + # git clone --branch $FULL_BRANCH_NAME https://github.com/PalisadoesFoundation/talawa-api && ls -a - - name: Validate Documents - run: graphql-inspector validate './src/GraphQl/**/*.ts' './talawa-api/schema.graphql' + # - name: Validate Documents + # run: graphql-inspector validate './src/GraphQl/**/*.ts' './talawa-api/schema.graphql' + Start-App-Without-Docker: name: Check if Talawa Admin app starts (No Docker) runs-on: ubuntu-latest From 7bf7ab14f082723697bb6c8663e63854fd5409a3 Mon Sep 17 00:00:00 2001 From: Abhishek Raj <113784630+abbi4code@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:07:24 +0530 Subject: [PATCH 30/57] Refactor: src/screens/OrganizationDashboard from Jest to Vitest (#2678) * migrate OrganizationDashboard tests from Jest to Vitest and fixed all the failing tests * chore: Removed debug console.log statements --------- Co-authored-by: Vamshi Maskuri <117595548+varshith257@users.noreply.github.com> --- .../OrganizationScreen.test.tsx | 2 - ...est.tsx => OrganizationDashboard.spec.tsx} | 56 +++++++++++++++---- 2 files changed, 45 insertions(+), 13 deletions(-) rename src/screens/OrganizationDashboard/{OrganizationDashboard.test.tsx => OrganizationDashboard.spec.tsx} (81%) diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.test.tsx index 913c038ab9..9bb95bbd4a 100644 --- a/src/components/OrganizationScreen/OrganizationScreen.test.tsx +++ b/src/components/OrganizationScreen/OrganizationScreen.test.tsx @@ -90,10 +90,8 @@ describe('Testing OrganizationScreen', () => { test('handles window resize', () => { renderComponent(); - window.innerWidth = 800; fireEvent(window, new Event('resize')); - expect(screen.getByTestId('mainpageright')).toHaveClass(styles.expand); }); }); diff --git a/src/screens/OrganizationDashboard/OrganizationDashboard.test.tsx b/src/screens/OrganizationDashboard/OrganizationDashboard.spec.tsx similarity index 81% rename from src/screens/OrganizationDashboard/OrganizationDashboard.test.tsx rename to src/screens/OrganizationDashboard/OrganizationDashboard.spec.tsx index 88db2aa737..b7b4e05a37 100644 --- a/src/screens/OrganizationDashboard/OrganizationDashboard.test.tsx +++ b/src/screens/OrganizationDashboard/OrganizationDashboard.spec.tsx @@ -7,7 +7,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; -import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { MemoryRouter, Route, Routes, useParams } from 'react-router-dom'; import { store } from 'state/store'; import { StaticMockLink } from 'utils/StaticMockLink'; import i18n from 'utils/i18nForTest'; @@ -15,11 +15,25 @@ import OrganizationDashboard from './OrganizationDashboard'; import type { ApolloLink } from '@apollo/client'; import { MOCKS, EMPTY_MOCKS, ERROR_MOCKS } from './OrganizationDashboardMocks'; import { toast } from 'react-toastify'; - -jest.mock('react-toastify', () => ({ +import { vi } from 'vitest'; + +/** + * This file contains unit tests for the OrganizationDashboard component. + * + * The tests cover: + * - Behavior when URL parameters are undefined, including redirection to fallback URLs. + * - Rendering of key sections, such as dashboard cards, upcoming events, latest posts, membership requests, and volunteer rankings. + * - Functionality of user interactions with dashboard elements (e.g., navigation via clicks on cards and buttons). + * - Handling of scenarios with empty data or errors in GraphQL responses. + * - Integration with mocked GraphQL queries and toast notifications. + * + * These tests are implemented using Vitest for test execution and MockedProvider for mocking GraphQL queries. + */ + +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -89,40 +103,48 @@ const renderOrganizationDashboard = (link: ApolloLink): RenderResult => { describe('Testing Organization Dashboard Screen', () => { beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId' }), - })); + vi.mock('react-router-dom', async () => { + const originalModule = await vi.importActual('react-router-dom'); + return { + ...originalModule, + useParams: vi.fn(), + }; + }); }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('should redirect to fallback URL if URL params are undefined', async () => { + vi.mocked(useParams).mockReturnValue({}); render( - } />

} /> + } />
, ); + await waitFor(() => { + expect(window.location.pathname).toBe('/'); + }); await waitFor(() => { expect(screen.getByTestId('paramsError')).toBeInTheDocument(); }); }); it('should render Organization Dashboard screen', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); // Dashboard cards @@ -151,6 +173,7 @@ describe('Testing Organization Dashboard Screen', () => { }); it('Click People Card', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const membersBtn = await screen.findByText(t.members); expect(membersBtn).toBeInTheDocument(); @@ -162,6 +185,7 @@ describe('Testing Organization Dashboard Screen', () => { }); it('Click Admin Card', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const adminsBtn = await screen.findByText(t.admins); expect(adminsBtn).toBeInTheDocument(); @@ -169,6 +193,7 @@ describe('Testing Organization Dashboard Screen', () => { }); it('Click Post Card', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const postsBtn = await screen.findByText(t.posts); expect(postsBtn).toBeInTheDocument(); @@ -180,6 +205,7 @@ it('Click Post Card', async () => { }); it('Click Events Card', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const eventsBtn = await screen.findByText(t.events); expect(eventsBtn).toBeInTheDocument(); @@ -191,6 +217,7 @@ it('Click Events Card', async () => { }); it('Click Blocked Users Card', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const blockedUsersBtn = await screen.findByText(t.blockedUsers); expect(blockedUsersBtn).toBeInTheDocument(); @@ -202,6 +229,7 @@ it('Click Blocked Users Card', async () => { }); it('Click Requests Card', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const requestsBtn = await screen.findByText(t.requests); expect(requestsBtn).toBeInTheDocument(); @@ -213,6 +241,7 @@ it('Click Requests Card', async () => { }); it('Click View All Events', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const viewAllBtn = await screen.findAllByText(t.viewAll); expect(viewAllBtn[0]).toBeInTheDocument(); @@ -224,6 +253,7 @@ it('Click View All Events', async () => { }); it('Click View All Posts', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const viewAllBtn = await screen.findAllByText(t.viewAll); expect(viewAllBtn[1]).toBeInTheDocument(); @@ -235,6 +265,7 @@ it('Click View All Posts', async () => { }); it('Click View All Requests', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const viewAllBtn = await screen.findAllByText(t.viewAll); expect(viewAllBtn[2]).toBeInTheDocument(); @@ -246,6 +277,7 @@ it('Click View All Requests', async () => { }); it('Click View All Leaderboard', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link1); const viewAllBtn = await screen.findAllByText(t.viewAll); expect(viewAllBtn[3]).toBeInTheDocument(); @@ -257,6 +289,7 @@ it('Click View All Leaderboard', async () => { }); it('should render Organization Dashboard screen with empty data', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link3); await waitFor(() => { @@ -268,6 +301,7 @@ it('should render Organization Dashboard screen with empty data', async () => { }); it('should redirectt to / if error occurs', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationDashboard(link2); await waitFor(() => { From a16e02e975440859c892e34c69d313e7fe6421ac Mon Sep 17 00:00:00 2001 From: MANDEEP N H <146331633+mandeepnh5@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:10:39 +0530 Subject: [PATCH 31/57] Update pagenotfound (#2698) Co-authored-by: Peter Harrison <16875803+palisadoes@users.noreply.github.com> --- .../PageNotFound/PageNotFound.module.css | 109 ----------------- src/screens/PageNotFound/PageNotFound.tsx | 4 +- src/style/app.module.css | 110 ++++++++++++++++++ 3 files changed, 112 insertions(+), 111 deletions(-) delete mode 100644 src/screens/PageNotFound/PageNotFound.module.css diff --git a/src/screens/PageNotFound/PageNotFound.module.css b/src/screens/PageNotFound/PageNotFound.module.css deleted file mode 100644 index 3c1b9a3413..0000000000 --- a/src/screens/PageNotFound/PageNotFound.module.css +++ /dev/null @@ -1,109 +0,0 @@ -.notfound { - position: relative; - bottom: 20px; -} - -.notfound h3 { - font-family: 'Roboto', sans-serif; - font-weight: normal; - letter-spacing: 1px; -} - -.notfound .brand span { - margin-top: 50px; - font-size: 40px; -} -.notfound .brand h3 { - font-weight: 300; - margin: 10px 0 0 0; -} -.notfound h1.head { - font-size: 250px; - font-weight: 900; - color: #31bb6b; - letter-spacing: 25px; - margin: 10px 0 0 0; -} -.notfound h1.head span { - position: relative; - display: inline-block; -} -.notfound h1.head span:before, -.notfound h1.head span:after { - position: absolute; - top: 50%; - width: 50%; - height: 1px; - background: #fff; - content: ''; -} -.notfound h1.head span:before { - left: -55%; -} -.notfound h1.head span:after { - right: -55%; -} - -@media (max-width: 1024px) { - .notfound h1.head { - font-size: 200px; - letter-spacing: 25px; - } -} - -@media (max-width: 768px) { - .notfound h1.head { - font-size: 150px; - letter-spacing: 25px; - } -} - -@media (max-width: 640px) { - .notfound h1.head { - font-size: 150px; - letter-spacing: 0; - } -} - -@media (max-width: 480px) { - .notfound .brand h3 { - font-size: 20px; - } - .notfound h1.head { - font-size: 130px; - letter-spacing: 0; - } - .notfound h1.head span:before, - .notfound h1.head span:after { - width: 40%; - } - .notfound h1.head span:before { - left: -45%; - } - .notfound h1.head span:after { - right: -45%; - } - .notfound p { - font-size: 18px; - } -} - -@media (max-width: 320px) { - .notfound .brand h3 { - font-size: 16px; - } - .notfound h1.head { - font-size: 100px; - letter-spacing: 0; - } - .notfound h1.head span:before, - .notfound h1.head span:after { - width: 25%; - } - .notfound h1.head span:before { - left: -30%; - } - .notfound h1.head span:after { - right: -30%; - } -} diff --git a/src/screens/PageNotFound/PageNotFound.tsx b/src/screens/PageNotFound/PageNotFound.tsx index 037aeecbab..62ed90c423 100644 --- a/src/screens/PageNotFound/PageNotFound.tsx +++ b/src/screens/PageNotFound/PageNotFound.tsx @@ -3,7 +3,7 @@ import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import useLocalStorage from 'utils/useLocalstorage'; -import styles from './PageNotFound.module.css'; +import styles from '../../style/app.module.css'; import Logo from 'assets/images/talawa-logo-600x600.png'; /** @@ -28,7 +28,7 @@ const PageNotFound = (): JSX.Element => { const adminFor = getItem('AdminFor'); return ( -
+
Logo diff --git a/src/style/app.module.css b/src/style/app.module.css index 982cec9672..d21c2bb845 100644 --- a/src/style/app.module.css +++ b/src/style/app.module.css @@ -539,6 +539,116 @@ hr { color: var(--bs-primary) !important; } +.pageNotFound { + position: relative; + bottom: 20px; +} + +.pageNotFound h3 { + font-family: 'Roboto', sans-serif; + font-weight: normal; + letter-spacing: 1px; +} + +.pageNotFound .brand span { + margin-top: 50px; + font-size: 40px; +} +.pageNotFound .brand h3 { + font-weight: 300; + margin: 10px 0 0 0; +} +.pageNotFound h1.head { + font-size: 250px; + font-weight: 900; + color: #31bb6b; + letter-spacing: 25px; + margin: 10px 0 0 0; +} +.pageNotFound h1.head span { + position: relative; + display: inline-block; +} +.pageNotFound h1.head span:before, +.pageNotFound h1.head span:after { + position: absolute; + top: 50%; + width: 50%; + height: 1px; + background: #fff; + content: ''; +} +.pageNotFound h1.head span:before { + left: -55%; +} +.pageNotFound h1.head span:after { + right: -55%; +} + +@media (max-width: 1024px) { + .pageNotFound h1.head { + font-size: 200px; + letter-spacing: 25px; + } +} + +@media (max-width: 768px) { + .pageNotFound h1.head { + font-size: 150px; + letter-spacing: 25px; + } +} + +@media (max-width: 640px) { + .pageNotFound h1.head { + font-size: 150px; + letter-spacing: 0; + } +} + +@media (max-width: 480px) { + .pageNotFound .brand h3 { + font-size: 20px; + } + .pageNotFound h1.head { + font-size: 130px; + letter-spacing: 0; + } + .pageNotFound h1.head span:before, + .pageNotFound h1.head span:after { + width: 40%; + } + .pageNotFound h1.head span:before { + left: -45%; + } + .pageNotFound h1.head span:after { + right: -45%; + } + .pageNotFound p { + font-size: 18px; + } +} + +@media (max-width: 320px) { + .pageNotFound .brand h3 { + font-size: 16px; + } + .pageNotFound h1.head { + font-size: 100px; + letter-spacing: 0; + } + .pageNotFound h1.head span:before, + .pageNotFound h1.head span:after { + width: 25%; + } + .pageNotFound h1.head span:before { + left: -30%; + } + .pageNotFound h1.head span:after { + right: -30%; + } +} + @media (max-width: 520px) { .btnsContainer { margin-bottom: 0; From 13aad7cf5a30e62cc75a57d22acdc7ca98850d7b Mon Sep 17 00:00:00 2001 From: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:28:59 +0530 Subject: [PATCH 32/57] refactor: jest to vitest of itemUpdateStatusModal : fixes #2557 (#2714) --- ...est.tsx => ItemUpdateStatusModal.spec.tsx} | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) rename src/screens/OrganizationActionItems/{ItemUpdateStatusModal.test.tsx => ItemUpdateStatusModal.spec.tsx} (96%) diff --git a/src/screens/OrganizationActionItems/ItemUpdateStatusModal.test.tsx b/src/screens/OrganizationActionItems/ItemUpdateStatusModal.spec.tsx similarity index 96% rename from src/screens/OrganizationActionItems/ItemUpdateStatusModal.test.tsx rename to src/screens/OrganizationActionItems/ItemUpdateStatusModal.spec.tsx index aa28b14d40..4d9e16d11e 100644 --- a/src/screens/OrganizationActionItems/ItemUpdateStatusModal.test.tsx +++ b/src/screens/OrganizationActionItems/ItemUpdateStatusModal.spec.tsx @@ -16,11 +16,12 @@ import { toast } from 'react-toastify'; import ItemUpdateStatusModal, { type InterfaceItemUpdateStatusModalProps, } from './ItemUpdateStatusModal'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -35,8 +36,8 @@ const t = JSON.parse( const itemProps: InterfaceItemUpdateStatusModalProps[] = [ { isOpen: true, - hide: jest.fn(), - actionItemsRefetch: jest.fn(), + hide: vi.fn(), + actionItemsRefetch: vi.fn(), actionItem: { _id: 'actionItemId1', assignee: null, @@ -75,8 +76,8 @@ const itemProps: InterfaceItemUpdateStatusModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), - actionItemsRefetch: jest.fn(), + hide: vi.fn(), + actionItemsRefetch: vi.fn(), actionItem: { _id: 'actionItemId1', assignee: null, @@ -148,8 +149,8 @@ const itemProps: InterfaceItemUpdateStatusModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), - actionItemsRefetch: jest.fn(), + hide: vi.fn(), + actionItemsRefetch: vi.fn(), actionItem: { _id: 'actionItemId1', assignee: { From ca0b0f679703324bee740a7007fda3b2a197db32 Mon Sep 17 00:00:00 2001 From: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:58:26 +0530 Subject: [PATCH 33/57] refactor: jest to vitest : fixes #2557 (#2713) --- ...odal.test.tsx => ItemDeleteModal.spec.tsx} | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) rename src/screens/OrganizationActionItems/{ItemDeleteModal.test.tsx => ItemDeleteModal.spec.tsx} (91%) diff --git a/src/screens/OrganizationActionItems/ItemDeleteModal.test.tsx b/src/screens/OrganizationActionItems/ItemDeleteModal.spec.tsx similarity index 91% rename from src/screens/OrganizationActionItems/ItemDeleteModal.test.tsx rename to src/screens/OrganizationActionItems/ItemDeleteModal.spec.tsx index fffeebfd7f..5bdb1ffde1 100644 --- a/src/screens/OrganizationActionItems/ItemDeleteModal.test.tsx +++ b/src/screens/OrganizationActionItems/ItemDeleteModal.spec.tsx @@ -3,7 +3,13 @@ import type { ApolloLink } from '@apollo/client'; import { MockedProvider } from '@apollo/react-testing'; import { LocalizationProvider } from '@mui/x-date-pickers'; import type { RenderResult } from '@testing-library/react'; -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { + fireEvent, + render, + screen, + waitFor, + act, +} from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; @@ -16,11 +22,12 @@ import { toast } from 'react-toastify'; import ItemDeleteModal, { type InterfaceItemDeleteModalProps, } from './ItemDeleteModal'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -34,8 +41,8 @@ const t = JSON.parse( const itemProps: InterfaceItemDeleteModalProps = { isOpen: true, - hide: jest.fn(), - actionItemsRefetch: jest.fn(), + hide: vi.fn(), + actionItemsRefetch: vi.fn(), actionItem: { _id: 'actionItemId1', assignee: null, @@ -102,7 +109,9 @@ describe('Testing ItemDeleteModal', () => { renderItemDeleteModal(link1, itemProps); expect(screen.getByTestId('deleteyesbtn')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('deleteyesbtn')); + await act(() => { + fireEvent.click(screen.getByTestId('deleteyesbtn')); + }); await waitFor(() => { expect(itemProps.actionItemsRefetch).toHaveBeenCalled(); From ed0a01cc93e30858b2ecc8450e9ab64a2d898734 Mon Sep 17 00:00:00 2001 From: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:59:16 +0530 Subject: [PATCH 34/57] Refactor: jest to vitest of itemModal : fixes #2557 (#2715) --- ...{ItemModal.test.tsx => ItemModal.spec.tsx} | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) rename src/screens/OrganizationActionItems/{ItemModal.test.tsx => ItemModal.spec.tsx} (98%) diff --git a/src/screens/OrganizationActionItems/ItemModal.test.tsx b/src/screens/OrganizationActionItems/ItemModal.spec.tsx similarity index 98% rename from src/screens/OrganizationActionItems/ItemModal.test.tsx rename to src/screens/OrganizationActionItems/ItemModal.spec.tsx index a58496e6df..1fe3d6fca5 100644 --- a/src/screens/OrganizationActionItems/ItemModal.test.tsx +++ b/src/screens/OrganizationActionItems/ItemModal.spec.tsx @@ -21,12 +21,13 @@ import { StaticMockLink } from 'utils/StaticMockLink'; import { toast } from 'react-toastify'; import type { InterfaceItemModalProps } from './ItemModal'; import ItemModal from './ItemModal'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), - warning: jest.fn(), + success: vi.fn(), + error: vi.fn(), + warning: vi.fn(), }, })); @@ -45,28 +46,28 @@ const t = { const itemProps: InterfaceItemModalProps[] = [ { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), orgId: 'orgId', eventId: undefined, - actionItemsRefetch: jest.fn(), + actionItemsRefetch: vi.fn(), editMode: false, actionItem: null, }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), orgId: 'orgId', eventId: 'eventId', - actionItemsRefetch: jest.fn(), + actionItemsRefetch: vi.fn(), editMode: false, actionItem: null, }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), orgId: 'orgId', eventId: undefined, - actionItemsRefetch: jest.fn(), + actionItemsRefetch: vi.fn(), editMode: true, actionItem: { _id: 'actionItemId1', @@ -106,10 +107,10 @@ const itemProps: InterfaceItemModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), orgId: 'orgId', eventId: undefined, - actionItemsRefetch: jest.fn(), + actionItemsRefetch: vi.fn(), editMode: true, actionItem: { _id: 'actionItemId2', @@ -149,10 +150,10 @@ const itemProps: InterfaceItemModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), orgId: 'orgId', eventId: 'eventId', - actionItemsRefetch: jest.fn(), + actionItemsRefetch: vi.fn(), editMode: true, actionItem: { _id: 'actionItemId2', @@ -202,10 +203,10 @@ const itemProps: InterfaceItemModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), orgId: 'orgId', eventId: 'eventId', - actionItemsRefetch: jest.fn(), + actionItemsRefetch: vi.fn(), editMode: true, actionItem: { _id: 'actionItemId2', From e2a86ae4773ade0d0cb835167bb5f905a1a2d6b6 Mon Sep 17 00:00:00 2001 From: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:59:59 +0530 Subject: [PATCH 35/57] refactor: jest to vitest of itemViewModal : fixes #2557 (#2716) * refactor: jest to vitest of itemViewModal : fixes #2557 * Update ItemViewModal.spec.tsx --- ...iewModal.test.tsx => ItemViewModal.spec.tsx} | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) rename src/screens/OrganizationActionItems/{ItemViewModal.test.tsx => ItemViewModal.spec.tsx} (97%) diff --git a/src/screens/OrganizationActionItems/ItemViewModal.test.tsx b/src/screens/OrganizationActionItems/ItemViewModal.spec.tsx similarity index 97% rename from src/screens/OrganizationActionItems/ItemViewModal.test.tsx rename to src/screens/OrganizationActionItems/ItemViewModal.spec.tsx index 297cfab6a8..b6423e356d 100644 --- a/src/screens/OrganizationActionItems/ItemViewModal.test.tsx +++ b/src/screens/OrganizationActionItems/ItemViewModal.spec.tsx @@ -18,11 +18,12 @@ import type { InterfaceUserInfo, InterfaceVolunteerGroupInfo, } from 'utils/interfaces'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -86,7 +87,7 @@ const actionItemCategory = { const itemProps: InterfaceViewModalProps[] = [ { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), item: { _id: 'actionItemId1', assignee: createAssignee(assigneeWithoutImage), @@ -108,7 +109,7 @@ const itemProps: InterfaceViewModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), item: { _id: 'actionItemId2', assignee: createAssignee(assigneeWithImage), @@ -130,7 +131,7 @@ const itemProps: InterfaceViewModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), item: { _id: 'actionItemId2', assignee: null, @@ -152,7 +153,7 @@ const itemProps: InterfaceViewModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), item: { _id: 'actionItemId2', assignee: null, @@ -174,7 +175,7 @@ const itemProps: InterfaceViewModalProps[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), item: { _id: 'actionItemId2', assignee: null, From 84c167cf4b9f8b846f1bab04e65f9e2db62eb63c Mon Sep 17 00:00:00 2001 From: Shiva <148421597+shivasankaran18@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:05:12 +0530 Subject: [PATCH 36/57] Refactor : Vitest to EventVolunteers/VolunteerGroups Screen (#2696) * refactor:vitest to EventVolunteers/VolunteerGroups screen * add:ts-doc * fix:codereabbit sugg --------- Co-authored-by: Peter Harrison <16875803+palisadoes@users.noreply.github.com> --- ...tsx => VolunteerGroupDeleteModal.spec.tsx} | 17 +++++++--- ....test.tsx => VolunteerGroupModal.spec.tsx} | 25 +++++++++----- ...t.tsx => VolunteerGroupViewModal.spec.tsx} | 5 +-- ...oups.test.tsx => VolunteerGroups.spec.tsx} | 34 +++++++++++++++---- 4 files changed, 59 insertions(+), 22 deletions(-) rename src/screens/EventVolunteers/VolunteerGroups/{VolunteerGroupDeleteModal.test.tsx => VolunteerGroupDeleteModal.spec.tsx} (92%) rename src/screens/EventVolunteers/VolunteerGroups/{VolunteerGroupModal.test.tsx => VolunteerGroupModal.spec.tsx} (96%) rename src/screens/EventVolunteers/VolunteerGroups/{VolunteerGroupViewModal.test.tsx => VolunteerGroupViewModal.spec.tsx} (98%) rename src/screens/EventVolunteers/VolunteerGroups/{VolunteerGroups.test.tsx => VolunteerGroups.spec.tsx} (89%) diff --git a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.test.tsx b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.spec.tsx similarity index 92% rename from src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.test.tsx rename to src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.spec.tsx index 05c2dab5ff..8e726028db 100644 --- a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.test.tsx +++ b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.spec.tsx @@ -16,11 +16,18 @@ import { toast } from 'react-toastify'; import type { InterfaceDeleteVolunteerGroupModal } from './VolunteerGroupDeleteModal'; import VolunteerGroupDeleteModal from './VolunteerGroupDeleteModal'; import userEvent from '@testing-library/user-event'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +/** + * Mock implementation of the `react-toastify` module. + * Mocks the `toast` object with `success` and `error` methods to allow testing + * without triggering actual toast notifications. + */ + +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -39,8 +46,8 @@ const t = { const itemProps: InterfaceDeleteVolunteerGroupModal[] = [ { isOpen: true, - hide: jest.fn(), - refetchGroups: jest.fn(), + hide: vi.fn(), + refetchGroups: vi.fn(), group: { _id: 'groupId', name: 'Group 1', diff --git a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.test.tsx b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.spec.tsx similarity index 96% rename from src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.test.tsx rename to src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.spec.tsx index 2fc0b2e348..79b1d94545 100644 --- a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.test.tsx +++ b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.spec.tsx @@ -22,11 +22,18 @@ import { toast } from 'react-toastify'; import type { InterfaceVolunteerGroupModal } from './VolunteerGroupModal'; import GroupModal from './VolunteerGroupModal'; import userEvent from '@testing-library/user-event'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +/** + * Mock implementation of the `react-toastify` module. + * Mocks the `toast` object with `success` and `error` methods to allow testing + * without triggering actual toast notifications. + */ + +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -45,19 +52,19 @@ const t = { const itemProps: InterfaceVolunteerGroupModal[] = [ { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), eventId: 'eventId', orgId: 'orgId', - refetchGroups: jest.fn(), + refetchGroups: vi.fn(), mode: 'create', group: null, }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), eventId: 'eventId', orgId: 'orgId', - refetchGroups: jest.fn(), + refetchGroups: vi.fn(), mode: 'edit', group: { _id: 'groupId', @@ -96,10 +103,10 @@ const itemProps: InterfaceVolunteerGroupModal[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), eventId: 'eventId', orgId: 'orgId', - refetchGroups: jest.fn(), + refetchGroups: vi.fn(), mode: 'edit', group: { _id: 'groupId', diff --git a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.test.tsx b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.spec.tsx similarity index 98% rename from src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.test.tsx rename to src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.spec.tsx index 94c34923a2..b029909809 100644 --- a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.test.tsx +++ b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.spec.tsx @@ -11,6 +11,7 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import i18n from 'utils/i18nForTest'; import type { InterfaceVolunteerGroupViewModal } from './VolunteerGroupViewModal'; import VolunteerGroupViewModal from './VolunteerGroupViewModal'; +import { vi } from 'vitest'; const t = { ...JSON.parse( @@ -25,7 +26,7 @@ const t = { const itemProps: InterfaceVolunteerGroupViewModal[] = [ { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), group: { _id: 'groupId', name: 'Group 1', @@ -63,7 +64,7 @@ const itemProps: InterfaceVolunteerGroupViewModal[] = [ }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), group: { _id: 'groupId', name: 'Group 1', diff --git a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroups.test.tsx b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroups.spec.tsx similarity index 89% rename from src/screens/EventVolunteers/VolunteerGroups/VolunteerGroups.test.tsx rename to src/screens/EventVolunteers/VolunteerGroups/VolunteerGroups.spec.tsx index 0dcba34a3a..2ce6e6a9dd 100644 --- a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroups.test.tsx +++ b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroups.spec.tsx @@ -7,13 +7,14 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; -import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { MemoryRouter, Route, Routes, useParams } from 'react-router-dom'; import { store } from 'state/store'; import { StaticMockLink } from 'utils/StaticMockLink'; import i18n from 'utils/i18nForTest'; import VolunteerGroups from './VolunteerGroups'; import type { ApolloLink } from '@apollo/client'; import { MOCKS, MOCKS_EMPTY, MOCKS_ERROR } from './VolunteerGroups.mocks'; +import { vi } from 'vitest'; const link1 = new StaticMockLink(MOCKS); const link2 = new StaticMockLink(MOCKS_ERROR); @@ -61,19 +62,30 @@ const renderVolunteerGroups = (link: ApolloLink): RenderResult => { ); }; +/** Mock useParams to provide consistent test data */ + describe('Testing VolunteerGroups Screen', () => { beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId', eventId: 'eventId' }), - })); + vi.mock('react-router-dom', async () => { + const actualDom = await vi.importActual('react-router-dom'); + return { + ...actualDom, + useParams: vi.fn(), + }; + }); }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); + const mockRouteParams = (orgId = 'orgId', eventId = 'eventId'): void => { + vi.mocked(useParams).mockReturnValue({ orgId, eventId }); + }; + it('should redirect to fallback URL if URL params are undefined', async () => { + /** Mocking the useParams hook to return undefined parameters */ + mockRouteParams('', ''); render( @@ -98,12 +110,14 @@ describe('Testing VolunteerGroups Screen', () => { }); it('should render Groups screen', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const searchInput = await screen.findByTestId('searchBy'); expect(searchInput).toBeInTheDocument(); }); it('Check Sorting Functionality', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const searchInput = await screen.findByTestId('searchBy'); expect(searchInput).toBeInTheDocument(); @@ -133,6 +147,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('Search by Groups', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const searchInput = await screen.findByTestId('searchBy'); expect(searchInput).toBeInTheDocument(); @@ -153,6 +168,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('Search by Leader', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const searchInput = await screen.findByTestId('searchBy'); expect(searchInput).toBeInTheDocument(); @@ -174,6 +190,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('should render screen with No Groups', async () => { + mockRouteParams(); renderVolunteerGroups(link3); await waitFor(() => { @@ -183,6 +200,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('Error while fetching groups data', async () => { + mockRouteParams(); renderVolunteerGroups(link2); await waitFor(() => { @@ -191,6 +209,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('Open and close ViewModal', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const viewGroupBtn = await screen.findAllByTestId('viewGroupBtn'); @@ -201,6 +220,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('Open and Close Delete Modal', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const deleteGroupBtn = await screen.findAllByTestId('deleteGroupBtn'); @@ -211,6 +231,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('Open and close GroupModal (Edit)', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const editGroupBtn = await screen.findAllByTestId('editGroupBtn'); @@ -221,6 +242,7 @@ describe('Testing VolunteerGroups Screen', () => { }); it('Open and close GroupModal (Create)', async () => { + mockRouteParams(); renderVolunteerGroups(link1); const createGroupBtn = await screen.findByTestId('createGroupBtn'); From 3d6b6bc778b51f182cb56abba6179fe850d0d38f Mon Sep 17 00:00:00 2001 From: Gurram Karthik <167804249+gurramkarthiknetha@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:10:49 +0530 Subject: [PATCH 37/57] fixed #2505 :Refactor CSS Structure for EventVolunteers: Improved Maintainability and UI Consistency (#2675) * fixed-2505 * changed acc to bot * changed acc to coderabbin * removed due to conflicts * final change * resloved conflict * changed some file to improve the codecov * improved time * deleted file * changed some path * changed acc to code rabbin * updated * changed acc to code rabbin * fixed-time * decreased the time --- .../OrganizationScreen.test.tsx | 1 + .../EventVolunteers.module.css | 266 ------------------ .../EventVolunteers/Requests/Requests.tsx | 22 +- .../EventVolunteers/VolunteerContainer.tsx | 2 +- .../VolunteerGroupDeleteModal.tsx | 2 +- .../VolunteerGroups/VolunteerGroupModal.tsx | 2 +- .../VolunteerGroupViewModal.tsx | 10 +- .../VolunteerGroups/VolunteerGroups.tsx | 26 +- .../Volunteers/VolunteerCreateModal.tsx | 2 +- .../Volunteers/VolunteerDeleteModal.tsx | 2 +- .../Volunteers/VolunteerViewModal.tsx | 6 +- .../EventVolunteers/Volunteers/Volunteers.tsx | 26 +- .../Volunteer/Groups/GroupModal.tsx | 2 +- .../UserPortal/Volunteer/Groups/Groups.tsx | 2 +- src/setupTests.ts | 2 +- src/style/app.module.css | 182 ++++++++++++ 16 files changed, 236 insertions(+), 319 deletions(-) delete mode 100644 src/screens/EventVolunteers/EventVolunteers.module.css diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.test.tsx index 9bb95bbd4a..d1c4c81c27 100644 --- a/src/components/OrganizationScreen/OrganizationScreen.test.tsx +++ b/src/components/OrganizationScreen/OrganizationScreen.test.tsx @@ -85,6 +85,7 @@ describe('Testing OrganizationScreen', () => { const openButton = screen.getByTestId('openMenu'); fireEvent.click(openButton); + // Check for expand class after opening expect(screen.getByTestId('mainpageright')).toHaveClass(styles.contract); }); diff --git a/src/screens/EventVolunteers/EventVolunteers.module.css b/src/screens/EventVolunteers/EventVolunteers.module.css deleted file mode 100644 index 84b19f0a9f..0000000000 --- a/src/screens/EventVolunteers/EventVolunteers.module.css +++ /dev/null @@ -1,266 +0,0 @@ -/* Toggle Btn */ -.toggleGroup { - width: 50%; - min-width: 20rem; - margin: 0.5rem 0rem; -} - -.toggleBtn { - padding: 0rem; - height: 2rem; - display: flex; - justify-content: center; - align-items: center; -} - -.toggleBtn:hover { - color: #31bb6b !important; -} - -input[type='radio']:checked + label { - background-color: #31bb6a50 !important; -} - -input[type='radio']:checked + label:hover { - color: black !important; -} - -.actionItemsContainer { - height: 90vh; -} - -.actionItemModal { - max-width: 80vw; - margin-top: 2vh; - margin-left: 13vw; -} - -.datediv { - display: flex; - flex-direction: row; -} - -.datebox { - width: 90%; - border-radius: 7px; - outline: none; - box-shadow: none; - padding-top: 2px; - padding-bottom: 2px; - padding-right: 5px; - padding-left: 5px; - margin-right: 5px; - margin-left: 5px; -} - -.dropdownToggle { - margin-bottom: 0; - display: flex; -} - -.dropdownModalToggle { - width: 50%; -} - -.errorIcon { - transform: scale(1.5); - color: var(--bs-danger); - margin-bottom: 1rem; -} - -.greenregbtn { - margin: 1rem 0 0; - margin-top: 15px; - border: 1px solid var(--bs-gray-300); - box-shadow: 0 2px 2px var(--bs-gray-300); - padding: 10px 10px; - border-radius: 5px; - background-color: var(--bs-primary); - width: 100%; - font-size: 16px; - color: var(--bs-white); - outline: none; - font-weight: 600; - cursor: pointer; - transition: - transform 0.2s, - box-shadow 0.2s; - width: 100%; -} - -hr { - border: none; - height: 1px; - background-color: var(--bs-gray-500); - margin: 1rem; -} - -.iconContainer { - display: flex; - justify-content: flex-end; -} -.icon { - margin: 1px; -} - -.message { - margin-top: 25%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.preview { - display: flex; - flex-direction: row; - font-weight: 900; - font-size: 16px; - color: rgb(80, 80, 80); -} - -.removeFilterIcon { - cursor: pointer; -} - -.searchForm { - display: inline; -} - -.view { - margin-left: 2%; - font-weight: 600; - font-size: 16px; - color: var(--bs-gray-600); -} - -/* header (search, filter, dropdown) */ -.btnsContainer { - display: flex; - margin: 0.5rem 0 1.5rem 0; -} - -.btnsContainer .input { - flex: 1; - min-width: 18rem; - position: relative; -} - -.btnsContainer input { - outline: 1px solid var(--bs-gray-400); -} - -.btnsContainer .input button { - width: 52px; -} - -.noOutline input { - outline: none; -} - -.noOutline input:disabled { - -webkit-text-fill-color: black !important; -} - -.noOutline textarea:disabled { - -webkit-text-fill-color: black !important; -} - -.inputField { - margin-top: 10px; - margin-bottom: 10px; - background-color: white; - box-shadow: 0 1px 1px #31bb6b; -} - -.inputField > button { - padding-top: 10px; - padding-bottom: 10px; -} - -.dropdown { - background-color: white; - border: 1px solid #31bb6b; - position: relative; - display: inline-block; - color: #31bb6b; -} - -/* Action Items Data Grid */ -.rowBackground { - background-color: var(--bs-white); - max-height: 120px; -} - -.tableHeader { - background-color: var(--bs-primary); - color: var(--bs-white); - font-size: 1rem; -} - -.chipIcon { - height: 0.9rem !important; -} - -.chip { - height: 1.5rem !important; -} - -.active { - background-color: #31bb6a50 !important; -} - -.pending { - background-color: #ffd76950 !important; - color: #bb952bd0 !important; - border-color: #bb952bd0 !important; -} - -/* Modals */ -.itemModal { - max-width: 80vw; - margin-top: 2vh; - margin-left: 13vw; -} - -.titlemodal { - color: #707070; - font-weight: 600; - font-size: 32px; - width: 65%; - margin-bottom: 0px; -} - -.modalCloseBtn { - width: 40px; - height: 40px; - padding: 1rem; - display: flex; - justify-content: center; - align-items: center; -} - -.imageContainer { - display: flex; - align-items: center; - justify-content: center; - margin-right: 0.5rem; -} - -.TableImage { - object-fit: cover; - width: 25px !important; - height: 25px !important; - border-radius: 100% !important; -} - -.avatarContainer { - width: 28px; - height: 26px; -} - -/* Modal Table (Groups & Assignments) */ -.modalTable { - max-height: 220px; - overflow-y: auto; -} diff --git a/src/screens/EventVolunteers/Requests/Requests.tsx b/src/screens/EventVolunteers/Requests/Requests.tsx index 41abcad763..b19be3d2a0 100644 --- a/src/screens/EventVolunteers/Requests/Requests.tsx +++ b/src/screens/EventVolunteers/Requests/Requests.tsx @@ -13,7 +13,7 @@ import { type GridColDef, } from '@mui/x-data-grid'; import Avatar from 'components/Avatar/Avatar'; -import styles from '../EventVolunteers.module.css'; +import styles from '../../../style/app.module.css'; import { USER_VOLUNTEER_MEMBERSHIP } from 'GraphQl/Queries/EventVolunteerQueries'; import type { InterfaceVolunteerMembership } from 'utils/interfaces'; import dayjs from 'dayjs'; @@ -154,7 +154,7 @@ function requests(): JSX.Element { align: 'center', headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { return params.row.id; }, @@ -167,7 +167,7 @@ function requests(): JSX.Element { minWidth: 100, headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { const { firstName, lastName, image } = params.row.volunteer.user; return ( @@ -180,14 +180,14 @@ function requests(): JSX.Element { src={image} alt="volunteer" data-testid={`volunteer_image`} - className={styles.TableImage} + className={styles.TableImages} /> ) : (
@@ -205,7 +205,7 @@ function requests(): JSX.Element { minWidth: 150, align: 'center', headerAlign: 'center', - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, sortable: false, renderCell: (params: GridCellParams) => { return dayjs(params.row.createdAt).format('DD/MM/YYYY'); @@ -219,7 +219,7 @@ function requests(): JSX.Element { minWidth: 100, headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { return ( <> @@ -251,7 +251,7 @@ function requests(): JSX.Element { return (
{/* Header with search, filter and Create Button */} -
+
{ setSearchValue(e.target.value); @@ -282,7 +282,7 @@ function requests(): JSX.Element { @@ -316,7 +316,7 @@ function requests(): JSX.Element { hideFooter={true} getRowId={(row) => row._id} sx={dataGridStyle} - getRowClassName={() => `${styles.rowBackground}`} + getRowClassName={() => `${styles.rowBackgrounds}`} autoHeight rowHeight={65} rows={requests.map((request, index) => ({ diff --git a/src/screens/EventVolunteers/VolunteerContainer.tsx b/src/screens/EventVolunteers/VolunteerContainer.tsx index 1a425a706e..e026c6f7c8 100644 --- a/src/screens/EventVolunteers/VolunteerContainer.tsx +++ b/src/screens/EventVolunteers/VolunteerContainer.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Navigate, useParams } from 'react-router-dom'; -import styles from './EventVolunteers.module.css'; +import styles from '../../style/app.module.css'; import { HiUserGroup, HiUser } from 'react-icons/hi2'; import Volunteers from './Volunteers/Volunteers'; import VolunteerGroups from './VolunteerGroups/VolunteerGroups'; diff --git a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.tsx b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.tsx index 33132bfd33..89c788e220 100644 --- a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.tsx +++ b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupDeleteModal.tsx @@ -1,5 +1,5 @@ import { Button, Modal } from 'react-bootstrap'; -import styles from '../EventVolunteers.module.css'; +import styles from '../../../style/app.module.css'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { useMutation } from '@apollo/client'; diff --git a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.tsx b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.tsx index 5bfb1eff2b..e36ecaa0bd 100644 --- a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.tsx +++ b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupModal.tsx @@ -5,7 +5,7 @@ import type { InterfaceUserInfo, InterfaceVolunteerGroupInfo, } from 'utils/interfaces'; -import styles from '../EventVolunteers.module.css'; +import styles from '../../../style/app.module.css'; import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery } from '@apollo/client'; diff --git a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.tsx b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.tsx index 70994bd4e5..5fb090649f 100644 --- a/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.tsx +++ b/src/screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal.tsx @@ -1,6 +1,6 @@ import { Button, Form, Modal } from 'react-bootstrap'; import type { InterfaceVolunteerGroupInfo } from 'utils/interfaces'; -import styles from '../EventVolunteers.module.css'; +import styles from '../../../style/app.module.css'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -123,14 +123,14 @@ const VolunteerGroupViewModal: React.FC = ({ src={leader.image} alt="Volunteer" data-testid="leader_image" - className={styles.TableImage} + className={styles.TableImages} /> ) : (
= ({ src={creator.image} alt="Volunteer" data-testid="creator_image" - className={styles.TableImage} + className={styles.TableImages} /> ) : (
{ return (
{ const { _id, firstName, lastName, image } = params.row.leader; return ( @@ -194,14 +194,14 @@ function volunteerGroups(): JSX.Element { src={image} alt="Assignee" data-testid={`image${_id + 1}`} - className={styles.TableImage} + className={styles.TableImages} /> ) : (
@@ -219,7 +219,7 @@ function volunteerGroups(): JSX.Element { align: 'center', headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { return (
@@ -235,7 +235,7 @@ function volunteerGroups(): JSX.Element { align: 'center', headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { return (
@@ -252,7 +252,7 @@ function volunteerGroups(): JSX.Element { minWidth: 100, headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { return ( <> @@ -293,7 +293,7 @@ function volunteerGroups(): JSX.Element { return (
{/* Header with search, filter and Create Button */} -
+
{ setSearchValue(e.target.value); @@ -325,7 +325,7 @@ function volunteerGroups(): JSX.Element { @@ -350,7 +350,7 @@ function volunteerGroups(): JSX.Element { @@ -400,7 +400,7 @@ function volunteerGroups(): JSX.Element { ), }} sx={dataGridStyle} - getRowClassName={() => `${styles.rowBackground}`} + getRowClassName={() => `${styles.rowBackgrounds}`} autoHeight rowHeight={65} rows={groups.map((group, index) => ({ diff --git a/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.tsx b/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.tsx index 6b4a1e3f0c..dee45376db 100644 --- a/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.tsx +++ b/src/screens/EventVolunteers/Volunteers/VolunteerCreateModal.tsx @@ -1,7 +1,7 @@ import type { ChangeEvent } from 'react'; import { Button, Form, Modal } from 'react-bootstrap'; import type { InterfaceUserInfo } from 'utils/interfaces'; -import styles from '../EventVolunteers.module.css'; +import styles from '../../../style/app.module.css'; import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery } from '@apollo/client'; diff --git a/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.tsx b/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.tsx index 8f253fdf50..5c841a2f11 100644 --- a/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.tsx +++ b/src/screens/EventVolunteers/Volunteers/VolunteerDeleteModal.tsx @@ -1,5 +1,5 @@ import { Button, Modal } from 'react-bootstrap'; -import styles from '../EventVolunteers.module.css'; +import styles from '../../../style/app.module.css'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { useMutation } from '@apollo/client'; diff --git a/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.tsx b/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.tsx index 0904d34b9c..830bacf8cc 100644 --- a/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.tsx +++ b/src/screens/EventVolunteers/Volunteers/VolunteerViewModal.tsx @@ -1,6 +1,6 @@ import { Button, Form, Modal } from 'react-bootstrap'; import type { InterfaceEventVolunteerInfo } from 'utils/interfaces'; -import styles from '../EventVolunteers.module.css'; +import styles from '../../../style/app.module.css'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -83,14 +83,14 @@ const VolunteerViewModal: React.FC = ({ src={user.image} alt="Volunteer" data-testid="volunteer_image" - className={styles.TableImage} + className={styles.TableImages} /> ) : (
{ const { _id, firstName, lastName, image } = params.row.user; return ( @@ -192,7 +192,7 @@ function volunteers(): JSX.Element { src={image} alt="volunteer" data-testid="volunteer_image" - className={styles.TableImage} + className={styles.TableImages} /> ) : (
@@ -200,7 +200,7 @@ function volunteers(): JSX.Element { key={_id + '1'} dataTestId="volunteer_avatar" containerStyle={styles.imageContainer} - avatarStyle={styles.TableImage} + avatarStyle={styles.TableImages} name={firstName + ' ' + lastName} alt={firstName + ' ' + lastName} /> @@ -219,7 +219,7 @@ function volunteers(): JSX.Element { minWidth: 100, headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { return ( { return (
{ return ( @@ -278,7 +278,7 @@ function volunteers(): JSX.Element { minWidth: 100, headerAlign: 'center', sortable: false, - headerClassName: `${styles.tableHeader}`, + headerClassName: `${styles.tableHeaders}`, renderCell: (params: GridCellParams) => { return ( <> @@ -310,7 +310,7 @@ function volunteers(): JSX.Element { return (
{/* Header with search, filter and Create Button */} -
+
{ setSearchValue(e.target.value); @@ -341,7 +341,7 @@ function volunteers(): JSX.Element { @@ -365,7 +365,7 @@ function volunteers(): JSX.Element { @@ -421,7 +421,7 @@ function volunteers(): JSX.Element { ), }} sx={dataGridStyle} - getRowClassName={() => `${styles.rowBackground}`} + getRowClassName={() => `${styles.rowBackgrounds}`} autoHeight rowHeight={65} rows={volunteers.map((volunteer, index) => ({ diff --git a/src/screens/UserPortal/Volunteer/Groups/GroupModal.tsx b/src/screens/UserPortal/Volunteer/Groups/GroupModal.tsx index 4ae162cd70..27f461b462 100644 --- a/src/screens/UserPortal/Volunteer/Groups/GroupModal.tsx +++ b/src/screens/UserPortal/Volunteer/Groups/GroupModal.tsx @@ -5,7 +5,7 @@ import type { InterfaceVolunteerGroupInfo, InterfaceVolunteerMembership, } from 'utils/interfaces'; -import styles from 'screens/EventVolunteers/EventVolunteers.module.css'; +import styles from 'style/app.module.css'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery } from '@apollo/client'; diff --git a/src/screens/UserPortal/Volunteer/Groups/Groups.tsx b/src/screens/UserPortal/Volunteer/Groups/Groups.tsx index 3941f461d5..160dc0b23a 100644 --- a/src/screens/UserPortal/Volunteer/Groups/Groups.tsx +++ b/src/screens/UserPortal/Volunteer/Groups/Groups.tsx @@ -16,7 +16,7 @@ import { } from '@mui/x-data-grid'; import { debounce, Stack } from '@mui/material'; import Avatar from 'components/Avatar/Avatar'; -import styles from 'screens/EventVolunteers/EventVolunteers.module.css'; +import styles from '../../../../style/app.module.css'; import { EVENT_VOLUNTEER_GROUP_LIST } from 'GraphQl/Queries/EventVolunteerQueries'; import VolunteerGroupViewModal from 'screens/EventVolunteers/VolunteerGroups/VolunteerGroupViewModal'; import useLocalStorage from 'utils/useLocalstorage'; diff --git a/src/setupTests.ts b/src/setupTests.ts index eac7093309..f0b48b39d0 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -32,4 +32,4 @@ jestPreviewConfigure({ autoPreview: true, }); -jest.setTimeout(15000); +jest.setTimeout(17000); diff --git a/src/style/app.module.css b/src/style/app.module.css index d21c2bb845..365b00556b 100644 --- a/src/style/app.module.css +++ b/src/style/app.module.css @@ -868,3 +868,185 @@ hr { max-height: calc(100vh - 18rem); overflow: auto; } + +#individualRadio, +#requestsRadio, +#groupsRadio, +.toggleBtn:hover { + color: var(--brand-primary) !important; +} + +input[type='radio']:checked + label { + background-color: var(--brand-primary-light) !important; +} + +.dropdownToggle { + margin-bottom: 0; + display: flex; +} + +.dropdownModalToggle { + width: 50%; +} + +.greenregbtn { + margin-top: 1rem; + border: 1px solid var(--bs-gray-300); + box-shadow: 0 2px 2px var(--bs-gray-300); + padding: 10px 10px; + border-radius: 5px; + background-color: var(--bs-primary); + width: 100%; + font-size: 16px; + color: var(--bs-white); + outline: none; + font-weight: 600; + cursor: pointer; + transition: + transform 0.2s, + box-shadow 0.2s; +} + +.removeFilterIcon { + cursor: pointer; +} + +.searchForm { + display: inline; +} + +.view { + margin-left: 2%; + font-weight: 600; + font-size: 16px; + color: var(--bs-gray-600); +} + +/* header (search, filter, dropdown) */ +.btncon .btnsContainer { + display: flex; + margin: 0.5rem 0 1.5rem 0; +} + +.btncon .btnsContainer .input { + flex: 1; + min-width: 18rem; + position: relative; +} + +.btncon .btnsContainer input { + outline: 1px solid var(--bs-gray-400); +} + +.btncon .btnsContainer .input button { + width: 52px; +} + +.noOutline input:disabled { + -webkit-text-fill-color: black !important; +} + +.noOutline textarea:disabled { + color: var(--bs-black) !important; + opacity: 1; +} + +.inputField { + margin-top: 10px; + margin-bottom: 10px; + background-color: white; + box-shadow: 0 1px 1px var(--brand-primary); +} + +.inputField > button { + padding-top: 10px; + padding-bottom: 10px; +} + +.dropdowns { + background-color: white; + border: 1px solid #31bb6b; + position: relative; + display: inline-block; + color: #31bb6b; +} + +/* Action Items Data Grid */ +.rowBackgrounds { + background-color: var(--bs-white); + max-height: 120px; +} + +.tableHeaders { + background-color: var(--bs-primary); + color: var(--bs-white); + font-size: 1rem; +} + +.chipIcon { + height: 0.9rem !important; +} + +.chip { + height: 1.5rem !important; +} + +.active { + background-color: var(--status-active-bg); +} + +.pending { + background-color: var(--status-pending-bg); + color: var(--status-pending-text); + border-color: var(--status-pending-border); +} + +/* Modals */ +.itemModal { + max-width: 80vw; + margin-top: 2vh; + margin-left: 13vw; +} + +.titlemodal { + color: #707070; + font-weight: 600; + font-size: 32px; + width: 65%; + margin-bottom: 0px; +} + +.modalCloseBtn { + width: 40px; + height: 40px; + padding: 1rem; + display: flex; + justify-content: center; + align-items: center; +} + +.imageContainer { + display: flex; + align-items: center; + justify-content: center; + margin-right: 0.5rem; +} + +.TableImages { + object-fit: cover; + width: var(--image-width, 100%); + height: var(--image-height, auto); + border-radius: 0; + margin-right: var(--image-spacing, 8px); +} + +.avatarContainer { + width: 28px; + height: 26px; +} + +/* Modal Table (Groups & Assignments) */ +.modalTable { + max-height: 220px; + overflow-y: auto; +} From 139e9854a3b37079fa09d8e6a4cec53cc6883e4c Mon Sep 17 00:00:00 2001 From: Abhishek Raj <113784630+abbi4code@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:15:55 +0530 Subject: [PATCH 38/57] chore: migrated checkinwrapper tests jest to vitest (#2709) --- ...InWrapper.test.tsx => CheckInWrapper.spec.tsx} | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) rename src/components/CheckIn/{CheckInWrapper.test.tsx => CheckInWrapper.spec.tsx} (76%) diff --git a/src/components/CheckIn/CheckInWrapper.test.tsx b/src/components/CheckIn/CheckInWrapper.spec.tsx similarity index 76% rename from src/components/CheckIn/CheckInWrapper.test.tsx rename to src/components/CheckIn/CheckInWrapper.spec.tsx index 81f53d0043..06b2695f88 100644 --- a/src/components/CheckIn/CheckInWrapper.test.tsx +++ b/src/components/CheckIn/CheckInWrapper.spec.tsx @@ -13,6 +13,19 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { checkInQueryMock } from './mocks'; import { StaticMockLink } from 'utils/StaticMockLink'; +/** + * This file contains unit tests for the CheckInWrapper component. + * + * The tests cover: + * - Rendering and behavior of the modal component. + * - Functionality of the button to open and close the modal. + * - Integration with mocked GraphQL queries for testing Apollo Client. + * + * Purpose: + * These tests ensure that the CheckInWrapper component behaves as expected + * when opening and closing modals, and correctly integrates with its dependencies. + */ + const link = new StaticMockLink(checkInQueryMock, true); describe('Testing CheckIn Wrapper', () => { @@ -20,7 +33,7 @@ describe('Testing CheckIn Wrapper', () => { eventId: 'event123', }; - test('The button to open and close the modal should work properly', async () => { + it('The button to open and close the modal should work properly', async () => { render( From 685a12c3566e4d32f02da96a7e8a65bdaee2bed9 Mon Sep 17 00:00:00 2001 From: Mayank Jha <132004139+MayankJha014@users.noreply.github.com> Date: Mon, 23 Dec 2024 03:46:05 +0530 Subject: [PATCH 39/57] manage tag css to app.module (#2722) * manage tag css to app.module * coderaddit ai chnages --- src/screens/ManageTag/ManageTag.module.css | 127 --------------------- src/style/app.module.css | 40 ++++--- 2 files changed, 23 insertions(+), 144 deletions(-) delete mode 100644 src/screens/ManageTag/ManageTag.module.css diff --git a/src/screens/ManageTag/ManageTag.module.css b/src/screens/ManageTag/ManageTag.module.css deleted file mode 100644 index deecd4a9b7..0000000000 --- a/src/screens/ManageTag/ManageTag.module.css +++ /dev/null @@ -1,127 +0,0 @@ -.btnsContainer { - display: flex; - margin: 2rem 0; -} - -.btnsContainer .btnsBlock { - display: flex; - width: max-content; -} - -.btnsContainer .btnsBlock button { - margin-left: 1rem; - display: flex; - justify-content: center; - align-items: center; -} - -.btnsContainer .input { - flex: 1; - position: relative; - max-width: 60%; - justify-content: space-between; -} - -.btnsContainer input { - outline: 1px solid var(--bs-gray-400); -} - -.btnsContainer .input button { - width: 52px; -} - -@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; - } -} - -/* 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%; - } -} - -.errorContainer { - min-height: 100vh; -} - -.errorMessage { - margin-top: 25%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.errorIcon { - transform: scale(1.5); - color: var(--bs-danger); - margin-bottom: 1rem; -} - -.tableHeader { - background-color: var(--bs-primary); - color: var(--bs-white); - font-size: 1rem; -} - -.rowBackground { - background-color: var(--bs-white); - max-height: 120px; -} - -.tagsBreadCrumbs { - color: var(--bs-gray); - cursor: pointer; -} - -.tagsBreadCrumbs:hover { - color: var(--bs-blue); - font-weight: 600; - text-decoration: underline; -} - -.manageTagScrollableDiv { - scrollbar-width: thin; - scrollbar-color: var(--bs-gray-400) var(--bs-white); - - max-height: calc(100vh - 18rem); - overflow: auto; -} diff --git a/src/style/app.module.css b/src/style/app.module.css index 365b00556b..56dc7e8854 100644 --- a/src/style/app.module.css +++ b/src/style/app.module.css @@ -218,6 +218,7 @@ .btnsContainer .btnsBlock { display: flex; + width: max-content; } .btnsContainer .btnsBlock button { @@ -230,6 +231,12 @@ .btnsContainer .input { flex: 1; position: relative; + max-width: 60%; + justify-content: space-between; +} + +.btnsContainer input { + outline: 1px solid var(--bs-gray-400); } .btnsContainer .input button { @@ -363,6 +370,8 @@ .rowBackground { background-color: var(--row-background); + max-height: 120px; + overflow-y: auto; } .tableHeader { @@ -430,6 +439,20 @@ cursor: pointer; } +.manageTagScrollableDiv { + scrollbar-width: thin; + scrollbar-color: var(--bs-gray-400) var(--bs-white); + + max-height: calc(100vh - 18rem); + overflow: auto; +} + +.tagsBreadCrumbs:hover { + color: var(--bs-blue); + font-weight: 600; + text-decoration: underline; +} + .orgUserTagsScrollableDiv { scrollbar-width: auto; scrollbar-color: var(--bs-gray-400) var(--bs-white); @@ -727,11 +750,6 @@ hr { } } -.btnsContainer .btnsBlock { - display: flex; - width: max-content; -} - .btnsContainer .btnsBlock button { margin-left: 1rem; display: flex; @@ -739,13 +757,6 @@ hr { align-items: center; } -.btnsContainer .input { - flex: 1; - position: relative; - max-width: 60%; - justify-content: space-between; -} - .btnsContainer input { outline: 1px solid var(--bs-gray-400); } @@ -815,11 +826,6 @@ hr { color: var(--bs-white); font-size: 1rem; } -.rowBackground { - background-color: var(--bs-white); - max-height: 120px; - overflow-y: auto; -} .subTagsLink { color: var(--bs-blue); From e31ccf655d122726fef4d87a29f9dcf85d966763 Mon Sep 17 00:00:00 2001 From: Syed Ali Ul Hasan Date: Mon, 23 Dec 2024 03:55:50 +0530 Subject: [PATCH 40/57] refactored src/screens/UserPortal/Volunteer/Invitations from jest to vitest (#2723) --- ...itations.test.tsx => Invitations.spec.tsx} | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) rename src/screens/UserPortal/Volunteer/Invitations/{Invitations.test.tsx => Invitations.spec.tsx} (96%) diff --git a/src/screens/UserPortal/Volunteer/Invitations/Invitations.test.tsx b/src/screens/UserPortal/Volunteer/Invitations/Invitations.spec.tsx similarity index 96% rename from src/screens/UserPortal/Volunteer/Invitations/Invitations.test.tsx rename to src/screens/UserPortal/Volunteer/Invitations/Invitations.spec.tsx index 2c0cafc6a9..867f95c1aa 100644 --- a/src/screens/UserPortal/Volunteer/Invitations/Invitations.test.tsx +++ b/src/screens/UserPortal/Volunteer/Invitations/Invitations.spec.tsx @@ -21,14 +21,24 @@ import { } from './Invitations.mocks'; import { toast } from 'react-toastify'; import useLocalStorage from 'utils/useLocalstorage'; +import { vi, expect } from 'vitest'; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); +vi.mock('react-router-dom', async () => { + const actual = await vi.importActual('react-router-dom'); + return { + ...actual, + useParams: () => ({ orgId: 'orgId' }), + useNavigate: vi.fn(), + }; +}); + const { setItem } = useLocalStorage(); const link1 = new StaticMockLink(MOCKS); @@ -79,19 +89,12 @@ const renderInvitations = (link: ApolloLink): RenderResult => { }; describe('Testing Invvitations Screen', () => { - beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId' }), - })); - }); - beforeEach(() => { setItem('userId', 'userId'); }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('should redirect to fallback URL if URL params are undefined', async () => { From cda28369f4a71e8a7175b9bd4c7916251e647792 Mon Sep 17 00:00:00 2001 From: khushi santosh patil <143253539+khushipatil1523@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:02:55 +0530 Subject: [PATCH 41/57] convert from jest to vitest #2486 (#2705) * convert from jest to vitest * changes done * changes done * changes done --- ...ments.test.tsx => Advertisements.spec.tsx} | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) rename src/components/Advertisements/{Advertisements.test.tsx => Advertisements.spec.tsx} (96%) diff --git a/src/components/Advertisements/Advertisements.test.tsx b/src/components/Advertisements/Advertisements.spec.tsx similarity index 96% rename from src/components/Advertisements/Advertisements.test.tsx rename to src/components/Advertisements/Advertisements.spec.tsx index 88bbb1255c..1e2d92b392 100644 --- a/src/components/Advertisements/Advertisements.test.tsx +++ b/src/components/Advertisements/Advertisements.spec.tsx @@ -1,4 +1,6 @@ import React, { act } from 'react'; +import { describe, test, expect, vi } from 'vitest'; + import { ApolloClient, ApolloLink, @@ -7,25 +9,28 @@ import { InMemoryCache, } from '@apollo/client'; import { MockedProvider } from '@apollo/client/testing'; + import { fireEvent, render, screen } from '@testing-library/react'; -import 'jest-location-mock'; import type { DocumentNode, NormalizedCacheObject } from '@apollo/client'; import userEvent from '@testing-library/user-event'; import { BACKEND_URL } from 'Constant/constant'; -import { ADD_ADVERTISEMENT_MUTATION } from 'GraphQl/Mutations/mutations'; + +import { ADD_ADVERTISEMENT_MUTATION } from '../../GraphQl/Mutations/mutations'; import { ORGANIZATIONS_LIST, ORGANIZATION_ADVERTISEMENT_LIST, PLUGIN_GET, -} from 'GraphQl/Queries/Queries'; +} from '../../GraphQl/Queries/Queries'; + import { I18nextProvider } from 'react-i18next'; + import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { ToastContainer } from 'react-toastify'; -import { store } from 'state/store'; -import i18nForTest from 'utils/i18nForTest'; -import useLocalStorage from 'utils/useLocalstorage'; +import { store } from '../../state/store'; +import i18nForTest from '../../utils/i18nForTest'; +import useLocalStorage from '../../utils/useLocalstorage'; import Advertisement from './Advertisements'; const { getItem } = useLocalStorage(); @@ -50,18 +55,22 @@ const client: ApolloClient = new ApolloClient({ link: ApolloLink.from([httpLink]), }); -jest.mock('components/AddOn/support/services/Plugin.helper', () => ({ +vi.mock('components/AddOn/support/services/Plugin.helper', () => ({ __esModule: true, - default: jest.fn().mockImplementation(() => ({ - fetchInstalled: jest.fn().mockResolvedValue([]), - fetchStore: jest.fn().mockResolvedValue([]), + default: vi.fn().mockImplementation(() => ({ + fetchInstalled: vi.fn().mockResolvedValue([]), + fetchStore: vi.fn().mockResolvedValue([]), })), })); let mockID: string | undefined = '1'; -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: mockID }), -})); + +vi.mock('react-router-dom', async () => { + const actual = await vi.importActual('react-router-dom'); + return { + ...actual, + useParams: () => ({ orgId: mockID }), + }; +}); const today = new Date(); const tomorrow = today; @@ -466,18 +475,16 @@ describe('Testing Advertisement Component', () => { /\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/, ); let dateObject = new Date(); - if (dateMatch) { const monthName = dateMatch[1]; const day = parseInt(dateMatch[2], 10); const year = parseInt(dateMatch[3], 10); - const monthIndex = 'JanFebMarAprMayJunJulAugSepOctNovDec'.indexOf(monthName) / 3; dateObject = new Date(year, monthIndex, day); } - + console.log(dateObject); expect(dateObject.getTime()).toBeLessThan(new Date().getTime()); }); From 986bef80972fef714745a932d77f890cf3d1dbf7 Mon Sep 17 00:00:00 2001 From: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Date: Sun, 22 Dec 2024 14:33:24 -0800 Subject: [PATCH 42/57] Revert "convert from jest to vitest #2486 (#2705)" (#2731) This reverts commit cda28369f4a71e8a7175b9bd4c7916251e647792. --- ...ments.spec.tsx => Advertisements.test.tsx} | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) rename src/components/Advertisements/{Advertisements.spec.tsx => Advertisements.test.tsx} (96%) diff --git a/src/components/Advertisements/Advertisements.spec.tsx b/src/components/Advertisements/Advertisements.test.tsx similarity index 96% rename from src/components/Advertisements/Advertisements.spec.tsx rename to src/components/Advertisements/Advertisements.test.tsx index 1e2d92b392..88bbb1255c 100644 --- a/src/components/Advertisements/Advertisements.spec.tsx +++ b/src/components/Advertisements/Advertisements.test.tsx @@ -1,6 +1,4 @@ import React, { act } from 'react'; -import { describe, test, expect, vi } from 'vitest'; - import { ApolloClient, ApolloLink, @@ -9,28 +7,25 @@ import { InMemoryCache, } from '@apollo/client'; import { MockedProvider } from '@apollo/client/testing'; - import { fireEvent, render, screen } from '@testing-library/react'; +import 'jest-location-mock'; import type { DocumentNode, NormalizedCacheObject } from '@apollo/client'; import userEvent from '@testing-library/user-event'; import { BACKEND_URL } from 'Constant/constant'; - -import { ADD_ADVERTISEMENT_MUTATION } from '../../GraphQl/Mutations/mutations'; +import { ADD_ADVERTISEMENT_MUTATION } from 'GraphQl/Mutations/mutations'; import { ORGANIZATIONS_LIST, ORGANIZATION_ADVERTISEMENT_LIST, PLUGIN_GET, -} from '../../GraphQl/Queries/Queries'; - +} from 'GraphQl/Queries/Queries'; import { I18nextProvider } from 'react-i18next'; - import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { ToastContainer } from 'react-toastify'; -import { store } from '../../state/store'; -import i18nForTest from '../../utils/i18nForTest'; -import useLocalStorage from '../../utils/useLocalstorage'; +import { store } from 'state/store'; +import i18nForTest from 'utils/i18nForTest'; +import useLocalStorage from 'utils/useLocalstorage'; import Advertisement from './Advertisements'; const { getItem } = useLocalStorage(); @@ -55,22 +50,18 @@ const client: ApolloClient = new ApolloClient({ link: ApolloLink.from([httpLink]), }); -vi.mock('components/AddOn/support/services/Plugin.helper', () => ({ +jest.mock('components/AddOn/support/services/Plugin.helper', () => ({ __esModule: true, - default: vi.fn().mockImplementation(() => ({ - fetchInstalled: vi.fn().mockResolvedValue([]), - fetchStore: vi.fn().mockResolvedValue([]), + default: jest.fn().mockImplementation(() => ({ + fetchInstalled: jest.fn().mockResolvedValue([]), + fetchStore: jest.fn().mockResolvedValue([]), })), })); let mockID: string | undefined = '1'; - -vi.mock('react-router-dom', async () => { - const actual = await vi.importActual('react-router-dom'); - return { - ...actual, - useParams: () => ({ orgId: mockID }), - }; -}); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: mockID }), +})); const today = new Date(); const tomorrow = today; @@ -475,16 +466,18 @@ describe('Testing Advertisement Component', () => { /\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/, ); let dateObject = new Date(); + if (dateMatch) { const monthName = dateMatch[1]; const day = parseInt(dateMatch[2], 10); const year = parseInt(dateMatch[3], 10); + const monthIndex = 'JanFebMarAprMayJunJulAugSepOctNovDec'.indexOf(monthName) / 3; dateObject = new Date(year, monthIndex, day); } - console.log(dateObject); + expect(dateObject.getTime()).toBeLessThan(new Date().getTime()); }); From 3c6f6dae1f1aa00dc64a99331a167669b8a52375 Mon Sep 17 00:00:00 2001 From: IITI-tushar <165766280+IITI-tushar@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:05:16 +0530 Subject: [PATCH 43/57] Add AdvertisementEntry.spec.tsx for Vitest migration (#2680) * Add AdvertisementEntry.spec.tsx for Vitest migration * Update AdvertisementEntry.spec.tsx removes eslint-disable * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx * Update AdvertisementEntry.spec.tsx --- .../AdvertisementEntry.spec.tsx | 649 ++++++++++++++++++ 1 file changed, 649 insertions(+) create mode 100644 src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.spec.tsx diff --git a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.spec.tsx b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.spec.tsx new file mode 100644 index 0000000000..4d27df6e22 --- /dev/null +++ b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.spec.tsx @@ -0,0 +1,649 @@ +import React from 'react'; +import { render, fireEvent, waitFor, screen } from '@testing-library/react'; +import { + ApolloClient, + ApolloProvider, + InMemoryCache, + ApolloLink, + HttpLink, +} from '@apollo/client'; +import type { NormalizedCacheObject } from '@apollo/client'; +import { BrowserRouter } from 'react-router-dom'; +import AdvertisementEntry from './AdvertisementEntry'; +import AdvertisementRegister from '../AdvertisementRegister/AdvertisementRegister'; +import { Provider } from 'react-redux'; +import { store } from 'state/store'; +import { BACKEND_URL } from 'Constant/constant'; +import i18nForTest from 'utils/i18nForTest'; +import { I18nextProvider } from 'react-i18next'; +import dayjs from 'dayjs'; +import useLocalStorage from 'utils/useLocalstorage'; +import { MockedProvider } from '@apollo/client/testing'; +import { ORGANIZATION_ADVERTISEMENT_LIST } from 'GraphQl/Queries/OrganizationQueries'; +import { DELETE_ADVERTISEMENT_BY_ID } from 'GraphQl/Mutations/mutations'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import '@testing-library/jest-dom'; + +const { getItem } = useLocalStorage(); + +const httpLink = new HttpLink({ + uri: BACKEND_URL, + headers: { + authorization: 'Bearer ' + getItem('token') || '', + }, +}); +const translations = JSON.parse( + JSON.stringify( + i18nForTest.getDataByLanguage('en')?.translation?.advertisement ?? null, + ), +); + +const client: ApolloClient = new ApolloClient({ + cache: new InMemoryCache(), + link: ApolloLink.from([httpLink]), +}); + +const mockUseMutation = vi.fn(); +vi.mock('@apollo/client', async () => { + const actual = await vi.importActual('@apollo/client'); + return { + ...actual, + useMutation: () => mockUseMutation(), + }; +}); + +vi.mock('react-router-dom', async () => { + const actual = await vi.importActual('react-router-dom'); + return { + ...actual, + useParams: () => ({ orgId: '1' }), + }; +}); + +describe('Testing Advertisement Entry Component', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('Testing rendering and deleting of advertisement', async () => { + const deleteAdByIdMock = vi.fn(); + mockUseMutation.mockReturnValue([deleteAdByIdMock]); + const { getByTestId, getAllByText } = render( + + + + + + + + + , + ); + + //Testing rendering + expect(getByTestId('AdEntry')).toBeInTheDocument(); + expect(getAllByText('POPUP')[0]).toBeInTheDocument(); + expect(getAllByText('Advert1')[0]).toBeInTheDocument(); + expect(screen.getByTestId('media')).toBeInTheDocument(); + + //Testing successful deletion + fireEvent.click(getByTestId('moreiconbtn')); + fireEvent.click(getByTestId('deletebtn')); + + await waitFor(() => { + expect(screen.getByTestId('delete_title')).toBeInTheDocument(); + expect(screen.getByTestId('delete_body')).toBeInTheDocument(); + }); + + fireEvent.click(getByTestId('delete_yes')); + + await waitFor(() => { + expect(deleteAdByIdMock).toHaveBeenCalledWith({ + variables: { + id: '1', + }, + }); + const deletedMessage = screen.queryByText('Advertisement Deleted'); + expect(deletedMessage).toBeNull(); + }); + + //Testing unsuccessful deletion + deleteAdByIdMock.mockRejectedValueOnce(new Error('Deletion Failed')); + + fireEvent.click(getByTestId('moreiconbtn')); + + fireEvent.click(getByTestId('delete_yes')); + + await waitFor(() => { + expect(deleteAdByIdMock).toHaveBeenCalledWith({ + variables: { + id: '1', + }, + }); + const deletionFailedText = screen.queryByText((content, element) => { + return ( + element?.textContent === 'Deletion Failed' && + element.tagName.toLowerCase() === 'div' + ); + }); + expect(deletionFailedText).toBeNull(); + }); + }); + + it('should use default props when none are provided', () => { + render( + , + : void { + throw new Error('Function not implemented.'); + }} + />, + ); + + //Check if component renders with default ''(empty string) + const elements = screen.getAllByText(''); // This will return an array of matching elements + elements.forEach((element) => expect(element).toBeInTheDocument()); + + // Check that the component renders with default `mediaUrl` (empty string) + const mediaElement = screen.getByTestId('media'); + expect(mediaElement).toHaveAttribute('src', ''); + + // Check that the component renders with default `endDate` + const defaultEndDate = new Date().toDateString(); + expect(screen.getByText(`Ends on ${defaultEndDate}`)).toBeInTheDocument(); + + // Check that the component renders with default `startDate` + const defaultStartDate = new Date().toDateString(); + expect(screen.getByText(`Ends on ${defaultStartDate}`)).toBeInTheDocument(); //fix text "Ends on"? + }); + + it('should correctly override default props when values are provided', () => { + const mockName = 'Test Ad'; + const mockType = 'Banner'; + const mockMediaUrl = 'https://example.com/media.png'; + const mockEndDate = new Date(2025, 11, 31); + const mockStartDate = new Date(2024, 0, 1); + const mockOrganizationId = 'org123'; + + const { getByText } = render( + , + : void { + throw new Error('Function not implemented.'); + }} + />, + ); + + // Check that the component renders with provided values + expect(getByText(mockName)).toBeInTheDocument(); + expect(getByText(mockType)).toBeInTheDocument(); + expect(screen.getByTestId('media')).toHaveAttribute('src', mockMediaUrl); + expect( + getByText(`Ends on ${mockEndDate.toDateString()}`), + ).toBeInTheDocument(); + }); + + it('should open and close the dropdown when options button is clicked', () => { + const { getByTestId, queryByText, getAllByText } = render( + + + + + + + + + , + ); + + // Test initial rendering + expect(getByTestId('AdEntry')).toBeInTheDocument(); + expect(getAllByText('POPUP')[0]).toBeInTheDocument(); + expect(getAllByText('Advert1')[0]).toBeInTheDocument(); + + // Test dropdown functionality + const optionsButton = getByTestId('moreiconbtn'); + + // Initially, the dropdown should not be visible + expect(queryByText('Edit')).toBeNull(); + + // Click to open the dropdown + fireEvent.click(optionsButton); + + // After clicking the button, the dropdown should be visible + expect(queryByText('Edit')).toBeInTheDocument(); + + // Click again to close the dropdown + fireEvent.click(optionsButton); + + // After the second click, the dropdown should be hidden again + expect(queryByText('Edit')).toBeNull(); + }); + + it('Updates the advertisement and shows success toast on successful update', async () => { + const updateAdByIdMock = vi.fn().mockResolvedValue({ + data: { + updateAdvertisement: { + advertisement: { + _id: '1', + name: 'Updated Advertisement', + mediaUrl: '', + startDate: dayjs(new Date()).add(1, 'day').format('YYYY-MM-DD'), + endDate: dayjs(new Date()).add(2, 'days').format('YYYY-MM-DD'), + type: 'BANNER', + }, + }, + }, + }); + + mockUseMutation.mockReturnValue([updateAdByIdMock]); + + render( + + + + + + + + + , + ); + + const optionsButton = screen.getByTestId('moreiconbtn'); + fireEvent.click(optionsButton); + fireEvent.click(screen.getByTestId('editBtn')); + + fireEvent.change(screen.getByLabelText('Enter name of Advertisement'), { + target: { value: 'Updated Advertisement' }, + }); + + expect(screen.getByLabelText('Enter name of Advertisement')).toHaveValue( + 'Updated Advertisement', + ); + + fireEvent.change(screen.getByLabelText(translations.Rtype), { + target: { value: 'BANNER' }, + }); + expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER'); + + fireEvent.change(screen.getByLabelText(translations.RstartDate), { + target: { value: dayjs().add(1, 'day').format('YYYY-MM-DD') }, + }); + + fireEvent.change(screen.getByLabelText(translations.RendDate), { + target: { value: dayjs().add(2, 'days').format('YYYY-MM-DD') }, + }); + + fireEvent.click(screen.getByTestId('addonupdate')); + + expect(updateAdByIdMock).toHaveBeenCalledWith({ + variables: { + id: '1', + name: 'Updated Advertisement', + type: 'BANNER', + startDate: dayjs().add(1, 'day').format('YYYY-MM-DD'), + endDate: dayjs().add(2, 'days').format('YYYY-MM-DD'), + }, + }); + }); + + it('Simulating if the mutation doesnt have data variable while updating', async () => { + const updateAdByIdMock = vi.fn().mockResolvedValue({ + updateAdvertisement: { + _id: '1', + name: 'Updated Advertisement', + type: 'BANNER', + }, + }); + + mockUseMutation.mockReturnValue([updateAdByIdMock]); + + render( + + + + + + + + + , + ); + + const optionsButton = screen.getByTestId('moreiconbtn'); + fireEvent.click(optionsButton); + fireEvent.click(screen.getByTestId('editBtn')); + + fireEvent.change(screen.getByLabelText('Enter name of Advertisement'), { + target: { value: 'Updated Advertisement' }, + }); + + expect(screen.getByLabelText('Enter name of Advertisement')).toHaveValue( + 'Updated Advertisement', + ); + + fireEvent.change(screen.getByLabelText(translations.Rtype), { + target: { value: 'BANNER' }, + }); + expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER'); + + fireEvent.click(screen.getByTestId('addonupdate')); + + expect(updateAdByIdMock).toHaveBeenCalledWith({ + variables: { + id: '1', + name: 'Updated Advertisement', + type: 'BANNER', + }, + }); + }); + + it('Simulating if the mutation does not have data variable while registering', async () => { + vi.stubGlobal('location', { + reload: vi.fn(), + href: 'https://example.com/page/id=1', + }); + + const createAdByIdMock = vi.fn().mockResolvedValue({ + data1: { + createAdvertisement: { + _id: '1', + }, + }, + }); + + mockUseMutation.mockReturnValue([createAdByIdMock]); + + render( + + + + + { + + } + + + + , + ); + + fireEvent.click(screen.getByTestId('createAdvertisement')); + + fireEvent.change(screen.getByLabelText('Enter name of Advertisement'), { + target: { value: 'Updated Advertisement' }, + }); + + expect(screen.getByLabelText('Enter name of Advertisement')).toHaveValue( + 'Updated Advertisement', + ); + + fireEvent.change(screen.getByLabelText(translations.Rtype), { + target: { value: 'BANNER' }, + }); + expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER'); + + fireEvent.change(screen.getByLabelText(translations.RstartDate), { + target: { value: '2023-01-01' }, + }); + expect(screen.getByLabelText(translations.RstartDate)).toHaveValue( + '2023-01-01', + ); + + fireEvent.change(screen.getByLabelText(translations.RendDate), { + target: { value: '2023-02-01' }, + }); + expect(screen.getByLabelText(translations.RendDate)).toHaveValue( + '2023-02-01', + ); + + fireEvent.click(screen.getByTestId('addonregister')); + + expect(createAdByIdMock).toHaveBeenCalledWith({ + variables: { + organizationId: '1', + name: 'Updated Advertisement', + file: '', + type: 'BANNER', + startDate: dayjs(new Date('2023-01-01')).format('YYYY-MM-DD'), + endDate: dayjs(new Date('2023-02-01')).format('YYYY-MM-DD'), + }, + }); + }); + + it('delete advertisement', async () => { + const deleteAdByIdMock = vi.fn(); + const mocks = [ + { + request: { + query: ORGANIZATION_ADVERTISEMENT_LIST, + variables: { + id: '1', + first: 2, + after: null, + last: null, + before: null, + }, + }, + result: { + data: { + organizations: [ + { + _id: '1', + advertisements: { + edges: [ + { + node: { + _id: '1', + name: 'Advertisement1', + startDate: '2022-01-01', + endDate: '2023-01-01', + mediaUrl: 'http://example1.com', + }, + cursor: 'cursor1', + }, + { + node: { + _id: '2', + name: 'Advertisement2', + startDate: '2024-02-01', + endDate: '2025-02-01', + mediaUrl: 'http://example2.com', + }, + cursor: 'cursor2', + }, + { + node: { + _id: '3', + name: 'Advertisement1', + startDate: '2022-01-01', + endDate: '2023-01-01', + mediaUrl: 'http://example1.com', + }, + cursor: 'cursor3', + }, + { + node: { + _id: '4', + name: 'Advertisement2', + startDate: '2024-02-01', + endDate: '2025-02-01', + mediaUrl: 'http://example2.com', + }, + cursor: 'cursor4', + }, + { + node: { + _id: '5', + name: 'Advertisement1', + startDate: '2022-01-01', + endDate: '2023-01-01', + mediaUrl: 'http://example1.com', + }, + cursor: 'cursor5', + }, + { + node: { + _id: '6', + name: 'Advertisement2', + startDate: '2024-02-01', + endDate: '2025-02-01', + mediaUrl: 'http://example2.com', + }, + cursor: 'cursor6', + }, + ], + pageInfo: { + startCursor: 'cursor1', + endCursor: 'cursor6', + hasNextPage: true, + hasPreviousPage: false, + }, + totalCount: 8, + }, + }, + ], + }, + }, + }, + { + request: { + query: DELETE_ADVERTISEMENT_BY_ID, + variables: { + id: '1', + }, + }, + result: { + data: { + advertisements: { + _id: null, + }, + }, + }, + }, + ]; + mockUseMutation.mockReturnValue([deleteAdByIdMock]); + const { getByTestId, getAllByText } = render( + + + + + + + + + + + , + ); + + //Testing rendering + expect(getByTestId('AdEntry')).toBeInTheDocument(); + expect(getAllByText('POPUP')[0]).toBeInTheDocument(); + expect(getAllByText('Advert1')[0]).toBeInTheDocument(); + expect(screen.getByTestId('media')).toBeInTheDocument(); + + //Testing successful deletion + fireEvent.click(getByTestId('moreiconbtn')); + fireEvent.click(getByTestId('deletebtn')); + + await waitFor(() => { + expect(screen.getByTestId('delete_title')).toBeInTheDocument(); + expect(screen.getByTestId('delete_body')).toBeInTheDocument(); + }); + + fireEvent.click(getByTestId('delete_yes')); + + await waitFor(() => { + expect(deleteAdByIdMock).toHaveBeenCalledWith({ + variables: { + id: '1', + }, + }); + const deletedMessage = screen.queryByText('Advertisement Deleted'); + expect(deletedMessage).toBeNull(); + }); + + //Testing unsuccessful deletion + deleteAdByIdMock.mockRejectedValueOnce(new Error('Deletion Failed')); + + fireEvent.click(getByTestId('moreiconbtn')); + + fireEvent.click(getByTestId('delete_yes')); + + await waitFor(() => { + expect(deleteAdByIdMock).toHaveBeenCalledWith({ + variables: { + id: '1', + }, + }); + const deletionFailedText = screen.queryByText((content, element) => { + return ( + element?.textContent === 'Deletion Failed' && + element.tagName.toLowerCase() === 'div' + ); + }); + expect(deletionFailedText).toBeNull(); + }); + }); +}); From 8633bc67eb4cfbcdeb9dc0b608683fcf17f715a6 Mon Sep 17 00:00:00 2001 From: IITI-tushar <165766280+IITI-tushar@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:06:27 +0530 Subject: [PATCH 44/57] update and rename Settings.test.tsx to Settings.spec.tsx fixes #2579 (#2718) * Update and rename Settings.test.tsx to Settings.spec.tsx * Update Settings.spec.tsx- removed-deprecated-methods-suggested-by-coderabbit --- .../{Settings.test.tsx => Settings.spec.tsx} | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) rename src/screens/UserPortal/Settings/{Settings.test.tsx => Settings.spec.tsx} (92%) diff --git a/src/screens/UserPortal/Settings/Settings.test.tsx b/src/screens/UserPortal/Settings/Settings.spec.tsx similarity index 92% rename from src/screens/UserPortal/Settings/Settings.test.tsx rename to src/screens/UserPortal/Settings/Settings.spec.tsx index fd9e1ed350..39d853862f 100644 --- a/src/screens/UserPortal/Settings/Settings.test.tsx +++ b/src/screens/UserPortal/Settings/Settings.spec.tsx @@ -1,5 +1,6 @@ -import React, { act } from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; +import { describe, expect, beforeAll, vi } from 'vitest'; +import { render, screen, fireEvent, act } from '@testing-library/react'; import { MockedProvider } from '@apollo/react-testing'; import { I18nextProvider } from 'react-i18next'; import { UPDATE_USER_MUTATION } from 'GraphQl/Mutations/mutations'; @@ -119,31 +120,27 @@ const resizeWindow = (width: number): void => { async function wait(ms = 100): Promise { await act(async () => { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); + vi.advanceTimersByTime(ms); }); } describe('Testing Settings Screen [User Portal]', () => { - // Mock implementation of matchMedia beforeAll(() => { + vi.useFakeTimers(); 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(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), })), }); }); - test('Screen should be rendered properly', async () => { + it('Screen should be rendered properly', async () => { await act(async () => { render( @@ -163,7 +160,7 @@ describe('Testing Settings Screen [User Portal]', () => { expect(screen.queryAllByText('Settings')).not.toBe([]); }); - test('input works properly', async () => { + it('input works properly', async () => { await act(async () => { render( @@ -220,7 +217,7 @@ describe('Testing Settings Screen [User Portal]', () => { expect(screen.getByTestId('profile-picture')).toBeInTheDocument(); }); - test('resetChangesBtn works properly', async () => { + it('resetChangesBtn works properly', async () => { await act(async () => { render( @@ -253,7 +250,7 @@ describe('Testing Settings Screen [User Portal]', () => { expect(screen.getByLabelText('Birth Date')).toHaveValue('2024-03-01'); }); - test('resetChangesBtn works properly when the details are empty', async () => { + it('resetChangesBtn works properly when the details are empty', async () => { await act(async () => { render( @@ -286,7 +283,7 @@ describe('Testing Settings Screen [User Portal]', () => { expect(screen.getByLabelText('Birth Date')).toHaveValue(''); }); - test('sidebar', async () => { + it('sidebar', async () => { await act(async () => { render( @@ -311,7 +308,7 @@ describe('Testing Settings Screen [User Portal]', () => { act(() => openMenuBtn.click()); }); - test('Testing sidebar when the screen size is less than or equal to 820px', async () => { + it('Testing sidebar when the screen size is less than or equal to 820px', async () => { resizeWindow(800); await act(async () => { render( @@ -328,7 +325,6 @@ describe('Testing Settings Screen [User Portal]', () => { }); await wait(); - screen.debug(); const openMenuBtn = screen.queryByTestId('openMenu'); @@ -348,7 +344,7 @@ describe('Testing Settings Screen [User Portal]', () => { } }); - test('renders events attended card correctly', async () => { + it('renders events attended card correctly', async () => { render( @@ -368,7 +364,7 @@ describe('Testing Settings Screen [User Portal]', () => { expect(screen.getByText('No Events Attended')).toBeInTheDocument(); }); - test('renders events attended card correctly with events', async () => { + it('renders events attended card correctly with events', async () => { const mockEventsAttended = [ { _id: '1', title: 'Event 1' }, { _id: '2', title: 'Event 2' }, From 8a21cbbcb27a954820266a02bcd5e83a194ec5b0 Mon Sep 17 00:00:00 2001 From: Nikhil Raj <125121367+hustlernik@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:08:04 +0530 Subject: [PATCH 45/57] fix:smooth infinite scroll for users list (#2726) --- src/screens/Users/Users.tsx | 112 ++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/screens/Users/Users.tsx b/src/screens/Users/Users.tsx index 3ea165d7b0..b046679a00 100644 --- a/src/screens/Users/Users.tsx +++ b/src/screens/Users/Users.tsx @@ -70,6 +70,7 @@ const Users = (): JSX.Element => { const { getItem } = useLocalStorage(); const perPageResult = 12; + const tableLoaderRowLength = 4; const [isLoading, setIsLoading] = useState(true); const [hasMore, setHasMore] = useState(true); const [isLoadingMore, setIsLoadingMore] = useState(false); @@ -432,67 +433,66 @@ const Users = (): JSX.Element => {
) : (
- {isLoading ? ( - - ) : ( - - } - hasMore={hasMore} - className={styles.listBox} - data-testid="users-list" - endMessage={ -
-
{tCommon('endOfResults')}
-
- } - > - - - - {headerTitles.map((title: string, index: number) => { + {isLoading && ( + + )} + + } + hasMore={hasMore} + className={styles.listBox} + data-testid="users-list" + endMessage={ +
+
{tCommon('endOfResults')}
+
+ } + > +
+ + + {headerTitles.map((title: string, index: number) => { + return ( + + ); + })} + + + + {usersData && + displayedUsers.map( + (user: InterfaceQueryUserListItem, index: number) => { return ( - + ); - })} - - - - {usersData && - displayedUsers.map( - (user: InterfaceQueryUserListItem, index: number) => { - return ( - - ); - }, - )} - -
+ {title} +
- {title} -
-
- )} + }, + )} + + +
)} ); }; - export default Users; From a295db30220035bbd00ef60c7977ae63c325436c Mon Sep 17 00:00:00 2001 From: Rafiul Hasan <67283985+rafidoth@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:40:07 +0600 Subject: [PATCH 46/57] Fixes #2670 : Refactored src/screen/PageNotFound component test from jest to vitest (#2708) * Refactored Refactor: src/screens/PageNotFound from Jest to Vitest * Refactored :src/screens/PageNotFound from Jest to Vitest * Refactored :src/screens/PageNotFound from Jest to Vitest * Refactored :src/screens/PageNotFound from Jest to Vitest --- ...otFound.test.tsx => PageNotFound.spec.tsx} | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) rename src/screens/PageNotFound/{PageNotFound.test.tsx => PageNotFound.spec.tsx} (68%) diff --git a/src/screens/PageNotFound/PageNotFound.test.tsx b/src/screens/PageNotFound/PageNotFound.spec.tsx similarity index 68% rename from src/screens/PageNotFound/PageNotFound.test.tsx rename to src/screens/PageNotFound/PageNotFound.spec.tsx index 501d9f7ef3..4557eb5af4 100644 --- a/src/screens/PageNotFound/PageNotFound.test.tsx +++ b/src/screens/PageNotFound/PageNotFound.spec.tsx @@ -8,11 +8,11 @@ import { store } from 'state/store'; import PageNotFound from './PageNotFound'; import i18nForTest from 'utils/i18nForTest'; import useLocalStorage from 'utils/useLocalstorage'; - +import { it, expect, describe } from 'vitest'; const { setItem } = useLocalStorage(); describe('Testing Page not found component', () => { - test('Component should be rendered properly for User', () => { + it('should render component properly for User', () => { //setItem('AdminFor', undefined); render( @@ -24,15 +24,15 @@ describe('Testing Page not found component', () => { , ); - expect(screen.getByText(/Talawa User/i)).toBeTruthy(); - expect(screen.getByText(/404/i)).toBeTruthy(); + expect(screen.getByText(/Talawa User/i)).toBeInTheDocument(); + expect(screen.getByText(/404/i)).toBeInTheDocument(); expect( screen.getByText(/Oops! The Page you requested was not found!/i), - ).toBeTruthy(); - expect(screen.getByText(/Back to Home/i)).toBeTruthy(); + ).toBeInTheDocument(); + expect(screen.getByText(/Back to Home/i)).toBeInTheDocument(); }); - test('Component should be rendered properly for ADMIN or SUPERADMIN', () => { + it('should render properly for ADMIN or SUPERADMIN', () => { setItem('AdminFor', [ { _id: '6537904485008f171cf29924', @@ -49,11 +49,11 @@ describe('Testing Page not found component', () => { , ); - expect(screen.getByText(/Talawa Admin Portal/i)).toBeTruthy(); - expect(screen.getByText(/404/i)).toBeTruthy(); + expect(screen.getByText(/Talawa Admin Portal/i)).toBeInTheDocument(); + expect(screen.getByText(/404/i)).toBeInTheDocument(); expect( screen.getByText(/Oops! The Page you requested was not found!/i), - ).toBeTruthy(); - expect(screen.getByText(/Back to Home/i)).toBeTruthy(); + ).toBeInTheDocument(); + expect(screen.getByText(/Back to Home/i)).toBeInTheDocument(); }); }); From d5a790d262477fd1a79cc0acfbcccfad20bea969 Mon Sep 17 00:00:00 2001 From: Vedant Gupta <115912707+im-vedant@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:12:07 +0530 Subject: [PATCH 47/57] Refactor: src/screens/OrganizationVenues from Jest to Vitest (#2665) * Refactored some code * Migrate src/screens/OrganizationVenues from jest to vitest * Revert "Refactored some code" This reverts commit 02eb9d4fa59009ba81d93d5b137ba2d0f95eae23. * replaced vi.mock with vi.doMock * Add comments * Suppressed Css error * Fix customTableCell issue * add data-testid attributes for toggle state for improved testability * feat: updated jest config and package-lock.json to include identity-obj-proxy to mock Css modules * Fix linting errors * refactor : src\components\MemberDetail\customTableCell.test.tsx and src\components\OrganizationScreen\OrganizationScreen.test.tsx from jest to vitest * Removed data-expanded attribute from mainpageright div * Add test cases in src/components/MemberDetail/CustomTableCell --------- Co-authored-by: im-vedant <194vedantgutpa@gmail.com> Co-authored-by: Peter Harrison <16875803+palisadoes@users.noreply.github.com> --- .../MemberDetail/customTableCell.spec.tsx | 160 ++++++++++++++++ .../MemberDetail/customTableCell.test.tsx | 171 ------------------ ...n.test.tsx => OrganizationScreen.spec.tsx} | 7 +- ...s.test.tsx => OrganizationVenues.spec.tsx} | 38 ++-- 4 files changed, 187 insertions(+), 189 deletions(-) create mode 100644 src/components/MemberDetail/customTableCell.spec.tsx delete mode 100644 src/components/MemberDetail/customTableCell.test.tsx rename src/components/OrganizationScreen/{OrganizationScreen.test.tsx => OrganizationScreen.spec.tsx} (95%) rename src/screens/OrganizationVenues/{OrganizationVenues.test.tsx => OrganizationVenues.spec.tsx} (92%) diff --git a/src/components/MemberDetail/customTableCell.spec.tsx b/src/components/MemberDetail/customTableCell.spec.tsx new file mode 100644 index 0000000000..81a088d5bb --- /dev/null +++ b/src/components/MemberDetail/customTableCell.spec.tsx @@ -0,0 +1,160 @@ +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import { MockedProvider } from '@apollo/client/testing'; +import { BrowserRouter } from 'react-router-dom'; +import { CustomTableCell } from './customTableCell'; +import { EVENT_DETAILS } from 'GraphQl/Queries/Queries'; +import { vi } from 'vitest'; +vi.mock('react-toastify', () => ({ + toast: { + success: vi.fn(), + error: vi.fn(), + }, +})); + +const mocks = [ + { + request: { + query: EVENT_DETAILS, + variables: { id: 'event123' }, + }, + result: { + data: { + event: { + _id: 'event123', + title: 'Test Event', + description: 'This is a test event description', + startDate: '2023-05-01', + endDate: '2023-05-02', + startTime: '09:00:00', + endTime: '17:00:00', + allDay: false, + location: 'Test Location', + recurring: true, + baseRecurringEvent: { + _id: 'recurringEvent123', + }, + organization: { + _id: 'org456', + members: [ + { _id: 'member1', firstName: 'John', lastName: 'Doe' }, + { _id: 'member2', firstName: 'Jane', lastName: 'Smith' }, + ], + }, + attendees: [{ _id: 'user1' }, { _id: 'user2' }], + }, + }, + }, + }, +]; + +describe('CustomTableCell', () => { + it('renders event details correctly', async () => { + render( + + + + + + +
+
+
, + ); + + await waitFor(() => screen.getByTestId('custom-row')); + + expect(screen.getByText('Test Event')).toBeInTheDocument(); + expect( + screen.getByText( + new Date('2023-05-01').toLocaleDateString(undefined, { + year: 'numeric', + month: 'long', + day: 'numeric', + timeZone: 'UTC', + }), + ), + ).toBeInTheDocument(); + expect(screen.getByText('Yes')).toBeInTheDocument(); + expect(screen.getByText('2')).toBeInTheDocument(); + + const link = screen.getByRole('link', { name: 'Test Event' }); + expect(link).toHaveAttribute('href', '/event/org456/event123'); + }); + + it('displays loading state', () => { + render( + + + + + +
+
, + ); + + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + }); + + it('displays error state', async () => { + const errorMock = [ + { + request: { + query: EVENT_DETAILS, + variables: { id: 'event123' }, + }, + error: new Error('An error occurred'), + }, + ]; + + render( + + + + + +
+
, + ); + + await waitFor(() => { + expect( + screen.getByText( + 'Unable to load event details. Please try again later.', + ), + ).toBeInTheDocument(); + }); + }); + + it('displays no event found message', async () => { + const noEventMock = [ + { + request: { + query: EVENT_DETAILS, + variables: { id: 'event123' }, + }, + result: { + data: { + event: null, + }, + }, + }, + ]; + + render( + + + + + +
+
, + ); + + await waitFor(() => { + expect( + screen.getByText('Event not found or has been deleted'), + ).toBeInTheDocument(); + }); + }); +}); diff --git a/src/components/MemberDetail/customTableCell.test.tsx b/src/components/MemberDetail/customTableCell.test.tsx deleted file mode 100644 index bc296a74f3..0000000000 --- a/src/components/MemberDetail/customTableCell.test.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import { MockedProvider } from '@apollo/client/testing'; -import { BrowserRouter } from 'react-router-dom'; -import { CustomTableCell } from './customTableCell'; -import { EVENT_DETAILS } from 'GraphQl/Queries/Queries'; - -jest.mock('react-toastify', () => ({ - toast: { - success: jest.fn(), - error: jest.fn(), - }, -})); - -const mocks = [ - { - request: { - query: EVENT_DETAILS, - variables: { id: 'event123' }, - }, - result: { - data: { - event: { - _id: 'event123', - title: 'Test Event', - description: 'This is a test event description', - startDate: '2023-05-01', - endDate: '2023-05-02', - startTime: '09:00:00', - endTime: '17:00:00', - allDay: false, - location: 'Test Location', - recurring: true, - baseRecurringEvent: { - _id: 'recurringEvent123', - }, - organization: { - _id: 'org456', - members: [ - { _id: 'member1', firstName: 'John', lastName: 'Doe' }, - { _id: 'member2', firstName: 'Jane', lastName: 'Smith' }, - ], - }, - attendees: [{ _id: 'user1' }, { _id: 'user2' }], - }, - }, - }, - }, -]; - -describe('CustomTableCell', () => { - it('renders event details correctly', async () => { - render( - - - - - - -
-
-
, - ); - - await waitFor(() => screen.getByTestId('custom-row')); - - expect(screen.getByText('Test Event')).toBeInTheDocument(); - expect(screen.getByText('May 1, 2023')).toBeInTheDocument(); - expect(screen.getByText('Yes')).toBeInTheDocument(); - expect(screen.getByText('2')).toBeInTheDocument(); - - const link = screen.getByRole('link', { name: 'Test Event' }); - expect(link).toHaveAttribute('href', '/event/org456/event123'); - }); - - it('displays loading state', () => { - render( - - - - - -
-
, - ); - - expect(screen.getByRole('progressbar')).toBeInTheDocument(); - }); - - // it('displays error state', async () => { - // const errorMock = [ - // { - // request: { - // query: EVENT_DETAILS, - // variables: { id: 'event123' }, - // }, - // error: new Error('An error occurred'), - // }, - // ]; - - // render( - // - // - // - // - // - //
- //
, - // ); - - // await waitFor( - // () => { - // expect( - // screen.getByText('Error loading event details'), - // ).toBeInTheDocument(); - // }, - // { timeout: 2000 }, - // ); - - // // Check if the error message from toast has been called - // expect(toast.error).toHaveBeenCalledWith('An error occurred'); - // }); - - // it('displays no event found message', async () => { - // const noEventMock = [ - // { - // request: { - // query: EVENT_DETAILS, - // variables: { id: 'event123' }, - // }, - // result: { - // data: { - // event: { - // _id: null, - // title: null, - // startDate: null, - // description: null, - // endDate: null, - // startTime: null, - // endTime: null, - // allDay: false, - // location: null, - // recurring: null, - // organization: { - // _id: null, - // members: [], - // }, - // baseRecurringEvent: { - // _id: 'recurringEvent123', - // }, - // attendees: [], - // }, - // }, - // }, - // }, - // ]; - - // render( - // - // - // - // - // - //
- //
, - // ); - - // await waitFor(() => screen.getByText('No event found')); - // expect(screen.getByText('No event found')).toBeInTheDocument(); - // }); -}); diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.spec.tsx similarity index 95% rename from src/components/OrganizationScreen/OrganizationScreen.test.tsx rename to src/components/OrganizationScreen/OrganizationScreen.spec.tsx index d1c4c81c27..e6a75c46d8 100644 --- a/src/components/OrganizationScreen/OrganizationScreen.test.tsx +++ b/src/components/OrganizationScreen/OrganizationScreen.spec.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { MockedProvider } from '@apollo/react-testing'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; -import 'jest-location-mock'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { store } from 'state/store'; @@ -11,10 +10,10 @@ import OrganizationScreen from './OrganizationScreen'; import { ORGANIZATION_EVENT_LIST } from 'GraphQl/Queries/Queries'; import { StaticMockLink } from 'utils/StaticMockLink'; import styles from './OrganizationScreen.module.css'; - +import { vi } from 'vitest'; const mockID: string | undefined = '123'; -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), +vi.mock('react-router-dom', async () => ({ + ...(await vi.importActual('react-router-dom')), useParams: () => ({ orgId: mockID }), useMatch: () => ({ params: { eventId: 'event123', orgId: '123' } }), })); diff --git a/src/screens/OrganizationVenues/OrganizationVenues.test.tsx b/src/screens/OrganizationVenues/OrganizationVenues.spec.tsx similarity index 92% rename from src/screens/OrganizationVenues/OrganizationVenues.test.tsx rename to src/screens/OrganizationVenues/OrganizationVenues.spec.tsx index 5b8b9933a1..e306c56cfc 100644 --- a/src/screens/OrganizationVenues/OrganizationVenues.test.tsx +++ b/src/screens/OrganizationVenues/OrganizationVenues.spec.tsx @@ -1,3 +1,15 @@ +/** + * Tests for the OrganizationVenues component. + * These tests include: + * - Ensuring the component renders correctly with default props. + * - Handling the absence of `orgId` by redirecting to the homepage. + * - Fetching and displaying venues via Apollo GraphQL queries. + * - Allowing users to search venues by name or description. + * - Sorting venues by capacity in ascending or descending order. + * - Verifying that long venue names or descriptions are handled gracefully. + * - Testing loading states and edge cases for Apollo queries. + * - Mocking GraphQL mutations for venue-related actions and validating their behavior. + */ import React from 'react'; import { MockedProvider } from '@apollo/react-testing'; import type { RenderResult } from '@testing-library/react'; @@ -10,7 +22,6 @@ import { } from '@testing-library/react'; import { Provider } from 'react-redux'; import { MemoryRouter, Route, Routes } from 'react-router-dom'; -import 'jest-location-mock'; import { I18nextProvider } from 'react-i18next'; import OrganizationVenues from './OrganizationVenues'; import { store } from 'state/store'; @@ -19,7 +30,7 @@ import { StaticMockLink } from 'utils/StaticMockLink'; import { VENUE_LIST } from 'GraphQl/Queries/OrganizationQueries'; import type { ApolloLink } from '@apollo/client'; import { DELETE_VENUE_MUTATION } from 'GraphQl/Mutations/VenueMutations'; - +import { vi } from 'vitest'; const MOCKS = [ { request: { @@ -239,11 +250,11 @@ async function wait(ms = 100): Promise { }); } -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - warning: jest.fn(), - error: jest.fn(), + success: vi.fn(), + warning: vi.fn(), + error: vi.fn(), }, })); @@ -272,14 +283,14 @@ const renderOrganizationVenue = (link: ApolloLink): RenderResult => { describe('OrganizationVenue with missing orgId', () => { beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), + vi.doMock('react-router-dom', async () => ({ + ...(await vi.importActual('react-router-dom')), useParams: () => ({ orgId: undefined }), })); }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('Redirect to /orglist when orgId is falsy/undefined', async () => { render( @@ -299,7 +310,6 @@ describe('OrganizationVenue with missing orgId', () => { , ); - await waitFor(() => { const paramsError = screen.getByTestId('paramsError'); expect(paramsError).toBeInTheDocument(); @@ -308,17 +318,17 @@ describe('OrganizationVenue with missing orgId', () => { }); describe('Organisation Venues', () => { - global.alert = jest.fn(); + global.alert = vi.fn(); beforeAll(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), + vi.doMock('react-router-dom', async () => ({ + ...(await vi.importActual('react-router-dom')), useParams: () => ({ orgId: 'orgId' }), })); }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('searches the venue list correctly by Name', async () => { From 9108753fc80fb7014fda74caa9419a5e33d0179e Mon Sep 17 00:00:00 2001 From: Aaditya Agarwal Date: Mon, 23 Dec 2024 04:14:58 +0530 Subject: [PATCH 48/57] Upgrade inquirer version to 11.0.2 (#2720) * upgraded inqurier version * updated custom port tests * chaged isnan with number.isnan * added tests for invalid port * added reserved ports functionality * improved type safety, maintainability and organisation * added clear all mocks --- package-lock.json | 1015 +++++++---------- package.json | 2 +- .../askForCustomPort/askForCustomPort.test.ts | 112 +- .../askForCustomPort/askForCustomPort.ts | 65 +- .../askForTalawaApiUrl.test.ts | 14 +- .../askForTalawaApiUrl/askForTalawaApiUrl.ts | 2 +- 6 files changed, 577 insertions(+), 633 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24478f4f69..07a65a79c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "i18next": "^23.15.1", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.6.1", - "inquirer": "^8.0.0", + "inquirer": "^11.0.2", "js-cookie": "^3.0.1", "lcov-result-merger": "^5.0.1", "markdown-toc": "^1.2.0", @@ -99,7 +99,6 @@ "@typescript-eslint/eslint-plugin": "^8.11.0", "@typescript-eslint/parser": "^8.5.0", "@vitest/coverage-istanbul": "^2.1.5", - "@vitest/eslint-plugin": "^1.1.14", "babel-jest": "^29.7.0", "cross-env": "^7.0.3", "eslint": "^8.49.0", @@ -109,7 +108,6 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.1", "eslint-plugin-tsdoc": "^0.3.0", - "eslint-plugin-vitest": "^0.5.4", "husky": "^9.1.6", "identity-obj-proxy": "^3.0.0", "jest": "^27.4.5", @@ -3159,6 +3157,239 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@inquirer/checkbox": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", + "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", + "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", + "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", + "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.9.tgz", + "integrity": "sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", + "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", + "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", + "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", + "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^3.0.1", + "@inquirer/confirm": "^4.0.1", + "@inquirer/editor": "^3.0.1", + "@inquirer/expand": "^3.0.1", + "@inquirer/input": "^3.0.1", + "@inquirer/number": "^2.0.1", + "@inquirer/password": "^3.0.1", + "@inquirer/rawlist": "^3.0.1", + "@inquirer/search": "^2.0.1", + "@inquirer/select": "^3.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", + "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", + "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", + "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -5706,11 +5937,19 @@ "@types/unist": "*" } }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "22.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", - "devOptional": true, "dependencies": { "undici-types": "~6.19.8" } @@ -5908,6 +6147,12 @@ "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "16.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", @@ -6379,36 +6624,15 @@ "node": ">=18" } }, - "node_modules/@vitest/eslint-plugin": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.14.tgz", - "integrity": "sha512-ej0cT5rUt7uvwxuu7Qxkm7fI+eaOq8vD34qGpuRoXCdvOybOlE5GDqtgvVCYbxLANkcRJfm5VDU1TnJmQRHi9g==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@typescript-eslint/utils": ">= 8.0", - "eslint": ">= 8.57.0", - "typescript": ">= 5.0.0", - "vitest": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "vitest": { - "optional": true - } - } - }, "node_modules/@vitest/expect": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", - "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.5.tgz", + "integrity": "sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", + "@vitest/spy": "2.1.5", + "@vitest/utils": "2.1.5", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -6417,13 +6641,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", - "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.5.tgz", + "integrity": "sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.8", + "@vitest/spy": "2.1.5", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, @@ -6457,13 +6681,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", - "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.5.tgz", + "integrity": "sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.8", + "@vitest/utils": "2.1.5", "pathe": "^1.1.2" }, "funding": { @@ -6471,13 +6695,13 @@ } }, "node_modules/@vitest/snapshot": { - "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==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.5.tgz", + "integrity": "sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.8", + "@vitest/pretty-format": "2.1.5", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -6485,10 +6709,23 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/snapshot/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vitest/spy": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", - "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.5.tgz", + "integrity": "sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==", "dev": true, "license": "MIT", "dependencies": { @@ -6499,13 +6736,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", - "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz", + "integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.8", + "@vitest/pretty-format": "2.1.5", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -6513,6 +6750,19 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/utils/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@wry/caches": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", @@ -6780,16 +7030,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -7364,39 +7604,6 @@ "node": ">=8" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -7914,7 +8121,8 @@ "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" }, "node_modules/chart.js": { "version": "4.4.6", @@ -8030,17 +8238,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -8108,11 +8305,12 @@ } }, "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/cliui": { @@ -8859,25 +9057,6 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defaults/node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -9029,19 +9208,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -9851,221 +10017,68 @@ "string.prototype.matchall": "^4.0.11", "string.prototype.repeat": "^1.0.0" }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-tsdoc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.3.0.tgz", - "integrity": "sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==", - "dev": true, - "dependencies": { - "@microsoft/tsdoc": "0.15.0", - "@microsoft/tsdoc-config": "0.17.0" - } - }, - "node_modules/eslint-plugin-vitest": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", - "integrity": "sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^7.7.1" - }, - "engines": { - "node": "^18.0.0 || >= 20.0.0" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "vitest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "vitest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, + "engines": { + "node": ">=4" + }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "esutils": "^2.0.2" }, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-vitest/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/eslint-plugin-react/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">=4.0" } }, - "node_modules/eslint-plugin-vitest/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=16 || 14 >=14.17" + "bin": { + "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.3.0.tgz", + "integrity": "sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.15.0", + "@microsoft/tsdoc-config": "0.17.0" } }, "node_modules/eslint-visitor-keys": { @@ -10508,6 +10521,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -10521,6 +10535,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -10606,28 +10621,6 @@ "bser": "2.1.1" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -11062,27 +11055,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globrex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", @@ -11242,6 +11214,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -11744,79 +11717,22 @@ "dev": true }, "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-11.1.0.tgz", + "integrity": "sha512-CmLAZT65GG/v30c+D2Fk8+ceP6pxD6RL+hIUOWAltCmeyEqWYwqu9v76q03OvjyZ3AB0C1Ala2stn1z/rMqGEw==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@inquirer/core": "^9.2.1", + "@inquirer/prompts": "^6.0.1", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "ansi-escapes": "^4.3.2", + "mute-stream": "^1.0.0", + "run-async": "^3.0.0", + "rxjs": "^7.8.1" }, "engines": { - "node": ">=8" + "node": ">=18" } }, "node_modules/internal-slot": { @@ -12122,14 +12038,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -12344,17 +12252,6 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -14688,7 +14585,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", @@ -14735,36 +14633,6 @@ "lodash._reinterpolate": "^3.0.0" } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/log-update": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", @@ -15277,6 +15145,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "engines": { "node": ">=6" } @@ -15377,9 +15246,13 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/nanoid": { "version": "3.3.7", @@ -15793,6 +15666,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -15870,66 +15744,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -15939,6 +15753,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -17642,9 +17457,10 @@ ] }, "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -17980,7 +17796,8 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -18438,6 +18255,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -18586,11 +18404,6 @@ "node": ">=8" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -18697,6 +18510,7 @@ "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -19537,8 +19351,7 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "devOptional": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", @@ -19958,9 +19771,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", - "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.5.tgz", + "integrity": "sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==", "dev": true, "license": "MIT", "dependencies": { @@ -20292,19 +20105,19 @@ } }, "node_modules/vitest": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", - "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.5.tgz", + "integrity": "sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==", "dev": true, "license": "MIT", "dependencies": { - "@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", + "@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", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", @@ -20316,7 +20129,7 @@ "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.8", + "vite-node": "2.1.5", "why-is-node-running": "^2.3.0" }, "bin": { @@ -20331,8 +20144,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.8", - "@vitest/ui": "2.1.8", + "@vitest/browser": "2.1.5", + "@vitest/ui": "2.1.5", "happy-dom": "*", "jsdom": "*" }, @@ -20409,14 +20222,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/web-vitals": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", @@ -20758,6 +20563,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", diff --git a/package.json b/package.json index aee85b5772..a922f88205 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "i18next": "^23.15.1", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.6.1", - "inquirer": "^8.0.0", + "inquirer": "^11.0.2", "js-cookie": "^3.0.1", "lcov-result-merger": "^5.0.1", "markdown-toc": "^1.2.0", diff --git a/src/setup/askForCustomPort/askForCustomPort.test.ts b/src/setup/askForCustomPort/askForCustomPort.test.ts index 0df6259ba1..c46f7b3f91 100644 --- a/src/setup/askForCustomPort/askForCustomPort.test.ts +++ b/src/setup/askForCustomPort/askForCustomPort.test.ts @@ -1,24 +1,110 @@ import inquirer from 'inquirer'; -import { askForCustomPort } from './askForCustomPort'; +import { askForCustomPort, validatePort } from './askForCustomPort'; jest.mock('inquirer'); describe('askForCustomPort', () => { - test('should return default port if user provides no input', async () => { - jest - .spyOn(inquirer, 'prompt') - .mockResolvedValueOnce({ customPort: '4321' }); + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('basic port validation', () => { + test('should return default port if user provides no input', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ customPort: '4321' }); + + const result = await askForCustomPort(); + expect(result).toBe(4321); + }); + + test('should return user-provided port', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ customPort: '8080' }); + + const result = await askForCustomPort(); + expect(result).toBe(8080); + }); + + test('should return validation error if port not between 1 and 65535', () => { + expect(validatePort('abcd')).toBe( + 'Please enter a valid port number between 1 and 65535.', + ); + expect(validatePort('-1')).toBe( + 'Please enter a valid port number between 1 and 65535.', + ); + expect(validatePort('70000')).toBe( + 'Please enter a valid port number between 1 and 65535.', + ); + }); + }); - const result = await askForCustomPort(); - expect(result).toBe('4321'); + describe('retry mechanism', () => { + test('should handle invalid port input and prompt again', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ customPort: 'abcd' }) + .mockResolvedValueOnce({ customPort: '8080' }); + + const result = await askForCustomPort(); + expect(result).toBe(8080); + }); + + test('should return default port after maximum retry attempts', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ customPort: 'invalid-port-attempt1' }) + .mockResolvedValueOnce({ customPort: 'invalid-port-attempt2' }) + .mockResolvedValueOnce({ customPort: 'invalid-port-attempt3' }) + .mockResolvedValueOnce({ customPort: 'invalid-port-attempt4' }) + .mockResolvedValueOnce({ customPort: 'invalid-port-attempt5' }) + .mockResolvedValueOnce({ customPort: 'invalid-port-attempt6' }); + + const result = await askForCustomPort(); + expect(result).toBe(4321); + }); }); - test('should return user-provided port', async () => { - jest - .spyOn(inquirer, 'prompt') - .mockResolvedValueOnce({ customPort: '8080' }); + describe('reserved ports', () => { + test('should return user-provided port after confirming reserved port', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ customPort: '80' }) + .mockResolvedValueOnce({ confirmPort: true }); + + const result = await askForCustomPort(); + expect(result).toBe(80); + }); + + test('should re-prompt user for port if reserved port confirmation is denied', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ customPort: '80' }) + .mockResolvedValueOnce({ confirmPort: false }) + .mockResolvedValueOnce({ customPort: '8080' }); + + const result = await askForCustomPort(); + expect(result).toBe(8080); + }); + + test('should return default port if reserved port confirmation is denied after maximum retry attempts', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ customPort: '80' }) + .mockResolvedValueOnce({ confirmPort: false }) + .mockResolvedValueOnce({ customPort: '80' }) + .mockResolvedValueOnce({ confirmPort: false }) + .mockResolvedValueOnce({ customPort: '80' }) + .mockResolvedValueOnce({ confirmPort: false }) + .mockResolvedValueOnce({ customPort: '80' }) + .mockResolvedValueOnce({ confirmPort: false }) + .mockResolvedValueOnce({ customPort: '80' }) + .mockResolvedValueOnce({ confirmPort: false }) + .mockResolvedValueOnce({ customPort: '80' }); - const result = await askForCustomPort(); - expect(result).toBe('8080'); + const result = await askForCustomPort(); + expect(result).toBe(4321); + }); }); }); diff --git a/src/setup/askForCustomPort/askForCustomPort.ts b/src/setup/askForCustomPort/askForCustomPort.ts index 8a923f678f..dd0fd51854 100644 --- a/src/setup/askForCustomPort/askForCustomPort.ts +++ b/src/setup/askForCustomPort/askForCustomPort.ts @@ -1,14 +1,63 @@ import inquirer from 'inquirer'; -export async function askForCustomPort(): Promise { - const { customPort } = await inquirer.prompt([ +const DEFAULT_PORT = 4321; +const MAX_RETRY_ATTEMPTS = 5; + +export function validatePort(input: string): string | boolean { + const port = Number(input); + if ( + Number.isNaN(port) || + !Number.isInteger(port) || + port <= 0 || + port > 65535 + ) { + return 'Please enter a valid port number between 1 and 65535.'; + } + return true; +} + +export async function reservedPortWarning(port: number): Promise { + const { confirmPort } = await inquirer.prompt<{ confirmPort: boolean }>([ { - type: 'input', - name: 'customPort', - message: - 'Enter custom port for development server (leave blank for default 4321):', - default: 4321, + type: 'confirm', + name: 'confirmPort', + message: `Port ${port} is a reserved port. Are you sure you want to use it?`, + default: false, }, ]); - return customPort; + + return confirmPort; +} + +export async function askForCustomPort(): Promise { + let remainingAttempts = MAX_RETRY_ATTEMPTS; + + while (remainingAttempts--) { + const { customPort } = await inquirer.prompt<{ customPort: string }>([ + { + type: 'input', + name: 'customPort', + message: `Enter custom port for development server (leave blank for default ${DEFAULT_PORT}):`, + default: DEFAULT_PORT.toString(), + validate: validatePort, + }, + ]); + + if (customPort && validatePort(customPort) === true) { + if (Number(customPort) >= 1024) { + return Number(customPort); + } + + if ( + Number(customPort) < 1024 && + (await reservedPortWarning(Number(customPort))) + ) { + return Number(customPort); + } + } + } + console.log( + `\nMaximum attempts reached. Using default port ${DEFAULT_PORT}.`, + ); + return DEFAULT_PORT; } diff --git a/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.test.ts b/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.test.ts index b1490222b4..3a11a0d799 100644 --- a/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.test.ts +++ b/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.test.ts @@ -30,17 +30,9 @@ describe('askForTalawaApiUrl', () => { }); test('should return the default endpoint when the user does not enter anything', async () => { - const mockPrompt = jest - .spyOn(inquirer, 'prompt') - .mockImplementation(async (questions: any) => { - const answers: Record = {}; - questions.forEach( - (question: { name: string | number; default: any }) => { - answers[question.name] = question.default; - }, - ); - return answers; - }); + const mockPrompt = jest.spyOn(inquirer, 'prompt').mockResolvedValueOnce({ + endpoint: 'http://localhost:4000/graphql/', + }); const result = await askForTalawaApiUrl(); diff --git a/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.ts b/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.ts index 97daa1ac89..713ed7dc68 100644 --- a/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.ts +++ b/src/setup/askForTalawaApiUrl/askForTalawaApiUrl.ts @@ -1,7 +1,7 @@ import inquirer from 'inquirer'; export async function askForTalawaApiUrl(): Promise { - const { endpoint } = await inquirer.prompt([ + const { endpoint } = await inquirer.prompt<{ endpoint: string }>([ { type: 'input', name: 'endpoint', From 705dcbc98755fe231f9b9cf636701c2c1b132625 Mon Sep 17 00:00:00 2001 From: Akshat Gupta <88035400+Akshat76845@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:16:07 +0530 Subject: [PATCH 49/57] Refactor : src/screens/OrgContribution component from Jest to Vitest (#2729) * Converted from Jest to Vitest * Converted the code from Jest to Vitest * Converted from Jest to Vitest --------- Co-authored-by: Akshat --- ...tion.test.tsx => OrgContribution.spec.tsx} | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) rename src/screens/OrgContribution/{OrgContribution.test.tsx => OrgContribution.spec.tsx} (80%) diff --git a/src/screens/OrgContribution/OrgContribution.test.tsx b/src/screens/OrgContribution/OrgContribution.spec.tsx similarity index 80% rename from src/screens/OrgContribution/OrgContribution.test.tsx rename to src/screens/OrgContribution/OrgContribution.spec.tsx index f9f63c6807..f12057a626 100644 --- a/src/screens/OrgContribution/OrgContribution.test.tsx +++ b/src/screens/OrgContribution/OrgContribution.spec.tsx @@ -1,9 +1,9 @@ -import React, { act } from 'react'; +import React from 'react'; import { MockedProvider } from '@apollo/react-testing'; import { render } from '@testing-library/react'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; -import 'jest-location-mock'; +import { vi, describe, test, expect } from 'vitest'; import { I18nextProvider } from 'react-i18next'; import OrgContribution from './OrgContribution'; @@ -12,15 +12,20 @@ import i18nForTest from 'utils/i18nForTest'; import { StaticMockLink } from 'utils/StaticMockLink'; const link = new StaticMockLink([], true); async function wait(ms = 100): Promise { - await act(() => { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); + await new Promise((resolve) => { + setTimeout(resolve, ms); }); } describe('Organisation Contribution Page', () => { test('should render props and text elements test for the screen', async () => { + Object.defineProperty(window, 'location', { + value: { + assign: vi.fn(), + }, + writable: true, + }); + window.location.assign('/orglist'); const { container } = render( @@ -37,11 +42,10 @@ describe('Organisation Contribution Page', () => { expect(container.textContent).not.toBe('Loading data...'); await wait(); - expect(container.textContent).toMatch('Filter by Name'); expect(container.textContent).toMatch('Filter by Trans. ID'); expect(container.textContent).toMatch('Recent Stats'); expect(container.textContent).toMatch('Contribution'); - expect(window.location).toBeAt('/orglist'); + expect(window.location.assign).toHaveBeenCalledWith('/orglist'); }); }); From 92ce58dfd00128a722943f4e38f739eaaa1eb1f1 Mon Sep 17 00:00:00 2001 From: Gurram Karthik <167804249+gurramkarthiknetha@users.noreply.github.com> Date: Mon, 23 Dec 2024 04:21:39 +0530 Subject: [PATCH 50/57] fixed:#1887 Event and Holiday Calendar View (#2676) * fixed-1887 * changed acc to bot * final change * gitpush final change * 1 conflict resloved * 1 conflict reslove * 2nd conflict resloved * changed css acc to gautam-divyanshu * decreased time * changed in event calendar --------- Co-authored-by: Peter Harrison <16875803+palisadoes@users.noreply.github.com> --- .../EventCalendar/EventCalendar.module.css | 177 ++++++++++++- .../EventCalendar/EventCalendar.tsx | 240 +++++++----------- src/screens/UserPortal/Posts/Posts.test.tsx | 5 +- src/setupTests.ts | 2 +- 4 files changed, 265 insertions(+), 159 deletions(-) diff --git a/src/components/EventCalendar/EventCalendar.module.css b/src/components/EventCalendar/EventCalendar.module.css index 921af48bae..607d750f85 100644 --- a/src/components/EventCalendar/EventCalendar.module.css +++ b/src/components/EventCalendar/EventCalendar.module.css @@ -271,14 +271,6 @@ .expand_event_list { display: block; } - -.list_container { - padding: 5px; - width: fit-content; - display: flex; - flex-direction: row; -} - .event_list_hour { display: flex; flex-direction: row; @@ -316,7 +308,8 @@ flex-grow: 1; } -@media only screen and (max-width: 700px) { +@media only screen and (max-width: var(--mobile-breakpoint)) { + /** @breakpoint --mobile-breakpoint: 768px */ .event_list { display: none; } @@ -331,7 +324,7 @@ } } -@media only screen and (max-width: 500px) { +@media only screen and (max-width: var(--small-mobile-breakpoint)) { .btn__more { font-size: 12px; } @@ -344,7 +337,22 @@ gap: 10px; margin-top: 35px; } +.base_card { + flex: 1; + padding: 20px; + border-radius: 10px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); +} + +.holidays_card { + composes: base_card; + background-color: var(--holiday-card-bg); +} +.events_card { + composes: base_card; + background-color: #ffffff; +} .card__holidays { background-color: rgba(246, 242, 229, 1); display: flex; @@ -415,9 +423,150 @@ border-radius: 10px; } -.userEvents__color { - height: 15px; - width: 40px; - background: rgba(146, 200, 141, 0.5); +.baseIndicator { + border-radius: 5px; + width: 20px; + height: 12px; + display: inline-block; +} + +.holidayText { + font-size: 14px; + color: #555555; +} +.eventsLegend { + display: flex; + align-items: center; + gap: 8px; +} + +.list_container { + padding: 5px; + width: fit-content; + display: flex; + align-items: center; + gap: var(--indicator-spacing); +} + +.holidayIndicator { + composes: baseIndicator; + background-color: rgba(0, 0, 0, 0.15); +} + +:root { + /* Color scheme for holiday-related elements */ + --color-user-event: rgba(139, 195, 74, 1); + /* Holiday colors */ + --color-holiday-indicator: rgba(0, 0, 0, 0.15); + --color-holiday-date: rgba(255, 152, 0, 1); + /* Breakpoints for responsive design */ + --mobile-breakpoint: 700px; + --small-mobile-breakpoint: 480px; + --text-color-primary: rgba(51, 51, 51, 1); + --text-color-secondary: rgba(85, 85, 85, 1); + /* Card styles */ + --card-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + --holiday-card-bg: #f6f2e5; + --holiday-date-color: #d35400; + --indicator-spacing: 8px; + /* Interactive states */ + --hover-bg-color: rgba(0, 0, 0, 0.05); +} +.organizationIndicator { + composes: baseIndicator; + background-color: rgba(82, 172, 255, 0.5); +} + +.legendText { + font-size: 14px; + color: #555555; +} +@media only screen and (max-width: var(--mobile-breakpoint)) { + .list_container, + .eventsLegend { + gap: 4px; + } + + .holidayIndicator, + .organizationIndicator { + width: 16px; + height: 10px; + } + + .holidayText, + .legendText { + font-size: 12px; + } +} +.card_title { + font-size: 16px; + font-weight: 600; + color: var(--text-color-primary); + margin-bottom: 15px; +} + +.card_list { + list-style: none; + padding: 0; + margin: 0; +} + +.card_list_item { + display: flex; + align-items: center; + margin-bottom: 10px; + font-size: 14px; + color: var(--text-color-secondary); +} + +.holiday_date { + font-weight: 500; + margin-right: 10px; + color: var(--holiday-date-color); +} + +.calendar_infocards { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + gap: 20px; + padding: 20px; + background-color: var(--grey-bg-color); +} +.holidays_card, +.events_card { + flex: 1; + padding: 20px; border-radius: 10px; + box-shadow: var(--card-shadow); +} + +.holidays_card { + background-color: var(--holiday-card-bg); +} + +.events_card { + background-color: white; +} + +.legend { + display: flex; + flex-direction: column; + gap: 12px; +} + +.userEvents__color { + composes: baseIndicator; + display: inline-block; + background-color: rgba(139, 195, 74, 1); +} + +.card_list_item:hover { + background-color: var(--hover-bg-color); + transition: background-color 0.2s ease; +} +.card_list_item:focus-visible { + background-color: var(--hover-bg-color); + transition: background-color 0.2s ease; } diff --git a/src/components/EventCalendar/EventCalendar.tsx b/src/components/EventCalendar/EventCalendar.tsx index 575bda391e..ebcf8be942 100644 --- a/src/components/EventCalendar/EventCalendar.tsx +++ b/src/components/EventCalendar/EventCalendar.tsx @@ -1,16 +1,14 @@ import EventListCard from 'components/EventListCard/EventListCard'; import dayjs from 'dayjs'; +import React, { useState, useEffect, useMemo } from 'react'; import Button from 'react-bootstrap/Button'; -import React, { useState, useEffect } from 'react'; import styles from './EventCalendar.module.css'; import { ChevronLeft, ChevronRight } from '@mui/icons-material'; -import CurrentHourIndicator from 'components/CurrentHourIndicator/CurrentHourIndicator'; import { ViewType } from 'screens/OrganizationEvents/OrganizationEvents'; import HolidayCard from '../HolidayCards/HolidayCard'; -import { holidays, hours, months, weekdays } from './constants'; +import { holidays, months, weekdays } from './constants'; import type { InterfaceRecurrenceRule } from 'utils/recurrenceUtils'; import YearlyEventCalender from './YearlyEventCalender'; - interface InterfaceEventListCardProps { userRole?: string; key?: string; @@ -75,7 +73,6 @@ const Calendar: React.FC = ({ ); const [expanded, setExpanded] = useState(-1); const [windowWidth, setWindowWidth] = useState(window.screen.width); - useEffect(() => { function handleResize(): void { setWindowWidth(window.screen.width); @@ -138,9 +135,21 @@ const Calendar: React.FC = ({ } }; - /** - * Moves the calendar view to the next month. - */ + const filteredHolidays = useMemo(() => { + return Array.isArray(holidays) + ? holidays.filter((holiday) => { + if (!holiday.date) { + if (process.env.NODE_ENV !== 'test') { + console.warn(`Holiday "${holiday.name}" has no date specified.`); + } + return false; + } + const holidayMonth = dayjs(holiday.date, 'MM-DD', true).month(); + return holidayMonth === currentMonth; + }) + : []; + }, [holidays, currentMonth]); + const handleNextMonth = (): void => { if (currentMonth === 11) { setCurrentMonth(0); @@ -149,10 +158,6 @@ const Calendar: React.FC = ({ setCurrentMonth(currentMonth + 1); } }; - - /** - * Moves the calendar view to the previous date. - */ const handlePrevDate = (): void => { if (currentDate > 1) { setCurrentDate(currentDate - 1); @@ -193,9 +198,6 @@ const Calendar: React.FC = ({ } }; - /** - * Moves the calendar view to today's date. - */ const handleTodayButton = (): void => { setCurrentYear(today.getFullYear()); setCurrentMonth(today.getMonth()); @@ -264,6 +266,17 @@ const Calendar: React.FC = ({ ); }) || []; + const shouldShowViewMore = useMemo(() => { + return ( + allDayEventsList.length > 2 || + (windowWidth <= 700 && allDayEventsList.length > 0) + ); + }, [allDayEventsList.length, windowWidth]); + + const handleExpandClick: () => void = () => { + toggleExpand(-100); + }; + return ( <>
@@ -284,7 +297,6 @@ const Calendar: React.FC = ({ ? styles.expand_list_container : styles.list_container } - style={{ width: 'fit-content' }} >
= ({ : styles.event_list_hour } > - {expanded === -100 - ? allDayEventsList - : allDayEventsList?.slice(0, 1)} + {Array.isArray(allDayEventsList) && + allDayEventsList.length > 0 ? ( + expanded === -100 ? ( + allDayEventsList + ) : ( + allDayEventsList.slice(0, 1) + ) + ) : ( +

+ No events available +

+ )}
- {(allDayEventsList?.length > 2 || - (windowWidth <= 700 && allDayEventsList?.length > 0)) && ( + {Array.isArray(allDayEventsList) && ( )}
- {hours.map((hour, index) => { - const timeEventsList: JSX.Element[] = - events - ?.filter((datas) => { - const currDate = new Date( - currentYear, - currentMonth, - currentDate, - ); - - if ( - parseInt(datas.startTime?.slice(0, 2) as string).toString() == - (index % 24).toString() && - datas.startDate == dayjs(currDate).format('YYYY-MM-DD') - ) { - return datas; - } - }) - .map((datas: InterfaceEventListCardProps) => { - const attendees: { _id: string }[] = []; - datas.attendees?.forEach((attendee: { _id: string }) => { - const r = { - _id: attendee._id, - }; - - attendees.push(r); - }); - - return ( - - ); - }) || []; - return ( -
-
-

{`${hour}`}

+
+
+

Holidays

+
    + {filteredHolidays.map((holiday, index) => ( +
  • + + {months[parseInt(holiday.date.slice(0, 2), 10) - 1]}{' '} + {holiday.date.slice(3)} + + {holiday.name} +
  • + ))} +
+
+ +
+

Events

+
+
+ + Holidays
-
-
0 - ? styles.event_list_parent_current - : styles.event_list_parent - } - > - {index % 24 == new Date().getHours() && - new Date().getDate() == currentDate && ( - - )} -
-
- {} - {expanded === index - ? timeEventsList - : timeEventsList?.slice(0, 1)} -
- {(timeEventsList?.length > 1 || - (windowWidth <= 700 && timeEventsList?.length > 0)) && ( - - )} -
+
+ + + Events Created by Organization + +
+
+ + + Events Created by User +
- ); - })} +
+
); }; @@ -448,10 +407,10 @@ const Calendar: React.FC = ({ return days.map((date, index) => { const className = [ date.getDay() === 0 || date.getDay() === 6 ? styles.day_weekends : '', - date.toLocaleDateString() === today.toLocaleDateString() //Styling for today day cell + date.toLocaleDateString() === today.toLocaleDateString() ? styles.day__today : '', - date.getMonth() !== currentMonth ? styles.day__outside : '', //Styling for days outside the current month + date.getMonth() !== currentMonth ? styles.day__outside : '', selectedDate?.getTime() === date.getTime() ? styles.day__selected : '', styles.day, ].join(' '); @@ -504,13 +463,12 @@ const Calendar: React.FC = ({ ); }) || []; - const holidayList: JSX.Element[] = holidays - .filter((holiday) => { - if (holiday.date == dayjs(date).format('MM-DD')) return holiday; - }) + const holidayList: JSX.Element[] = filteredHolidays + .filter((holiday) => holiday.date === dayjs(date).format('MM-DD')) .map((holiday) => { return ; }); + return (
= ({ )}
{viewType == ViewType.MONTH ? ( -
+ <>
{weekdays.map((weekday, index) => (
@@ -611,18 +569,14 @@ const Calendar: React.FC = ({ ))}
{renderDays()}
-
+ + ) : viewType == ViewType.YEAR ? ( + ) : ( - // -
- {viewType == ViewType.YEAR ? ( - - ) : ( -
{renderHours()}
- )} -
+
{renderHours()}
)}
+
{viewType == ViewType.YEAR ? ( diff --git a/src/screens/UserPortal/Posts/Posts.test.tsx b/src/screens/UserPortal/Posts/Posts.test.tsx index aa5f03fdcf..433e36f94a 100644 --- a/src/screens/UserPortal/Posts/Posts.test.tsx +++ b/src/screens/UserPortal/Posts/Posts.test.tsx @@ -1,7 +1,7 @@ import React, { act } from 'react'; import { MockedProvider } from '@apollo/react-testing'; import type { RenderResult } from '@testing-library/react'; -import { render, screen, waitFor, within } from '@testing-library/react'; +import { render, screen, within } from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; import userEvent from '@testing-library/user-event'; import { @@ -395,5 +395,8 @@ describe('HomeScreen with invalid orgId', () => { ); const homeEl = await screen.findByTestId('homeEl'); expect(homeEl).toBeInTheDocument(); + + const postCardContainers = screen.queryAllByTestId('postCardContainer'); + expect(postCardContainers).toHaveLength(0); }); }); diff --git a/src/setupTests.ts b/src/setupTests.ts index f0b48b39d0..d204b3ddc9 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -32,4 +32,4 @@ jestPreviewConfigure({ autoPreview: true, }); -jest.setTimeout(17000); +jest.setTimeout(18000); From fd0fe5caf89ab653976e2f9be5f3ea4d0d2b0987 Mon Sep 17 00:00:00 2001 From: prathmesh703 <146568950+prathmesh703@users.noreply.github.com> Date: Mon, 23 Dec 2024 07:56:57 +0530 Subject: [PATCH 51/57] Refactor: src/screens/OrganizationFunds from Jest to Vitest (#2685) --- ...{FundModal.test.tsx => FundModal.spec.tsx} | 47 ++++++++++++++++--- src/screens/OrganizationFunds/FundModal.tsx | 8 ++-- ...ds.test.tsx => OrganizationFunds.spec.tsx} | 41 +++++++++++----- 3 files changed, 72 insertions(+), 24 deletions(-) rename src/screens/OrganizationFunds/{FundModal.test.tsx => FundModal.spec.tsx} (85%) rename src/screens/OrganizationFunds/{OrganizationFunds.test.tsx => OrganizationFunds.spec.tsx} (85%) diff --git a/src/screens/OrganizationFunds/FundModal.test.tsx b/src/screens/OrganizationFunds/FundModal.spec.tsx similarity index 85% rename from src/screens/OrganizationFunds/FundModal.test.tsx rename to src/screens/OrganizationFunds/FundModal.spec.tsx index c74b0434c3..7296f31661 100644 --- a/src/screens/OrganizationFunds/FundModal.test.tsx +++ b/src/screens/OrganizationFunds/FundModal.spec.tsx @@ -21,11 +21,12 @@ import { toast } from 'react-toastify'; import { MOCKS, MOCKS_ERROR } from './OrganizationFundsMocks'; import type { InterfaceFundModal } from './FundModal'; import FundModal from './FundModal'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -38,7 +39,7 @@ const translations = JSON.parse( const fundProps: InterfaceFundModal[] = [ { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), fund: { _id: 'fundId', name: 'Fund 1', @@ -54,13 +55,13 @@ const fundProps: InterfaceFundModal[] = [ lastName: 'Doe', }, }, - refetchFunds: jest.fn(), + refetchFunds: vi.fn(), orgId: 'orgId', mode: 'create', }, { isOpen: true, - hide: jest.fn(), + hide: vi.fn(), fund: { _id: 'fundId', name: 'Fund 1', @@ -76,7 +77,7 @@ const fundProps: InterfaceFundModal[] = [ lastName: 'Doe', }, }, - refetchFunds: jest.fn(), + refetchFunds: vi.fn(), orgId: 'orgId', mode: 'edit', }, @@ -104,6 +105,7 @@ const renderFundModal = ( describe('PledgeModal', () => { afterEach(() => { cleanup(); + vi.clearAllMocks(); }); it('should populate form fields with correct values in edit mode', async () => { @@ -184,6 +186,37 @@ describe('PledgeModal', () => { }); }); + it('should not update the fund when no fields are changed', async () => { + renderFundModal(link1, fundProps[1]); + + // Simulate no change to the fields + const fundNameInput = screen.getByLabelText(translations.fundName); + fireEvent.change(fundNameInput, { target: { value: 'Fund 1' } }); + + const fundIdInput = screen.getByLabelText(translations.fundId); + fireEvent.change(fundIdInput, { target: { value: '1111' } }); + + const taxDeductibleSwitch = screen.getByTestId('setTaxDeductibleSwitch'); + fireEvent.click(taxDeductibleSwitch); + fireEvent.click(taxDeductibleSwitch); + + const defaultSwitch = screen.getByTestId('setDefaultSwitch'); + fireEvent.click(defaultSwitch); + fireEvent.click(defaultSwitch); + + const archivedSwitch = screen.getByTestId('archivedSwitch'); + fireEvent.click(archivedSwitch); + fireEvent.click(archivedSwitch); + + fireEvent.click(screen.getByTestId('createFundFormSubmitBtn')); + + await waitFor(() => { + expect(toast.success).not.toHaveBeenCalled(); + expect(fundProps[1].refetchFunds).not.toHaveBeenCalled(); + expect(fundProps[1].hide).not.toHaveBeenCalled(); + }); + }); + it('should update fund', async () => { renderFundModal(link1, fundProps[1]); diff --git a/src/screens/OrganizationFunds/FundModal.tsx b/src/screens/OrganizationFunds/FundModal.tsx index de4329e55c..b33270b30f 100644 --- a/src/screens/OrganizationFunds/FundModal.tsx +++ b/src/screens/OrganizationFunds/FundModal.tsx @@ -139,7 +139,9 @@ const FundModal: React.FC = ({ if (isDefault != fund?.isDefault) { updatedFields.isDefault = isDefault; } - + if (Object.keys(updatedFields).length === 0) { + return; + } await updateFund({ variables: { id: fund?._id, @@ -157,9 +159,7 @@ const FundModal: React.FC = ({ hide(); toast.success(t('fundUpdated') as string); } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - } + toast.error((error as Error).message); } }; diff --git a/src/screens/OrganizationFunds/OrganizationFunds.test.tsx b/src/screens/OrganizationFunds/OrganizationFunds.spec.tsx similarity index 85% rename from src/screens/OrganizationFunds/OrganizationFunds.test.tsx rename to src/screens/OrganizationFunds/OrganizationFunds.spec.tsx index c6983e1d6d..b259b7da65 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.test.tsx +++ b/src/screens/OrganizationFunds/OrganizationFunds.spec.tsx @@ -11,7 +11,7 @@ import { import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; -import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { MemoryRouter, Route, Routes, useParams } from 'react-router-dom'; import { store } from 'state/store'; import { StaticMockLink } from 'utils/StaticMockLink'; import i18nForTest from 'utils/i18nForTest'; @@ -20,11 +20,12 @@ import { MOCKS, MOCKS_ERROR, NO_FUNDS } from './OrganizationFundsMocks'; import type { ApolloLink } from '@apollo/client'; import { LocalizationProvider } from '@mui/x-date-pickers'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { vi } from 'vitest'; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); @@ -67,21 +68,22 @@ const renderOrganizationFunds = (link: ApolloLink): RenderResult => { describe('OrganizationFunds Screen =>', () => { beforeEach(() => { - jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ orgId: 'orgId' }), - })); - }); - - afterEach(() => { - jest.clearAllMocks(); + vi.mock('react-router-dom', async () => { + const actual = await vi.importActual('react-router-dom'); + return { + ...actual, + useParams: vi.fn(), + }; + }); }); afterEach(() => { + vi.clearAllMocks(); cleanup(); }); it('should render the Campaign Pledge screen', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); await waitFor(() => { expect(screen.getByTestId('searchByName')).toBeInTheDocument(); @@ -89,29 +91,34 @@ describe('OrganizationFunds Screen =>', () => { }); it('should redirect to fallback URL if URL params are undefined', async () => { + vi.mocked(useParams).mockReturnValue({}); render( - } />
} /> + } /> , ); + await waitFor(() => { + expect(window.location.pathname).toBe('/'); + }); await waitFor(() => { expect(screen.getByTestId('paramsError')).toBeInTheDocument(); }); }); it('open and close Create Fund modal', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); const createFundBtn = await screen.findByTestId('createFundBtn'); @@ -128,6 +135,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('open and close update fund modal', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); await waitFor(() => { @@ -150,6 +158,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('Search the Funds list by name', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); const searchField = await screen.findByTestId('searchByName'); fireEvent.change(searchField, { @@ -163,6 +172,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('should render the Fund screen with error', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link2); await waitFor(() => { expect(screen.getByTestId('errorMsg')).toBeInTheDocument(); @@ -170,6 +180,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('renders the empty fund component', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link3); await waitFor(() => expect(screen.getByText(translations.noFundsFound)).toBeInTheDocument(), @@ -177,6 +188,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('Sort the Pledges list by Latest created Date', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); const sortBtn = await screen.findByTestId('filter'); @@ -198,6 +210,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('Sort the Pledges list by Earliest created Date', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); const sortBtn = await screen.findByTestId('filter'); @@ -219,6 +232,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('Click on Fund Name', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); const fundName = await screen.findAllByTestId('fundName'); @@ -231,6 +245,7 @@ describe('OrganizationFunds Screen =>', () => { }); it('Click on View Campaign', async () => { + vi.mocked(useParams).mockReturnValue({ orgId: 'orgId' }); renderOrganizationFunds(link1); const viewBtn = await screen.findAllByTestId('viewBtn'); From ea264ce6a2c8dc3e2f997c9f3a041018fab57955 Mon Sep 17 00:00:00 2001 From: Shiva <148421597+shivasankaran18@users.noreply.github.com> Date: Mon, 23 Dec 2024 22:38:31 +0530 Subject: [PATCH 52/57] Refactor : Vitest to SidePanel Component (#2735) * refactor:vitest to Sidepanel * ts-doc comments --- .../SidePanel/{SidePanel.test.tsx => SidePanel.spec.tsx} | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) rename src/components/AddOn/support/components/SidePanel/{SidePanel.test.tsx => SidePanel.spec.tsx} (82%) diff --git a/src/components/AddOn/support/components/SidePanel/SidePanel.test.tsx b/src/components/AddOn/support/components/SidePanel/SidePanel.spec.tsx similarity index 82% rename from src/components/AddOn/support/components/SidePanel/SidePanel.test.tsx rename to src/components/AddOn/support/components/SidePanel/SidePanel.spec.tsx index d929278d0e..4a5f4e5692 100644 --- a/src/components/AddOn/support/components/SidePanel/SidePanel.test.tsx +++ b/src/components/AddOn/support/components/SidePanel/SidePanel.spec.tsx @@ -11,11 +11,16 @@ const client: ApolloClient = new ApolloClient({ }); describe('Testing Contribution Stats', () => { + /** + * Props to be passed to the `SidePanel` component during the test. + */ const props = { collapse: true, children: '234', }; - + /** + * Verifies that the `SidePanel` component renders correctly with given props. + */ test('should render props and text elements test for the SidePanel component', () => { render( From 23a5f6300ab5e6403ab8347b52347d8c917b2ea9 Mon Sep 17 00:00:00 2001 From: Shiva <148421597+shivasankaran18@users.noreply.github.com> Date: Mon, 23 Dec 2024 22:42:45 +0530 Subject: [PATCH 53/57] refactor:vitest to checkInModal (#2734) --- .../{CheckInModal.test.tsx => CheckInModal.spec.tsx} | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) rename src/components/CheckIn/{CheckInModal.test.tsx => CheckInModal.spec.tsx} (89%) diff --git a/src/components/CheckIn/CheckInModal.test.tsx b/src/components/CheckIn/CheckInModal.spec.tsx similarity index 89% rename from src/components/CheckIn/CheckInModal.test.tsx rename to src/components/CheckIn/CheckInModal.spec.tsx index 1660c7c4bb..4f5a05328e 100644 --- a/src/components/CheckIn/CheckInModal.test.tsx +++ b/src/components/CheckIn/CheckInModal.spec.tsx @@ -12,6 +12,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { checkInQueryMock } from './mocks'; import { StaticMockLink } from 'utils/StaticMockLink'; +import { vi } from 'vitest'; const link = new StaticMockLink(checkInQueryMock, true); @@ -19,9 +20,14 @@ describe('Testing Check In Attendees Modal', () => { const props = { show: true, eventId: 'event123', - handleClose: jest.fn(), + handleClose: vi.fn(), }; + /** + * Test case for rendering the CheckInModal component and verifying functionality. + * It checks that the modal renders fetched users and verifies the filtering mechanism. + */ + test('The modal should be rendered, and all the fetched users should be shown properly and user filtering should work', async () => { const { queryByText, queryByLabelText } = render( From 86d59c211a7d914120f54051f2e9e9708b1a46dc Mon Sep 17 00:00:00 2001 From: MANDEEP N H <146331633+mandeepnh5@users.noreply.github.com> Date: Mon, 23 Dec 2024 22:54:52 +0530 Subject: [PATCH 54/57] Refactoring CSS files: merged FundCompaignPledge styles into global app.module.css (#2706) * Update fundcomapignpledge * deleted fundcompaignpledge local css file * Fix linting issues * fixed type check * Update src/style/app.module.css Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fixed some coderabbit issue * Fixed testing issue * Update src/style/app.module.css Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Fix coderabbit suggestion --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../FundCampaignPledge.module.css | 273 ------------------ .../FundCampaignPledge/FundCampaignPledge.tsx | 28 +- .../FundCampaignPledge/PledgeDeleteModal.tsx | 2 +- .../FundCampaignPledge/PledgeModal.tsx | 12 +- src/style/app.module.css | 223 ++++++++++++++ 5 files changed, 244 insertions(+), 294 deletions(-) delete mode 100644 src/screens/FundCampaignPledge/FundCampaignPledge.module.css diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css deleted file mode 100644 index cdf4476267..0000000000 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css +++ /dev/null @@ -1,273 +0,0 @@ -.pledgeContainer { - margin: 0.6rem 0; -} - -.container { - min-height: 100vh; -} - -.pledgeModal { - max-width: 80vw; - margin-top: 2vh; - margin-left: 13vw; -} - -.titlemodal { - color: #707070; - font-weight: 600; - font-size: 32px; - width: 65%; - margin-bottom: 0px; -} - -.modalCloseBtn { - width: 40px; - height: 40px; - padding: 1rem; - display: flex; - justify-content: center; - align-items: center; -} - -.greenregbtn { - margin: 1rem 0 0; - margin-top: 15px; - border: 1px solid #e8e5e5; - box-shadow: 0 2px 2px #e8e5e5; - padding: 10px 10px; - border-radius: 5px; - background-color: #31bb6b; - width: 100%; - font-size: 16px; - color: white; - outline: none; - font-weight: 600; - cursor: pointer; - transition: - transform 0.2s, - box-shadow 0.2s; - width: 100%; -} -.message { - margin-top: 25%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.errorIcon { - transform: scale(1.5); - color: var(--bs-danger); - margin-bottom: 1rem; -} - -.btnsContainer { - display: flex; - gap: 0.8rem; - margin: 2.2rem 0 0.8rem 0; -} - -.btnsContainer .input { - flex: 1; - min-width: 18rem; - position: relative; -} - -.btnsContainer input { - outline: 1px solid var(--bs-gray-400); -} - -.btnsContainer .input button { - width: 52px; -} - -.inputField { - background-color: white; - box-shadow: 0 1px 1px #31bb6b; -} - -.dropdown { - background-color: white; - border: 1px solid #31bb6b; - position: relative; - display: inline-block; - color: #31bb6b; -} - -.tableHeader { - background-color: var(--bs-primary); - color: var(--bs-white); - font-size: 1rem; -} - -.rowBackground { - background-color: var(--bs-white); - max-height: 120px; -} - -.TableImage { - object-fit: cover; - width: 25px !important; - height: 25px !important; - border-radius: 100% !important; -} - -.avatarContainer { - width: 28px; - height: 26px; -} - -.imageContainer { - display: flex; - align-items: center; - justify-content: center; -} - -.pledgerContainer { - display: flex; - align-items: center; - justify-content: center; - margin: 0.1rem 0.25rem; - gap: 0.25rem; - padding: 0.25rem 0.45rem; - border-radius: 0.35rem; - background-color: #31bb6b33; - height: 2.2rem; - margin-top: 0.75rem; -} - -.noOutline input { - outline: none; -} - -.overviewContainer { - display: flex; - gap: 7rem; - width: 100%; - justify-content: space-between; - margin: 1.5rem 0 0 0; - padding: 1.25rem 2rem; - background-color: rgba(255, 255, 255, 0.591); - - box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; - border-radius: 0.5rem; -} - -.titleContainer { - display: flex; - flex-direction: column; - gap: 0.6rem; -} - -.titleContainer h3 { - font-size: 1.75rem; - font-weight: 750; - color: #5e5e5e; - margin-top: 0.2rem; -} - -.titleContainer span { - font-size: 0.9rem; - margin-left: 0.5rem; - font-weight: lighter; - color: #707070; -} - -.raisedAmount { - display: flex; - justify-content: center; - align-items: center; - font-size: 1.25rem; - font-weight: 750; - color: #5e5e5e; -} - -.progressContainer { - display: flex; - flex-direction: column; - gap: 0.5rem; - flex-grow: 1; -} - -.progress { - margin-top: 0.2rem; - display: flex; - flex-direction: column; - gap: 0.3rem; -} - -.endpoints { - display: flex; - position: relative; - font-size: 0.85rem; -} - -.start { - position: absolute; - top: 0px; -} - -.end { - position: absolute; - top: 0px; - right: 0px; -} - -.moreContainer { - display: flex; - align-items: center; -} - -.moreContainer:hover { - text-decoration: underline; - cursor: pointer; -} - -.popup { - z-index: 50; - border-radius: 0.5rem; - font-family: sans-serif; - font-weight: 500; - font-size: 0.875rem; - margin-top: 0.5rem; - padding: 0.75rem; - border: 1px solid #e2e8f0; - background-color: white; - color: #1e293b; - box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 0.15); - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.popupExtra { - max-height: 15rem; - overflow-y: auto; -} - -.toggleGroup { - width: 50%; - min-width: 27.75rem; - margin: 0.5rem 0rem; -} - -.toggleBtn { - padding: 0rem; - height: 30px; - display: flex; - justify-content: center; - align-items: center; -} - -.toggleBtn:hover { - color: #31bb6b !important; -} - -input[type='radio']:checked + label { - background-color: #31bb6a50 !important; -} - -input[type='radio']:checked + label:hover { - color: black !important; -} diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index d14ee9de06..8942265eea 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -9,7 +9,7 @@ import { Button, Dropdown, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { Navigate, useParams } from 'react-router-dom'; import { currencySymbols } from 'utils/currency'; -import styles from './FundCampaignPledge.module.css'; +import styles from '../../style/app.module.css'; import PledgeDeleteModal from './PledgeDeleteModal'; import PledgeModal from './PledgeModal'; import { Breadcrumbs, Link, Stack, Typography } from '@mui/material'; @@ -233,14 +233,14 @@ const fundCampaignPledge = (): JSX.Element => { src={user.image} alt="pledge" data-testid={`image${index + 1}`} - className={styles.TableImage} + className={styles.TableImagePledge} /> ) : (
@@ -425,14 +425,14 @@ const fundCampaignPledge = (): JSX.Element => { > setProgressIndicator('pledged')} />
-
-
+
+
setSearchTerm(e.target.value)} data-testid="searchPledger" @@ -557,7 +557,7 @@ const fundCampaignPledge = (): JSX.Element => { ), }} sx={dataGridStyle} - getRowClassName={() => `${styles.rowBackground}`} + getRowClassName={() => `${styles.rowBackgroundPledge}`} autoHeight rowHeight={65} rows={pledges.map((pledge) => ({ @@ -607,14 +607,14 @@ const fundCampaignPledge = (): JSX.Element => { src={user.image} alt="pledger" data-testid={`extraImage${index + 1}`} - className={styles.TableImage} + className={styles.TableImagePledge} /> ) : (
= ({ = ({ format="DD/MM/YYYY" label={tCommon('startDate')} value={dayjs(pledgeStartDate)} - className={styles.noOutline} + className={styles.noOutlinePledge} onChange={(date: Dayjs | null): void => { if (date) { setFormState({ @@ -280,7 +280,7 @@ const PledgeModal: React.FC = ({ { if (date) { @@ -327,7 +327,7 @@ const PledgeModal: React.FC = ({ { if (parseInt(e.target.value) > 0) { @@ -343,7 +343,7 @@ const PledgeModal: React.FC = ({ {/* Button to submit the pledge form */} + + { + setShowModal(false); + setVerificationStep(false); + setEmail(''); + setError(''); + }} + > + + + Leave Joined Organization + + + + {!verificationStep ? ( + <> +

Are you sure you want to leave this organization?

+

+ This action cannot be undone, and you may need to request access + again if you reconsider. +

+ + ) : ( +
+ + + Enter your email to confirm: + + setEmail(e.target.value)} + onKeyDown={handleKeyPress} + aria-label="confirm-email-input" + /> + + {error && ( + + {error} + + )} +
+ )} +
+ + {!verificationStep ? ( + <> + + + + ) : ( + <> + + + + )} + +
+
+ ); +}; + +export default LeaveOrganization; diff --git a/src/screens/UserPortal/UserScreen/UserScreen.tsx b/src/screens/UserPortal/UserScreen/UserScreen.tsx index bcb1d867f3..39b422858f 100644 --- a/src/screens/UserPortal/UserScreen/UserScreen.tsx +++ b/src/screens/UserPortal/UserScreen/UserScreen.tsx @@ -20,6 +20,7 @@ const map: InterfaceMapType = { campaigns: 'userCampaigns', pledges: 'userPledges', volunteer: 'userVolunteer', + leaveorg: 'leaveOrganization', }; /** diff --git a/src/state/reducers/userRoutersReducer.test.ts b/src/state/reducers/userRoutersReducer.test.ts index e2987dcd38..67f075fd44 100644 --- a/src/state/reducers/userRoutersReducer.test.ts +++ b/src/state/reducers/userRoutersReducer.test.ts @@ -18,6 +18,7 @@ describe('Testing Routes reducer', () => { { name: 'Donate', url: 'user/donate/undefined' }, { name: 'Campaigns', url: 'user/campaigns/undefined' }, { name: 'My Pledges', url: 'user/pledges/undefined' }, + { name: 'Leave Organization', url: 'user/leaveorg/undefined' }, ], components: [ { @@ -44,6 +45,11 @@ describe('Testing Routes reducer', () => { component: 'Campaigns', }, { name: 'My Pledges', comp_id: 'pledges', component: 'Pledges' }, + { + name: 'Leave Organization', + comp_id: 'leaveorg', + component: 'LeaveOrganization', + }, ], }); }); @@ -64,6 +70,7 @@ describe('Testing Routes reducer', () => { { name: 'Donate', url: 'user/donate/orgId' }, { name: 'Campaigns', url: 'user/campaigns/orgId' }, { name: 'My Pledges', url: 'user/pledges/orgId' }, + { name: 'Leave Organization', url: 'user/leaveorg/orgId' }, ], components: [ { @@ -90,6 +97,11 @@ describe('Testing Routes reducer', () => { component: 'Campaigns', }, { name: 'My Pledges', comp_id: 'pledges', component: 'Pledges' }, + { + name: 'Leave Organization', + comp_id: 'leaveorg', + component: 'LeaveOrganization', + }, ], }); }); diff --git a/src/state/reducers/userRoutesReducer.ts b/src/state/reducers/userRoutesReducer.ts index e1bf5de0dc..3e99c51b3f 100644 --- a/src/state/reducers/userRoutesReducer.ts +++ b/src/state/reducers/userRoutesReducer.ts @@ -63,6 +63,11 @@ const components: ComponentType[] = [ component: 'Campaigns', }, { name: 'My Pledges', comp_id: 'pledges', component: 'Pledges' }, + { + name: 'Leave Organization', + comp_id: 'leaveorg', + component: 'LeaveOrganization', + }, ]; const generateRoutes = ( From df2dd67df95bef25398831b5fe13b58f2f68a10a Mon Sep 17 00:00:00 2001 From: Ramneet Singh <144323012+Ramneet04@users.noreply.github.com> Date: Tue, 24 Dec 2024 00:59:59 +0530 Subject: [PATCH 57/57] Refactor : src/components/AgendaItems/AgendaItemsCreateModal from Jest to Vitest #2489 (#2727) * file name changed * migrate jest to vitest * changes done * formated the code * fixed the type error * removed @testing-library/jest-dom * @testing-library/jest-dom removed --- ...est.tsx => AgendaItemsCreateModal.spec.tsx} | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) rename src/components/AgendaItems/{AgendaItemsCreateModal.test.tsx => AgendaItemsCreateModal.spec.tsx} (96%) diff --git a/src/components/AgendaItems/AgendaItemsCreateModal.test.tsx b/src/components/AgendaItems/AgendaItemsCreateModal.spec.tsx similarity index 96% rename from src/components/AgendaItems/AgendaItemsCreateModal.test.tsx rename to src/components/AgendaItems/AgendaItemsCreateModal.spec.tsx index 5b7339ad67..35ea8c7681 100644 --- a/src/components/AgendaItems/AgendaItemsCreateModal.test.tsx +++ b/src/components/AgendaItems/AgendaItemsCreateModal.spec.tsx @@ -19,6 +19,8 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import AgendaItemsCreateModal from './AgendaItemsCreateModal'; import { toast } from 'react-toastify'; import convertToBase64 from 'utils/convertToBase64'; +import type { MockedFunction } from 'vitest'; +import { describe, test, expect, vi } from 'vitest'; const mockFormState = { title: 'Test Title', @@ -28,9 +30,9 @@ const mockFormState = { urls: ['https://example.com'], agendaItemCategoryIds: ['category'], }; -const mockHideCreateModal = jest.fn(); -const mockSetFormState = jest.fn(); -const mockCreateAgendaItemHandler = jest.fn(); +const mockHideCreateModal = vi.fn(); +const mockSetFormState = vi.fn(); +const mockCreateAgendaItemHandler = vi.fn(); const mockT = (key: string): string => key; const mockAgendaItemCategories = [ { @@ -64,14 +66,14 @@ const mockAgendaItemCategories = [ }, }, ]; -jest.mock('react-toastify', () => ({ +vi.mock('react-toastify', () => ({ toast: { - success: jest.fn(), - error: jest.fn(), + success: vi.fn(), + error: vi.fn(), }, })); -jest.mock('utils/convertToBase64'); -const mockedConvertToBase64 = convertToBase64 as jest.MockedFunction< +vi.mock('utils/convertToBase64'); +const mockedConvertToBase64 = convertToBase64 as MockedFunction< typeof convertToBase64 >;