visualizationStep.spec.tsx 6.5 KB

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