The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/core/utils/richHistory.test.ts

337 lines
11 KiB

import {
addToRichHistory,
getRichHistory,
updateStarredInRichHistory,
updateCommentInRichHistory,
mapNumbertoTimeInSlider,
createDateStringFromTs,
createQueryHeading,
deleteAllFromRichHistory,
deleteQueryInRichHistory,
filterAndSortQueries,
SortOrder,
MAX_HISTORY_ITEMS,
} from './richHistory';
import store from 'app/core/store';
import { dateTime, DataQuery } from '@grafana/data';
import { RichHistoryQuery } from '../../types';
const mock: any = {
storedHistory: [
{
comment: '',
datasourceId: 'datasource historyId',
datasourceName: 'datasource history name',
queries: [
{ expr: 'query1', maxLines: null, refId: '1' },
{ expr: 'query2', refId: '2' },
],
sessionName: '',
starred: true,
ts: 1,
},
],
testComment: '',
testDatasourceId: 'datasourceId',
testDatasourceName: 'datasourceName',
testQueries: [
{ expr: 'query3', refId: 'B' },
{ expr: 'query4', refId: 'C' },
],
testSessionName: '',
testStarred: false,
};
const key = 'grafana.explore.richHistory';
describe('richHistory', () => {
beforeEach(() => {
jest.useFakeTimers('modern');
jest.setSystemTime(new Date(1970, 0, 1));
});
afterEach(() => {
jest.useRealTimers();
});
describe('addToRichHistory', () => {
beforeEach(() => {
deleteAllFromRichHistory();
expect(store.exists(key)).toBeFalsy();
});
const expectedResult = [
{
comment: mock.testComment,
datasourceId: mock.testDatasourceId,
datasourceName: mock.testDatasourceName,
queries: mock.testQueries,
sessionName: mock.testSessionName,
starred: mock.testStarred,
ts: 2,
},
mock.storedHistory[0],
];
it('should append query to query history', () => {
Date.now = jest.fn(() => 2);
const { richHistory: newHistory } = addToRichHistory(
mock.storedHistory,
mock.testDatasourceId,
mock.testDatasourceName,
mock.testQueries,
mock.testStarred,
mock.testComment,
mock.testSessionName,
true,
true
);
expect(newHistory).toEqual(expectedResult);
});
it('should save query history to localStorage', () => {
Date.now = jest.fn(() => 2);
addToRichHistory(
mock.storedHistory,
mock.testDatasourceId,
mock.testDatasourceName,
mock.testQueries,
mock.testStarred,
mock.testComment,
mock.testSessionName,
true,
true
);
expect(store.exists(key)).toBeTruthy();
expect(store.getObject(key)).toMatchObject(expectedResult);
});
it('should not append duplicated query to query history', () => {
Date.now = jest.fn(() => 2);
const { richHistory: newHistory } = addToRichHistory(
mock.storedHistory,
mock.storedHistory[0].datasourceId,
mock.storedHistory[0].datasourceName,
[{ expr: 'query1', maxLines: null, refId: 'A' } as DataQuery, { expr: 'query2', refId: 'B' } as DataQuery],
mock.testStarred,
mock.testComment,
mock.testSessionName,
true,
true
);
expect(newHistory).toEqual([mock.storedHistory[0]]);
});
it('should not save duplicated query to localStorage', () => {
Date.now = jest.fn(() => 2);
addToRichHistory(
mock.storedHistory,
mock.storedHistory[0].datasourceId,
mock.storedHistory[0].datasourceName,
[{ expr: 'query1', maxLines: null, refId: 'A' } as DataQuery, { expr: 'query2', refId: 'B' } as DataQuery],
mock.testStarred,
mock.testComment,
mock.testSessionName,
true,
true
);
expect(store.exists(key)).toBeFalsy();
});
it('should not save more than MAX_HISTORY_ITEMS', () => {
Date.now = jest.fn(() => 2);
const extraItems = 100;
// the history has more than MAX
let history = [];
// history = [ { starred: true, comment: "0" }, { starred: false, comment: "1" }, ... ]
for (let i = 0; i < MAX_HISTORY_ITEMS + extraItems; i++) {
history.push({
starred: i % 2 === 0,
comment: i.toString(),
queries: [],
ts: new Date(2019, 11, 31).getTime(),
});
}
const starredItemsInHistory = (MAX_HISTORY_ITEMS + extraItems) / 2;
const notStarredItemsInHistory = (MAX_HISTORY_ITEMS + extraItems) / 2;
expect(history.filter((h) => h.starred)).toHaveLength(starredItemsInHistory);
expect(history.filter((h) => !h.starred)).toHaveLength(notStarredItemsInHistory);
const { richHistory: newHistory } = addToRichHistory(
(history as any) as RichHistoryQuery[],
mock.storedHistory[0].datasourceId,
mock.storedHistory[0].datasourceName,
[{ expr: 'query1', maxLines: null, refId: 'A' } as DataQuery, { expr: 'query2', refId: 'B' } as DataQuery],
true,
mock.testComment,
mock.testSessionName,
true,
true
);
// one not starred replaced with a newly added starred item
const removedNotStarredItems = extraItems + 1; // + 1 to make space for the new item
expect(newHistory.filter((h) => h.starred)).toHaveLength(starredItemsInHistory + 1); // starred item added
expect(newHistory.filter((h) => !h.starred)).toHaveLength(starredItemsInHistory - removedNotStarredItems);
});
});
describe('updateStarredInRichHistory', () => {
it('should update starred in query in history', () => {
const updatedStarred = updateStarredInRichHistory(mock.storedHistory, 1);
expect(updatedStarred[0].starred).toEqual(false);
});
it('should update starred in localStorage', () => {
updateStarredInRichHistory(mock.storedHistory, 1);
expect(store.exists(key)).toBeTruthy();
expect(store.getObject(key)[0].starred).toEqual(false);
});
});
describe('updateCommentInRichHistory', () => {
it('should update comment in query in history', () => {
const updatedComment = updateCommentInRichHistory(mock.storedHistory, 1, 'new comment');
expect(updatedComment[0].comment).toEqual('new comment');
});
it('should update comment in localStorage', () => {
updateCommentInRichHistory(mock.storedHistory, 1, 'new comment');
expect(store.exists(key)).toBeTruthy();
expect(store.getObject(key)[0].comment).toEqual('new comment');
});
});
describe('deleteQueryInRichHistory', () => {
it('should delete query in query in history', () => {
const deletedHistory = deleteQueryInRichHistory(mock.storedHistory, 1);
expect(deletedHistory).toEqual([]);
});
it('should delete query in localStorage', () => {
deleteQueryInRichHistory(mock.storedHistory, 1);
expect(store.exists(key)).toBeTruthy();
expect(store.getObject(key)).toEqual([]);
});
});
describe('mapNumbertoTimeInSlider', () => {
it('should correctly map number to value', () => {
const value = mapNumbertoTimeInSlider(25);
expect(value).toEqual('25 days ago');
});
});
describe('createDateStringFromTs', () => {
it('should correctly create string value from timestamp', () => {
const value = createDateStringFromTs(1583932327000);
expect(value).toEqual('March 11');
});
});
describe('filterQueries', () => {
it('should filter out queries based on data source filter', () => {
const filteredQueries = filterAndSortQueries(
mock.storedHistory,
SortOrder.Ascending,
['not provided data source'],
''
);
expect(filteredQueries).toHaveLength(0);
});
it('should keep queries based on data source filter', () => {
const filteredQueries = filterAndSortQueries(
mock.storedHistory,
SortOrder.Ascending,
['datasource history name'],
''
);
expect(filteredQueries).toHaveLength(1);
});
it('should filter out all queries based on search filter', () => {
const filteredQueries = filterAndSortQueries(
mock.storedHistory,
SortOrder.Ascending,
[],
'i do not exist in query'
);
expect(filteredQueries).toHaveLength(0);
});
it('should include queries based on search filter', () => {
const filteredQueries = filterAndSortQueries(mock.storedHistory, SortOrder.Ascending, [], 'query1');
expect(filteredQueries).toHaveLength(1);
});
});
describe('createQueryHeading', () => {
it('should correctly create heading for queries when sort order is ascending ', () => {
// Have to offset the timezone of a 1 microsecond epoch, and then reverse the changes
mock.storedHistory[0].ts = 1 + -1 * dateTime().utcOffset() * 60 * 1000;
const heading = createQueryHeading(mock.storedHistory[0], SortOrder.Ascending);
expect(heading).toEqual('January 1');
});
it('should correctly create heading for queries when sort order is datasourceAZ ', () => {
const heading = createQueryHeading(mock.storedHistory[0], SortOrder.DatasourceAZ);
expect(heading).toEqual(mock.storedHistory[0].datasourceName);
});
});
describe('getRichHistory', () => {
afterEach(() => {
deleteAllFromRichHistory();
expect(store.exists(key)).toBeFalsy();
});
describe('should load from localStorage data in old formats', () => {
it('should load when queries are strings', () => {
const oldHistoryItem = { ...mock.storedHistory[0], queries: ['test query 1', 'test query 2', 'test query 3'] };
store.setObject(key, [oldHistoryItem]);
const expectedHistoryItem = {
...mock.storedHistory[0],
queries: [
{
expr: 'test query 1',
refId: 'A',
},
{
expr: 'test query 2',
refId: 'B',
},
{
expr: 'test query 3',
refId: 'C',
},
],
};
const result = getRichHistory();
expect(result).toStrictEqual([expectedHistoryItem]);
});
it('should load when queries are json-encoded strings', () => {
const oldHistoryItem = {
...mock.storedHistory[0],
queries: ['{"refId":"A","key":"key1","metrics":[]}', '{"refId":"B","key":"key2","metrics":[]}'],
};
store.setObject(key, [oldHistoryItem]);
const expectedHistoryItem = {
...mock.storedHistory[0],
queries: [
{
refId: 'A',
key: 'key1',
metrics: [],
},
{
refId: 'B',
key: 'key2',
metrics: [],
},
],
};
const result = getRichHistory();
expect(result).toStrictEqual([expectedHistoryItem]);
});
});
});
});