123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- import {act} from 'react-dom/test-utils';
- import {browserHistory} from 'react-router';
- import {createListeners} from 'sentry-test/createListeners';
- import {mountWithTheme} from 'sentry-test/enzyme';
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import {mountGlobalModal} from 'sentry-test/modal';
- import ViewEditDashboard from 'app/views/dashboardsV2/view';
- describe('Dashboards > Detail', function () {
- const organization = TestStubs.Organization({
- features: ['global-views', 'dashboards-basic', 'dashboards-edit', 'discover-query'],
- projects: [TestStubs.Project()],
- });
- describe('prebuilt dashboards', function () {
- let wrapper;
- let initialData, mockVisit;
- beforeEach(function () {
- initialData = initializeOrg({organization});
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/tags/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [TestStubs.Project()],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/',
- body: [
- TestStubs.Dashboard([], {id: 'default-overview', title: 'Default'}),
- TestStubs.Dashboard([], {id: '1', title: 'Custom Errors'}),
- ],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/default-overview/',
- body: TestStubs.Dashboard([], {id: 'default-overview', title: 'Default'}),
- });
- mockVisit = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/1/visit/',
- method: 'POST',
- body: [],
- statusCode: 200,
- });
- });
- afterEach(function () {
- MockApiClient.clearMockResponses();
- });
- it('can delete', async function () {
- const deleteMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/default-overview/',
- method: 'DELETE',
- });
- wrapper = mountWithTheme(
- <ViewEditDashboard
- organization={initialData.organization}
- params={{orgId: 'org-slug', dashboardId: 'default-overview'}}
- router={initialData.router}
- location={location}
- />,
- initialData.routerContext
- );
- await tick();
- wrapper.update();
- // Enter edit mode.
- wrapper.find('Controls Button[data-test-id="dashboard-edit"]').simulate('click');
- const modal = await mountGlobalModal();
- // Click delete, confirm will show
- wrapper.find('Controls Button[data-test-id="dashboard-delete"]').simulate('click');
- await tick();
- await modal.update();
- // Click confirm
- modal.find('button[aria-label="Confirm"]').simulate('click');
- expect(deleteMock).toHaveBeenCalled();
- });
- it('can rename and save', async function () {
- const fireEvent = createListeners('window');
- const updateMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/default-overview/',
- method: 'PUT',
- body: TestStubs.Dashboard([], {id: '8', title: 'Updated prebuilt'}),
- });
- wrapper = mountWithTheme(
- <ViewEditDashboard
- organization={initialData.organization}
- params={{orgId: 'org-slug', dashboardId: 'default-overview'}}
- router={initialData.router}
- location={initialData.router.location}
- />,
- initialData.routerContext
- );
- await tick();
- wrapper.update();
- // Enter edit mode.
- wrapper.find('Controls Button[data-test-id="dashboard-edit"]').simulate('click');
- // Rename
- const dashboardTitle = wrapper.find('DashboardTitle Label');
- dashboardTitle.simulate('click');
- wrapper.find('StyledInput').simulate('change', {
- target: {innerText: 'Updated prebuilt', value: 'Updated prebuilt'},
- });
- act(() => {
- // Press enter
- fireEvent.keyDown('Enter');
- });
- wrapper.find('Controls Button[data-test-id="dashboard-commit"]').simulate('click');
- await tick();
- expect(updateMock).toHaveBeenCalledWith(
- '/organizations/org-slug/dashboards/default-overview/',
- expect.objectContaining({
- data: expect.objectContaining({title: 'Updated prebuilt'}),
- })
- );
- // Should redirect to the new dashboard.
- expect(browserHistory.replace).toHaveBeenCalledWith(
- expect.objectContaining({
- pathname: '/organizations/org-slug/dashboard/8/',
- })
- );
- });
- it('disables buttons based on features', async function () {
- initialData = initializeOrg({
- organization: TestStubs.Organization({
- features: ['global-views', 'dashboards-basic', 'discover-query'],
- projects: [TestStubs.Project()],
- }),
- });
- wrapper = mountWithTheme(
- <ViewEditDashboard
- organization={initialData.organization}
- params={{orgId: 'org-slug', dashboardId: 'default-overview'}}
- router={initialData.router}
- location={initialData.router.location}
- />,
- initialData.routerContext
- );
- await tick();
- wrapper.update();
- // Edit should be disabled
- const editProps = wrapper
- .find('Controls Button[data-test-id="dashboard-edit"]')
- .props();
- expect(editProps.disabled).toBe(true);
- expect(mockVisit).not.toHaveBeenCalled();
- });
- });
- describe('custom dashboards', function () {
- let wrapper, initialData, widgets, mockVisit;
- beforeEach(function () {
- initialData = initializeOrg({organization});
- 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',
- }
- ),
- ];
- mockVisit = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/1/visit/',
- method: 'POST',
- body: [],
- statusCode: 200,
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/tags/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [TestStubs.Project()],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/',
- body: [
- TestStubs.Dashboard([], {id: 'default-overview', title: 'Default'}),
- TestStubs.Dashboard([], {id: '1', title: 'Custom Errors'}),
- ],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/1/',
- body: TestStubs.Dashboard(widgets, {id: '1', title: 'Custom Errors'}),
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: {data: []},
- });
- });
- afterEach(function () {
- MockApiClient.clearMockResponses();
- });
- it('can remove widgets', async function () {
- const updateMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/dashboards/1/',
- method: 'PUT',
- });
- wrapper = mountWithTheme(
- <ViewEditDashboard
- organization={initialData.organization}
- params={{orgId: 'org-slug', dashboardId: '1'}}
- router={initialData.router}
- location={initialData.router.location}
- />,
- initialData.routerContext
- );
- await tick();
- wrapper.update();
- expect(mockVisit).toHaveBeenCalledTimes(1);
- // Enter edit mode.
- wrapper.find('Controls Button[data-test-id="dashboard-edit"]').simulate('click');
- const card = wrapper.find('WidgetCard').first();
- card.find('StyledPanel').simulate('mouseOver');
- // Remove the second and third widgets
- wrapper
- .find('WidgetCard')
- .at(1)
- .find('IconClick[data-test-id="widget-delete"]')
- .simulate('click');
- wrapper
- .find('WidgetCard')
- .at(1)
- .find('IconClick[data-test-id="widget-delete"]')
- .simulate('click');
- // Save changes
- wrapper.find('Controls Button[data-test-id="dashboard-commit"]').simulate('click');
- expect(updateMock).toHaveBeenCalled();
- expect(updateMock).toHaveBeenCalledWith(
- '/organizations/org-slug/dashboards/1/',
- expect.objectContaining({
- data: expect.objectContaining({
- title: 'Custom Errors',
- widgets: [widgets[0]],
- }),
- })
- );
- // Visit should not be called again on dashboard update
- expect(mockVisit).toHaveBeenCalledTimes(1);
- });
- it('can enter edit mode for widgets', async function () {
- wrapper = mountWithTheme(
- <ViewEditDashboard
- organization={initialData.organization}
- params={{orgId: 'org-slug', dashboardId: '1'}}
- router={initialData.router}
- location={initialData.router.location}
- />,
- initialData.routerContext
- );
- await tick();
- wrapper.update();
- // Enter edit mode.
- wrapper.find('Controls Button[data-test-id="dashboard-edit"]').simulate('click');
- const card = wrapper.find('WidgetCard').first();
- card.find('StyledPanel').simulate('mouseOver');
- // Edit the first widget
- wrapper
- .find('WidgetCard')
- .first()
- .find('IconClick[data-test-id="widget-edit"]')
- .simulate('click');
- await tick();
- await wrapper.update();
- const modal = await mountGlobalModal();
- expect(modal.find('AddDashboardWidgetModal').props().widget).toEqual(widgets[0]);
- });
- it('hides and shows breadcrumbs based on feature', async function () {
- const newOrg = initializeOrg({
- organization: TestStubs.Organization({
- features: ['global-views', 'dashboards-basic', 'discover-query'],
- projects: [TestStubs.Project()],
- }),
- });
- wrapper = mountWithTheme(
- <ViewEditDashboard
- organization={newOrg.organization}
- params={{orgId: 'org-slug', dashboardId: '1'}}
- router={newOrg.router}
- location={newOrg.router.location}
- />,
- newOrg.routerContext
- );
- await tick();
- wrapper.update();
- expect(wrapper.find('Breadcrumbs').exists()).toBe(false);
- wrapper = mountWithTheme(
- <ViewEditDashboard
- organization={initialData.organization}
- params={{orgId: 'org-slug', dashboardId: '1'}}
- router={initialData.router}
- location={initialData.router.location}
- />,
- initialData.routerContext
- );
- await tick();
- wrapper.update();
- const breadcrumbs = wrapper.find('Breadcrumbs');
- expect(breadcrumbs.exists()).toBe(true);
- expect(breadcrumbs.find('BreadcrumbLink').find('a').text()).toEqual('Dashboards');
- expect(breadcrumbs.find('BreadcrumbItem').last().text()).toEqual('Custom Errors');
- });
- });
- });
|