This is an automated email from the ASF dual-hosted git repository. msyavuz pushed a commit to branch msyavuz/fix/infinite-refresh-bug in repository https://gitbox.apache.org/repos/asf/superset.git
commit 45bb64d08e441a16bcbd3041c9fd36af29edbc30 Author: Mehmet Salih Yavuz <[email protected]> AuthorDate: Fri Jan 9 21:20:27 2026 +0300 test: add regression tests --- .../components/gridComponents/Tab/Tab.test.tsx | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.test.tsx b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.test.tsx index e4e529eade..7f0392fc5f 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.test.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.test.tsx @@ -541,3 +541,105 @@ test('Should not refresh charts when tab becomes active if no dashboard refresh expect(onRefresh).not.toHaveBeenCalled(); }); + +test('Should not cause infinite refresh loop with nested tabs - regression test', async () => { + jest.clearAllMocks(); + const getChartIdsFromComponent = require('src/dashboard/util/getChartIdsFromComponent'); + getChartIdsFromComponent.mockReset(); + getChartIdsFromComponent.mockReturnValue([201, 202]); + + const props = createProps(); + props.renderType = 'RENDER_TAB_CONTENT'; + props.isComponentVisible = false; + + const initialState = { + dashboardState: { + lastRefreshTime: Date.now() - 1000, // Dashboard was refreshed recently + tabActivationTimes: { + 'TAB-YT6eNksV-': Date.now() - 5000, // Tab was activated before refresh + }, + }, + dashboardInfo: { + id: 23, + dash_edit_perm: true, + }, + }; + + const { rerender } = render(<Tab {...props} />, { + useRedux: true, + useDnd: true, + initialState, + }); + + // Initial state - no refresh should happen + expect(onRefresh).not.toHaveBeenCalled(); + + // Make tab visible - should trigger ONE refresh + rerender(<Tab {...props} isComponentVisible />); + + await waitFor( + () => { + expect(onRefresh).toHaveBeenCalledTimes(1); + }, + { timeout: 500 }, + ); + + // Clear the mock to track subsequent calls + jest.clearAllMocks(); + + // REGRESSION TEST: Multiple re-renders should NOT trigger additional refreshes + // This simulates the infinite loop scenario that was happening with nested tabs + for (let i = 0; i < 5; i++) { + rerender(<Tab {...props} isComponentVisible />); + await new Promise(resolve => setTimeout(resolve, 20)); + } + + expect(onRefresh).not.toHaveBeenCalled(); +}); + +test('Should use isLazyLoad flag for tab refreshes', async () => { + jest.clearAllMocks(); + const getChartIdsFromComponent = require('src/dashboard/util/getChartIdsFromComponent'); + getChartIdsFromComponent.mockReset(); + getChartIdsFromComponent.mockReturnValue([401, 402]); + + const props = createProps(); + props.renderType = 'RENDER_TAB_CONTENT'; + props.isComponentVisible = true; + + const initialState = { + dashboardState: { + lastRefreshTime: Date.now() - 1000, // Dashboard was refreshed recently + tabActivationTimes: { + 'TAB-YT6eNksV-': Date.now() - 5000, // Tab was activated before refresh + }, + }, + dashboardInfo: { + id: 42, + dash_edit_perm: true, + }, + }; + + render(<Tab {...props} />, { + useRedux: true, + useDnd: true, + initialState, + }); + + // Tab should trigger refresh with isLazyLoad = true + await waitFor( + () => { + expect(onRefresh).toHaveBeenCalled(); + }, + { timeout: 500 }, + ); + + // Verify that isLazyLoad flag is set to true for tab refreshes + expect(onRefresh).toHaveBeenCalledWith( + [401, 402], + true, // force + 0, // interval + 42, // dashboardId + true, // isLazyLoad should be true to prevent infinite loops + ); +});
