actions.test.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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 = {
  37. datasource: 'some-datasource',
  38. queries: [],
  39. range,
  40. ui,
  41. };
  42. const updateDefaults = makeInitialUpdateState();
  43. const update = { ...updateDefaults, ...updateOverides };
  44. const initialState = {
  45. explore: {
  46. [exploreId]: {
  47. initialized: true,
  48. urlState,
  49. containerWidth,
  50. eventBridge,
  51. update,
  52. datasourceInstance: { name: 'some-datasource' },
  53. queries: [] as DataQuery[],
  54. range,
  55. ui,
  56. refreshInterval: {
  57. label: 'Off',
  58. value: 0,
  59. },
  60. },
  61. },
  62. };
  63. return {
  64. initialState,
  65. exploreId,
  66. range,
  67. ui,
  68. containerWidth,
  69. eventBridge,
  70. };
  71. };
  72. describe('refreshExplore', () => {
  73. describe('when explore is initialized', () => {
  74. describe('and update datasource is set', () => {
  75. it('then it should dispatch initializeExplore', async () => {
  76. const { exploreId, ui, range, initialState, containerWidth, eventBridge } = setup({ datasource: true });
  77. const dispatchedActions = await thunkTester(initialState)
  78. .givenThunk(refreshExplore)
  79. .whenThunkIsDispatched(exploreId);
  80. const initializeExplore = dispatchedActions[2] as ActionOf<InitializeExplorePayload>;
  81. const { type, payload } = initializeExplore;
  82. expect(type).toEqual(initializeExploreAction.type);
  83. expect(payload.containerWidth).toEqual(containerWidth);
  84. expect(payload.eventBridge).toEqual(eventBridge);
  85. expect(payload.queries.length).toBe(1); // Queries have generated keys hard to expect on
  86. expect(payload.range).toEqual(range);
  87. expect(payload.ui).toEqual(ui);
  88. });
  89. });
  90. describe('and update range is set', () => {
  91. it('then it should dispatch changeTimeAction', async () => {
  92. const { exploreId, range, initialState } = setup({ range: true });
  93. const dispatchedActions = await thunkTester(initialState)
  94. .givenThunk(refreshExplore)
  95. .whenThunkIsDispatched(exploreId);
  96. expect(dispatchedActions[0].type).toEqual(changeTimeAction.type);
  97. expect(dispatchedActions[0].payload).toEqual({ exploreId, range });
  98. });
  99. });
  100. describe('and update ui is set', () => {
  101. it('then it should dispatch updateUIStateAction', async () => {
  102. const { exploreId, initialState, ui } = setup({ ui: true });
  103. const dispatchedActions = await thunkTester(initialState)
  104. .givenThunk(refreshExplore)
  105. .whenThunkIsDispatched(exploreId);
  106. expect(dispatchedActions[0].type).toEqual(updateUIStateAction.type);
  107. expect(dispatchedActions[0].payload).toEqual({ ...ui, exploreId });
  108. });
  109. });
  110. describe('and update queries is set', () => {
  111. it('then it should dispatch setQueriesAction', async () => {
  112. const { exploreId, initialState } = setup({ queries: true });
  113. const dispatchedActions = await thunkTester(initialState)
  114. .givenThunk(refreshExplore)
  115. .whenThunkIsDispatched(exploreId);
  116. expect(dispatchedActions[0].type).toEqual(setQueriesAction.type);
  117. expect(dispatchedActions[0].payload).toEqual({ exploreId, queries: [] });
  118. });
  119. });
  120. });
  121. describe('when update is not initialized', () => {
  122. it('then it should not dispatch any actions', async () => {
  123. const exploreId = ExploreId.left;
  124. const initialState = { explore: { [exploreId]: { initialized: false } } };
  125. const dispatchedActions = await thunkTester(initialState)
  126. .givenThunk(refreshExplore)
  127. .whenThunkIsDispatched(exploreId);
  128. expect(dispatchedActions).toEqual([]);
  129. });
  130. });
  131. });
  132. describe('test datasource', () => {
  133. describe('when testDatasource thunk is dispatched', () => {
  134. describe('and testDatasource call on instance is successful', () => {
  135. it('then it should dispatch testDataSourceSuccessAction', async () => {
  136. const exploreId = ExploreId.left;
  137. const mockDatasourceInstance = {
  138. testDatasource: () => {
  139. return Promise.resolve({ status: 'success' });
  140. },
  141. };
  142. const dispatchedActions = await thunkTester({})
  143. .givenThunk(testDatasource)
  144. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  145. expect(dispatchedActions).toEqual([
  146. testDataSourcePendingAction({ exploreId }),
  147. testDataSourceSuccessAction({ exploreId }),
  148. ]);
  149. });
  150. });
  151. describe('and testDatasource call on instance is not successful', () => {
  152. it('then it should dispatch testDataSourceFailureAction', async () => {
  153. const exploreId = ExploreId.left;
  154. const error = 'something went wrong';
  155. const mockDatasourceInstance = {
  156. testDatasource: () => {
  157. return Promise.resolve({ status: 'fail', message: error });
  158. },
  159. };
  160. const dispatchedActions = await thunkTester({})
  161. .givenThunk(testDatasource)
  162. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  163. expect(dispatchedActions).toEqual([
  164. testDataSourcePendingAction({ exploreId }),
  165. testDataSourceFailureAction({ exploreId, error }),
  166. ]);
  167. });
  168. });
  169. describe('and testDatasource call on instance throws', () => {
  170. it('then it should dispatch testDataSourceFailureAction', async () => {
  171. const exploreId = ExploreId.left;
  172. const error = 'something went wrong';
  173. const mockDatasourceInstance = {
  174. testDatasource: () => {
  175. throw { statusText: error };
  176. },
  177. };
  178. const dispatchedActions = await thunkTester({})
  179. .givenThunk(testDatasource)
  180. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  181. expect(dispatchedActions).toEqual([
  182. testDataSourcePendingAction({ exploreId }),
  183. testDataSourceFailureAction({ exploreId, error }),
  184. ]);
  185. });
  186. });
  187. });
  188. });
  189. describe('loading datasource', () => {
  190. describe('when loadDatasource thunk is dispatched', () => {
  191. describe('and all goes fine', () => {
  192. it('then it should dispatch correct actions', async () => {
  193. const exploreId = ExploreId.left;
  194. const name = 'some-datasource';
  195. const initialState = { explore: { [exploreId]: { requestedDatasourceName: name } } };
  196. const mockDatasourceInstance = {
  197. testDatasource: () => {
  198. return Promise.resolve({ status: 'success' });
  199. },
  200. name,
  201. init: jest.fn(),
  202. meta: { id: 'some id' },
  203. };
  204. const dispatchedActions = await thunkTester(initialState)
  205. .givenThunk(loadDatasource)
  206. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  207. expect(dispatchedActions).toEqual([
  208. loadDatasourcePendingAction({
  209. exploreId,
  210. requestedDatasourceName: mockDatasourceInstance.name,
  211. }),
  212. testDataSourcePendingAction({ exploreId }),
  213. testDataSourceSuccessAction({ exploreId }),
  214. loadDatasourceReadyAction({ exploreId, history: [] }),
  215. ]);
  216. });
  217. });
  218. describe('and user changes datasource during load', () => {
  219. it('then it should dispatch correct actions', async () => {
  220. const exploreId = ExploreId.left;
  221. const name = 'some-datasource';
  222. const initialState = { explore: { [exploreId]: { requestedDatasourceName: 'some-other-datasource' } } };
  223. const mockDatasourceInstance = {
  224. testDatasource: () => {
  225. return Promise.resolve({ status: 'success' });
  226. },
  227. name,
  228. init: jest.fn(),
  229. meta: { id: 'some id' },
  230. };
  231. const dispatchedActions = await thunkTester(initialState)
  232. .givenThunk(loadDatasource)
  233. .whenThunkIsDispatched(exploreId, mockDatasourceInstance);
  234. expect(dispatchedActions).toEqual([
  235. loadDatasourcePendingAction({
  236. exploreId,
  237. requestedDatasourceName: mockDatasourceInstance.name,
  238. }),
  239. testDataSourcePendingAction({ exploreId }),
  240. testDataSourceSuccessAction({ exploreId }),
  241. ]);
  242. });
  243. });
  244. });
  245. });