actions.test.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import { refreshExplore, testDatasource, loadDatasource } from './actions';
  2. import { ExploreId, ExploreUrlState, ExploreUpdateState } from 'app/types';
  3. import { thunkTester } from 'test/core/thunk/thunkTester';
  4. import { LogsDedupStrategy } from 'app/core/logs_model';
  5. import {
  6. initializeExploreAction,
  7. InitializeExplorePayload,
  8. changeTimeAction,
  9. updateUIStateAction,
  10. setQueriesAction,
  11. testDataSourcePendingAction,
  12. testDataSourceSuccessAction,
  13. testDataSourceFailureAction,
  14. loadDatasourcePendingAction,
  15. loadDatasourceReadyAction,
  16. } from './actionTypes';
  17. import { Emitter } from 'app/core/core';
  18. import { ActionOf } from 'app/core/redux/actionCreatorFactory';
  19. import { makeInitialUpdateState } from './reducers';
  20. import { DataQuery } from '@grafana/ui/src/types/datasource';
  21. jest.mock('app/features/plugins/datasource_srv', () => ({
  22. getDatasourceSrv: () => ({
  23. getExternal: jest.fn().mockReturnValue([]),
  24. get: jest.fn().mockReturnValue({
  25. testDatasource: jest.fn(),
  26. init: jest.fn(),
  27. }),
  28. }),
  29. }));
  30. const setup = (updateOverides?: Partial<ExploreUpdateState>) => {
  31. const exploreId = ExploreId.left;
  32. const containerWidth = 1920;
  33. const eventBridge = {} as Emitter;
  34. const ui = { dedupStrategy: LogsDedupStrategy.none, showingGraph: false, showingLogs: false, showingTable: false };
  35. const range = { from: 'now', to: 'now' };
  36. const urlState: ExploreUrlState = { datasource: 'some-datasource', queries: [], range, ui };
  37. const updateDefaults = makeInitialUpdateState();
  38. const update = { ...updateDefaults, ...updateOverides };
  39. const initialState = {
  40. explore: {
  41. [exploreId]: {
  42. initialized: true,
  43. urlState,
  44. containerWidth,
  45. eventBridge,
  46. update,
  47. datasourceInstance: { name: 'some-datasource' },
  48. queries: [] as DataQuery[],
  49. range,
  50. ui,
  51. },
  52. },
  53. };
  54. return {
  55. initialState,
  56. exploreId,
  57. range,
  58. ui,
  59. containerWidth,
  60. eventBridge,
  61. };
  62. };
  63. describe('refreshExplore', () => {
  64. describe('when explore is initialized', () => {
  65. describe('and update datasource is set', () => {
  66. it('then it should dispatch initializeExplore', async () => {
  67. const { exploreId, ui, range, initialState, containerWidth, eventBridge } = setup({ datasource: true });
  68. const dispatchedActions = await thunkTester(initialState)
  69. .givenThunk(refreshExplore)
  70. .whenThunkIsDispatched(exploreId);
  71. const initializeExplore = dispatchedActions[2] as ActionOf<InitializeExplorePayload>;
  72. const { type, payload } = initializeExplore;
  73. expect(type).toEqual(initializeExploreAction.type);
  74. expect(payload.containerWidth).toEqual(containerWidth);
  75. expect(payload.eventBridge).toEqual(eventBridge);
  76. expect(payload.queries.length).toBe(1); // Queries have generated keys hard to expect on
  77. expect(payload.range).toEqual(range);
  78. expect(payload.ui).toEqual(ui);
  79. });
  80. });
  81. describe('and update range is set', () => {
  82. it('then it should dispatch changeTimeAction', async () => {
  83. const { exploreId, range, initialState } = setup({ range: true });
  84. const dispatchedActions = await thunkTester(initialState)
  85. .givenThunk(refreshExplore)
  86. .whenThunkIsDispatched(exploreId);
  87. expect(dispatchedActions[0].type).toEqual(changeTimeAction.type);
  88. expect(dispatchedActions[0].payload).toEqual({ exploreId, range });
  89. });
  90. });
  91. describe('and update ui is set', () => {
  92. it('then it should dispatch updateUIStateAction', async () => {
  93. const { exploreId, initialState, ui } = setup({ ui: true });
  94. const dispatchedActions = await thunkTester(initialState)
  95. .givenThunk(refreshExplore)
  96. .whenThunkIsDispatched(exploreId);
  97. expect(dispatchedActions[0].type).toEqual(updateUIStateAction.type);
  98. expect(dispatchedActions[0].payload).toEqual({ ...ui, exploreId });
  99. });
  100. });
  101. describe('and update queries is set', () => {
  102. it('then it should dispatch setQueriesAction', async () => {
  103. const { exploreId, initialState } = setup({ queries: true });
  104. const dispatchedActions = await thunkTester(initialState)
  105. .givenThunk(refreshExplore)
  106. .whenThunkIsDispatched(exploreId);
  107. expect(dispatchedActions[0].type).toEqual(setQueriesAction.type);
  108. expect(dispatchedActions[0].payload).toEqual({ exploreId, queries: [] });
  109. });
  110. });
  111. });
  112. describe('when update is not initialized', () => {
  113. it('then it should not dispatch any actions', async () => {
  114. const exploreId = ExploreId.left;
  115. const initialState = { explore: { [exploreId]: { initialized: false } } };
  116. const dispatchedActions = await thunkTester(initialState)
  117. .givenThunk(refreshExplore)
  118. .whenThunkIsDispatched(exploreId);
  119. expect(dispatchedActions).toEqual([]);
  120. });
  121. });
  122. });
  123. describe('test datasource', () => {
  124. describe('when testDatasource thunk is dispatched', () => {
  125. describe('and testDatasource call on instance is successful', () => {
  126. it('then it should dispatch testDataSourceSuccessAction', async () => {
  127. const exploreId = ExploreId.left;
  128. const mockDatasourceInstance = {
  129. testDatasource: () => {
  130. return Promise.resolve({ status: 'success' });
  131. },
  132. };
  133. const dispatchedActions = await thunkTester({})
  134. .givenThunk(testDatasource)
  135. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  136. expect(dispatchedActions).toEqual([
  137. testDataSourcePendingAction({ exploreId }),
  138. testDataSourceSuccessAction({ exploreId }),
  139. ]);
  140. });
  141. });
  142. describe('and testDatasource call on instance is not successful', () => {
  143. it('then it should dispatch testDataSourceFailureAction', async () => {
  144. const exploreId = ExploreId.left;
  145. const error = 'something went wrong';
  146. const mockDatasourceInstance = {
  147. testDatasource: () => {
  148. return Promise.resolve({ status: 'fail', message: error });
  149. },
  150. };
  151. const dispatchedActions = await thunkTester({})
  152. .givenThunk(testDatasource)
  153. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  154. expect(dispatchedActions).toEqual([
  155. testDataSourcePendingAction({ exploreId }),
  156. testDataSourceFailureAction({ exploreId, error }),
  157. ]);
  158. });
  159. });
  160. describe('and testDatasource call on instance throws', () => {
  161. it('then it should dispatch testDataSourceFailureAction', async () => {
  162. const exploreId = ExploreId.left;
  163. const error = 'something went wrong';
  164. const mockDatasourceInstance = {
  165. testDatasource: () => {
  166. throw { statusText: error };
  167. },
  168. };
  169. const dispatchedActions = await thunkTester({})
  170. .givenThunk(testDatasource)
  171. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  172. expect(dispatchedActions).toEqual([
  173. testDataSourcePendingAction({ exploreId }),
  174. testDataSourceFailureAction({ exploreId, error }),
  175. ]);
  176. });
  177. });
  178. });
  179. });
  180. describe('loading datasource', () => {
  181. describe('when loadDatasource thunk is dispatched', () => {
  182. describe('and all goes fine', () => {
  183. it('then it should dispatch correct actions', async () => {
  184. const exploreId = ExploreId.left;
  185. const name = 'some-datasource';
  186. const initialState = { explore: { [exploreId]: { requestedDatasourceName: name } } };
  187. const mockDatasourceInstance = {
  188. testDatasource: () => {
  189. return Promise.resolve({ status: 'success' });
  190. },
  191. name,
  192. init: jest.fn(),
  193. meta: { id: 'some id' },
  194. };
  195. const dispatchedActions = await thunkTester(initialState)
  196. .givenThunk(loadDatasource)
  197. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  198. expect(dispatchedActions).toEqual([
  199. loadDatasourcePendingAction({
  200. exploreId,
  201. requestedDatasourceName: mockDatasourceInstance.name,
  202. }),
  203. testDataSourcePendingAction({ exploreId }),
  204. testDataSourceSuccessAction({ exploreId }),
  205. loadDatasourceReadyAction({ exploreId, history: [] }),
  206. ]);
  207. });
  208. });
  209. describe('and user changes datasource during load', () => {
  210. it('then it should dispatch correct actions', async () => {
  211. const exploreId = ExploreId.left;
  212. const name = 'some-datasource';
  213. const initialState = { explore: { [exploreId]: { requestedDatasourceName: 'some-other-datasource' } } };
  214. const mockDatasourceInstance = {
  215. testDatasource: () => {
  216. return Promise.resolve({ status: 'success' });
  217. },
  218. name,
  219. init: jest.fn(),
  220. meta: { id: 'some id' },
  221. };
  222. const dispatchedActions = await thunkTester(initialState)
  223. .givenThunk(loadDatasource)
  224. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  225. expect(dispatchedActions).toEqual([
  226. loadDatasourcePendingAction({
  227. exploreId,
  228. requestedDatasourceName: mockDatasourceInstance.name,
  229. }),
  230. testDataSourcePendingAction({ exploreId }),
  231. testDataSourceSuccessAction({ exploreId }),
  232. ]);
  233. });
  234. });
  235. });
  236. });