issueWidgetCard.spec.tsx 6.4 KB

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