slowestFunctionsWidget.spec.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. 'sum()': 1000000,
  97. 'examples()': [
  98. 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  99. 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
  100. ],
  101. },
  102. {
  103. transaction: 'transaction-2',
  104. 'count()': 2500,
  105. 'sum()': 500000,
  106. 'examples()': ['cccccccccccccccccccccccccccccccc'],
  107. },
  108. ],
  109. },
  110. match: [
  111. MockApiClient.matchQuery({
  112. dataset: 'profileFunctions',
  113. query: 'project.id:1 fingerprint:123',
  114. field: ['transaction', 'count()', 'sum()', 'examples()'],
  115. }),
  116. ],
  117. });
  118. // second function examples
  119. MockApiClient.addMockResponse({
  120. url: '/organizations/org-slug/events/',
  121. body: {
  122. data: [
  123. {
  124. transaction: 'transaction-3',
  125. 'count()': 2000,
  126. 'sum()': 2000000,
  127. 'examples()': [
  128. 'dddddddddddddddddddddddddddddddd',
  129. 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
  130. ],
  131. },
  132. {
  133. transaction: 'transaction-4',
  134. 'count()': 3500,
  135. 'sum()': 700000,
  136. 'examples()': ['ffffffffffffffffffffffffffffffff'],
  137. },
  138. ],
  139. },
  140. match: [
  141. MockApiClient.matchQuery({
  142. dataset: 'profileFunctions',
  143. query: 'project.id:1 fingerprint:456',
  144. field: ['transaction', 'count()', 'sum()', 'examples()'],
  145. }),
  146. ],
  147. });
  148. render(<SlowestFunctionsWidget widgetHeight="100px" />);
  149. // starts by rendering loading
  150. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  151. // switches to the transactions list once the api responds with data
  152. expect(await screen.findByTestId('transactions-list')).toBeInTheDocument();
  153. // headers
  154. expect(screen.getByText('Transaction')).toBeInTheDocument();
  155. expect(screen.getByText('Count')).toBeInTheDocument();
  156. expect(screen.getByText('Total Self Time')).toBeInTheDocument();
  157. // first row
  158. const transaction1 = screen.getByText('transaction-1');
  159. expect(transaction1).toBeInTheDocument();
  160. expect(transaction1).toHaveAttribute(
  161. 'href',
  162. '/organizations/org-slug/profiling/profile/proj-slug/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/flamechart/?frameName=bar&framePackage=foo'
  163. );
  164. expect(screen.getByText('1k')).toBeInTheDocument();
  165. expect(screen.getByText('1.00ms')).toBeInTheDocument();
  166. // second row
  167. const transaction2 = screen.getByText('transaction-2');
  168. expect(transaction2).toBeInTheDocument();
  169. expect(transaction2).toHaveAttribute(
  170. 'href',
  171. '/organizations/org-slug/profiling/profile/proj-slug/cccccccccccccccccccccccccccccccc/flamechart/?frameName=bar&framePackage=foo'
  172. );
  173. expect(screen.getByText('2.5k')).toBeInTheDocument();
  174. expect(screen.getByText('0.50ms')).toBeInTheDocument();
  175. // toggle the second function
  176. const toggles = screen.getAllByRole('button', {});
  177. expect(toggles.length).toEqual(2);
  178. await userEvent.click(toggles[1]);
  179. // first row
  180. const transaction3 = await screen.findByText('transaction-3');
  181. expect(transaction3).toBeInTheDocument();
  182. expect(transaction3).toHaveAttribute(
  183. 'href',
  184. '/organizations/org-slug/profiling/profile/proj-slug/dddddddddddddddddddddddddddddddd/flamechart/?frameName=qux&framePackage=baz'
  185. );
  186. expect(screen.getByText('2k')).toBeInTheDocument();
  187. expect(screen.getByText('2.00ms')).toBeInTheDocument();
  188. // second row
  189. const transaction4 = screen.getByText('transaction-4');
  190. expect(transaction4).toBeInTheDocument();
  191. expect(transaction4).toHaveAttribute(
  192. 'href',
  193. '/organizations/org-slug/profiling/profile/proj-slug/ffffffffffffffffffffffffffffffff/flamechart/?frameName=qux&framePackage=baz'
  194. );
  195. expect(screen.getByText('3.5k')).toBeInTheDocument();
  196. expect(screen.getByText('0.70ms')).toBeInTheDocument();
  197. });
  198. });