slowestFunctionsWidget.spec.tsx 6.9 KB

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