diff --git a/src/ErrorHandler.tsx b/src/ErrorHandler.ts similarity index 94% rename from src/ErrorHandler.tsx rename to src/ErrorHandler.ts index c8b66687..53ad9064 100644 --- a/src/ErrorHandler.tsx +++ b/src/ErrorHandler.ts @@ -12,8 +12,7 @@ export interface ErrorCallback { } export class ErrorHandler { - - static callbacks: ErrorCallback[] = []; + private static callbacks: ErrorCallback[] = []; public static registerCallbacks(callbacks: ErrorCallback[]): string { let guid = generateGUID(); diff --git a/src/ErrorInjector.tsx b/src/ErrorInjector.ts similarity index 84% rename from src/ErrorInjector.tsx rename to src/ErrorInjector.ts index c9a0610a..2cfc2bec 100644 --- a/src/ErrorInjector.tsx +++ b/src/ErrorInjector.ts @@ -4,14 +4,7 @@ */ import { AT } from './types/ActionTypes' -export interface ErrorCallback { - actionType: AT; - callback: ((actionType: AT) => void); - guid?: string; -} - export class ErrorInjector { - private static disabledActions: string[] = []; public static SetError(actionType: string, enabled: boolean) { @@ -26,5 +19,4 @@ export class ErrorInjector { public static ShouldError(actionType: AT) { return (ErrorInjector.disabledActions.find(s => s == AT[AT[actionType]]) != null) } - } \ No newline at end of file diff --git a/src/actions/displayActions.ts b/src/actions/displayActions.ts index 9ba91317..ff9f12d0 100644 --- a/src/actions/displayActions.ts +++ b/src/actions/displayActions.ts @@ -43,9 +43,9 @@ export const setErrorDisplay = (errorType: ErrorType, title: string, messages: s return { type: AT.SET_ERROR_DISPLAY, errorType, - title: title, - messages: messages, - actionType: actionType + title, + messages, + actionType } } diff --git a/src/components/modals/AppCreator.tsx b/src/components/modals/AppCreator.tsx index 52b3b2ba..e99d5d51 100644 --- a/src/components/modals/AppCreator.tsx +++ b/src/components/modals/AppCreator.tsx @@ -172,7 +172,8 @@ class AppCreator extends React.Component { const appInput = this.getAppInput(); this.props.onSubmit(appInput, source) } - catch (error) { + catch (e) { + const error = e as Error this.props.setErrorDisplay(ErrorType.Error, error.message, ["Invalid file contents"], AT.CREATE_APPLICATION_ASYNC) } } diff --git a/src/components/modals/ErrorPanel.tsx b/src/components/modals/ErrorPanel.tsx index 6b92b4fb..bd8c7549 100644 --- a/src/components/modals/ErrorPanel.tsx +++ b/src/components/modals/ErrorPanel.tsx @@ -10,7 +10,7 @@ import { Panel, PanelType, FontClassNames, DefaultButton } from 'office-ui-fabri import { clearErrorDisplay } from '../../actions/displayActions' import { State } from '../../types' import { ErrorHandler } from '../../ErrorHandler' -import { injectIntl, InjectedIntlProps, InjectedIntl } from 'react-intl' +import { injectIntl, InjectedIntlProps, InjectedIntl, FormattedMessage } from 'react-intl' import { AT } from '../../types/ActionTypes' import { FM } from '../../react-intl-messages' import { GetTip, TipType } from '../ToolTips' @@ -88,7 +88,11 @@ class ErrorPanel extends React.Component { customWidth='600px' >
- {this.props.error.actionType &&
{this.props.error.actionType} Failed
} + {this.props.error.actionType &&
+ Failed
}
{this.props.error.error}
{this.props.error && this.props.error.messages.map((message: any) => { if (message == null) diff --git a/src/react-intl-messages.ts b/src/react-intl-messages.ts index 2f3643df..d969a032 100644 --- a/src/react-intl-messages.ts +++ b/src/react-intl-messages.ts @@ -1,3 +1,5 @@ +import { AT } from "./types"; + /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. @@ -488,8 +490,6 @@ export default { [FM.BUTTON_IMPORT]: 'Import', [FM.BUTTON_INFO]: 'Info', - // Custom Errors - [FM.CUSTOMERROR_NETWORK_ERROR]: 'Is your Bot running?', // Dashboard [FM.DASHBOARD_TITLE]: 'Overview', @@ -849,6 +849,40 @@ export default { [FM.TRAINDIALOGMODAL_PRIMARYBUTTON_ARIADESCRIPTION]: 'Done', [FM.TRAINDIALOGMODAL_PRIMARYBUTTON_TEXT]: 'Done', [FM.TRAINDIALOGMODAL_CONFIRMDELETE_TITLE]: 'Are you sure you want to delete this Training Dialog?', + + // Error Messages + [FM.CUSTOMERROR_NETWORK_ERROR]: 'Is your Bot running?', + /** + * This is kind of hack which re-uses redux action types as unit strings for localized error messages + * + * It should probably be `errors.application.create` but we're using something like: `CREATE_APPLICATION_ASYNC` + */ + [AT.CREATE_APPLICATION_ASYNC]: 'Creating application', + [AT.COPY_APPLICATION_ASYNC]: 'Copying application ', + [AT.CREATE_ENTITY_ASYNC]: 'Creating entity', + [AT.CREATE_ACTION_ASYNC]: 'Creating action', + [AT.CREATE_APP_TAG_ASYNC]: 'Creating application tag', + [AT.CREATE_CHAT_SESSION_ASYNC]: 'Creating chat session', + [AT.CREATE_TEACH_SESSION_ASYNC]: 'Creating teach session', + [AT.CREATE_TEACH_SESSION_FROMHISTORYASYNC]: 'Creating teach session from history', + [AT.CREATE_TEACH_SESSION_FROMUNDOASYNC]: 'Creating teach session from undo', + [AT.FETCH_HISTORY_ASYNC]: 'Fetching history', + [AT.FETCH_TUTORIALS_ASYNC]: 'Fetching tutorials', + [AT.FETCH_APPSOURCE_ASYNC]: 'Fetching application source', + [AT.FETCH_ENTITY_DELETE_VALIDATION_ASYNC]: 'Fetching entity deletion information', + [AT.FETCH_ENTITY_EDIT_VALIDATION_ASYNC]: 'Fetching entity edit information', + [AT.FETCH_ACTION_DELETE_VALIDATION_ASYNC]: 'Fetching action delete information', + [AT.FETCH_ACTION_EDIT_VALIDATION_ASYNC]: 'Fetching action edit information', + [AT.INIT_MEMORY_ASYNC]: 'Initializing memory', + [AT.RUN_EXTRACTOR_ASYNC]: 'Running extractor', + [AT.GET_SCORES_ASYNC]: 'Fetching scores', + [AT.RUN_SCORER_ASYNC]: 'Running scorer', + [AT.POST_SCORE_FEEDBACK_ASYNC]: 'Fetching score feedback', + [AT.EDIT_ENTITY_ASYNC]: 'Editing entity', + [AT.EDIT_ACTION_ASYNC]: 'Editing action', + [AT.EDIT_TRAINDIALOG_ASYNC]: 'Editing train dialog', + [AT.EDIT_APP_LIVE_TAG_ASYNC]: 'Editing application live tag', + [AT.EDIT_APP_EDITING_TAG_ASYNC]: 'Editing application editing tag', }, 'ko': { [FM.ABOUT_TITLE]: '약', diff --git a/src/reducers/errorReducer.ts b/src/reducers/errorReducer.ts index e81916b5..d59cc3f6 100644 --- a/src/reducers/errorReducer.ts +++ b/src/reducers/errorReducer.ts @@ -12,17 +12,27 @@ const initialState: ErrorState = { error: null, messages: null, actionType: AT.NO_OP -}; +} const errorReducer: Reducer = (state = initialState, action: ActionObject): ErrorState => { switch (action.type) { case AT.CLEAR_ERROR_DISPLAY: - return { ...initialState }; + return { ...initialState } case AT.SET_ERROR_DISPLAY: - return { errorType: action.errorType, error: action.title, messages: action.messages, actionType: action.actionType } + return { + errorType: action.errorType, + error: action.title, + messages: action.messages, + actionType: action.actionType + } case AT.FETCH_BOTINFO_FULFILLED: if (action.botInfo.validationErrors.length > 0) { - return { errorType: ErrorType.Error, error: `Configuration Error`, messages: action.botInfo.validationErrors, actionType: null } + return { + errorType: ErrorType.Error, + error: `Configuration Error`, + messages: action.botInfo.validationErrors, + actionType: null + } } return { ...state } default: diff --git a/src/routes/Apps/App/Index.tsx b/src/routes/Apps/App/Index.tsx index be5f261d..89e74184 100644 --- a/src/routes/Apps/App/Index.tsx +++ b/src/routes/Apps/App/Index.tsx @@ -16,7 +16,6 @@ import { AppBase, AppDefinition, ActionBase, ActionTypes, ApiAction, CardAction, import { injectIntl, InjectedIntlProps } from 'react-intl' import { FM } from '../../../react-intl-messages' import { State } from '../../../types'; -import { setErrorDisplay } from '../../../actions/displayActions'; import { Icon } from 'office-ui-fabric-react/lib/Icon' import Entities from './Entities' import TrainDialogs from './TrainDialogs' @@ -245,7 +244,6 @@ class Index extends React.Component { } const mapDispatchToProps = (dispatch: any) => { return bindActionCreators({ - setErrorDisplay, setCurrentApp: actions.display.setCurrentApp, createApplicationThunkAsync: actions.create.createApplicationThunkAsync, fetchAppSource: actions.fetch.fetchAppSourceThunkAsync, diff --git a/src/routes/Apps/App/LogDialogs.tsx b/src/routes/Apps/App/LogDialogs.tsx index 86b16c67..a89b880c 100644 --- a/src/routes/Apps/App/LogDialogs.tsx +++ b/src/routes/Apps/App/LogDialogs.tsx @@ -17,7 +17,6 @@ import { } from '../../../actions/createActions' import { deleteLogDialogThunkAsync } from '../../../actions/deleteActions'; import { fetchAllLogDialogsAsync, fetchHistoryThunkAsync } from '../../../actions/fetchActions'; -import { setErrorDisplay } from '../../../actions/displayActions'; import { injectIntl, InjectedIntl, InjectedIntlProps, FormattedMessage } from 'react-intl' import { FM } from '../../../react-intl-messages' import { Activity } from 'botframework-directlinejs'; @@ -599,7 +598,7 @@ class LogDialogs extends React.Component { teach={this.props.teachSessions.current} dialogMode={this.props.teachSessions.mode} isOpen={this.state.isTeachDialogModalOpen} - onClose={this.onCloseTeachSession} + onClose={this.onCloseTeachSession} onUndo={(popRound) => this.onUndoTeachStep(popRound)} history={this.state.isTeachDialogModalOpen ? this.state.history : null} lastAction={this.state.lastAction} @@ -618,7 +617,6 @@ const mapDispatchToProps = (dispatch: any) => { deleteLogDialogThunkAsync, fetchAllLogDialogsAsync, fetchHistoryThunkAsync, - setErrorDisplay }, dispatch) } const mapStateToProps = (state: State) => { diff --git a/src/routes/Apps/App/TrainDialogs.tsx b/src/routes/Apps/App/TrainDialogs.tsx index 27c43f66..381c3e2c 100644 --- a/src/routes/Apps/App/TrainDialogs.tsx +++ b/src/routes/Apps/App/TrainDialogs.tsx @@ -21,7 +21,6 @@ import { deleteTrainDialogThunkAsync, deleteMemoryThunkAsync } from '../../../ac import { editTrainDialogThunkAsync } from '../../../actions/updateActions'; import { injectIntl, InjectedIntl, InjectedIntlProps, FormattedMessage } from 'react-intl' import { FM } from '../../../react-intl-messages' -import { setErrorDisplay } from '../../../actions/displayActions'; import { Activity } from 'botframework-directlinejs'; import { autobind } from 'office-ui-fabric-react/lib/Utilities'; import { getDefaultEntityMap } from '../../../util'; @@ -750,7 +749,6 @@ const mapDispatchToProps = (dispatch: any) => { createTeachSessionFromUndoThunkAsync, createTeachSessionFromHistoryThunkAsync, editTrainDialogThunkAsync, - setErrorDisplay }, dispatch) } const mapStateToProps = (state: State) => { diff --git a/src/services/poller.test.ts b/src/services/poller.test.ts index 56b70a9c..798307c0 100644 --- a/src/services/poller.test.ts +++ b/src/services/poller.test.ts @@ -26,7 +26,7 @@ describe('Poller', () => { await poller1.addPoll(pollConfig) expect(onExpiredMock.mock.calls.length).toBe(1) - expect(onUpdateMock.mock.calls.length).toBeGreaterThan(3) + expect(onUpdateMock.mock.calls.length).toBeGreaterThanOrEqual(3) }) test('poll should invoke request, isResolved, and onUpdate for each interval', async () => { @@ -143,6 +143,6 @@ describe('Poller', () => { await p1 // Will still resolve after 400 expiration const after = new Date().getTime() - expect(after - now).toBeGreaterThan(400) + expect(after - now).toBeGreaterThanOrEqual(400) }) }) \ No newline at end of file