visualizationStep.spec.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import {TagsFixture} from 'sentry-fixture/tags';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  4. import {DEFAULT_DEBOUNCE_DURATION} from 'sentry/constants';
  5. import ProjectsStore from 'sentry/stores/projectsStore';
  6. import type {Organization} from 'sentry/types';
  7. import {DashboardWidgetSource} from 'sentry/views/dashboards/types';
  8. import WidgetBuilder from 'sentry/views/dashboards/widgetBuilder';
  9. jest.unmock('lodash/debounce');
  10. function mockRequests(orgSlug: Organization['slug']) {
  11. const eventsMock = MockApiClient.addMockResponse({
  12. url: `/organizations/${orgSlug}/events/`,
  13. method: 'GET',
  14. statusCode: 200,
  15. body: {
  16. meta: {},
  17. data: [],
  18. },
  19. });
  20. MockApiClient.addMockResponse({
  21. url: '/organizations/org-slug/tags/',
  22. method: 'GET',
  23. body: TagsFixture(),
  24. });
  25. MockApiClient.addMockResponse({
  26. url: '/organizations/org-slug/users/',
  27. body: [],
  28. });
  29. MockApiClient.addMockResponse({
  30. url: '/organizations/org-slug/projects/',
  31. method: 'GET',
  32. body: [],
  33. });
  34. MockApiClient.addMockResponse({
  35. url: '/organizations/org-slug/measurements-meta/',
  36. method: 'GET',
  37. body: {'measurements.custom.measurement': {functions: ['p99']}},
  38. });
  39. MockApiClient.addMockResponse({
  40. url: '/organizations/org-slug/metrics-compatibility/',
  41. method: 'GET',
  42. body: {
  43. incompatible_projects: [],
  44. compatible_projects: [1],
  45. },
  46. });
  47. MockApiClient.addMockResponse({
  48. url: '/organizations/org-slug/metrics-compatibility-sums/',
  49. method: 'GET',
  50. body: {
  51. sum: {
  52. metrics: 988803,
  53. metrics_null: 0,
  54. metrics_unparam: 132,
  55. },
  56. },
  57. });
  58. MockApiClient.addMockResponse({
  59. url: '/organizations/org-slug/releases/',
  60. body: [],
  61. });
  62. return {eventsMock};
  63. }
  64. describe('VisualizationStep', function () {
  65. const {organization, router, routerContext} = initializeOrg({
  66. organization: {
  67. features: ['dashboards-edit', 'global-views', 'dashboards-mep'],
  68. },
  69. router: {
  70. location: {
  71. query: {
  72. source: DashboardWidgetSource.DASHBOARDS,
  73. },
  74. },
  75. },
  76. });
  77. beforeEach(function () {
  78. ProjectsStore.loadInitialData(organization.projects);
  79. });
  80. it('debounce works as expected and requests are not triggered often', async function () {
  81. const {eventsMock} = mockRequests(organization.slug);
  82. jest.useFakeTimers();
  83. render(
  84. <WidgetBuilder
  85. route={{}}
  86. router={router}
  87. routes={router.routes}
  88. routeParams={router.params}
  89. location={router.location}
  90. dashboard={{
  91. id: 'new',
  92. title: 'Dashboard',
  93. createdBy: undefined,
  94. dateCreated: '2020-01-01T00:00:00.000Z',
  95. widgets: [],
  96. projects: [],
  97. filters: {},
  98. }}
  99. onSave={jest.fn()}
  100. params={{
  101. orgId: organization.slug,
  102. dashboardId: 'new',
  103. }}
  104. />,
  105. {
  106. context: routerContext,
  107. organization,
  108. }
  109. );
  110. await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
  111. await userEvent.type(await screen.findByPlaceholderText('Alias'), 'abc', {
  112. delay: null,
  113. });
  114. act(() => jest.advanceTimersByTime(DEFAULT_DEBOUNCE_DURATION + 1));
  115. jest.useRealTimers();
  116. await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
  117. });
  118. it('displays stored data alert', async function () {
  119. mockRequests(organization.slug);
  120. MockApiClient.addMockResponse({
  121. url: `/organizations/${organization.slug}/events/`,
  122. method: 'GET',
  123. statusCode: 200,
  124. body: {
  125. meta: {isMetricsData: false},
  126. data: [],
  127. },
  128. });
  129. render(
  130. <WidgetBuilder
  131. route={{}}
  132. router={router}
  133. routes={router.routes}
  134. routeParams={router.params}
  135. location={router.location}
  136. dashboard={{
  137. id: 'new',
  138. title: 'Dashboard',
  139. createdBy: undefined,
  140. dateCreated: '2020-01-01T00:00:00.000Z',
  141. widgets: [],
  142. projects: [],
  143. filters: {},
  144. }}
  145. onSave={jest.fn()}
  146. params={{
  147. orgId: organization.slug,
  148. dashboardId: 'new',
  149. }}
  150. />,
  151. {
  152. context: routerContext,
  153. organization: {
  154. ...organization,
  155. features: [...organization.features, 'dynamic-sampling', 'mep-rollout-flag'],
  156. },
  157. }
  158. );
  159. await screen.findByText(/we've automatically adjusted your results/i);
  160. });
  161. it('uses release from URL params when querying', async function () {
  162. const {eventsMock} = mockRequests(organization.slug);
  163. render(
  164. <WidgetBuilder
  165. route={{}}
  166. router={router}
  167. routes={router.routes}
  168. routeParams={router.params}
  169. location={{
  170. ...router.location,
  171. query: {
  172. ...router.location.query,
  173. release: ['v1'],
  174. },
  175. }}
  176. dashboard={{
  177. id: 'new',
  178. title: 'Dashboard',
  179. createdBy: undefined,
  180. dateCreated: '2020-01-01T00:00:00.000Z',
  181. widgets: [],
  182. projects: [],
  183. filters: {},
  184. }}
  185. onSave={jest.fn()}
  186. params={{
  187. orgId: organization.slug,
  188. dashboardId: 'new',
  189. }}
  190. />,
  191. {
  192. context: routerContext,
  193. organization,
  194. }
  195. );
  196. await waitFor(() =>
  197. expect(eventsMock).toHaveBeenCalledWith(
  198. '/organizations/org-slug/events/',
  199. expect.objectContaining({
  200. query: expect.objectContaining({query: ' release:"v1" '}),
  201. })
  202. )
  203. );
  204. });
  205. it('does not trigger an extra events request when adding a column', async function () {
  206. const {eventsMock} = mockRequests(organization.slug);
  207. render(
  208. <WidgetBuilder
  209. route={{}}
  210. router={router}
  211. routes={router.routes}
  212. routeParams={router.params}
  213. location={{
  214. ...router.location,
  215. query: {
  216. ...router.location.query,
  217. release: ['v1'],
  218. },
  219. }}
  220. dashboard={{
  221. id: 'new',
  222. title: 'Dashboard',
  223. createdBy: undefined,
  224. dateCreated: '2020-01-01T00:00:00.000Z',
  225. widgets: [],
  226. projects: [],
  227. filters: {},
  228. }}
  229. onSave={jest.fn()}
  230. params={{
  231. orgId: organization.slug,
  232. dashboardId: 'new',
  233. }}
  234. />,
  235. {
  236. context: routerContext,
  237. organization,
  238. }
  239. );
  240. await userEvent.click(screen.getByText('Add a Column'));
  241. // Only called once on the initial render
  242. await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
  243. });
  244. });