queryHistory.test.tsx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import React from 'react';
  2. import { of } from 'rxjs';
  3. import { serializeStateToUrlParam } from '@grafana/data';
  4. import { config } from '@grafana/runtime';
  5. import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
  6. import { ExploreId } from '../../../types';
  7. import {
  8. assertDataSourceFilterVisibility,
  9. assertLoadMoreQueryHistoryNotVisible,
  10. assertQueryHistory,
  11. assertQueryHistoryComment,
  12. assertQueryHistoryElementsShown,
  13. assertQueryHistoryExists,
  14. assertQueryHistoryIsStarred,
  15. assertQueryHistoryTabIsSelected,
  16. } from './helper/assert';
  17. import {
  18. commentQueryHistory,
  19. closeQueryHistory,
  20. deleteQueryHistory,
  21. inputQuery,
  22. loadMoreQueryHistory,
  23. openQueryHistory,
  24. runQuery,
  25. selectOnlyActiveDataSource,
  26. selectStarredTabFirst,
  27. starQueryHistory,
  28. switchToQueryHistoryTab,
  29. } from './helper/interactions';
  30. import { makeLogsQueryResponse } from './helper/query';
  31. import {
  32. localStorageHasAlreadyBeenMigrated,
  33. setupExplore,
  34. setupLocalStorageRichHistory,
  35. tearDown,
  36. waitForExplore,
  37. } from './helper/setup';
  38. const fetchMock = jest.fn();
  39. const postMock = jest.fn();
  40. const getMock = jest.fn();
  41. const reportInteractionMock = jest.fn();
  42. jest.mock('@grafana/runtime', () => ({
  43. ...jest.requireActual('@grafana/runtime'),
  44. getBackendSrv: () => ({ fetch: fetchMock, post: postMock, get: getMock }),
  45. reportInteraction: (...args: object[]) => {
  46. reportInteractionMock(...args);
  47. },
  48. }));
  49. jest.mock('app/core/core', () => ({
  50. contextSrv: {
  51. hasAccess: () => true,
  52. isSignedIn: true,
  53. },
  54. }));
  55. jest.mock('app/core/services/PreferencesService', () => ({
  56. PreferencesService: function () {
  57. return {
  58. patch: jest.fn(),
  59. load: jest.fn().mockResolvedValue({
  60. queryHistory: {
  61. homeTab: 'query',
  62. },
  63. }),
  64. };
  65. },
  66. }));
  67. jest.mock('react-virtualized-auto-sizer', () => {
  68. return {
  69. __esModule: true,
  70. default(props: any) {
  71. return <div>{props.children({ width: 1000 })}</div>;
  72. },
  73. };
  74. });
  75. describe('Explore: Query History', () => {
  76. const USER_INPUT = 'my query';
  77. const RAW_QUERY = `{"expr":"${USER_INPUT}"}`;
  78. silenceConsoleOutput();
  79. afterEach(() => {
  80. config.queryHistoryEnabled = false;
  81. fetchMock.mockClear();
  82. postMock.mockClear();
  83. getMock.mockClear();
  84. reportInteractionMock.mockClear();
  85. tearDown();
  86. });
  87. it('adds new query history items after the query is run.', async () => {
  88. // when Explore is opened
  89. const { datasources, unmount } = setupExplore();
  90. (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
  91. await waitForExplore();
  92. // and a user runs a query and opens query history
  93. await inputQuery(USER_INPUT);
  94. await runQuery();
  95. await openQueryHistory();
  96. // the query that was run is in query history
  97. await assertQueryHistoryExists(RAW_QUERY);
  98. // when Explore is opened again
  99. unmount();
  100. setupExplore({ clearLocalStorage: false });
  101. await waitForExplore();
  102. // previously added query is in query history
  103. await openQueryHistory();
  104. await assertQueryHistoryExists(RAW_QUERY);
  105. expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', {
  106. queryHistoryEnabled: false,
  107. });
  108. });
  109. it('adds recently added query if the query history panel is already open', async () => {
  110. const urlParams = {
  111. left: serializeStateToUrlParam({
  112. datasource: 'loki',
  113. queries: [{ refId: 'A', expr: 'query #1' }],
  114. range: { from: 'now-1h', to: 'now' },
  115. }),
  116. };
  117. const { datasources } = setupExplore({ urlParams });
  118. (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
  119. await waitForExplore();
  120. await openQueryHistory();
  121. await inputQuery('query #2');
  122. await runQuery();
  123. await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}']);
  124. });
  125. it('updates the state in both Explore panes', async () => {
  126. const urlParams = {
  127. left: serializeStateToUrlParam({
  128. datasource: 'loki',
  129. queries: [{ refId: 'A', expr: 'query #1' }],
  130. range: { from: 'now-1h', to: 'now' },
  131. }),
  132. right: serializeStateToUrlParam({
  133. datasource: 'loki',
  134. queries: [{ refId: 'A', expr: 'query #2' }],
  135. range: { from: 'now-1h', to: 'now' },
  136. }),
  137. };
  138. const { datasources } = setupExplore({ urlParams });
  139. (datasources.loki.query as jest.Mock).mockReturnValue(makeLogsQueryResponse());
  140. await waitForExplore();
  141. await waitForExplore(ExploreId.right);
  142. // queries in history
  143. await openQueryHistory(ExploreId.left);
  144. await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}'], ExploreId.left);
  145. await openQueryHistory(ExploreId.right);
  146. await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}'], ExploreId.right);
  147. // star one one query
  148. starQueryHistory(1, ExploreId.left);
  149. await assertQueryHistoryIsStarred([false, true], ExploreId.left);
  150. await assertQueryHistoryIsStarred([false, true], ExploreId.right);
  151. expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_starred', {
  152. queryHistoryEnabled: false,
  153. newValue: true,
  154. });
  155. deleteQueryHistory(0, ExploreId.left);
  156. await assertQueryHistory(['{"expr":"query #1"}'], ExploreId.left);
  157. await assertQueryHistory(['{"expr":"query #1"}'], ExploreId.right);
  158. expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_deleted', {
  159. queryHistoryEnabled: false,
  160. });
  161. });
  162. it('add comments to query history', async () => {
  163. const urlParams = {
  164. left: serializeStateToUrlParam({
  165. datasource: 'loki',
  166. queries: [{ refId: 'A', expr: 'query #1' }],
  167. range: { from: 'now-1h', to: 'now' },
  168. }),
  169. };
  170. const { datasources } = setupExplore({ urlParams });
  171. (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
  172. await waitForExplore();
  173. await openQueryHistory();
  174. await assertQueryHistory(['{"expr":"query #1"}'], ExploreId.left);
  175. await commentQueryHistory(0, 'test comment');
  176. await assertQueryHistoryComment(['test comment'], ExploreId.left);
  177. });
  178. it('updates query history settings', async () => {
  179. // open settings page
  180. setupExplore();
  181. await waitForExplore();
  182. await openQueryHistory();
  183. // assert default values
  184. assertQueryHistoryTabIsSelected('Query history');
  185. assertDataSourceFilterVisibility(true);
  186. await switchToQueryHistoryTab('Settings');
  187. // change settings
  188. await selectStarredTabFirst();
  189. await selectOnlyActiveDataSource();
  190. await closeQueryHistory();
  191. await openQueryHistory();
  192. // assert new settings
  193. assertQueryHistoryTabIsSelected('Starred');
  194. assertDataSourceFilterVisibility(false);
  195. });
  196. describe('local storage migration', () => {
  197. it('does not migrate if query history is not enabled', async () => {
  198. config.queryHistoryEnabled = false;
  199. const { datasources } = setupExplore();
  200. setupLocalStorageRichHistory('loki');
  201. (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
  202. getMock.mockReturnValue({ result: { queryHistory: [] } });
  203. await waitForExplore();
  204. await openQueryHistory();
  205. expect(postMock).not.toBeCalledWith('/api/query-history/migrate', { queries: [] });
  206. expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', {
  207. queryHistoryEnabled: false,
  208. });
  209. });
  210. it('migrates query history from local storage', async () => {
  211. config.queryHistoryEnabled = true;
  212. const { datasources } = setupExplore();
  213. setupLocalStorageRichHistory('loki');
  214. (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
  215. fetchMock.mockReturnValue(of({ data: { result: { queryHistory: [] } } }));
  216. await waitForExplore();
  217. await openQueryHistory();
  218. expect(fetchMock).toBeCalledWith(
  219. expect.objectContaining({
  220. url: expect.stringMatching('/api/query-history/migrate'),
  221. data: { queries: [expect.objectContaining({ datasourceUid: 'loki' })] },
  222. })
  223. );
  224. fetchMock.mockReset();
  225. fetchMock.mockReturnValue(of({ data: { result: { queryHistory: [] } } }));
  226. await closeQueryHistory();
  227. await openQueryHistory();
  228. expect(fetchMock).not.toBeCalledWith(
  229. expect.objectContaining({
  230. url: expect.stringMatching('/api/query-history/migrate'),
  231. })
  232. );
  233. expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', {
  234. queryHistoryEnabled: true,
  235. });
  236. });
  237. });
  238. it('pagination', async () => {
  239. config.queryHistoryEnabled = true;
  240. localStorageHasAlreadyBeenMigrated();
  241. const { datasources } = setupExplore();
  242. (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse());
  243. fetchMock.mockReturnValue(
  244. of({
  245. data: { result: { queryHistory: [{ datasourceUid: 'loki', queries: [{ expr: 'query' }] }], totalCount: 2 } },
  246. })
  247. );
  248. await waitForExplore();
  249. await openQueryHistory();
  250. await assertQueryHistory(['{"expr":"query"}']);
  251. assertQueryHistoryElementsShown(1, 2);
  252. await loadMoreQueryHistory();
  253. await assertQueryHistory(['{"expr":"query"}', '{"expr":"query"}']);
  254. assertLoadMoreQueryHistoryNotVisible();
  255. });
  256. });