diff --git a/package.json b/package.json index 77fd92baf57..5ac751ced3f 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "prettier": "1.9.2", "react-hot-loader": "^4.3.6", "react-test-renderer": "^16.5.0", + "redux-mock-store": "^1.5.3", "regexp-replace-loader": "^1.0.1", "sass-lint": "^1.10.2", "sass-loader": "^7.0.1", diff --git a/public/app/features/dashboard/state/initDashboard.test.ts b/public/app/features/dashboard/state/initDashboard.test.ts new file mode 100644 index 00000000000..eebeb5010fb --- /dev/null +++ b/public/app/features/dashboard/state/initDashboard.test.ts @@ -0,0 +1,130 @@ +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { initDashboard, InitDashboardArgs } from './initDashboard'; +import { DashboardRouteInfo, DashboardLoadingState } from 'app/types'; + +const mockStore = configureMockStore([thunk]); + +interface ScenarioContext { + args: InitDashboardArgs; + timeSrv: any; + annotationsSrv: any; + unsavedChangesSrv: any; + variableSrv: any; + dashboardSrv: any; + keybindingSrv: any; + setup: (fn: () => void) => void; + actions: any[]; + storeState: any; +} + +type ScenarioFn = (ctx: ScenarioContext) => void; + +function describeInitScenario(description: string, scenarioFn: ScenarioFn) { + describe(description, () => { + const timeSrv = { init: jest.fn() }; + const annotationsSrv = { init: jest.fn() }; + const unsavedChangesSrv = { init: jest.fn() }; + const variableSrv = { init: jest.fn() }; + const dashboardSrv = { setCurrent: jest.fn() }; + const keybindingSrv = { setupDashboardBindings: jest.fn() }; + + const injectorMock = { + get: (name: string) => { + switch (name) { + case 'timeSrv': + return timeSrv; + case 'annotationsSrv': + return annotationsSrv; + case 'unsavedChangesSrv': + return unsavedChangesSrv; + case 'dashboardSrv': + return dashboardSrv; + case 'variableSrv': + return variableSrv; + case 'keybindingSrv': + return keybindingSrv; + default: + throw { message: 'Unknown service ' + name }; + } + }, + }; + + let setupFn = () => {}; + + const ctx: ScenarioContext = { + args: { + $injector: injectorMock, + $scope: {}, + fixUrl: false, + routeInfo: DashboardRouteInfo.Normal, + }, + timeSrv, + annotationsSrv, + unsavedChangesSrv, + variableSrv, + dashboardSrv, + keybindingSrv, + actions: [], + storeState: { + location: { + query: {}, + }, + user: {}, + }, + setup: (fn: () => void) => { + setupFn = fn; + }, + }; + + beforeEach(async () => { + setupFn(); + + const store = mockStore(ctx.storeState); + + await store.dispatch(initDashboard(ctx.args)); + + ctx.actions = store.getActions(); + }); + + scenarioFn(ctx); + }); +} + +describeInitScenario('Initializing new dashboard', ctx => { + ctx.setup(() => { + ctx.storeState.user.orgId = 12; + ctx.args.routeInfo = DashboardRouteInfo.New; + }); + + it('Should send action to set loading state to fetching', () => { + expect(ctx.actions[0].type).toBe('SET_DASHBOARD_LOADING_STATE'); + expect(ctx.actions[0].payload).toBe(DashboardLoadingState.Fetching); + }); + + it('Should send action to set loading state to Initializing', () => { + expect(ctx.actions[1].type).toBe('SET_DASHBOARD_LOADING_STATE'); + expect(ctx.actions[1].payload).toBe(DashboardLoadingState.Initializing); + }); + + it('Should update location with orgId query param', () => { + expect(ctx.actions[2].type).toBe('UPDATE_LOCATION'); + expect(ctx.actions[2].payload.query.orgId).toBe(12); + }); + + it('Should send action to set dashboard model', () => { + expect(ctx.actions[3].type).toBe('SET_DASHBOARD_MODEL'); + expect(ctx.actions[3].payload.title).toBe('New dashboard'); + }); + + it('Should Initializing services', () => { + expect(ctx.timeSrv.init).toBeCalled(); + expect(ctx.annotationsSrv.init).toBeCalled(); + expect(ctx.variableSrv.init).toBeCalled(); + expect(ctx.unsavedChangesSrv.init).toBeCalled(); + expect(ctx.keybindingSrv.setupDashboardBindings).toBeCalled(); + expect(ctx.dashboardSrv.setCurrent).toBeCalled(); + }); +}); + + diff --git a/public/app/features/dashboard/state/initDashboard.ts b/public/app/features/dashboard/state/initDashboard.ts index ba218c1583d..2c68435b313 100644 --- a/public/app/features/dashboard/state/initDashboard.ts +++ b/public/app/features/dashboard/state/initDashboard.ts @@ -9,7 +9,6 @@ import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { AnnotationsSrv } from 'app/features/annotations/annotations_srv'; import { VariableSrv } from 'app/features/templating/variable_srv'; import { KeybindingSrv } from 'app/core/services/keybindingSrv'; -import { config } from 'app/core/config'; // Actions import { updateLocation } from 'app/core/actions'; @@ -150,8 +149,9 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult { } // add missing orgId query param - if (!getState().location.query.orgId) { - dispatch(updateLocation({ query: { orgId: config.bootData.user.orgId }, partial: true, replace: true })); + const storeState = getState() ; + if (!storeState.location.query.orgId) { + dispatch(updateLocation({ query: { orgId: storeState.user.orgId }, partial: true, replace: true })); } // init services diff --git a/public/app/features/profile/state/reducers.ts b/public/app/features/profile/state/reducers.ts new file mode 100644 index 00000000000..dc6e841449e --- /dev/null +++ b/public/app/features/profile/state/reducers.ts @@ -0,0 +1,14 @@ +import { UserState } from 'app/types'; +import config from 'app/core/config'; + +export const initialState: UserState = { + orgId: config.bootData.user.orgId, +}; + +export const userReducer = (state = initialState, action: any): UserState => { + return state; +}; + +export default { + user: userReducer, +}; diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index 570a387cd74..e2c33523271 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -11,6 +11,7 @@ import exploreReducers from 'app/features/explore/state/reducers'; import pluginReducers from 'app/features/plugins/state/reducers'; import dataSourcesReducers from 'app/features/datasources/state/reducers'; import usersReducers from 'app/features/users/state/reducers'; +import userReducers from 'app/features/profile/state/reducers'; import organizationReducers from 'app/features/org/state/reducers'; import { setStore } from './store'; @@ -25,6 +26,7 @@ const rootReducers = { ...pluginReducers, ...dataSourcesReducers, ...usersReducers, + ...userReducers, ...organizationReducers, }; diff --git a/public/app/types/user.ts b/public/app/types/user.ts index 365411147bb..7691558ce90 100644 --- a/public/app/types/user.ts +++ b/public/app/types/user.ts @@ -1,5 +1,3 @@ -import { DashboardSearchHit } from './search'; - export interface OrgUser { avatarUrl: string; email: string; @@ -47,5 +45,5 @@ export interface UsersState { } export interface UserState { - starredDashboards: DashboardSearchHit[]; + orgId: number; } diff --git a/yarn.lock b/yarn.lock index 169abd40ee4..df2e1cea37e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14582,6 +14582,13 @@ redux-logger@^3.0.6: dependencies: deep-diff "^0.3.5" +redux-mock-store@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.3.tgz#1f10528949b7ce8056c2532624f7cafa98576c6d" + integrity sha512-ryhkkb/4D4CUGpAV2ln1GOY/uh51aczjcRz9k2L2bPx/Xja3c5pSGJJPyR25GNVRXtKIExScdAgFdiXp68GmJA== + dependencies: + lodash.isplainobject "^4.0.6" + redux-thunk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"