actions.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import { of } from 'rxjs';
  2. import { thunkTester } from 'test/core/thunk/thunkTester';
  3. import { BackendSrvRequest, FetchResponse } from '@grafana/runtime';
  4. import { getBackendSrv } from 'app/core/services/backend_srv';
  5. import { ThunkResult, ThunkDispatch } from 'app/types';
  6. import { getMockPlugin, getMockPlugins } from '../../plugins/__mocks__/pluginMocks';
  7. import { GenericDataSourcePlugin } from '../settings/PluginSettings';
  8. import { initDataSourceSettings } from '../state/actions';
  9. import {
  10. findNewName,
  11. nameExits,
  12. InitDataSourceSettingDependencies,
  13. testDataSource,
  14. TestDataSourceDependencies,
  15. getDataSourceUsingUidOrId,
  16. } from './actions';
  17. import {
  18. initDataSourceSettingsSucceeded,
  19. initDataSourceSettingsFailed,
  20. testDataSourceStarting,
  21. testDataSourceSucceeded,
  22. testDataSourceFailed,
  23. } from './reducers';
  24. jest.mock('app/core/services/backend_srv');
  25. jest.mock('@grafana/runtime', () => ({
  26. ...(jest.requireActual('@grafana/runtime') as unknown as object),
  27. getBackendSrv: jest.fn(),
  28. }));
  29. const getBackendSrvMock = () =>
  30. ({
  31. get: jest.fn().mockReturnValue({
  32. testDatasource: jest.fn().mockReturnValue({
  33. status: '',
  34. message: '',
  35. }),
  36. }),
  37. withNoBackendCache: jest.fn().mockImplementationOnce((cb) => cb()),
  38. } as any);
  39. const failDataSourceTest = async (error: object) => {
  40. const dependencies: TestDataSourceDependencies = {
  41. getDatasourceSrv: () =>
  42. ({
  43. get: jest.fn().mockReturnValue({
  44. testDatasource: jest.fn().mockImplementation(() => {
  45. throw error;
  46. }),
  47. }),
  48. } as any),
  49. getBackendSrv: getBackendSrvMock,
  50. };
  51. const state = {
  52. testingStatus: {
  53. message: '',
  54. status: '',
  55. },
  56. };
  57. const dispatchedActions = await thunkTester(state)
  58. .givenThunk(testDataSource)
  59. .whenThunkIsDispatched('Azure Monitor', dependencies);
  60. return dispatchedActions;
  61. };
  62. describe('getDataSourceUsingUidOrId', () => {
  63. const uidResponse = {
  64. ok: true,
  65. data: {
  66. id: 111,
  67. uid: 'abcdefg',
  68. },
  69. };
  70. const idResponse = {
  71. ok: true,
  72. data: {
  73. id: 222,
  74. uid: 'xyz',
  75. },
  76. };
  77. it('should return UID response data', async () => {
  78. (getBackendSrv as jest.Mock).mockReturnValueOnce({
  79. fetch: (options: BackendSrvRequest) => {
  80. return of(uidResponse as FetchResponse);
  81. },
  82. });
  83. expect(await getDataSourceUsingUidOrId('abcdefg')).toBe(uidResponse.data);
  84. });
  85. it('should return ID response data', async () => {
  86. const uidResponse = {
  87. ok: false,
  88. };
  89. (getBackendSrv as jest.Mock)
  90. .mockReturnValueOnce({
  91. fetch: (options: BackendSrvRequest) => {
  92. return of(uidResponse as FetchResponse);
  93. },
  94. })
  95. .mockReturnValueOnce({
  96. fetch: (options: BackendSrvRequest) => {
  97. return of(idResponse as FetchResponse);
  98. },
  99. });
  100. expect(await getDataSourceUsingUidOrId(222)).toBe(idResponse.data);
  101. });
  102. it('should return empty response data', async () => {
  103. // @ts-ignore
  104. delete window.location;
  105. window.location = {} as Location;
  106. const uidResponse = {
  107. ok: false,
  108. };
  109. (getBackendSrv as jest.Mock)
  110. .mockReturnValueOnce({
  111. fetch: (options: BackendSrvRequest) => {
  112. return of(uidResponse as FetchResponse);
  113. },
  114. })
  115. .mockReturnValueOnce({
  116. fetch: (options: BackendSrvRequest) => {
  117. return of(idResponse as FetchResponse);
  118. },
  119. });
  120. expect(await getDataSourceUsingUidOrId('222')).toStrictEqual({});
  121. expect(window.location.href).toBe('/datasources/edit/xyz');
  122. });
  123. });
  124. describe('Name exists', () => {
  125. const plugins = getMockPlugins(5);
  126. it('should be true', () => {
  127. const name = 'pretty cool plugin-1';
  128. expect(nameExits(plugins, name)).toEqual(true);
  129. });
  130. it('should be false', () => {
  131. const name = 'pretty cool plugin-6';
  132. expect(nameExits(plugins, name));
  133. });
  134. });
  135. describe('Find new name', () => {
  136. it('should create a new name', () => {
  137. const plugins = getMockPlugins(5);
  138. const name = 'pretty cool plugin-1';
  139. expect(findNewName(plugins, name)).toEqual('pretty cool plugin-6');
  140. });
  141. it('should create new name without suffix', () => {
  142. const plugin = getMockPlugin();
  143. plugin.name = 'prometheus';
  144. const plugins = [plugin];
  145. const name = 'prometheus';
  146. expect(findNewName(plugins, name)).toEqual('prometheus-1');
  147. });
  148. it('should handle names that end with -', () => {
  149. const plugin = getMockPlugin();
  150. const plugins = [plugin];
  151. const name = 'pretty cool plugin-';
  152. expect(findNewName(plugins, name)).toEqual('pretty cool plugin-');
  153. });
  154. });
  155. describe('initDataSourceSettings', () => {
  156. describe('when pageId is missing', () => {
  157. it('then initDataSourceSettingsFailed should be dispatched', async () => {
  158. const dispatchedActions = await thunkTester({}).givenThunk(initDataSourceSettings).whenThunkIsDispatched('');
  159. expect(dispatchedActions).toEqual([initDataSourceSettingsFailed(new Error('Invalid ID'))]);
  160. });
  161. });
  162. describe('when pageId is a valid', () => {
  163. it('then initDataSourceSettingsSucceeded should be dispatched', async () => {
  164. const dataSource = { type: 'app' };
  165. const dataSourceMeta = { id: 'some id' };
  166. const dependencies: InitDataSourceSettingDependencies = {
  167. loadDataSource: jest.fn((): ThunkResult<void> => (dispatch: ThunkDispatch, getState) => dataSource) as any,
  168. loadDataSourceMeta: jest.fn((): ThunkResult<void> => (dispatch: ThunkDispatch, getState) => {}),
  169. getDataSource: jest.fn().mockReturnValue(dataSource),
  170. getDataSourceMeta: jest.fn().mockReturnValue(dataSourceMeta),
  171. importDataSourcePlugin: jest.fn().mockReturnValue({} as GenericDataSourcePlugin),
  172. };
  173. const state = {
  174. dataSourceSettings: {},
  175. dataSources: {},
  176. };
  177. const dispatchedActions = await thunkTester(state)
  178. .givenThunk(initDataSourceSettings)
  179. .whenThunkIsDispatched(256, dependencies);
  180. expect(dispatchedActions).toEqual([initDataSourceSettingsSucceeded({} as GenericDataSourcePlugin)]);
  181. expect(dependencies.loadDataSource).toHaveBeenCalledTimes(1);
  182. expect(dependencies.loadDataSource).toHaveBeenCalledWith(256);
  183. expect(dependencies.loadDataSourceMeta).toHaveBeenCalledTimes(1);
  184. expect(dependencies.loadDataSourceMeta).toHaveBeenCalledWith(dataSource);
  185. expect(dependencies.getDataSource).toHaveBeenCalledTimes(1);
  186. expect(dependencies.getDataSource).toHaveBeenCalledWith({}, 256);
  187. expect(dependencies.getDataSourceMeta).toHaveBeenCalledTimes(1);
  188. expect(dependencies.getDataSourceMeta).toHaveBeenCalledWith({}, 'app');
  189. expect(dependencies.importDataSourcePlugin).toHaveBeenCalledTimes(1);
  190. expect(dependencies.importDataSourcePlugin).toHaveBeenCalledWith(dataSourceMeta);
  191. });
  192. });
  193. describe('when plugin loading fails', () => {
  194. it('then initDataSourceSettingsFailed should be dispatched', async () => {
  195. const dataSource = { type: 'app' };
  196. const dependencies: InitDataSourceSettingDependencies = {
  197. loadDataSource: jest.fn((): ThunkResult<void> => (dispatch: ThunkDispatch, getState) => dataSource) as any,
  198. loadDataSourceMeta: jest.fn().mockImplementation(() => {
  199. throw new Error('Error loading plugin');
  200. }),
  201. getDataSource: jest.fn(),
  202. getDataSourceMeta: jest.fn(),
  203. importDataSourcePlugin: jest.fn(),
  204. };
  205. const state = {
  206. dataSourceSettings: {},
  207. dataSources: {},
  208. };
  209. const dispatchedActions = await thunkTester(state)
  210. .givenThunk(initDataSourceSettings)
  211. .whenThunkIsDispatched(301, dependencies);
  212. expect(dispatchedActions).toEqual([initDataSourceSettingsFailed(new Error('Error loading plugin'))]);
  213. expect(dependencies.loadDataSource).toHaveBeenCalledTimes(1);
  214. expect(dependencies.loadDataSource).toHaveBeenCalledWith(301);
  215. expect(dependencies.loadDataSourceMeta).toHaveBeenCalledTimes(1);
  216. expect(dependencies.loadDataSourceMeta).toHaveBeenCalledWith(dataSource);
  217. });
  218. });
  219. });
  220. describe('testDataSource', () => {
  221. describe('when a datasource is tested', () => {
  222. it('then testDataSourceStarting and testDataSourceSucceeded should be dispatched', async () => {
  223. const dependencies: TestDataSourceDependencies = {
  224. getDatasourceSrv: () =>
  225. ({
  226. get: jest.fn().mockReturnValue({
  227. testDatasource: jest.fn().mockReturnValue({
  228. status: '',
  229. message: '',
  230. }),
  231. }),
  232. } as any),
  233. getBackendSrv: getBackendSrvMock,
  234. };
  235. const state = {
  236. testingStatus: {
  237. status: '',
  238. message: '',
  239. },
  240. };
  241. const dispatchedActions = await thunkTester(state)
  242. .givenThunk(testDataSource)
  243. .whenThunkIsDispatched('Azure Monitor', dependencies);
  244. expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceSucceeded(state.testingStatus)]);
  245. });
  246. it('then testDataSourceFailed should be dispatched', async () => {
  247. const dependencies: TestDataSourceDependencies = {
  248. getDatasourceSrv: () =>
  249. ({
  250. get: jest.fn().mockReturnValue({
  251. testDatasource: jest.fn().mockImplementation(() => {
  252. throw new Error('Error testing datasource');
  253. }),
  254. }),
  255. } as any),
  256. getBackendSrv: getBackendSrvMock,
  257. };
  258. const result = {
  259. message: 'Error testing datasource',
  260. };
  261. const state = {
  262. testingStatus: {
  263. message: '',
  264. status: '',
  265. },
  266. };
  267. const dispatchedActions = await thunkTester(state)
  268. .givenThunk(testDataSource)
  269. .whenThunkIsDispatched('Azure Monitor', dependencies);
  270. expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceFailed(result)]);
  271. });
  272. it('then testDataSourceFailed should be dispatched with response error message', async () => {
  273. const result = {
  274. message: 'Error testing datasource',
  275. };
  276. const dispatchedActions = await failDataSourceTest({
  277. message: 'Error testing datasource',
  278. data: { message: 'Response error message' },
  279. statusText: 'Bad Request',
  280. });
  281. expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceFailed(result)]);
  282. });
  283. it('then testDataSourceFailed should be dispatched with response data message', async () => {
  284. const result = {
  285. message: 'Response error message',
  286. };
  287. const dispatchedActions = await failDataSourceTest({
  288. data: { message: 'Response error message' },
  289. statusText: 'Bad Request',
  290. });
  291. expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceFailed(result)]);
  292. });
  293. it('then testDataSourceFailed should be dispatched with response statusText', async () => {
  294. const result = {
  295. message: 'HTTP error Bad Request',
  296. };
  297. const dispatchedActions = await failDataSourceTest({ data: {}, statusText: 'Bad Request' });
  298. expect(dispatchedActions).toEqual([testDataSourceStarting(), testDataSourceFailed(result)]);
  299. });
  300. });
  301. });