Files
2024-02-15 17:01:50 +00:00

408 lines
12 KiB
TypeScript

import {OrganizationFixture} from 'sentry-fixture/organization';
import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
import type {NewQuery} from 'sentry/types';
import EventView from 'sentry/utils/discover/eventView';
import {DisplayModes} from 'sentry/utils/discover/types';
import {ALL_VIEWS} from 'sentry/views/discover/data';
import SavedQueryButtonGroup from 'sentry/views/discover/savedQuery';
import * as utils from 'sentry/views/discover/savedQuery/utils';
jest.mock('sentry/actionCreators/modal');
function mount(
location,
organization,
router,
eventView,
savedQuery,
yAxis,
disabled = false,
setSavedQuery = jest.fn()
) {
return render(
<SavedQueryButtonGroup
location={location}
organization={organization}
eventView={eventView}
savedQuery={savedQuery}
disabled={disabled}
updateCallback={() => {}}
yAxis={yAxis}
router={router}
queryDataLoading={false}
setSavedQuery={setSavedQuery}
setHomepageQuery={jest.fn()}
/>
);
}
describe('Discover > SaveQueryButtonGroup', function () {
let organization;
const location = {
pathname: '/organization/eventsv2/',
query: {},
};
const router = {
location: {query: {}},
};
const yAxis = ['count()', 'failure_count()'];
const errorsQuery = {
...(ALL_VIEWS.find(view => view.name === 'Errors by Title') as NewQuery),
yAxis: ['count()'],
display: DisplayModes.DEFAULT,
};
const errorsView = EventView.fromSavedQuery(errorsQuery);
const errorsViewSaved = EventView.fromSavedQuery(errorsQuery);
errorsViewSaved.id = '1';
const errorsViewModified = EventView.fromSavedQuery(errorsQuery);
errorsViewModified.id = '1';
errorsViewModified.name = 'Modified Name';
const savedQuery = {
...errorsViewSaved.toNewQuery(),
yAxis,
dateCreated: '',
dateUpdated: '',
id: '1',
};
beforeEach(() => {
organization = OrganizationFixture({
features: ['discover-query', 'dashboards-edit'],
});
});
afterEach(() => {
MockApiClient.clearMockResponses();
jest.clearAllMocks();
});
describe('building on a new query', () => {
const mockUtils = jest
.spyOn(utils, 'handleCreateQuery')
.mockImplementation(() => Promise.resolve(savedQuery));
beforeEach(() => {
mockUtils.mockClear();
});
it('renders disabled buttons when disabled prop is used', () => {
mount(location, organization, router, errorsView, undefined, yAxis, true);
expect(screen.getByRole('button', {name: /save as/i})).toBeDisabled();
});
it('renders the correct set of buttons', async () => {
mount(location, organization, router, errorsView, undefined, yAxis);
expect(screen.getByRole('button', {name: /save as/i})).toBeInTheDocument();
expect(
screen.queryByRole('button', {name: /save changes/i})
).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: /discover context menu/i}));
expect(
screen.queryByRole('menuitemradio', {name: /delete saved query/i})
).not.toBeInTheDocument();
});
it('renders the correct set of buttons with the homepage query feature', async () => {
organization = OrganizationFixture({
features: ['discover-query', 'dashboards-edit'],
});
mount(location, organization, router, errorsView, undefined, yAxis);
expect(screen.getByRole('button', {name: /save as/i})).toBeInTheDocument();
expect(screen.getByRole('button', {name: /set as default/i})).toBeInTheDocument();
expect(screen.getByRole('button', {name: /saved queries/i})).toBeInTheDocument();
expect(
screen.getByRole('button', {name: /discover context menu/i})
).toBeInTheDocument();
expect(
screen.queryByRole('button', {name: /save changes/i})
).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: /discover context menu/i}));
expect(
screen.queryByRole('menuitemradio', {name: /add to dashboard/i})
).toBeInTheDocument();
});
it('hides the banner when save is complete.', async () => {
mount(location, organization, router, errorsView, undefined, yAxis);
// Click on ButtonSaveAs to open dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save as'}));
// Fill in the Input
await userEvent.type(
screen.getByPlaceholderText('Display name'),
'My New Query Name'
);
// Click on Save in the Dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save for Org'}));
// The banner should not render
expect(screen.queryByText('Discover Trends')).not.toBeInTheDocument();
});
it('saves a well-formed query', async () => {
mount(location, organization, router, errorsView, undefined, yAxis);
// Click on ButtonSaveAs to open dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save as'}));
// Fill in the Input
await userEvent.type(
screen.getByPlaceholderText('Display name'),
'My New Query Name'
);
// Click on Save in the Dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save for Org'}));
expect(mockUtils).toHaveBeenCalledWith(
expect.anything(), // api
organization,
expect.objectContaining({
...errorsView,
name: 'My New Query Name',
}),
yAxis,
true
);
});
it('rejects if query.name is empty', async () => {
mount(location, organization, router, errorsView, undefined, yAxis);
// Click on ButtonSaveAs to open dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save as'}));
// Do not fill in Input
// Click on Save in the Dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save for Org'}));
// Check that EventView has a name
expect(errorsView.name).toBe('Errors by Title');
expect(mockUtils).not.toHaveBeenCalled();
});
});
describe('viewing a saved query', () => {
let mockUtils;
beforeEach(() => {
mockUtils = jest
.spyOn(utils, 'handleDeleteQuery')
.mockImplementation(() => Promise.resolve());
});
afterEach(() => {
mockUtils.mockClear();
});
it('renders the correct set of buttons', async () => {
mount(
location,
organization,
router,
EventView.fromSavedQuery({...errorsQuery, yAxis}),
savedQuery,
yAxis
);
expect(screen.queryByRole('button', {name: /save as/i})).not.toBeInTheDocument();
expect(
screen.queryByRole('button', {name: /save changes/i})
).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: /discover context menu/i}));
expect(
screen.getByRole('menuitemradio', {name: /delete saved query/i})
).toBeInTheDocument();
});
it('treats undefined yAxis the same as count() when checking for changes', async () => {
mount(
location,
organization,
router,
errorsViewSaved,
{...savedQuery, yAxis: undefined},
['count()']
);
expect(screen.queryByRole('button', {name: /save as/i})).not.toBeInTheDocument();
expect(
screen.queryByRole('button', {name: /save changes/i})
).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: /discover context menu/i}));
expect(
screen.getByRole('menuitemradio', {name: /delete saved query/i})
).toBeInTheDocument();
});
it('converts string yAxis values to array when checking for changes', async () => {
mount(
location,
organization,
router,
errorsViewSaved,
{...savedQuery, yAxis: 'count()'},
['count()']
);
expect(screen.queryByRole('button', {name: /save as/i})).not.toBeInTheDocument();
expect(
screen.queryByRole('button', {name: /save changes/i})
).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: /discover context menu/i}));
expect(
screen.getByRole('menuitemradio', {name: /delete saved query/i})
).toBeInTheDocument();
});
it('deletes the saved query', async () => {
mount(location, organization, router, errorsViewSaved, savedQuery, yAxis);
await userEvent.click(screen.getByRole('button', {name: /discover context menu/i}));
await userEvent.click(
screen.getByRole('menuitemradio', {name: /delete saved query/i})
);
expect(mockUtils).toHaveBeenCalledWith(
expect.anything(), // api
organization,
expect.objectContaining({id: '1'})
);
});
});
describe('modifying a saved query', () => {
let mockUtils;
it('renders the correct set of buttons', async () => {
mount(
location,
organization,
router,
errorsViewModified,
errorsViewSaved.toNewQuery(),
yAxis
);
expect(screen.queryByRole('button', {name: /save as/i})).toBeInTheDocument();
expect(screen.getByRole('button', {name: /save changes/i})).toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: /discover context menu/i}));
expect(
screen.getByRole('menuitemradio', {name: /delete saved query/i})
).toBeInTheDocument();
});
describe('updates the saved query', () => {
beforeEach(() => {
mockUtils = jest
.spyOn(utils, 'handleUpdateQuery')
.mockImplementation(() => Promise.resolve(savedQuery));
});
afterEach(() => {
mockUtils.mockClear();
});
it('accepts a well-formed query', async () => {
const mockSetSavedQuery = jest.fn();
mount(
location,
organization,
router,
errorsViewModified,
savedQuery,
yAxis,
false,
mockSetSavedQuery
);
// Click on Save in the Dropdown
await userEvent.click(screen.getByRole('button', {name: /save changes/i}));
await waitFor(() => {
expect(mockUtils).toHaveBeenCalledWith(
expect.anything(), // api
organization,
expect.objectContaining({
...errorsViewModified,
}),
yAxis
);
expect(mockSetSavedQuery).toHaveBeenCalled();
});
});
});
describe('creates a separate query', () => {
beforeEach(() => {
mockUtils = jest
.spyOn(utils, 'handleCreateQuery')
.mockImplementation(() => Promise.resolve(savedQuery));
});
afterEach(() => {
mockUtils.mockClear();
});
it('checks that it is forked from a saved query', async () => {
mount(location, organization, router, errorsViewModified, savedQuery, yAxis);
// Click on ButtonSaveAs to open dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save as'}));
// Fill in the Input
await userEvent.type(screen.getByPlaceholderText('Display name'), 'Forked Query');
// Click on Save in the Dropdown
await userEvent.click(screen.getByRole('button', {name: 'Save for Org'}));
expect(mockUtils).toHaveBeenCalledWith(
expect.anything(), // api
organization,
expect.objectContaining({
...errorsViewModified,
name: 'Forked Query',
}),
yAxis,
false
);
});
});
});
describe('create alert from discover', () => {
it('renders create alert button when metrics alerts is enabled', () => {
const metricAlertOrg = {
...organization,
features: ['incidents'],
};
mount(location, metricAlertOrg, router, errorsViewModified, savedQuery, yAxis);
expect(screen.getByRole('button', {name: /create alert/i})).toBeInTheDocument();
});
it('does not render create alert button without metric alerts', () => {
mount(location, organization, router, errorsViewModified, savedQuery, yAxis);
expect(
screen.queryByRole('button', {name: /create alert/i})
).not.toBeInTheDocument();
});
});
});