+
{tCommon(name || '')}
diff --git a/src/screens/OrgPost/OrgPost.spec.tsx b/src/screens/OrgPost/OrgPost.spec.tsx
index cfe2d8c9bf..60a2734ac3 100644
--- a/src/screens/OrgPost/OrgPost.spec.tsx
+++ b/src/screens/OrgPost/OrgPost.spec.tsx
@@ -1,5 +1,5 @@
import { MockedProvider } from '@apollo/react-testing';
-import { fireEvent, render, screen } from '@testing-library/react';
+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';
@@ -12,7 +12,7 @@ import { store } from 'state/store';
import { StaticMockLink } from 'utils/StaticMockLink';
import i18nForTest from 'utils/i18nForTest';
import OrgPost from './OrgPost';
-import { vi } from 'vitest';
+import { describe, it, expect, beforeEach, vi } from 'vitest';
const MOCKS = [
{
request: {
@@ -47,9 +47,9 @@ const MOCKS = [
},
likeCount: 0,
commentCount: 0,
- comments: [],
pinned: true,
likedBy: [],
+ comments: [],
},
cursor: '6411e53835d7ba2344a78e21',
},
@@ -178,7 +178,125 @@ async function wait(ms = 500): Promise
{
});
}
+const initialMock = {
+ request: {
+ query: ORGANIZATION_POST_LIST,
+ variables: {
+ id: undefined,
+ after: null,
+ before: null,
+ first: 6,
+ last: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ posts: {
+ edges: [],
+ pageInfo: {
+ startCursor: 'startCursor1',
+ endCursor: 'endCursor1',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 10,
+ },
+ },
+ ],
+ },
+ },
+};
+
+const nextPageMock = {
+ request: {
+ query: ORGANIZATION_POST_LIST,
+ variables: {
+ id: undefined,
+ after: 'endCursor1',
+ before: null,
+ first: 6,
+ last: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ posts: {
+ edges: [],
+ pageInfo: {
+ startCursor: 'startCursor2',
+ endCursor: 'endCursor2',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 10,
+ },
+ },
+ ],
+ },
+ },
+};
+
+const prevPageMock = {
+ request: {
+ query: ORGANIZATION_POST_LIST,
+ variables: {
+ id: undefined,
+ after: null,
+ before: 'startCursor2',
+ first: null,
+ last: 6,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ posts: {
+ edges: [],
+ pageInfo: {
+ startCursor: 'startCursor1',
+ endCursor: 'endCursor1',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 10,
+ },
+ },
+ ],
+ },
+ },
+};
+
+const successMock = {
+ request: {
+ query: CREATE_POST_MUTATION,
+ variables: {
+ title: 'Test Post',
+ text: 'Test Content',
+ file: '',
+ pinned: false,
+ },
+ },
+ result: {
+ data: {
+ createPost: {
+ _id: '123',
+ title: 'Test Post',
+ text: 'Test Content',
+ },
+ },
+ },
+};
+
describe('Organisation Post Page', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
const formData = {
posttitle: 'dummy post',
postinfo: 'This is a dummy post',
@@ -186,7 +304,121 @@ describe('Organisation Post Page', () => {
postVideo: new File(['hello'], 'hello.mp4', { type: 'video/mp4' }),
};
- test('correct mock data should be queried', async () => {
+ it('handleAddMediaChange: uploading a file and verifying the preview', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('createPostModalBtn'));
+
+ const fileInput = screen.getByTestId('addMediaField');
+ userEvent.upload(fileInput, formData.postImage);
+
+ const imagePreview = await screen.findByAltText('Post Image Preview');
+ expect(imagePreview).toBeInTheDocument();
+
+ const closeButton = screen.getByTestId('mediaCloseButton');
+ fireEvent.click(closeButton);
+ expect(imagePreview).not.toBeInTheDocument();
+ });
+
+ it('handleSearch: searching by title and text', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const searchInput = screen.getByPlaceholderText(/Search By/i);
+ userEvent.type(searchInput, 'postone{enter}');
+ expect(searchInput).toHaveValue('postone');
+
+ const sortDropdown = screen.getByTestId('sort');
+ userEvent.click(sortDropdown);
+ });
+
+ it('createPost: creating a post with and without media, and error handling', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('createPostModalBtn'));
+
+ const postTitleInput = screen.getByTestId('modalTitle');
+ fireEvent.change(postTitleInput, { target: { value: 'Test Post' } });
+
+ const postInfoTextarea = screen.getByTestId('modalinfo');
+ fireEvent.change(postInfoTextarea, {
+ target: { value: 'Test post information' },
+ });
+
+ const createPostBtn = screen.getByTestId('createPostBtn');
+ fireEvent.click(createPostBtn);
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('closeOrganizationModal'));
+ });
+
+ it('Modal interactions: opening and closing the modal', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const createPostModalBtn = screen.getByTestId('createPostModalBtn');
+ userEvent.click(createPostModalBtn);
+
+ const modalTitle = screen.getByTestId('modalOrganizationHeader');
+ expect(modalTitle).toBeInTheDocument();
+
+ const closeButton = screen.getByTestId(/modalOrganizationHeader/i);
+ userEvent.click(closeButton);
+
+ await wait();
+
+ const closedModalTitle = screen.queryByText(/postDetail/i);
+ expect(closedModalTitle).not.toBeInTheDocument();
+ });
+
+ it('correct mock data should be queried', async () => {
const dataQuery1 = MOCKS[0]?.result?.data?.organizations[0].posts.edges[0];
expect(dataQuery1).toEqual({
@@ -213,7 +445,7 @@ describe('Organisation Post Page', () => {
});
});
- test('Testing create post functionality', async () => {
+ it('Testing create post functionality', async () => {
render(
@@ -247,7 +479,7 @@ describe('Organisation Post Page', () => {
userEvent.click(screen.getByTestId('closeOrganizationModal'));
}, 15000);
- test('Testing search functionality', async () => {
+ it('Testing search functionality', async () => {
render(
@@ -272,9 +504,9 @@ describe('Organisation Post Page', () => {
const sortDropdown = screen.getByTestId('sort');
userEvent.click(sortDropdown);
});
- test('Testing search text and title toggle', async () => {
+
+ it('Testing search text and title toggle', async () => {
await act(async () => {
- // Wrap the test code in act
render(
@@ -315,9 +547,9 @@ describe('Organisation Post Page', () => {
expect(searchInput).toHaveAttribute('placeholder', 'Search By Title');
});
- test('Testing search latest and oldest toggle', async () => {
+
+ it('Testing search latest and oldest toggle', async () => {
await act(async () => {
- // Wrap the test code in act
render(
@@ -358,11 +590,12 @@ describe('Organisation Post Page', () => {
});
expect(searchInput).toBeInTheDocument();
});
- test('After creating a post, the data should be refetched', async () => {
+
+ it('After creating a post, the data should be refetched', async () => {
const refetchMock = vi.fn();
render(
-
+
@@ -378,8 +611,6 @@ describe('Organisation Post Page', () => {
userEvent.click(screen.getByTestId('createPostModalBtn'));
- // Fill in post form fields...
-
userEvent.click(screen.getByTestId('createPostBtn'));
await wait();
@@ -387,7 +618,7 @@ describe('Organisation Post Page', () => {
expect(refetchMock).toHaveBeenCalledTimes(0);
});
- test('Create post without media', async () => {
+ it('Create post without media', async () => {
render(
@@ -416,80 +647,6 @@ describe('Organisation Post Page', () => {
fireEvent.click(createPostBtn);
}, 15000);
- test('Create post and preview', async () => {
- render(
-
-
-
-
-
-
-
-
-
- ,
- );
-
- await wait();
- userEvent.click(screen.getByTestId('createPostModalBtn'));
-
- const postTitleInput = screen.getByTestId('modalTitle');
- fireEvent.change(postTitleInput, { target: { value: 'Test Post' } });
-
- const postInfoTextarea = screen.getByTestId('modalinfo');
- fireEvent.change(postInfoTextarea, {
- target: { value: 'Test post information' },
- });
-
- // Simulate uploading an image
- const imageFile = new File(['image content'], 'image.png', {
- type: 'image/png',
- });
- const imageInput = screen.getByTestId('addMediaField');
- userEvent.upload(imageInput, imageFile);
-
- // Check if the image is displayed
- const imagePreview = await screen.findByAltText('Post Image Preview');
- expect(imagePreview).toBeInTheDocument();
-
- // Check if the close button for the image works
- const closeButton = screen.getByTestId('mediaCloseButton');
- fireEvent.click(closeButton);
-
- // Check if the image is removed from the preview
- expect(imagePreview).not.toBeInTheDocument();
- }, 15000);
-
- test('Modal opens and closes', async () => {
- render(
-
-
-
-
-
-
-
-
- ,
- );
-
- await wait();
-
- const createPostModalBtn = screen.getByTestId('createPostModalBtn');
-
- userEvent.click(createPostModalBtn);
-
- const modalTitle = screen.getByTestId('modalOrganizationHeader');
- expect(modalTitle).toBeInTheDocument();
-
- const closeButton = screen.getByTestId(/modalOrganizationHeader/i);
- userEvent.click(closeButton);
-
- await wait();
-
- const closedModalTitle = screen.queryByText(/postDetail/i);
- expect(closedModalTitle).not.toBeInTheDocument();
- });
it('renders the form with input fields and buttons', async () => {
render(
@@ -507,7 +664,6 @@ describe('Organisation Post Page', () => {
await wait();
userEvent.click(screen.getByTestId('createPostModalBtn'));
- // Check if input fields and buttons are present
expect(screen.getByTestId('modalTitle')).toBeInTheDocument();
expect(screen.getByTestId('modalinfo')).toBeInTheDocument();
expect(screen.getByTestId('createPostBtn')).toBeInTheDocument();
@@ -530,7 +686,6 @@ describe('Organisation Post Page', () => {
await wait();
userEvent.click(screen.getByTestId('createPostModalBtn'));
- // Simulate user input
fireEvent.change(screen.getByTestId('modalTitle'), {
target: { value: 'Test Title' },
});
@@ -538,12 +693,11 @@ describe('Organisation Post Page', () => {
target: { value: 'Test Info' },
});
- // Check if input values are set correctly
expect(screen.getByTestId('modalTitle')).toHaveValue('Test Title');
expect(screen.getByTestId('modalinfo')).toHaveValue('Test Info');
});
- test('allows users to upload an image', async () => {
+ it('allows users to upload an image', async () => {
render(
@@ -579,7 +733,8 @@ describe('Organisation Post Page', () => {
const closeButton = screen.getByTestId('mediaCloseButton');
fireEvent.click(closeButton);
}, 15000);
- test('Create post, preview image, and close preview', async () => {
+
+ it('Create post, preview image, and close preview', async () => {
await act(async () => {
render(
@@ -620,19 +775,17 @@ describe('Organisation Post Page', () => {
userEvent.upload(screen.getByTestId('addMediaField'), videoFile);
});
- // Check if the video is displayed
const videoPreview = await screen.findByTestId('videoPreview');
expect(videoPreview).toBeInTheDocument();
- // Check if the close button for the video works
const closeVideoPreviewButton = screen.getByTestId('mediaCloseButton');
await act(async () => {
fireEvent.click(closeVideoPreviewButton);
});
expect(videoPreview).not.toBeInTheDocument();
});
- test('Sorting posts by pinned status', async () => {
- // Mocked data representing posts with different pinned statuses
+
+ it('Sorting posts by pinned status', async () => {
const mockedPosts = [
{
_id: '1',
@@ -656,7 +809,6 @@ describe('Organisation Post Page', () => {
},
];
- // Render the OrgPost component and pass the mocked data to it
render(
@@ -674,7 +826,6 @@ describe('Organisation Post Page', () => {
const sortedPosts = screen.getAllByTestId('post-item');
- // Assert that the posts are sorted correctly
expect(sortedPosts).toHaveLength(mockedPosts.length);
expect(sortedPosts[0]).toHaveTextContent(
'postoneThis is the first po... Aditya Shelke',
@@ -686,4 +837,331 @@ describe('Organisation Post Page', () => {
'posttwoTis is the post two Aditya Shelke',
);
});
+
+ it('successful post creation should reset form and close modal', async () => {
+ const customMocks = [...MOCKS, successMock];
+ const customLink = new StaticMockLink(customMocks, true);
+
+ render(
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('createPostModalBtn'));
+
+ userEvent.type(screen.getByTestId('modalTitle'), 'Test Post');
+ userEvent.type(screen.getByTestId('modalinfo'), 'Test Content');
+
+ await act(async () => {
+ fireEvent.click(screen.getByTestId('createPostBtn'));
+ });
+
+ await wait();
+
+ expect(
+ screen.queryByTestId('modalOrganizationHeader'),
+ ).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('createPostModalBtn'));
+ expect(screen.getByTestId('modalTitle')).toHaveValue('');
+ expect(screen.getByTestId('modalinfo')).toHaveValue('');
+ expect(screen.getByTestId('pinPost')).not.toBeChecked();
+
+ const toastContainer = screen.getByRole('alert');
+ expect(toastContainer).toBeInTheDocument();
+ });
+
+ it('pagination controls work correctly', async () => {
+ const mockPostEdges =
+ MOCKS[0]?.result?.data?.organizations[0].posts.edges ?? [];
+ const paginationMock = {
+ request: {
+ query: ORGANIZATION_POST_LIST,
+ variables: {
+ id: undefined,
+ after: null,
+ before: null,
+ first: 6,
+ last: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ posts: {
+ edges: mockPostEdges,
+ pageInfo: {
+ startCursor: 'startCursor123',
+ endCursor: 'endCursor123',
+ hasNextPage: true,
+ hasPreviousPage: true,
+ },
+ totalCount: 10,
+ },
+ },
+ ],
+ },
+ },
+ };
+
+ const customMocks = [...MOCKS, paginationMock];
+ const customLink = new StaticMockLink(customMocks, true);
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const nextButton = screen.getByRole('button', { name: /next/i });
+ const previousButton = screen.getByRole('button', { name: /previous/i });
+
+ expect(nextButton).toBeInTheDocument();
+ expect(previousButton).toBeInTheDocument();
+
+ await act(async () => {
+ fireEvent.click(nextButton);
+ });
+ await wait();
+
+ await act(async () => {
+ fireEvent.click(previousButton);
+ });
+ await wait();
+
+ expect(nextButton).toBeInTheDocument();
+ expect(previousButton).toBeInTheDocument();
+ });
+
+ it('pagination buttons states are correctly set', async () => {
+ const mockPostEdges =
+ MOCKS[0]?.result?.data?.organizations[0].posts.edges ?? [];
+ const noPaginationMock = {
+ request: {
+ query: ORGANIZATION_POST_LIST,
+ variables: {
+ id: undefined,
+ after: null,
+ before: null,
+ first: 6,
+ last: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ posts: {
+ edges: mockPostEdges,
+ pageInfo: {
+ startCursor: 'startCursor123',
+ endCursor: 'endCursor123',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 4,
+ },
+ },
+ ],
+ },
+ },
+ };
+
+ const customMocks = [...MOCKS, noPaginationMock];
+ const customLink = new StaticMockLink(customMocks, true);
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const nextButton = screen.getByRole('button', { name: /next/i });
+ const previousButton = screen.getByRole('button', { name: /previous/i });
+
+ expect(nextButton).toHaveAttribute('disabled');
+ expect(previousButton).toHaveAttribute('disabled');
+ });
+
+ it('handleNextPage updates pagination variables correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('nextButton')).not.toBeDisabled();
+ });
+
+ await waitFor(() => {
+ fireEvent.click(screen.getByTestId('previousButton'));
+ });
+
+ await waitFor(() => {
+ fireEvent.click(screen.getByTestId('nextButton'));
+ expect(screen.getByTestId('nextButton')).toBeDisabled();
+ });
+ });
+
+ it('handlePreviousPage updates pagination variables correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('nextButton')).not.toBeDisabled();
+ });
+
+ await act(async () => {
+ fireEvent.click(screen.getByTestId('nextButton'));
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('previousButton')).not.toBeDisabled();
+ });
+
+ await act(async () => {
+ fireEvent.click(screen.getByTestId('previousButton'));
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('previousButton')).toBeDisabled();
+ expect(screen.getByTestId('nextButton')).not.toBeDisabled();
+ });
+ });
+
+ it('handles create post error cases', async () => {
+ const errorMock = {
+ request: {
+ query: CREATE_POST_MUTATION,
+ variables: {
+ title: '',
+ text: '',
+ organizationId: 'undefined',
+ file: '',
+ pinned: false,
+ },
+ },
+ error: new Error('Text fields cannot be empty strings'),
+ };
+
+ render(
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('createPostModalBtn'));
+
+ await act(async () => {
+ fireEvent.click(screen.getByTestId('createPostBtn'));
+ });
+
+ await wait();
+
+ const toastContainer = screen.getByRole('alert');
+ expect(toastContainer).toBeInTheDocument();
+ });
+
+ it('handles empty search input', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const searchInput = screen.getByTestId('searchByName');
+ fireEvent.change(searchInput, { target: { value: '' } });
+
+ await wait();
+
+ expect(searchInput).toHaveValue('');
+ });
+
+ it('handles file input change with no file selected', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('createPostModalBtn'));
+
+ const fileInput = screen.getByTestId('addMediaField');
+ fireEvent.change(fileInput, { target: { files: [] } });
+
+ await wait();
+
+ expect(screen.queryByTestId('mediaPreview')).not.toBeInTheDocument();
+ });
});
diff --git a/src/screens/OrgPost/OrgPost.tsx b/src/screens/OrgPost/OrgPost.tsx
index 857071ca25..2371b9e264 100644
--- a/src/screens/OrgPost/OrgPost.tsx
+++ b/src/screens/OrgPost/OrgPost.tsx
@@ -1,4 +1,5 @@
-import { useMutation, useQuery, type ApolloError } from '@apollo/client';
+import type { ApolloError } from '@apollo/client';
+import { useMutation, useQuery } from '@apollo/client';
import { CREATE_POST_MUTATION } from 'GraphQl/Mutations/mutations';
import { ORGANIZATION_POST_LIST } from 'GraphQl/Queries/Queries';
import Loader from 'components/Loader/Loader';
@@ -383,6 +384,7 @@ function orgPost(): JSX.Element {
!orgPostListData?.organizations[0].posts.pageInfo
.hasPreviousPage
}
+ data-testid="previousButton"
>
{t('Previous')}
@@ -394,6 +396,7 @@ function orgPost(): JSX.Element {
disabled={
!orgPostListData?.organizations[0].posts.pageInfo.hasNextPage
}
+ data-testid="nextButton"
>
{t('Next')}
diff --git a/src/screens/OrganizationActionItems/ItemModal.spec.tsx b/src/screens/OrganizationActionItems/ItemModal.spec.tsx
index a3467d4cf6..044fcc26cb 100644
--- a/src/screens/OrganizationActionItems/ItemModal.spec.tsx
+++ b/src/screens/OrganizationActionItems/ItemModal.spec.tsx
@@ -778,6 +778,142 @@ describe('Testing ItemModal', () => {
});
});
+ it('handles infinite for allottedHours', async () => {
+ renderItemModal(link1, itemProps[0]);
+ const hoursInput = screen.getByLabelText(t.allottedHours);
+
+ // Required field setup for form submission
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ // Test Infinity
+ fireEvent.change(hoursInput, { target: { value: Infinity } });
+ expect(hoursInput).toHaveValue('');
+
+ // Test -Infinity
+ fireEvent.change(hoursInput, { target: { value: -Infinity } });
+ expect(hoursInput).toHaveValue('');
+
+ // Test Number.POSITIVE_INFINITY and Number.NEGATIVE_INFINITY
+ fireEvent.change(hoursInput, {
+ target: { value: Number.POSITIVE_INFINITY },
+ });
+ expect(hoursInput).toHaveValue('');
+
+ fireEvent.change(hoursInput, {
+ target: { value: Number.NEGATIVE_INFINITY },
+ });
+ expect(hoursInput).toHaveValue('');
+ });
+
+ it('should not allow letters or negative values in allotted hours', async () => {
+ renderItemModal(link1, itemProps[0]);
+ const hoursInput = screen.getByLabelText(t.allottedHours);
+ expect(hoursInput).toBeInTheDocument();
+
+ // Test letter input
+ fireEvent.change(hoursInput, { target: { value: 'abc' } });
+ await waitFor(() => {
+ expect(hoursInput).toHaveValue('');
+ });
+
+ // Test negative value
+ fireEvent.change(hoursInput, { target: { value: '-5' } });
+ await waitFor(() => {
+ expect(hoursInput).toHaveValue('');
+ });
+
+ // Test zero as boundary
+ fireEvent.change(hoursInput, { target: { value: '0' } });
+ await waitFor(() => {
+ expect(hoursInput).toHaveValue('0');
+ });
+
+ // Test maximum allowed value
+ fireEvent.change(hoursInput, { target: { value: '999999' } });
+ await waitFor(() => {
+ expect(hoursInput).toHaveValue('999999');
+ });
+ });
+
+ it('validates allotted hours maximum values', async () => {
+ renderItemModal(link1, itemProps[0]);
+ const hoursInput = screen.getByLabelText(t.allottedHours);
+
+ // Test various large values
+ const testCases = [
+ { input: '9007199254740991', expected: '9007199254740991' }, // MAX_SAFE_INTEGER
+ { input: '9007199254740992', expected: '9007199254740992' }, // MAX_SAFE_INTEGER + 1
+ ];
+
+ for (const { input, expected } of testCases) {
+ fireEvent.change(hoursInput, { target: { value: input } });
+ await waitFor(() => {
+ expect(hoursInput).toHaveValue(expected);
+ });
+ }
+
+ // Test that reasonable large values are still accepted
+ const validLargeValues = ['1000', '9999', '99999'];
+ for (const value of validLargeValues) {
+ fireEvent.change(hoursInput, { target: { value } });
+ await waitFor(() => {
+ expect(hoursInput).toHaveValue(value);
+ });
+ }
+ });
+
+ it('validates allottedHours edge cases', async () => {
+ renderItemModal(link1, itemProps[0]);
+ const allottedHours = screen.getByLabelText(t.allottedHours);
+
+ // Test invalid string
+ fireEvent.change(allottedHours, { target: { value: 'invalid' } });
+ expect(allottedHours).toHaveValue('');
+
+ // Test NaN
+ fireEvent.change(allottedHours, { target: { value: NaN } });
+ expect(allottedHours).toHaveValue('');
+
+ // Test negative number
+ fireEvent.change(allottedHours, { target: { value: -5 } });
+ expect(allottedHours).toHaveValue('');
+
+ // Test boundary values
+ fireEvent.change(allottedHours, {
+ target: { value: Number.MAX_SAFE_INTEGER },
+ });
+ expect(allottedHours).toHaveValue('9007199254740991');
+
+ // Test decimal values - according to the component's implementation,
+ // it uses parseInt() so decimals should be truncated
+ fireEvent.change(allottedHours, { target: { value: 5.7 } });
+ expect(allottedHours).toHaveValue('5');
+
+ // Required fields for form submission
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ // Test form submission with valid number
+ fireEvent.change(allottedHours, { target: { value: 10 } });
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ });
+ });
+
it('should fail to Create Action Item', async () => {
renderItemModal(link2, itemProps[0]);
// Click Submit
@@ -790,6 +926,249 @@ describe('Testing ItemModal', () => {
});
});
+ it('handles empty strings in all text fields', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ // Fill required fields first since they're needed for form submission
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ // Test empty strings in optional fields
+ const preCompletionNotes = screen.getByLabelText(t.preCompletionNotes);
+ fireEvent.change(preCompletionNotes, { target: { value: '' } });
+ expect(preCompletionNotes).toHaveValue('');
+
+ const allottedHours = screen.getByLabelText(t.allottedHours);
+ fireEvent.change(allottedHours, { target: { value: '' } });
+ expect(allottedHours).toHaveValue('');
+
+ // Submit form
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ // Verify optional fields remain empty after submission
+ expect(preCompletionNotes).toHaveValue('');
+ expect(allottedHours).toHaveValue('');
+ });
+ });
+
+ it('handles whitespace-only strings', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ // Select Category 1
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ // Select assignee
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ const preCompletionNotes = screen.getByLabelText(t.preCompletionNotes);
+
+ // Test various whitespace combinations
+ fireEvent.change(preCompletionNotes, { target: { value: ' ' } });
+ expect(preCompletionNotes).toHaveValue(' ');
+
+ // Test leading/trailing whitespace
+ fireEvent.change(preCompletionNotes, { target: { value: ' test ' } });
+ expect(preCompletionNotes).toHaveValue(' test ');
+
+ // Test multiple consecutive spaces
+ fireEvent.change(preCompletionNotes, { target: { value: 'test test' } });
+ expect(preCompletionNotes).toHaveValue('test test');
+
+ // Test tab characters
+ fireEvent.change(preCompletionNotes, { target: { value: '\ttest\t' } });
+ expect(preCompletionNotes).toHaveValue('\ttest\t');
+
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ });
+ });
+
+ it('handles special characters in text fields', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ // Select Category 1
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ // Select assignee
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ const preCompletionNotes = screen.getByLabelText(t.preCompletionNotes);
+
+ // Test basic special characters
+ fireEvent.change(preCompletionNotes, {
+ target: { value: '!@#$%^&*()_+-=[]{}|;:,.<>?' },
+ });
+ expect(preCompletionNotes).toHaveValue('!@#$%^&*()_+-=[]{}|;:,.<>?');
+
+ // Test Unicode characters including emojis and international characters
+ fireEvent.change(preCompletionNotes, {
+ target: { value: '🚀 Unicode Test ä½ å¥½ ñ é è ü ö 한글 עברית العربية' },
+ });
+ expect(preCompletionNotes).toHaveValue(
+ '🚀 Unicode Test ä½ å¥½ ñ é è ü ö 한글 עברית العربية',
+ );
+
+ // Test HTML-like content
+ fireEvent.change(preCompletionNotes, {
+ target: { value: 'Test
' },
+ });
+ expect(preCompletionNotes).toHaveValue(
+ 'Test
',
+ );
+
+ // Test SQL-like characters and quotes
+ fireEvent.change(preCompletionNotes, {
+ target: { value: '\'; DROP TABLE users; -- "quoted" text\'s test' },
+ });
+ expect(preCompletionNotes).toHaveValue(
+ '\'; DROP TABLE users; -- "quoted" text\'s test',
+ );
+
+ // Test mathematical and currency symbols
+ fireEvent.change(preCompletionNotes, {
+ target: { value: '∑ π ∆ ∞ € £ ¥ ₹ ± ≠≈ ∴ ∵' },
+ });
+ expect(preCompletionNotes).toHaveValue('∑ π ∆ ∞ € £ ¥ ₹ ± ≠≈ ∴ ∵');
+
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ // Verify the last special character value persists after submission
+ expect(preCompletionNotes).toHaveValue('∑ π ∆ ∞ € £ ¥ ₹ ± ≠≈ ∴ ∵');
+ });
+ });
+
+ it('handles extremely long text input', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ // Select Category
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ // Select assignee
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ const preCompletionNotes = screen.getByLabelText(t.preCompletionNotes);
+
+ // Test various lengths
+ const lengths = [100, 500, 1000, 5000, 10000];
+ for (const length of lengths) {
+ fireEvent.change(preCompletionNotes, {
+ target: { value: 'a'.repeat(length) },
+ });
+ expect(preCompletionNotes).toHaveValue('a'.repeat(length));
+ }
+
+ // Test with long repeated words
+ const longWords = Array(100)
+ .fill('supercalifragilisticexpialidocious')
+ .join(' ');
+ fireEvent.change(preCompletionNotes, {
+ target: { value: longWords },
+ });
+ expect(preCompletionNotes).toHaveValue(longWords);
+
+ // Verify form submission with long text
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ // Verify the long text persists after submission
+ expect(preCompletionNotes).toHaveValue(longWords);
+ });
+ });
+
+ it('handles rapid form field changes', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ // Required fields setup
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ const hoursInput = screen.getByLabelText(t.allottedHours);
+
+ // Test rapid sequential changes
+ const values = Array.from({ length: 100 }, (_, i) => i.toString());
+
+ // Fire all changes in immediate succession
+ values.forEach((value) => {
+ fireEvent.change(hoursInput, { target: { value } });
+ expect(hoursInput).toHaveValue(value);
+ });
+
+ // Test rapid changes with special values interspersed
+ const mixedValues = [
+ '50',
+ '', // Empty value
+ '51',
+ 'abc', // Invalid value
+ '52',
+ '-1', // Negative value
+ '53',
+ '99999', // Large value
+ '54',
+ ];
+
+ mixedValues.forEach((value) => {
+ fireEvent.change(hoursInput, { target: { value } });
+ // For valid numbers, should have the value
+ // For invalid/empty/negative values, should be empty
+ const expectedValue = /^\d+$/.test(value) ? value : '';
+ expect(hoursInput).toHaveValue(expectedValue);
+ });
+
+ // Verify final value after rapid changes
+ expect(hoursInput).toHaveValue('54');
+
+ // Test rapid changes to multiple fields simultaneously
+ const preCompletionNotes = screen.getByLabelText(t.preCompletionNotes);
+
+ // Rapidly alternate between changing hours and notes
+ for (let i = 0; i < 10; i++) {
+ fireEvent.change(hoursInput, { target: { value: i.toString() } });
+ fireEvent.change(preCompletionNotes, { target: { value: `Note ${i}` } });
+
+ expect(hoursInput).toHaveValue(i.toString());
+ expect(preCompletionNotes).toHaveValue(`Note ${i}`);
+ }
+
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ // Verify final values persist after submission
+ expect(hoursInput).toHaveValue('9');
+ expect(preCompletionNotes).toHaveValue('Note 9');
+ });
+ });
+
it('No Fields Updated while Updating', async () => {
renderItemModal(link2, itemProps[2]);
// Click Submit
@@ -802,6 +1181,267 @@ describe('Testing ItemModal', () => {
});
});
+ //checking for empty and null values
+ it('handles empty and null form values correctly', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ const allottedHours = screen.getByLabelText(t.allottedHours);
+ const preCompletionNotes = screen.getByLabelText(t.preCompletionNotes);
+
+ // Test empty values
+ fireEvent.change(allottedHours, { target: { value: '' } });
+ expect(allottedHours).toHaveValue('');
+
+ fireEvent.change(preCompletionNotes, { target: { value: '' } });
+ expect(preCompletionNotes).toHaveValue('');
+
+ // Test null values
+ fireEvent.change(allottedHours, { target: { value: null } });
+ expect(allottedHours).toHaveValue('');
+
+ fireEvent.change(preCompletionNotes, { target: { value: null } });
+ expect(preCompletionNotes).toHaveValue('');
+
+ // Test undefined values
+ fireEvent.change(allottedHours, { target: { value: undefined } });
+ expect(allottedHours).toHaveValue('');
+
+ fireEvent.change(preCompletionNotes, { target: { value: undefined } });
+ expect(preCompletionNotes).toHaveValue('');
+
+ // Test form submission with missing required fields
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+
+ // Form should not submit without required fields
+ await waitFor(() => {
+ // Verify required fields are marked as required
+ const categorySelect = screen.getByTestId('categorySelect');
+ const categoryInput = within(categorySelect).getByRole('combobox');
+ expect(categoryInput).toBeRequired();
+
+ // Verify form values remain empty
+ expect(allottedHours).toHaveValue('');
+ expect(preCompletionNotes).toHaveValue('');
+ });
+
+ // Fill required fields and test submission
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ // Submit form with empty optional fields
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ // Verify optional fields remain empty after successful submission
+ expect(allottedHours).toHaveValue('');
+ expect(preCompletionNotes).toHaveValue('');
+ });
+ });
+
+ // validation of catergory selection
+ it('validates category selection', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ const categorySelect = await screen.findByTestId('categorySelect');
+ const inputField = within(categorySelect).getByRole('combobox');
+
+ // Test initial state and required validation
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+ expect(inputField).toBeRequired();
+
+ // Test valid category selection
+ fireEvent.mouseDown(inputField);
+ const categoryOption = await screen.findByText('Category 1');
+ fireEvent.click(categoryOption);
+ expect(inputField).toHaveValue('Category 1');
+
+ // Test direct input of invalid category
+ fireEvent.change(inputField, { target: { value: 'Invalid Category' } });
+ // Autocomplete should show no options for invalid input
+ await waitFor(() => {
+ expect(screen.queryByText('Invalid Category')).not.toBeInTheDocument();
+ });
+
+ // Clear selection
+ fireEvent.change(inputField, { target: { value: '' } });
+ fireEvent.blur(inputField);
+ expect(inputField).toHaveValue('');
+
+ // Test cycling through multiple valid categories
+ const validCategories = ['Category 1', 'Category 2'];
+ for (const category of validCategories) {
+ fireEvent.mouseDown(inputField);
+ const option = await screen.findByText(category);
+ fireEvent.click(option);
+ expect(inputField).toHaveValue(category);
+ }
+
+ // Test form submission with valid category
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ });
+ });
+
+ // changing of assignee type handling
+ it('handles assignee type changes correctly', async () => {
+ renderItemModal(link1, itemProps[1]);
+
+ // First select required category
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ // Test individual volunteer selection
+ const groupRadio = await screen.findByText(t.groups);
+ const individualRadio = await screen.findByText(t.individuals);
+
+ fireEvent.click(individualRadio);
+ const volunteerSelect = await screen.findByTestId('volunteerSelect');
+ expect(volunteerSelect).toBeInTheDocument();
+
+ // Test selecting an individual volunteer
+ const volunteerInput = within(volunteerSelect).getByRole('combobox');
+ fireEvent.mouseDown(volunteerInput);
+ fireEvent.click(await screen.findByText('Teresa Bradley'));
+ expect(volunteerInput).toHaveValue('Teresa Bradley');
+
+ // Test switching to group selection
+ fireEvent.click(groupRadio);
+ const groupSelect = await screen.findByTestId('volunteerGroupSelect');
+ expect(groupSelect).toBeInTheDocument();
+
+ // Test selecting a group
+ const groupInput = within(groupSelect).getByRole('combobox');
+ fireEvent.mouseDown(groupInput);
+ fireEvent.click(await screen.findByText('group1'));
+ expect(groupInput).toHaveValue('group1');
+
+ // Test switching back to individual
+ fireEvent.click(individualRadio);
+ const newVolunteerSelect = await screen.findByTestId('volunteerSelect');
+ expect(newVolunteerSelect).toBeInTheDocument();
+
+ // Test submission without selection
+ const newVolunteerInput = within(newVolunteerSelect).getByRole('combobox');
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.change(newVolunteerInput, { target: { value: '' } });
+ fireEvent.blur(newVolunteerInput);
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(newVolunteerInput).toBeRequired();
+ });
+ });
+
+ // for handling null change of date
+ it('handles date changes correctly', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ // First select required fields
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ const dateInput = screen.getByLabelText(t.dueDate);
+
+ // Test past date input
+ const pastDate = '01/01/2020';
+ fireEvent.change(dateInput, { target: { value: pastDate } });
+ expect(dateInput).toHaveValue(pastDate);
+
+ // Test valid future date
+ const futureDate = '01/01/2025';
+ fireEvent.change(dateInput, { target: { value: futureDate } });
+ expect(dateInput).toHaveValue(futureDate);
+
+ // Test form submission with valid date
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ });
+ });
+
+ // for handling form state changes and validations
+ it('handles all form state changes and validations', async () => {
+ renderItemModal(link1, itemProps[1]);
+
+ // Test category selection
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ // Test assignee type changes
+ const groupRadio = screen.getByLabelText(t.groups);
+ fireEvent.click(groupRadio);
+
+ const groupSelect = await screen.getByTestId('volunteerGroupSelect');
+ fireEvent.mouseDown(within(groupSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('group1'));
+
+ // Test date changes
+ const dateInput = screen.getByLabelText(t.dueDate);
+ fireEvent.change(dateInput, { target: { value: '' } });
+ fireEvent.change(dateInput, { target: { value: '01/01/2024' } });
+
+ // Test allotted hours with various inputs
+ const hoursInput = screen.getByLabelText(t.allottedHours);
+ ['abc', '-5', '', '0', '10'].forEach((value) => {
+ fireEvent.change(hoursInput, { target: { value } });
+ });
+
+ // Test notes
+ const notesInput = screen.getByLabelText(t.preCompletionNotes);
+ fireEvent.change(notesInput, { target: { value: 'Test notes' } });
+
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ });
+ });
+
+ // for handling edge cases in timezone
+ it('handles timezone edge cases', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ await waitFor(async () => {
+ const dateInput = screen.getByLabelText(t.dueDate);
+
+ // Test dates around DST changes
+ const dstDates = [
+ '03/12/2025', // Spring forward
+ '11/05/2025', // Fall back
+ ];
+
+ for (const date of dstDates) {
+ fireEvent.change(dateInput, { target: { value: date } });
+ expect(dateInput).toHaveValue(date);
+ }
+
+ // Test midnight boundary dates
+ fireEvent.change(dateInput, { target: { value: '01/01/2025' } });
+ expect(dateInput).toHaveValue('01/01/2025');
+ });
+ });
+
+ // For testing failure of updating action item
it('should fail to Update Action Item', async () => {
renderItemModal(link2, itemProps[2]);
expect(screen.getAllByText(t.updateActionItem)).toHaveLength(2);
@@ -820,4 +1460,55 @@ describe('Testing ItemModal', () => {
expect(toast.error).toHaveBeenCalledWith('Mock Graphql Error');
});
});
+
+ it('handles potentially malicious input patterns correctly', async () => {
+ renderItemModal(link1, itemProps[0]);
+
+ // Select required fields
+ const categorySelect = screen.getByTestId('categorySelect');
+ fireEvent.mouseDown(within(categorySelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Category 1'));
+
+ const memberSelect = screen.getByTestId('memberSelect');
+ fireEvent.mouseDown(within(memberSelect).getByRole('combobox'));
+ fireEvent.click(await screen.findByText('Harve Lance'));
+
+ const preCompletionNotes = screen.getByLabelText(t.preCompletionNotes);
+
+ // Test HTML-like content
+ fireEvent.change(preCompletionNotes, {
+ target: { value: 'Test
' },
+ });
+ expect(preCompletionNotes).toHaveValue(
+ 'Test
',
+ );
+
+ // Test common XSS patterns
+ const xssPatterns = [
+ ' ',
+ 'javascript:alert(1)',
+ '">',
+ '',
+ '\'--"',
+ '"; DROP TABLE users; --',
+ '${alert(1)}',
+ "{{constructor.constructor('alert(1)')()}}",
+ ];
+
+ for (const pattern of xssPatterns) {
+ fireEvent.change(preCompletionNotes, { target: { value: pattern } });
+ expect(preCompletionNotes).toHaveValue(pattern);
+ }
+
+ // Test form submission with special characters
+ const submitButton = screen.getByTestId('submitBtn');
+ fireEvent.click(submitButton);
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(t.successfulCreation);
+ // Verify the last input value persists after submission
+ expect(preCompletionNotes).toHaveValue(
+ xssPatterns[xssPatterns.length - 1],
+ );
+ });
+ });
});
diff --git a/src/screens/OrganizationActionItems/ItemModal.tsx b/src/screens/OrganizationActionItems/ItemModal.tsx
index 98a4611bf6..b7ca0accae 100644
--- a/src/screens/OrganizationActionItems/ItemModal.tsx
+++ b/src/screens/OrganizationActionItems/ItemModal.tsx
@@ -226,6 +226,20 @@ const ItemModal: FC = ({
field: keyof InterfaceFormStateType,
value: string | number | boolean | Date | undefined | null,
): void => {
+ // Special handling for allottedHours
+ if (field === 'allottedHours') {
+ // If the value is not a valid number or is negative, set to null
+ const numValue = typeof value === 'string' ? Number(value) : value;
+ if (
+ typeof numValue !== 'number' ||
+ Number.isNaN(numValue) ||
+ numValue < 0
+ ) {
+ setFormState((prevState) => ({ ...prevState, [field]: null }));
+ return;
+ }
+ }
+
setFormState((prevState) => ({ ...prevState, [field]: value }));
};
@@ -574,8 +588,6 @@ const ItemModal: FC = ({
className={styles.noOutline}
value={dayjs(dueDate)}
onChange={(date: Dayjs | null): void => {
- // Added istanbul ignore else, which will ignore else condition, we are not using else condition here
- /* istanbul ignore else -- @preserve */
if (date) handleFormChange('dueDate', date.toDate());
}}
/>
diff --git a/src/style/app.module.css b/src/style/app.module.css
index 1a604d7409..0861af482e 100644
--- a/src/style/app.module.css
+++ b/src/style/app.module.css
@@ -216,7 +216,7 @@
--input-area-color: #f1f3f6;
--date-picker-background: #f2f2f2;
--grey-bg-color-dark: #707070;
- --gret-bg-color: #4b5563;
+ --grey-bg-color-agenda-category: #4b5563;
--dropdown-border-color: #cccccc;
--primary-border-solid: 1px solid var(--dropdown-border-color);
--disabled-btn: #e7f0fe;
@@ -9128,6 +9128,23 @@ button[data-testid='createPostBtn'] {
}
}
+/* CollapsibleDropdown */
+
+.collapsibleDropdownIconWrapper {
+ width: 36px;
+}
+
+.collapsibleDropdownCollapseBtn {
+ height: 48px;
+}
+
+.collapsibleDropdownIconWrapperSm {
+ width: 36px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
/* AgendaCategory.tsx */
.createModalAgendaCategory {
@@ -9143,7 +9160,7 @@ button[data-testid='createPostBtn'] {
}
.titlemodalAgendaCategory {
- color: var(--grey-bg-color);
+ color: var( --grey-bg-color-agenda-category);
font-weight: 600;
font-size: 20px;
margin-bottom: 20px;