/** * 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 { shallow } from 'enzyme'; import sinon from 'sinon'; 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 injectCustomCss from 'src/dashboard/util/injectCustomCss'; import { HeaderActionsDropdown } from '.'; const createProps = (): HeaderDropdownProps => ({ addSuccessToast: jest.fn(), addDangerToast: jest.fn(), customCss: '.ant-menu {margin-left: 100px;}', dashboardId: 1, dashboardInfo: { id: 1, dash_edit_perm: true, dash_save_perm: true, userId: '1', metadata: {}, common: { conf: { DASHBOARD_AUTO_REFRESH_INTERVALS: [ [0, "Don't refresh"], [10, '10 seconds'], ], }, }, }, 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, userCanCurate: false, lastModifiedTime: 0, isDropdownVisible: true, setIsDropdownVisible: jest.fn(), directPathToChild: [], manageEmbedded: jest.fn(), dataMask: {}, logEvent: jest.fn(), refreshLimit: 0, refreshWarning: '', }); const editModeOnProps = { ...createProps(), editMode: true, }; const editModeOnWithFilterScopesProps = { ...editModeOnProps, dashboardInfo: { ...editModeOnProps.dashboardInfo, metadata: { filter_scopes: { '1': { scopes: ['ROOT_ID'], immune: [] }, }, }, }, }; const guestUserProps = { ...createProps(), dashboardInfo: { ...createProps().dashboardInfo, userId: undefined, }, }; function setup(props: HeaderDropdownProps) { return render(
, { useRedux: true }, ); } fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {}); test('should render', () => { const mockedProps = createProps(); const { container } = setup(mockedProps); expect(container).toBeInTheDocument(); }); test('should render the Download dropdown button when not in edit mode', () => { const mockedProps = createProps(); setup(mockedProps); expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument(); }); test('should render the menu items', async () => { const mockedProps = createProps(); setup(mockedProps); expect(screen.getAllByRole('menuitem')).toHaveLength(4); expect(screen.getByText('Refresh dashboard')).toBeInTheDocument(); expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument(); expect(screen.getByText('Enter fullscreen')).toBeInTheDocument(); expect(screen.getByText('Download')).toBeInTheDocument(); }); test('should render the menu items in edit mode', async () => { setup(editModeOnProps); expect(screen.getAllByRole('menuitem')).toHaveLength(4); expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument(); expect(screen.getByText('Edit properties')).toBeInTheDocument(); expect(screen.getByText('Edit CSS')).toBeInTheDocument(); expect(screen.getByText('Download')).toBeInTheDocument(); }); test('should render the menu items in Embedded mode', async () => { setup(guestUserProps); expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getByText('Refresh dashboard')).toBeInTheDocument(); expect(screen.getByText('Download')).toBeInTheDocument(); expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument(); }); test('should not render filter mapping in edit mode if explicit filter scopes undefined', async () => { setup(editModeOnProps); expect(screen.queryByText('Set filter mapping')).not.toBeInTheDocument(); }); test('should render filter mapping in edit mode if explicit filter scopes defined', async () => { setup(editModeOnWithFilterScopesProps); expect(screen.getByText('Set filter mapping')).toBeInTheDocument(); }); test('should show the share actions', async () => { const mockedProps = createProps(); const canShareProps = { ...mockedProps, userCanShare: true, }; setup(canShareProps); expect(screen.getByText('Share')).toBeInTheDocument(); }); test('should render the "Save as" menu item when user can save', async () => { const mockedProps = createProps(); const canSaveProps = { ...mockedProps, userCanSave: true, }; setup(canSaveProps); expect(screen.getByText('Save as')).toBeInTheDocument(); }); test('should NOT render the "Save as" menu item when user cannot save', async () => { const mockedProps = createProps(); setup(mockedProps); 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, }; setup(loadingProps); expect(screen.getByText('Refresh dashboard')).toHaveClass( 'ant-menu-item-disabled', ); }); test('should NOT render the "Refresh dashboard" menu item as disabled', async () => { const mockedProps = createProps(); setup(mockedProps); expect(screen.getByText('Refresh dashboard')).not.toHaveClass( 'ant-menu-item-disabled', ); }); test('should render with custom css', () => { const mockedProps = createProps(); const { customCss } = mockedProps; setup(mockedProps); injectCustomCss(customCss); expect(screen.getByTestId('header-actions-menu')).toHaveStyle( 'margin-left: 100px', ); }); test('should refresh the charts', async () => { const mockedProps = createProps(); setup(mockedProps); userEvent.click(screen.getByText('Refresh dashboard')); expect(mockedProps.forceRefreshAllCharts).toHaveBeenCalledTimes(1); expect(mockedProps.addSuccessToast).toHaveBeenCalledTimes(1); }); test('should show the properties modal', async () => { setup(editModeOnProps); userEvent.click(screen.getByText('Edit properties')); expect(editModeOnProps.showPropertiesModal).toHaveBeenCalledTimes(1); }); describe('UNSAFE_componentWillReceiveProps', () => { let wrapper: any; const mockedProps = createProps(); const props = { ...mockedProps, customCss: '' }; beforeEach(() => { wrapper = shallow(); wrapper.setState({ css: props.customCss }); sinon.spy(wrapper.instance(), 'setState'); }); afterEach(() => { wrapper.instance().setState.restore(); }); it('css should update state and inject custom css', () => { wrapper.instance().UNSAFE_componentWillReceiveProps({ ...props, customCss: mockedProps.customCss, }); expect(wrapper.instance().setState.calledOnce).toBe(true); const stateKeys = Object.keys(wrapper.instance().setState.lastCall.args[0]); expect(stateKeys).toContain('css'); }); });