Skip to content

Commit

Permalink
Typescript dashboard app code. Pulls out part of the massive embeddab…
Browse files Browse the repository at this point in the history
…le API PR.

I typscripted this code as part of that PR because it helped me find errors, but really unnecessarily blew up the size of that PR. So pulling out.
  • Loading branch information
stacey-gammon committed Jun 3, 2019
1 parent de0e410 commit 23ab63a
Show file tree
Hide file tree
Showing 36 changed files with 640 additions and 289 deletions.
Expand Up @@ -17,21 +17,31 @@
* under the License.
*/

import { IAppState } from 'ui/state_management/app_state';

/**
* A poor excuse for a mock just to get some basic tests to run in jest without requiring the injector.
* This could be improved if we extract the appState and state classes externally of their angular providers.
* @return {AppStateMock}
*/
export function getAppStateMock() {
export function getAppStateMock(): IAppState {
class AppStateMock {
constructor(defaults) {
constructor(defaults: any) {
Object.assign(this, defaults);
}

on() {}
off() {}
toJSON() { return ''; }
toJSON() {
return '';
}
save() {}
translateHashToRison(stateHashOrRison: string | string[]) {
return stateHashOrRison;
}
getQueryParamName() {
return '';
}
}

return AppStateMock;
Expand Down
Expand Up @@ -18,7 +18,7 @@
*/

/* global jest */
export function getEmbeddableFactoryMock(config) {
export function getEmbeddableFactoryMock(config?: any) {
const embeddableFactoryMockDefaults = {
create: jest.fn(() => Promise.resolve({})),
};
Expand Down
@@ -0,0 +1,45 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. 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 { SavedObjectDashboard } from '../saved_dashboard/saved_dashboard';

export function getSavedDashboardMock(
config?: Partial<SavedObjectDashboard>
): SavedObjectDashboard {
return {
id: '123',
title: 'my dashboard',
panelsJSON: '[]',
searchSource: {
getOwnField: (param: any) => param,
setField: () => {},
},
copyOnSave: false,
timeRestore: false,
timeTo: 'now',
timeFrom: 'now-15m',
optionsJSON: '',
lastSavedTitle: '',
destroy: () => {},
save: () => {
return Promise.resolve('123');
},
...config,
};
}
Expand Up @@ -23,8 +23,9 @@ import _ from 'lodash';
import { createAction } from 'redux-actions';
import { EmbeddableMetadata, EmbeddableState } from 'ui/embeddable';
import { getEmbeddableCustomization, getPanel } from '../../selectors';
import { PanelId, PanelState } from '../selectors';
import { PanelId } from '../selectors';
import { updatePanel } from './panels';
import { SavedDashboardPanel } from '../types';

import { KibanaAction, KibanaThunk } from '../../selectors/types';

Expand Down Expand Up @@ -113,7 +114,7 @@ export function embeddableStateChanged(changeData: {
const customization = getEmbeddableCustomization(getState(), panelId);
if (!_.isEqual(embeddableState.customization, customization)) {
const originalPanelState = getPanel(getState(), panelId);
const newPanelState: PanelState = {
const newPanelState: SavedDashboardPanel = {
...originalPanelState,
embeddableConfig: _.cloneDeep(embeddableState.customization),
};
Expand Down
Expand Up @@ -39,7 +39,5 @@ export interface UpdateDescriptionAction

export type MetadataActions = UpdateDescriptionAction | UpdateTitleAction;

export const updateDescription = createAction<UpdateDescriptionAction>(
MetadataActionTypeKeys.UPDATE_DESCRIPTION
);
export const updateTitle = createAction<UpdateTitleAction>(MetadataActionTypeKeys.UPDATE_TITLE);
export const updateDescription = createAction<string>(MetadataActionTypeKeys.UPDATE_DESCRIPTION);
export const updateTitle = createAction<string>(MetadataActionTypeKeys.UPDATE_TITLE);
19 changes: 12 additions & 7 deletions src/legacy/core_plugins/kibana/public/dashboard/actions/panels.ts
Expand Up @@ -21,7 +21,8 @@

import { createAction } from 'redux-actions';
import { KibanaAction } from '../../selectors/types';
import { PanelId, PanelState, PanelStateMap } from '../selectors';
import { PanelId } from '../selectors';
import { SavedDashboardPanel } from '../types';

export enum PanelActionTypeKeys {
DELETE_PANEL = 'DELETE_PANEL',
Expand All @@ -36,10 +37,10 @@ export interface DeletePanelAction
extends KibanaAction<PanelActionTypeKeys.DELETE_PANEL, PanelId> {}

export interface UpdatePanelAction
extends KibanaAction<PanelActionTypeKeys.UPDATE_PANEL, PanelState> {}
extends KibanaAction<PanelActionTypeKeys.UPDATE_PANEL, SavedDashboardPanel> {}

export interface UpdatePanelsAction
extends KibanaAction<PanelActionTypeKeys.UPDATE_PANELS, PanelStateMap> {}
extends KibanaAction<PanelActionTypeKeys.UPDATE_PANELS, { [key: string]: SavedDashboardPanel }> {}

export interface ResetPanelTitleAction
extends KibanaAction<PanelActionTypeKeys.RESET_PANEL_TITLE, PanelId> {}
Expand All @@ -53,7 +54,7 @@ export interface SetPanelTitleAction
extends KibanaAction<PanelActionTypeKeys.SET_PANEL_TITLE, SetPanelTitleActionPayload> {}

export interface SetPanelsAction
extends KibanaAction<PanelActionTypeKeys.SET_PANELS, PanelStateMap> {}
extends KibanaAction<PanelActionTypeKeys.SET_PANELS, { [key: string]: SavedDashboardPanel }> {}

export type PanelActions =
| DeletePanelAction
Expand All @@ -64,10 +65,14 @@ export type PanelActions =
| SetPanelsAction;

export const deletePanel = createAction<PanelId>(PanelActionTypeKeys.DELETE_PANEL);
export const updatePanel = createAction<PanelState>(PanelActionTypeKeys.UPDATE_PANEL);
export const updatePanel = createAction<SavedDashboardPanel>(PanelActionTypeKeys.UPDATE_PANEL);
export const resetPanelTitle = createAction<PanelId>(PanelActionTypeKeys.RESET_PANEL_TITLE);
export const setPanelTitle = createAction<SetPanelTitleActionPayload>(
PanelActionTypeKeys.SET_PANEL_TITLE
);
export const updatePanels = createAction<PanelStateMap>(PanelActionTypeKeys.UPDATE_PANELS);
export const setPanels = createAction<PanelStateMap>(PanelActionTypeKeys.SET_PANELS);
export const updatePanels = createAction<{ [key: string]: SavedDashboardPanel }>(
PanelActionTypeKeys.UPDATE_PANELS
);
export const setPanels = createAction<{ [key: string]: SavedDashboardPanel }>(
PanelActionTypeKeys.SET_PANELS
);
Expand Up @@ -110,7 +110,7 @@ app.directive('dashboardApp', function ($injector) {

const dashboardStateManager = new DashboardStateManager({
savedDashboard: dash,
AppState,
AppStateClass: AppState,
hideWriteControls: dashboardConfig.getHideWriteControls(),
addFilter: ({ field, value, operator, index }) => {
filterActions.addFilter(field, value, operator, index, dashboardStateManager.getAppState(), filterManager);
Expand Down
Expand Up @@ -22,29 +22,38 @@ import { DashboardViewMode } from './dashboard_view_mode';
import { embeddableIsInitialized, setPanels } from './actions';
import { getAppStateMock, getSavedDashboardMock } from './__tests__';
import { store } from '../store';
import { IAppState } from 'ui/state_management/app_state';
import { DashboardAppState } from './types';
import { TimeRange } from 'ui/embeddable';
import { IndexPattern } from 'ui/index_patterns';

jest.mock('ui/chrome', () => ({ getKibanaVersion: () => '6.0.0' }), { virtual: true });


describe('DashboardState', function () {
let dashboardState;
describe('DashboardState', function() {
let dashboardState: DashboardStateManager;
const savedDashboard = getSavedDashboardMock();
const mockTimefilter = {
time: {},
setTime: function (time) { this.time = time; },
const mockTimefilter: {
time: TimeRange;
setTime: (time: TimeRange) => void;
} = {
time: { to: 'now', from: 'now-15m' },
setTime(time) {
this.time = time;
},
};
const mockIndexPattern = { id: 'index1' };
const mockIndexPattern: IndexPattern = { id: 'index1', fields: [], title: 'hi' };

function initDashboardState() {
dashboardState = new DashboardStateManager({
savedDashboard,
AppState: getAppStateMock(),
AppStateClass: getAppStateMock() as IAppState<DashboardAppState>,
hideWriteControls: false,
addFilter: () => {},
});
}

describe('syncTimefilterWithDashboard', function () {
test('syncs quick time', function () {
describe('syncTimefilterWithDashboard', function() {
test('syncs quick time', function() {
savedDashboard.timeRestore = true;
savedDashboard.timeFrom = 'now/w';
savedDashboard.timeTo = 'now/w';
Expand All @@ -59,7 +68,7 @@ describe('DashboardState', function () {
expect(mockTimefilter.time.from).toBe('now/w');
});

test('syncs relative time', function () {
test('syncs relative time', function() {
savedDashboard.timeRestore = true;
savedDashboard.timeFrom = 'now-13d';
savedDashboard.timeTo = 'now';
Expand All @@ -74,7 +83,7 @@ describe('DashboardState', function () {
expect(mockTimefilter.time.from).toBe('now-13d');
});

test('syncs absolute time', function () {
test('syncs absolute time', function() {
savedDashboard.timeRestore = true;
savedDashboard.timeFrom = '2015-09-19 06:31:44.000';
savedDashboard.timeTo = '2015-09-29 06:31:44.000';
Expand All @@ -90,7 +99,7 @@ describe('DashboardState', function () {
});
});

describe('isDirty', function () {
describe('isDirty', function() {
beforeAll(() => {
initDashboardState();
});
Expand All @@ -108,29 +117,52 @@ describe('DashboardState', function () {
});
});

describe('panelIndexPatternMapping', function () {
describe('panelIndexPatternMapping', function() {
beforeAll(() => {
initDashboardState();
});

function simulateNewEmbeddableWithIndexPatterns({ panelId, indexPatterns }) {
store.dispatch(setPanels({ [panelId]: { panelIndex: panelId } }));
function simulateNewEmbeddableWithIndexPatterns({
panelId,
indexPatterns,
}: {
panelId: string;
indexPatterns?: IndexPattern[];
}) {
store.dispatch(
setPanels({
[panelId]: {
id: '123',
panelIndex: panelId,
version: '1',
type: 'hi',
embeddableConfig: {},
gridData: { x: 1, y: 1, h: 1, w: 1, i: '1' },
},
})
);
const metadata = { title: 'my embeddable title', editUrl: 'editme', indexPatterns };
store.dispatch(embeddableIsInitialized({ metadata, panelId: panelId }));
store.dispatch(embeddableIsInitialized({ metadata, panelId }));
}

test('initially has no index patterns', () => {
expect(dashboardState.getPanelIndexPatterns().length).toBe(0);
});

test('registers index pattern when an embeddable is initialized with one', async () => {
simulateNewEmbeddableWithIndexPatterns({ panelId: 'foo1', indexPatterns: [mockIndexPattern] });
simulateNewEmbeddableWithIndexPatterns({
panelId: 'foo1',
indexPatterns: [mockIndexPattern],
});
await new Promise(resolve => process.nextTick(resolve));
expect(dashboardState.getPanelIndexPatterns().length).toBe(1);
});

test('registers unique index patterns', async () => {
simulateNewEmbeddableWithIndexPatterns({ panelId: 'foo2', indexPatterns: [mockIndexPattern] });
simulateNewEmbeddableWithIndexPatterns({
panelId: 'foo2',
indexPatterns: [mockIndexPattern],
});
await new Promise(resolve => process.nextTick(resolve));
expect(dashboardState.getPanelIndexPatterns().length).toBe(1);
});
Expand Down

0 comments on commit 23ab63a

Please sign in to comment.