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();
});
});