import selectEvent from 'react-select-event';
import {initializeOrg} from 'sentry-test/initializeOrg';
import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
import {ModalRenderProps} from 'sentry/actionCreators/modal';
import AddToDashboardModal from 'sentry/components/modals/widgetBuilder/addToDashboardModal';
import {
DashboardDetails,
DashboardWidgetSource,
DisplayType,
} from 'sentry/views/dashboardsV2/types';
const stubEl = (props: {children?: React.ReactNode}) =>
{props.children}
;
const mockWidgetAsQueryParams = {
defaultTableColumns: ['field1', 'field2'],
defaultTitle: 'Default title',
defaultWidgetQuery: '',
displayType: DisplayType.LINE,
environment: [],
project: [],
source: DashboardWidgetSource.DISCOVERV2,
};
describe('add to dashboard modal', () => {
let eventsStatsMock;
const initialData = initializeOrg({
...initializeOrg(),
organization: {
features: ['new-widget-builder-experience-design'],
},
});
const testDashboard: DashboardDetails = {
id: '1',
title: 'Test Dashboard',
createdBy: undefined,
dateCreated: '2020-01-01T00:00:00.000Z',
widgets: [],
projects: [],
filters: {},
};
let widget = {
title: 'Test title',
description: 'Test description',
displayType: DisplayType.LINE,
interval: '5m',
queries: [
{
conditions: '',
fields: ['count()'],
aggregates: ['count()'],
fieldAliases: [],
columns: [] as string[],
orderby: '',
name: '',
},
],
};
const defaultSelection = {
projects: [],
environments: [],
datetime: {
start: null,
end: null,
period: '24h',
utc: false,
},
};
beforeEach(() => {
MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/',
body: [{...testDashboard, widgetDisplay: [DisplayType.AREA]}],
});
eventsStatsMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/events-stats/',
body: [],
});
});
it('renders with the widget title and description', async function () {
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
expect(screen.getByText('Test title')).toBeInTheDocument();
expect(screen.getByText('Select Dashboard')).toBeInTheDocument();
expect(
screen.getByText(
'This is a preview of how the widget will appear in your dashboard.'
)
).toBeInTheDocument();
expect(screen.getByRole('button', {name: 'Add + Stay in Discover'})).toBeDisabled();
expect(screen.getByRole('button', {name: 'Open in Widget Builder'})).toBeDisabled();
});
it('enables the buttons when a dashboard is selected', async function () {
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
expect(screen.getByRole('button', {name: 'Add + Stay in Discover'})).toBeDisabled();
expect(screen.getByRole('button', {name: 'Open in Widget Builder'})).toBeDisabled();
await selectEvent.select(screen.getByText('Select Dashboard'), 'Test Dashboard');
expect(screen.getByRole('button', {name: 'Add + Stay in Discover'})).toBeEnabled();
expect(screen.getByRole('button', {name: 'Open in Widget Builder'})).toBeEnabled();
});
it('includes a New Dashboard option in the selector with saved dashboards', async function () {
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
selectEvent.openMenu(screen.getByText('Select Dashboard'));
expect(screen.getByText('+ Create New Dashboard')).toBeInTheDocument();
expect(screen.getByText('Test Dashboard')).toBeInTheDocument();
});
it('calls the events stats endpoint with the query and selection values', async function () {
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
expect(eventsStatsMock).toHaveBeenCalledWith(
'/organizations/org-slug/events-stats/',
expect.objectContaining({
query: expect.objectContaining({
environment: [],
project: [],
interval: '5m',
orderby: '',
statsPeriod: '24h',
yAxis: ['count()'],
}),
})
);
});
it('navigates to the widget builder when clicking Open in Widget Builder', async () => {
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
await selectEvent.select(screen.getByText('Select Dashboard'), 'Test Dashboard');
userEvent.click(screen.getByText('Open in Widget Builder'));
expect(initialData.router.push).toHaveBeenCalledWith({
pathname: '/organizations/org-slug/dashboard/1/widget/new/',
query: mockWidgetAsQueryParams,
});
});
it('updates the selected dashboard with the widget when clicking Add + Stay in Discover', async () => {
const dashboardDetailGetMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/1/',
body: {id: '1', widgets: []},
});
const dashboardDetailPutMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/1/',
method: 'PUT',
body: {},
});
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
await selectEvent.select(screen.getByText('Select Dashboard'), 'Test Dashboard');
userEvent.click(screen.getByText('Add + Stay in Discover'));
expect(dashboardDetailGetMock).toHaveBeenCalled();
// mocked widgets response is an empty array, assert this new widget
// is sent as an update to the dashboard
await waitFor(() => {
expect(dashboardDetailPutMock).toHaveBeenCalledWith(
'/organizations/org-slug/dashboards/1/',
expect.objectContaining({data: expect.objectContaining({widgets: [widget]})})
);
});
});
it('clears sort when clicking Add + Stay in Discover with line chart', async () => {
const dashboardDetailGetMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/1/',
body: {id: '1', widgets: []},
});
const dashboardDetailPutMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/1/',
method: 'PUT',
body: {},
});
widget = {
...widget,
queries: [
{
conditions: '',
fields: ['count()'],
aggregates: ['count()'],
fieldAliases: [],
columns: [],
orderby: '-project',
name: '',
},
],
};
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
await selectEvent.select(screen.getByText('Select Dashboard'), 'Test Dashboard');
userEvent.click(screen.getByText('Add + Stay in Discover'));
expect(dashboardDetailGetMock).toHaveBeenCalled();
// mocked widgets response is an empty array, assert this new widget
// is sent as an update to the dashboard
await waitFor(() => {
expect(dashboardDetailPutMock).toHaveBeenCalledWith(
'/organizations/org-slug/dashboards/1/',
expect.objectContaining({
data: expect.objectContaining({
widgets: [
{
description: 'Test description',
displayType: 'line',
interval: '5m',
queries: [
{
aggregates: ['count()'],
columns: [],
conditions: '',
fieldAliases: [],
fields: ['count()'],
name: '',
orderby: '',
},
],
title: 'Test title',
},
],
}),
})
);
});
});
it('saves sort when clicking Add + Stay in Discover with top period chart', async () => {
const dashboardDetailGetMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/1/',
body: {id: '1', widgets: []},
});
const dashboardDetailPutMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/1/',
method: 'PUT',
body: {},
});
widget = {
...widget,
displayType: DisplayType.TOP_N,
queries: [
{
conditions: '',
fields: ['count()'],
aggregates: ['count()'],
fieldAliases: [],
columns: ['project'],
orderby: '-project',
name: '',
},
],
};
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
await selectEvent.select(screen.getByText('Select Dashboard'), 'Test Dashboard');
userEvent.click(screen.getByText('Add + Stay in Discover'));
expect(dashboardDetailGetMock).toHaveBeenCalled();
// mocked widgets response is an empty array, assert this new widget
// is sent as an update to the dashboard
await waitFor(() => {
expect(dashboardDetailPutMock).toHaveBeenCalledWith(
'/organizations/org-slug/dashboards/1/',
expect.objectContaining({
data: expect.objectContaining({
widgets: [
{
description: 'Test description',
displayType: 'top_n',
interval: '5m',
limit: 5,
queries: [
{
aggregates: ['count()'],
columns: ['project'],
conditions: '',
fieldAliases: [],
fields: ['count()'],
name: '',
orderby: '-project',
},
],
title: 'Test title',
},
],
}),
})
);
});
});
it('disables Add + Stay in Discover when a new dashboard is selected', async () => {
render(
undefined}
organization={initialData.organization}
widget={widget}
selection={defaultSelection}
router={initialData.router}
widgetAsQueryParams={mockWidgetAsQueryParams}
/>
);
await waitFor(() => {
expect(screen.getByText('Select Dashboard')).toBeEnabled();
});
await selectEvent.select(
screen.getByText('Select Dashboard'),
'+ Create New Dashboard'
);
expect(screen.getByRole('button', {name: 'Add + Stay in Discover'})).toBeDisabled();
expect(screen.getByRole('button', {name: 'Open in Widget Builder'})).toBeEnabled();
});
});