Browse Source

test(ui): Convert perf landing to RTL (#36770)

* test(ui): Convert perf landing to RTL

This converts the performance landing page tests to RTL, and fixes a couple issues uncovered while converting the tests. Also adds a few key test ids to common points (grideditable), and fixes a bug with the initializeData setup with RTL.
Kev 2 years ago
parent
commit
fb96ac457b

+ 10 - 4
static/app/components/gridEditable/index.tsx

@@ -310,7 +310,7 @@ class GridEditable<
       ? grid.renderPrependColumns(true)
       : [];
     return (
-      <GridRow>
+      <GridRow data-test-id="grid-head-row">
         {prependColumns &&
           prependColumns.map((item, i) => (
             <GridHeadCellStatic key={`prepend-${i}`}>{item}</GridHeadCellStatic>
@@ -319,7 +319,11 @@ class GridEditable<
           /* Note that this.onResizeMouseDown assumes GridResizer is nested
             1 levels under GridHeadCell */
           columnOrder.map((column, i) => (
-            <GridHeadCell key={`${i}.${column.key}`} isFirst={i === 0}>
+            <GridHeadCell
+              data-test-id="grid-head-cell"
+              key={`${i}.${column.key}`}
+              isFirst={i === 0}
+            >
               {grid.renderHeadCell ? grid.renderHeadCell(column, i) : column.name}
               {i !== numColumn - 1 && (
                 <GridResizer
@@ -364,10 +368,12 @@ class GridEditable<
       <GridRow key={row}>
         {prependColumns &&
           prependColumns.map((item, i) => (
-            <GridBodyCell key={`prepend-${i}`}>{item}</GridBodyCell>
+            <GridBodyCell data-test-id="grid-body-cell" key={`prepend-${i}`}>
+              {item}
+            </GridBodyCell>
           ))}
         {columnOrder.map((col, i) => (
-          <GridBodyCell key={`${col.key}${i}`}>
+          <GridBodyCell data-test-id="grid-body-cell" key={`${col.key}${i}`}>
             {grid.renderBodyCell
               ? grid.renderBodyCell(col, dataRow, row, i)
               : dataRow[col.key]}

+ 1 - 1
static/app/views/performance/table.tsx

@@ -435,7 +435,7 @@ class _Table extends Component<Props, State> {
     const prependColumnWidths = ['max-content'];
 
     return (
-      <div>
+      <div data-test-id="performance-table">
         <MEPConsumer>
           {value => {
             return (

+ 4 - 3
tests/js/sentry-test/performance/initializePerformanceData.ts

@@ -14,6 +14,7 @@ export interface initializeDataSettings {
   project?: any; // TODO(k-fish): Fix this project type.
   projects?: Project[];
   query?: {};
+  selectedProject?: number | string;
 }
 
 export function initializeData(settings?: initializeDataSettings) {
@@ -25,7 +26,7 @@ export function initializeData(settings?: initializeDataSettings) {
     project: _defaultProject,
     ...settings,
   };
-  const {query, features, projects, project} = _settings;
+  const {query, features, projects, selectedProject: project} = _settings;
 
   const organization = TestStubs.Organization({
     features,
@@ -36,8 +37,8 @@ export function initializeData(settings?: initializeDataSettings) {
       ...query,
     },
   };
-  if (settings?.project) {
-    routerLocation.query.project = project;
+  if (settings?.selectedProject || settings?.project) {
+    routerLocation.query.project = (project || settings?.project) as any;
   }
   const router = {
     location: routerLocation,

+ 87 - 109
tests/js/spec/views/performance/landing/index.spec.tsx

@@ -1,22 +1,22 @@
 import {browserHistory} from 'react-router';
 
-import {mountWithTheme} from 'sentry-test/enzyme';
 import {initializeData} from 'sentry-test/performance/initializePerformanceData';
-import {act} from 'sentry-test/reactTestingLibrary';
+import {act, render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
 
 import TeamStore from 'sentry/stores/teamStore';
-import EventView from 'sentry/utils/discover/eventView';
 import {MetricsCardinalityProvider} from 'sentry/utils/performance/contexts/metricsCardinality';
 import {OrganizationContext} from 'sentry/views/organizationContext';
+import {generatePerformanceEventView} from 'sentry/views/performance/data';
 import {PerformanceLanding} from 'sentry/views/performance/landing';
 import {REACT_NATIVE_COLUMN_TITLES} from 'sentry/views/performance/landing/data';
-import * as utils from 'sentry/views/performance/landing/utils';
 import {LandingDisplayField} from 'sentry/views/performance/landing/utils';
 
 import {addMetricsDataMock} from './metricsDataSwitcher.spec';
 
 const WrappedComponent = ({data, withStaticFilters = false}) => {
-  const eventView = EventView.fromLocation(data.router.location);
+  const eventView = generatePerformanceEventView(data.router.location, data.projects, {
+    withStaticFilters,
+  });
 
   return (
     <OrganizationContext.Provider value={data.organization}>
@@ -61,6 +61,10 @@ describe('Performance > Landing > Index', function () {
       url: '/prompts-activity/',
       body: {},
     });
+    MockApiClient.addMockResponse({
+      url: '/organizations/org-slug/projects/',
+      body: [],
+    });
     MockApiClient.addMockResponse({
       method: 'GET',
       url: `/organizations/org-slug/key-transactions-list/`,
@@ -81,10 +85,24 @@ describe('Performance > Landing > Index', function () {
       url: `/organizations/org-slug/events-trends-stats/`,
       body: [],
     });
+    MockApiClient.addMockResponse({
+      method: 'GET',
+      url: `/organizations/org-slug/events-histogram/`,
+      body: [],
+    });
     eventsV2Mock = MockApiClient.addMockResponse({
       method: 'GET',
       url: `/organizations/org-slug/eventsv2/`,
-      body: [],
+      body: {
+        meta: {
+          id: 'string',
+        },
+        data: [
+          {
+            id: '1234',
+          },
+        ],
+      },
     });
     MockApiClient.addMockResponse({
       method: 'GET',
@@ -109,92 +127,80 @@ describe('Performance > Landing > Index', function () {
     }
   });
 
-  it('renders basic UI elements', async function () {
+  it('renders basic UI elements', function () {
     const data = initializeData();
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
 
-    expect(wrapper.find('div[data-test-id="performance-landing-v3"]').exists()).toBe(
-      true
-    );
+    expect(screen.getByTestId('performance-landing-v3')).toBeInTheDocument();
   });
 
-  it('renders frontend pageload view', async function () {
+  it('renders frontend pageload view', function () {
     const data = initializeData({
       query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD},
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
-
-    expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
-      true
-    );
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
 
-    expect(wrapper.find('Table')).toHaveLength(1);
+    expect(screen.getByTestId('frontend-pageload-view')).toBeInTheDocument();
+    expect(screen.getByTestId('performance-table')).toBeInTheDocument();
 
-    const titles = wrapper.find('div[data-test-id="performance-widget-title"]');
+    const titles = screen.getAllByTestId('performance-widget-title');
     expect(titles).toHaveLength(5);
 
-    expect(titles.at(0).text()).toEqual('p75 LCP');
-    expect(titles.at(1).text()).toEqual('LCP Distribution');
-    expect(titles.at(2).text()).toEqual('FCP Distribution');
-    expect(titles.at(3).text()).toEqual('Worst LCP Web Vitals');
-    expect(titles.at(4).text()).toEqual('Worst FCP Web Vitals');
+    expect(titles[0]).toHaveTextContent('p75 LCP');
+    expect(titles[1]).toHaveTextContent('LCP Distribution');
+    expect(titles[2]).toHaveTextContent('FCP Distribution');
+    expect(titles[3]).toHaveTextContent('Worst LCP Web Vitals');
+    expect(titles[4]).toHaveTextContent('Worst FCP Web Vitals');
   });
 
-  it('renders frontend other view', async function () {
+  it('renders frontend other view', function () {
     const data = initializeData({
       query: {landingDisplay: LandingDisplayField.FRONTEND_OTHER},
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
-
-    expect(wrapper.find('Table').exists()).toBe(true);
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
+    expect(screen.getByTestId('performance-table')).toBeInTheDocument();
   });
 
-  it('renders backend view', async function () {
+  it('renders backend view', function () {
     const data = initializeData({
       query: {landingDisplay: LandingDisplayField.BACKEND},
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
-
-    expect(wrapper.find('Table').exists()).toBe(true);
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
+    expect(screen.getByTestId('performance-table')).toBeInTheDocument();
   });
 
-  it('renders mobile view', async function () {
+  it('renders mobile view', function () {
     const data = initializeData({
       query: {landingDisplay: LandingDisplayField.MOBILE},
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
-
-    expect(wrapper.find('Table').exists()).toBe(true);
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
+    expect(screen.getByTestId('performance-table')).toBeInTheDocument();
   });
 
   it('renders react-native table headers in mobile view', async function () {
-    jest.spyOn(utils, 'checkIsReactNative').mockReturnValueOnce(true);
+    const project = TestStubs.Project({platform: 'react-native'});
+    const projects = [project];
     const data = initializeData({
       query: {landingDisplay: LandingDisplayField.MOBILE},
+      selectedProject: project.id,
+      projects,
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
 
-    const table = wrapper.find('Table');
-    expect(table.exists()).toBe(true);
-    expect(table.props().columnTitles).toEqual(REACT_NATIVE_COLUMN_TITLES);
+    expect(await screen.findByTestId('performance-table')).toBeInTheDocument();
+    expect(screen.getByTestId('grid-editable')).toBeInTheDocument();
+    const columnHeaders = await screen.findAllByTestId('grid-head-cell');
+
+    expect(columnHeaders).toHaveLength(REACT_NATIVE_COLUMN_TITLES.length);
+    for (const [index, title] of columnHeaders.entries()) {
+      expect(title).toHaveTextContent(REACT_NATIVE_COLUMN_TITLES[index]);
+    }
   });
 
   it('renders all transactions view', async function () {
@@ -202,11 +208,9 @@ describe('Performance > Landing > Index', function () {
       query: {landingDisplay: LandingDisplayField.ALL},
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
 
-    expect(wrapper.find('Table').exists()).toBe(true);
+    expect(await screen.findByTestId('performance-table')).toBeInTheDocument();
 
     expect(eventStatsMock).toHaveBeenCalledTimes(1); // Only one request is made since the query batcher is working.
 
@@ -216,45 +220,37 @@ describe('Performance > Landing > Index', function () {
       expect.objectContaining({
         query: expect.objectContaining({
           environment: [],
-          interval: '1h',
+          interval: '15m',
           partial: '1',
           project: [],
-          query: '',
+          query: 'transaction.duration:<15m event.type:transaction',
           referrer: 'api.performance.generic-widget-chart.user-misery-area',
-          statsPeriod: '28d',
+          statsPeriod: '48h',
           yAxis: ['user_misery()', 'tpm()', 'failure_rate()'],
         }),
       })
     );
 
-    expect(eventsV2Mock).toHaveBeenCalledTimes(1);
+    expect(eventsV2Mock).toHaveBeenCalledTimes(2);
 
-    const titles = wrapper.find('div[data-test-id="performance-widget-title"]');
+    const titles = await screen.findAllByTestId('performance-widget-title');
     expect(titles).toHaveLength(5);
 
-    expect(titles.at(0).text()).toEqual('User Misery');
-    expect(titles.at(1).text()).toEqual('Transactions Per Minute');
-    expect(titles.at(2).text()).toEqual('Failure Rate');
-    expect(titles.at(3).text()).toEqual('Most Related Issues');
-    expect(titles.at(4).text()).toEqual('Most Improved');
+    expect(titles.at(0)).toHaveTextContent('User Misery');
+    expect(titles.at(1)).toHaveTextContent('Transactions Per Minute');
+    expect(titles.at(2)).toHaveTextContent('Failure Rate');
+    expect(titles.at(3)).toHaveTextContent('Most Related Issues');
+    expect(titles.at(4)).toHaveTextContent('Most Improved');
   });
 
-  it('Can switch between landing displays', async function () {
+  it('Can switch between landing displays', function () {
     const data = initializeData({
       query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD, abc: '123'},
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
-
-    expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
-      true
-    );
-
-    wrapper.find('a[data-test-id="landing-tab-all"]').simulate('click');
-    await tick();
-    wrapper.update();
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
+    expect(screen.getByTestId('frontend-pageload-view')).toBeInTheDocument();
+    userEvent.click(screen.getByTestId('landing-tab-all'));
 
     expect(browserHistory.push).toHaveBeenNthCalledWith(
       1,
@@ -265,46 +261,33 @@ describe('Performance > Landing > Index', function () {
     );
   });
 
-  it('Updating projects switches performance view', async function () {
+  it('Updating projects switches performance view', function () {
     const data = initializeData({
       query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD},
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
 
-    expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
-      true
-    );
+    expect(screen.getByTestId('frontend-pageload-view')).toBeInTheDocument();
 
     const updatedData = initializeData({
       projects: [TestStubs.Project({id: 123, platform: 'unknown'})],
-      project: 123 as any,
+      selectedProject: 123,
     });
 
-    wrapper.setProps({
-      data: updatedData,
-    } as any);
-    await tick();
-    wrapper.update();
+    wrapper.rerender(<WrappedComponent data={updatedData} />, data.routerContext);
 
-    expect(wrapper.find('div[data-test-id="all-transactions-view"]').exists()).toBe(true);
+    expect(screen.getByTestId('all-transactions-view')).toBeInTheDocument();
   });
 
-  it('View correctly defaults based on project without url param', async function () {
+  it('View correctly defaults based on project without url param', function () {
     const data = initializeData({
       projects: [TestStubs.Project({id: 99, platform: 'javascript-react'})],
-      project: 99 as any,
+      selectedProject: 99,
     });
 
-    wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
-    await tick();
-    wrapper.update();
-
-    expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
-      true
-    );
+    wrapper = render(<WrappedComponent data={data} />, data.routerContext);
+    expect(screen.getByTestId('frontend-pageload-view')).toBeInTheDocument();
   });
 
   describe('with transaction search feature', function () {
@@ -318,17 +301,12 @@ describe('Performance > Landing > Index', function () {
         },
       });
 
-      wrapper = mountWithTheme(
+      wrapper = render(
         <WrappedComponent data={data} withStaticFilters />,
         data.routerContext
       );
 
-      await tick();
-      wrapper.update();
-
-      expect(wrapper.find('div[data-test-id="transaction-search-bar"]').exists()).toBe(
-        true
-      );
+      expect(await screen.findByTestId('transaction-search-bar')).toBeInTheDocument();
     });
   });
 });

+ 1 - 1
tests/js/spec/views/performance/table.spec.tsx

@@ -19,7 +19,7 @@ const initializeData = (settings = {}, features: string[] = []) => {
   return _initializeData({
     features: [...FEATURES, ...features],
     projects,
-    project: projects[0],
+    selectedProject: projects[0],
     ...settings,
   });
 };

+ 4 - 4
tests/js/spec/views/performance/trends/index.spec.tsx

@@ -114,14 +114,14 @@ function _initializeData(
       throw new Error("Test is selecting project that isn't loaded");
     } else {
       PageFiltersStore.updateProjects(
-        settings.project ? [Number(selectedProject)] : [],
+        settings.selectedProject ? [Number(selectedProject)] : [],
         []
       );
     }
-    newSettings.project = selectedProject;
+    newSettings.selectedProject = selectedProject.id;
   }
 
-  newSettings.project = settings.project ?? newSettings.projects[0];
+  newSettings.selectedProject = settings.selectedProject ?? newSettings.projects[0].id;
   const data = initializeData(newSettings);
 
   // Modify page filters store to stop rerendering due to the test harness.
@@ -136,7 +136,7 @@ function _initializeData(
   PageFiltersStore.updateDateTime(defaultTrendsSelectionDate);
   if (!options?.selectedProjectId) {
     PageFiltersStore.updateProjects(
-      settings.project ? [Number(newSettings.projects[0].id)] : [],
+      settings.selectedProject ? [Number(newSettings.projects[0].id)] : [],
       []
     );
   }