Merge pull request #3934 from influxdata/fun/tests

Convert tests to typescript
pull/3950/head
Iris Scholten 2018-07-17 10:32:55 -07:00 committed by GitHub
commit 2bb548d7ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 474 additions and 289 deletions

View File

@ -5,20 +5,28 @@ import {notifyPresentationMode} from 'src/shared/copy/notifications'
import {Dispatch} from 'redux'
import * as AppActions from 'src/types/actions/app'
import {
ActionTypes,
EnablePresentationModeAction,
DisablePresentationModeAction,
DelayEnablePresentationModeDispatcher,
SetAutoRefreshActionCreator,
SetAutoRefreshAction,
TemplateControlBarVisibilityToggledAction,
} from 'src/types/actions/app'
// ephemeral state action creators
export const enablePresentationMode = (): AppActions.EnablePresentationModeAction => ({
type: 'ENABLE_PRESENTATION_MODE',
export const enablePresentationMode = (): EnablePresentationModeAction => ({
type: ActionTypes.EnablePresentationMode,
})
export const disablePresentationMode = (): AppActions.DisablePresentationModeAction => ({
type: 'DISABLE_PRESENTATION_MODE',
export const disablePresentationMode = (): DisablePresentationModeAction => ({
type: ActionTypes.DisablePresentationMode,
})
export const delayEnablePresentationMode: AppActions.DelayEnablePresentationModeDispatcher = () => async (
dispatch: Dispatch<AppActions.EnablePresentationModeAction>
export const delayEnablePresentationMode: DelayEnablePresentationModeDispatcher = () => async (
dispatch: Dispatch<EnablePresentationModeAction>
): Promise<NodeJS.Timer> =>
setTimeout(() => {
dispatch(enablePresentationMode())
@ -27,20 +35,15 @@ export const delayEnablePresentationMode: AppActions.DelayEnablePresentationMode
// persistent state action creators
export const setAutoRefresh: AppActions.SetAutoRefreshActionCreator = (
export const setAutoRefresh: SetAutoRefreshActionCreator = (
milliseconds: number
): AppActions.SetAutoRefreshAction => ({
type: 'SET_AUTOREFRESH',
): SetAutoRefreshAction => ({
type: ActionTypes.SetAutoRefresh,
payload: {
milliseconds,
},
})
export const templateControlBarVisibilityToggled = (): AppActions.TemplateControlBarVisibilityToggledAction => ({
type: 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED',
})
export const noop = (): AppActions.NoopAction => ({
type: 'NOOP',
payload: {},
export const templateControlBarVisibilityToggled = (): TemplateControlBarVisibilityToggledAction => ({
type: ActionTypes.TemplateControlBarVisibilityToggled,
})

View File

@ -1,112 +0,0 @@
import {getMe as getMeAJAX, updateMe as updateMeAJAX} from 'shared/apis/auth'
import {getLinksAsync} from 'shared/actions/links'
import {notify} from 'shared/actions/notifications'
import {errorThrown} from 'shared/actions/errors'
import {notifyUserSwitchedOrgs} from 'shared/copy/notifications'
export const authExpired = auth => ({
type: 'AUTH_EXPIRED',
payload: {
auth,
},
})
export const authRequested = () => ({
type: 'AUTH_REQUESTED',
})
export const meGetRequested = () => ({
type: 'ME_GET_REQUESTED',
})
export const meGetCompleted = ({me, auth, logoutLink}) => ({
type: 'ME_GET_COMPLETED',
payload: {
me,
auth,
logoutLink,
},
})
export const meGetFailed = () => ({
type: 'ME_GET_FAILED',
})
export const meChangeOrganizationRequested = () => ({
type: 'ME_CHANGE_ORGANIZATION_REQUESTED',
})
export const meChangeOrganizationCompleted = () => ({
type: 'ME_CHANGE_ORGANIZATION_COMPLETED',
})
export const meChangeOrganizationFailed = () => ({
type: 'ME_CHANGE_ORGANIZATION_FAILED',
})
// shouldResetMe protects against `me` being nullified in Redux temporarily,
// which currently causes the app to show a loading spinner until me is
// re-hydrated. if `getMeAsync` is only being used to refresh me after creating
// an organization, this is undesirable behavior
export const getMeAsync = ({shouldResetMe = false} = {}) => async dispatch => {
if (shouldResetMe) {
dispatch(authRequested())
dispatch(meGetRequested())
}
try {
// These non-me objects are added to every response by some AJAX trickery
const {data: me, auth, logoutLink} = await getMeAJAX()
// TODO: eventually, get the links for auth and logout out of here and into linksGetCompleted
dispatch(
meGetCompleted({
me,
auth,
logoutLink,
})
)
} catch (error) {
dispatch(meGetFailed())
dispatch(errorThrown(error))
}
}
// meChangeOrganizationAsync is for switching the user's current organization.
//
// Global links state also needs to be refreshed upon organization change so
// that Admin Chronograf / Current Org User tab's link is valid, but this is
// happening automatically because we are using a browser redirect to reload
// the application. If at some point we stop using a redirect and instead
// make it a seamless SPA experience, a la issue #2463, we'll need to make sure
// links are still refreshed.
export const meChangeOrganizationAsync = (
url,
organization
) => async dispatch => {
dispatch(meChangeOrganizationRequested())
try {
const {data: me, auth, logoutLink} = await updateMeAJAX(url, organization)
const currentRole = me.roles.find(
r => r.organization === me.currentOrganization.id
)
dispatch(
notify(
notifyUserSwitchedOrgs(me.currentOrganization.name, currentRole.name)
)
)
dispatch(meChangeOrganizationCompleted())
dispatch(meGetCompleted({me, auth, logoutLink}))
// refresh links after every successful meChangeOrganization to refresh
// /organizations/:id/users link for Admin / Current Org Users page to load
dispatch(getLinksAsync())
// TODO: reload sources upon me change org if non-refresh behavior preferred
// instead of current behavior on both invocations of meChangeOrganization,
// which is to refresh index via router.push('')
} catch (error) {
dispatch(errorThrown(error))
dispatch(meChangeOrganizationFailed())
}
}

View File

@ -0,0 +1,177 @@
import {Dispatch} from 'redux'
import {
getMe as getMeAJAX,
updateMe as updateMeAJAX,
} from 'src/shared/apis/auth'
import {getLinksAsync} from 'src/shared/actions/links'
import {notify} from 'src/shared/actions/notifications'
import {errorThrown} from 'src/shared/actions/errors'
import {notifyUserSwitchedOrgs} from 'src/shared/copy/notifications'
import {Me, Organization} from 'src/types/auth'
export type Action =
| AuthExpiredAction
| AuthRequestedAction
| MeGetRequestedAction
| MeGetCompletedAction
| MeGetRequestedAction
| MeGetFailedAction
| MeChangeOrganizationCompletedAction
| MeChangeOrganizationFailedAction
| MeChangeOrganizationRequestedAction
export enum ActionTypes {
AuthExpired = 'AUTH_EXPIRED',
AuthRequested = 'AUTH_REQUESTED',
MeGetRequested = 'ME_GET_REQUESTED',
MeGetCompleted = 'ME_GET_COMPLETED',
MeGetFailed = 'ME_GET_FAILED',
MeChangeOrganizationRequested = 'ME_CHANGE_ORGANIZATION_REQUESTED',
MeChangeOrganizationCompleted = 'ME_CHANGE_ORGANIZATION_COMPLETED',
MeChangeOrganizationFailed = 'ME_CHANGE_ORGANIZATION_FAILED',
}
export interface AuthExpiredAction {
type: ActionTypes.AuthExpired
payload: {auth}
}
export const authExpired = (auth): AuthExpiredAction => ({
type: ActionTypes.AuthExpired,
payload: {
auth,
},
})
export interface AuthRequestedAction {
type: ActionTypes.AuthRequested
}
export const authRequested = (): AuthRequestedAction => ({
type: ActionTypes.AuthRequested,
})
export interface MeGetRequestedAction {
type: ActionTypes.MeGetRequested
}
export const meGetRequested = (): MeGetRequestedAction => ({
type: ActionTypes.MeGetRequested,
})
export interface MeGetCompletedAction {
type: ActionTypes.MeGetCompleted
payload: {
me: Me
auth
logoutLink: string
}
}
export const meGetCompleted = ({
me,
auth,
logoutLink,
}): MeGetCompletedAction => ({
type: ActionTypes.MeGetCompleted,
payload: {
me,
auth,
logoutLink,
},
})
export interface MeGetFailedAction {
type: ActionTypes.MeGetFailed
}
export const meGetFailed = (): MeGetFailedAction => ({
type: ActionTypes.MeGetFailed,
})
export interface MeChangeOrganizationRequestedAction {
type: ActionTypes.MeChangeOrganizationRequested
}
export const meChangeOrganizationRequested = (): MeChangeOrganizationRequestedAction => ({
type: ActionTypes.MeChangeOrganizationRequested,
})
export interface MeChangeOrganizationCompletedAction {
type: ActionTypes.MeChangeOrganizationCompleted
}
export const meChangeOrganizationCompleted = (): MeChangeOrganizationCompletedAction => ({
type: ActionTypes.MeChangeOrganizationCompleted,
})
export interface MeChangeOrganizationFailedAction {
type: ActionTypes.MeChangeOrganizationFailed
}
export const meChangeOrganizationFailed = (): MeChangeOrganizationFailedAction => ({
type: ActionTypes.MeChangeOrganizationFailed,
})
// shouldResetMe protects against `me` being nullified in Redux temporarily,
// which currently causes the app to show a loading spinner until me is
// re-hydrated. if `getMeAsync` is only being used to refresh me after creating
// an organization, this is undesirable behavior
export const getMeAsync = ({shouldResetMe = false} = {}) => async (
dispatch: Dispatch<Action>
): Promise<void> => {
if (shouldResetMe) {
dispatch(authRequested())
dispatch(meGetRequested())
}
try {
// These non-me objects are added to every response by some AJAX trickery
const {data: me, auth, logoutLink} = await getMeAJAX()
// TODO: eventually, get the links for auth and logout out of here and into linksGetCompleted
dispatch(
meGetCompleted({
me,
auth,
logoutLink,
})
)
} catch (error) {
dispatch(meGetFailed())
dispatch(errorThrown(error))
}
}
// meChangeOrganizationAsync is for switching the user's current organization.
//
// Global links state also needs to be refreshed upon organization change so
// that Admin Chronograf / Current Org User tab's link is valid, but this is
// happening automatically because we are using a browser redirect to reload
// the application. If at some point we stop using a redirect and instead
// make it a seamless SPA experience, a la issue #2463, we'll need to make sure
// links are still refreshed.
export const meChangeOrganizationAsync = (
url: string,
organization: Organization
) => async (dispatch): Promise<void> => {
dispatch(meChangeOrganizationRequested())
try {
const {data: me, auth, logoutLink} = await updateMeAJAX(url, organization)
const currentRole = me.roles.find(
r => r.organization === me.currentOrganization.id
)
dispatch(
notify(
notifyUserSwitchedOrgs(me.currentOrganization.name, currentRole.name)
)
)
dispatch(meChangeOrganizationCompleted())
dispatch(meGetCompleted({me, auth, logoutLink}))
// refresh links after every successful meChangeOrganization to refresh
// /organizations/:id/users link for Admin / Current Org Users page to load
dispatch(getLinksAsync())
// TODO: reload sources upon me change org if non-refresh behavior preferred
// instead of current behavior on both invocations of meChangeOrganization,
// which is to refresh index via router.push('')
} catch (error) {
dispatch(errorThrown(error))
dispatch(meChangeOrganizationFailed())
}
}

View File

@ -1,30 +0,0 @@
import {getLinks as getLinksAJAX} from 'shared/apis/links'
import {errorThrown} from 'shared/actions/errors'
import {linksLink} from 'shared/constants'
const linksGetRequested = () => ({
type: 'LINKS_GET_REQUESTED',
})
export const linksGetCompleted = links => ({
type: 'LINKS_GET_COMPLETED',
payload: {links},
})
const linksGetFailed = () => ({
type: 'LINKS_GET_FAILED',
})
export const getLinksAsync = () => async dispatch => {
dispatch(linksGetRequested())
try {
const {data} = await getLinksAJAX()
dispatch(linksGetCompleted(data))
} catch (error) {
const message = `Failed to retrieve api links from ${linksLink}`
dispatch(errorThrown(error, message))
dispatch(linksGetFailed())
}
}

View File

@ -0,0 +1,56 @@
import {Dispatch} from 'redux'
import {getLinks as getLinksAJAX} from 'src/shared/apis/links'
import {errorThrown} from 'src/shared/actions/errors'
import {linksLink} from 'src/shared/constants'
import {AuthLinks} from 'src/types/auth'
export enum ActionTypes {
LinksGetRequested = 'LINKS_GET_REQUESTED',
LinksGetCompleted = 'LINKS_GET_COMPLETED',
LinksGetFailed = 'LINKS_GET_FAILED',
}
export interface LinksGetRequestedAction {
type: ActionTypes.LinksGetRequested
}
const linksGetRequested = (): LinksGetRequestedAction => ({
type: ActionTypes.LinksGetRequested,
})
export interface LinksGetCompletedAction {
type: ActionTypes.LinksGetCompleted
payload: {links: AuthLinks}
}
export const linksGetCompleted = (
links: AuthLinks
): LinksGetCompletedAction => ({
type: ActionTypes.LinksGetCompleted,
payload: {links},
})
export interface LinksGetFailedAction {
type: ActionTypes.LinksGetFailed
}
const linksGetFailed = (): LinksGetFailedAction => ({
type: ActionTypes.LinksGetFailed,
})
export const getLinksAsync = () => async (
dispatch: Dispatch<
LinksGetRequestedAction | LinksGetCompletedAction | LinksGetFailedAction
>
): Promise<void> => {
dispatch(linksGetRequested())
try {
const {data} = await getLinksAJAX()
dispatch(linksGetCompleted(data))
} catch (error) {
const message = `Failed to retrieve api links from ${linksLink}`
dispatch(errorThrown(error, message))
dispatch(linksGetFailed())
}
}

View File

@ -9,12 +9,15 @@ import {analyzeQueries} from 'src/shared/apis'
import {DEFAULT_DURATION_MS} from 'src/shared/constants'
import replaceTemplates, {replaceInterval} from 'src/tempVars/utils/replace'
import {proxy} from 'src/utils/queryUrlGenerator'
import {noop} from 'src/shared/actions/app'
import {Source} from 'src/types'
import {Template} from 'src/types'
const noop = () => ({
type: 'NOOP',
payload: {},
})
interface Query {
text: string
database?: string

View File

@ -1,8 +1,19 @@
import {combineReducers} from 'redux'
import {AUTOREFRESH_DEFAULT} from 'shared/constants'
import {AUTOREFRESH_DEFAULT} from 'src/shared/constants'
import {ActionTypes, Action} from 'src/types/actions/app'
const initialState = {
interface State {
ephemeral: {
inPresentationMode: boolean
}
persisted: {
autoRefresh: number
showTemplateControlBar: boolean
}
}
const initialState: State = {
ephemeral: {
inPresentationMode: false,
},
@ -17,16 +28,19 @@ const {
persisted: initialAppPersistedState,
} = initialState
const appEphemeralReducer = (state = initialAppEphemeralState, action) => {
const appEphemeralReducer = (
state = initialAppEphemeralState,
action: Action
) => {
switch (action.type) {
case 'ENABLE_PRESENTATION_MODE': {
case ActionTypes.EnablePresentationMode: {
return {
...state,
inPresentationMode: true,
}
}
case 'DISABLE_PRESENTATION_MODE': {
case ActionTypes.DisablePresentationMode: {
return {
...state,
inPresentationMode: false,
@ -38,16 +52,19 @@ const appEphemeralReducer = (state = initialAppEphemeralState, action) => {
}
}
const appPersistedReducer = (state = initialAppPersistedState, action) => {
const appPersistedReducer = (
state = initialAppPersistedState,
action: Action
) => {
switch (action.type) {
case 'SET_AUTOREFRESH': {
case ActionTypes.SetAutoRefresh: {
return {
...state,
autoRefresh: action.payload.milliseconds,
}
}
case 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED': {
case ActionTypes.TemplateControlBarVisibilityToggled: {
const {showTemplateControlBar} = state
return {...state, showTemplateControlBar: !showTemplateControlBar}
@ -58,7 +75,7 @@ const appPersistedReducer = (state = initialAppPersistedState, action) => {
}
}
const appReducer = combineReducers({
const appReducer = combineReducers<State>({
ephemeral: appEphemeralReducer,
persisted: appPersistedReducer,
})

View File

@ -1,72 +0,0 @@
const getInitialState = () => ({
links: null,
me: null,
isMeLoading: false,
isAuthLoading: false,
logoutLink: null,
})
import {getMeRole} from 'shared/reducers/helpers/auth'
export const initialState = getInitialState()
const meGetCompleted = (state, {me}, isUsingAuth) => {
let newMe = me
if (isUsingAuth) {
newMe = {
...newMe,
role: getMeRole(me),
currentOrganization: me.currentOrganization,
}
}
return {
...state,
me: {...newMe},
isMeLoading: false,
}
}
const authReceived = (state, {auth: {links}}) => ({
...state,
links,
isAuthLoading: false,
})
const logoutLinkReceived = (state, {logoutLink}, isUsingAuth) => ({
...state,
logoutLink,
isUsingAuth,
})
const authReducer = (state = initialState, action) => {
switch (action.type) {
case 'AUTH_EXPIRED': {
const {
auth: {links},
} = action.payload
return {...initialState, links}
}
case 'AUTH_REQUESTED': {
return {...state, isAuthLoading: true}
}
case 'ME_GET_REQUESTED': {
return {...state, isMeLoading: true}
}
case 'ME_GET_COMPLETED': {
const {logoutLink} = action.payload
const isUsingAuth = !!logoutLink
let newState = meGetCompleted(state, action.payload, isUsingAuth)
newState = authReceived(newState, action.payload)
newState = logoutLinkReceived(newState, action.payload, isUsingAuth)
return newState
}
}
return state
}
export default authReducer

View File

@ -0,0 +1,103 @@
import {AuthMe, AuthLink} from 'src/types/auth'
import {getMeRole} from 'src/shared/reducers/helpers/auth'
import {getDeep} from 'src/utils/wrappers'
import {ActionTypes} from 'src/shared/actions/auth'
interface State {
links: AuthLink[] | null
me: AuthMe | null
isMeLoading: boolean
isAuthLoading: boolean
logoutLink: string | null
isUsingAuth: boolean
}
interface Auth {
auth: {
links: AuthLink[]
}
}
const getInitialState = (): State => ({
links: null,
me: null,
isMeLoading: false,
isAuthLoading: false,
logoutLink: null,
isUsingAuth: false,
})
export const initialState = getInitialState()
const meGetCompleted = (
state: State,
{me}: {me: AuthMe},
isUsingAuth: boolean
): State => {
let newMe = me
if (isUsingAuth) {
newMe = {
...newMe,
role: getMeRole(me),
currentOrganization: me.currentOrganization,
}
}
return {
...state,
me: {...newMe},
isMeLoading: false,
}
}
const authReceived = (state: State, auth: Auth): State => {
const links = getDeep<AuthLink[]>(auth, 'auth.links', [])
return {
...state,
links,
isAuthLoading: false,
}
}
const logoutLinkReceived = (
state: State,
{logoutLink}: {logoutLink: string},
isUsingAuth: boolean
): State => ({
...state,
logoutLink,
isUsingAuth,
})
const authReducer = (state: State = initialState, action): State => {
switch (action.type) {
case ActionTypes.AuthExpired: {
const {
auth: {links},
} = action.payload
return {...initialState, links}
}
case ActionTypes.AuthRequested: {
return {...state, isAuthLoading: true}
}
case ActionTypes.MeGetRequested: {
return {...state, isMeLoading: true}
}
case ActionTypes.MeGetCompleted: {
const {logoutLink} = action.payload
const isUsingAuth = !!logoutLink
let newState = meGetCompleted(state, action.payload, isUsingAuth)
newState = authReceived(newState, action.payload)
newState = logoutLinkReceived(newState, action.payload, isUsingAuth)
return newState
}
}
return state
}
export default authReducer

View File

@ -1,8 +1,9 @@
import _ from 'lodash'
import {SUPERADMIN_ROLE, MEMBER_ROLE} from 'src/auth/Authorized'
import {AuthMe} from 'src/types/auth'
export const getMeRole = me => {
export const getMeRole = (me: AuthMe): string => {
const currentRoleOrg = me.roles.find(
role => me.currentOrganization.id === role.organization
)

View File

@ -1,13 +1,27 @@
import {Dispatch} from 'redux'
export enum ActionTypes {
EnablePresentationMode = 'ENABLE_PRESENTATION_MODE',
DisablePresentationMode = 'DISABLE_PRESENTATION_MODE',
SetAutoRefresh = 'SET_AUTOREFRESH',
TemplateControlBarVisibilityToggled = 'TemplateControlBarVisibilityToggledAction',
Noop = 'NOOP',
}
export type Action =
| EnablePresentationModeAction
| DisablePresentationModeAction
| SetAutoRefreshAction
| TemplateControlBarVisibilityToggledAction
export type EnablePresentationModeActionCreator = () => EnablePresentationModeAction
export interface EnablePresentationModeAction {
type: 'ENABLE_PRESENTATION_MODE'
type: ActionTypes.EnablePresentationMode
}
export interface DisablePresentationModeAction {
type: 'DISABLE_PRESENTATION_MODE'
type: ActionTypes.DisablePresentationMode
}
export type DelayEnablePresentationModeDispatcher = () => DelayEnablePresentationModeThunk
@ -21,7 +35,7 @@ export type SetAutoRefreshActionCreator = (
) => SetAutoRefreshAction
export interface SetAutoRefreshAction {
type: 'SET_AUTOREFRESH'
type: ActionTypes.SetAutoRefresh
payload: {
milliseconds: number
}
@ -30,10 +44,5 @@ export interface SetAutoRefreshAction {
export type TemplateControlBarVisibilityToggledActionCreator = () => TemplateControlBarVisibilityToggledAction
export interface TemplateControlBarVisibilityToggledAction {
type: 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED'
}
export interface NoopAction {
type: 'NOOP'
payload: object
type: ActionTypes.TemplateControlBarVisibilityToggled
}

View File

@ -8,8 +8,27 @@ export interface Organization {
}
export interface Me {
currentOrganization?: Organization
role: Role
currentOrganization?: Organization
}
export interface AuthMe {
id?: string
role: string
name: string
provider: string
scheme: string
superAdmin: boolean
logoutLink: string
roles: Role[]
links: {
self: string
}
organizations?: Organization[]
currentOrganization?: Organization
isUsingAuth: boolean
isMeLoading: boolean
isAuthLoading: boolean
}
export enum InfluxDBPermissions {
@ -90,3 +109,11 @@ export interface AuthLinks {
sources: string
users: string
}
export interface AuthLink {
name: string
label: string
login: string
logout: string
callback: string
}

View File

@ -2,22 +2,9 @@ import reducer from 'src/data_explorer/reducers/ui'
import {addQuery, deleteQuery} from 'src/data_explorer/actions/view'
const noopAction = () => {
return {type: 'NOOP'}
}
let state
describe('DataExplorer.Reducers.UI', () => {
it('it sets the default state for UI', () => {
const actual = reducer(state, noopAction())
const expected = {
queryIDs: [],
}
expect(actual).toEqual(expected)
})
it('it can add a query', () => {
const actual = reducer(state, addQuery())
expect(actual.queryIDs.length).toBe(1)

View File

@ -1,10 +1,10 @@
import appReducer from 'shared/reducers/app'
import appReducer from 'src/shared/reducers/app'
import {
enablePresentationMode,
disablePresentationMode,
setAutoRefresh,
templateControlBarVisibilityToggled,
} from 'shared/actions/app'
} from 'src/shared/actions/app'
describe('Shared.Reducers.appReducer', () => {
const initialState = {

View File

@ -1,11 +1,11 @@
import {default as authReducer, initialState} from 'shared/reducers/auth'
import {default as authReducer, initialState} from 'src/shared/reducers/auth'
import {
authExpired,
authRequested,
meGetRequested,
meGetCompleted,
} from 'shared/actions/auth'
} from 'src/shared/actions/auth'
const defaultAuth = {
links: [
@ -17,9 +17,16 @@ const defaultAuth = {
callback: '/oauth/github/callback',
},
],
me: null,
isMeLoading: false,
isAuthLoading: false,
logoutLink: null,
isUsingAuth: false,
}
const defaultMe = {
roles: [],
currentOrganization: '',
name: 'wishful_modal@overlay.technology',
links: {
self: '/chronograf/v1/users/wishful_modal@overlay.technology',
@ -83,11 +90,13 @@ describe('Shared.Reducers.authReducer', () => {
isAuthLoading: true,
isMeLoading: true,
}
const reducedState = authReducer(
loadingState,
meGetCompleted({
me: defaultMe,
auth: defaultAuth,
logoutLink: '',
})
)

View File

@ -2,7 +2,6 @@ import _ from 'lodash'
import linksReducer from 'shared/reducers/links'
import {linksGetCompleted} from 'shared/actions/links'
import {noop} from 'shared/actions/app'
const links = {
layouts: '/chronograf/v1/layouts',
@ -25,8 +24,7 @@ const links = {
describe('Shared.Reducers.linksReducer', () => {
it('can handle LINKS_GET_COMPLETED', () => {
const initial = linksReducer(undefined, noop())
const actual = linksReducer(initial, linksGetCompleted(links))
const actual = linksReducer(undefined, linksGetCompleted(links))
const expected = links
expect(_.isEqual(actual, expected)).toBe(true)
})

View File

@ -1,25 +1,29 @@
import buildInfluxQLQuery, {buildQuery} from 'utils/influxql'
import buildInfluxQLQuery, {buildQuery} from 'src/utils/influxql'
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
import {NONE, NULL_STRING} from 'shared/constants/queryFillOptions'
import {NONE, NULL_STRING} from 'src/shared/constants/queryFillOptions'
import {TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
function mergeConfig(options) {
return Object.assign({}, defaultQueryConfig(123), options)
import {QueryConfig} from 'src/types'
function mergeConfig(options: Partial<QueryConfig>) {
return {...defaultQueryConfig({id: '123'}), ...options}
}
describe('buildInfluxQLQuery', () => {
let config, timeBounds
let config
let timeBounds
describe('when information is missing', () => {
it('returns a null select statement', () => {
expect(buildInfluxQLQuery({}, mergeConfig())).toBe(null)
timeBounds = {lower: 'now() - 15m', upper: null}
expect(buildInfluxQLQuery(timeBounds, mergeConfig({}))).toBe(null)
let merged = mergeConfig({database: 'db1'})
let actual = buildInfluxQLQuery({}, merged)
let actual = buildInfluxQLQuery(timeBounds, merged)
expect(actual).toBe(null) // no measurement
merged = mergeConfig({database: 'db1', measurement: 'm1'})
actual = buildInfluxQLQuery({}, merged)
actual = buildInfluxQLQuery(timeBounds, merged)
expect(actual).toBe(null) // no fields
})
})
@ -31,10 +35,11 @@ describe('buildInfluxQLQuery', () => {
measurement: 'm1',
fields: [{value: 'f1', type: 'field'}],
})
timeBounds = {lower: null, upper: null}
})
it('builds the right query', () => {
expect(buildInfluxQLQuery({}, config)).toBe(
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
'SELECT "f1" FROM "db1".."m1"'
)
})
@ -48,19 +53,21 @@ describe('buildInfluxQLQuery', () => {
retentionPolicy: 'rp1',
fields: [{value: 'f1', type: 'field'}],
})
timeBounds = {lower: 'now() - 1hr'}
})
it('builds the right query', () => {
expect(buildInfluxQLQuery({}, config)).toBe(
'SELECT "f1" FROM "db1"."rp1"."m1"'
)
const actual = buildInfluxQLQuery(timeBounds, config)
const expected = 'SELECT "f1" FROM "db1"."rp1"."m1"'
expect(actual).toBe(expected)
})
it('builds the right query with a time range', () => {
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
timeBounds = {lower: 'now() - 1hr', upper: null}
const actual = buildInfluxQLQuery(timeBounds, config)
const expected =
'SELECT "f1" FROM "db1"."rp1"."m1" WHERE time > now() - 1hr'
)
expect(actual).toBe(expected)
})
})
@ -72,10 +79,11 @@ describe('buildInfluxQLQuery', () => {
retentionPolicy: 'rp1',
fields: [{value: '*', type: 'field'}],
})
timeBounds = {lower: null, upper: null}
})
it('does not quote the star', () => {
expect(buildInfluxQLQuery({}, config)).toBe(
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
'SELECT * FROM "db1"."rp1"."m1"'
)
})
@ -193,7 +201,8 @@ describe('buildInfluxQLQuery', () => {
})
it('builds the right query', () => {
expect(buildInfluxQLQuery({}, config)).toBe(
timeBounds = {lower: null, upper: null}
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
'SELECT "f0", "f1" FROM "db1"."rp1"."m0"'
)
})