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()); }} />