MuteTimings.test.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import { render, waitFor, fireEvent } from '@testing-library/react';
  2. import userEvent from '@testing-library/user-event';
  3. import React from 'react';
  4. import { Provider } from 'react-redux';
  5. import { Router } from 'react-router-dom';
  6. import { byRole, byTestId, byText } from 'testing-library-selector';
  7. import { locationService, setDataSourceSrv } from '@grafana/runtime';
  8. import { AlertManagerCortexConfig, MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
  9. import { configureStore } from 'app/store/configureStore';
  10. import MuteTimings from './MuteTimings';
  11. import { fetchAlertManagerConfig, updateAlertManagerConfig } from './api/alertmanager';
  12. import { disableRBAC, mockDataSource, MockDataSourceSrv } from './mocks';
  13. import { DataSourceType } from './utils/datasource';
  14. jest.mock('./api/alertmanager');
  15. const mocks = {
  16. api: {
  17. fetchAlertManagerConfig: jest.mocked(fetchAlertManagerConfig),
  18. updateAlertManagerConfig: jest.mocked(updateAlertManagerConfig),
  19. },
  20. };
  21. const renderMuteTimings = (location = '/alerting/routes/mute-timing/new') => {
  22. const store = configureStore();
  23. locationService.push(location);
  24. return render(
  25. <Provider store={store}>
  26. <Router history={locationService.getHistory()}>
  27. <MuteTimings />
  28. </Router>
  29. </Provider>
  30. );
  31. };
  32. const dataSources = {
  33. am: mockDataSource({
  34. name: 'Alertmanager',
  35. type: DataSourceType.Alertmanager,
  36. }),
  37. };
  38. const ui = {
  39. form: byTestId('mute-timing-form'),
  40. nameField: byTestId('mute-timing-name'),
  41. startsAt: byTestId('mute-timing-starts-at'),
  42. endsAt: byTestId('mute-timing-ends-at'),
  43. addTimeRange: byRole('button', { name: /add another time range/i }),
  44. weekdays: byTestId('mute-timing-weekdays'),
  45. days: byTestId('mute-timing-days'),
  46. months: byTestId('mute-timing-months'),
  47. years: byTestId('mute-timing-years'),
  48. addInterval: byRole('button', { name: /add another time interval/i }),
  49. submitButton: byText(/submit/i),
  50. };
  51. const muteTimeInterval: MuteTimeInterval = {
  52. name: 'default-mute',
  53. time_intervals: [
  54. {
  55. times: [
  56. {
  57. start_time: '12:00',
  58. end_time: '24:00',
  59. },
  60. ],
  61. days_of_month: ['15', '-1'],
  62. months: ['august:december', 'march'],
  63. },
  64. ],
  65. };
  66. const defaultConfig: AlertManagerCortexConfig = {
  67. alertmanager_config: {
  68. receivers: [{ name: 'default' }, { name: 'critical' }],
  69. route: {
  70. receiver: 'default',
  71. group_by: ['alertname'],
  72. routes: [
  73. {
  74. matchers: ['env=prod', 'region!=EU'],
  75. mute_time_intervals: [muteTimeInterval.name],
  76. },
  77. ],
  78. },
  79. templates: [],
  80. mute_time_intervals: [muteTimeInterval],
  81. },
  82. template_files: {},
  83. };
  84. const resetMocks = () => {
  85. jest.resetAllMocks();
  86. mocks.api.fetchAlertManagerConfig.mockImplementation(() => {
  87. return Promise.resolve({ ...defaultConfig });
  88. });
  89. mocks.api.updateAlertManagerConfig.mockImplementation(() => {
  90. return Promise.resolve();
  91. });
  92. };
  93. describe('Mute timings', () => {
  94. beforeEach(() => {
  95. setDataSourceSrv(new MockDataSourceSrv(dataSources));
  96. resetMocks();
  97. });
  98. it('creates a new mute timing', async () => {
  99. disableRBAC();
  100. await renderMuteTimings();
  101. await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled());
  102. expect(ui.nameField.get()).toBeInTheDocument();
  103. await userEvent.type(ui.nameField.get(), 'maintenance period');
  104. await userEvent.type(ui.startsAt.get(), '22:00');
  105. await userEvent.type(ui.endsAt.get(), '24:00');
  106. await userEvent.type(ui.days.get(), '-1');
  107. await userEvent.type(ui.months.get(), 'january, july');
  108. fireEvent.submit(ui.form.get());
  109. await waitFor(() => expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalled());
  110. expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalledWith('grafana', {
  111. ...defaultConfig,
  112. alertmanager_config: {
  113. ...defaultConfig.alertmanager_config,
  114. mute_time_intervals: [
  115. muteTimeInterval,
  116. {
  117. name: 'maintenance period',
  118. time_intervals: [
  119. {
  120. days_of_month: ['-1'],
  121. months: ['january', 'july'],
  122. times: [
  123. {
  124. start_time: '22:00',
  125. end_time: '24:00',
  126. },
  127. ],
  128. },
  129. ],
  130. },
  131. ],
  132. },
  133. });
  134. });
  135. it('prepoluates the form when editing a mute timing', async () => {
  136. await renderMuteTimings(
  137. '/alerting/routes/mute-timing/edit' + `?muteName=${encodeURIComponent(muteTimeInterval.name)}`
  138. );
  139. await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled());
  140. expect(ui.nameField.get()).toBeInTheDocument();
  141. expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name);
  142. expect(ui.months.get()).toHaveValue(muteTimeInterval.time_intervals[0].months?.join(', '));
  143. await userEvent.clear(ui.startsAt.getAll()?.[0]);
  144. await userEvent.clear(ui.endsAt.getAll()?.[0]);
  145. await userEvent.clear(ui.weekdays.get());
  146. await userEvent.clear(ui.days.get());
  147. await userEvent.clear(ui.months.get());
  148. await userEvent.clear(ui.years.get());
  149. await userEvent.type(ui.weekdays.get(), 'monday');
  150. await userEvent.type(ui.days.get(), '-7:-1');
  151. await userEvent.type(ui.months.get(), '3, 6, 9, 12');
  152. await userEvent.type(ui.years.get(), '2021:2024');
  153. fireEvent.submit(ui.form.get());
  154. await waitFor(() => expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalled());
  155. expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalledWith('grafana', {
  156. alertmanager_config: {
  157. receivers: [
  158. {
  159. name: 'default',
  160. },
  161. {
  162. name: 'critical',
  163. },
  164. ],
  165. route: {
  166. receiver: 'default',
  167. group_by: ['alertname'],
  168. routes: [
  169. {
  170. matchers: ['env=prod', 'region!=EU'],
  171. mute_time_intervals: ['default-mute'],
  172. },
  173. ],
  174. },
  175. templates: [],
  176. mute_time_intervals: [
  177. {
  178. name: 'default-mute',
  179. time_intervals: [
  180. {
  181. times: [],
  182. weekdays: ['monday'],
  183. days_of_month: ['-7:-1'],
  184. months: ['3', '6', '9', '12'],
  185. years: ['2021:2024'],
  186. },
  187. ],
  188. },
  189. ],
  190. },
  191. template_files: {},
  192. });
  193. });
  194. it('form is invalid with duplicate mute timing name', async () => {
  195. await renderMuteTimings();
  196. await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled());
  197. await waitFor(() => expect(ui.nameField.get()).toBeInTheDocument());
  198. await userEvent.type(ui.nameField.get(), 'default-mute');
  199. await userEvent.type(ui.days.get(), '1');
  200. await waitFor(() => expect(ui.nameField.get()).toHaveValue('default-mute'));
  201. fireEvent.submit(ui.form.get());
  202. // Form state should be invalid and prevent firing of update action
  203. await waitFor(() => expect(byRole('alert').get()).toBeInTheDocument());
  204. expect(mocks.api.updateAlertManagerConfig).not.toHaveBeenCalled();
  205. });
  206. it('replaces mute timings in routes when the mute timing name is changed', async () => {
  207. await renderMuteTimings(
  208. '/alerting/routes/mute-timing/edit' + `?muteName=${encodeURIComponent(muteTimeInterval.name)}`
  209. );
  210. await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled());
  211. expect(ui.nameField.get()).toBeInTheDocument();
  212. expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name);
  213. await userEvent.clear(ui.nameField.get());
  214. await userEvent.type(ui.nameField.get(), 'Lunch breaks');
  215. fireEvent.submit(ui.form.get());
  216. await waitFor(() => expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalled());
  217. expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalledWith('grafana', {
  218. alertmanager_config: {
  219. receivers: [
  220. {
  221. name: 'default',
  222. },
  223. {
  224. name: 'critical',
  225. },
  226. ],
  227. route: {
  228. receiver: 'default',
  229. group_by: ['alertname'],
  230. routes: [
  231. {
  232. matchers: ['env=prod', 'region!=EU'],
  233. mute_time_intervals: ['Lunch breaks'],
  234. },
  235. ],
  236. },
  237. templates: [],
  238. mute_time_intervals: [
  239. {
  240. name: 'Lunch breaks',
  241. time_intervals: [
  242. {
  243. times: [
  244. {
  245. start_time: '12:00',
  246. end_time: '24:00',
  247. },
  248. ],
  249. days_of_month: ['15', '-1'],
  250. months: ['august:december', 'march'],
  251. },
  252. ],
  253. },
  254. ],
  255. },
  256. template_files: {},
  257. });
  258. });
  259. });