blob: da527571e0de736a18ae61eb63308cd9d847dc29 [file] [log] [blame]
/**
* 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: {},
},
},
userId: 1,
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(),
onRefresh: 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 NOT render the fave icon on anonymous user', () => {
const mockedProps = createProps();
const anonymousUserProps = {
...mockedProps,
userId: undefined,
};
render(setup(anonymousUserProps));
expect(mockedProps.fetchFaveStar).not.toHaveBeenCalled();
expect(() =>
screen.getByRole('img', { name: 'favorite-unselected' }),
).toThrowError('Unable to find');
expect(() =>
screen.getByRole('img', { name: 'favorite-selected' }),
).toThrowError('Unable to find');
});
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.onRefresh).toHaveBeenCalledTimes(1);
});