Improved dashboard page test
This commit is contained in:
@@ -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<Props>): ShallowWrapper<Props, State, DashboardPage> {
|
||||
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<Props, State, DashboardPage>;
|
||||
mount: (propOverrides?: Partial<Props>) => void;
|
||||
setup?: (fn: () => void) => void;
|
||||
}
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
return shallow(<DashboardPage {...props} />);
|
||||
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<Props>) => {
|
||||
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(<DashboardPage {...props} />);
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupFn();
|
||||
});
|
||||
|
||||
scenarioFn(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
describe('DashboardPage', () => {
|
||||
let wrapper: ShallowWrapper<Props, State, DashboardPage>;
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,8 +90,8 @@ export class DashboardPage extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
console.log('unmount', this.props.cleanUpDashboard);
|
||||
if (this.props.dashboard) {
|
||||
this.props.dashboard.destroy();
|
||||
this.props.cleanUpDashboard();
|
||||
}
|
||||
}
|
||||
|
||||
+329
-3
@@ -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`] = `
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
@@ -218,3 +216,331 @@ exports[`DashboardPage Given dashboard model After render dashboard 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`DashboardPage Dashboard is fetching slowly should render slow init state 1`] = `
|
||||
<div
|
||||
className="dashboard-loading"
|
||||
>
|
||||
<div
|
||||
className="dashboard-loading__text"
|
||||
>
|
||||
<i
|
||||
className="fa fa-spinner fa-spin"
|
||||
/>
|
||||
|
||||
Fetching
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`DashboardPage Given initial state should render nothing 1`] = `""`;
|
||||
|
||||
exports[`DashboardPage When dashboard has editview url state should render settings view 1`] = `
|
||||
<div
|
||||
className="dashboard-page--settings-opening"
|
||||
>
|
||||
<Connect(DashNav)
|
||||
$injector={Object {}}
|
||||
dashboard={
|
||||
DashboardModel {
|
||||
"annotations": Object {
|
||||
"list": Array [
|
||||
Object {
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard",
|
||||
},
|
||||
],
|
||||
},
|
||||
"autoUpdate": undefined,
|
||||
"description": undefined,
|
||||
"editable": true,
|
||||
"events": Emitter {
|
||||
"emitter": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
},
|
||||
},
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": Array [],
|
||||
"meta": Object {
|
||||
"canEdit": true,
|
||||
"canMakeEditable": false,
|
||||
"canSave": true,
|
||||
"canShare": true,
|
||||
"canStar": true,
|
||||
"fullscreen": false,
|
||||
"isEditing": false,
|
||||
"showSettings": true,
|
||||
},
|
||||
"originalTemplating": Array [],
|
||||
"originalTime": Object {
|
||||
"from": "now-6h",
|
||||
"to": "now",
|
||||
},
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"datasource": null,
|
||||
"events": Emitter {
|
||||
"emitter": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
},
|
||||
},
|
||||
"gridPos": Object {
|
||||
"h": 1,
|
||||
"w": 1,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"id": 1,
|
||||
"targets": Array [
|
||||
Object {
|
||||
"refId": "A",
|
||||
},
|
||||
],
|
||||
"title": "My graph",
|
||||
"transparent": false,
|
||||
"type": "graph",
|
||||
},
|
||||
],
|
||||
"refresh": undefined,
|
||||
"revision": undefined,
|
||||
"schemaVersion": 17,
|
||||
"snapshot": undefined,
|
||||
"style": "dark",
|
||||
"tags": Array [],
|
||||
"templating": Object {
|
||||
"list": Array [],
|
||||
},
|
||||
"time": Object {
|
||||
"from": "now-6h",
|
||||
"to": "now",
|
||||
},
|
||||
"timepicker": Object {},
|
||||
"timezone": "",
|
||||
"title": "My dashboard",
|
||||
"uid": null,
|
||||
"version": 0,
|
||||
}
|
||||
}
|
||||
editview="settings"
|
||||
isEditing={false}
|
||||
isFullscreen={false}
|
||||
onAddPanel={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="scroll-canvas scroll-canvas--dashboard"
|
||||
>
|
||||
<CustomScrollbar
|
||||
autoHeightMax="100%"
|
||||
autoHeightMin="100%"
|
||||
autoHide={false}
|
||||
autoHideDuration={200}
|
||||
autoHideTimeout={200}
|
||||
customClassName="custom-scrollbars"
|
||||
hideTracksWhenNotNeeded={false}
|
||||
scrollTop={0}
|
||||
setScrollTop={[Function]}
|
||||
>
|
||||
<DashboardSettings
|
||||
dashboard={
|
||||
DashboardModel {
|
||||
"annotations": Object {
|
||||
"list": Array [
|
||||
Object {
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard",
|
||||
},
|
||||
],
|
||||
},
|
||||
"autoUpdate": undefined,
|
||||
"description": undefined,
|
||||
"editable": true,
|
||||
"events": Emitter {
|
||||
"emitter": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
},
|
||||
},
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": Array [],
|
||||
"meta": Object {
|
||||
"canEdit": true,
|
||||
"canMakeEditable": false,
|
||||
"canSave": true,
|
||||
"canShare": true,
|
||||
"canStar": true,
|
||||
"fullscreen": false,
|
||||
"isEditing": false,
|
||||
"showSettings": true,
|
||||
},
|
||||
"originalTemplating": Array [],
|
||||
"originalTime": Object {
|
||||
"from": "now-6h",
|
||||
"to": "now",
|
||||
},
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"datasource": null,
|
||||
"events": Emitter {
|
||||
"emitter": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
},
|
||||
},
|
||||
"gridPos": Object {
|
||||
"h": 1,
|
||||
"w": 1,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"id": 1,
|
||||
"targets": Array [
|
||||
Object {
|
||||
"refId": "A",
|
||||
},
|
||||
],
|
||||
"title": "My graph",
|
||||
"transparent": false,
|
||||
"type": "graph",
|
||||
},
|
||||
],
|
||||
"refresh": undefined,
|
||||
"revision": undefined,
|
||||
"schemaVersion": 17,
|
||||
"snapshot": undefined,
|
||||
"style": "dark",
|
||||
"tags": Array [],
|
||||
"templating": Object {
|
||||
"list": Array [],
|
||||
},
|
||||
"time": Object {
|
||||
"from": "now-6h",
|
||||
"to": "now",
|
||||
},
|
||||
"timepicker": Object {},
|
||||
"timezone": "",
|
||||
"title": "My dashboard",
|
||||
"uid": null,
|
||||
"version": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="dashboard-container"
|
||||
>
|
||||
<DashboardGrid
|
||||
dashboard={
|
||||
DashboardModel {
|
||||
"annotations": Object {
|
||||
"list": Array [
|
||||
Object {
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard",
|
||||
},
|
||||
],
|
||||
},
|
||||
"autoUpdate": undefined,
|
||||
"description": undefined,
|
||||
"editable": true,
|
||||
"events": Emitter {
|
||||
"emitter": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
},
|
||||
},
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": Array [],
|
||||
"meta": Object {
|
||||
"canEdit": true,
|
||||
"canMakeEditable": false,
|
||||
"canSave": true,
|
||||
"canShare": true,
|
||||
"canStar": true,
|
||||
"fullscreen": false,
|
||||
"isEditing": false,
|
||||
"showSettings": true,
|
||||
},
|
||||
"originalTemplating": Array [],
|
||||
"originalTime": Object {
|
||||
"from": "now-6h",
|
||||
"to": "now",
|
||||
},
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"datasource": null,
|
||||
"events": Emitter {
|
||||
"emitter": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
},
|
||||
},
|
||||
"gridPos": Object {
|
||||
"h": 1,
|
||||
"w": 1,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"id": 1,
|
||||
"targets": Array [
|
||||
Object {
|
||||
"refId": "A",
|
||||
},
|
||||
],
|
||||
"title": "My graph",
|
||||
"transparent": false,
|
||||
"type": "graph",
|
||||
},
|
||||
],
|
||||
"refresh": undefined,
|
||||
"revision": undefined,
|
||||
"schemaVersion": 17,
|
||||
"snapshot": undefined,
|
||||
"style": "dark",
|
||||
"tags": Array [],
|
||||
"templating": Object {
|
||||
"list": Array [],
|
||||
},
|
||||
"time": Object {
|
||||
"from": "now-6h",
|
||||
"to": "now",
|
||||
},
|
||||
"timepicker": Object {},
|
||||
"timezone": "",
|
||||
"title": "My dashboard",
|
||||
"uid": null,
|
||||
"version": 0,
|
||||
}
|
||||
}
|
||||
isEditing={false}
|
||||
isFullscreen={false}
|
||||
/>
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user