fix: chart rendering race condition and homepage connection reset (#40065)

Co-authored-by: Geidō <60598000+geido@users.noreply.github.com>
This commit is contained in:
Mehmet Salih Yavuz
2026-05-14 17:10:11 +03:00
committed by GitHub
parent 01224007da
commit a62bf2b0bb
6 changed files with 210 additions and 82 deletions

View File

@@ -156,6 +156,78 @@ describe('chart actions', () => {
.mockImplementation((data: unknown) => Promise.resolve(data));
});
test('should drop stale success dispatches when a newer controller has replaced ours in state', async () => {
const chartKey = 'stale_success_test';
const formData: Partial<QueryFormData> = {
slice_id: 456,
datasource: 'table__1',
viz_type: 'table',
};
// A controller belonging to a *newer* in-flight request, already stored
// in state by the time this thunk's response resolves.
const newerController = new AbortController();
const state: MockState = {
charts: {
[chartKey]: {
queryController: newerController,
},
},
common: {
conf: {
SUPERSET_WEBSERVER_TIMEOUT: 60,
},
},
};
const getState = jest.fn(() => state);
const dispatchMock = jest.fn();
const getChartDataRequestSpy = jest
.spyOn(actions, 'getChartDataRequest')
.mockResolvedValue({
response: { status: 200 } as Response,
json: { result: [{ data: [{ stale: true }] }] },
});
const handleChartDataResponseSpy = jest
.spyOn(actions, 'handleChartDataResponse')
.mockResolvedValue([{ data: [{ stale: true }] }]);
const updateDataMaskSpy = jest
.spyOn(dataMaskActions, 'updateDataMask')
.mockReturnValue({ type: 'UPDATE_DATA_MASK' } as ReturnType<
typeof dataMaskActions.updateDataMask
>);
const getQuerySettingsStub = jest
.spyOn(exploreUtils, 'getQuerySettings')
.mockReturnValue([false, () => {}] as unknown as ReturnType<
typeof exploreUtils.getQuerySettings
>);
try {
const thunkAction = actions.exploreJSON(
formData as QueryFormData,
false,
undefined,
chartKey,
);
await thunkAction(
dispatchMock as unknown as actions.ChartThunkDispatch,
getState as unknown as () => actions.RootState,
undefined,
);
// CHART_UPDATE_STARTED is fine (it ran before the gate),
// but CHART_UPDATE_SUCCEEDED must NOT have fired with the stale data.
const dispatchedTypes = dispatchMock.mock.calls.map(
([action]) => action?.type,
);
expect(dispatchedTypes).toContain(actions.CHART_UPDATE_STARTED);
expect(dispatchedTypes).not.toContain(actions.CHART_UPDATE_SUCCEEDED);
} finally {
getChartDataRequestSpy.mockRestore();
handleChartDataResponseSpy.mockRestore();
updateDataMaskSpy.mockRestore();
getQuerySettingsStub.mockRestore();
}
});
test('should defer abort of previous controller to avoid Redux state mutation', async () => {
jest.useFakeTimers();
const chartKey = 'defer_abort_test';