slowestFunctionsWidget.spec.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  2. import ProjectsStore from 'sentry/stores/projectsStore';
  3. import {SlowestFunctionsWidget} from 'sentry/views/profiling/landing/slowestFunctionsWidget';
  4. describe('SlowestFunctionsWidget', function () {
  5. beforeEach(function () {
  6. const project = TestStubs.Project({
  7. id: '1',
  8. slug: 'proj-slug',
  9. });
  10. ProjectsStore.loadInitialData([project]);
  11. });
  12. afterEach(function () {
  13. MockApiClient.clearMockResponses();
  14. });
  15. it('renders errors', async function () {
  16. // return 400 for all queries
  17. MockApiClient.addMockResponse({
  18. url: '/organizations/org-slug/events/',
  19. statusCode: 400,
  20. });
  21. render(<SlowestFunctionsWidget />);
  22. // starts by rendering loading
  23. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  24. // switches to errors once the api responds with an error
  25. expect(await screen.findByTestId('error-indicator')).toBeInTheDocument();
  26. });
  27. it('renders no functions', async function () {
  28. // for the slowest functions query
  29. MockApiClient.addMockResponse({
  30. url: '/organizations/org-slug/events/',
  31. body: {
  32. data: [],
  33. },
  34. match: [
  35. MockApiClient.matchQuery({
  36. dataset: 'profileFunctions',
  37. field: ['project.id', 'package', 'function', 'count()', 'sum()'],
  38. }),
  39. ],
  40. });
  41. render(<SlowestFunctionsWidget />);
  42. // starts by rendering loading
  43. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  44. // switches to the no functions view
  45. expect(await screen.findByText('No functions found')).toBeInTheDocument();
  46. });
  47. it('renders example transactions', async function () {
  48. // for the slowest functions query
  49. MockApiClient.addMockResponse({
  50. url: '/organizations/org-slug/events/',
  51. body: {
  52. data: [
  53. {
  54. 'project.id': 1,
  55. package: 'foo',
  56. function: 'bar',
  57. 'sum()': 150,
  58. },
  59. {
  60. 'project.id': 1,
  61. package: 'baz',
  62. function: 'qux',
  63. 'sum()': 100,
  64. },
  65. ],
  66. },
  67. match: [
  68. MockApiClient.matchQuery({
  69. dataset: 'profileFunctions',
  70. field: ['project.id', 'package', 'function', 'count()', 'sum()'],
  71. }),
  72. ],
  73. });
  74. // for the totals query
  75. MockApiClient.addMockResponse({
  76. url: '/organizations/org-slug/events/',
  77. body: {data: [{'project.id': 1, 'sum()': 2500000}]},
  78. match: [
  79. MockApiClient.matchQuery({
  80. dataset: 'profileFunctions',
  81. field: ['project.id', 'sum()'],
  82. project: [1],
  83. }),
  84. ],
  85. });
  86. // first function examples
  87. MockApiClient.addMockResponse({
  88. url: '/organizations/org-slug/events/',
  89. body: {
  90. data: [
  91. {
  92. transaction: 'transaction-1',
  93. 'count()': 1000,
  94. 'sum()': 1000000,
  95. 'examples()': [
  96. 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  97. 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
  98. ],
  99. },
  100. {
  101. transaction: 'transaction-2',
  102. 'count()': 2500,
  103. 'sum()': 500000,
  104. 'examples()': ['cccccccccccccccccccccccccccccccc'],
  105. },
  106. ],
  107. },
  108. match: [
  109. MockApiClient.matchQuery({
  110. dataset: 'profileFunctions',
  111. query: 'project.id:1 package:foo function:bar',
  112. field: ['transaction', 'count()', 'sum()', 'examples()'],
  113. }),
  114. ],
  115. });
  116. // second function examples
  117. MockApiClient.addMockResponse({
  118. url: '/organizations/org-slug/events/',
  119. body: {
  120. data: [
  121. {
  122. transaction: 'transaction-3',
  123. 'count()': 2000,
  124. 'sum()': 2000000,
  125. 'examples()': [
  126. 'dddddddddddddddddddddddddddddddd',
  127. 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
  128. ],
  129. },
  130. {
  131. transaction: 'transaction-4',
  132. 'count()': 3500,
  133. 'sum()': 700000,
  134. 'examples()': ['ffffffffffffffffffffffffffffffff'],
  135. },
  136. ],
  137. },
  138. match: [
  139. MockApiClient.matchQuery({
  140. dataset: 'profileFunctions',
  141. query: 'project.id:1 package:baz function:qux',
  142. field: ['transaction', 'count()', 'sum()', 'examples()'],
  143. }),
  144. ],
  145. });
  146. render(<SlowestFunctionsWidget />);
  147. // starts by rendering loading
  148. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  149. // switches to the transactions list once the api responds with data
  150. expect(await screen.findByTestId('transactions-list')).toBeInTheDocument();
  151. // headers
  152. expect(screen.getByText('Transaction')).toBeInTheDocument();
  153. expect(screen.getByText('Count')).toBeInTheDocument();
  154. expect(screen.getByText('Total Self Time')).toBeInTheDocument();
  155. // first row
  156. const transaction1 = screen.getByText('transaction-1');
  157. expect(transaction1).toBeInTheDocument();
  158. expect(transaction1).toHaveAttribute(
  159. 'href',
  160. '/organizations/org-slug/profiling/profile/proj-slug/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/flamechart/?frameName=bar&framePackage=foo'
  161. );
  162. expect(screen.getByText('1k')).toBeInTheDocument();
  163. expect(screen.getByText('1.00ms')).toBeInTheDocument();
  164. // second row
  165. const transaction2 = screen.getByText('transaction-2');
  166. expect(transaction2).toBeInTheDocument();
  167. expect(transaction2).toHaveAttribute(
  168. 'href',
  169. '/organizations/org-slug/profiling/profile/proj-slug/cccccccccccccccccccccccccccccccc/flamechart/?frameName=bar&framePackage=foo'
  170. );
  171. expect(screen.getByText('2.5k')).toBeInTheDocument();
  172. expect(screen.getByText('0.50ms')).toBeInTheDocument();
  173. // toggle the second function
  174. const toggles = screen.getAllByRole('button', {});
  175. expect(toggles.length).toEqual(2);
  176. await userEvent.click(toggles[1]);
  177. // first row
  178. const transaction3 = await screen.findByText('transaction-3');
  179. expect(transaction3).toBeInTheDocument();
  180. expect(transaction3).toHaveAttribute(
  181. 'href',
  182. '/organizations/org-slug/profiling/profile/proj-slug/dddddddddddddddddddddddddddddddd/flamechart/?frameName=qux&framePackage=baz'
  183. );
  184. expect(screen.getByText('2k')).toBeInTheDocument();
  185. expect(screen.getByText('2.00ms')).toBeInTheDocument();
  186. // second row
  187. const transaction4 = screen.getByText('transaction-4');
  188. expect(transaction4).toBeInTheDocument();
  189. expect(transaction4).toHaveAttribute(
  190. 'href',
  191. '/organizations/org-slug/profiling/profile/proj-slug/ffffffffffffffffffffffffffffffff/flamechart/?frameName=qux&framePackage=baz'
  192. );
  193. expect(screen.getByText('3.5k')).toBeInTheDocument();
  194. expect(screen.getByText('0.70ms')).toBeInTheDocument();
  195. });
  196. });