123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- 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();
- });
- });
- });
|