import * as T from 'typings' import * as TT from 'typings/tutorial' import * as vscode from 'vscode' import createTestRunner from './services/testRunner' import createWebView from './services/webview/create' import * as hooks from './services/hooks' import logger from './services/logger' import Channel from './channel' export const COMMANDS = { START: 'coderoad.start', CONFIG_TEST_RUNNER: 'coderoad.config_test_runner', RUN_TEST: 'coderoad.run_test', SET_CURRENT_POSITION: 'coderoad.set_current_position', ENTER: 'coderoad.enter', } interface CreateCommandProps { extensionPath: string workspaceState: vscode.Memento } let sendToClient = (action: T.Action): void => { // function is replaced when webview mounts } // This makes it easier to pass the send // function throughout the codebase export const send = (action: T.Action): void => { // log send of event to client logger(`${typeof action === 'string' ? action : action.type}`) sendToClient(action) } export const createCommands = (commandProps: CreateCommandProps): { [key: string]: any } => { const { extensionPath, workspaceState } = commandProps // React panel webview let webview: any let currentPosition: T.Position let testRunner: any const channel = new Channel(workspaceState) const start = async () => { if (webview && webview.state.loaded) { webview.createOrShow() } else { // activate machine webview = await createWebView({ extensionPath, channel, }) // make send to client function exportable // as "send". sendToClient = webview.send } } // run activation if triggered by "workspaceContains" start() return { [COMMANDS.START]: start, [COMMANDS.CONFIG_TEST_RUNNER]: async ({ data, alreadyConfigured, }: { data: TT.Tutorial alreadyConfigured: boolean }) => { if (!alreadyConfigured) { const setupActions = data.config.setup if (setupActions) { hooks.onInit(setupActions, data.id) } } testRunner = createTestRunner(data, { onSuccess: (position: T.Position) => { logger(`Test pass position: ${JSON.stringify(position)}`) // send test pass message back to client channel.context.position.set({ ...position, complete: true }) send({ type: 'TEST_PASS', payload: { position: { ...position, complete: true } } }) }, onFail: (position: T.Position, failSummary: T.TestFail): void => { // send test fail message back to client with failure message send({ type: 'TEST_FAIL', payload: { position, fail: failSummary } }) }, onError: (position: T.Position) => { // TODO: send test error message back to client const message = 'Error with test runner' send({ type: 'TEST_ERROR', payload: { position, message } }) }, onRun: (position: T.Position) => { // send test run message back to client send({ type: 'START_TEST', payload: { position } }) }, onLoadSubtasks: ({ summary }) => { send({ type: 'LOAD_SUBTASK_RESULTS', payload: { summary } }) }, }) }, [COMMANDS.SET_CURRENT_POSITION]: (position: T.Position) => { // set from last setup stepAction currentPosition = position channel.context.position.set(position) }, [COMMANDS.RUN_TEST]: ({ subtasks, callbacks, }: { subtasks?: boolean; callbacks?: { onSuccess: () => void } } = {}) => { testRunner({ position: currentPosition, onSuccess: callbacks?.onSuccess, subtasks }) }, [COMMANDS.ENTER]: () => { send({ type: 'KEY_PRESS_ENTER' }) }, } }