diff --git a/public/app/features/dashboard/containers/DashboardPage.test.tsx b/public/app/features/dashboard/containers/DashboardPage.test.tsx index 7ef4918aa90..41b09a7c361 100644 --- a/public/app/features/dashboard/containers/DashboardPage.test.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.test.tsx @@ -10,117 +10,205 @@ jest.mock('sass/_variables.scss', () => ({ panelVerticalPadding: 10, })); -jest.mock('app/features/dashboard/components/DashboardSettings/SettingsCtrl', () => ({ -})); +jest.mock('app/features/dashboard/components/DashboardSettings/SettingsCtrl', () => ({})); -function setup(propOverrides?: Partial): ShallowWrapper { - const props: Props = { - urlUid: '11', - urlSlug: 'my-dash', - $scope: {}, - $injector: {}, - routeInfo: DashboardRouteInfo.Normal, - urlEdit: false, - urlFullscreen: false, - initPhase: DashboardInitPhase.Completed, - isInitSlow: false, - initDashboard: jest.fn(), - updateLocation: jest.fn(), - notifyApp: jest.fn(), - dashboard: null, - cleanUpDashboard: cleanUpDashboard, - }; +interface ScenarioContext { + dashboard?: DashboardModel; + setDashboardProp: (overrides?: any, metaOverrides?: any) => void; + wrapper?: ShallowWrapper; + mount: (propOverrides?: Partial) => void; + setup?: (fn: () => void) => void; +} - Object.assign(props, propOverrides); - return shallow(); +function getTestDashboard(overrides?: any, metaOverrides?: any): DashboardModel { + const data = Object.assign({ + title: 'My dashboard', + panels: [ + { + id: 1, + type: 'graph', + title: 'My graph', + gridPos: { x: 0, y: 0, w: 1, h: 1 }, + }, + ], + }, overrides); + + const meta = Object.assign({ canSave: true, canEdit: true }, metaOverrides); + return new DashboardModel(data, meta); +} + +function dashboardPageScenario(description, scenarioFn: (ctx: ScenarioContext) => void) { + describe(description, () => { + let setupFn: () => void; + + const ctx: ScenarioContext = { + setup: fn => { + setupFn = fn; + }, + setDashboardProp: (overrides?: any, metaOverrides?: any) => { + ctx.dashboard = getTestDashboard(overrides, metaOverrides); + ctx.wrapper.setProps({ dashboard: ctx.dashboard }); + }, + mount: (propOverrides?: Partial) => { + const props: Props = { + urlSlug: 'my-dash', + $scope: {}, + urlUid: '11', + $injector: {}, + routeInfo: DashboardRouteInfo.Normal, + urlEdit: false, + urlFullscreen: false, + initPhase: DashboardInitPhase.NotStarted, + isInitSlow: false, + initDashboard: jest.fn(), + updateLocation: jest.fn(), + notifyApp: jest.fn(), + cleanUpDashboard: cleanUpDashboard, + dashboard: null, + }; + + Object.assign(props, propOverrides); + + ctx.dashboard = props.dashboard; + ctx.wrapper = shallow(); + } + }; + + beforeEach(() => { + setupFn(); + }); + + scenarioFn(ctx); + }); } describe('DashboardPage', () => { - let wrapper: ShallowWrapper; - beforeEach(() => { - wrapper = setup(); - }); + dashboardPageScenario("Given initial state", (ctx) => { + ctx.setup(() => { + ctx.mount(); + }); - describe('Given dashboard has not loaded yet', () => { it('should render nothing', () => { - expect(wrapper).toMatchSnapshot(); + expect(ctx.wrapper).toMatchSnapshot(); }); }); - describe('Given dashboard model', () => { - let dashboard: DashboardModel; - - beforeEach(() => { - dashboard = new DashboardModel({ - title: 'My dashboard', - panels: [ - { - id: 1, - type: 'graph', - title: 'My graph', - gridPos: { x: 0, y: 0, w: 1, h: 1 } - } - ] - }, { - canEdit: true, - canSave: true, + dashboardPageScenario("Dashboard is fetching slowly", (ctx) => { + ctx.setup(() => { + ctx.mount(); + ctx.wrapper.setProps({ + isInitSlow: true, + initPhase: DashboardInitPhase.Fetching, }); - wrapper.setProps({ dashboard, initPhase: DashboardInitPhase.Completed }); + }); + + it('should render slow init state', () => { + expect(ctx.wrapper).toMatchSnapshot(); + }); + }); + + dashboardPageScenario("Dashboard init completed ", (ctx) => { + ctx.setup(() => { + ctx.mount(); + ctx.setDashboardProp(); }); it('Should update title', () => { expect(document.title).toBe('My dashboard - Grafana'); }); - it('After render dashboard', () => { - expect(wrapper).toMatchSnapshot(); + it('Should render dashboard grid', () => { + expect(ctx.wrapper).toMatchSnapshot(); + }); + }); + + dashboardPageScenario("where user goes into panel edit", (ctx) => { + ctx.setup(() => { + ctx.mount(); + ctx.setDashboardProp(); + ctx.wrapper.setProps({ + urlFullscreen: true, + urlEdit: true, + urlPanelId: '1', + }); }); - describe('Given user has scrolled down and goes into fullscreen edit', () => { - beforeEach(() => { - wrapper.setState({ scrollTop: 100 }); - wrapper.setProps({ - urlFullscreen: true, - urlEdit: true, - urlPanelId: '1', - }); + it('Should update model state to fullscreen & edit', () => { + expect(ctx.dashboard.meta.fullscreen).toBe(true); + expect(ctx.dashboard.meta.isEditing).toBe(true); + }); + + it('Should update component state to fullscreen and edit', () => { + const state = ctx.wrapper.state(); + expect(state.isEditing).toBe(true); + expect(state.isFullscreen).toBe(true); + expect(state.rememberScrollTop).toBe(100); + }); + }); + + dashboardPageScenario("where user goes back to dashboard from panel edit", (ctx) => { + ctx.setup(() => { + ctx.mount(); + ctx.setDashboardProp(); + ctx.wrapper.setState({ scrollTop: 100 }); + ctx.wrapper.setProps({ + urlFullscreen: true, + urlEdit: true, + urlPanelId: '1', }); - - it('Should update model state to fullscreen & edit', () => { - expect(dashboard.meta.fullscreen).toBe(true); - expect(dashboard.meta.isEditing).toBe(true); + ctx.wrapper.setProps({ + urlFullscreen: false, + urlEdit: false, + urlPanelId: null, }); + }); - it('Should update component state to fullscreen and edit', () => { - const state = wrapper.state(); - expect(state.isEditing).toBe(true); - expect(state.isFullscreen).toBe(true); - expect(state.rememberScrollTop).toBe(100); + it('Should update model state normal state', () => { + expect(ctx.dashboard.meta.fullscreen).toBe(false); + expect(ctx.dashboard.meta.isEditing).toBe(false); + }); + + it('Should update component state to normal and restore scrollTop', () => { + const state = ctx.wrapper.state(); + expect(state.isEditing).toBe(false); + expect(state.isFullscreen).toBe(false); + expect(state.scrollTop).toBe(100); + }); + }); + + dashboardPageScenario("When dashboard has editview url state", (ctx) => { + ctx.setup(() => { + ctx.mount(); + ctx.setDashboardProp(); + ctx.wrapper.setProps({ + editview: 'settings', }); + }); - describe('Given user goes back to dashboard', () => { - beforeEach(() => { - wrapper.setState({ scrollTop: 0 }); - wrapper.setProps({ - urlFullscreen: false, - urlEdit: false, - urlPanelId: null, - }); - }); + it('should render settings view', () => { + expect(ctx.wrapper).toMatchSnapshot(); + }); - it('Should update model state normal state', () => { - expect(dashboard.meta.fullscreen).toBe(false); - expect(dashboard.meta.isEditing).toBe(false); - }); + it('should set animation state', () => { + expect(ctx.wrapper.state().isSettingsOpening).toBe(true); + }); + }); - it('Should update component state to normal and restore scrollTop', () => { - const state = wrapper.state(); - expect(state.isEditing).toBe(false); - expect(state.isFullscreen).toBe(false); - expect(state.scrollTop).toBe(100); - }); - }); + dashboardPageScenario("When adding panel", (ctx) => { + ctx.setup(() => { + ctx.mount(); + ctx.setDashboardProp(); + ctx.wrapper.setState({ scrollTop: 100 }); + ctx.wrapper.instance().onAddPanel(); + }); + + it('should set scrollTop to 0', () => { + expect(ctx.wrapper.state().scrollTop).toBe(0); + }); + + it('should add panel widget to dashboard panels', () => { + expect(ctx.dashboard.panels[0].type).toBe('add-panel'); }); }); }); diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index 0773d85b368..4e059e9ee6c 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -90,8 +90,8 @@ export class DashboardPage extends PureComponent { } componentWillUnmount() { + console.log('unmount', this.props.cleanUpDashboard); if (this.props.dashboard) { - this.props.dashboard.destroy(); this.props.cleanUpDashboard(); } } diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap index d3808513e7b..c1aa5972b6d 100644 --- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap +++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap @@ -1,8 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DashboardPage Given dashboard has not loaded yet should render nothing 1`] = `""`; - -exports[`DashboardPage Given dashboard model After render dashboard 1`] = ` +exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`] = `
@@ -218,3 +216,331 @@ exports[`DashboardPage Given dashboard model After render dashboard 1`] = `
`; + +exports[`DashboardPage Dashboard is fetching slowly should render slow init state 1`] = ` +
+
+ + + Fetching +
+
+`; + +exports[`DashboardPage Given initial state should render nothing 1`] = `""`; + +exports[`DashboardPage When dashboard has editview url state should render settings view 1`] = ` +
+ +
+ + +
+ +
+
+
+
+`; diff --git a/public/app/features/dashboard/state/reducers.ts b/public/app/features/dashboard/state/reducers.ts index ecf34f2f1a3..7a320beee93 100644 --- a/public/app/features/dashboard/state/reducers.ts +++ b/public/app/features/dashboard/state/reducers.ts @@ -70,7 +70,9 @@ export const dashboardReducer = reducerFactory(initialState) .addMapper({ filter: cleanUpDashboard, mapper: (state, action) => { - // tear down current dashboard + + // Destroy current DashboardModel + // Very important as this removes all dashboard event listeners state.model.destroy(); return {