test: Add tests for Dashboard Header and HeaderActionsDropdown components (#13973)

* Add tests for HeaderActionsDropdown - WIP

* Fix trigger node

* Add types

* Clean up

* Add tests for Header

* Delete obsolete tests

* Add factory and clean up

* Add opposite case

* Fix file name

* Include latest changes
This commit is contained in:
Geido 2021-04-14 16:54:24 +03:00 committed by GitHub
parent a548b69201
commit 474f1e2044
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 603 additions and 436 deletions

View File

@ -1,174 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { shallow } from 'enzyme';
import { Menu, NoAnimationDropdown } from 'src/common/components';
import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal';
import HeaderActionsDropdown from 'src/dashboard/components/HeaderActionsDropdown';
import SaveModal from 'src/dashboard/components/SaveModal';
import CssEditor from 'src/dashboard/components/CssEditor';
import fetchMock from 'fetch-mock';
import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems';
fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {});
describe('HeaderActionsDropdown', () => {
const props = {
addSuccessToast: () => {},
addDangerToast: () => {},
customCss: '',
dashboardId: 1,
dashboardInfo: {},
dashboardTitle: 'Title',
editMode: false,
expandedSlices: {},
filters: {},
forceRefreshAllCharts: () => {},
hasUnsavedChanges: false,
isLoading: false,
layout: {},
onChange: () => {},
onSave: () => {},
refreshFrequency: 200,
setRefreshFrequency: () => {},
shouldPersistRefreshFrequency: true,
showPropertiesModal: () => {},
startPeriodicRender: () => {},
updateCss: () => {},
userCanEdit: false,
userCanSave: false,
lastModifiedTime: 0,
};
function setup(overrideProps) {
const wrapper = shallow(
<HeaderActionsDropdown {...props} {...overrideProps} />,
);
const menu = shallow(
<div>{wrapper.find(NoAnimationDropdown).props().overlay}</div>,
);
return { wrapper, menu };
}
describe('readonly-user', () => {
const overrideProps = { userCanSave: false, userCanShare: false };
it('should render the DropdownButton', () => {
const { wrapper } = setup(overrideProps);
expect(wrapper.find(NoAnimationDropdown)).toExist();
});
it('should not render the SaveModal', () => {
const { menu } = setup(overrideProps);
expect(menu.find(SaveModal)).not.toExist();
});
it('should render available Menu items', () => {
const { menu } = setup(overrideProps);
expect(menu.find(Menu.Item)).toHaveLength(4);
});
it('should render the RefreshIntervalModal', () => {
const { menu } = setup(overrideProps);
expect(menu.find(RefreshIntervalModal)).toExist();
});
it('should not render the ShareMenuItems', () => {
const { menu } = setup(overrideProps);
expect(menu.find(ShareMenuItems)).not.toExist();
});
it('should not render the CssEditor', () => {
const { menu } = setup(overrideProps);
expect(menu.find(CssEditor)).not.toExist();
});
});
describe('write-user', () => {
const overrideProps = { userCanSave: true, userCanShare: true };
it('should render the DropdownButton', () => {
const { wrapper } = setup(overrideProps);
expect(wrapper.find(NoAnimationDropdown)).toExist();
});
it('should render the SaveModal', () => {
const { menu } = setup(overrideProps);
expect(menu.find(SaveModal)).toExist();
});
it('should render available Menu items', () => {
const { menu } = setup(overrideProps);
expect(menu.find(Menu.Item)).toHaveLength(5);
});
it('should render the RefreshIntervalModal', () => {
const { menu } = setup(overrideProps);
expect(menu.find(RefreshIntervalModal)).toExist();
});
it('should render the ShareMenuItems', () => {
const { menu } = setup(overrideProps);
expect(menu.find(ShareMenuItems)).toExist();
});
it('should not render the CssEditor', () => {
const { menu } = setup(overrideProps);
expect(menu.find(CssEditor)).not.toExist();
});
});
describe('write-user-with-edit-mode', () => {
const overrideProps = {
userCanSave: true,
editMode: true,
userCanShare: true,
};
it('should render the DropdownButton', () => {
const { wrapper } = setup(overrideProps);
expect(wrapper.find(NoAnimationDropdown)).toExist();
});
it('should render the SaveModal', () => {
const { menu } = setup(overrideProps);
expect(menu.find(SaveModal)).toExist();
});
it('should render available MenuItems', () => {
const { menu } = setup(overrideProps);
expect(menu.find(Menu.Item)).toHaveLength(6);
});
it('should render the RefreshIntervalModal', () => {
const { menu } = setup(overrideProps);
expect(menu.find(RefreshIntervalModal)).toExist();
});
it('should render the ShareMenuItems', () => {
const { menu } = setup(overrideProps);
expect(menu.find(ShareMenuItems)).toExist();
});
it('should render the CssEditor', () => {
const { menu } = setup(overrideProps);
expect(menu.find(CssEditor)).toExist();
});
});
});

View File

@ -1,244 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { styledMount as mount } from 'spec/helpers/theming';
import Header from 'src/dashboard/components/Header';
import EditableTitle from 'src/components/EditableTitle';
import FaveStar from 'src/components/FaveStar';
import PublishedStatus from 'src/dashboard/components/PublishedStatus';
import HeaderActionsDropdown from 'src/dashboard/components/HeaderActionsDropdown';
import Button from 'src/components/Button';
import UndoRedoKeyListeners from 'src/dashboard/components/UndoRedoKeyListeners';
describe('Header', () => {
const props = {
addSuccessToast: () => {},
addDangerToast: () => {},
addWarningToast: () => {},
dashboardInfo: {
id: 1,
dash_edit_perm: true,
dash_save_perm: true,
userId: 1,
metadata: {},
common: {
conf: {},
},
},
dashboardTitle: 'title',
charts: {},
layout: {},
filters: {},
expandedSlices: {},
css: '',
customCss: '',
isStarred: false,
isLoading: false,
lastModifiedTime: 0,
refreshFrequency: 0,
shouldPersistRefreshFrequency: false,
onSave: () => {},
onChange: () => {},
fetchFaveStar: () => {},
fetchCharts: () => {},
saveFaveStar: () => {},
savePublished: () => {},
isPublished: false,
updateDashboardTitle: () => {},
editMode: false,
setEditMode: () => {},
showBuilderPane: () => {},
updateCss: () => {},
setColorSchemeAndUnsavedChanges: () => {},
logEvent: () => {},
setRefreshFrequency: () => {},
hasUnsavedChanges: false,
maxUndoHistoryExceeded: false,
// redux
onUndo: () => {},
onRedo: () => {},
undoLength: 0,
redoLength: 0,
setMaxUndoHistoryExceeded: () => {},
maxUndoHistoryToast: () => {},
dashboardInfoChanged: () => {},
dashboardTitleChanged: () => {},
};
function setup(overrideProps) {
const wrapper = mount(<Header {...props} {...overrideProps} />);
return wrapper;
}
describe('read-only-user', () => {
const overrideProps = {
dashboardInfo: {
...props.dashboardInfo,
id: 1,
dash_edit_perm: false,
dash_save_perm: false,
userId: 1,
},
};
it('should render the EditableTitle', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(EditableTitle)).toExist();
});
it('should render the PublishedStatus', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(PublishedStatus)).toExist();
});
it('should render the FaveStar', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(FaveStar)).toExist();
});
it('should render the HeaderActionsDropdown', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(HeaderActionsDropdown)).toExist();
});
it('should not set up undo/redo', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(UndoRedoKeyListeners)).not.toExist();
});
});
describe('write-user', () => {
const overrideProps = {
editMode: false,
dashboardInfo: {
...props.dashboardInfo,
id: 1,
dash_edit_perm: true,
dash_save_perm: true,
userId: 1,
},
};
it('should render the EditableTitle', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(EditableTitle)).toExist();
});
it('should render the PublishedStatus', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(PublishedStatus)).toExist();
});
it('should render the FaveStar', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(FaveStar)).toExist();
});
it('should render the HeaderActionsDropdown', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(HeaderActionsDropdown)).toExist();
});
it('should not set up undo/redo', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(UndoRedoKeyListeners)).not.toExist();
});
});
describe('write-user-with-edit-mode', () => {
const overrideProps = {
editMode: true,
dashboardInfo: {
...props.dashboardInfo,
id: 1,
dash_edit_perm: true,
dash_save_perm: true,
userId: 1,
},
};
it('should render the EditableTitle', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(EditableTitle)).toExist();
});
it('should render the FaveStar', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(FaveStar)).toExist();
});
it('should render the PublishedStatus', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(PublishedStatus)).toExist();
});
it('should render the HeaderActionsDropdown', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(HeaderActionsDropdown)).toExist();
});
it('should render five Buttons', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(Button)).toHaveLength(4);
});
it('should set up undo/redo', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(UndoRedoKeyListeners)).toExist();
});
});
describe('logged-out-user', () => {
const overrideProps = {
dashboardInfo: {
...props.dashboardInfo,
id: 1,
dash_edit_perm: false,
dash_save_perm: false,
userId: null,
},
};
it('should render the EditableTitle', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(EditableTitle)).toExist();
});
it('should render the PublishedStatus', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(PublishedStatus)).toExist();
});
it('should not render the FaveStar', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(FaveStar)).not.toExist();
});
it('should render the HeaderActionsDropdown', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(HeaderActionsDropdown)).toExist();
});
it('should not set up undo/redo', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(UndoRedoKeyListeners)).not.toExist();
});
});
});

View File

@ -0,0 +1,288 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen, fireEvent } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import fetchMock from 'fetch-mock';
import { HeaderProps } from './types';
import Header from '.';
const createProps = () => ({
addSuccessToast: jest.fn(),
addDangerToast: jest.fn(),
addWarningToast: jest.fn(),
dashboardInfo: {
id: 1,
dash_edit_perm: false,
dash_save_perm: false,
dash_share_perm: false,
userId: 1,
metadata: {},
common: {
conf: {},
},
},
dashboardTitle: 'Dashboard Title',
charts: {},
layout: {},
expandedSlices: {},
css: '',
customCss: '',
isStarred: false,
isLoading: false,
lastModifiedTime: 0,
refreshFrequency: 0,
shouldPersistRefreshFrequency: false,
onSave: jest.fn(),
onChange: jest.fn(),
fetchFaveStar: jest.fn(),
fetchCharts: jest.fn(),
saveFaveStar: jest.fn(),
savePublished: jest.fn(),
isPublished: false,
updateDashboardTitle: jest.fn(),
editMode: false,
setEditMode: jest.fn(),
showBuilderPane: jest.fn(),
updateCss: jest.fn(),
setColorSchemeAndUnsavedChanges: jest.fn(),
logEvent: jest.fn(),
setRefreshFrequency: jest.fn(),
hasUnsavedChanges: false,
maxUndoHistoryExceeded: false,
onUndo: jest.fn(),
onRedo: jest.fn(),
undoLength: 0,
redoLength: 0,
setMaxUndoHistoryExceeded: jest.fn(),
maxUndoHistoryToast: jest.fn(),
dashboardInfoChanged: jest.fn(),
dashboardTitleChanged: jest.fn(),
});
const props = createProps();
const editableProps = {
...props,
editMode: true,
dashboardInfo: {
...props.dashboardInfo,
dash_edit_perm: true,
dash_save_perm: true,
},
};
const undoProps = {
...editableProps,
undoLength: 1,
};
const redoProps = {
...editableProps,
redoLength: 1,
};
fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {});
function setup(props: HeaderProps) {
return (
<div className="dashboard">
<Header {...props} />
</div>
);
}
async function openActionsDropdown() {
const btn = screen.getByRole('img', { name: 'more-horiz' });
userEvent.click(btn);
expect(await screen.findByRole('menu')).toBeInTheDocument();
}
test('should render', () => {
const mockedProps = createProps();
const { container } = render(setup(mockedProps));
expect(container).toBeInTheDocument();
});
test('should render the title', () => {
const mockedProps = createProps();
render(setup(mockedProps));
expect(screen.getByText('Dashboard Title')).toBeInTheDocument();
});
test('should render the editable title', () => {
render(setup(editableProps));
expect(screen.getByDisplayValue('Dashboard Title')).toBeInTheDocument();
});
test('should edit the title', () => {
render(setup(editableProps));
const editableTitle = screen.getByDisplayValue('Dashboard Title');
expect(editableProps.onChange).not.toHaveBeenCalled();
userEvent.click(editableTitle);
userEvent.clear(editableTitle);
userEvent.type(editableTitle, 'New Title');
userEvent.click(document.body);
expect(editableProps.onChange).toHaveBeenCalled();
expect(screen.getByDisplayValue('New Title')).toBeInTheDocument();
});
test('should render the "Draft" status', () => {
const mockedProps = createProps();
render(setup(mockedProps));
expect(screen.getByText('Draft')).toBeInTheDocument();
});
test('should publish', () => {
render(setup(editableProps));
const draft = screen.getByText('Draft');
expect(editableProps.savePublished).not.toHaveBeenCalled();
userEvent.click(draft);
expect(editableProps.savePublished).toHaveBeenCalledTimes(1);
});
test('should render the "Undo" action as disabled', () => {
render(setup(editableProps));
expect(screen.getByTitle('Undo').parentElement).toBeDisabled();
});
test('should undo', () => {
render(setup(undoProps));
const undo = screen.getByTitle('Undo');
expect(undoProps.onUndo).not.toHaveBeenCalled();
userEvent.click(undo);
expect(undoProps.onUndo).toHaveBeenCalledTimes(1);
});
test('should undo with key listener', () => {
undoProps.onUndo.mockReset();
render(setup(undoProps));
expect(undoProps.onUndo).not.toHaveBeenCalled();
fireEvent.keyDown(document.body, { key: 'z', code: 'KeyZ', ctrlKey: true });
expect(undoProps.onUndo).toHaveBeenCalledTimes(1);
});
test('should render the "Redo" action as disabled', () => {
render(setup(editableProps));
expect(screen.getByTitle('Redo').parentElement).toBeDisabled();
});
test('should redo', () => {
render(setup(redoProps));
const redo = screen.getByTitle('Redo');
expect(redoProps.onRedo).not.toHaveBeenCalled();
userEvent.click(redo);
expect(redoProps.onRedo).toHaveBeenCalledTimes(1);
});
test('should redo with key listener', () => {
redoProps.onRedo.mockReset();
render(setup(redoProps));
expect(redoProps.onRedo).not.toHaveBeenCalled();
fireEvent.keyDown(document.body, { key: 'y', code: 'KeyY', ctrlKey: true });
expect(redoProps.onRedo).toHaveBeenCalledTimes(1);
});
test('should render the "Discard changes" button', () => {
render(setup(editableProps));
expect(screen.getByText('Discard changes')).toBeInTheDocument();
});
test('should render the "Save" button as disabled', () => {
render(setup(editableProps));
expect(screen.getByText('Save').parentElement).toBeDisabled();
});
test('should save', () => {
const unsavedProps = {
...editableProps,
hasUnsavedChanges: true,
};
render(setup(unsavedProps));
const save = screen.getByText('Save');
expect(unsavedProps.onSave).not.toHaveBeenCalled();
userEvent.click(save);
expect(unsavedProps.onSave).toHaveBeenCalledTimes(1);
});
test('should NOT render the "Draft" status', () => {
const mockedProps = createProps();
const publishedProps = {
...mockedProps,
isPublished: true,
};
render(setup(publishedProps));
expect(screen.queryByText('Draft')).not.toBeInTheDocument();
});
test('should render the unselected fave icon', () => {
const mockedProps = createProps();
render(setup(mockedProps));
expect(mockedProps.fetchFaveStar).toHaveBeenCalled();
expect(
screen.getByRole('img', { name: 'favorite-unselected' }),
).toBeInTheDocument();
});
test('should render the selected fave icon', () => {
const mockedProps = createProps();
const favedProps = {
...mockedProps,
isStarred: true,
};
render(setup(favedProps));
expect(
screen.getByRole('img', { name: 'favorite-selected' }),
).toBeInTheDocument();
});
test('should fave', async () => {
const mockedProps = createProps();
render(setup(mockedProps));
const fave = screen.getByRole('img', { name: 'favorite-unselected' });
expect(mockedProps.saveFaveStar).not.toHaveBeenCalled();
userEvent.click(fave);
expect(mockedProps.saveFaveStar).toHaveBeenCalledTimes(1);
});
test('should toggle the edit mode', () => {
const mockedProps = createProps();
const canEditProps = {
...mockedProps,
dashboardInfo: {
...mockedProps.dashboardInfo,
dash_edit_perm: true,
},
};
render(setup(canEditProps));
const editDashboard = screen.getByTitle('Edit dashboard');
expect(screen.queryByTitle('Edit dashboard')).toBeInTheDocument();
userEvent.click(editDashboard);
expect(mockedProps.logEvent).toHaveBeenCalled();
});
test('should render the dropdown icon', () => {
const mockedProps = createProps();
render(setup(mockedProps));
expect(screen.getByRole('img', { name: 'more-horiz' })).toBeInTheDocument();
});
test('should refresh the charts', async () => {
const mockedProps = createProps();
render(setup(mockedProps));
await openActionsDropdown();
userEvent.click(screen.getByText('Refresh dashboard'));
expect(mockedProps.fetchCharts).toHaveBeenCalledTimes(1);
});

View File

@ -0,0 +1,200 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import fetchMock from 'fetch-mock';
import { HeaderDropdownProps } from 'src/dashboard/components/Header/types';
import HeaderActionsDropdown from '.';
const createProps = () => ({
addSuccessToast: jest.fn(),
addDangerToast: jest.fn(),
customCss: '#save-dash-split-button{margin-left: 100px;}',
dashboardId: 1,
dashboardInfo: {
id: 1,
dash_edit_perm: true,
dash_save_perm: true,
userId: 1,
metadata: {},
common: {
conf: {},
},
},
dashboardTitle: 'Title',
editMode: false,
expandedSlices: {},
forceRefreshAllCharts: jest.fn(),
hasUnsavedChanges: false,
isLoading: false,
layout: {},
onChange: jest.fn(),
onSave: jest.fn(),
refreshFrequency: 200,
setRefreshFrequency: jest.fn(),
shouldPersistRefreshFrequency: false,
showPropertiesModal: jest.fn(),
startPeriodicRender: jest.fn(),
updateCss: jest.fn(),
userCanEdit: false,
userCanSave: false,
userCanShare: false,
lastModifiedTime: 0,
});
const editModeOnProps = {
...createProps(),
editMode: true,
};
function setup(props: HeaderDropdownProps) {
return (
<div className="dashboard">
<HeaderActionsDropdown {...props} />
</div>
);
}
fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {});
async function openDropdown() {
const btn = screen.getByRole('img', { name: 'more-horiz' });
userEvent.click(btn);
expect(await screen.findByRole('menu')).toBeInTheDocument();
}
test('should render', () => {
const mockedProps = createProps();
const { container } = render(setup(mockedProps));
expect(container).toBeInTheDocument();
});
test('should render the dropdown button', () => {
const mockedProps = createProps();
render(setup(mockedProps));
expect(screen.getByRole('button')).toBeInTheDocument();
});
test('should render the dropdown icon', () => {
const mockedProps = createProps();
render(setup(mockedProps));
expect(screen.getByRole('img', { name: 'more-horiz' })).toBeInTheDocument();
});
test('should open the dropdown', async () => {
const mockedProps = createProps();
render(setup(mockedProps));
await openDropdown();
expect(await screen.findByRole('menu')).toBeInTheDocument();
});
test('should render the menu items', async () => {
const mockedProps = createProps();
render(setup(mockedProps));
await openDropdown();
expect(screen.getAllByRole('menuitem')).toHaveLength(4);
expect(screen.getByText('Refresh dashboard')).toBeInTheDocument();
expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument();
expect(screen.getByText('Download as image')).toBeInTheDocument();
expect(screen.getByText('Toggle fullscreen')).toBeInTheDocument();
});
test('should render the menu items in edit mode', async () => {
render(setup(editModeOnProps));
await openDropdown();
expect(screen.getAllByRole('menuitem')).toHaveLength(5);
expect(screen.getByText('Refresh dashboard')).toBeInTheDocument();
expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument();
expect(screen.getByText('Set filter mapping')).toBeInTheDocument();
expect(screen.getByText('Edit dashboard properties')).toBeInTheDocument();
expect(screen.getByText('Edit CSS')).toBeInTheDocument();
});
test('should show the share actions', async () => {
const mockedProps = createProps();
const canShareProps = {
...mockedProps,
userCanShare: true,
};
render(setup(canShareProps));
await openDropdown();
expect(screen.getByText('Copy dashboard URL')).toBeInTheDocument();
expect(screen.getByText('Share dashboard by email')).toBeInTheDocument();
});
test('should render the "Save Modal" when user can save', async () => {
const mockedProps = createProps();
const canSaveProps = {
...mockedProps,
userCanSave: true,
};
render(setup(canSaveProps));
await openDropdown();
expect(screen.getByText('Save as')).toBeInTheDocument();
});
test('should NOT render the "Save Modal" menu item when user cannot save', async () => {
const mockedProps = createProps();
render(setup(mockedProps));
await openDropdown();
expect(screen.queryByText('Save as')).not.toBeInTheDocument();
});
test('should render the "Refresh dashboard" menu item as disabled when loading', async () => {
const mockedProps = createProps();
const loadingProps = {
...mockedProps,
isLoading: true,
};
render(setup(loadingProps));
await openDropdown();
expect(screen.getByText('Refresh dashboard')).toHaveClass(
'ant-dropdown-menu-item-disabled',
);
});
test('should NOT render the "Refresh dashboard" menu item as disabled', async () => {
const mockedProps = createProps();
render(setup(mockedProps));
await openDropdown();
expect(screen.getByText('Refresh dashboard')).not.toHaveClass(
'ant-dropdown-menu-item-disabled',
);
});
test('should render with custom css', () => {
const mockedProps = createProps();
render(setup(mockedProps));
expect(screen.getByRole('button')).toHaveStyle('margin-left: 100px');
});
test('should refresh the charts', async () => {
const mockedProps = createProps();
render(setup(mockedProps));
await openDropdown();
userEvent.click(screen.getByText('Refresh dashboard'));
expect(mockedProps.forceRefreshAllCharts).toHaveBeenCalledTimes(1);
});
test('should show the properties modal', async () => {
render(setup(editModeOnProps));
await openDropdown();
userEvent.click(screen.getByText('Edit dashboard properties'));
expect(editModeOnProps.showPropertiesModal).toHaveBeenCalledTimes(1);
});

View File

@ -25,16 +25,16 @@ import { Menu, NoAnimationDropdown } from 'src/common/components';
import Icon from 'src/components/Icon';
import { URL_PARAMS } from 'src/constants';
import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems';
import CssEditor from './CssEditor';
import RefreshIntervalModal from './RefreshIntervalModal';
import SaveModal from './SaveModal';
import injectCustomCss from '../util/injectCustomCss';
import { SAVE_TYPE_NEWDASHBOARD } from '../util/constants';
import FilterScopeModal from './filterscope/FilterScopeModal';
import downloadAsImage from '../../utils/downloadAsImage';
import getDashboardUrl from '../util/getDashboardUrl';
import { getActiveFilters } from '../util/activeDashboardFilters';
import { getUrlParam } from '../../utils/urlUtils';
import CssEditor from 'src/dashboard/components/CssEditor';
import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal';
import SaveModal from 'src/dashboard/components/SaveModal';
import injectCustomCss from 'src/dashboard/util/injectCustomCss';
import { SAVE_TYPE_NEWDASHBOARD } from 'src/dashboard/util/constants';
import FilterScopeModal from 'src/dashboard/components/filterscope/FilterScopeModal';
import downloadAsImage from 'src/utils/downloadAsImage';
import getDashboardUrl from 'src/dashboard/util/getDashboardUrl';
import { getActiveFilters } from 'src/dashboard/util/activeDashboardFilters';
import { getUrlParam } from 'src/utils/urlUtils';
const propTypes = {
addSuccessToast: PropTypes.func.isRequired,

View File

@ -34,19 +34,18 @@ import Button from 'src/components/Button';
import EditableTitle from 'src/components/EditableTitle';
import FaveStar from 'src/components/FaveStar';
import { safeStringify } from 'src/utils/safeStringify';
import HeaderActionsDropdown from 'src/dashboard/components/Header/HeaderActionsDropdown';
import PublishedStatus from 'src/dashboard/components/PublishedStatus';
import UndoRedoKeyListeners from 'src/dashboard/components/UndoRedoKeyListeners';
import PropertiesModal from 'src/dashboard/components/PropertiesModal';
import { chartPropShape } from 'src/dashboard/util/propShapes';
import HeaderActionsDropdown from './HeaderActionsDropdown';
import PublishedStatus from './PublishedStatus';
import UndoRedoKeyListeners from './UndoRedoKeyListeners';
import PropertiesModal from './PropertiesModal';
import {
UNDO_LIMIT,
SAVE_TYPE_OVERWRITE,
DASHBOARD_POSITION_DATA_LIMIT,
} from '../util/constants';
import setPeriodicRunner from '../util/setPeriodicRunner';
import { options as PeriodicRefreshOptions } from './RefreshIntervalModal';
} from 'src/dashboard/util/constants';
import setPeriodicRunner from 'src/dashboard/util/setPeriodicRunner';
import { options as PeriodicRefreshOptions } from 'src/dashboard/components/RefreshIntervalModal';
const propTypes = {
addSuccessToast: PropTypes.func.isRequired,

View File

@ -0,0 +1,98 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Layout } from 'src/dashboard/types';
import { ChartState } from 'src/explore/types';
interface DashboardInfo {
id: number;
userId: number;
dash_edit_perm: boolean;
dash_save_perm: boolean;
metadata?: Record<string, any>;
common?: { conf: Record<string, any> };
}
export interface HeaderDropdownProps {
addSuccessToast: () => void;
addDangerToast: () => void;
customCss: string;
colorNamespace?: string;
colorScheme?: string;
dashboardId: number;
dashboardInfo: DashboardInfo;
dashboardTitle: string;
editMode: boolean;
expandedSlices: Record<number, boolean>;
forceRefreshAllCharts: () => void;
hasUnsavedChanges: boolean;
isLoading: boolean;
layout: Layout;
onChange: () => void;
onSave: () => void;
refreshFrequency: number;
setRefreshFrequency: () => void;
shouldPersistRefreshFrequency: boolean;
showPropertiesModal: () => void;
startPeriodicRender: () => void;
updateCss: () => void;
userCanEdit: boolean;
userCanSave: boolean;
lastModifiedTime: number;
}
export interface HeaderProps {
addSuccessToast: () => void;
addDangerToast: () => void;
addWarningToast: () => void;
colorNamespace?: string;
charts: ChartState | {};
colorScheme?: string;
customCss: string;
dashboardInfo: DashboardInfo;
dashboardTitle: string;
setColorSchemeAndUnsavedChanges: () => void;
isStarred: boolean;
isPublished: boolean;
onChange: () => void;
onSave: () => void;
fetchFaveStar: () => void;
saveFaveStar: () => void;
savePublished: () => void;
updateDashboardTitle: () => void;
editMode: boolean;
setEditMode: () => void;
showBuilderPane: () => void;
updateCss: () => void;
logEvent: () => void;
hasUnsavedChanges: boolean;
maxUndoHistoryExceeded: boolean;
lastModifiedTime: number;
onUndo: () => void;
onRedo: () => void;
undoLength: number;
redoLength: number;
setMaxUndoHistoryExceeded: () => void;
maxUndoHistoryToast: () => void;
refreshFrequency: number;
shouldPersistRefreshFrequency: boolean;
setRefreshFrequency: () => void;
dashboardInfoChanged: () => void;
dashboardTitleChanged: () => void;
}