spanOperationTable.spec.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import {LocationFixture} from 'sentry-fixture/locationFixture';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {ProjectFixture} from 'sentry-fixture/project';
  4. import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
  5. import {useLocation} from 'sentry/utils/useLocation';
  6. import usePageFilters from 'sentry/utils/usePageFilters';
  7. import {SpanOperationTable} from 'sentry/views/insights/mobile/appStarts/components/tables/spanOperationTable';
  8. jest.mock('sentry/utils/usePageFilters');
  9. jest.mock('sentry/utils/useLocation');
  10. describe('SpanOpSelector', function () {
  11. const organization = OrganizationFixture();
  12. const project = ProjectFixture();
  13. let mockEventsRequest;
  14. jest.mocked(usePageFilters).mockReturnValue({
  15. isReady: true,
  16. desyncedFilters: new Set(),
  17. pinnedFilters: new Set(),
  18. shouldPersist: true,
  19. selection: {
  20. datetime: {
  21. period: '10d',
  22. start: null,
  23. end: null,
  24. utc: false,
  25. },
  26. environments: [],
  27. projects: [parseInt(project.id, 10)],
  28. },
  29. });
  30. jest.mocked(useLocation).mockReturnValue(LocationFixture());
  31. beforeEach(function () {
  32. MockApiClient.clearMockResponses();
  33. mockEventsRequest = MockApiClient.addMockResponse({
  34. url: `/organizations/${organization.slug}/events/`,
  35. body: {
  36. meta: {
  37. fields: {
  38. 'project.id': 'integer',
  39. 'span.op': 'string',
  40. 'span.description': 'string',
  41. 'span.group': 'string',
  42. 'avg_if(span.self_time,release,release1)': 'duration',
  43. 'avg_compare(span.self_time,release,release1,release2)': 'percent_change',
  44. 'count()': 'integer',
  45. 'avg_if(span.self_time,release,release2)': 'duration',
  46. 'sum(span.self_time)': 'duration',
  47. },
  48. },
  49. data: [
  50. {
  51. 'project.id': parseInt(project.id, 10),
  52. 'span.op': 'app.start.warm',
  53. 'span.description': 'Application Init',
  54. 'span.group': '7f4be68f08c0455f',
  55. 'avg_if(span.self_time,release,release1)': 22.549867,
  56. 'avg_compare(span.self_time,release,release1,release2)': 0.5,
  57. 'count()': 14,
  58. 'avg_if(span.self_time,release,release2)': 12504.931908384617,
  59. 'sum(span.self_time)': 162586.66467600001,
  60. },
  61. ],
  62. },
  63. });
  64. });
  65. it('renders data properly', async function () {
  66. render(
  67. <SpanOperationTable
  68. transaction="foo-bar"
  69. primaryRelease="release1"
  70. secondaryRelease="release2"
  71. />
  72. );
  73. expect(await screen.findByRole('link', {name: 'Operation'})).toBeInTheDocument();
  74. expect(screen.getByRole('link', {name: 'Span Description'})).toBeInTheDocument();
  75. expect(screen.getByRole('link', {name: 'Avg Duration (R1)'})).toBeInTheDocument();
  76. expect(screen.getByRole('link', {name: 'Avg Duration (R2)'})).toBeInTheDocument();
  77. expect(screen.getByRole('link', {name: 'Change'})).toBeInTheDocument();
  78. expect(await screen.findByRole('cell', {name: 'app.start.warm'})).toBeInTheDocument();
  79. expect(screen.getByRole('cell', {name: 'Application Init'})).toBeInTheDocument();
  80. expect(screen.getByRole('cell', {name: '22.55ms'})).toBeInTheDocument();
  81. expect(screen.getByRole('cell', {name: '12.50s'})).toBeInTheDocument();
  82. expect(screen.getByRole('cell', {name: '+50%'})).toBeInTheDocument();
  83. expect(screen.getByRole('link', {name: 'Application Init'})).toHaveAttribute(
  84. 'href',
  85. '/organizations/org-slug/insights/app-startup/spans/?spanDescription=Application%20Init&spanGroup=7f4be68f08c0455f&spanOp=app.start.warm&transaction=foo-bar'
  86. );
  87. });
  88. it('displays the infinity symbol for new spans with null percent change', async function () {
  89. mockEventsRequest = MockApiClient.addMockResponse({
  90. url: `/organizations/${organization.slug}/events/`,
  91. body: {
  92. meta: {
  93. fields: {
  94. 'project.id': 'integer',
  95. 'span.op': 'string',
  96. 'span.description': 'string',
  97. 'span.group': 'string',
  98. 'avg_if(span.self_time,release,release1)': 'duration',
  99. 'avg_compare(span.self_time,release,release1,release2)': 'percent_change',
  100. 'count()': 'integer',
  101. 'avg_if(span.self_time,release,release2)': 'duration',
  102. 'sum(span.self_time)': 'duration',
  103. },
  104. },
  105. data: [
  106. {
  107. 'project.id': parseInt(project.id, 10),
  108. 'span.op': 'app.start.warm',
  109. 'span.description': 'Application Init',
  110. 'span.group': '7f4be68f08c0455f',
  111. 'count()': 14,
  112. 'sum(span.self_time)': 162586.66467600001,
  113. // simulate a scenario where a span was added in release 2
  114. 'avg_if(span.self_time,release,release1)': 0,
  115. 'avg_if(span.self_time,release,release2)': 12504.931908384617,
  116. 'avg_compare(span.self_time,release,release1,release2)': null,
  117. },
  118. ],
  119. },
  120. });
  121. render(
  122. <SpanOperationTable
  123. transaction="foo-bar"
  124. primaryRelease="release1"
  125. secondaryRelease="release2"
  126. />
  127. );
  128. expect(await screen.findByRole('cell', {name: '+∞%'})).toBeInTheDocument();
  129. });
  130. it('modifies the request to events when a span operation is selected', async function () {
  131. // Mock useLocation to simulate the span op query param
  132. jest.mocked(useLocation).mockReturnValue(
  133. LocationFixture({
  134. query: {
  135. 'span.op': 'app.start.cold',
  136. },
  137. })
  138. );
  139. MockApiClient.addMockResponse({
  140. url: `/organizations/${organization.slug}/events/`,
  141. body: {
  142. meta: {
  143. fields: {
  144. 'span.op': 'string',
  145. 'count()': 'integer',
  146. },
  147. },
  148. data: [
  149. {
  150. 'span.op': 'app.start.cold',
  151. 'count()': 1,
  152. },
  153. ],
  154. },
  155. match: [
  156. function (_url: string, options: Record<string, any>) {
  157. return options?.query?.referrer === 'api.starfish.get-span-operations';
  158. },
  159. ],
  160. });
  161. render(
  162. <SpanOperationTable
  163. transaction="foo-bar"
  164. primaryRelease="release1"
  165. secondaryRelease="release2"
  166. />
  167. );
  168. await waitFor(function () {
  169. expect(mockEventsRequest).toHaveBeenCalledWith(
  170. '/organizations/org-slug/events/',
  171. expect.objectContaining({
  172. query: expect.objectContaining({
  173. query: expect.stringContaining('span.op:app.start.cold'),
  174. }),
  175. })
  176. );
  177. });
  178. });
  179. });