From 0256ecdb490d41cdc2fd1630760a5655347c802f Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Thu, 18 Apr 2024 03:30:59 +0530 Subject: [PATCH 01/10] UIU-3080 - error message on invalid Image URL --- .../ExternalLinkModal/ExternalLinkModal.js | 35 ++++++++++++++----- .../ExternalLinkModal.test.js | 4 +-- translations/ui-users/en.json | 3 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js index 5d30fbc63..fa3c8bce4 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js @@ -23,20 +23,16 @@ const ExternalLinkModal = ({ const [inputValue, setInputValue] = useState(''); const previousInputValue = useRef(profilePictureLink); const [disabled, setDisabled] = useState(false); - const [error, setError] = useState(false); - const externalURLValidityError = error ? - - : null; + const [externalURLValidityError, setExternalURLValidityError] = useState(null); useEffect(() => { setInputValue(profilePictureLink); - setError(false); }, [profilePictureLink]); useEffect(() => { if (inputValue) { setDisabled(previousInputValue.current === inputValue); - setError(false); + setExternalURLValidityError(null); } else { setDisabled(true); } @@ -51,9 +47,30 @@ const ExternalLinkModal = ({ setInputValue(e.target.value); }; - const handleBlur = () => { - if (inputValue && !isAValidURL(inputValue)) { - setError(true); + async function isValidImageUrl(url) { + try { + const response = await fetch(url); + if (response.ok) { + const contentType = response.headers.get('content-type'); + return contentType && contentType.startsWith('image/'); + } else { + return false; + } + } catch (e) { + return false; + } + } + + const handleBlur = async () => { + if (!inputValue || !isAValidURL(inputValue)) { + setExternalURLValidityError(); + setDisabled(true); + return; + } + + const isValidImgURL = await isValidImageUrl(inputValue); + if (!isValidImgURL) { + setExternalURLValidityError(); setDisabled(true); } }; diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js index 1593d849c..69d6dedeb 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js @@ -61,14 +61,14 @@ describe('ExternalLinkModal', () => { expect(props.onSave).toHaveBeenCalled(); }); - it('should show error text when url is invalid', async () => { + it('should show error text when url is invalid url', async () => { isAValidURL.mockImplementationOnce(() => false); const inputElement = screen.getByLabelText('ui-users.information.profilePicture.externalLink.modal.externalURL'); fireEvent.change(inputElement, { target: { value: 'profile picture' } }); fireEvent.blur(inputElement); - await waitFor(() => expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL.errorMessage')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL.invalidURLErrorMessage')).toBeInTheDocument()); }); it('should call onClose', async () => { const cancelButton = screen.getByRole('button', { name: 'stripes-core.button.cancel' }); diff --git a/translations/ui-users/en.json b/translations/ui-users/en.json index 08d0ce2e1..9e67888cf 100644 --- a/translations/ui-users/en.json +++ b/translations/ui-users/en.json @@ -316,7 +316,8 @@ "information.profilePicture.delete": "Delete", "information.profilePicture.externalLink.modal.updateProfilePicture": "Update profile picture", "information.profilePicture.externalLink.modal.externalURL": "External URL", - "information.profilePicture.externalLink.modal.externalURL.errorMessage": "Invalid image URL", + "information.profilePicture.externalLink.modal.externalURL.invalidURLErrorMessage": "Invalid image URL", + "information.profilePicture.externalLink.modal.externalURL.invalidImageURLErrorMessage": "The provided URL is valid but does not point to an image file", "information.profilePicture.delete.modal.message": "You are deleting the profile picture for {name}", "information.profilePicture.delete.modal.heading": "Delete profile picture", "information.profilePicture.localFile.modal.previewAndEdit": "Preview and edit", From d21e1911b541a29a1716ffa95cf8f40f8cfaa6d5 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Wed, 24 Apr 2024 16:23:44 +0530 Subject: [PATCH 02/10] restrict save on invalid url n update test cases --- CHANGELOG.md | 1 + .../ExternalLinkModal/ExternalLinkModal.js | 31 +++++++------------ .../ExternalLinkModal.test.js | 4 ++- src/components/util/util.js | 11 +++++++ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6237223da..60c22dc0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Fix incorrect translation key having count-disagreements in Pay fees/fines modal in Fees/Fines Page. Refs UIU-1097. * Trim input values and delete properties with empty string when user record save. Refs UIU-2049. * Update username field validation to trim leading and trailing spaces. Refs UIU-3099. +* Handle invalid image URLs when uploading profile photo via External URL. Refs UIU-3080. ## [10.1.0](https://github.com/folio-org/ui-users/tree/v10.1.0) (2024-03-20) [Full Changelog](https://github.com/folio-org/ui-users/compare/v10.0.4...v10.1.0) diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js index fa3c8bce4..07a2e3549 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js @@ -12,7 +12,7 @@ import { TextField, } from '@folio/stripes/components'; import { FormattedMessage } from 'react-intl'; -import { isAValidURL } from '../../../../util'; +import { isAValidURL, isAValidImageUrl } from '../../../../util'; const ExternalLinkModal = ({ open, @@ -33,13 +33,14 @@ const ExternalLinkModal = ({ if (inputValue) { setDisabled(previousInputValue.current === inputValue); setExternalURLValidityError(null); - } else { - setDisabled(true); } }, [inputValue]); - const handleSave = () => { - onSave(inputValue); + const handleSave = async () => { + const isValidImgURL = await isAValidImageUrl(inputValue); + if (isValidImgURL) { + onSave(inputValue); + } }; const handleInputChange = (e) => { @@ -47,20 +48,6 @@ const ExternalLinkModal = ({ setInputValue(e.target.value); }; - async function isValidImageUrl(url) { - try { - const response = await fetch(url); - if (response.ok) { - const contentType = response.headers.get('content-type'); - return contentType && contentType.startsWith('image/'); - } else { - return false; - } - } catch (e) { - return false; - } - } - const handleBlur = async () => { if (!inputValue || !isAValidURL(inputValue)) { setExternalURLValidityError(); @@ -68,11 +55,15 @@ const ExternalLinkModal = ({ return; } - const isValidImgURL = await isValidImageUrl(inputValue); + const isValidImgURL = await isAValidImageUrl(inputValue); if (!isValidImgURL) { setExternalURLValidityError(); setDisabled(true); + return; } + + setExternalURLValidityError(null); + setDisabled(false); }; const renderModalFooter = () => { diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js index 69d6dedeb..06e3cf5b6 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js @@ -6,7 +6,7 @@ import { } from '@folio/jest-config-stripes/testing-library/react'; import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; -import { isAValidURL } from '../../../../util'; +import { isAValidURL, isAValidImageUrl } from '../../../../util'; import '../../../../../../test/jest/__mock__'; @@ -43,6 +43,7 @@ describe('ExternalLinkModal', () => { beforeEach(() => { isAValidURL.mockReset(); + isAValidImageUrl.mockReset(); renderExternalLinkModal(props); }); @@ -53,6 +54,7 @@ describe('ExternalLinkModal', () => { expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL')).toBeInTheDocument(); }); it('should call onSave', async () => { + isAValidImageUrl.mockImplementationOnce(() => true); const saveButton = screen.getByRole('button', { name: 'ui-users.save' }); const inputElement = screen.getByLabelText('ui-users.information.profilePicture.externalLink.modal.externalURL'); diff --git a/src/components/util/util.js b/src/components/util/util.js index 5659c28a9..35ba9a7b5 100644 --- a/src/components/util/util.js +++ b/src/components/util/util.js @@ -222,3 +222,14 @@ export const isAValidURL = (str) => { return URL.canParse(str); }; +export const isAValidImageUrl = async (url) => { + try { + const response = await fetch(url); + if (!response.ok) return false; + + const contentType = response.headers.get('content-type'); + return contentType && contentType.startsWith('image/'); + } catch (e) { + return false; + } +}; From 492a6c8b7faf1f0c80c60267aea028629c0134c3 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Wed, 24 Apr 2024 16:54:39 +0530 Subject: [PATCH 03/10] increase test coverage --- .../components/ExternalLinkModal/ExternalLinkModal.js | 4 ---- .../ExternalLinkModal/ExternalLinkModal.test.js | 10 ++++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js index 07a2e3549..662249aae 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js @@ -59,11 +59,7 @@ const ExternalLinkModal = ({ if (!isValidImgURL) { setExternalURLValidityError(); setDisabled(true); - return; } - - setExternalURLValidityError(null); - setDisabled(false); }; const renderModalFooter = () => { diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js index 06e3cf5b6..3009237cc 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js @@ -72,6 +72,16 @@ describe('ExternalLinkModal', () => { await waitFor(() => expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL.invalidURLErrorMessage')).toBeInTheDocument()); }); + it('should show error text when url is invalid image url', async () => { + isAValidURL.mockImplementationOnce(() => true); + isAValidImageUrl.mockImplementationOnce(() => false); + const inputElement = screen.getByLabelText('ui-users.information.profilePicture.externalLink.modal.externalURL'); + + fireEvent.change(inputElement, { target: { value: 'https://folio-org.atlassian.net/browse/UIU-3080' } }); + await fireEvent.blur(inputElement); + + await waitFor(() => expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL.invalidImageURLErrorMessage')).toBeInTheDocument()); + }); it('should call onClose', async () => { const cancelButton = screen.getByRole('button', { name: 'stripes-core.button.cancel' }); await userEvent.click(cancelButton); From 24bfc311c72fcda0fbbc68db57c9a2980c6039c5 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Thu, 25 Apr 2024 02:46:02 +0530 Subject: [PATCH 04/10] test cases for isAValidImageUrl function --- src/components/util/util.test.js | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/components/util/util.test.js b/src/components/util/util.test.js index 64ef8977c..5c9f7a476 100644 --- a/src/components/util/util.test.js +++ b/src/components/util/util.test.js @@ -28,6 +28,7 @@ import { getRequestUrl, isAffiliationsEnabled, isDCBItem, + isAValidImageUrl, } from './util'; const STRIPES = { @@ -474,3 +475,42 @@ describe('isDCBItem ', () => { expect(isDCBItem(item)).toBeFalsy(); }); }); + +describe('isAValidImageUrl', () => { + it('should return true for a valid image URL with correct content-type', async () => { + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + headers: { + get: jest.fn().mockReturnValue('image/jpeg'), + }, + }); + + const url = 'https://folio.org/wp-content/folio-site-general-Illustration-social-image-1200.jpg'; + const result = await isAValidImageUrl(url); + + expect(result).toBe(true); + }); + + it('should return false for an invalid image URL with incorrect content-type', async () => { + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + headers: { + get: jest.fn().mockReturnValue('text/plain'), + }, + }); + + const url = 'https://folio.org/wp-content/folio-site-general-Illustration-social-image-1200.txt'; + const result = await isAValidImageUrl(url); + + expect(result).toBe(false); + }); + + it('should return false for network errors', async () => { + global.fetch = jest.fn().mockRejectedValue(new Error('Network Error')); + + const url = 'https://folio.org/wp-content/folio-site-general-Illustration-social-image-1200.jpg'; + const result = await isAValidImageUrl(url); + expect(result).toBe(false); + }); +}); + From 297588942b5786da0fac1a032cb986a48baad704 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Thu, 25 Apr 2024 02:51:00 +0530 Subject: [PATCH 05/10] add optional chaining in isAValidImageUrl --- src/components/util/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/util/util.js b/src/components/util/util.js index 35ba9a7b5..8b91675a2 100644 --- a/src/components/util/util.js +++ b/src/components/util/util.js @@ -228,7 +228,7 @@ export const isAValidImageUrl = async (url) => { if (!response.ok) return false; const contentType = response.headers.get('content-type'); - return contentType && contentType.startsWith('image/'); + return contentType && contentType?.startsWith('image/'); } catch (e) { return false; } From 98be8aa20330226a078b39d3948db44860ea8912 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Thu, 25 Apr 2024 03:03:59 +0530 Subject: [PATCH 06/10] isAValidImageUrl refinement --- src/components/util/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/util/util.js b/src/components/util/util.js index 8b91675a2..3dcf77f3e 100644 --- a/src/components/util/util.js +++ b/src/components/util/util.js @@ -228,7 +228,7 @@ export const isAValidImageUrl = async (url) => { if (!response.ok) return false; const contentType = response.headers.get('content-type'); - return contentType && contentType?.startsWith('image/'); + return contentType?.startsWith('image/') ?? false; } catch (e) { return false; } From e5cbccc1a0276b4a194117e248759fc039ce58ea Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Fri, 26 Apr 2024 13:27:04 +0530 Subject: [PATCH 07/10] change img url validation approach --- CHANGELOG.md | 2 +- .../ExternalLinkModal/ExternalLinkModal.js | 9 ++- src/components/util/util.js | 11 ++-- src/components/util/util.test.js | 64 +++++++++++-------- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c91eafe8b..5f82281d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ * Trim input values and delete properties with empty string when user record save. Refs UIU-2049. * Update username field validation to trim leading and trailing spaces. Refs UIU-3099. * Fix "Total paid amount" value that set as "$NaN" on "Refund fee/fine" modal. Refs UIU-3094. -* Handle invalid image URLs when uploading profile photo via External URL. Refs UIU-3080. +* Validate image url provided as external url for user profile picture. Refs UIU-3080. ## [10.1.0](https://github.com/folio-org/ui-users/tree/v10.1.0) (2024-03-20) [Full Changelog](https://github.com/folio-org/ui-users/compare/v10.0.4...v10.1.0) diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js index 662249aae..455210f9e 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js @@ -32,6 +32,8 @@ const ExternalLinkModal = ({ useEffect(() => { if (inputValue) { setDisabled(previousInputValue.current === inputValue); + } else { + setDisabled(true); setExternalURLValidityError(null); } }, [inputValue]); @@ -49,7 +51,10 @@ const ExternalLinkModal = ({ }; const handleBlur = async () => { - if (!inputValue || !isAValidURL(inputValue)) { + setExternalURLValidityError(null); + if (!inputValue) return; + + if (!isAValidURL(inputValue)) { setExternalURLValidityError(); setDisabled(true); return; @@ -69,7 +74,7 @@ const ExternalLinkModal = ({ buttonStyle="primary" id="save-external-link-btn" disabled={disabled} - onClick={handleSave} + onMouseDown={handleSave} > diff --git a/src/components/util/util.js b/src/components/util/util.js index 3dcf77f3e..260e48779 100644 --- a/src/components/util/util.js +++ b/src/components/util/util.js @@ -224,11 +224,12 @@ export const isAValidURL = (str) => { export const isAValidImageUrl = async (url) => { try { - const response = await fetch(url); - if (!response.ok) return false; - - const contentType = response.headers.get('content-type'); - return contentType?.startsWith('image/') ?? false; + return new Promise((resolve) => { + const img = new Image(); + img.onload = () => resolve(true); + img.onerror = () => resolve(false); + img.src = url; + }); } catch (e) { return false; } diff --git a/src/components/util/util.test.js b/src/components/util/util.test.js index 5c9f7a476..0aac27032 100644 --- a/src/components/util/util.test.js +++ b/src/components/util/util.test.js @@ -477,40 +477,50 @@ describe('isDCBItem ', () => { }); describe('isAValidImageUrl', () => { - it('should return true for a valid image URL with correct content-type', async () => { - global.fetch = jest.fn().mockResolvedValue({ - ok: true, - headers: { - get: jest.fn().mockReturnValue('image/jpeg'), - }, - }); + it('should return true for a valid image URL', async () => { + class MockImage { + constructor() { + this.onload = null; + this.onerror = null; + } - const url = 'https://folio.org/wp-content/folio-site-general-Illustration-social-image-1200.jpg'; - const result = await isAValidImageUrl(url); + set src(value) { + if (this.onload) { + setTimeout(() => { + this.onload(); + }, 0); + } + } + } - expect(result).toBe(true); - }); + global.Image = MockImage; + const validImageUrl = 'https://upload.wikimedia.org/wikipedia/commons/e/e2/FOLIO_400x400.jpg'; - it('should return false for an invalid image URL with incorrect content-type', async () => { - global.fetch = jest.fn().mockResolvedValue({ - ok: true, - headers: { - get: jest.fn().mockReturnValue('text/plain'), - }, - }); + const isValid = await isAValidImageUrl(validImageUrl); + expect(isValid).toBe(true); + }); - const url = 'https://folio.org/wp-content/folio-site-general-Illustration-social-image-1200.txt'; - const result = await isAValidImageUrl(url); + it('should return false for an invalid image URL', async () => { + class MockImage { + constructor() { + this.onload = null; + this.onerror = null; + } - expect(result).toBe(false); - }); + set src(value) { + if (this.onerror) { + setTimeout(() => { + this.onerror(); + }, 0); + } + } + } - it('should return false for network errors', async () => { - global.fetch = jest.fn().mockRejectedValue(new Error('Network Error')); + global.Image = MockImage; + const invalidImageUrl = 'https://example.com'; - const url = 'https://folio.org/wp-content/folio-site-general-Illustration-social-image-1200.jpg'; - const result = await isAValidImageUrl(url); - expect(result).toBe(false); + const isValid = await isAValidImageUrl(invalidImageUrl); + expect(isValid).toBe(false); }); }); From 20384779144495f0c41c0e6110672494942db1e4 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Fri, 26 Apr 2024 15:40:30 +0530 Subject: [PATCH 08/10] remove handleBlur and update test cases --- .../ExternalLinkModal/ExternalLinkModal.js | 23 ++++++++----------- .../ExternalLinkModal.test.js | 10 ++++++-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js index 455210f9e..5ffff9962 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js @@ -39,18 +39,6 @@ const ExternalLinkModal = ({ }, [inputValue]); const handleSave = async () => { - const isValidImgURL = await isAValidImageUrl(inputValue); - if (isValidImgURL) { - onSave(inputValue); - } - }; - - const handleInputChange = (e) => { - previousInputValue.current = inputValue; - setInputValue(e.target.value); - }; - - const handleBlur = async () => { setExternalURLValidityError(null); if (!inputValue) return; @@ -64,7 +52,15 @@ const ExternalLinkModal = ({ if (!isValidImgURL) { setExternalURLValidityError(); setDisabled(true); + return; } + onSave(inputValue); + }; + + const handleInputChange = (e) => { + setExternalURLValidityError(null); + previousInputValue.current = inputValue; + setInputValue(e.target.value); }; const renderModalFooter = () => { @@ -74,7 +70,7 @@ const ExternalLinkModal = ({ buttonStyle="primary" id="save-external-link-btn" disabled={disabled} - onMouseDown={handleSave} + onClick={handleSave} > @@ -101,7 +97,6 @@ const ExternalLinkModal = ({ label={} error={externalURLValidityError} onChange={handleInputChange} - onBlur={handleBlur} value={inputValue} hasClearIcon={false} /> diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js index 3009237cc..5d739d02f 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.test.js @@ -54,7 +54,9 @@ describe('ExternalLinkModal', () => { expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL')).toBeInTheDocument(); }); it('should call onSave', async () => { + isAValidURL.mockImplementationOnce(() => true); isAValidImageUrl.mockImplementationOnce(() => true); + const saveButton = screen.getByRole('button', { name: 'ui-users.save' }); const inputElement = screen.getByLabelText('ui-users.information.profilePicture.externalLink.modal.externalURL'); @@ -65,20 +67,24 @@ describe('ExternalLinkModal', () => { }); it('should show error text when url is invalid url', async () => { isAValidURL.mockImplementationOnce(() => false); + + const saveButton = screen.getByRole('button', { name: 'ui-users.save' }); const inputElement = screen.getByLabelText('ui-users.information.profilePicture.externalLink.modal.externalURL'); fireEvent.change(inputElement, { target: { value: 'profile picture' } }); - fireEvent.blur(inputElement); + await userEvent.click(saveButton); await waitFor(() => expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL.invalidURLErrorMessage')).toBeInTheDocument()); }); it('should show error text when url is invalid image url', async () => { isAValidURL.mockImplementationOnce(() => true); isAValidImageUrl.mockImplementationOnce(() => false); + + const saveButton = screen.getByRole('button', { name: 'ui-users.save' }); const inputElement = screen.getByLabelText('ui-users.information.profilePicture.externalLink.modal.externalURL'); fireEvent.change(inputElement, { target: { value: 'https://folio-org.atlassian.net/browse/UIU-3080' } }); - await fireEvent.blur(inputElement); + await userEvent.click(saveButton); await waitFor(() => expect(screen.getByText('ui-users.information.profilePicture.externalLink.modal.externalURL.invalidImageURLErrorMessage')).toBeInTheDocument()); }); From 2b0e357e70df4e3b11780d73a11f7823edab6e04 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Fri, 26 Apr 2024 15:57:46 +0530 Subject: [PATCH 09/10] refine state variable usage --- .../components/ExternalLinkModal/ExternalLinkModal.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js index 5ffff9962..75be3e6b3 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js @@ -30,11 +30,11 @@ const ExternalLinkModal = ({ }, [profilePictureLink]); useEffect(() => { + setExternalURLValidityError(null); if (inputValue) { setDisabled(previousInputValue.current === inputValue); } else { setDisabled(true); - setExternalURLValidityError(null); } }, [inputValue]); @@ -58,7 +58,6 @@ const ExternalLinkModal = ({ }; const handleInputChange = (e) => { - setExternalURLValidityError(null); previousInputValue.current = inputValue; setInputValue(e.target.value); }; From 7f589a772f479ff115848b8bb5607aede4b11af1 Mon Sep 17 00:00:00 2001 From: manvendra-s-rathore Date: Mon, 29 Apr 2024 15:18:18 +0530 Subject: [PATCH 10/10] remove unnecessary code --- .../components/ExternalLinkModal/ExternalLinkModal.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js index 75be3e6b3..fbc259cb2 100644 --- a/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js +++ b/src/components/EditSections/EditUserInfo/components/ExternalLinkModal/ExternalLinkModal.js @@ -39,9 +39,6 @@ const ExternalLinkModal = ({ }, [inputValue]); const handleSave = async () => { - setExternalURLValidityError(null); - if (!inputValue) return; - if (!isAValidURL(inputValue)) { setExternalURLValidityError(); setDisabled(true);