initDashboard.test.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import configureMockStore from 'redux-mock-store';
  2. import thunk from 'redux-thunk';
  3. import { Subject } from 'rxjs';
  4. import { locationService, setEchoSrv } from '@grafana/runtime';
  5. import { getBackendSrv } from 'app/core/services/backend_srv';
  6. import { keybindingSrv } from 'app/core/services/keybindingSrv';
  7. import { variableAdapters } from 'app/features/variables/adapters';
  8. import { createConstantVariableAdapter } from 'app/features/variables/constant/adapter';
  9. import { constantBuilder } from 'app/features/variables/shared/testing/builders';
  10. import { DashboardInitPhase, DashboardRoutes } from 'app/types';
  11. import { Echo } from '../../../core/services/echo/Echo';
  12. import {
  13. getDashboardQueryRunner,
  14. setDashboardQueryRunnerFactory,
  15. } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
  16. import { emptyResult } from '../../query/state/DashboardQueryRunner/utils';
  17. import { getPreloadedState } from '../../variables/state/helpers';
  18. import { initialTransactionState, variablesInitTransaction } from '../../variables/state/transactionReducer';
  19. import { TransactionStatus } from '../../variables/types';
  20. import { DashboardLoaderSrv, setDashboardLoaderSrv } from '../services/DashboardLoaderSrv';
  21. import { getDashboardSrv, setDashboardSrv } from '../services/DashboardSrv';
  22. import { getTimeSrv, setTimeSrv } from '../services/TimeSrv';
  23. import { initDashboard, InitDashboardArgs } from './initDashboard';
  24. import { dashboardInitCompleted, dashboardInitFetching, dashboardInitServices } from './reducers';
  25. jest.mock('app/core/services/backend_srv');
  26. jest.mock('app/features/dashboard/services/TimeSrv', () => {
  27. const original = jest.requireActual('app/features/dashboard/services/TimeSrv');
  28. return {
  29. ...original,
  30. getTimeSrv: () => ({
  31. ...original.getTimeSrv(),
  32. timeRange: jest.fn().mockReturnValue(undefined),
  33. }),
  34. };
  35. });
  36. jest.mock('app/core/services/context_srv', () => ({
  37. contextSrv: {
  38. user: { orgId: 1, orgName: 'TestOrg' },
  39. },
  40. }));
  41. variableAdapters.register(createConstantVariableAdapter());
  42. const mockStore = configureMockStore([thunk]);
  43. interface ScenarioContext {
  44. args: InitDashboardArgs;
  45. loaderSrv: any;
  46. backendSrv: any;
  47. setup: (fn: () => void) => void;
  48. actions: any[];
  49. storeState: any;
  50. }
  51. type ScenarioFn = (ctx: ScenarioContext) => void;
  52. const DASH_UID = 'DGmvKKxZz';
  53. function describeInitScenario(description: string, scenarioFn: ScenarioFn) {
  54. describe(description, () => {
  55. const loaderSrv = {
  56. loadDashboard: jest.fn(() => ({
  57. meta: {
  58. canStar: false,
  59. canShare: false,
  60. isNew: true,
  61. folderId: 0,
  62. },
  63. dashboard: {
  64. title: 'My cool dashboard',
  65. panels: [
  66. {
  67. type: 'add-panel',
  68. gridPos: { x: 0, y: 0, w: 12, h: 9 },
  69. title: 'Panel Title',
  70. id: 2,
  71. targets: [
  72. {
  73. refId: 'A',
  74. expr: 'old expr',
  75. },
  76. ],
  77. },
  78. ],
  79. templating: {
  80. list: [constantBuilder().build()],
  81. },
  82. uid: DASH_UID,
  83. },
  84. })),
  85. };
  86. setDashboardLoaderSrv(loaderSrv as unknown as DashboardLoaderSrv);
  87. setDashboardQueryRunnerFactory(() => ({
  88. getResult: emptyResult,
  89. run: jest.fn(),
  90. cancel: () => undefined,
  91. cancellations: () => new Subject<any>(),
  92. destroy: () => undefined,
  93. }));
  94. let setupFn = () => {};
  95. const ctx: ScenarioContext = {
  96. args: {
  97. urlUid: DASH_UID,
  98. fixUrl: false,
  99. routeName: DashboardRoutes.Normal,
  100. },
  101. backendSrv: getBackendSrv(),
  102. loaderSrv,
  103. actions: [],
  104. storeState: {
  105. location: {
  106. query: {},
  107. },
  108. dashboard: {
  109. initPhase: DashboardInitPhase.Services,
  110. },
  111. user: {},
  112. explore: {
  113. left: {
  114. queries: [],
  115. },
  116. },
  117. ...getPreloadedState(DASH_UID, {
  118. variables: {},
  119. transaction: { ...initialTransactionState, uid: DASH_UID, status: TransactionStatus.Completed },
  120. }),
  121. },
  122. setup: (fn: () => void) => {
  123. setupFn = fn;
  124. },
  125. };
  126. beforeEach(async () => {
  127. keybindingSrv.setupDashboardBindings = jest.fn();
  128. setDashboardSrv({
  129. setCurrent: jest.fn(),
  130. } as any);
  131. setTimeSrv({
  132. init: jest.fn(),
  133. } as any);
  134. setupFn();
  135. setEchoSrv(new Echo());
  136. const store = mockStore(ctx.storeState);
  137. // @ts-ignore
  138. await store.dispatch(initDashboard(ctx.args));
  139. ctx.actions = store.getActions();
  140. });
  141. scenarioFn(ctx);
  142. });
  143. }
  144. describeInitScenario('Initializing new dashboard', (ctx) => {
  145. ctx.setup(() => {
  146. ctx.storeState.user.orgId = 12;
  147. ctx.args.routeName = DashboardRoutes.New;
  148. });
  149. it('Should send action dashboardInitFetching', () => {
  150. expect(ctx.actions[0].type).toBe(dashboardInitFetching.type);
  151. });
  152. it('Should send action dashboardInitServices ', () => {
  153. expect(ctx.actions[1].type).toBe(dashboardInitServices.type);
  154. });
  155. it('Should update location with orgId query param', () => {
  156. const search = locationService.getSearch();
  157. expect(search.get('orgId')).toBe('12');
  158. });
  159. it('Should send action dashboardInitCompleted', () => {
  160. expect(ctx.actions[7].type).toBe(dashboardInitCompleted.type);
  161. expect(ctx.actions[7].payload.title).toBe('New dashboard');
  162. });
  163. it('Should initialize services', () => {
  164. expect(getTimeSrv().init).toBeCalled();
  165. expect(getDashboardSrv().setCurrent).toBeCalled();
  166. expect(getDashboardQueryRunner().run).toBeCalled();
  167. expect(keybindingSrv.setupDashboardBindings).toBeCalled();
  168. });
  169. });
  170. describeInitScenario('Initializing home dashboard', (ctx) => {
  171. ctx.setup(() => {
  172. ctx.args.routeName = DashboardRoutes.Home;
  173. ctx.backendSrv.get.mockResolvedValue({
  174. redirectUri: '/u/123/my-home',
  175. });
  176. });
  177. it('Should redirect to custom home dashboard', () => {
  178. const location = locationService.getLocation();
  179. expect(location.pathname).toBe('/u/123/my-home');
  180. });
  181. });
  182. describeInitScenario('Initializing home dashboard cancelled', (ctx) => {
  183. ctx.setup(() => {
  184. ctx.args.routeName = DashboardRoutes.Home;
  185. ctx.backendSrv.get.mockRejectedValue({ cancelled: true });
  186. });
  187. it('Should abort init process', () => {
  188. expect(ctx.actions.length).toBe(1);
  189. });
  190. });
  191. describeInitScenario('Initializing existing dashboard', (ctx) => {
  192. const mockQueries = [
  193. {
  194. context: 'explore',
  195. key: 'jdasldsa98dsa9',
  196. refId: 'A',
  197. expr: 'new expr',
  198. },
  199. {
  200. context: 'explore',
  201. key: 'fdsjkfds78fd',
  202. refId: 'B',
  203. },
  204. ];
  205. ctx.setup(() => {
  206. ctx.storeState.user.orgId = 12;
  207. ctx.storeState.explore.left.queries = mockQueries;
  208. });
  209. it('Should send action dashboardInitFetching', () => {
  210. expect(ctx.actions[0].type).toBe(dashboardInitFetching.type);
  211. });
  212. it('Should send action dashboardInitServices ', () => {
  213. expect(ctx.actions[1].type).toBe(dashboardInitServices.type);
  214. });
  215. it('Should update location with orgId query param', () => {
  216. const search = locationService.getSearch();
  217. expect(search.get('orgId')).toBe('12');
  218. });
  219. it('Should send action dashboardInitCompleted', () => {
  220. expect(ctx.actions[8].type).toBe(dashboardInitCompleted.type);
  221. expect(ctx.actions[8].payload.title).toBe('My cool dashboard');
  222. });
  223. it('Should initialize services', () => {
  224. expect(getTimeSrv().init).toBeCalled();
  225. expect(getDashboardSrv().setCurrent).toBeCalled();
  226. expect(getDashboardQueryRunner().run).toBeCalled();
  227. expect(keybindingSrv.setupDashboardBindings).toBeCalled();
  228. });
  229. it('Should initialize redux variables if newVariables is enabled', () => {
  230. expect(ctx.actions[2].payload.action.type).toBe(variablesInitTransaction.type);
  231. });
  232. });
  233. describeInitScenario('Initializing previously canceled dashboard initialization', (ctx) => {
  234. ctx.setup(() => {
  235. ctx.storeState.dashboard.initPhase = DashboardInitPhase.Fetching;
  236. });
  237. it('Should send action dashboardInitFetching', () => {
  238. expect(ctx.actions[0].type).toBe(dashboardInitFetching.type);
  239. });
  240. it('Should send action dashboardInitServices ', () => {
  241. expect(ctx.actions[1].type).toBe(dashboardInitServices.type);
  242. });
  243. it('Should not send action dashboardInitCompleted', () => {
  244. const dashboardInitCompletedAction = ctx.actions.find((a) => {
  245. return a.type === dashboardInitCompleted.type;
  246. });
  247. expect(dashboardInitCompletedAction).toBe(undefined);
  248. });
  249. it('Should initialize timeSrv and dashboard query runner', () => {
  250. expect(getTimeSrv().init).toBeCalled();
  251. expect(getDashboardQueryRunner().run).toBeCalled();
  252. });
  253. });
  254. describeInitScenario('Initializing snapshot dashboard', (ctx) => {
  255. ctx.setup(() => {
  256. ctx.args.urlUid = undefined;
  257. });
  258. it('Should send action initVariablesTransaction with correct payload', () => {
  259. expect(ctx.actions[2].payload.action.type).toBe(variablesInitTransaction.type);
  260. expect(ctx.actions[2].payload.action.payload.uid).toBe(DASH_UID);
  261. });
  262. });