import {act} from 'react-dom/test-utils';
import {selectDropdownMenuItem} from 'sentry-test/dropdownMenu';
import {mountWithTheme} from 'sentry-test/enzyme';
import {mountGlobalModal} from 'sentry-test/modal';
import {triggerPress} from 'sentry-test/utils';
import DashboardList from 'sentry/views/dashboardsV2/manage/dashboardList';
describe('Dashboards > DashboardList', function () {
let dashboards, widgets, deleteMock, dashboardUpdateMock, createMock;
const organization = TestStubs.Organization({
features: ['global-views', 'dashboards-basic', 'dashboards-edit', 'discover-query'],
projects: [TestStubs.Project()],
});
beforeEach(function () {
MockApiClient.addMockResponse({
url: '/organizations/org-slug/projects/',
body: [],
});
widgets = [
TestStubs.Widget(
[{name: '', conditions: 'event.type:error', fields: ['count()']}],
{
title: 'Errors',
interval: '1d',
id: '1',
}
),
TestStubs.Widget(
[{name: '', conditions: 'event.type:transaction', fields: ['count()']}],
{
title: 'Transactions',
interval: '1d',
id: '2',
}
),
TestStubs.Widget(
[
{
name: '',
conditions: 'event.type:transaction transaction:/api/cats',
fields: ['p50()'],
},
],
{
title: 'p50 of /api/cats',
interval: '1d',
id: '3',
}
),
];
dashboards = [
TestStubs.Dashboard([], {
id: '1',
title: 'Dashboard 1',
dateCreated: '2021-04-19T13:13:23.962105Z',
createdBy: {id: '1'},
widgetDisplay: [],
}),
TestStubs.Dashboard(widgets, {
id: '2',
title: 'Dashboard 2',
dateCreated: '2021-04-19T13:13:23.962105Z',
createdBy: {id: '1'},
widgetDisplay: ['line', 'table'],
}),
];
deleteMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/2/',
method: 'DELETE',
statusCode: 200,
});
MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/2/',
method: 'GET',
statusCode: 200,
body: {
id: '2',
title: 'Dashboard Demo',
widgets: [
{
id: '1',
title: 'Errors',
displayType: 'big_number',
interval: '5m',
},
{
id: '2',
title: 'Transactions',
displayType: 'big_number',
interval: '5m',
},
{
id: '3',
title: 'p50 of /api/cat',
displayType: 'big_number',
interval: '5m',
},
],
},
});
createMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/',
method: 'POST',
statusCode: 200,
});
dashboardUpdateMock = jest.fn();
});
afterEach(function () {
MockApiClient.clearMockResponses();
});
it('renders an empty list', function () {
const wrapper = mountWithTheme(
);
const content = wrapper.find('DashboardCard');
// No dashboards
expect(content).toHaveLength(0);
expect(wrapper.find('EmptyStateWarning')).toHaveLength(1);
});
it('renders dashboard list', function () {
const wrapper = mountWithTheme(
);
const content = wrapper.find('DashboardCard');
expect(content).toHaveLength(2);
});
it('returns landing page url for dashboards', function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('DashboardCard').last();
const link = card.find('Link').last().prop('to');
expect(link.pathname).toEqual(`/organizations/org-slug/dashboard/2/`);
});
it('persists global selection headers', function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('DashboardCard').last();
const link = card.find('Link').last().prop('to');
expect(link.pathname).toEqual(`/organizations/org-slug/dashboard/2/`);
expect(link.query).toEqual({statsPeriod: '7d'});
});
it('can delete dashboards', async function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('DashboardCard').last();
expect(card.find('Title').text()).toEqual(dashboards[1].title);
await selectDropdownMenuItem({
wrapper,
specifiers: {prefix: 'DashboardCard', last: true},
itemKey: 'dashboard-delete',
});
expect(deleteMock).not.toHaveBeenCalled();
// Confirm
const modal = await mountGlobalModal();
modal.find('Button').last().simulate('click');
await tick();
expect(deleteMock).toHaveBeenCalled();
expect(dashboardUpdateMock).toHaveBeenCalled();
});
it('cannot delete last dashboard', async function () {
const singleDashboard = [
TestStubs.Dashboard([], {
id: '1',
title: 'Dashboard 1',
dateCreated: '2021-04-19T13:13:23.962105Z',
createdBy: {id: '1'},
widgetDisplay: [],
}),
];
const wrapper = mountWithTheme(
);
let card = wrapper.find('DashboardCard').last();
expect(card.find('Title').text()).toEqual(singleDashboard[0].title);
await act(async () => {
triggerPress(card.find('DropdownTrigger'));
await tick();
wrapper.update();
});
card = wrapper.find('DashboardCard').last();
const dashboardDelete = card.find(`MenuItemWrap[data-test-id="dashboard-delete"]`);
expect(dashboardDelete.prop('aria-disabled')).toBe(true);
});
it('can duplicate dashboards', async function () {
const wrapper = mountWithTheme(
);
const card = wrapper.find('DashboardCard').last();
expect(card.find('Title').text()).toEqual(dashboards[1].title);
await selectDropdownMenuItem({
wrapper,
specifiers: {prefix: 'DashboardCard', last: true},
itemKey: 'dashboard-duplicate',
});
expect(createMock).toHaveBeenCalled();
expect(dashboardUpdateMock).toHaveBeenCalled();
});
it('does not throw an error if the POST fails during duplication', async function () {
MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/',
method: 'POST',
statusCode: 404,
});
const wrapper = mountWithTheme(
);
const card = wrapper.find('DashboardCard').last();
expect(card.find('Title').text()).toEqual(dashboards[1].title);
await selectDropdownMenuItem({
wrapper,
specifiers: {prefix: 'DashboardCard', last: true},
itemKey: 'dashboard-duplicate',
});
// Should not update, and not throw error
expect(dashboardUpdateMock).not.toHaveBeenCalled();
});
});