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 TeamStore from 'sentry/stores/teamStore';
import EventView from 'sentry/utils/discover/eventView';
import {OrganizationContext} from 'sentry/views/organizationContext';
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);
return (
{}}
handleTrendsClick={() => {}}
setError={() => {}}
withStaticFilters={withStaticFilters}
/>
);
};
describe('Performance > Landing > Index', function () {
let eventStatsMock: any;
let eventsV2Mock: any;
let wrapper: any;
act(() => void TeamStore.loadInitialData([], false, null));
beforeEach(function () {
// @ts-ignore no-console
// eslint-disable-next-line no-console
jest.spyOn(console, 'error').mockImplementation(jest.fn());
MockApiClient.addMockResponse({
url: '/organizations/org-slug/sdk-updates/',
body: [],
});
MockApiClient.addMockResponse({
url: '/prompts-activity/',
body: {},
});
MockApiClient.addMockResponse({
method: 'GET',
url: `/organizations/org-slug/key-transactions-list/`,
body: [],
});
MockApiClient.addMockResponse({
method: 'GET',
url: `/organizations/org-slug/legacy-key-transactions-count/`,
body: [],
});
eventStatsMock = MockApiClient.addMockResponse({
method: 'GET',
url: `/organizations/org-slug/events-stats/`,
body: [],
});
MockApiClient.addMockResponse({
method: 'GET',
url: `/organizations/org-slug/events-trends-stats/`,
body: [],
});
eventsV2Mock = MockApiClient.addMockResponse({
method: 'GET',
url: `/organizations/org-slug/eventsv2/`,
body: [],
});
MockApiClient.addMockResponse({
method: 'GET',
url: `/organizations/org-slug/events/`,
body: {
data: [{}],
meta: {},
},
});
});
afterEach(function () {
MockApiClient.clearMockResponses();
// @ts-ignore no-console
// eslint-disable-next-line no-console
console.error.mockRestore();
if (wrapper) {
wrapper.unmount();
wrapper = undefined;
}
});
it('renders basic UI elements', async function () {
const data = initializeData();
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('div[data-test-id="performance-landing-v3"]').exists()).toBe(
true
);
});
it('renders frontend pageload view', async function () {
const data = initializeData({
query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD},
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
true
);
expect(wrapper.find('Table')).toHaveLength(1);
const titles = wrapper.find('div[data-test-id="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');
});
it('renders frontend other view', async function () {
const data = initializeData({
query: {landingDisplay: LandingDisplayField.FRONTEND_OTHER},
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('Table').exists()).toBe(true);
});
it('renders backend view', async function () {
const data = initializeData({
query: {landingDisplay: LandingDisplayField.BACKEND},
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('Table').exists()).toBe(true);
});
it('renders mobile view', async function () {
const data = initializeData({
query: {landingDisplay: LandingDisplayField.MOBILE},
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('Table').exists()).toBe(true);
});
it('renders react-native table headers in mobile view', async function () {
jest.spyOn(utils, 'checkIsReactNative').mockReturnValueOnce(true);
const data = initializeData({
query: {landingDisplay: LandingDisplayField.MOBILE},
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
const table = wrapper.find('Table');
expect(table.exists()).toBe(true);
expect(table.props().columnTitles).toEqual(REACT_NATIVE_COLUMN_TITLES);
});
it('renders all transactions view', async function () {
const data = initializeData({
query: {landingDisplay: LandingDisplayField.ALL},
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('Table').exists()).toBe(true);
expect(eventStatsMock).toHaveBeenCalledTimes(1); // Only one request is made since the query batcher is working.
expect(eventStatsMock).toHaveBeenNthCalledWith(
1,
expect.anything(),
expect.objectContaining({
query: expect.objectContaining({
environment: [],
interval: '1h',
partial: '1',
project: [],
query: '',
referrer: 'api.performance.generic-widget-chart.user-misery-area',
statsPeriod: '28d',
yAxis: ['user_misery()', 'tpm()', 'failure_rate()'],
}),
})
);
expect(eventsV2Mock).toHaveBeenCalledTimes(1);
const titles = wrapper.find('div[data-test-id="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');
});
it('Can switch between landing displays', async function () {
const data = initializeData({
query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD, abc: '123'},
});
wrapper = mountWithTheme(, 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();
expect(browserHistory.push).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
pathname: data.location.pathname,
query: {query: '', abc: '123'},
})
);
});
it('Updating projects switches performance view', async function () {
const data = initializeData({
query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD},
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
true
);
const updatedData = initializeData({
projects: [TestStubs.Project({id: 123, platform: 'unknown'})],
project: 123 as any,
});
wrapper.setProps({
data: updatedData,
} as any);
await tick();
wrapper.update();
expect(wrapper.find('div[data-test-id="all-transactions-view"]').exists()).toBe(true);
});
it('View correctly defaults based on project without url param', async function () {
const data = initializeData({
projects: [TestStubs.Project({id: 99, platform: 'javascript-react'})],
project: 99 as any,
});
wrapper = mountWithTheme(, data.routerContext);
await tick();
wrapper.update();
expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
true
);
});
describe('with transaction search feature', function () {
it('renders the search bar', async function () {
addMetricsDataMock();
const data = initializeData({
features: ['performance-transaction-name-only-search'],
query: {
field: 'test',
},
});
wrapper = mountWithTheme(
,
data.routerContext
);
await tick();
wrapper.update();
expect(wrapper.find('div[data-test-id="transaction-search-bar"]').exists()).toBe(
true
);
});
});
});