visualizationStep.spec.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. ...initializeOrg(),
  65. organization: {
  66. features: ['dashboards-edit', 'global-views', 'dashboards-mep'],
  67. },
  68. router: {
  69. location: {
  70. query: {
  71. source: DashboardWidgetSource.DASHBOARDS,
  72. },
  73. },
  74. },
  75. });
  76. it('debounce works as expected and requests are not triggered often', async function () {
  77. const {eventsMock} = mockRequests(organization.slug);
  78. jest.useFakeTimers();
  79. render(
  80. <WidgetBuilder
  81. route={{}}
  82. router={router}
  83. routes={router.routes}
  84. routeParams={router.params}
  85. location={router.location}
  86. dashboard={{
  87. id: 'new',
  88. title: 'Dashboard',
  89. createdBy: undefined,
  90. dateCreated: '2020-01-01T00:00:00.000Z',
  91. widgets: [],
  92. projects: [],
  93. filters: {},
  94. }}
  95. onSave={jest.fn()}
  96. params={{
  97. orgId: organization.slug,
  98. dashboardId: 'new',
  99. }}
  100. />,
  101. {
  102. context: routerContext,
  103. organization,
  104. }
  105. );
  106. await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
  107. await userEvent.type(await screen.findByPlaceholderText('Alias'), 'abc', {
  108. delay: null,
  109. });
  110. act(() => jest.advanceTimersByTime(DEFAULT_DEBOUNCE_DURATION + 1));
  111. jest.useRealTimers();
  112. await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
  113. });
  114. it('displays stored data alert', async function () {
  115. mockRequests(organization.slug);
  116. MockApiClient.addMockResponse({
  117. url: `/organizations/${organization.slug}/events/`,
  118. method: 'GET',
  119. statusCode: 200,
  120. body: {
  121. meta: {isMetricsData: false},
  122. data: [],
  123. },
  124. });
  125. render(
  126. <WidgetBuilder
  127. route={{}}
  128. router={router}
  129. routes={router.routes}
  130. routeParams={router.params}
  131. location={router.location}
  132. dashboard={{
  133. id: 'new',
  134. title: 'Dashboard',
  135. createdBy: undefined,
  136. dateCreated: '2020-01-01T00:00:00.000Z',
  137. widgets: [],
  138. projects: [],
  139. filters: {},
  140. }}
  141. onSave={jest.fn()}
  142. params={{
  143. orgId: organization.slug,
  144. dashboardId: 'new',
  145. }}
  146. />,
  147. {
  148. context: routerContext,
  149. organization: {
  150. ...organization,
  151. features: [...organization.features, 'dynamic-sampling', 'mep-rollout-flag'],
  152. },
  153. }
  154. );
  155. await screen.findByText(/we've automatically adjusted your results/i);
  156. });
  157. it('uses release from URL params when querying', async function () {
  158. const {eventsMock} = mockRequests(organization.slug);
  159. render(
  160. <WidgetBuilder
  161. route={{}}
  162. router={router}
  163. routes={router.routes}
  164. routeParams={router.params}
  165. location={{
  166. ...router.location,
  167. query: {
  168. ...router.location.query,
  169. release: ['v1'],
  170. },
  171. }}
  172. dashboard={{
  173. id: 'new',
  174. title: 'Dashboard',
  175. createdBy: undefined,
  176. dateCreated: '2020-01-01T00:00:00.000Z',
  177. widgets: [],
  178. projects: [],
  179. filters: {},
  180. }}
  181. onSave={jest.fn()}
  182. params={{
  183. orgId: organization.slug,
  184. dashboardId: 'new',
  185. }}
  186. />,
  187. {
  188. context: routerContext,
  189. organization,
  190. }
  191. );
  192. await waitFor(() =>
  193. expect(eventsMock).toHaveBeenCalledWith(
  194. '/organizations/org-slug/events/',
  195. expect.objectContaining({
  196. query: expect.objectContaining({query: ' release:v1 '}),
  197. })
  198. )
  199. );
  200. });
  201. it('does not trigger an extra events request when adding a column', async function () {
  202. const {eventsMock} = mockRequests(organization.slug);
  203. render(
  204. <WidgetBuilder
  205. route={{}}
  206. router={router}
  207. routes={router.routes}
  208. routeParams={router.params}
  209. location={{
  210. ...router.location,
  211. query: {
  212. ...router.location.query,
  213. release: ['v1'],
  214. },
  215. }}
  216. dashboard={{
  217. id: 'new',
  218. title: 'Dashboard',
  219. createdBy: undefined,
  220. dateCreated: '2020-01-01T00:00:00.000Z',
  221. widgets: [],
  222. projects: [],
  223. filters: {},
  224. }}
  225. onSave={jest.fn()}
  226. params={{
  227. orgId: organization.slug,
  228. dashboardId: 'new',
  229. }}
  230. />,
  231. {
  232. context: routerContext,
  233. organization,
  234. }
  235. );
  236. await userEvent.click(screen.getByText('Add a Column'));
  237. // Only called once on the initial render
  238. await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
  239. });
  240. });