@ -1,23 +1,43 @@
import React from 'react' ;
import { shallow , ShallowWrapper } from 'enzyme' ;
import { UnthemedDashboardPage , mapStateToProps , Props , State } from './DashboardPage' ;
import { Provider } from 'react-redux' ;
import { render , screen } from '@testing-library/react' ;
import { UnthemedDashboardPage , Props } from './DashboardPage' ;
import { Router } from 'react-router-dom' ;
import { locationService } from '@grafana/runtime' ;
import { DashboardModel } from '../state' ;
import { configureStore } from '../../../store/configureStore' ;
import { mockToolkitActionCreator } from 'test/core/redux/mocks' ;
import { DashboardInitPhase , DashboardRoutes } from 'app/types' ;
import { notifyApp } from 'app/core/actions' ;
import { cleanUpDashboardAndVariables } from '../state/actions' ;
import { selectors } from '@grafana/e2e-selectors' ;
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps' ;
import { createTheme } from '@grafana/data' ;
jest . mock ( 'app/features/dashboard/components/DashboardSettings/GeneralSettings' , ( ) = > ( { } ) ) ;
jest . mock ( 'app/features/dashboard/components/DashboardSettings/GeneralSettings' , ( ) = > {
class GeneralSettings extends React . Component < { } , { } > {
render() {
return < > general settings < / > ;
}
}
return { GeneralSettings } ;
} ) ;
jest . mock ( 'app/core/core' , ( ) = > ( {
appEvents : {
subscribe : ( ) = > {
return { unsubscribe : ( ) = > { } } ;
} ,
} ,
} ) ) ;
interface ScenarioContext {
cleanUpDashboardAndVariablesMock : typeof cleanUpDashboardAndVariables ;
dashboard? : DashboardModel | null ;
setDashboardProp : ( overrides? : any , metaOverrides? : any ) = > void ;
wrapper? : ShallowWrapper < Props , State , UnthemedDashboardPage > ;
container? : HTMLElement ;
mount : ( propOverrides? : Partial < Props > ) = > void ;
unmount : ( ) = > void ;
props : Props ;
rerender : ( propOverrides? : Partial < Props > ) = > void ;
setup : ( fn : ( ) = > void ) = > void ;
}
@ -28,8 +48,8 @@ function getTestDashboard(overrides?: any, metaOverrides?: any): DashboardModel
panels : [
{
id : 1 ,
type : 'graph ' ,
title : 'My graph ' ,
type : 'timeseries ' ,
title : 'My panel title ' ,
gridPos : { x : 0 , y : 0 , w : 1 , h : 1 } ,
} ,
] ,
@ -46,15 +66,11 @@ function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioCo
let setupFn : ( ) = > void ;
const ctx : ScenarioContext = {
cleanUpDashboardAndVariablesMock : jest.fn ( ) ,
setup : ( fn ) = > {
setupFn = fn ;
} ,
setDashboardProp : ( overrides? : any , metaOverrides? : any ) = > {
ctx . dashboard = getTestDashboard ( overrides , metaOverrides ) ;
ctx . wrapper ? . setProps ( { dashboard : ctx.dashboard } ) ;
} ,
mount : ( propOverrides? : Partial < Props > ) = > {
const store = configureStore ( ) ;
const props : Props = {
. . . getRouteComponentProps ( {
match : { params : { slug : 'my-dash' , uid : '11' } } as any ,
@ -64,7 +80,7 @@ function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioCo
isInitSlow : false ,
initDashboard : jest.fn ( ) ,
notifyApp : mockToolkitActionCreator ( notifyApp ) ,
cleanUpDashboardAndVariables : ctx.cleanUpDashboardAndVariablesMock ,
cleanUpDashboardAndVariables : jest.fn ( ) ,
cancelVariables : jest.fn ( ) ,
templateVarsChangedInUrl : jest.fn ( ) ,
dashboard : null ,
@ -73,9 +89,36 @@ function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioCo
Object . assign ( props , propOverrides ) ;
ctx . props = props ;
ctx . dashboard = props . dashboard ;
ctx . wrapper = shallow ( < UnthemedDashboardPage { ...props } / > ) ;
const { container , rerender , unmount } = render (
< Provider store = { store } >
< Router history = { locationService . getHistory ( ) } >
< UnthemedDashboardPage { ...props } / >
< / Router >
< / Provider >
) ;
ctx . container = container ;
ctx . rerender = ( newProps? : Partial < Props > ) = > {
Object . assign ( props , newProps ) ;
rerender (
< Provider store = { store } >
< Router history = { locationService . getHistory ( ) } >
< UnthemedDashboardPage { ...props } / >
< / Router >
< / Provider >
) ;
} ;
ctx . unmount = unmount ;
} ,
props : { } as Props ,
rerender : ( ) = > { } ,
unmount : ( ) = > { } ,
} ;
beforeEach ( ( ) = > {
@ -92,226 +135,140 @@ describe('DashboardPage', () => {
ctx . mount ( ) ;
} ) ;
it ( 'Should render nothing' , ( ) = > {
expect ( ctx . wrapper ) . toMatchSnapshot ( ) ;
it ( 'Should call initDashboard on mount' , ( ) = > {
expect ( ctx . props . initDashboard ) . toBeCalledWith ( {
fixUrl : true ,
routeName : 'normal-dashboard' ,
urlSlug : 'my-dash' ,
urlUid : '11' ,
} ) ;
expect ( ctx . container ) . toBeEmptyDOMElement ( ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'Dashboard is fetching slowly' , ( ctx ) = > {
dashboardPageScenario ( 'Given dashboard slow loading state ' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . wrapper ? . setProps ( {
isInitSlow : true ,
initPhase : DashboardInitPhase.Fetching ,
} ) ;
ctx . rerender ( { isInitSlow : true } ) ;
} ) ;
it ( 'Should render slow init state ' , ( ) = > {
expect ( ctx . wrapper ) . toMatchSnapsho t( ) ;
it ( 'Should show spinner' , ( ) = > {
expect ( screen . getByText ( 'Cancel loading dashboard' ) ) . toBeInTheDocumen t( ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'Dashboard init completed ' , ( ctx ) = > {
dashboardPageScenario ( 'Given a simple dashboard ' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . s etDashboardProp ( ) ;
ctx . rerender ( { dashboard : g etTes tDashboard( ) } ) ;
} ) ;
it ( 'Should update title ' , ( ) = > {
expect ( document . title ) . toBe ( 'My dashboard - Grafana' ) ;
it ( 'Should render panels ' , ( ) = > {
expect ( screen . getByText ( 'My panel title' ) ) . toBeInTheDocument ( ) ;
} ) ;
it ( 'Should render dashboard grid ' , ( ) = > {
expect ( ctx . wrapper ) . toMatchSnapshot ( ) ;
it ( 'Should update title ' , ( ) = > {
expect ( document . title ) . toBe ( 'My dashboard - Grafana' ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'When user goes into panel edit ' , ( ctx ) = > {
dashboardPageScenario ( 'When going into view mode ' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . setDashboardProp ( ) ;
ctx . wrapper ? . setProps ( {
queryParams : { editPanel : '1' } ,
ctx . mount ( {
dashboard : getTestDashboard ( ) ,
queryParams : { viewPanel : '1' } ,
} ) ;
} ) ;
it ( 'Should update component state to fullscreen and edit' , ( ) = > {
const state = ctx . wrapper ? . state ( ) ;
expect ( state ) . not . toBe ( null ) ;
expect ( state ? . editPanel ) . toBeDefined ( ) ;
it ( 'Should render panel in view mode' , ( ) = > {
expect ( ctx . dashboard ? . panelInView ) . toBeDefined ( ) ;
expect ( ctx . dashboard ? . panels [ 0 ] . isViewing ) . toBe ( true ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'When user goes into panel edit but has no edit permissions' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . setDashboardProp ( { } , { canEdit : false } ) ;
ctx . wrapper ? . setProps ( {
queryParams : { editPanel : '1' } ,
} ) ;
} ) ;
it ( 'Should reset state when leaving' , ( ) = > {
ctx . rerender ( { queryParams : { } } ) ;
it ( 'Should update component state to fullscreen and edit' , ( ) = > {
const state = ctx . wrapper ? . state ( ) ;
expect ( state ? . editPanel ) . toBe ( null ) ;
expect ( ctx . dashboard ? . panelInView ) . toBeUndefined ( ) ;
expect ( ctx . dashboard ? . panels [ 0 ] . isViewing ) . toBe ( false ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'When user goes back to dashboard from edit panel' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . setDashboardProp ( ) ;
ctx . wrapper ? . setState ( { scrollTop : 100 } ) ;
ctx . wrapper ? . setProps ( {
queryParams : { editPanel : '1' } ,
} ) ;
ctx . wrapper ? . setProps ( {
queryParams : { } ,
} ) ;
} ) ;
it ( 'Should update model state normal state' , ( ) = > {
expect ( ctx . dashboard ) . toBeDefined ( ) ;
// @ts-ignore typescript doesn't understand that dashboard must be defined to reach the row below
expect ( ctx . dashboard . panelInEdit ) . toBeUndefined ( ) ;
} ) ;
it ( 'Should update component state to normal and restore scrollTop' , ( ) = > {
const state = ctx . wrapper ? . state ( ) ;
expect ( ctx . wrapper ) . not . toBe ( null ) ;
expect ( state ) . not . toBe ( null ) ;
expect ( state ? . editPanel ) . toBe ( null ) ;
expect ( state ? . scrollTop ) . toBe ( 100 ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'When dashboard has editview url state' , ( ctx ) = > {
dashboardPageScenario ( 'When going into edit mode' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . setDashboardProp ( ) ;
ctx . wrapper ? . setProps ( {
queryParams : { editview : 'settings' } ,
ctx . mount ( {
dashboard : getTestDashboard ( ) ,
queryParams : { editPanel : '1' } ,
} ) ;
} ) ;
it ( 'should render settings view' , ( ) = > {
expect ( ctx . wrapper ) . toMatchSnapshot ( ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'When adding panel' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . setDashboardProp ( ) ;
ctx . wrapper ? . setState ( { scrollTop : 100 } ) ;
ctx . wrapper ? . instance ( ) . onAddPanel ( ) ;
it ( 'Should render panel in edit mode' , ( ) = > {
expect ( ctx . dashboard ? . panelInEdit ) . toBeDefined ( ) ;
} ) ;
it ( 'should set scrollTop to 0' , ( ) = > {
expect ( ctx . wrapper ) . not . toBe ( null ) ;
expect ( ctx . wrapper ? . state ( ) ) . not . toBe ( null ) ;
expect ( ctx . wrapper ? . state ( ) . updateScrollTop ) . toBe ( 0 ) ;
it ( 'Should render panel editor' , ( ) = > {
expect ( screen . getByTitle ( 'Apply changes and go back to dashboard' ) ) . toBeInTheDocument ( ) ;
} ) ;
it ( 'should add panel widget to dashboard panels' , ( ) = > {
expect ( ctx . dashboard ) . not . toBe ( null ) ;
expect ( ctx . dashboard ? . panels [ 0 ] . type ) . toBe ( 'add-panel' ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'Given panel with id 0' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . setDashboardProp ( {
panels : [ { id : 0 , type : 'graph' } ] ,
schemaVersion : 17 ,
} ) ;
ctx . wrapper ? . setProps ( {
queryParams : { editPanel : '0' } ,
} ) ;
} ) ;
it ( 'Should go into edit mode' , ( ) = > {
const state = ctx . wrapper ? . state ( ) ;
expect ( ctx . wrapper ) . not . toBe ( null ) ;
expect ( state ) . not . toBe ( null ) ;
expect ( state ? . editPanel ) . not . toBe ( null ) ;
it ( 'Should reset state when leaving' , ( ) = > {
ctx . rerender ( { queryParams : { } } ) ;
expect ( screen . queryByTitle ( 'Apply changes and go back to dashboard' ) ) . not . toBeInTheDocument ( ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'When dashboard unmounts' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( ) ;
ctx . setDashboardProp ( {
panels : [ { id : 0 , type : 'graph' } ] ,
schemaVersion : 17 ,
} ) ;
ctx . wrapper ? . unmount ( ) ;
ctx . rerender ( { dashboard : getTestDashboard ( ) } ) ;
ctx . unmount ( ) ;
} ) ;
it ( 'Should call clean up action' , ( ) = > {
expect ( ctx . cleanUpDashboardAndVariablesMock ) . toHaveBeenCalledTimes ( 1 ) ;
it ( 'Should call close action' , ( ) = > {
expect ( ctx . props . cleanUpDashboardAndVariables ) . toHaveBeenCalledTimes ( 1 ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'Kiosk mode none ' , ( ctx ) = > {
dashboardPageScenario ( 'When dashboard changes' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( {
queryParams : { } ,
} ) ;
ctx . setDashboardProp ( {
panels : [ { id : 0 , type : 'graph' } ] ,
schemaVersion : 17 ,
ctx . mount ( ) ;
ctx . rerender ( { dashboard : getTestDashboard ( ) } ) ;
ctx . rerender ( {
match : {
params : { uid : 'new-uid' } ,
} as any ,
dashboard : getTestDashboard ( { title : 'Another dashboard' } ) ,
} ) ;
} ) ;
it ( 'should not render dashboard navigation ' , ( ) = > {
expect ( ctx . wrapper ? . find ( ` [aria-label=" ${ selectors . pages . Dashboard . DashNav . nav } "] ` ) ) . toHaveLength ( 1 ) ;
expect ( ctx . wrapper ? . find ( ` [aria-label=" ${ selectors . pages . Dashboard . SubMenu . submenu } "] ` ) ) . toHaveLength ( 1 ) ;
it ( 'Should call clean up action and init ' , ( ) = > {
expect ( ctx . props . cleanUpDashboardAndVariables ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( ctx . props . initDashboard ) . toHaveBeenCalledTimes ( 2 ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'K iosk mode tv' , ( ctx ) = > {
dashboardPageScenario ( 'No k iosk mode tv' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( {
queryParams : { kiosk : 'tv' } ,
} ) ;
ctx . setDashboardProp ( {
panels : [ { id : 0 , type : 'graph' } ] ,
schemaVersion : 17 ,
} ) ;
ctx . mount ( { dashboard : getTestDashboard ( ) } ) ;
ctx . rerender ( { dashboard : ctx.dashboard } ) ;
} ) ;
it ( 'should not render dashboard navigation ' , ( ) = > {
expect ( ctx . wrapper ? . find ( ` [aria-label=" ${ selectors . pages . Dashboard . DashNav . nav } "] ` ) ) . toHaveLength ( 1 ) ;
expect ( ctx . wrapper ? . find ( ` [aria-label=" ${ selectors . pages . Dashboard . SubMenu . submenu } "] ` ) ) . toHaveLength ( 0 ) ;
it ( 'should render dashboard page toolbar and submenu' , ( ) = > {
expect ( screen . queryAllByLabelText ( selectors . pages . Dashboard . DashNav . nav ) ) . toHaveLength ( 1 ) ;
expect ( screen . queryAllByLabelText ( selectors . pages . Dashboard . SubMenu . submenu ) ) . toHaveLength ( 1 ) ;
} ) ;
} ) ;
dashboardPageScenario ( 'Kiosk mode full ' , ( ctx ) = > {
dashboardPageScenario ( 'When in full kiosk mode ' , ( ctx ) = > {
ctx . setup ( ( ) = > {
ctx . mount ( {
queryParams : { kiosk : true } ,
dashboard : getTestDashboard ( ) ,
} ) ;
ctx . setDashboardProp ( {
panels : [ { id : 0 , type : 'graph' } ] ,
schemaVersion : 17 ,
} ) ;
ctx . rerender ( { dashboard : ctx.dashboard } ) ;
} ) ;
it ( 'should not render dashboard navigation and submenu' , ( ) = > {
expect ( ctx . wrapper ? . find ( ` [aria-label=" ${ selectors . pages . Dashboard . DashNav . nav } "] ` ) ) . toHaveLength ( 0 ) ;
expect ( ctx . wrapper ? . find ( ` [aria-label=" ${ selectors . pages . Dashboard . SubMenu . submenu } "] ` ) ) . toHaveLength ( 0 ) ;
it ( 'should not render page toolbar and submenu' , ( ) = > {
expect ( screen . queryAllByLabelText ( selectors . pages . Dashboard . DashNav . nav ) ) . toHaveLength ( 0 ) ;
expect ( screen . queryAllByLabelText ( selectors . pages . Dashboard . SubMenu . submenu ) ) . toHaveLength ( 0 ) ;
} ) ;
} ) ;
describe ( 'mapStateToProps' , ( ) = > {
const props = mapStateToProps ( {
panelEditor : { } ,
dashboard : {
getModel : ( ) = > ( { } as DashboardModel ) ,
} ,
} as any ) ;
expect ( props . dashboard ) . toBeDefined ( ) ;
} ) ;
} ) ;