index.spec.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. import {GroupFixture} from 'sentry-fixture/group';
  2. import {LocationFixture} from 'sentry-fixture/locationFixture';
  3. import {OrganizationFixture} from 'sentry-fixture/organization';
  4. import {ProjectFixture} from 'sentry-fixture/project';
  5. import {RouteComponentPropsFixture} from 'sentry-fixture/routeComponentPropsFixture';
  6. import {initializeOrg} from 'sentry-test/initializeOrg';
  7. import {act, render, screen} from 'sentry-test/reactTestingLibrary';
  8. import ProjectsStore from 'sentry/stores/projectsStore';
  9. import EventView from 'sentry/utils/discover/eventView';
  10. import {DEFAULT_EVENT_VIEW, getAllViews} from 'sentry/views/discover/data';
  11. import EventDetails from 'sentry/views/discover/eventDetails';
  12. describe('Discover > EventDetails', function () {
  13. const allEventsView = EventView.fromSavedQuery(DEFAULT_EVENT_VIEW);
  14. const org = OrganizationFixture();
  15. const errorsView = EventView.fromSavedQuery(
  16. getAllViews(org).find(view => view.name === 'Errors by Title')!
  17. );
  18. beforeEach(function () {
  19. act(() => ProjectsStore.loadInitialData([ProjectFixture()]));
  20. MockApiClient.addMockResponse({
  21. url: '/organizations/org-slug/projects/',
  22. body: [],
  23. });
  24. MockApiClient.addMockResponse({
  25. url: '/organizations/org-slug/discover/',
  26. body: {
  27. meta: {
  28. id: 'string',
  29. title: 'string',
  30. 'project.name': 'string',
  31. timestamp: 'date',
  32. },
  33. data: [
  34. {
  35. id: 'deadbeef',
  36. title: 'Oh no something bad',
  37. 'project.name': 'project-slug',
  38. timestamp: '2019-05-23T22:12:48+00:00',
  39. },
  40. ],
  41. },
  42. });
  43. MockApiClient.addMockResponse({
  44. url: '/organizations/org-slug/events/project-slug:deadbeef/',
  45. method: 'GET',
  46. body: {
  47. id: '1234',
  48. size: 1200,
  49. projectSlug: 'project-slug',
  50. eventID: 'deadbeef',
  51. groupID: '123',
  52. title: 'Oh no something bad',
  53. location: '/users/login',
  54. message: 'It was not good',
  55. dateCreated: '2019-05-23T22:12:48+00:00',
  56. entries: [
  57. {
  58. type: 'message',
  59. message: 'bad stuff',
  60. data: {},
  61. },
  62. ],
  63. tags: [
  64. {key: 'browser', value: 'Firefox'},
  65. {key: 'device.uuid', value: 'test-uuid'},
  66. {key: 'release', value: '82ebf297206a'},
  67. ],
  68. },
  69. });
  70. MockApiClient.addMockResponse({
  71. url: '/issues/123/',
  72. method: 'GET',
  73. body: GroupFixture({id: '123'}),
  74. });
  75. MockApiClient.addMockResponse({
  76. url: '/organizations/org-slug/events-stats/',
  77. method: 'GET',
  78. body: {
  79. data: [
  80. [1234561700, [1]],
  81. [1234561800, [1]],
  82. ],
  83. },
  84. });
  85. MockApiClient.addMockResponse({
  86. url: '/projects/org-slug/project-slug/events/1234/committers/',
  87. method: 'GET',
  88. statusCode: 404,
  89. body: {},
  90. });
  91. MockApiClient.addMockResponse({
  92. url: '/projects/org-slug/project-slug/events/1234/grouping-info/',
  93. body: {},
  94. });
  95. // Missing event
  96. MockApiClient.addMockResponse({
  97. url: '/organizations/org-slug/events/project-slug:abad1/',
  98. method: 'GET',
  99. statusCode: 404,
  100. body: {},
  101. });
  102. MockApiClient.addMockResponse({
  103. url: '/projects/org-slug/project-slug/events/1234/actionable-items/',
  104. body: {
  105. errors: [],
  106. },
  107. });
  108. });
  109. it('renders', async function () {
  110. render(
  111. <EventDetails
  112. {...RouteComponentPropsFixture()}
  113. organization={OrganizationFixture()}
  114. params={{eventSlug: 'project-slug:deadbeef'}}
  115. location={{
  116. ...LocationFixture(),
  117. query: allEventsView.generateQueryStringObject(),
  118. }}
  119. />
  120. );
  121. expect(await screen.findByText('Oh no something bad')).toBeInTheDocument();
  122. });
  123. it('renders a 404', async function () {
  124. render(
  125. <EventDetails
  126. {...RouteComponentPropsFixture()}
  127. organization={OrganizationFixture()}
  128. params={{eventSlug: 'project-slug:abad1'}}
  129. location={{
  130. ...LocationFixture(),
  131. query: allEventsView.generateQueryStringObject(),
  132. }}
  133. />
  134. );
  135. expect(await screen.findByText('Page Not Found')).toBeInTheDocument();
  136. });
  137. it('renders a chart in grouped view', async function () {
  138. render(
  139. <EventDetails
  140. {...RouteComponentPropsFixture()}
  141. organization={OrganizationFixture()}
  142. params={{eventSlug: 'project-slug:deadbeef'}}
  143. location={{
  144. ...LocationFixture(),
  145. query: errorsView.generateQueryStringObject(),
  146. }}
  147. />
  148. );
  149. expect(await screen.findByText('Oh no something bad')).toBeInTheDocument();
  150. });
  151. it('renders an alert when linked issues are missing', async function () {
  152. MockApiClient.addMockResponse({
  153. url: '/issues/123/',
  154. statusCode: 404,
  155. method: 'GET',
  156. body: {},
  157. });
  158. render(
  159. <EventDetails
  160. {...RouteComponentPropsFixture()}
  161. organization={OrganizationFixture()}
  162. params={{eventSlug: 'project-slug:deadbeef'}}
  163. location={{
  164. ...LocationFixture(),
  165. query: allEventsView.generateQueryStringObject(),
  166. }}
  167. />
  168. );
  169. expect(
  170. await screen.findByText(
  171. 'The linked issue cannot be found. It may have been deleted, or merged.'
  172. )
  173. ).toBeInTheDocument();
  174. });
  175. it('navigates when tag values are clicked', async function () {
  176. const {organization, router} = initializeOrg({
  177. organization: OrganizationFixture(),
  178. router: {
  179. location: {
  180. pathname: '/organizations/org-slug/discover/project-slug:deadbeef',
  181. query: {},
  182. },
  183. },
  184. });
  185. render(
  186. <EventDetails
  187. {...RouteComponentPropsFixture()}
  188. organization={organization}
  189. params={{eventSlug: 'project-slug:deadbeef'}}
  190. location={{
  191. ...LocationFixture(),
  192. query: allEventsView.generateQueryStringObject(),
  193. }}
  194. />,
  195. {router}
  196. );
  197. // Get the first link as we wrap react-router's link
  198. expect(await screen.findByText('Firefox')).toBeInTheDocument();
  199. expect(screen.getByRole('link', {name: 'Firefox'})).toHaveAttribute(
  200. 'href',
  201. '/organizations/org-slug/discover/results/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=browser%3AFirefox%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  202. );
  203. // Get the second link
  204. expect(screen.getByRole('link', {name: 'test-uuid'})).toHaveAttribute(
  205. 'href',
  206. '/organizations/org-slug/discover/results/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=tags%5Bdevice.uuid%5D%3Atest-uuid%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  207. );
  208. // Get the third link
  209. expect(screen.getByRole('link', {name: '82ebf297206a'})).toHaveAttribute(
  210. 'href',
  211. '/organizations/org-slug/discover/results/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=release%3A82ebf297206a%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  212. );
  213. });
  214. it('navigates to homepage when tag values are clicked', async function () {
  215. const {organization, router} = initializeOrg({
  216. organization: OrganizationFixture(),
  217. router: {
  218. location: {
  219. pathname: '/organizations/org-slug/discover/project-slug:deadbeef',
  220. query: {...allEventsView.generateQueryStringObject(), homepage: 'true'},
  221. },
  222. },
  223. });
  224. render(
  225. <EventDetails
  226. {...RouteComponentPropsFixture()}
  227. organization={organization}
  228. params={{eventSlug: 'project-slug:deadbeef'}}
  229. location={router.location}
  230. />,
  231. {router}
  232. );
  233. // Get the first link as we wrap react-router's link
  234. expect(await screen.findByText('Firefox')).toBeInTheDocument();
  235. expect(screen.getByRole('link', {name: 'Firefox'})).toHaveAttribute(
  236. 'href',
  237. '/organizations/org-slug/discover/homepage/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=browser%3AFirefox%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  238. );
  239. // Get the second link
  240. expect(screen.getByRole('link', {name: 'test-uuid'})).toHaveAttribute(
  241. 'href',
  242. '/organizations/org-slug/discover/homepage/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=tags%5Bdevice.uuid%5D%3Atest-uuid%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  243. );
  244. // Get the third link
  245. expect(screen.getByRole('link', {name: '82ebf297206a'})).toHaveAttribute(
  246. 'href',
  247. '/organizations/org-slug/discover/homepage/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=release%3A82ebf297206a%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  248. );
  249. });
  250. it('appends tag value to existing query when clicked', async function () {
  251. const {organization, router} = initializeOrg({
  252. organization: OrganizationFixture(),
  253. router: {
  254. location: {
  255. pathname: '/organizations/org-slug/discover/project-slug:deadbeef',
  256. query: {},
  257. },
  258. },
  259. });
  260. render(
  261. <EventDetails
  262. {...RouteComponentPropsFixture()}
  263. organization={organization}
  264. params={{eventSlug: 'project-slug:deadbeef'}}
  265. location={{
  266. ...LocationFixture(),
  267. query: {...allEventsView.generateQueryStringObject(), query: 'Dumpster'},
  268. }}
  269. />,
  270. {router}
  271. );
  272. // Get the first link as we wrap react-router's link
  273. expect(await screen.findByText('Firefox')).toBeInTheDocument();
  274. expect(screen.getByRole('link', {name: 'Firefox'})).toHaveAttribute(
  275. 'href',
  276. '/organizations/org-slug/discover/results/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=Dumpster%20browser%3AFirefox%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  277. );
  278. // Get the second link
  279. expect(screen.getByRole('link', {name: 'test-uuid'})).toHaveAttribute(
  280. 'href',
  281. '/organizations/org-slug/discover/results/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=Dumpster%20tags%5Bdevice.uuid%5D%3Atest-uuid%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  282. );
  283. // Get the third link
  284. expect(screen.getByRole('link', {name: '82ebf297206a'})).toHaveAttribute(
  285. 'href',
  286. '/organizations/org-slug/discover/results/?field=title&field=event.type&field=project&field=user.display&field=timestamp&name=All%20Events&query=Dumpster%20release%3A82ebf297206a%20title%3A%22Oh%20no%20something%20bad%22&sort=-timestamp&statsPeriod=24h&yAxis=count%28%29'
  287. );
  288. });
  289. it('links back to the homepage if the query param contains homepage flag', async () => {
  290. const {organization, router} = initializeOrg({
  291. organization: OrganizationFixture(),
  292. router: {
  293. location: {
  294. pathname: '/organizations/org-slug/discover/project-slug:deadbeef',
  295. query: {...allEventsView.generateQueryStringObject(), homepage: '1'},
  296. },
  297. },
  298. });
  299. render(
  300. <EventDetails
  301. {...RouteComponentPropsFixture()}
  302. organization={organization}
  303. params={{eventSlug: 'project-slug:deadbeef'}}
  304. location={router.location}
  305. />,
  306. {router, organization}
  307. );
  308. const breadcrumb = await screen.findByTestId('breadcrumb-link');
  309. expect(breadcrumb).toHaveTextContent('Discover');
  310. expect(breadcrumb).toHaveAttribute(
  311. 'href',
  312. expect.stringMatching(new RegExp('^/organizations/org-slug/discover/homepage/?'))
  313. );
  314. });
  315. });