import {act} from 'react-dom/test-utils';
import {browserHistory} from 'react-router';
import {selectDropdownMenuItem} from 'sentry-test/dropdownMenu';
import {mountWithTheme} from 'sentry-test/enzyme';
import {triggerPress} from 'sentry-test/utils';
import {openAddToDashboardModal} from 'sentry/actionCreators/modal';
import {DisplayModes} from 'sentry/utils/discover/types';
import {DashboardWidgetSource, DisplayType} from 'sentry/views/dashboardsV2/types';
import QueryList from 'sentry/views/eventsV2/queryList';
jest.mock('sentry/actionCreators/modal');
jest.mock('sentry/components/charts/eventsRequest');
describe('EventsV2 > QueryList', function () {
let location, savedQueries, organization, deleteMock, duplicateMock, queryChangeMock;
beforeAll(async function () {
await import('sentry/components/modals/widgetBuilder/addToDashboardModal');
});
beforeEach(function () {
organization = TestStubs.Organization();
savedQueries = [
TestStubs.DiscoverSavedQuery(),
TestStubs.DiscoverSavedQuery({name: 'saved query 2', id: '2'}),
];
MockApiClient.addMockResponse({
url: '/organizations/org-slug/events-stats/',
method: 'GET',
statusCode: 200,
body: {data: []},
});
deleteMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/discover/saved/2/',
method: 'DELETE',
statusCode: 200,
body: {},
});
duplicateMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/discover/saved/',
method: 'POST',
body: {
id: '3',
name: 'Saved query copy',
},
});
location = {
pathname: '/organizations/org-slug/discover/queries/',
query: {cursor: '0:1:1', statsPeriod: '14d'},
};
queryChangeMock = jest.fn();
});
afterEach(() => {
jest.clearAllMocks();
});
it('renders an empty list', function () {
const wrapper = mountWithTheme(
);
const content = wrapper.find('QueryCard');
// No queries
expect(content).toHaveLength(0);
expect(wrapper.find('EmptyStateWarning')).toHaveLength(1);
});
it('renders pre-built queries and saved ones', function () {
const wrapper = mountWithTheme(
);
const content = wrapper.find('QueryCard');
// pre built + saved queries
expect(content).toHaveLength(5);
});
it('can duplicate and trigger change callback', async function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('QueryCard').last();
expect(card.find('QueryCardContent').text()).toEqual(savedQueries[1].name);
await selectDropdownMenuItem({
wrapper,
specifiers: {prefix: 'QueryCard', last: true},
itemKey: 'duplicate',
});
expect(duplicateMock).toHaveBeenCalled();
expect(queryChangeMock).toHaveBeenCalled();
});
it('can delete and trigger change callback', async function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('QueryCard').last();
expect(card.find('QueryCardContent').text()).toEqual(savedQueries[1].name);
await selectDropdownMenuItem({
wrapper,
specifiers: {prefix: 'QueryCard', last: true},
itemKey: 'delete',
});
expect(deleteMock).toHaveBeenCalled();
expect(queryChangeMock).toHaveBeenCalled();
});
it('returns short url location for saved query', function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('QueryCard').last();
const link = card.find('Link').last().prop('to');
expect(link.pathname).toEqual('/organizations/org-slug/discover/results/');
expect(link.query).toEqual({
id: '2',
statsPeriod: '14d',
});
});
it('can redirect on last query deletion', async function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('QueryCard').last();
expect(card.find('QueryCardContent').text()).toEqual(savedQueries[1].name);
await selectDropdownMenuItem({
wrapper,
specifiers: {prefix: 'QueryCard', last: true},
itemKey: 'delete',
});
expect(deleteMock).toHaveBeenCalled();
expect(queryChangeMock).not.toHaveBeenCalled();
expect(browserHistory.push).toHaveBeenCalledWith({
pathname: location.pathname,
query: {cursor: undefined, statsPeriod: '14d'},
});
});
it('renders Add to Dashboard in context menu with feature flag', async function () {
const featuredOrganization = TestStubs.Organization({
features: ['dashboards-edit'],
});
const wrapper = mountWithTheme(
);
let card = wrapper.find('QueryCard').last();
await act(async () => {
triggerPress(card.find('DropdownTrigger'));
await tick();
wrapper.update();
});
card = wrapper.find('QueryCard').last();
const menuItems = card.find('MenuItemWrap');
expect(menuItems.length).toEqual(3);
expect(menuItems.at(0).text()).toEqual('Add to Dashboard');
expect(menuItems.at(1).text()).toEqual('Duplicate Query');
expect(menuItems.at(2).text()).toEqual('Delete Query');
});
it('only renders Delete Query and Duplicate Query in context menu', async function () {
const wrapper = mountWithTheme(
);
let card = wrapper.find('QueryCard').last();
await act(async () => {
triggerPress(card.find('DropdownTrigger'));
await tick();
wrapper.update();
});
card = wrapper.find('QueryCard').last();
const menuItems = card.find('MenuItemWrap');
expect(menuItems.length).toEqual(2);
expect(menuItems.at(0).text()).toEqual('Duplicate Query');
expect(menuItems.at(1).text()).toEqual('Delete Query');
});
it('passes yAxis from the savedQuery to MiniGraph', function () {
const featuredOrganization = TestStubs.Organization({
features: ['dashboards-edit'],
});
const yAxis = ['count()', 'failure_count()'];
const savedQueryWithMultiYAxis = {
...savedQueries.slice(1)[0],
yAxis,
};
const wrapper = mountWithTheme(
);
const miniGraph = wrapper.find('MiniGraph');
expect(miniGraph.props().yAxis).toEqual(['count()', 'failure_count()']);
});
describe('Add to Dashboard modal', () => {
it('opens a modal with the correct params for Top 5 chart', async function () {
const featuredOrganization = TestStubs.Organization({
features: ['dashboards-edit'],
});
const wrapper = mountWithTheme(
);
let card = wrapper.find('QueryCard').last();
await act(async () => {
triggerPress(card.find('DropdownTrigger'));
await tick();
wrapper.update();
});
card = wrapper.find('QueryCard').last();
const menuItems = card.find('MenuItemWrap');
expect(menuItems.length).toEqual(3);
expect(menuItems.at(0).text()).toEqual('Add to Dashboard');
await act(async () => {
triggerPress(menuItems.at(0));
await tick();
wrapper.update();
});
expect(openAddToDashboardModal).toHaveBeenCalledWith(
expect.objectContaining({
widget: {
title: 'Saved query #1',
displayType: DisplayType.AREA,
limit: 5,
queries: [
{
aggregates: ['count()'],
columns: ['test'],
conditions: '',
fields: ['test', 'count()', 'count()'],
name: '',
orderby: 'test',
},
],
},
widgetAsQueryParams: expect.objectContaining({
defaultTableColumns: ['test', 'count()'],
defaultTitle: 'Saved query #1',
defaultWidgetQuery:
'name=&aggregates=count()&columns=test&fields=test%2Ccount()%2Ccount()&conditions=&orderby=test',
displayType: DisplayType.AREA,
source: DashboardWidgetSource.DISCOVERV2,
}),
})
);
});
it('opens a modal with the correct params for other chart', async function () {
const featuredOrganization = TestStubs.Organization({
features: ['dashboards-edit'],
});
const wrapper = mountWithTheme(
);
let card = wrapper.find('QueryCard').last();
await act(async () => {
triggerPress(card.find('DropdownTrigger'));
await tick();
wrapper.update();
});
card = wrapper.find('QueryCard').last();
const menuItems = card.find('MenuItemWrap');
expect(menuItems.length).toEqual(3);
expect(menuItems.at(0).text()).toEqual('Add to Dashboard');
await act(async () => {
triggerPress(menuItems.at(0));
await tick();
wrapper.update();
});
expect(openAddToDashboardModal).toHaveBeenCalledWith(
expect.objectContaining({
widget: {
title: 'Saved query #1',
displayType: DisplayType.LINE,
queries: [
{
aggregates: ['count()'],
columns: [],
conditions: '',
fields: ['count()'],
name: '',
// Orderby gets dropped because ordering only applies to
// Top-N and tables
orderby: '',
},
],
},
widgetAsQueryParams: expect.objectContaining({
defaultTableColumns: ['test', 'count()'],
defaultTitle: 'Saved query #1',
defaultWidgetQuery:
'name=&aggregates=count()&columns=&fields=count()&conditions=&orderby=',
displayType: DisplayType.LINE,
source: DashboardWidgetSource.DISCOVERV2,
}),
})
);
});
});
});