issueWidgetCard.spec.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  3. import MemberListStore from 'sentry/stores/memberListStore';
  4. import {DisplayType, Widget, WidgetType} from 'sentry/views/dashboards/types';
  5. import WidgetCard from 'sentry/views/dashboards/widgetCard';
  6. import {IssueSortOptions} from 'sentry/views/issueList/utils';
  7. describe('Dashboards > IssueWidgetCard', function () {
  8. const {router, organization, routerContext} = initializeOrg({
  9. organization: TestStubs.Organization({
  10. features: ['dashboards-edit'],
  11. }),
  12. router: {orgId: 'orgId'},
  13. } as Parameters<typeof initializeOrg>[0]);
  14. const widget: Widget = {
  15. title: 'Issues',
  16. interval: '5m',
  17. displayType: DisplayType.TABLE,
  18. widgetType: WidgetType.ISSUE,
  19. queries: [
  20. {
  21. conditions: 'event.type:default',
  22. fields: ['issue', 'assignee', 'title'],
  23. columns: ['issue', 'assignee', 'title'],
  24. aggregates: [],
  25. name: '',
  26. orderby: IssueSortOptions.FREQ,
  27. },
  28. ],
  29. };
  30. const selection = {
  31. projects: [1],
  32. environments: ['prod'],
  33. datetime: {
  34. period: '14d',
  35. start: null,
  36. end: null,
  37. utc: false,
  38. },
  39. };
  40. const api = new MockApiClient();
  41. beforeEach(function () {
  42. MockApiClient.addMockResponse({
  43. url: '/organizations/org-slug/issues/',
  44. body: [
  45. {
  46. id: '44444444',
  47. title: 'ChunkLoadError: Loading chunk app_bootstrap_index_tsx failed.',
  48. shortId: 'ISSUE',
  49. assignedTo: {
  50. type: 'user',
  51. id: '2222222',
  52. name: 'dashboard user',
  53. email: 'dashboarduser@sentry.io',
  54. },
  55. lifetime: {count: 10, userCount: 5},
  56. count: 6,
  57. userCount: 3,
  58. project: {id: 1},
  59. },
  60. ],
  61. });
  62. MockApiClient.addMockResponse({
  63. url: '/organizations/org-slug/users/',
  64. method: 'GET',
  65. body: [],
  66. });
  67. });
  68. afterEach(function () {
  69. MockApiClient.clearMockResponses();
  70. });
  71. it('renders with title and issues chart', async function () {
  72. MemberListStore.loadInitialData([]);
  73. render(
  74. <WidgetCard
  75. api={api}
  76. organization={organization}
  77. widget={widget}
  78. selection={selection}
  79. isEditing={false}
  80. onDelete={() => undefined}
  81. onEdit={() => undefined}
  82. onDuplicate={() => undefined}
  83. renderErrorMessage={() => undefined}
  84. showContextMenu
  85. widgetLimitReached={false}
  86. />
  87. );
  88. expect(await screen.findByText('Issues')).toBeInTheDocument();
  89. expect(screen.getByText('assignee')).toBeInTheDocument();
  90. expect(screen.getByText('title')).toBeInTheDocument();
  91. expect(screen.getByText('issue')).toBeInTheDocument();
  92. expect(screen.getByText('DU')).toBeInTheDocument();
  93. expect(screen.getByText('ISSUE')).toBeInTheDocument();
  94. expect(
  95. screen.getByText('ChunkLoadError: Loading chunk app_bootstrap_index_tsx failed.')
  96. ).toBeInTheDocument();
  97. await userEvent.hover(screen.getByTitle('dashboard user'));
  98. expect(await screen.findByText('Assigned to dashboard user')).toBeInTheDocument();
  99. });
  100. it('opens in issues page', async function () {
  101. render(
  102. <WidgetCard
  103. api={api}
  104. organization={organization}
  105. widget={widget}
  106. selection={selection}
  107. isEditing={false}
  108. onDelete={() => undefined}
  109. onEdit={() => undefined}
  110. onDuplicate={() => undefined}
  111. renderErrorMessage={() => undefined}
  112. showContextMenu
  113. widgetLimitReached={false}
  114. />,
  115. {context: routerContext}
  116. );
  117. await userEvent.click(await screen.findByLabelText('Widget actions'));
  118. expect(screen.getByText('Duplicate Widget')).toBeInTheDocument();
  119. expect(screen.getByText('Open in Issues')).toBeInTheDocument();
  120. await userEvent.click(screen.getByText('Open in Issues'));
  121. expect(router.push).toHaveBeenCalledWith(
  122. '/organizations/org-slug/issues/?environment=prod&project=1&query=event.type%3Adefault&sort=freq&statsPeriod=14d'
  123. );
  124. });
  125. it('calls onDuplicate when Duplicate Widget is clicked', async function () {
  126. const mock = jest.fn();
  127. render(
  128. <WidgetCard
  129. api={api}
  130. organization={organization}
  131. widget={widget}
  132. selection={selection}
  133. isEditing={false}
  134. onDelete={() => undefined}
  135. onEdit={() => undefined}
  136. onDuplicate={mock}
  137. renderErrorMessage={() => undefined}
  138. showContextMenu
  139. widgetLimitReached={false}
  140. />
  141. );
  142. await userEvent.click(await screen.findByLabelText('Widget actions'));
  143. expect(screen.getByText('Duplicate Widget')).toBeInTheDocument();
  144. await userEvent.click(screen.getByText('Duplicate Widget'));
  145. expect(mock).toHaveBeenCalledTimes(1);
  146. });
  147. it('disables the duplicate widget button if max widgets is reached', async function () {
  148. const mock = jest.fn();
  149. render(
  150. <WidgetCard
  151. api={api}
  152. organization={organization}
  153. widget={widget}
  154. selection={selection}
  155. isEditing={false}
  156. onDelete={() => undefined}
  157. onEdit={() => undefined}
  158. onDuplicate={mock}
  159. renderErrorMessage={() => undefined}
  160. showContextMenu
  161. widgetLimitReached
  162. />
  163. );
  164. await userEvent.click(await screen.findByLabelText('Widget actions'));
  165. expect(screen.getByText('Duplicate Widget')).toBeInTheDocument();
  166. await userEvent.click(screen.getByText('Duplicate Widget'));
  167. expect(mock).toHaveBeenCalledTimes(0);
  168. });
  169. it('maps lifetimeEvents and lifetimeUsers headers to more human readable', async function () {
  170. MemberListStore.loadInitialData([]);
  171. render(
  172. <WidgetCard
  173. api={api}
  174. organization={organization}
  175. widget={{
  176. ...widget,
  177. queries: [
  178. {
  179. ...widget.queries[0],
  180. fields: ['issue', 'assignee', 'title', 'lifetimeEvents', 'lifetimeUsers'],
  181. },
  182. ],
  183. }}
  184. selection={selection}
  185. isEditing={false}
  186. onDelete={() => undefined}
  187. onEdit={() => undefined}
  188. onDuplicate={() => undefined}
  189. renderErrorMessage={() => undefined}
  190. showContextMenu
  191. widgetLimitReached={false}
  192. />
  193. );
  194. expect(await screen.findByText('Lifetime Events')).toBeInTheDocument();
  195. expect(screen.getByText('Lifetime Users')).toBeInTheDocument();
  196. });
  197. });