1import { DataQuery, DefaultTimeZone, EventBusExtended, serializeStateToUrlParam, toUtc } from '@grafana/data'; 2import { ExploreId, StoreState, ThunkDispatch } from 'app/types'; 3import { refreshExplore } from './explorePane'; 4import { setDataSourceSrv } from '@grafana/runtime'; 5import { configureStore } from '../../../store/configureStore'; 6import { of } from 'rxjs'; 7 8jest.mock('../../dashboard/services/TimeSrv', () => ({ 9 getTimeSrv: jest.fn().mockReturnValue({ 10 init: jest.fn(), 11 }), 12})); 13 14const t = toUtc(); 15const testRange = { 16 from: t, 17 to: t, 18 raw: { 19 from: t, 20 to: t, 21 }, 22}; 23 24const defaultInitialState = { 25 user: { 26 orgId: '1', 27 timeZone: DefaultTimeZone, 28 }, 29 explore: { 30 [ExploreId.left]: { 31 initialized: true, 32 containerWidth: 1920, 33 eventBridge: {} as EventBusExtended, 34 queries: [] as DataQuery[], 35 range: testRange, 36 refreshInterval: { 37 label: 'Off', 38 value: 0, 39 }, 40 cache: [], 41 }, 42 }, 43}; 44 45function setupStore(state?: any) { 46 return configureStore({ 47 ...defaultInitialState, 48 explore: { 49 [ExploreId.left]: { 50 ...defaultInitialState.explore[ExploreId.left], 51 ...(state || {}), 52 }, 53 }, 54 } as any); 55} 56 57function setup(state?: any) { 58 const datasources: Record<string, any> = { 59 newDs: { 60 testDatasource: jest.fn(), 61 init: jest.fn(), 62 query: jest.fn(), 63 name: 'newDs', 64 meta: { id: 'newDs' }, 65 getRef: () => ({ uid: 'newDs' }), 66 }, 67 someDs: { 68 testDatasource: jest.fn(), 69 init: jest.fn(), 70 query: jest.fn(), 71 name: 'someDs', 72 meta: { id: 'someDs' }, 73 getRef: () => ({ uid: 'someDs' }), 74 }, 75 }; 76 77 setDataSourceSrv({ 78 getList() { 79 return Object.values(datasources).map((d) => ({ name: d.name })); 80 }, 81 getInstanceSettings(name: string) { 82 return { name, getRef: () => ({ uid: name }) }; 83 }, 84 get(name?: string) { 85 return Promise.resolve( 86 name 87 ? datasources[name] 88 : { 89 testDatasource: jest.fn(), 90 init: jest.fn(), 91 name: 'default', 92 } 93 ); 94 }, 95 } as any); 96 97 const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = setupStore({ 98 datasourceInstance: datasources.someDs, 99 ...(state || {}), 100 }); 101 102 return { 103 dispatch, 104 getState, 105 datasources, 106 }; 107} 108 109describe('refreshExplore', () => { 110 it('should change data source when datasource in url changes', async () => { 111 const { dispatch, getState } = setup(); 112 await dispatch( 113 refreshExplore(ExploreId.left, serializeStateToUrlParam({ datasource: 'newDs', queries: [], range: testRange })) 114 ); 115 expect(getState().explore[ExploreId.left].datasourceInstance?.name).toBe('newDs'); 116 }); 117 118 it('should change and run new queries from the URL', async () => { 119 const { dispatch, getState, datasources } = setup(); 120 datasources.someDs.query.mockReturnValueOnce(of({})); 121 await dispatch( 122 refreshExplore( 123 ExploreId.left, 124 serializeStateToUrlParam({ datasource: 'someDs', queries: [{ expr: 'count()', refId: 'A' }], range: testRange }) 125 ) 126 ); 127 // same 128 const state = getState().explore[ExploreId.left]; 129 expect(state.datasourceInstance?.name).toBe('someDs'); 130 expect(state.queries.length).toBe(1); 131 expect(state.queries).toMatchObject([{ expr: 'count()' }]); 132 expect(datasources.someDs.query).toHaveBeenCalledTimes(1); 133 }); 134 135 it('should not do anything if pane is not initialized', async () => { 136 const { dispatch, getState } = setup({ 137 initialized: false, 138 }); 139 const state = getState(); 140 await dispatch( 141 refreshExplore( 142 ExploreId.left, 143 serializeStateToUrlParam({ datasource: 'newDs', queries: [{ expr: 'count()', refId: 'A' }], range: testRange }) 144 ) 145 ); 146 147 expect(state).toEqual(getState()); 148 }); 149}); 150