diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx index 35b65ac7727..b02d3bb04a8 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx @@ -471,3 +471,95 @@ test('should handle chartId changes', async () => { expect(mockChartClient.loadFormData).toHaveBeenCalledTimes(2); }); }); + +test('should display error message when HTTP request fails with Response object', async () => { + const errorBody = JSON.stringify({ message: 'Error: division by zero' }); + const mockResponse = new Response(errorBody, { + status: 400, + statusText: 'Bad Request', + headers: { 'Content-Type': 'application/json' }, + }); + mockChartClient.client.post.mockRejectedValue(mockResponse); + + const onError = jest.fn(); + const { findByText } = render( + , + ); + + const errorElement = await findByText(/Error: division by zero/i); + expect(errorElement).toBeInTheDocument(); + + await waitFor(() => { + expect(onError).toHaveBeenCalledTimes(1); + expect(onError).toHaveBeenCalledWith(expect.any(Error)); + expect(onError.mock.calls[0][0].message).toBe('Error: division by zero'); + }); +}); + +test('should display error message when HTTP request fails with errors array', async () => { + const errorBody = JSON.stringify({ + errors: [ + { + message: 'Query failed: column "invalid_col" does not exist', + error_type: 'COLUMN_DOES_NOT_EXIST_ERROR', + }, + ], + }); + const mockResponse = new Response(errorBody, { + status: 422, + statusText: 'Unprocessable Entity', + headers: { 'Content-Type': 'application/json' }, + }); + mockChartClient.client.post.mockRejectedValue(mockResponse); + + const { findByText } = render( + , + ); + + const errorElement = await findByText( + /Query failed: column "invalid_col" does not exist/i, + ); + expect(errorElement).toBeInTheDocument(); +}); + +test('should display generic error message for network failures', async () => { + const networkError = new TypeError('Failed to fetch'); + mockChartClient.client.post.mockRejectedValue(networkError); + + const { findByText } = render( + , + ); + + const errorElement = await findByText(/Network error/i); + expect(errorElement).toBeInTheDocument(); +}); + +test('should pass error to custom errorComponent when provided', async () => { + const errorBody = JSON.stringify({ message: 'Custom error message' }); + const mockResponse = new Response(errorBody, { + status: 400, + statusText: 'Bad Request', + headers: { 'Content-Type': 'application/json' }, + }); + mockChartClient.client.post.mockRejectedValue(mockResponse); + + const CustomErrorComponent = ({ error }: { error: Error }) => ( +
Custom: {error.message}
+ ); + + const { findByTestId } = render( + , + ); + + const customError = await findByTestId('custom-error'); + expect(customError).toBeInTheDocument(); + expect(customError).toHaveTextContent('Custom: Custom error message'); +}); diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx index ba22e64808a..83ddb688b96 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx @@ -25,6 +25,7 @@ import { SupersetClientInterface, buildQueryContext, RequestConfig, + getClientErrorObject, } from '../..'; import { Loading } from '../../components/Loading'; import ChartClient from '../clients/ChartClient'; @@ -279,11 +280,17 @@ export default function StatefulChart(props: StatefulChartProps) { } } catch (err) { // Ignore abort errors - if (err.name === 'AbortError') { + if ((err as Error).name === 'AbortError') { return; } - const errorObj = err as Error; + const parsedError = await getClientErrorObject( + err as Parameters[0], + ); + const errorMessage = + parsedError.error || parsedError.message || 'An error occurred'; + + const errorObj = new Error(errorMessage); setStatus('error'); setError(errorObj);