import React from 'react'; import { of } from 'rxjs'; import { serializeStateToUrlParam } from '@grafana/data'; import { config } from '@grafana/runtime'; import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput'; import { ExploreId } from '../../../types'; import { assertDataSourceFilterVisibility, assertLoadMoreQueryHistoryNotVisible, assertQueryHistory, assertQueryHistoryComment, assertQueryHistoryElementsShown, assertQueryHistoryExists, assertQueryHistoryIsStarred, assertQueryHistoryTabIsSelected, } from './helper/assert'; import { commentQueryHistory, closeQueryHistory, deleteQueryHistory, inputQuery, loadMoreQueryHistory, openQueryHistory, runQuery, selectOnlyActiveDataSource, selectStarredTabFirst, starQueryHistory, switchToQueryHistoryTab, } from './helper/interactions'; import { makeLogsQueryResponse } from './helper/query'; import { localStorageHasAlreadyBeenMigrated, setupExplore, setupLocalStorageRichHistory, tearDown, waitForExplore, } from './helper/setup'; const fetchMock = jest.fn(); const postMock = jest.fn(); const getMock = jest.fn(); const reportInteractionMock = jest.fn(); jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), getBackendSrv: () => ({ fetch: fetchMock, post: postMock, get: getMock }), reportInteraction: (...args: object[]) => { reportInteractionMock(...args); }, })); jest.mock('app/core/core', () => ({ contextSrv: { hasAccess: () => true, isSignedIn: true, }, })); jest.mock('app/core/services/PreferencesService', () => ({ PreferencesService: function () { return { patch: jest.fn(), load: jest.fn().mockResolvedValue({ queryHistory: { homeTab: 'query', }, }), }; }, })); jest.mock('react-virtualized-auto-sizer', () => { return { __esModule: true, default(props: any) { return
{props.children({ width: 1000 })}
; }, }; }); describe('Explore: Query History', () => { const USER_INPUT = 'my query'; const RAW_QUERY = `{"expr":"${USER_INPUT}"}`; silenceConsoleOutput(); afterEach(() => { config.queryHistoryEnabled = false; fetchMock.mockClear(); postMock.mockClear(); getMock.mockClear(); reportInteractionMock.mockClear(); tearDown(); }); it('adds new query history items after the query is run.', async () => { // when Explore is opened const { datasources, unmount } = setupExplore(); (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse()); await waitForExplore(); // and a user runs a query and opens query history await inputQuery(USER_INPUT); await runQuery(); await openQueryHistory(); // the query that was run is in query history await assertQueryHistoryExists(RAW_QUERY); // when Explore is opened again unmount(); setupExplore({ clearLocalStorage: false }); await waitForExplore(); // previously added query is in query history await openQueryHistory(); await assertQueryHistoryExists(RAW_QUERY); expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', { queryHistoryEnabled: false, }); }); it('adds recently added query if the query history panel is already open', async () => { const urlParams = { left: serializeStateToUrlParam({ datasource: 'loki', queries: [{ refId: 'A', expr: 'query #1' }], range: { from: 'now-1h', to: 'now' }, }), }; const { datasources } = setupExplore({ urlParams }); (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse()); await waitForExplore(); await openQueryHistory(); await inputQuery('query #2'); await runQuery(); await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}']); }); it('updates the state in both Explore panes', async () => { const urlParams = { left: serializeStateToUrlParam({ datasource: 'loki', queries: [{ refId: 'A', expr: 'query #1' }], range: { from: 'now-1h', to: 'now' }, }), right: serializeStateToUrlParam({ datasource: 'loki', queries: [{ refId: 'A', expr: 'query #2' }], range: { from: 'now-1h', to: 'now' }, }), }; const { datasources } = setupExplore({ urlParams }); (datasources.loki.query as jest.Mock).mockReturnValue(makeLogsQueryResponse()); await waitForExplore(); await waitForExplore(ExploreId.right); // queries in history await openQueryHistory(ExploreId.left); await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}'], ExploreId.left); await openQueryHistory(ExploreId.right); await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}'], ExploreId.right); // star one one query starQueryHistory(1, ExploreId.left); await assertQueryHistoryIsStarred([false, true], ExploreId.left); await assertQueryHistoryIsStarred([false, true], ExploreId.right); expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_starred', { queryHistoryEnabled: false, newValue: true, }); deleteQueryHistory(0, ExploreId.left); await assertQueryHistory(['{"expr":"query #1"}'], ExploreId.left); await assertQueryHistory(['{"expr":"query #1"}'], ExploreId.right); expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_deleted', { queryHistoryEnabled: false, }); }); it('add comments to query history', async () => { const urlParams = { left: serializeStateToUrlParam({ datasource: 'loki', queries: [{ refId: 'A', expr: 'query #1' }], range: { from: 'now-1h', to: 'now' }, }), }; const { datasources } = setupExplore({ urlParams }); (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse()); await waitForExplore(); await openQueryHistory(); await assertQueryHistory(['{"expr":"query #1"}'], ExploreId.left); await commentQueryHistory(0, 'test comment'); await assertQueryHistoryComment(['test comment'], ExploreId.left); }); it('updates query history settings', async () => { // open settings page setupExplore(); await waitForExplore(); await openQueryHistory(); // assert default values assertQueryHistoryTabIsSelected('Query history'); assertDataSourceFilterVisibility(true); await switchToQueryHistoryTab('Settings'); // change settings await selectStarredTabFirst(); await selectOnlyActiveDataSource(); await closeQueryHistory(); await openQueryHistory(); // assert new settings assertQueryHistoryTabIsSelected('Starred'); assertDataSourceFilterVisibility(false); }); describe('local storage migration', () => { it('does not migrate if query history is not enabled', async () => { config.queryHistoryEnabled = false; const { datasources } = setupExplore(); setupLocalStorageRichHistory('loki'); (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse()); getMock.mockReturnValue({ result: { queryHistory: [] } }); await waitForExplore(); await openQueryHistory(); expect(postMock).not.toBeCalledWith('/api/query-history/migrate', { queries: [] }); expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', { queryHistoryEnabled: false, }); }); it('migrates query history from local storage', async () => { config.queryHistoryEnabled = true; const { datasources } = setupExplore(); setupLocalStorageRichHistory('loki'); (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse()); fetchMock.mockReturnValue(of({ data: { result: { queryHistory: [] } } })); await waitForExplore(); await openQueryHistory(); expect(fetchMock).toBeCalledWith( expect.objectContaining({ url: expect.stringMatching('/api/query-history/migrate'), data: { queries: [expect.objectContaining({ datasourceUid: 'loki' })] }, }) ); fetchMock.mockReset(); fetchMock.mockReturnValue(of({ data: { result: { queryHistory: [] } } })); await closeQueryHistory(); await openQueryHistory(); expect(fetchMock).not.toBeCalledWith( expect.objectContaining({ url: expect.stringMatching('/api/query-history/migrate'), }) ); expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', { queryHistoryEnabled: true, }); }); }); it('pagination', async () => { config.queryHistoryEnabled = true; localStorageHasAlreadyBeenMigrated(); const { datasources } = setupExplore(); (datasources.loki.query as jest.Mock).mockReturnValueOnce(makeLogsQueryResponse()); fetchMock.mockReturnValue( of({ data: { result: { queryHistory: [{ datasourceUid: 'loki', queries: [{ expr: 'query' }] }], totalCount: 2 } }, }) ); await waitForExplore(); await openQueryHistory(); await assertQueryHistory(['{"expr":"query"}']); assertQueryHistoryElementsShown(1, 2); await loadMoreQueryHistory(); await assertQueryHistory(['{"expr":"query"}', '{"expr":"query"}']); assertLoadMoreQueryHistoryNotVisible(); }); });