diff --git a/src/components/Points/PointImage.jsx b/src/components/Points/PointImage.jsx index 9a694da01..12bddb1a9 100644 --- a/src/components/Points/PointImage.jsx +++ b/src/components/Points/PointImage.jsx @@ -10,6 +10,11 @@ function PointImage({ data, sx }) { const images = []; function isImgUrl(string) { + // Handle data URIs + if (/^data:image\//.test(string)) { + return true; + } + let url; try { url = new URL(string); @@ -17,7 +22,8 @@ function PointImage({ data, sx }) { return false; } if (url) { - return /\.(jpg|jpeg|png|webp|gif|svg)$/.test(url.pathname); + const pathname = url.pathname.split('?')[0].split('#')[0]; + return /\.(jpg|jpeg|png|webp|gif|svg|bmp|ico|tiff|tif|avif|heic|heif)$/i.test(pathname); } return false; } diff --git a/src/components/ToastNotifications/ErrorNotifier.jsx b/src/components/ToastNotifications/ErrorNotifier.jsx index 75bb88703..cf9245f35 100644 --- a/src/components/ToastNotifications/ErrorNotifier.jsx +++ b/src/components/ToastNotifications/ErrorNotifier.jsx @@ -10,7 +10,7 @@ export const ErrorNotifier = ({ message = 'Something went wrong', callback }) => useEffect(() => { enqueueSnackbar(message, errorSnackbarOptions); typeof callback === 'function' && callback(); - }, [enqueueSnackbar, errorSnackbarOptions, message]); + }, [enqueueSnackbar, errorSnackbarOptions, message, callback]); return null; }; diff --git a/src/components/ToastNotifications/ErrorNotifier.test.jsx b/src/components/ToastNotifications/ErrorNotifier.test.jsx new file mode 100644 index 000000000..02b7d7f81 --- /dev/null +++ b/src/components/ToastNotifications/ErrorNotifier.test.jsx @@ -0,0 +1,25 @@ +import { render, waitFor } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import { ErrorNotifier } from './ErrorNotifier'; + +const enqueueSnackbar = vi.fn(); +const closeSnackbar = vi.fn(); + +vi.mock('notistack', () => ({ + useSnackbar: () => ({ + enqueueSnackbar, + closeSnackbar, + }), +})); + +describe('ErrorNotifier', () => { + it('passes the full message to the snackbar', async () => { + const message = 'x'.repeat(300); + + render(); + + await waitFor(() => { + expect(enqueueSnackbar).toHaveBeenCalledWith(message, expect.any(Object)); + }); + }); +}); diff --git a/src/lib/get-error-message.js b/src/lib/get-error-message.js index f6d755ae8..ab9fc145e 100644 --- a/src/lib/get-error-message.js +++ b/src/lib/get-error-message.js @@ -27,5 +27,10 @@ export const getErrorMessage = (e, options = {}) => { // error is not instance of ApiError message = e?.message || fallbackMessage; } - return message; + + if (!message || !message.trim()) { + message = fallbackMessage; + } + + return message.trim(); }; diff --git a/src/lib/get-error-message.test.js b/src/lib/get-error-message.test.js new file mode 100644 index 000000000..98f2bffb4 --- /dev/null +++ b/src/lib/get-error-message.test.js @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { getErrorMessage } from './get-error-message'; + +describe('getErrorMessage', () => { + it('returns the fallback when the extracted API error message is empty', () => { + const error = { + message: 'Request error:', + getActualType: () => ({ + status: 400, + data: { + status: { + error: ' ', + }, + }, + }), + }; + + expect(getErrorMessage(error, { fallbackMessage: 'Something went wrong.' })).toBe('Something went wrong.'); + }); + + it('trims a non-empty API error message', () => { + const error = { + message: 'Request error:', + getActualType: () => ({ + status: 400, + data: { + status: { + error: ' Detailed backend message ', + }, + }, + }), + }; + + expect(getErrorMessage(error)).toBe('Detailed backend message'); + }); +});