webVitalsDetailPanel.spec.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
  3. import {useLocation} from 'sentry/utils/useLocation';
  4. import usePageFilters from 'sentry/utils/usePageFilters';
  5. import {WebVitalsDetailPanel} from 'sentry/views/insights/browser/webVitals/components/webVitalsDetailPanel';
  6. jest.mock('sentry/utils/useLocation');
  7. jest.mock('sentry/utils/usePageFilters');
  8. describe('WebVitalsDetailPanel', function () {
  9. const organization = OrganizationFixture();
  10. let eventsMock, eventsStatsMock;
  11. beforeEach(function () {
  12. jest.mocked(useLocation).mockReturnValue({
  13. pathname: '',
  14. search: '',
  15. query: {},
  16. hash: '',
  17. state: undefined,
  18. action: 'PUSH',
  19. key: '',
  20. });
  21. jest.mocked(usePageFilters).mockReturnValue({
  22. isReady: true,
  23. desyncedFilters: new Set(),
  24. pinnedFilters: new Set(),
  25. shouldPersist: true,
  26. selection: {
  27. datetime: {
  28. period: '10d',
  29. start: null,
  30. end: null,
  31. utc: false,
  32. },
  33. environments: [],
  34. projects: [],
  35. },
  36. });
  37. eventsMock = MockApiClient.addMockResponse({
  38. url: `/organizations/${organization.slug}/events/`,
  39. body: {
  40. data: [],
  41. },
  42. });
  43. eventsStatsMock = MockApiClient.addMockResponse({
  44. url: `/organizations/${organization.slug}/events-stats/`,
  45. body: {},
  46. });
  47. });
  48. afterEach(function () {
  49. jest.resetAllMocks();
  50. });
  51. it('renders correctly with empty results', async () => {
  52. render(<WebVitalsDetailPanel onClose={() => undefined} webVital="lcp" />, {
  53. organization,
  54. });
  55. await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
  56. // Raw web vital metric tile queries
  57. expect(eventsMock).toHaveBeenNthCalledWith(
  58. 1,
  59. expect.anything(),
  60. expect.objectContaining({
  61. query: expect.objectContaining({
  62. dataset: 'metrics',
  63. field: [
  64. 'p75(measurements.lcp)',
  65. 'p75(measurements.fcp)',
  66. 'p75(measurements.cls)',
  67. 'p75(measurements.ttfb)',
  68. 'p75(measurements.inp)',
  69. 'p75(transaction.duration)',
  70. 'count_web_vitals(measurements.lcp, any)',
  71. 'count_web_vitals(measurements.fcp, any)',
  72. 'count_web_vitals(measurements.cls, any)',
  73. 'count_web_vitals(measurements.ttfb, any)',
  74. 'count_web_vitals(measurements.inp, any)',
  75. 'count()',
  76. ],
  77. query:
  78. 'transaction.op:[pageload,""] span.op:[ui.interaction.click,ui.interaction.hover,ui.interaction.drag,ui.interaction.press,ui.webvital.cls,""] !transaction:"<< unparameterized >>"',
  79. }),
  80. })
  81. );
  82. // Project performance score ring query
  83. expect(eventsMock).toHaveBeenNthCalledWith(
  84. 2,
  85. expect.anything(),
  86. expect.objectContaining({
  87. query: expect.objectContaining({
  88. dataset: 'metrics',
  89. field: [
  90. 'performance_score(measurements.score.lcp)',
  91. 'performance_score(measurements.score.fcp)',
  92. 'performance_score(measurements.score.cls)',
  93. `performance_score(measurements.score.inp)`,
  94. 'performance_score(measurements.score.ttfb)',
  95. 'performance_score(measurements.score.total)',
  96. 'avg(measurements.score.weight.lcp)',
  97. 'avg(measurements.score.weight.fcp)',
  98. 'avg(measurements.score.weight.cls)',
  99. `avg(measurements.score.weight.inp)`,
  100. 'avg(measurements.score.weight.ttfb)',
  101. 'count()',
  102. 'count_scores(measurements.score.total)',
  103. 'count_scores(measurements.score.lcp)',
  104. 'count_scores(measurements.score.fcp)',
  105. 'count_scores(measurements.score.cls)',
  106. 'count_scores(measurements.score.ttfb)',
  107. 'count_scores(measurements.score.inp)',
  108. 'sum(measurements.score.weight.lcp)',
  109. ],
  110. query:
  111. 'transaction.op:[pageload,""] span.op:[ui.interaction.click,ui.interaction.hover,ui.interaction.drag,ui.interaction.press,ui.webvital.cls,""] !transaction:"<< unparameterized >>"',
  112. }),
  113. })
  114. );
  115. // Table query
  116. expect(eventsMock).toHaveBeenNthCalledWith(
  117. 3,
  118. expect.anything(),
  119. expect.objectContaining({
  120. query: expect.objectContaining({
  121. dataset: 'metrics',
  122. field: [
  123. 'project.id',
  124. 'project',
  125. 'transaction',
  126. 'p75(measurements.lcp)',
  127. 'p75(measurements.fcp)',
  128. 'p75(measurements.cls)',
  129. 'p75(measurements.ttfb)',
  130. 'p75(measurements.inp)',
  131. 'performance_score(measurements.score.lcp)',
  132. 'opportunity_score(measurements.score.lcp)',
  133. 'performance_score(measurements.score.total)',
  134. 'count()',
  135. 'count_scores(measurements.score.lcp)',
  136. 'count_scores(measurements.score.fcp)',
  137. 'count_scores(measurements.score.cls)',
  138. 'count_scores(measurements.score.inp)',
  139. 'count_scores(measurements.score.ttfb)',
  140. 'total_opportunity_score()',
  141. ],
  142. query:
  143. 'transaction.op:[pageload,""] span.op:[ui.interaction.click,ui.interaction.hover,ui.interaction.drag,ui.interaction.press,ui.webvital.cls,""] !transaction:"<< unparameterized >>" avg(measurements.score.total):>=0 count_scores(measurements.score.lcp):>0',
  144. }),
  145. })
  146. );
  147. expect(eventsStatsMock).toHaveBeenCalledTimes(1);
  148. expect(screen.getByText('Largest Contentful Paint (P75)')).toBeInTheDocument();
  149. expect(screen.getByText('—')).toBeInTheDocument();
  150. expect(
  151. screen.getByText(/Largest Contentful Paint \(LCP\) measures the render/)
  152. ).toBeInTheDocument();
  153. });
  154. });