2020-01-13 01:03:22 -06:00
|
|
|
import { PayloadAction } from '@reduxjs/toolkit';
|
2020-10-01 05:18:21 -05:00
|
|
|
import { DataQuery, DefaultTimeZone, toUtc, ExploreUrlState } from '@grafana/data';
|
2020-01-13 01:03:22 -06:00
|
|
|
|
2020-07-09 09:14:55 -05:00
|
|
|
import { cancelQueries, loadDatasource, navigateToExplore, refreshExplore } from './actions';
|
2020-06-30 07:51:04 -05:00
|
|
|
import { ExploreId, ExploreUpdateState } from 'app/types';
|
2019-03-22 09:24:30 -05:00
|
|
|
import { thunkTester } from 'test/core/thunk/thunkTester';
|
|
|
|
import {
|
2020-06-18 04:35:11 -05:00
|
|
|
cancelQueriesAction,
|
2019-03-22 09:24:30 -05:00
|
|
|
initializeExploreAction,
|
|
|
|
InitializeExplorePayload,
|
2019-04-01 00:38:00 -05:00
|
|
|
loadDatasourcePendingAction,
|
|
|
|
loadDatasourceReadyAction,
|
2020-06-18 04:35:11 -05:00
|
|
|
scanStopAction,
|
2019-11-07 06:49:45 -06:00
|
|
|
setQueriesAction,
|
2019-03-22 09:24:30 -05:00
|
|
|
} from './actionTypes';
|
|
|
|
import { Emitter } from 'app/core/core';
|
|
|
|
import { makeInitialUpdateState } from './reducers';
|
2019-11-07 06:49:45 -06:00
|
|
|
import { PanelModel } from 'app/features/dashboard/state';
|
|
|
|
import { updateLocation } from '../../../core/actions';
|
|
|
|
import { MockDataSourceApi } from '../../../../test/mocks/datasource_srv';
|
2019-12-06 10:00:05 -06:00
|
|
|
import * as DatasourceSrv from 'app/features/plugins/datasource_srv';
|
2020-03-25 05:38:14 -05:00
|
|
|
import { interval } from 'rxjs';
|
2019-12-06 10:00:05 -06:00
|
|
|
|
|
|
|
jest.mock('app/features/plugins/datasource_srv');
|
|
|
|
const getDatasourceSrvMock = (DatasourceSrv.getDatasourceSrv as any) as jest.Mock<DatasourceSrv.DatasourceSrv>;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
getDatasourceSrvMock.mockClear();
|
|
|
|
getDatasourceSrvMock.mockImplementation(
|
|
|
|
() =>
|
|
|
|
({
|
|
|
|
getExternal: jest.fn().mockReturnValue([]),
|
|
|
|
get: jest.fn().mockReturnValue({
|
|
|
|
testDatasource: jest.fn(),
|
|
|
|
init: jest.fn(),
|
|
|
|
}),
|
|
|
|
} as any)
|
|
|
|
);
|
|
|
|
});
|
2019-03-22 09:24:30 -05:00
|
|
|
|
2019-08-28 11:24:52 -05:00
|
|
|
jest.mock('../../dashboard/services/TimeSrv', () => ({
|
|
|
|
getTimeSrv: jest.fn().mockReturnValue({
|
|
|
|
init: jest.fn(),
|
|
|
|
}),
|
|
|
|
}));
|
|
|
|
|
2019-05-08 06:51:44 -05:00
|
|
|
const t = toUtc();
|
2019-04-29 11:28:41 -05:00
|
|
|
const testRange = {
|
|
|
|
from: t,
|
|
|
|
to: t,
|
|
|
|
raw: {
|
|
|
|
from: t,
|
|
|
|
to: t,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
jest.mock('app/core/utils/explore', () => ({
|
2020-06-18 04:35:11 -05:00
|
|
|
...((jest.requireActual('app/core/utils/explore') as unknown) as object),
|
|
|
|
getTimeRangeFromUrl: (range: any) => testRange,
|
2019-04-29 11:28:41 -05:00
|
|
|
}));
|
|
|
|
|
2019-03-22 09:24:30 -05:00
|
|
|
const setup = (updateOverides?: Partial<ExploreUpdateState>) => {
|
|
|
|
const exploreId = ExploreId.left;
|
|
|
|
const containerWidth = 1920;
|
|
|
|
const eventBridge = {} as Emitter;
|
2019-04-29 11:28:41 -05:00
|
|
|
const timeZone = DefaultTimeZone;
|
|
|
|
const range = testRange;
|
2019-04-16 02:15:23 -05:00
|
|
|
const urlState: ExploreUrlState = {
|
|
|
|
datasource: 'some-datasource',
|
|
|
|
queries: [],
|
2019-04-29 11:28:41 -05:00
|
|
|
range: range.raw,
|
2019-04-16 02:15:23 -05:00
|
|
|
};
|
2019-03-22 09:24:30 -05:00
|
|
|
const updateDefaults = makeInitialUpdateState();
|
|
|
|
const update = { ...updateDefaults, ...updateOverides };
|
|
|
|
const initialState = {
|
2019-04-29 11:28:41 -05:00
|
|
|
user: {
|
2019-08-28 11:24:52 -05:00
|
|
|
orgId: '1',
|
2019-04-29 11:28:41 -05:00
|
|
|
timeZone,
|
|
|
|
},
|
2019-03-22 09:24:30 -05:00
|
|
|
explore: {
|
|
|
|
[exploreId]: {
|
|
|
|
initialized: true,
|
|
|
|
urlState,
|
|
|
|
containerWidth,
|
|
|
|
eventBridge,
|
|
|
|
update,
|
|
|
|
datasourceInstance: { name: 'some-datasource' },
|
2019-04-01 00:38:00 -05:00
|
|
|
queries: [] as DataQuery[],
|
2019-03-22 09:24:30 -05:00
|
|
|
range,
|
2019-04-16 02:15:23 -05:00
|
|
|
refreshInterval: {
|
|
|
|
label: 'Off',
|
|
|
|
value: 0,
|
|
|
|
},
|
2019-03-22 09:24:30 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return {
|
|
|
|
initialState,
|
|
|
|
exploreId,
|
|
|
|
range,
|
|
|
|
containerWidth,
|
|
|
|
eventBridge,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
describe('refreshExplore', () => {
|
|
|
|
describe('when explore is initialized', () => {
|
|
|
|
describe('and update datasource is set', () => {
|
2019-04-01 00:38:00 -05:00
|
|
|
it('then it should dispatch initializeExplore', async () => {
|
2020-10-01 05:18:21 -05:00
|
|
|
const { exploreId, initialState, containerWidth, eventBridge } = setup({ datasource: true });
|
2019-03-22 09:24:30 -05:00
|
|
|
|
2019-04-01 00:38:00 -05:00
|
|
|
const dispatchedActions = await thunkTester(initialState)
|
2019-03-22 09:24:30 -05:00
|
|
|
.givenThunk(refreshExplore)
|
2019-04-01 00:38:00 -05:00
|
|
|
.whenThunkIsDispatched(exploreId);
|
|
|
|
|
2020-01-13 01:03:22 -06:00
|
|
|
const initializeExplore = dispatchedActions[1] as PayloadAction<InitializeExplorePayload>;
|
2019-04-01 00:38:00 -05:00
|
|
|
const { type, payload } = initializeExplore;
|
|
|
|
|
|
|
|
expect(type).toEqual(initializeExploreAction.type);
|
|
|
|
expect(payload.containerWidth).toEqual(containerWidth);
|
|
|
|
expect(payload.eventBridge).toEqual(eventBridge);
|
|
|
|
expect(payload.queries.length).toBe(1); // Queries have generated keys hard to expect on
|
2019-04-29 11:28:41 -05:00
|
|
|
expect(payload.range.from).toEqual(testRange.from);
|
|
|
|
expect(payload.range.to).toEqual(testRange.to);
|
|
|
|
expect(payload.range.raw.from).toEqual(testRange.raw.from);
|
|
|
|
expect(payload.range.raw.to).toEqual(testRange.raw.to);
|
2019-03-22 09:24:30 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('and update queries is set', () => {
|
2019-04-01 00:38:00 -05:00
|
|
|
it('then it should dispatch setQueriesAction', async () => {
|
2019-03-22 09:24:30 -05:00
|
|
|
const { exploreId, initialState } = setup({ queries: true });
|
|
|
|
|
2019-04-01 00:38:00 -05:00
|
|
|
const dispatchedActions = await thunkTester(initialState)
|
2019-03-22 09:24:30 -05:00
|
|
|
.givenThunk(refreshExplore)
|
2019-04-01 00:38:00 -05:00
|
|
|
.whenThunkIsDispatched(exploreId);
|
2019-03-22 09:24:30 -05:00
|
|
|
|
2019-04-01 00:38:00 -05:00
|
|
|
expect(dispatchedActions[0].type).toEqual(setQueriesAction.type);
|
|
|
|
expect(dispatchedActions[0].payload).toEqual({ exploreId, queries: [] });
|
2019-03-22 09:24:30 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when update is not initialized', () => {
|
2019-04-01 00:38:00 -05:00
|
|
|
it('then it should not dispatch any actions', async () => {
|
2019-03-22 09:24:30 -05:00
|
|
|
const exploreId = ExploreId.left;
|
|
|
|
const initialState = { explore: { [exploreId]: { initialized: false } } };
|
|
|
|
|
2019-04-01 00:38:00 -05:00
|
|
|
const dispatchedActions = await thunkTester(initialState)
|
2019-03-22 09:24:30 -05:00
|
|
|
.givenThunk(refreshExplore)
|
2019-04-01 00:38:00 -05:00
|
|
|
.whenThunkIsDispatched(exploreId);
|
|
|
|
|
|
|
|
expect(dispatchedActions).toEqual([]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-25 05:38:14 -05:00
|
|
|
describe('running queries', () => {
|
|
|
|
it('should cancel running query when cancelQueries is dispatched', async () => {
|
|
|
|
const unsubscribable = interval(1000);
|
|
|
|
unsubscribable.subscribe();
|
|
|
|
const exploreId = ExploreId.left;
|
|
|
|
const initialState = {
|
|
|
|
explore: {
|
|
|
|
[exploreId]: {
|
|
|
|
datasourceInstance: 'test-datasource',
|
|
|
|
initialized: true,
|
|
|
|
loading: true,
|
|
|
|
querySubscription: unsubscribable,
|
|
|
|
queries: ['A'],
|
|
|
|
range: testRange,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
user: {
|
|
|
|
orgId: 'A',
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const dispatchedActions = await thunkTester(initialState)
|
|
|
|
.givenThunk(cancelQueries)
|
|
|
|
.whenThunkIsDispatched(exploreId);
|
|
|
|
|
|
|
|
expect(dispatchedActions).toEqual([
|
|
|
|
scanStopAction({ exploreId }),
|
|
|
|
cancelQueriesAction({ exploreId }),
|
|
|
|
expect.anything(),
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-04-01 00:38:00 -05:00
|
|
|
describe('loading datasource', () => {
|
|
|
|
describe('when loadDatasource thunk is dispatched', () => {
|
|
|
|
describe('and all goes fine', () => {
|
|
|
|
it('then it should dispatch correct actions', async () => {
|
|
|
|
const exploreId = ExploreId.left;
|
|
|
|
const name = 'some-datasource';
|
|
|
|
const initialState = { explore: { [exploreId]: { requestedDatasourceName: name } } };
|
|
|
|
const mockDatasourceInstance = {
|
|
|
|
testDatasource: () => {
|
|
|
|
return Promise.resolve({ status: 'success' });
|
|
|
|
},
|
|
|
|
name,
|
|
|
|
init: jest.fn(),
|
|
|
|
meta: { id: 'some id' },
|
|
|
|
};
|
|
|
|
|
|
|
|
const dispatchedActions = await thunkTester(initialState)
|
|
|
|
.givenThunk(loadDatasource)
|
|
|
|
.whenThunkIsDispatched(exploreId, mockDatasourceInstance);
|
|
|
|
|
|
|
|
expect(dispatchedActions).toEqual([
|
|
|
|
loadDatasourcePendingAction({
|
|
|
|
exploreId,
|
|
|
|
requestedDatasourceName: mockDatasourceInstance.name,
|
|
|
|
}),
|
|
|
|
loadDatasourceReadyAction({ exploreId, history: [] }),
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('and user changes datasource during load', () => {
|
|
|
|
it('then it should dispatch correct actions', async () => {
|
|
|
|
const exploreId = ExploreId.left;
|
|
|
|
const name = 'some-datasource';
|
|
|
|
const initialState = { explore: { [exploreId]: { requestedDatasourceName: 'some-other-datasource' } } };
|
|
|
|
const mockDatasourceInstance = {
|
|
|
|
testDatasource: () => {
|
|
|
|
return Promise.resolve({ status: 'success' });
|
|
|
|
},
|
|
|
|
name,
|
|
|
|
init: jest.fn(),
|
|
|
|
meta: { id: 'some id' },
|
|
|
|
};
|
|
|
|
|
|
|
|
const dispatchedActions = await thunkTester(initialState)
|
|
|
|
.givenThunk(loadDatasource)
|
|
|
|
.whenThunkIsDispatched(exploreId, mockDatasourceInstance);
|
|
|
|
|
|
|
|
expect(dispatchedActions).toEqual([
|
|
|
|
loadDatasourcePendingAction({
|
|
|
|
exploreId,
|
|
|
|
requestedDatasourceName: mockDatasourceInstance.name,
|
|
|
|
}),
|
|
|
|
]);
|
|
|
|
});
|
2019-03-22 09:24:30 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2019-11-07 06:49:45 -06:00
|
|
|
|
2020-03-12 04:22:33 -05:00
|
|
|
const getNavigateToExploreContext = async (openInNewWindow?: (url: string) => void) => {
|
2019-11-07 06:49:45 -06:00
|
|
|
const url = 'http://www.someurl.com';
|
|
|
|
const panel: Partial<PanelModel> = {
|
|
|
|
datasource: 'mocked datasource',
|
|
|
|
targets: [{ refId: 'A' }],
|
|
|
|
};
|
Chore: Fix all Typescript strict null errors (#26204)
* Chore: Fix typescript strict null errors
* Added new limit
* Fixed ts issue
* fixed tests
* trying to fix type inference
* Fixing more ts errors
* Revert tsconfig option
* Fix
* Fixed code
* More fixes
* fix tests
* Updated snapshot
* Chore: More ts strict null fixes
* More fixes in some really messed up azure config components
* More fixes, current count: 441
* 419
* More fixes
* Fixed invalid initial state in explore
* Fixing tests
* Fixed tests
* Explore fix
* More fixes
* Progress
* Sub 300
* Now at 218
* Progress
* Update
* Progress
* Updated tests
* at 159
* fixed tests
* Progress
* YAy blow 100! at 94
* 10,9,8,7,6,5,4,3,2,1... lift off
* Fixed tests
* Fixed more type errors
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2020-07-10 05:46:59 -05:00
|
|
|
const datasource = new MockDataSourceApi(panel.datasource!);
|
2019-11-07 06:49:45 -06:00
|
|
|
const get = jest.fn().mockResolvedValue(datasource);
|
|
|
|
const getDataSourceSrv = jest.fn().mockReturnValue({ get });
|
|
|
|
const getTimeSrv = jest.fn();
|
|
|
|
const getExploreUrl = jest.fn().mockResolvedValue(url);
|
|
|
|
|
|
|
|
const dispatchedActions = await thunkTester({})
|
|
|
|
.givenThunk(navigateToExplore)
|
|
|
|
.whenThunkIsDispatched(panel, { getDataSourceSrv, getTimeSrv, getExploreUrl, openInNewWindow });
|
|
|
|
|
|
|
|
return {
|
|
|
|
url,
|
|
|
|
panel,
|
|
|
|
datasource,
|
|
|
|
get,
|
|
|
|
getDataSourceSrv,
|
|
|
|
getTimeSrv,
|
|
|
|
getExploreUrl,
|
|
|
|
dispatchedActions,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
describe('navigateToExplore', () => {
|
|
|
|
describe('when navigateToExplore thunk is dispatched', () => {
|
|
|
|
describe('and openInNewWindow is undefined', () => {
|
2020-03-12 04:22:33 -05:00
|
|
|
const openInNewWindow: (url: string) => void = (undefined as unknown) as (url: string) => void;
|
2019-11-07 06:49:45 -06:00
|
|
|
it('then it should dispatch correct actions', async () => {
|
|
|
|
const { dispatchedActions, url } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(dispatchedActions).toEqual([updateLocation({ path: url, query: {} })]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getDataSourceSrv should have been once', async () => {
|
|
|
|
const { getDataSourceSrv } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(getDataSourceSrv).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getDataSourceSrv.get should have been called with correct arguments', async () => {
|
|
|
|
const { get, panel } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(get).toHaveBeenCalledTimes(1);
|
|
|
|
expect(get).toHaveBeenCalledWith(panel.datasource);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getTimeSrv should have been called once', async () => {
|
|
|
|
const { getTimeSrv } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(getTimeSrv).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getExploreUrl should have been called with correct arguments', async () => {
|
|
|
|
const { getExploreUrl, panel, datasource, getDataSourceSrv, getTimeSrv } = await getNavigateToExploreContext(
|
|
|
|
openInNewWindow
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(getExploreUrl).toHaveBeenCalledTimes(1);
|
|
|
|
expect(getExploreUrl).toHaveBeenCalledWith({
|
|
|
|
panel,
|
|
|
|
panelTargets: panel.targets,
|
|
|
|
panelDatasource: datasource,
|
|
|
|
datasourceSrv: getDataSourceSrv(),
|
|
|
|
timeSrv: getTimeSrv(),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('and openInNewWindow is defined', () => {
|
|
|
|
const openInNewWindow: (url: string) => void = jest.fn();
|
|
|
|
it('then it should dispatch no actions', async () => {
|
|
|
|
const { dispatchedActions } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(dispatchedActions).toEqual([]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getDataSourceSrv should have been once', async () => {
|
|
|
|
const { getDataSourceSrv } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(getDataSourceSrv).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getDataSourceSrv.get should have been called with correct arguments', async () => {
|
|
|
|
const { get, panel } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(get).toHaveBeenCalledTimes(1);
|
|
|
|
expect(get).toHaveBeenCalledWith(panel.datasource);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getTimeSrv should have been called once', async () => {
|
|
|
|
const { getTimeSrv } = await getNavigateToExploreContext(openInNewWindow);
|
|
|
|
|
|
|
|
expect(getTimeSrv).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then getExploreUrl should have been called with correct arguments', async () => {
|
|
|
|
const { getExploreUrl, panel, datasource, getDataSourceSrv, getTimeSrv } = await getNavigateToExploreContext(
|
|
|
|
openInNewWindow
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(getExploreUrl).toHaveBeenCalledTimes(1);
|
|
|
|
expect(getExploreUrl).toHaveBeenCalledWith({
|
|
|
|
panel,
|
|
|
|
panelTargets: panel.targets,
|
|
|
|
panelDatasource: datasource,
|
|
|
|
datasourceSrv: getDataSourceSrv(),
|
|
|
|
timeSrv: getTimeSrv(),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('then openInNewWindow should have been called with correct arguments', async () => {
|
|
|
|
const openInNewWindowFunc = jest.fn();
|
|
|
|
const { url } = await getNavigateToExploreContext(openInNewWindowFunc);
|
|
|
|
|
|
|
|
expect(openInNewWindowFunc).toHaveBeenCalledTimes(1);
|
|
|
|
expect(openInNewWindowFunc).toHaveBeenCalledWith(url);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|