@@ -31,6 +31,7 @@ import * as exploreUtils from 'src/explore/exploreUtils';
3131import * as actions from 'src/components/Chart/chartAction' ;
3232import * as asyncEvent from 'src/middleware/asyncEvent' ;
3333import { handleChartDataResponse } from 'src/components/Chart/chartAction' ;
34+ import * as dataMaskActions from 'src/dataMask/actions' ;
3435
3536import configureMockStore from 'redux-mock-store' ;
3637import thunk from 'redux-thunk' ;
@@ -110,6 +111,69 @@ describe('chart actions', () => {
110111 . callsFake ( data => Promise . resolve ( data ) ) ;
111112 } ) ;
112113
114+ test ( 'should defer abort of previous controller to avoid Redux state mutation' , async ( ) => {
115+ jest . useFakeTimers ( ) ;
116+ const chartKey = 'defer_abort_test' ;
117+ const formData = {
118+ slice_id : 123 ,
119+ datasource : 'table__1' ,
120+ viz_type : 'table' ,
121+ } ;
122+ const oldController = new AbortController ( ) ;
123+ const abortSpy = jest . spyOn ( oldController , 'abort' ) ;
124+ const state = {
125+ charts : {
126+ [ chartKey ] : {
127+ queryController : oldController ,
128+ } ,
129+ } ,
130+ common : {
131+ conf : {
132+ SUPERSET_WEBSERVER_TIMEOUT : 60 ,
133+ } ,
134+ } ,
135+ } ;
136+ const getState = jest . fn ( ( ) => state ) ;
137+ const dispatchMock = jest . fn ( ) ;
138+ const getChartDataRequestSpy = jest
139+ . spyOn ( actions , 'getChartDataRequest' )
140+ . mockResolvedValue ( {
141+ response : { status : 200 } ,
142+ json : { result : [ ] } ,
143+ } ) ;
144+ const handleChartDataResponseSpy = jest
145+ . spyOn ( actions , 'handleChartDataResponse' )
146+ . mockResolvedValue ( [ ] ) ;
147+ const updateDataMaskSpy = jest
148+ . spyOn ( dataMaskActions , 'updateDataMask' )
149+ . mockReturnValue ( { type : 'UPDATE_DATA_MASK' } ) ;
150+ const getQuerySettingsStub = sinon
151+ . stub ( exploreUtils , 'getQuerySettings' )
152+ . returns ( [ false , ( ) => { } ] ) ;
153+
154+ try {
155+ const thunk = actions . exploreJSON ( formData , false , undefined , chartKey ) ;
156+ const promise = thunk ( dispatchMock , getState ) ;
157+
158+ expect ( abortSpy ) . not . toHaveBeenCalled ( ) ;
159+ expect ( oldController . signal . aborted ) . toBe ( false ) ;
160+
161+ jest . runOnlyPendingTimers ( ) ;
162+
163+ expect ( abortSpy ) . toHaveBeenCalledTimes ( 1 ) ;
164+ expect ( oldController . signal . aborted ) . toBe ( true ) ;
165+
166+ await promise ;
167+ } finally {
168+ getChartDataRequestSpy . mockRestore ( ) ;
169+ handleChartDataResponseSpy . mockRestore ( ) ;
170+ updateDataMaskSpy . mockRestore ( ) ;
171+ getQuerySettingsStub . restore ( ) ;
172+ abortSpy . mockRestore ( ) ;
173+ jest . useRealTimers ( ) ;
174+ }
175+ } ) ;
176+
113177 afterEach ( ( ) => {
114178 getExploreUrlStub . restore ( ) ;
115179 getChartDataUriStub . restore ( ) ;
0 commit comments