slowestFunctionsWidget.spec.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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 widgetHeight="100px" />);
  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', 'fingerprint', 'package', 'function', 'count()', 'sum()'],
  38. }),
  39. ],
  40. });
  41. render(<SlowestFunctionsWidget widgetHeight="100px" />);
  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. fingerprint: 123,
  56. package: 'foo',
  57. function: 'bar',
  58. 'sum()': 150,
  59. },
  60. {
  61. 'project.id': 1,
  62. fingerprint: 456,
  63. package: 'baz',
  64. function: 'qux',
  65. 'sum()': 100,
  66. },
  67. ],
  68. },
  69. match: [
  70. MockApiClient.matchQuery({
  71. dataset: 'profileFunctions',
  72. field: ['project.id', 'fingerprint', 'package', 'function', 'count()', 'sum()'],
  73. }),
  74. ],
  75. });
  76. // for the totals query
  77. MockApiClient.addMockResponse({
  78. url: '/organizations/org-slug/events/',
  79. body: {data: [{'project.id': 1, 'sum()': 2500000}]},
  80. match: [
  81. MockApiClient.matchQuery({
  82. dataset: 'profileFunctions',
  83. field: ['project.id', 'sum()'],
  84. project: [1],
  85. }),
  86. ],
  87. });
  88. // first function examples
  89. MockApiClient.addMockResponse({
  90. url: '/organizations/org-slug/events/',
  91. body: {
  92. data: [
  93. {
  94. transaction: 'transaction-1',
  95. 'count()': 1000,
  96. 'p75()': 100000,
  97. 'sum()': 1000000,
  98. 'examples()': [
  99. 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  100. 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
  101. ],
  102. },
  103. {
  104. transaction: 'transaction-2',
  105. 'count()': 2500,
  106. 'p75()': 50000,
  107. 'sum()': 500000,
  108. 'examples()': ['cccccccccccccccccccccccccccccccc'],
  109. },
  110. ],
  111. },
  112. match: [
  113. MockApiClient.matchQuery({
  114. dataset: 'profileFunctions',
  115. query: 'project.id:1 fingerprint:123',
  116. field: ['transaction', 'count()', 'p75()', 'sum()', 'examples()'],
  117. }),
  118. ],
  119. });
  120. // second function examples
  121. MockApiClient.addMockResponse({
  122. url: '/organizations/org-slug/events/',
  123. body: {
  124. data: [
  125. {
  126. transaction: 'transaction-3',
  127. 'count()': 2000,
  128. 'p75()': 200000,
  129. 'sum()': 2000000,
  130. 'examples()': [
  131. 'dddddddddddddddddddddddddddddddd',
  132. 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
  133. ],
  134. },
  135. {
  136. transaction: 'transaction-4',
  137. 'count()': 3500,
  138. 'p75()': 70000,
  139. 'sum()': 700000,
  140. 'examples()': ['ffffffffffffffffffffffffffffffff'],
  141. },
  142. ],
  143. },
  144. match: [
  145. MockApiClient.matchQuery({
  146. dataset: 'profileFunctions',
  147. query: 'project.id:1 fingerprint:456',
  148. field: ['transaction', 'count()', 'p75()', 'sum()', 'examples()'],
  149. }),
  150. ],
  151. });
  152. render(<SlowestFunctionsWidget widgetHeight="100px" />);
  153. // starts by rendering loading
  154. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  155. // switches to the transactions list once the api responds with data
  156. expect(await screen.findByTestId('transactions-list')).toBeInTheDocument();
  157. // headers
  158. expect(screen.getByText('Transaction')).toBeInTheDocument();
  159. expect(screen.getByText('Count')).toBeInTheDocument();
  160. expect(screen.getByText('Time Spent')).toBeInTheDocument();
  161. // first row
  162. const transaction1 = screen.getByText('transaction-1');
  163. expect(transaction1).toBeInTheDocument();
  164. expect(transaction1).toHaveAttribute(
  165. 'href',
  166. '/organizations/org-slug/profiling/profile/proj-slug/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/flamegraph/?frameName=bar&framePackage=foo'
  167. );
  168. expect(screen.getByText('1k')).toBeInTheDocument();
  169. expect(screen.getByText('1.00ms')).toBeInTheDocument();
  170. // second row
  171. const transaction2 = screen.getByText('transaction-2');
  172. expect(transaction2).toBeInTheDocument();
  173. expect(transaction2).toHaveAttribute(
  174. 'href',
  175. '/organizations/org-slug/profiling/profile/proj-slug/cccccccccccccccccccccccccccccccc/flamegraph/?frameName=bar&framePackage=foo'
  176. );
  177. expect(screen.getByText('2.5k')).toBeInTheDocument();
  178. expect(screen.getByText('0.50ms')).toBeInTheDocument();
  179. // toggle the second function
  180. const toggles = screen.getAllByRole('button', {});
  181. expect(toggles.length).toEqual(2);
  182. await userEvent.click(toggles[1]);
  183. // first row
  184. const transaction3 = await screen.findByText('transaction-3');
  185. expect(transaction3).toBeInTheDocument();
  186. expect(transaction3).toHaveAttribute(
  187. 'href',
  188. '/organizations/org-slug/profiling/profile/proj-slug/dddddddddddddddddddddddddddddddd/flamegraph/?frameName=qux&framePackage=baz'
  189. );
  190. expect(screen.getByText('2k')).toBeInTheDocument();
  191. expect(screen.getByText('2.00ms')).toBeInTheDocument();
  192. // second row
  193. const transaction4 = screen.getByText('transaction-4');
  194. expect(transaction4).toBeInTheDocument();
  195. expect(transaction4).toHaveAttribute(
  196. 'href',
  197. '/organizations/org-slug/profiling/profile/proj-slug/ffffffffffffffffffffffffffffffff/flamegraph/?frameName=qux&framePackage=baz'
  198. );
  199. expect(screen.getByText('3.5k')).toBeInTheDocument();
  200. expect(screen.getByText('0.70ms')).toBeInTheDocument();
  201. });
  202. });