widgetBuilderSlideout.spec.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import {DashboardFixture} from 'sentry-fixture/dashboard';
  2. import {LocationFixture} from 'sentry-fixture/locationFixture';
  3. import {OrganizationFixture} from 'sentry-fixture/organization';
  4. import {RouterFixture} from 'sentry-fixture/routerFixture';
  5. import {
  6. render,
  7. renderGlobalModal,
  8. screen,
  9. userEvent,
  10. waitFor,
  11. } from 'sentry-test/reactTestingLibrary';
  12. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  13. import ModalStore from 'sentry/stores/modalStore';
  14. import useCustomMeasurements from 'sentry/utils/useCustomMeasurements';
  15. import {DisplayType, WidgetType} from 'sentry/views/dashboards/types';
  16. import WidgetBuilderSlideout from 'sentry/views/dashboards/widgetBuilder/components/widgetBuilderSlideout';
  17. import {WidgetBuilderProvider} from 'sentry/views/dashboards/widgetBuilder/contexts/widgetBuilderContext';
  18. import {useSpanTags} from 'sentry/views/explore/contexts/spanTagsContext';
  19. jest.mock('sentry/utils/useCustomMeasurements');
  20. jest.mock('sentry/views/explore/contexts/spanTagsContext');
  21. jest.mock('sentry/actionCreators/indicator');
  22. describe('WidgetBuilderSlideout', () => {
  23. let organization!: ReturnType<typeof OrganizationFixture>;
  24. beforeEach(() => {
  25. organization = OrganizationFixture();
  26. jest.mocked(useCustomMeasurements).mockReturnValue({
  27. customMeasurements: {},
  28. });
  29. jest.mocked(useSpanTags).mockReturnValue({});
  30. MockApiClient.addMockResponse({
  31. url: '/organizations/org-slug/recent-searches/',
  32. });
  33. MockApiClient.addMockResponse({
  34. url: '/organizations/org-slug/dashboards/widgets/',
  35. method: 'POST',
  36. body: {
  37. title: 'Title is required during creation',
  38. },
  39. statusCode: 400,
  40. });
  41. });
  42. afterEach(() => {
  43. ModalStore.reset();
  44. });
  45. it('should show the sort by step if the widget is a chart and there are fields selected', async () => {
  46. render(
  47. <WidgetBuilderProvider>
  48. <WidgetBuilderSlideout
  49. dashboard={DashboardFixture([])}
  50. dashboardFilters={{
  51. release: undefined,
  52. }}
  53. isWidgetInvalid={false}
  54. onClose={jest.fn()}
  55. onQueryConditionChange={jest.fn()}
  56. onSave={jest.fn()}
  57. setIsPreviewDraggable={jest.fn()}
  58. isOpen
  59. />
  60. </WidgetBuilderProvider>,
  61. {
  62. organization,
  63. router: RouterFixture({
  64. location: LocationFixture({
  65. query: {
  66. field: ['project'],
  67. yAxis: ['count()'],
  68. dataset: WidgetType.TRANSACTIONS,
  69. displayType: DisplayType.LINE,
  70. },
  71. }),
  72. }),
  73. }
  74. );
  75. expect(await screen.findByText('Sort by')).toBeInTheDocument();
  76. expect(await screen.findByText('Limit to 5 results')).toBeInTheDocument();
  77. expect(await screen.findByText('High to low')).toBeInTheDocument();
  78. expect(await screen.findByText('(Required)')).toBeInTheDocument();
  79. });
  80. it('should show the sort by step if the widget is a table', async () => {
  81. render(
  82. <WidgetBuilderProvider>
  83. <WidgetBuilderSlideout
  84. dashboard={DashboardFixture([])}
  85. dashboardFilters={{
  86. release: undefined,
  87. }}
  88. isWidgetInvalid={false}
  89. onClose={jest.fn()}
  90. onQueryConditionChange={jest.fn()}
  91. onSave={jest.fn()}
  92. setIsPreviewDraggable={jest.fn()}
  93. isOpen
  94. />
  95. </WidgetBuilderProvider>,
  96. {
  97. organization,
  98. router: RouterFixture({
  99. location: LocationFixture({
  100. query: {
  101. field: [],
  102. yAxis: [],
  103. dataset: WidgetType.TRANSACTIONS,
  104. displayType: DisplayType.TABLE,
  105. },
  106. }),
  107. }),
  108. }
  109. );
  110. expect(await screen.findByText('Sort by')).toBeInTheDocument();
  111. });
  112. it('should not show the sort by step if the widget is a chart without fields', async () => {
  113. render(
  114. <WidgetBuilderProvider>
  115. <WidgetBuilderSlideout
  116. dashboard={DashboardFixture([])}
  117. dashboardFilters={{
  118. release: undefined,
  119. }}
  120. isWidgetInvalid={false}
  121. onClose={jest.fn()}
  122. onQueryConditionChange={jest.fn()}
  123. onSave={jest.fn()}
  124. setIsPreviewDraggable={jest.fn()}
  125. isOpen
  126. />
  127. </WidgetBuilderProvider>,
  128. {
  129. organization,
  130. router: RouterFixture({
  131. location: LocationFixture({
  132. query: {
  133. field: [],
  134. yAxis: ['count()'],
  135. dataset: WidgetType.TRANSACTIONS,
  136. displayType: DisplayType.LINE,
  137. },
  138. }),
  139. }),
  140. }
  141. );
  142. expect(await screen.findByText('count')).toBeInTheDocument();
  143. expect(screen.queryByText('Sort by')).not.toBeInTheDocument();
  144. });
  145. it('should show the confirm modal if the widget is unsaved', async () => {
  146. render(
  147. <WidgetBuilderProvider>
  148. <WidgetBuilderSlideout
  149. dashboard={DashboardFixture([])}
  150. dashboardFilters={{release: undefined}}
  151. isWidgetInvalid={false}
  152. onClose={jest.fn()}
  153. onQueryConditionChange={jest.fn()}
  154. onSave={jest.fn()}
  155. setIsPreviewDraggable={jest.fn()}
  156. isOpen
  157. />
  158. </WidgetBuilderProvider>,
  159. {organization}
  160. );
  161. renderGlobalModal();
  162. await userEvent.type(await screen.findByPlaceholderText('Name'), 'some name');
  163. await userEvent.click(await screen.findByText('Close'));
  164. expect(screen.getByRole('dialog')).toBeInTheDocument();
  165. expect(
  166. screen.getByText('You have unsaved changes. Are you sure you want to leave?')
  167. ).toBeInTheDocument();
  168. });
  169. it('should not show the confirm modal if the widget is unsaved', async () => {
  170. render(
  171. <WidgetBuilderProvider>
  172. <WidgetBuilderSlideout
  173. dashboard={DashboardFixture([])}
  174. dashboardFilters={{release: undefined}}
  175. isWidgetInvalid={false}
  176. onClose={jest.fn()}
  177. onQueryConditionChange={jest.fn()}
  178. onSave={jest.fn()}
  179. setIsPreviewDraggable={jest.fn()}
  180. isOpen
  181. />
  182. </WidgetBuilderProvider>,
  183. {organization}
  184. );
  185. renderGlobalModal();
  186. await userEvent.click(await screen.findByText('Close'));
  187. await waitFor(() => {
  188. expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
  189. });
  190. expect(
  191. screen.queryByText('You have unsaved changes. Are you sure you want to leave?')
  192. ).not.toBeInTheDocument();
  193. });
  194. it('should not save and close the widget builder if the widget is invalid', async () => {
  195. render(
  196. <WidgetBuilderProvider>
  197. <WidgetBuilderSlideout
  198. dashboard={DashboardFixture([])}
  199. dashboardFilters={{release: undefined}}
  200. isWidgetInvalid
  201. onClose={jest.fn()}
  202. onQueryConditionChange={jest.fn()}
  203. onSave={jest.fn()}
  204. setIsPreviewDraggable={jest.fn()}
  205. isOpen
  206. />
  207. </WidgetBuilderProvider>,
  208. {
  209. organization,
  210. router: RouterFixture({
  211. location: LocationFixture({
  212. query: {
  213. field: [],
  214. yAxis: ['count()'],
  215. dataset: WidgetType.TRANSACTIONS,
  216. displayType: DisplayType.LINE,
  217. title: undefined,
  218. },
  219. }),
  220. }),
  221. }
  222. );
  223. await userEvent.click(await screen.findByText('Add Widget'));
  224. await waitFor(() => {
  225. expect(addErrorMessage).toHaveBeenCalledWith('Unable to save widget');
  226. });
  227. expect(screen.getByText('Create Custom Widget')).toBeInTheDocument();
  228. });
  229. });