import {browserHistory} from 'react-router';
import {mountWithTheme} from 'sentry-test/enzyme';
import {initializeData as _initializeData} from 'sentry-test/performance/initializePerformanceData';
import EventView from 'sentry/utils/discover/eventView';
import {MEPSettingProvider} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import {OrganizationContext} from 'sentry/views/organizationContext';
import Table from 'sentry/views/performance/table';
const FEATURES = ['performance-view'];
const initializeData = (settings = {}, features: string[] = []) => {
const projects = [
TestStubs.Project({id: '1', slug: '1'}),
TestStubs.Project({id: '2', slug: '2'}),
];
return _initializeData({
features: [...FEATURES, ...features],
projects,
project: projects[0],
...settings,
});
};
const WrappedComponent = ({data, ...rest}) => {
return (
);
};
function openContextMenu(wrapper, cellIndex) {
const menu = wrapper.find('CellAction').at(cellIndex);
// Hover over the menu
menu.find('Container > div').at(0).simulate('mouseEnter');
wrapper.update();
// Open the menu
wrapper.find('MenuButton').simulate('click');
// Return the menu wrapper so we can interact with it.
return wrapper.find('CellAction').at(cellIndex).find('Menu');
}
function mockEventView(data) {
const eventView = new EventView({
id: '1',
name: 'my query',
fields: [
{
field: 'team_key_transaction',
},
{
field: 'transaction',
},
{
field: 'project',
},
{
field: 'tpm()',
},
{
field: 'p50()',
},
{
field: 'p95()',
},
{
field: 'failure_rate()',
},
{
field: 'apdex()',
},
{
field: 'count_unique(user)',
},
{
field: 'count_miserable(user)',
},
{
field: 'user_misery()',
},
],
sorts: [{field: 'tpm ', kind: 'desc'}],
query: 'event.type:transaction transaction:/api*',
project: [data.projects[0].id, data.projects[1].id],
start: '2019-10-01T00:00:00',
end: '2019-10-02T00:00:00',
statsPeriod: '14d',
environment: [],
additionalConditions: new MutableSearch(''),
createdBy: undefined,
interval: undefined,
display: '',
team: [],
topEvents: undefined,
yAxis: undefined,
});
return eventView;
}
describe('Performance > Table', function () {
let eventsV2Mock, eventsMock;
beforeEach(function () {
browserHistory.push = jest.fn();
MockApiClient.addMockResponse({
url: '/organizations/org-slug/projects/',
body: [],
});
const eventsMetaFieldsMock = {
user: 'string',
transaction: 'string',
project: 'string',
tpm: 'number',
p50: 'number',
p95: 'number',
failure_rate: 'number',
apdex: 'number',
count_unique_user: 'number',
count_miserable_user: 'number',
user_misery: 'number',
};
const eventsBodyMock = [
{
team_key_transaction: 1,
transaction: '/apple/cart',
project: '2',
user: 'uhoh@example.com',
tpm: 30,
p50: 100,
p95: 500,
failure_rate: 0.1,
apdex: 0.6,
count_unique_user: 1000,
count_miserable_user: 122,
user_misery: 0.114,
project_threshold_config: ['duration', 300],
},
{
team_key_transaction: 0,
transaction: '/apple/checkout',
project: '1',
user: 'uhoh@example.com',
tpm: 30,
p50: 100,
p95: 500,
failure_rate: 0.1,
apdex: 0.6,
count_unique_user: 1000,
count_miserable_user: 122,
user_misery: 0.114,
project_threshold_config: ['duration', 300],
},
];
eventsV2Mock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/eventsv2/',
body: {
meta: eventsMetaFieldsMock,
data: eventsBodyMock,
},
});
eventsMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/events/',
body: {
meta: {fields: eventsMetaFieldsMock},
data: eventsBodyMock,
},
});
MockApiClient.addMockResponse({
method: 'GET',
url: `/organizations/org-slug/key-transactions-list/`,
body: [],
});
});
afterEach(function () {
MockApiClient.clearMockResponses();
});
describe('with eventsv2', function () {
it('renders correct cell actions without feature', async function () {
const data = initializeData({
query: 'event.type:transaction transaction:/api*',
});
const wrapper = mountWithTheme(
);
await tick();
wrapper.update();
const firstRow = wrapper.find('GridBody').find('GridRow').at(0);
const transactionCell = firstRow.find('GridBodyCell').at(1);
expect(transactionCell.find('Link').prop('to')).toEqual({
pathname: '/organizations/org-slug/performance/summary/',
query: {
transaction: '/apple/cart',
project: '2',
environment: [],
statsPeriod: '14d',
start: '2019-10-01T00:00:00',
end: '2019-10-02T00:00:00',
query: '', // drops 'transaction:/api*' and 'event.type:transaction' from the query
unselectedSeries: 'p100()',
showTransactions: undefined,
display: undefined,
trendFunction: undefined,
trendColumn: undefined,
},
});
const userMiseryCell = firstRow.find('GridBodyCell').at(9);
const cellAction = userMiseryCell.find('CellAction');
expect(cellAction.prop('allowActions')).toEqual([
'add',
'exclude',
'show_greater_than',
'show_less_than',
'edit_threshold',
]);
const menu = openContextMenu(wrapper, 8); // User Misery Cell Action
expect(menu.find('MenuButtons').find('ActionItem')).toHaveLength(3);
expect(menu.find('MenuButtons').find('ActionItem').at(2).text()).toEqual(
'Edit threshold (300ms)'
);
});
it('hides cell actions when withStaticFilters is true', async function () {
const data = initializeData(
{
query: 'event.type:transaction transaction:/api*',
},
['performance-frontend-use-events-endpoint']
);
const wrapper = mountWithTheme(
);
await tick();
wrapper.update();
const firstRow = wrapper.find('GridBody').find('GridRow').at(0);
const userMiseryCell = firstRow.find('GridBodyCell').at(9);
const cellAction = userMiseryCell.find('CellAction');
expect(cellAction.prop('allowActions')).toEqual([]);
});
it('sends MEP param when setting enabled', async function () {
const data = initializeData(
{
query: 'event.type:transaction transaction:/api*',
},
['performance-use-metrics']
);
const wrapper = mountWithTheme(
);
await tick();
wrapper.update();
expect(eventsV2Mock).toHaveBeenCalledTimes(1);
expect(eventsV2Mock).toHaveBeenNthCalledWith(
1,
expect.anything(),
expect.objectContaining({
query: expect.objectContaining({
environment: [],
field: [
'team_key_transaction',
'transaction',
'project',
'tpm()',
'p50()',
'p95()',
'failure_rate()',
'apdex()',
'count_unique(user)',
'count_miserable(user)',
'user_misery()',
],
metricsEnhanced: '1',
preventMetricAggregates: '1',
per_page: 50,
project: ['1', '2'],
query: 'event.type:transaction transaction:/api*',
referrer: 'api.performance.landing-table',
sort: '-team_key_transaction',
statsPeriod: '14d',
}),
})
);
});
});
describe('with events', function () {
it('renders correct cell actions without feature', async function () {
const data = initializeData(
{
query: 'event.type:transaction transaction:/api*',
},
['performance-frontend-use-events-endpoint']
);
const wrapper = mountWithTheme(
);
await tick();
wrapper.update();
const firstRow = wrapper.find('GridBody').find('GridRow').at(0);
const transactionCell = firstRow.find('GridBodyCell').at(1);
expect(transactionCell.find('Link').prop('to')).toEqual({
pathname: '/organizations/org-slug/performance/summary/',
query: {
transaction: '/apple/cart',
project: '2',
environment: [],
statsPeriod: '14d',
start: '2019-10-01T00:00:00',
end: '2019-10-02T00:00:00',
query: '', // drops 'transaction:/api*' and 'event.type:transaction' from the query
unselectedSeries: 'p100()',
showTransactions: undefined,
display: undefined,
trendFunction: undefined,
trendColumn: undefined,
},
});
const userMiseryCell = firstRow.find('GridBodyCell').at(9);
const cellAction = userMiseryCell.find('CellAction');
expect(cellAction.prop('allowActions')).toEqual([
'add',
'exclude',
'show_greater_than',
'show_less_than',
'edit_threshold',
]);
const menu = openContextMenu(wrapper, 8); // User Misery Cell Action
expect(menu.find('MenuButtons').find('ActionItem')).toHaveLength(3);
expect(menu.find('MenuButtons').find('ActionItem').at(2).text()).toEqual(
'Edit threshold (300ms)'
);
});
it('hides cell actions when withStaticFilters is true', async function () {
const data = initializeData(
{
query: 'event.type:transaction transaction:/api*',
},
['performance-frontend-use-events-endpoint']
);
const wrapper = mountWithTheme(
);
await tick();
wrapper.update();
const firstRow = wrapper.find('GridBody').find('GridRow').at(0);
const userMiseryCell = firstRow.find('GridBodyCell').at(9);
const cellAction = userMiseryCell.find('CellAction');
expect(cellAction.prop('allowActions')).toEqual([]);
});
it('sends MEP param when setting enabled', async function () {
const data = initializeData(
{
query: 'event.type:transaction transaction:/api*',
},
['performance-use-metrics', 'performance-frontend-use-events-endpoint']
);
const wrapper = mountWithTheme(
);
await tick();
wrapper.update();
expect(eventsMock).toHaveBeenCalledTimes(1);
expect(eventsMock).toHaveBeenNthCalledWith(
1,
expect.anything(),
expect.objectContaining({
query: expect.objectContaining({
environment: [],
field: [
'team_key_transaction',
'transaction',
'project',
'tpm()',
'p50()',
'p95()',
'failure_rate()',
'apdex()',
'count_unique(user)',
'count_miserable(user)',
'user_misery()',
],
metricsEnhanced: '1',
preventMetricAggregates: '1',
per_page: 50,
project: ['1', '2'],
query: 'event.type:transaction transaction:/api*',
referrer: 'api.performance.landing-table',
sort: '-team_key_transaction',
statsPeriod: '14d',
}),
})
);
});
});
});