actions.test.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import { dateTime } from '@grafana/data';
  2. import { reduxTester } from '../../../../test/core/redux/reduxTester';
  3. import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
  4. import { afterEach, beforeEach } from '../../../../test/lib/common';
  5. import { notifyApp } from '../../../core/actions';
  6. import { getTimeSrv, setTimeSrv, TimeSrv } from '../../dashboard/services/TimeSrv';
  7. import { TemplateSrv } from '../../templating/template_srv';
  8. import { variableAdapters } from '../adapters';
  9. import { intervalBuilder } from '../shared/testing/builders';
  10. import { updateOptions } from '../state/actions';
  11. import { getRootReducer, RootReducerType } from '../state/helpers';
  12. import { toKeyedAction } from '../state/keyedVariablesReducer';
  13. import {
  14. addVariable,
  15. setCurrentVariableValue,
  16. variableStateFailed,
  17. variableStateFetching,
  18. } from '../state/sharedReducer';
  19. import { variablesInitTransaction } from '../state/transactionReducer';
  20. import { toKeyedVariableIdentifier, toVariablePayload } from '../utils';
  21. import { updateAutoValue, UpdateAutoValueDependencies, updateIntervalVariableOptions } from './actions';
  22. import { createIntervalVariableAdapter } from './adapter';
  23. import { createIntervalOptions } from './reducer';
  24. describe('interval actions', () => {
  25. variableAdapters.setInit(() => [createIntervalVariableAdapter()]);
  26. describe('when updateIntervalVariableOptions is dispatched', () => {
  27. it('then correct actions are dispatched', async () => {
  28. const interval = intervalBuilder()
  29. .withId('0')
  30. .withRootStateKey('key')
  31. .withQuery('1s,1m,1h,1d')
  32. .withAuto(false)
  33. .build();
  34. const tester = await reduxTester<RootReducerType>()
  35. .givenRootReducer(getRootReducer())
  36. .whenActionIsDispatched(
  37. toKeyedAction('key', addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
  38. )
  39. .whenAsyncActionIsDispatched(updateIntervalVariableOptions(toKeyedVariableIdentifier(interval)), true);
  40. tester.thenDispatchedActionsShouldEqual(
  41. toKeyedAction('key', createIntervalOptions({ type: 'interval', id: '0', data: undefined })),
  42. toKeyedAction(
  43. 'key',
  44. setCurrentVariableValue({
  45. type: 'interval',
  46. id: '0',
  47. data: { option: { text: '1s', value: '1s', selected: false } },
  48. })
  49. )
  50. );
  51. });
  52. });
  53. describe('when updateOptions is dispatched but something throws', () => {
  54. silenceConsoleOutput();
  55. const originalTimeSrv = getTimeSrv();
  56. beforeEach(() => {
  57. const timeSrvMock = {
  58. timeRange: jest.fn().mockReturnValue({
  59. from: dateTime(new Date()).subtract(1, 'days').toDate(),
  60. to: new Date(),
  61. raw: {
  62. from: 'now-1d',
  63. to: 'now',
  64. },
  65. }),
  66. } as unknown as TimeSrv;
  67. setTimeSrv(timeSrvMock);
  68. });
  69. afterEach(() => {
  70. setTimeSrv(originalTimeSrv);
  71. });
  72. it('then an notifyApp action should be dispatched', async () => {
  73. const interval = intervalBuilder()
  74. .withId('0')
  75. .withRootStateKey('key')
  76. .withQuery('1s,1m,1h,1d')
  77. .withAuto(true)
  78. .withAutoMin('1xyz') // illegal interval string
  79. .build();
  80. const tester = await reduxTester<RootReducerType>()
  81. .givenRootReducer(getRootReducer())
  82. .whenActionIsDispatched(
  83. toKeyedAction('key', addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
  84. )
  85. .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
  86. .whenAsyncActionIsDispatched(updateOptions(toKeyedVariableIdentifier(interval)), true);
  87. tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => {
  88. const expectedNumberOfActions = 4;
  89. expect(dispatchedActions[0]).toEqual(toKeyedAction('key', variableStateFetching(toVariablePayload(interval))));
  90. expect(dispatchedActions[1]).toEqual(toKeyedAction('key', createIntervalOptions(toVariablePayload(interval))));
  91. expect(dispatchedActions[2]).toEqual(
  92. toKeyedAction(
  93. 'key',
  94. variableStateFailed(
  95. toVariablePayload(interval, {
  96. error: new Error(
  97. 'Invalid interval string, has to be either unit-less or end with one of the following units: "y, M, w, d, h, m, s, ms"'
  98. ),
  99. })
  100. )
  101. )
  102. );
  103. expect(dispatchedActions[3].type).toEqual(notifyApp.type);
  104. expect(dispatchedActions[3].payload.title).toEqual('Templating [0]');
  105. expect(dispatchedActions[3].payload.text).toEqual(
  106. 'Error updating options: Invalid interval string, has to be either unit-less or end with one of the following units: "y, M, w, d, h, m, s, ms"'
  107. );
  108. expect(dispatchedActions[3].payload.severity).toEqual('error');
  109. return dispatchedActions.length === expectedNumberOfActions;
  110. });
  111. });
  112. describe('but there is no ongoing transaction', () => {
  113. it('then no actions are dispatched', async () => {
  114. const interval = intervalBuilder()
  115. .withId('0')
  116. .withRootStateKey('key')
  117. .withQuery('1s,1m,1h,1d')
  118. .withAuto(true)
  119. .withAutoMin('1xyz') // illegal interval string
  120. .build();
  121. const tester = await reduxTester<RootReducerType>()
  122. .givenRootReducer(getRootReducer())
  123. .whenActionIsDispatched(
  124. toKeyedAction('key', addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
  125. )
  126. .whenAsyncActionIsDispatched(updateOptions(toKeyedVariableIdentifier(interval)), true);
  127. tester.thenNoActionsWhereDispatched();
  128. });
  129. });
  130. });
  131. describe('when updateAutoValue is dispatched', () => {
  132. describe('and auto is false', () => {
  133. it('then no dependencies are called', async () => {
  134. const interval = intervalBuilder().withId('0').withRootStateKey('key').withAuto(false).build();
  135. const dependencies: UpdateAutoValueDependencies = {
  136. calculateInterval: jest.fn(),
  137. getTimeSrv: () => {
  138. return {
  139. timeRange: jest.fn().mockReturnValue({
  140. from: '2001-01-01',
  141. to: '2001-01-02',
  142. raw: {
  143. from: '2001-01-01',
  144. to: '2001-01-02',
  145. },
  146. }),
  147. } as unknown as TimeSrv;
  148. },
  149. templateSrv: {
  150. setGrafanaVariable: jest.fn(),
  151. } as unknown as TemplateSrv,
  152. };
  153. await reduxTester<RootReducerType>()
  154. .givenRootReducer(getRootReducer())
  155. .whenActionIsDispatched(
  156. toKeyedAction('key', addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
  157. )
  158. .whenAsyncActionIsDispatched(updateAutoValue(toKeyedVariableIdentifier(interval), dependencies), true);
  159. expect(dependencies.calculateInterval).toHaveBeenCalledTimes(0);
  160. expect(dependencies.getTimeSrv().timeRange).toHaveBeenCalledTimes(0);
  161. expect(dependencies.templateSrv.setGrafanaVariable).toHaveBeenCalledTimes(0);
  162. });
  163. });
  164. describe('and auto is true', () => {
  165. it('then correct dependencies are called', async () => {
  166. const interval = intervalBuilder()
  167. .withId('0')
  168. .withRootStateKey('key')
  169. .withName('intervalName')
  170. .withAuto(true)
  171. .withAutoCount(33)
  172. .withAutoMin('13s')
  173. .build();
  174. const timeRangeMock = jest.fn().mockReturnValue({
  175. from: '2001-01-01',
  176. to: '2001-01-02',
  177. raw: {
  178. from: '2001-01-01',
  179. to: '2001-01-02',
  180. },
  181. });
  182. const setGrafanaVariableMock = jest.fn();
  183. const dependencies: UpdateAutoValueDependencies = {
  184. calculateInterval: jest.fn().mockReturnValue({ interval: '10s' }),
  185. getTimeSrv: () => {
  186. return {
  187. timeRange: timeRangeMock,
  188. } as unknown as TimeSrv;
  189. },
  190. templateSrv: {
  191. setGrafanaVariable: setGrafanaVariableMock,
  192. } as unknown as TemplateSrv,
  193. };
  194. await reduxTester<RootReducerType>()
  195. .givenRootReducer(getRootReducer())
  196. .whenActionIsDispatched(
  197. toKeyedAction('key', addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
  198. )
  199. .whenAsyncActionIsDispatched(updateAutoValue(toKeyedVariableIdentifier(interval), dependencies), true);
  200. expect(dependencies.calculateInterval).toHaveBeenCalledTimes(1);
  201. expect(dependencies.calculateInterval).toHaveBeenCalledWith(
  202. {
  203. from: '2001-01-01',
  204. to: '2001-01-02',
  205. raw: {
  206. from: '2001-01-01',
  207. to: '2001-01-02',
  208. },
  209. },
  210. 33,
  211. '13s'
  212. );
  213. expect(timeRangeMock).toHaveBeenCalledTimes(1);
  214. expect(setGrafanaVariableMock).toHaveBeenCalledTimes(2);
  215. expect(setGrafanaVariableMock.mock.calls[0][0]).toBe('$__auto_interval_intervalName');
  216. expect(setGrafanaVariableMock.mock.calls[0][1]).toBe('10s');
  217. expect(setGrafanaVariableMock.mock.calls[1][0]).toBe('$__auto_interval');
  218. expect(setGrafanaVariableMock.mock.calls[1][1]).toBe('10s');
  219. });
  220. });
  221. });
  222. });