ShareLink.test.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import { shallow, ShallowWrapper } from 'enzyme';
  2. import React from 'react';
  3. import { getDefaultTimeRange } from '@grafana/data';
  4. import { setTemplateSrv } from '@grafana/runtime';
  5. import config from 'app/core/config';
  6. import { initTemplateSrv } from '../../../../../test/helpers/initTemplateSrv';
  7. import { variableAdapters } from '../../../variables/adapters';
  8. import { createQueryVariableAdapter } from '../../../variables/query/adapter';
  9. import { DashboardModel, PanelModel } from '../../state';
  10. import { Props, ShareLink, State } from './ShareLink';
  11. jest.mock('app/features/dashboard/services/TimeSrv', () => ({
  12. getTimeSrv: () => ({
  13. timeRange: () => {
  14. return { from: new Date(1000), to: new Date(2000) };
  15. },
  16. }),
  17. }));
  18. function mockLocationHref(href: string) {
  19. const location = window.location;
  20. let search = '';
  21. const searchPos = href.indexOf('?');
  22. if (searchPos >= 0) {
  23. search = href.substring(searchPos);
  24. }
  25. //@ts-ignore
  26. delete window.location;
  27. (window as any).location = {
  28. ...location,
  29. href,
  30. origin: new URL(href).origin,
  31. search,
  32. };
  33. }
  34. function setUTCTimeZone() {
  35. (window as any).Intl.DateTimeFormat = () => {
  36. return {
  37. resolvedOptions: () => {
  38. return { timeZone: 'UTC' };
  39. },
  40. };
  41. };
  42. }
  43. const mockUid = 'abc123';
  44. jest.mock('@grafana/runtime', () => {
  45. const original = jest.requireActual('@grafana/runtime');
  46. return {
  47. ...original,
  48. getBackendSrv: () => ({
  49. post: jest.fn().mockResolvedValue({
  50. uid: mockUid,
  51. url: `http://localhost:3000/goto/${mockUid}`,
  52. }),
  53. }),
  54. };
  55. });
  56. interface ScenarioContext {
  57. wrapper?: ShallowWrapper<Props, State, ShareLink>;
  58. mount: (propOverrides?: Partial<Props>) => void;
  59. setup: (fn: () => void) => void;
  60. }
  61. function shareLinkScenario(description: string, scenarioFn: (ctx: ScenarioContext) => void) {
  62. describe(description, () => {
  63. let setupFn: () => void;
  64. const ctx: any = {
  65. setup: (fn: any) => {
  66. setupFn = fn;
  67. },
  68. mount: (propOverrides?: any) => {
  69. const props: any = {
  70. panel: undefined,
  71. dashboard: { time: getDefaultTimeRange() },
  72. };
  73. Object.assign(props, propOverrides);
  74. ctx.wrapper = shallow(<ShareLink {...props} />);
  75. },
  76. };
  77. beforeEach(() => {
  78. setUTCTimeZone();
  79. setupFn();
  80. });
  81. scenarioFn(ctx);
  82. });
  83. }
  84. describe('ShareModal', () => {
  85. let templateSrv = initTemplateSrv('key', []);
  86. beforeAll(() => {
  87. variableAdapters.register(createQueryVariableAdapter());
  88. setTemplateSrv(templateSrv);
  89. });
  90. shareLinkScenario('shareUrl with current time range and panel', (ctx) => {
  91. ctx.setup(() => {
  92. mockLocationHref('http://server/#!/test');
  93. config.bootData = {
  94. user: {
  95. orgId: 1,
  96. },
  97. } as any;
  98. ctx.mount({
  99. panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }),
  100. });
  101. });
  102. it('should generate share url absolute time', async () => {
  103. await ctx.wrapper?.instance().buildUrl();
  104. const state = ctx.wrapper?.state();
  105. expect(state?.shareUrl).toBe('http://server/#!/test?from=1000&to=2000&orgId=1&viewPanel=22');
  106. });
  107. it('should generate render url', async () => {
  108. mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash');
  109. ctx.mount({
  110. panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }),
  111. });
  112. await ctx.wrapper?.instance().buildUrl();
  113. const state = ctx.wrapper?.state();
  114. const base = 'http://dashboards.grafana.com/render/d-solo/abcdefghi/my-dash';
  115. const params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
  116. expect(state?.imageUrl).toContain(base + params);
  117. });
  118. it('should generate render url for scripted dashboard', async () => {
  119. mockLocationHref('http://dashboards.grafana.com/dashboard/script/my-dash.js');
  120. ctx.mount({
  121. panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }),
  122. });
  123. await ctx.wrapper?.instance().buildUrl();
  124. const state = ctx.wrapper?.state();
  125. const base = 'http://dashboards.grafana.com/render/dashboard-solo/script/my-dash.js';
  126. const params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
  127. expect(state?.imageUrl).toContain(base + params);
  128. });
  129. it('should remove panel id when no panel in scope', async () => {
  130. ctx.mount({
  131. panel: undefined,
  132. });
  133. await ctx.wrapper?.instance().buildUrl();
  134. const state = ctx.wrapper?.state();
  135. expect(state?.shareUrl).toBe('http://server/#!/test?from=1000&to=2000&orgId=1');
  136. });
  137. it('should add theme when specified', async () => {
  138. ctx.wrapper?.setProps({ panel: undefined });
  139. ctx.wrapper?.setState({ selectedTheme: 'light' });
  140. await ctx.wrapper?.instance().buildUrl();
  141. const state = ctx.wrapper?.state();
  142. expect(state?.shareUrl).toBe('http://server/#!/test?from=1000&to=2000&orgId=1&theme=light');
  143. });
  144. it('should remove editPanel from image url when is first param in querystring', async () => {
  145. mockLocationHref('http://server/#!/test?editPanel=1');
  146. ctx.mount({
  147. panel: new PanelModel({ id: 1, options: {}, fieldConfig: { defaults: {}, overrides: [] } }),
  148. });
  149. await ctx.wrapper?.instance().buildUrl();
  150. const state = ctx.wrapper?.state();
  151. expect(state?.shareUrl).toContain('?editPanel=1&from=1000&to=2000&orgId=1');
  152. expect(state?.imageUrl).toContain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
  153. });
  154. it('should shorten url', () => {
  155. mockLocationHref('http://server/#!/test');
  156. ctx.mount();
  157. ctx.wrapper?.setState({ useShortUrl: true }, async () => {
  158. await ctx.wrapper?.instance().buildUrl();
  159. const state = ctx.wrapper?.state();
  160. expect(state?.shareUrl).toContain(`/goto/${mockUid}`);
  161. });
  162. });
  163. });
  164. });
  165. describe('when default_home_dashboard_path is set in the grafana config', () => {
  166. let originalBootData: any;
  167. beforeAll(() => {
  168. originalBootData = config.bootData;
  169. config.appUrl = 'http://dashboards.grafana.com/';
  170. config.bootData = {
  171. user: {
  172. orgId: 1,
  173. },
  174. } as any;
  175. });
  176. afterAll(() => {
  177. config.bootData = originalBootData;
  178. });
  179. it('should render the correct link', async () => {
  180. const mockDashboard = new DashboardModel({
  181. uid: 'mockDashboardUid',
  182. });
  183. const mockPanel = new PanelModel({
  184. id: 'mockPanelId',
  185. });
  186. mockLocationHref('http://dashboards.grafana.com/?orgId=1');
  187. const test: ShallowWrapper<Props, State, ShareLink> = shallow(
  188. <ShareLink dashboard={mockDashboard} panel={mockPanel} />
  189. );
  190. await test.instance().buildUrl();
  191. expect(test.state().imageUrl).toBe(
  192. `http://dashboards.grafana.com/render/d-solo/${mockDashboard.uid}?orgId=1&from=1000&to=2000&panelId=${mockPanel.id}&width=1000&height=500&tz=UTC`
  193. );
  194. });
  195. });