pageOverview.spec.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
  3. import {useLocation} from 'sentry/utils/useLocation';
  4. import usePageFilters from 'sentry/utils/usePageFilters';
  5. import PageOverview from 'sentry/views/performance/browser/webVitals/pageOverview';
  6. jest.mock('sentry/utils/useLocation');
  7. jest.mock('sentry/utils/usePageFilters');
  8. describe('PageOverview', function () {
  9. const organization = OrganizationFixture({
  10. features: ['insights-initial-modules'],
  11. });
  12. let eventsMock;
  13. beforeEach(function () {
  14. jest.mocked(useLocation).mockReturnValue({
  15. pathname: '',
  16. search: '',
  17. query: {},
  18. hash: '',
  19. state: undefined,
  20. action: 'PUSH',
  21. key: '',
  22. });
  23. jest.mocked(usePageFilters).mockReturnValue({
  24. isReady: true,
  25. desyncedFilters: new Set(),
  26. pinnedFilters: new Set(),
  27. shouldPersist: true,
  28. selection: {
  29. datetime: {
  30. period: '10d',
  31. start: null,
  32. end: null,
  33. utc: false,
  34. },
  35. environments: [],
  36. projects: [],
  37. },
  38. });
  39. eventsMock = MockApiClient.addMockResponse({
  40. url: `/organizations/${organization.slug}/events/`,
  41. body: {
  42. data: [],
  43. },
  44. });
  45. MockApiClient.addMockResponse({
  46. url: `/organizations/${organization.slug}/events-stats/`,
  47. body: {},
  48. });
  49. MockApiClient.addMockResponse({
  50. url: `/organizations/${organization.slug}/spans-aggregation/`,
  51. body: {},
  52. });
  53. });
  54. afterEach(function () {
  55. jest.clearAllMocks();
  56. });
  57. it('renders', () => {
  58. render(<PageOverview />, {organization});
  59. // Raw web vital metric tile queries
  60. expect(eventsMock).toHaveBeenNthCalledWith(
  61. 1,
  62. expect.anything(),
  63. expect.objectContaining({
  64. query: expect.objectContaining({
  65. dataset: 'metrics',
  66. field: [
  67. 'p75(measurements.lcp)',
  68. 'p75(measurements.fcp)',
  69. 'p75(measurements.cls)',
  70. 'p75(measurements.ttfb)',
  71. 'p75(measurements.fid)',
  72. 'p75(measurements.inp)',
  73. 'p75(transaction.duration)',
  74. 'count_web_vitals(measurements.lcp, any)',
  75. 'count_web_vitals(measurements.fcp, any)',
  76. 'count_web_vitals(measurements.cls, any)',
  77. 'count_web_vitals(measurements.fid, any)',
  78. 'count_web_vitals(measurements.ttfb, any)',
  79. 'count()',
  80. ],
  81. query:
  82. 'transaction.op:[pageload,""] span.op:[ui.interaction.click,ui.interaction.hover,ui.interaction.drag,ui.interaction.press,""] !transaction:"<< unparameterized >>"',
  83. }),
  84. })
  85. );
  86. // Project performance score ring query
  87. expect(eventsMock).toHaveBeenNthCalledWith(
  88. 2,
  89. expect.anything(),
  90. expect.objectContaining({
  91. query: expect.objectContaining({
  92. dataset: 'metrics',
  93. field: [
  94. 'performance_score(measurements.score.lcp)',
  95. 'performance_score(measurements.score.fcp)',
  96. 'performance_score(measurements.score.cls)',
  97. `performance_score(measurements.score.inp)`,
  98. 'performance_score(measurements.score.ttfb)',
  99. 'avg(measurements.score.total)',
  100. 'avg(measurements.score.weight.lcp)',
  101. 'avg(measurements.score.weight.fcp)',
  102. 'avg(measurements.score.weight.cls)',
  103. `avg(measurements.score.weight.inp)`,
  104. 'avg(measurements.score.weight.ttfb)',
  105. 'count()',
  106. 'count_scores(measurements.score.total)',
  107. 'count_scores(measurements.score.lcp)',
  108. 'count_scores(measurements.score.fcp)',
  109. 'count_scores(measurements.score.cls)',
  110. 'count_scores(measurements.score.ttfb)',
  111. `count_scores(measurements.score.inp)`,
  112. ],
  113. query:
  114. 'transaction.op:[pageload,""] span.op:[ui.interaction.click,ui.interaction.hover,ui.interaction.drag,ui.interaction.press,""] !transaction:"<< unparameterized >>"',
  115. }),
  116. })
  117. );
  118. });
  119. it('renders FID deprecation alert', async () => {
  120. jest.mocked(useLocation).mockReturnValue({
  121. pathname: '',
  122. search: '',
  123. query: {useStoredScores: 'true', transaction: '/'},
  124. hash: '',
  125. state: undefined,
  126. action: 'PUSH',
  127. key: '',
  128. });
  129. render(<PageOverview />, {organization});
  130. await screen.findByText(/\(Interaction to Next Paint\) will replace/);
  131. await screen.findByText(
  132. /\(First Input Delay\) in our performance score calculation./
  133. );
  134. });
  135. it('renders interaction samples', async () => {
  136. const organizationWithInp = OrganizationFixture({
  137. features: ['insights-initial-modules'],
  138. });
  139. jest.mocked(useLocation).mockReturnValue({
  140. pathname: '',
  141. search: '',
  142. query: {useStoredScores: 'true', transaction: '/', type: 'interactions'},
  143. hash: '',
  144. state: undefined,
  145. action: 'PUSH',
  146. key: '',
  147. });
  148. render(<PageOverview />, {organization: organizationWithInp});
  149. await waitFor(() =>
  150. expect(eventsMock).toHaveBeenCalledWith(
  151. '/organizations/org-slug/events/',
  152. expect.objectContaining({
  153. query: expect.objectContaining({
  154. dataset: 'spansIndexed',
  155. field: [
  156. 'measurements.inp',
  157. 'measurements.score.inp',
  158. 'measurements.score.weight.inp',
  159. 'measurements.score.total',
  160. 'span_id',
  161. 'timestamp',
  162. 'profile_id',
  163. 'replay.id',
  164. 'user',
  165. 'origin.transaction',
  166. 'project',
  167. 'browser.name',
  168. 'span.self_time',
  169. 'span.description',
  170. ],
  171. query:
  172. 'has:message !span.description:<unknown> span.op:ui.interaction.click measurements.score.weight.inp:>0 origin.transaction:/',
  173. }),
  174. })
  175. )
  176. );
  177. });
  178. it('escapes transaction name before querying discover', async () => {
  179. const organizationWithInp = OrganizationFixture({
  180. features: ['insights-initial-modules'],
  181. });
  182. jest.mocked(useLocation).mockReturnValue({
  183. pathname: '',
  184. search: '',
  185. query: {
  186. useStoredScores: 'true',
  187. transaction: '/page-with-a-*/',
  188. type: 'interactions',
  189. },
  190. hash: '',
  191. state: undefined,
  192. action: 'PUSH',
  193. key: '',
  194. });
  195. render(<PageOverview />, {organization: organizationWithInp});
  196. await waitFor(() =>
  197. expect(eventsMock).toHaveBeenCalledWith(
  198. '/organizations/org-slug/events/',
  199. expect.objectContaining({
  200. query: expect.objectContaining({
  201. dataset: 'spansIndexed',
  202. field: [
  203. 'measurements.inp',
  204. 'measurements.score.inp',
  205. 'measurements.score.weight.inp',
  206. 'measurements.score.total',
  207. 'span_id',
  208. 'timestamp',
  209. 'profile_id',
  210. 'replay.id',
  211. 'user',
  212. 'origin.transaction',
  213. 'project',
  214. 'browser.name',
  215. 'span.self_time',
  216. 'span.description',
  217. ],
  218. query:
  219. 'has:message !span.description:<unknown> span.op:ui.interaction.click measurements.score.weight.inp:>0 origin.transaction:"/page-with-a-\\*/"',
  220. }),
  221. })
  222. )
  223. );
  224. });
  225. });