index.spec.tsx 12 KB

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