From 7515204eb63b94ef8d62dcd04baf34b4c0c8dd29 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 16:33:52 +0800 Subject: [PATCH 01/16] Migrate GitHubPersistenceSaga to use helper --- src/commons/sagas/GitHubPersistenceSaga.ts | 32 ++++++++++------------ 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/commons/sagas/GitHubPersistenceSaga.ts b/src/commons/sagas/GitHubPersistenceSaga.ts index e45e402805..aac47c7978 100644 --- a/src/commons/sagas/GitHubPersistenceSaga.ts +++ b/src/commons/sagas/GitHubPersistenceSaga.ts @@ -2,35 +2,33 @@ import { GetResponseDataTypeFromEndpointMethod, GetResponseTypeFromEndpointMethod } from '@octokit/types'; -import { SagaIterator } from 'redux-saga'; -import { call, put, select, takeLatest } from 'redux-saga/effects'; -import { - githubOpenFile, - githubSaveFile, - githubSaveFileAs -} from 'src/features/github/GitHubActions'; - +import { call, put, select } from 'redux-saga/effects'; +import GitHubActions from 'src/features/github/GitHubActions'; import * as GitHubUtils from '../../features/github/GitHubUtils'; import { getGitHubOctokitInstance } from '../../features/github/GitHubUtils'; import { store } from '../../pages/createStore'; -import { loginGitHub, logoutGitHub } from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { OverallState } from '../application/ApplicationTypes'; import FileExplorerDialog, { FileExplorerDialogProps } from '../gitHubOverlay/FileExplorerDialog'; import RepositoryDialog, { RepositoryDialogProps } from '../gitHubOverlay/RepositoryDialog'; +import { combineSagaHandlers } from '../redux/utils'; import { actions } from '../utils/ActionsHelper'; import Constants from '../utils/Constants'; import { promisifyDialog } from '../utils/DialogHelper'; import { showSuccessMessage } from '../utils/notifications/NotificationsHelper'; import { EditorTabState } from '../workspace/WorkspaceTypes'; -export function* GitHubPersistenceSaga(): SagaIterator { - yield takeLatest(loginGitHub.type, githubLoginSaga); - yield takeLatest(logoutGitHub.type, githubLogoutSaga); - - yield takeLatest(githubOpenFile.type, githubOpenFileSaga); - yield takeLatest(githubSaveFile.type, githubSaveFileSaga); - yield takeLatest(githubSaveFileAs.type, githubSaveFileAsSaga); -} +export const GitHubPersistenceSaga = combineSagaHandlers( + // TODO: Refactor and combine in a future commit + { ...SessionActions, ...GitHubActions }, + { + loginGitHub: githubLoginSaga, + logoutGitHub: githubLogoutSaga, + githubOpenFile: githubOpenFileSaga, + githubSaveFile: githubSaveFileSaga, + githubSaveFileAs: githubSaveFileAsSaga + } +); function* githubLoginSaga() { const githubOauthLoginLink = `https://github.com/login/oauth/authorize?client_id=${Constants.githubClientId}&scope=repo`; From 198a6bdd745c910441a7810c5fd609f5a466f63a Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 16:47:13 +0800 Subject: [PATCH 02/16] Use default import for SessionActions in components --- src/commons/achievement/AchievementOverview.tsx | 6 +++--- src/commons/achievement/AchievementView.tsx | 12 ++++-------- src/commons/application/Application.tsx | 4 ++-- src/commons/assessment/Assessment.tsx | 14 ++++++-------- .../assessmentWorkspace/AssessmentWorkspace.tsx | 15 +++++---------- src/commons/profile/Profile.tsx | 6 +++--- src/pages/academy/Academy.tsx | 15 +++++---------- .../subcomponents/NotificationConfigPanel.tsx | 17 +++++++---------- src/pages/academy/grading/Grading.tsx | 6 +++--- .../grading/subcomponents/GradingActions.tsx | 17 ++++++----------- .../grading/subcomponents/GradingEditor.tsx | 13 +++++-------- src/pages/login/Login.tsx | 9 ++++++--- 12 files changed, 55 insertions(+), 79 deletions(-) diff --git a/src/commons/achievement/AchievementOverview.tsx b/src/commons/achievement/AchievementOverview.tsx index d902c3e447..707acd7ed8 100644 --- a/src/commons/achievement/AchievementOverview.tsx +++ b/src/commons/achievement/AchievementOverview.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { AchievementUser } from 'src/features/achievement/AchievementTypes'; -import { fetchTotalXp, fetchTotalXpAdmin } from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { useTypedSelector } from '../utils/Hooks'; import AchievementLevel from './overview/AchievementLevel'; @@ -20,9 +20,9 @@ const AchievementOverview: React.FC = ({ name, userState }) => { useEffect(() => { // If user is student, fetch assessment details from assessment route instead, as seen below if (crid && crid !== userCrid) { - dispatch(fetchTotalXpAdmin(crid)); + dispatch(SessionActions.fetchTotalXpAdmin(crid)); } else { - dispatch(fetchTotalXp()); + dispatch(SessionActions.fetchTotalXp()); } }, [crid, userCrid, dispatch]); diff --git a/src/commons/achievement/AchievementView.tsx b/src/commons/achievement/AchievementView.tsx index 7644a40989..06cd62623b 100644 --- a/src/commons/achievement/AchievementView.tsx +++ b/src/commons/achievement/AchievementView.tsx @@ -9,11 +9,7 @@ import { getAbilityGlow } from '../../features/achievement/AchievementConstants'; import { AchievementStatus, AchievementUser } from '../../features/achievement/AchievementTypes'; -import { - fetchAssessment, - fetchAssessmentAdmin, - fetchAssessmentOverviews -} from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { Assessment } from '../assessment/AssessmentTypes'; import { useTypedSelector } from '../utils/Hooks'; import AchievementCommentCard from './AchievementCommentCard'; @@ -40,17 +36,17 @@ const AchievementView: React.FC = ({ focusUuid, userState }) => { const dispatch = useDispatch(); useEffect(() => { - dispatch(fetchAssessmentOverviews()); + dispatch(SessionActions.fetchAssessmentOverviews()); if (!assessmentId) { return; } if (isAdminView) { // Fetch selected user's assessment from admin route // Safe to use non-null assertion (refer to `isAdminView` declaration above) - dispatch(fetchAssessmentAdmin(assessmentId, courseRegId!)); + dispatch(SessionActions.fetchAssessmentAdmin(assessmentId, courseRegId!)); } else { // If user is student, fetch assessment details from assessment route instead, as seen below - dispatch(fetchAssessment(assessmentId)); + dispatch(SessionActions.fetchAssessment(assessmentId)); } }, [dispatch, assessmentId, courseRegId, isAdminView]); diff --git a/src/commons/application/Application.tsx b/src/commons/application/Application.tsx index c9adc1be18..45562ee80c 100644 --- a/src/commons/application/Application.tsx +++ b/src/commons/application/Application.tsx @@ -6,7 +6,7 @@ import NavigationBar from '../navigationBar/NavigationBar'; import Constants from '../utils/Constants'; import { useLocalStorageState, useSession } from '../utils/Hooks'; import { defaultWorkspaceSettings, WorkspaceSettingsContext } from '../WorkspaceSettingsContext'; -import { fetchUserAndCourse } from './actions/SessionActions'; +import SessionActions from './actions/SessionActions'; const Application: React.FC = () => { const dispatch = useDispatch(); @@ -26,7 +26,7 @@ const Application: React.FC = () => { // if the user was previously logged in React.useEffect(() => { if (isLoggedIn) { - dispatch(fetchUserAndCourse()); + dispatch(SessionActions.fetchUserAndCourse()); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/commons/assessment/Assessment.tsx b/src/commons/assessment/Assessment.tsx index a4cbd75279..ed3aad57f0 100644 --- a/src/commons/assessment/Assessment.tsx +++ b/src/commons/assessment/Assessment.tsx @@ -27,11 +27,7 @@ import { NavLink } from 'react-router-dom'; import { numberRegExp } from 'src/features/academy/AcademyTypes'; import defaultCoverImage from '../../assets/default_cover_image.jpg'; -import { - acknowledgeNotifications, - fetchAssessmentOverviews, - submitAssessment -} from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { Role } from '../application/ApplicationTypes'; import AssessmentWorkspace, { AssessmentWorkspaceProps @@ -74,7 +70,7 @@ const Assessment: React.FC = props => { const setBetchaAssessmentNull = () => setBetchaAssessment(null); const handleSubmitAssessment = () => { if (betchaAssessment) { - dispatch(submitAssessment(betchaAssessment.id)); + dispatch(SessionActions.submitAssessment(betchaAssessment.id)); setBetchaAssessmentNull(); } }; @@ -137,7 +133,9 @@ const Assessment: React.FC = props => { icon={icon} minimal={true} onClick={() => - dispatch(acknowledgeNotifications(filterNotificationsByAssessment(overview.id))) + dispatch( + SessionActions.acknowledgeNotifications(filterNotificationsByAssessment(overview.id)) + ) } > {label} @@ -413,7 +411,7 @@ const Assessment: React.FC = props => {
dispatch(fetchAssessmentOverviews())} + loadContentDispatch={() => dispatch(SessionActions.fetchAssessmentOverviews())} /> {betchaDialog}
diff --git a/src/commons/assessmentWorkspace/AssessmentWorkspace.tsx b/src/commons/assessmentWorkspace/AssessmentWorkspace.tsx index 7a29a60dd6..68a6c3f325 100644 --- a/src/commons/assessmentWorkspace/AssessmentWorkspace.tsx +++ b/src/commons/assessmentWorkspace/AssessmentWorkspace.tsx @@ -28,12 +28,7 @@ import { KeyboardCommand, SelectionRange } from '../../features/sourceRecorder/SourceRecorderTypes'; -import { - checkAnswerLastModifiedAt, - fetchAssessment, - fetchTeamFormationOverview, - submitAnswer -} from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { defaultWorkspaceManager } from '../application/ApplicationTypes'; import { AssessmentConfiguration, @@ -174,7 +169,7 @@ const AssessmentWorkspace: React.FC = props => { } = useMemo(() => { return { handleTeamOverviewFetch: (assessmentId: number) => - dispatch(fetchTeamFormationOverview(assessmentId)), + dispatch(SessionActions.fetchTeamFormationOverview(assessmentId)), handleTestcaseEval: (id: number) => dispatch(evalTestcase(workspaceLocation, id)), handleClearContext: (library: Library, shouldInitLibrary: boolean) => dispatch(beginClearContext(workspaceLocation, library, shouldInitLibrary)), @@ -187,16 +182,16 @@ const AssessmentWorkspace: React.FC = props => { handleRunAllTestcases: () => dispatch(runAllTestcases(workspaceLocation)), handleEditorEval: () => dispatch(evalEditor(workspaceLocation)), handleAssessmentFetch: (assessmentId: number, assessmentPassword?: string) => - dispatch(fetchAssessment(assessmentId, assessmentPassword)), + dispatch(SessionActions.fetchAssessment(assessmentId, assessmentPassword)), handleEditorValueChange: (editorTabIndex: number, newEditorValue: string) => dispatch(updateEditorValue(workspaceLocation, editorTabIndex, newEditorValue)), handleEditorUpdateBreakpoints: (editorTabIndex: number, newBreakpoints: string[]) => dispatch(setEditorBreakpoint(workspaceLocation, editorTabIndex, newBreakpoints)), handleReplEval: () => dispatch(evalRepl(workspaceLocation)), handleCheckLastModifiedAt: (id: number, lastModifiedAt: string, saveAnswer: Function) => - dispatch(checkAnswerLastModifiedAt(id, lastModifiedAt, saveAnswer)), + dispatch(SessionActions.checkAnswerLastModifiedAt(id, lastModifiedAt, saveAnswer)), handleSave: (id: number, answer: number | string | ContestEntry[]) => - dispatch(submitAnswer(id, answer)), + dispatch(SessionActions.submitAnswer(id, answer)), handleUpdateHasUnsavedChanges: (hasUnsavedChanges: boolean) => dispatch(updateHasUnsavedChanges(workspaceLocation, hasUnsavedChanges)), handleEnableTokenCounter: () => dispatch(enableTokenCounter(workspaceLocation)), diff --git a/src/commons/profile/Profile.tsx b/src/commons/profile/Profile.tsx index d9abf07520..98e7637321 100644 --- a/src/commons/profile/Profile.tsx +++ b/src/commons/profile/Profile.tsx @@ -3,7 +3,7 @@ import { IconName, IconNames } from '@blueprintjs/icons'; import React, { useEffect, useState } from 'react'; import { useDispatch } from 'react-redux'; -import { fetchAssessmentOverviews, fetchTotalXp } from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { AssessmentStatuses, AssessmentType } from '../assessment/AssessmentTypes'; import Constants from '../utils/Constants'; import { useSession } from '../utils/Hooks'; @@ -34,13 +34,13 @@ const Profile: React.FC = props => { useEffect(() => { if (isLoggedIn && isEnrolledInACourse && !assessmentOverviews) { // If assessment overviews are not loaded, fetch them - dispatch(fetchAssessmentOverviews()); + dispatch(SessionActions.fetchAssessmentOverviews()); } }, [assessmentOverviews, dispatch, isLoggedIn, isEnrolledInACourse, xp]); useEffect(() => { if (isEnrolledInACourse && !xp) { - dispatch(fetchTotalXp()); + dispatch(SessionActions.fetchTotalXp()); } }, [isEnrolledInACourse, dispatch, xp]); diff --git a/src/pages/academy/Academy.tsx b/src/pages/academy/Academy.tsx index 4dc179621b..34dab9a266 100644 --- a/src/pages/academy/Academy.tsx +++ b/src/pages/academy/Academy.tsx @@ -9,12 +9,7 @@ import Constants from 'src/commons/utils/Constants'; import { useSession } from 'src/commons/utils/Hooks'; import classes from 'src/styles/Academy.module.scss'; -import { - fetchNotifications, - fetchStudents, - fetchTeamFormationOverviews, - updateLatestViewedCourse -} from '../../commons/application/actions/SessionActions'; +import SessionActions from '../../commons/application/actions/SessionActions'; import Assessment from '../../commons/assessment/Assessment'; import { assessmentTypeLink } from '../../commons/utils/ParamParseHelper'; import { @@ -41,9 +36,9 @@ import TeamFormation from './teamFormation/TeamFormation'; const Academy: React.FC = () => { const dispatch = useDispatch(); React.useEffect(() => { - dispatch(fetchStudents()); - dispatch(fetchNotifications()); - dispatch(fetchTeamFormationOverviews(false)); + dispatch(SessionActions.fetchStudents()); + dispatch(SessionActions.fetchNotifications()); + dispatch(SessionActions.fetchTeamFormationOverviews(false)); }, [dispatch]); const { agreedToResearch, assessmentConfigurations, enableGame, role } = useSession(); @@ -122,7 +117,7 @@ const CourseSelectingAcademy: React.FC = () => { } if (routeCourseId !== undefined && !Number.isNaN(routeCourseId) && courseId !== routeCourseId) { - dispatch(updateLatestViewedCourse(routeCourseId)); + dispatch(SessionActions.updateLatestViewedCourse(routeCourseId)); } }, [courseId, dispatch, routeCourseId, navigate, routeCourseIdStr]); diff --git a/src/pages/academy/adminPanel/subcomponents/NotificationConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/NotificationConfigPanel.tsx index 5f23eb4190..b02582e18b 100644 --- a/src/pages/academy/adminPanel/subcomponents/NotificationConfigPanel.tsx +++ b/src/pages/academy/adminPanel/subcomponents/NotificationConfigPanel.tsx @@ -4,12 +4,7 @@ import { AgGridReact } from 'ag-grid-react'; import { cloneDeep } from 'lodash'; import React, { useEffect, useState } from 'react'; import { useDispatch } from 'react-redux'; -import { - deleteTimeOptions, - fetchNotificationConfigs, - updateNotificationConfigs, - updateTimeOptions -} from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { NotificationConfiguration, TimeOption } from 'src/commons/application/types/SessionTypes'; import { useTypedSelector } from 'src/commons/utils/Hooks'; @@ -53,7 +48,7 @@ const NotificationConfigPanel = () => { }; useEffect(() => { - dispatch(fetchNotificationConfigs()); + dispatch(SessionActions.fetchNotificationConfigs()); }, [dispatch]); useEffect(() => { @@ -176,14 +171,16 @@ const NotificationConfigPanel = () => { }); if (allTimeOptions.length > 0) { - dispatch(updateTimeOptions(allTimeOptions)); + dispatch(SessionActions.updateTimeOptions(allTimeOptions)); } if (timeOptionsToDelete.length > 0) { - dispatch(deleteTimeOptions(timeOptionsToDelete.map(timeOption => timeOption.id))); + dispatch( + SessionActions.deleteTimeOptions(timeOptionsToDelete.map(timeOption => timeOption.id)) + ); setTimeOptionsToDelete([]); } - dispatch(updateNotificationConfigs(notificationConfig.current ?? [])); + dispatch(SessionActions.updateNotificationConfigs(notificationConfig.current ?? [])); } }; diff --git a/src/pages/academy/grading/Grading.tsx b/src/pages/academy/grading/Grading.tsx index 529758da4e..32943bb689 100644 --- a/src/pages/academy/grading/Grading.tsx +++ b/src/pages/academy/grading/Grading.tsx @@ -6,7 +6,7 @@ import { Button, Card, Flex, Text, Title } from '@tremor/react'; import React, { useCallback, useState } from 'react'; import { useDispatch } from 'react-redux'; import { Navigate, useParams } from 'react-router'; -import { fetchGradingOverviews } from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { Role } from 'src/commons/application/ApplicationTypes'; import SimpleDropdown from 'src/commons/SimpleDropdown'; import { useSession } from 'src/commons/utils/Hooks'; @@ -53,7 +53,7 @@ const Grading: React.FC = () => { const updateGradingOverviewsCallback = useCallback( (page: number, filterParams: Object) => { dispatch( - fetchGradingOverviews( + SessionActions.fetchGradingOverviews( showAllGroups, unpublishedToBackendParams(showAllSubmissions), paginationToBackendParams(page, pageSize), @@ -102,7 +102,7 @@ const Grading: React.FC = () => { return ( dispatch(fetchGradingOverviews(showAllGroups))} + loadContentDispatch={() => dispatch(SessionActions.fetchGradingOverviews(showAllGroups))} display={ gradingOverviews?.data === undefined ? ( loadingDisplay diff --git a/src/pages/academy/grading/subcomponents/GradingActions.tsx b/src/pages/academy/grading/subcomponents/GradingActions.tsx index c99df8c3fd..1b123f3b06 100644 --- a/src/pages/academy/grading/subcomponents/GradingActions.tsx +++ b/src/pages/academy/grading/subcomponents/GradingActions.tsx @@ -1,14 +1,9 @@ -import { Button, Icon as BpIcon } from '@blueprintjs/core'; +import { Icon as BpIcon, Button } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { Flex, Icon } from '@tremor/react'; import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; -import { - publishGrading, - reautogradeSubmission, - unpublishGrading, - unsubmitSubmission -} from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { ProgressStatus, ProgressStatuses } from 'src/commons/assessment/AssessmentTypes'; import { showSimpleConfirmDialog } from 'src/commons/utils/DialogHelper'; import { useTypedSelector } from 'src/commons/utils/Hooks'; @@ -34,7 +29,7 @@ const GradingActions: React.FC = ({ submissionId, progress }) => { positiveLabel: 'Reautograde' }); if (confirm) { - dispatch(reautogradeSubmission(submissionId)); + dispatch(SessionActions.reautogradeSubmission(submissionId)); } }; @@ -45,7 +40,7 @@ const GradingActions: React.FC = ({ submissionId, progress }) => { positiveLabel: 'Unsubmit' }); if (confirm) { - dispatch(unsubmitSubmission(submissionId)); + dispatch(SessionActions.unsubmitSubmission(submissionId)); } }; @@ -56,7 +51,7 @@ const GradingActions: React.FC = ({ submissionId, progress }) => { positiveLabel: 'Publish' }); if (confirm) { - dispatch(publishGrading(submissionId)); + dispatch(SessionActions.publishGrading(submissionId)); } }; @@ -67,7 +62,7 @@ const GradingActions: React.FC = ({ submissionId, progress }) => { positiveLabel: 'Unpublish' }); if (confirm) { - dispatch(unpublishGrading(submissionId)); + dispatch(SessionActions.unpublishGrading(submissionId)); } }; diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx index 7fb564168b..3c162606dd 100644 --- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx +++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx @@ -14,11 +14,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import ReactMde, { ReactMdeProps } from 'react-mde'; import { useDispatch } from 'react-redux'; -import { - reautogradeAnswer, - submitGrading, - submitGradingAndContinue -} from '../../../../commons/application/actions/SessionActions'; +import SessionActions from '../../../../commons/application/actions/SessionActions'; import ControlButton from '../../../../commons/ControlButton'; import Markdown from '../../../../commons/Markdown'; import { Prompt } from '../../../../commons/ReactRouterPrompt'; @@ -58,9 +54,10 @@ const GradingEditor: React.FC = props => { const { handleGradingSave, handleGradingSaveAndContinue, handleReautogradeAnswer } = useMemo( () => ({ - handleGradingSave: (...args) => dispatch(submitGrading(...args)), - handleGradingSaveAndContinue: (...args) => dispatch(submitGradingAndContinue(...args)), - handleReautogradeAnswer: (...args) => dispatch(reautogradeAnswer(...args)) + handleGradingSave: (...args) => dispatch(SessionActions.submitGrading(...args)), + handleGradingSaveAndContinue: (...args) => + dispatch(SessionActions.submitGradingAndContinue(...args)), + handleReautogradeAnswer: (...args) => dispatch(SessionActions.reautogradeAnswer(...args)) }) satisfies { handleGradingSave: GradingSaveFunction; handleGradingSaveAndContinue: GradingSaveFunction; diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx index 8d79691373..4b7a2cb87b 100644 --- a/src/pages/login/Login.tsx +++ b/src/pages/login/Login.tsx @@ -20,7 +20,7 @@ import { AuthProviderType } from 'src/commons/utils/AuthHelper'; import { useSession } from 'src/commons/utils/Hooks'; import classes from 'src/styles/Login.module.scss'; -import { fetchAuth, login } from '../../commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import Constants from '../../commons/utils/Constants'; import { parseQuery } from '../../commons/utils/QueryHelper'; @@ -40,7 +40,10 @@ const Login: React.FC = () => { // `code` parameter from OAuth2 redirect, `ticket` from CAS redirect const authCode = code || ticket; - const handleLogin = useCallback((providerId: string) => dispatch(login(providerId)), [dispatch]); + const handleLogin = useCallback( + (providerId: string) => dispatch(SessionActions.login(providerId)), + [dispatch] + ); const isSaml = Constants.authProviders.get(providerId)?.type === AuthProviderType.SAML_SSO; @@ -58,7 +61,7 @@ const Login: React.FC = () => { // Else fetch JWT tokens and user info from backend when auth provider code is present // SAML does not require code, as relay is handled in backend if ((authCode || isSaml) && !isLoggedIn) { - dispatch(fetchAuth(authCode, providerId)); + dispatch(SessionActions.fetchAuth(authCode, providerId)); } }, [authCode, isSaml, providerId, dispatch, courseId, navigate, isLoggedIn]); From 9b806835ec40eb412cf44970311c2abb1d2d8834 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 16:59:51 +0800 Subject: [PATCH 03/16] Use default import for more files --- .../editingWorkspace/EditingWorkspace.tsx | 4 +-- .../notificationBadge/NotificationBadge.tsx | 4 +-- src/commons/sagas/LoginSaga.ts | 6 ++-- src/commons/sagas/PersistenceSaga.tsx | 4 +-- .../sagas/__tests__/GitHubPersistenceSaga.ts | 4 +-- src/pages/academy/adminPanel/AdminPanel.tsx | 30 ++++++------------ .../subcomponents/GradingWorkspace.tsx | 5 +-- .../groundControl/GroundControlContainer.ts | 9 ++---- .../academy/notiPreference/NotiPreference.tsx | 9 ++---- .../subcomponents/TeamFormationActions.tsx | 4 +-- .../subcomponents/TeamFormationForm.tsx | 6 ++-- .../subcomponents/TeamFormationImport.tsx | 4 +-- .../subcomponents/AchievementDashboard.tsx | 4 +-- src/pages/login/__tests__/Login.tsx | 4 +-- src/pages/playground/Playground.tsx | 31 ++++++++----------- 15 files changed, 54 insertions(+), 74 deletions(-) diff --git a/src/commons/editingWorkspace/EditingWorkspace.tsx b/src/commons/editingWorkspace/EditingWorkspace.tsx index 0f8c7e091e..70f3750436 100644 --- a/src/commons/editingWorkspace/EditingWorkspace.tsx +++ b/src/commons/editingWorkspace/EditingWorkspace.tsx @@ -14,7 +14,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useNavigate } from 'react-router'; -import { submitAnswer } from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { Assessment, AssessmentOverview, @@ -171,7 +171,7 @@ const EditingWorkspace: React.FC = props => { handleUpdateWorkspace: (options: Partial) => dispatch(updateWorkspace(workspaceLocation, options)), handleSubmitAnswer: (id: number, answer: string | number) => - dispatch(submitAnswer(id, answer)), + dispatch(SessionActions.submitAnswer(id, answer)), handleSideContentHeightChange: (heightChange: number) => dispatch(changeSideContentHeight(heightChange, workspaceLocation)), handleUpdateHasUnsavedChanges: (hasUnsavedChanges: boolean) => diff --git a/src/commons/notificationBadge/NotificationBadge.tsx b/src/commons/notificationBadge/NotificationBadge.tsx index 038f3123e5..ea4a2efa4a 100644 --- a/src/commons/notificationBadge/NotificationBadge.tsx +++ b/src/commons/notificationBadge/NotificationBadge.tsx @@ -2,7 +2,7 @@ import { Intent, Popover, PopoverInteractionKind, Position, Tag } from '@bluepri import React from 'react'; import { useDispatch } from 'react-redux'; -import { acknowledgeNotifications } from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { useSession } from '../utils/Hooks'; import { filterNotificationsById } from './NotificationBadgeHelper'; import { Notification, NotificationType, NotificationTypes } from './NotificationBadgeTypes'; @@ -35,7 +35,7 @@ const NotificationBadge: React.FC = props => { if (!props.disableHover) { const makeNotificationTag = (notification: Notification) => { const onRemove = () => - dispatch(acknowledgeNotifications(filterNotificationsById(notification.id))); + dispatch(SessionActions.acknowledgeNotifications(filterNotificationsById(notification.id))); return ( ) => { + yield takeEvery(SessionActions.setUser.type, (action: ReturnType) => { Sentry.setUser({ id: action.payload.userId.toString() }); }); diff --git a/src/commons/sagas/PersistenceSaga.tsx b/src/commons/sagas/PersistenceSaga.tsx index 8bf139ab08..71668b2448 100644 --- a/src/commons/sagas/PersistenceSaga.tsx +++ b/src/commons/sagas/PersistenceSaga.tsx @@ -11,7 +11,7 @@ import { PersistenceFile } from '../../features/persistence/PersistenceTypes'; import { store } from '../../pages/createStore'; -import { logoutGoogle } from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { OverallState } from '../application/ApplicationTypes'; import { ExternalLibraryName } from '../application/types/ExternalTypes'; import { actions } from '../utils/ActionsHelper'; @@ -37,7 +37,7 @@ const MIME_SOURCE = 'text/plain'; // const MIME_FOLDER = 'application/vnd.google-apps.folder'; export function* persistenceSaga(): SagaIterator { - yield takeLatest(logoutGoogle.type, function* () { + yield takeLatest(SessionActions.logoutGoogle.type, function* () { yield put(actions.playgroundUpdatePersistenceFile(undefined)); yield call(ensureInitialised); yield call([gapi.auth2.getAuthInstance(), 'signOut']); diff --git a/src/commons/sagas/__tests__/GitHubPersistenceSaga.ts b/src/commons/sagas/__tests__/GitHubPersistenceSaga.ts index ae23aa0466..0ad09f9171 100644 --- a/src/commons/sagas/__tests__/GitHubPersistenceSaga.ts +++ b/src/commons/sagas/__tests__/GitHubPersistenceSaga.ts @@ -1,5 +1,5 @@ import { expectSaga } from 'redux-saga-test-plan'; -import { removeGitHubOctokitObjectAndAccessToken } from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { actions } from '../../utils/ActionsHelper'; @@ -12,7 +12,7 @@ const GitHubPersistenceSaga = require('../GitHubPersistenceSaga').default; test('logoutGitHub results in REMOVE_GITHUB_OCTOKIT_OBJECT being dispatched', async () => { await expectSaga(GitHubPersistenceSaga) .put({ - type: removeGitHubOctokitObjectAndAccessToken.type, + type: SessionActions.removeGitHubOctokitObjectAndAccessToken.type, payload: {} }) .dispatch(actions.logoutGitHub()) diff --git a/src/pages/academy/adminPanel/AdminPanel.tsx b/src/pages/academy/adminPanel/AdminPanel.tsx index e28ef4bf7f..2199a8db47 100644 --- a/src/pages/academy/adminPanel/AdminPanel.tsx +++ b/src/pages/academy/adminPanel/AdminPanel.tsx @@ -10,17 +10,7 @@ import { addNewUsersToCourse } from 'src/features/academy/AcademyActions'; -import { - deleteAssessmentConfig, - deleteUserCourseRegistration, - fetchAdminPanelCourseRegistrations, - fetchAssessmentConfigs, - fetchCourseConfig, - fetchNotificationConfigs, - updateAssessmentConfigs, - updateCourseConfig, - updateUserRole -} from '../../../commons/application/actions/SessionActions'; +import SessionActions from '../../../commons/application/actions/SessionActions'; import { UpdateCourseConfiguration } from '../../../commons/application/types/SessionTypes'; import ContentDisplay from '../../../commons/ContentDisplay'; import AddStoriesUserPanel from './subcomponents/AddStoriesUserPanel'; @@ -52,10 +42,10 @@ const AdminPanel: React.FC = () => { const session = useSession(); useEffect(() => { - dispatch(fetchCourseConfig()); - dispatch(fetchAssessmentConfigs()); - dispatch(fetchAdminPanelCourseRegistrations()); - dispatch(fetchNotificationConfigs()); + dispatch(SessionActions.fetchCourseConfig()); + dispatch(SessionActions.fetchAssessmentConfigs()); + dispatch(SessionActions.fetchAdminPanelCourseRegistrations()); + dispatch(SessionActions.fetchNotificationConfigs()); }, [dispatch]); useEffect(() => { @@ -98,7 +88,7 @@ const AdminPanel: React.FC = () => { // Changes made to users are handled separately. const submitHandler = useCallback(() => { if (hasChangesCourseConfig) { - dispatch(updateCourseConfig(courseConfiguration)); + dispatch(SessionActions.updateCourseConfig(courseConfiguration)); setHasChangesCourseConfig(false); } const tableState = tableRef.current?.getData() ?? []; @@ -107,9 +97,9 @@ const AdminPanel: React.FC = () => { const configsToDelete = currentConfigs.filter( config => !currentIds.has(config.assessmentConfigId) ); - configsToDelete.forEach(config => dispatch(deleteAssessmentConfig(config))); + configsToDelete.forEach(config => dispatch(SessionActions.deleteAssessmentConfig(config))); if (hasChangesAssessmentConfig) { - dispatch(updateAssessmentConfigs(tableState)); + dispatch(SessionActions.updateAssessmentConfigs(tableState)); setHasChangesAssessmentConfig(false); } }, [ @@ -159,10 +149,10 @@ const AdminPanel: React.FC = () => { courseRegId={session.courseRegId} userCourseRegistrations={session.userCourseRegistrations} handleUpdateUserRole={(courseRegId, role) => - dispatch(updateUserRole(courseRegId, role)) + dispatch(SessionActions.updateUserRole(courseRegId, role)) } handleDeleteUserFromCourse={(courseRegId: number) => - dispatch(deleteUserCourseRegistration(courseRegId)) + dispatch(SessionActions.deleteUserCourseRegistration(courseRegId)) } /> } diff --git a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx index aba253626b..996933e88e 100644 --- a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx +++ b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx @@ -5,7 +5,7 @@ import { Chapter, Variant } from 'js-slang/dist/types'; import React, { useEffect, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { useNavigate } from 'react-router'; -import { fetchGrading } from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { changeSideContentHeight } from 'src/commons/sideContent/SideContentActions'; import { showSimpleErrorDialog } from 'src/commons/utils/DialogHelper'; import { useTypedSelector } from 'src/commons/utils/Hooks'; @@ -135,7 +135,8 @@ const GradingWorkspace: React.FC = props => { dispatch(updateEditorValue(workspaceLocation, 0, newEditorValue)), handleEditorUpdateBreakpoints: (editorTabIndex: number, newBreakpoints: string[]) => dispatch(setEditorBreakpoint(workspaceLocation, editorTabIndex, newBreakpoints)), - handleGradingFetch: (submissionId: number) => dispatch(fetchGrading(submissionId)), + handleGradingFetch: (submissionId: number) => + dispatch(SessionActions.fetchGrading(submissionId)), handleReplEval: () => dispatch(evalRepl(workspaceLocation)), handleReplOutputClear: () => dispatch(clearReplOutput(workspaceLocation)), handleReplValueChange: (newValue: string) => diff --git a/src/pages/academy/groundControl/GroundControlContainer.ts b/src/pages/academy/groundControl/GroundControlContainer.ts index 482eb62641..0b94c862fc 100644 --- a/src/pages/academy/groundControl/GroundControlContainer.ts +++ b/src/pages/academy/groundControl/GroundControlContainer.ts @@ -1,10 +1,7 @@ import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; -import { - fetchAssessmentOverviews, - fetchCourseConfig -} from '../../../commons/application/actions/SessionActions'; +import SessionActions from '../../../commons/application/actions/SessionActions'; import { OverallState } from '../../../commons/application/ApplicationTypes'; import { assignEntriesForVoting, @@ -26,13 +23,13 @@ const mapDispatchToProps: MapDispatchToProps = (dispatch: Dis { handleAssessmentChangeDate: changeDateAssessment, handleAssessmentChangeTeamSize: changeTeamSizeAssessment, - handleAssessmentOverviewFetch: fetchAssessmentOverviews, + handleAssessmentOverviewFetch: SessionActions.fetchAssessmentOverviews, handleDeleteAssessment: deleteAssessment, handleUploadAssessment: uploadAssessment, handlePublishAssessment: publishAssessment, handlePublishGradingAll: publishGradingAll, handleUnpublishGradingAll: unpublishGradingAll, - handleFetchCourseConfigs: fetchCourseConfig, + handleFetchCourseConfigs: SessionActions.fetchCourseConfig, handleConfigureAssessment: configureAssessment, handleAssignEntriesForVoting: assignEntriesForVoting }, diff --git a/src/pages/academy/notiPreference/NotiPreference.tsx b/src/pages/academy/notiPreference/NotiPreference.tsx index a03d62b8c0..bd9a9e47ba 100644 --- a/src/pages/academy/notiPreference/NotiPreference.tsx +++ b/src/pages/academy/notiPreference/NotiPreference.tsx @@ -4,10 +4,7 @@ import { AgGridReact } from 'ag-grid-react'; import { cloneDeep } from 'lodash'; import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; -import { - fetchConfigurableNotificationConfigs, - updateNotificationPreferences -} from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { NotificationConfiguration, NotificationPreference, @@ -34,7 +31,7 @@ const NotiPreference: React.FC = () => { React.useEffect(() => { if (!session.courseRegId) return; - dispatch(fetchConfigurableNotificationConfigs(session.courseRegId)); + dispatch(SessionActions.fetchConfigurableNotificationConfigs(session.courseRegId)); }, [dispatch, session.courseRegId]); // After updated configs have been loaded from the backend, put them into local React state @@ -144,7 +141,7 @@ const NotiPreference: React.FC = () => { notificationConfigId: config.id }; }) ?? []; - dispatch(updateNotificationPreferences(preferences, session.courseRegId!)); + dispatch(SessionActions.updateNotificationPreferences(preferences, session.courseRegId!)); setHasChanges(false); }; diff --git a/src/pages/academy/teamFormation/subcomponents/TeamFormationActions.tsx b/src/pages/academy/teamFormation/subcomponents/TeamFormationActions.tsx index 6677c040b6..3fe8d8cede 100644 --- a/src/pages/academy/teamFormation/subcomponents/TeamFormationActions.tsx +++ b/src/pages/academy/teamFormation/subcomponents/TeamFormationActions.tsx @@ -4,7 +4,7 @@ import { Flex, Icon } from '@tremor/react'; import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; -import { deleteTeam } from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { showSimpleConfirmDialog } from 'src/commons/utils/DialogHelper'; import { useSession } from 'src/commons/utils/Hooks'; @@ -28,7 +28,7 @@ const TeamFormationActions: React.FC = ({ teamId }) => { positiveLabel: 'Delete Team' }); if (confirm) { - dispatch(deleteTeam(teamId)); + dispatch(SessionActions.deleteTeam(teamId)); } }, [dispatch, teamId]); diff --git a/src/pages/academy/teamFormation/subcomponents/TeamFormationForm.tsx b/src/pages/academy/teamFormation/subcomponents/TeamFormationForm.tsx index 4bee6f29e6..dd4e37df0f 100644 --- a/src/pages/academy/teamFormation/subcomponents/TeamFormationForm.tsx +++ b/src/pages/academy/teamFormation/subcomponents/TeamFormationForm.tsx @@ -6,7 +6,7 @@ import { useDispatch } from 'react-redux'; import { useNavigate } from 'react-router'; import { Form, useParams } from 'react-router-dom'; import Select, { ActionMeta, MultiValue } from 'react-select'; -import { createTeam, updateTeam } from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { User } from 'src/commons/application/types/SessionTypes'; import { AssessmentOverview } from 'src/commons/assessment/AssessmentTypes'; import { useSession } from 'src/commons/utils/Hooks'; @@ -106,9 +106,9 @@ const TeamFormationForm: React.FC = () => { } if (teamId) { - dispatch(updateTeam(parseInt(teamId, 10), selectedAssessment, teams)); + dispatch(SessionActions.updateTeam(parseInt(teamId, 10), selectedAssessment, teams)); } else { - dispatch(createTeam(selectedAssessment, teams)); + dispatch(SessionActions.createTeam(selectedAssessment, teams)); } navigate(`/courses/${courseId}/teamformation`); }; diff --git a/src/pages/academy/teamFormation/subcomponents/TeamFormationImport.tsx b/src/pages/academy/teamFormation/subcomponents/TeamFormationImport.tsx index 2e8fa53b74..9ac12891c2 100644 --- a/src/pages/academy/teamFormation/subcomponents/TeamFormationImport.tsx +++ b/src/pages/academy/teamFormation/subcomponents/TeamFormationImport.tsx @@ -7,7 +7,7 @@ import { useDispatch } from 'react-redux'; import { useNavigate } from 'react-router'; import { Form } from 'react-router-dom'; import Select from 'react-select'; -import { bulkUploadTeam } from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { AssessmentOverview } from 'src/commons/assessment/AssessmentTypes'; import { useTypedSelector } from 'src/commons/utils/Hooks'; import classes from 'src/styles/TeamFormation.module.scss'; @@ -47,7 +47,7 @@ const TeamFormationImport: React.FC = () => { alert('Please upload the teams.'); return; } - dispatch(bulkUploadTeam(selectedAssessment, file, students)); + dispatch(SessionActions.bulkUploadTeam(selectedAssessment, file, students)); navigate(`/courses/${courseId}/teamformation`); }; diff --git a/src/pages/achievement/subcomponents/AchievementDashboard.tsx b/src/pages/achievement/subcomponents/AchievementDashboard.tsx index 72dfd548dd..9f1391a92a 100644 --- a/src/pages/achievement/subcomponents/AchievementDashboard.tsx +++ b/src/pages/achievement/subcomponents/AchievementDashboard.tsx @@ -11,7 +11,7 @@ import AchievementTask from '../../../commons/achievement/AchievementTask'; import AchievementView from '../../../commons/achievement/AchievementView'; import AchievementInferencer from '../../../commons/achievement/utils/AchievementInferencer'; import insertFakeAchievements from '../../../commons/achievement/utils/InsertFakeAchievements'; -import { fetchAssessmentOverviews } from '../../../commons/application/actions/SessionActions'; +import SessionActions from '../../../commons/application/actions/SessionActions'; import { getAchievements, getGoals, @@ -80,7 +80,7 @@ const AchievementDashboard: React.FC = () => { handleUpdateGoalProgress } = useMemo(() => { return { - handleFetchAssessmentOverviews: () => dispatch(fetchAssessmentOverviews()), + handleFetchAssessmentOverviews: () => dispatch(SessionActions.fetchAssessmentOverviews()), handleGetAchievements: () => dispatch(getAchievements()), handleGetGoals: (studentCourseRegId: number) => dispatch(getGoals(studentCourseRegId)), handleGetOwnGoals: () => dispatch(getOwnGoals()), diff --git a/src/pages/login/__tests__/Login.tsx b/src/pages/login/__tests__/Login.tsx index 56322b0c0e..7929b6c1ff 100644 --- a/src/pages/login/__tests__/Login.tsx +++ b/src/pages/login/__tests__/Login.tsx @@ -2,7 +2,7 @@ import { render } from '@testing-library/react'; import { Provider, useDispatch } from 'react-redux'; import * as ReactRouter from 'react-router'; import { StaticRouter } from 'react-router-dom/server'; -import { fetchAuth } from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { mockInitialStore } from '../../../commons/mocks/StoreMocks'; import Login from '../Login'; @@ -70,7 +70,7 @@ describe('Login', () => { ); render(app); - expect(dispatchMock).toBeCalledWith(fetchAuth(code, providerId)); + expect(dispatchMock).toBeCalledWith(SessionActions.fetchAuth(code, providerId)); }); test('Redirects to /welcome when isLoggedIn and no course', () => { diff --git a/src/pages/playground/Playground.tsx b/src/pages/playground/Playground.tsx index 33eff6549b..f9a6905b31 100644 --- a/src/pages/playground/Playground.tsx +++ b/src/pages/playground/Playground.tsx @@ -17,25 +17,21 @@ import { debuggerReset, debuggerResume } from 'src/commons/application/actions/InterpreterActions'; -import { - loginGitHub, - logoutGitHub, - logoutGoogle -} from 'src/commons/application/actions/SessionActions'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { setEditorSessionId, setSessionDetails, setSharedbConnected } from 'src/commons/collabEditing/CollabEditingActions'; +import { changeSideContentHeight } from 'src/commons/sideContent/SideContentActions'; +import { useSideContent } from 'src/commons/sideContent/SideContentHelper'; import makeCseMachineTabFrom from 'src/commons/sideContent/content/SideContentCseMachine'; import makeDataVisualizerTabFrom from 'src/commons/sideContent/content/SideContentDataVisualizer'; import makeHtmlDisplayTabFrom from 'src/commons/sideContent/content/SideContentHtmlDisplay'; -import { changeSideContentHeight } from 'src/commons/sideContent/SideContentActions'; -import { useSideContent } from 'src/commons/sideContent/SideContentHelper'; import { useResponsive, useTypedSelector } from 'src/commons/utils/Hooks'; import { - showFullJSWarningOnUrlLoad, showFulTSWarningOnUrlLoad, + showFullJSWarningOnUrlLoad, showHTMLDisclaimer } from 'src/commons/utils/WarningDialogHelper'; import { @@ -83,14 +79,13 @@ import { shortenURL, updateShortURL } from 'src/features/playground/PlaygroundActions'; - import { - getDefaultFilePath, - getLanguageConfig, - isSourceLanguage, OverallState, ResultOutput, - SALanguage + SALanguage, + getDefaultFilePath, + getLanguageConfig, + isSourceLanguage } from '../../commons/application/ApplicationTypes'; import { ExternalLibraryName } from '../../commons/application/types/ExternalTypes'; import { ControlBarAutorunButtons } from '../../commons/controlBar/ControlBarAutorunButtons'; @@ -105,8 +100,8 @@ import { ControlBarStepLimit } from '../../commons/controlBar/ControlBarStepLimi import { ControlBarToggleFolderModeButton } from '../../commons/controlBar/ControlBarToggleFolderModeButton'; import { ControlBarGitHubButtons } from '../../commons/controlBar/github/ControlBarGitHubButtons'; import { - convertEditorTabStateToProps, - NormalEditorContainerProps + NormalEditorContainerProps, + convertEditorTabStateToProps } from '../../commons/editor/EditorContainer'; import { Position } from '../../commons/editor/EditorTypes'; import { overwriteFilesInWorkspace } from '../../commons/fileSystem/utils'; @@ -594,7 +589,7 @@ const Playground: React.FC = props => { onClickSave={ persistenceFile ? () => dispatch(persistenceSaveFile(persistenceFile)) : undefined } - onClickLogOut={() => dispatch(logoutGoogle())} + onClickLogOut={() => dispatch(SessionActions.logoutGoogle())} onPopoverOpening={() => dispatch(persistenceInitialise())} /> ); @@ -613,8 +608,8 @@ const Playground: React.FC = props => { onClickOpen={() => dispatch(githubOpenFile())} onClickSaveAs={() => dispatch(githubSaveFileAs())} onClickSave={() => dispatch(githubSaveFile())} - onClickLogIn={() => dispatch(loginGitHub())} - onClickLogOut={() => dispatch(logoutGitHub())} + onClickLogIn={() => dispatch(SessionActions.loginGitHub())} + onClickLogOut={() => dispatch(SessionActions.logoutGitHub())} /> ); }, [ From 76b2367a87411abba71e2f988ab095f730545dc0 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 17:00:31 +0800 Subject: [PATCH 04/16] Use default import in SessionsReducer --- .../application/reducers/SessionsReducer.ts | 66 +++++++------------ 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/src/commons/application/reducers/SessionsReducer.ts b/src/commons/application/reducers/SessionsReducer.ts index 6d4442a4be..a654b0317a 100644 --- a/src/commons/application/reducers/SessionsReducer.ts +++ b/src/commons/application/reducers/SessionsReducer.ts @@ -7,29 +7,7 @@ import { import { SourceActionType } from '../../utils/ActionsHelper'; import { logOut } from '../actions/CommonsActions'; -import { - removeGitHubOctokitObjectAndAccessToken, - setAdminPanelCourseRegistrations, - setAssessmentConfigurations, - setConfigurableNotificationConfigs, - setCourseConfiguration, - setCourseRegistration, - setGitHubAccessToken, - setGitHubOctokitObject, - setGoogleUser, - setNotificationConfigs, - setTokens, - setUser, - updateAssessment, - updateAssessmentOverviews, - updateGrading, - updateGradingOverviews, - updateNotifications, - updateStudents, - updateTeamFormationOverview, - updateTeamFormationOverviews, - updateTotalXp -} from '../actions/SessionActions'; +import SessionActions from '../actions/SessionActions'; import { defaultSession } from '../ApplicationTypes'; import { SessionState } from '../types/SessionTypes'; @@ -46,64 +24,64 @@ const newSessionsReducer = createReducer(defaultSession, builder => { .addCase(logOut, () => { return defaultSession; }) - .addCase(setGitHubOctokitObject, (state, action) => { + .addCase(SessionActions.setGitHubOctokitObject, (state, action) => { state.githubOctokitObject = { octokit: action.payload }; }) - .addCase(setGitHubAccessToken, (state, action) => { + .addCase(SessionActions.setGitHubAccessToken, (state, action) => { state.githubAccessToken = action.payload; }) - .addCase(setGoogleUser, (state, action) => { + .addCase(SessionActions.setGoogleUser, (state, action) => { state.googleUser = action.payload; }) - .addCase(setTokens, (state, action) => { + .addCase(SessionActions.setTokens, (state, action) => { return { ...state, ...action.payload }; }) - .addCase(setUser, (state, action) => { + .addCase(SessionActions.setUser, (state, action) => { return { ...state, ...action.payload }; }) - .addCase(setCourseConfiguration, (state, action) => { + .addCase(SessionActions.setCourseConfiguration, (state, action) => { return { ...state, ...action.payload }; }) - .addCase(setCourseRegistration, (state, action) => { + .addCase(SessionActions.setCourseRegistration, (state, action) => { return { ...state, ...action.payload }; }) - .addCase(setAssessmentConfigurations, (state, action) => { + .addCase(SessionActions.setAssessmentConfigurations, (state, action) => { state.assessmentConfigurations = action.payload; }) - .addCase(setNotificationConfigs, (state, action) => { + .addCase(SessionActions.setNotificationConfigs, (state, action) => { state.notificationConfigs = action.payload; }) - .addCase(setConfigurableNotificationConfigs, (state, action) => { + .addCase(SessionActions.setConfigurableNotificationConfigs, (state, action) => { state.configurableNotificationConfigs = action.payload; }) - .addCase(setAdminPanelCourseRegistrations, (state, action) => { + .addCase(SessionActions.setAdminPanelCourseRegistrations, (state, action) => { state.userCourseRegistrations = action.payload; }) - .addCase(updateAssessment, (state, action) => { + .addCase(SessionActions.updateAssessment, (state, action) => { state.assessments[action.payload.id] = action.payload; }) - .addCase(updateAssessmentOverviews, (state, action) => { + .addCase(SessionActions.updateAssessmentOverviews, (state, action) => { state.assessmentOverviews = action.payload; }) - .addCase(updateTotalXp, (state, action) => { + .addCase(SessionActions.updateTotalXp, (state, action) => { state.xp = action.payload; }) - .addCase(updateGrading, (state, action) => { + .addCase(SessionActions.updateGrading, (state, action) => { state.gradings[action.payload.submissionId] = action.payload.grading; }) - .addCase(updateGradingOverviews, (state, action) => { + .addCase(SessionActions.updateGradingOverviews, (state, action) => { state.gradingOverviews = action.payload; }) - .addCase(updateNotifications, (state, action) => { + .addCase(SessionActions.updateNotifications, (state, action) => { state.notifications = action.payload; }) - .addCase(updateStudents, (state, action) => { + .addCase(SessionActions.updateStudents, (state, action) => { state.students = action.payload; }) - .addCase(updateTeamFormationOverviews, (state, action) => { + .addCase(SessionActions.updateTeamFormationOverviews, (state, action) => { state.teamFormationOverviews = action.payload; }) - .addCase(updateTeamFormationOverview, (state, action) => { + .addCase(SessionActions.updateTeamFormationOverview, (state, action) => { state.teamFormationOverview = action.payload; }) .addCase(remoteExecUpdateDevices, (state, action) => { @@ -112,7 +90,7 @@ const newSessionsReducer = createReducer(defaultSession, builder => { .addCase(remoteExecUpdateSession, (state, action) => { state.remoteExecutionSession = action.payload; }) - .addCase(removeGitHubOctokitObjectAndAccessToken, (state, action) => { + .addCase(SessionActions.removeGitHubOctokitObjectAndAccessToken, (state, action) => { state.githubOctokitObject = { octokit: undefined }; state.githubAccessToken = undefined; }); From 4a56145ce6ecacff092a913f455817fe798f7693 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 17:01:07 +0800 Subject: [PATCH 05/16] Use default import in BackendMocks --- src/commons/mocks/BackendMocks.ts | 149 ++++++++++++++---------------- 1 file changed, 68 insertions(+), 81 deletions(-) diff --git a/src/commons/mocks/BackendMocks.ts b/src/commons/mocks/BackendMocks.ts index 4738ebeef9..20cd20afdd 100644 --- a/src/commons/mocks/BackendMocks.ts +++ b/src/commons/mocks/BackendMocks.ts @@ -7,32 +7,7 @@ import { GradingQuery, GradingQuestion } from '../../features/grading/GradingTypes'; -import { - acknowledgeNotifications, - bulkUploadTeam, - createTeam, - deleteTeam, - fetchAdminPanelCourseRegistrations, - fetchAssessment, - fetchAssessmentOverviews, - fetchAuth, - fetchCourseConfig, - fetchGrading, - fetchGradingOverviews, - fetchNotifications, - fetchStudents, - fetchTeamFormationOverviews, - fetchUserAndCourse, - submitAnswer, - submitAssessment, - submitGrading, - submitGradingAndContinue, - unsubmitSubmission, - updateAssessmentConfigs, - updateCourseConfig, - updateLatestViewedCourse, - updateTeam -} from '../application/actions/SessionActions'; +import SessionActions from '../application/actions/SessionActions'; import { OverallState, Role, @@ -80,17 +55,20 @@ import { // TODO: Removal/implementation pending on outcome of // https://github.com/source-academy/frontend/issues/2974 export function* mockBackendSaga(): SagaIterator { - yield takeEvery(fetchAuth.type, function* (action: ReturnType) { - const tokens: Tokens = { - accessToken: 'accessToken', - refreshToken: 'refreshToken' - }; + yield takeEvery( + SessionActions.fetchAuth.type, + function* (action: ReturnType) { + const tokens: Tokens = { + accessToken: 'accessToken', + refreshToken: 'refreshToken' + }; - yield put(actions.setTokens(tokens)); - yield mockGetUserAndCourse(); - const courseId: number = yield select((state: OverallState) => state.session.courseId!); - yield routerNavigate(`/courses/${courseId}`); - }); + yield put(actions.setTokens(tokens)); + yield mockGetUserAndCourse(); + const courseId: number = yield select((state: OverallState) => state.session.courseId!); + yield routerNavigate(`/courses/${courseId}`); + } + ); const mockGetUserAndCourse = function* () { const user = { ...mockUser }; @@ -115,19 +93,19 @@ export function* mockBackendSaga(): SagaIterator { yield put(actions.updateSublanguage(sublanguage)); }; - yield takeEvery(fetchUserAndCourse.type, mockGetUserAndCourse); + yield takeEvery(SessionActions.fetchUserAndCourse.type, mockGetUserAndCourse); - yield takeEvery(fetchCourseConfig.type, function* () { + yield takeEvery(SessionActions.fetchCourseConfig.type, function* () { const courseConfiguration = { ...mockCourseConfigurations[0] }; yield put(actions.setCourseConfiguration(courseConfiguration)); }); - yield takeEvery(fetchAssessmentOverviews.type, function* () { + yield takeEvery(SessionActions.fetchAssessmentOverviews.type, function* () { yield put(actions.updateAssessmentOverviews([...mockAssessmentOverviews])); }); yield takeEvery( - fetchAssessment.type, + SessionActions.fetchAssessment.type, function* (action: ReturnType) { const { assessmentId: id } = action.payload; const assessment = mockAssessments[id - 1]; @@ -136,7 +114,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - submitAnswer.type, + SessionActions.submitAnswer.type, function* (action: ReturnType): any { const questionId = action.payload.id; const answer = action.payload.answer; @@ -164,7 +142,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - submitAssessment.type, + SessionActions.submitAssessment.type, function* (action: ReturnType): any { const assessmentId = action.payload; @@ -185,7 +163,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - fetchGradingOverviews.type, + SessionActions.fetchGradingOverviews.type, function* (action: ReturnType): any { const accessToken = yield select((state: OverallState) => state.session.accessToken); const { filterToGroup, pageParams, filterParams } = action.payload; @@ -199,7 +177,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - fetchTeamFormationOverviews.type, + SessionActions.fetchTeamFormationOverviews.type, function* (action: ReturnType): any { const accessToken = yield select((state: OverallState) => state.session.accessToken); const filterToGroup = action.payload; @@ -212,20 +190,23 @@ export function* mockBackendSaga(): SagaIterator { } ); - yield takeEvery(createTeam.type, function* (action: ReturnType): any { - const accessToken = yield select((state: OverallState) => state.session.accessToken); - const { assessment, teams } = action.payload; + yield takeEvery( + SessionActions.createTeam.type, + function* (action: ReturnType): any { + const accessToken = yield select((state: OverallState) => state.session.accessToken); + const { assessment, teams } = action.payload; - const teamFormationOverviews = yield call(() => - mockCreateTeam(accessToken, assessment.id, assessment.title, assessment.type, teams) - ); - if (teamFormationOverviews !== null) { - yield put(actions.updateTeamFormationOverviews([...teamFormationOverviews])); + const teamFormationOverviews = yield call(() => + mockCreateTeam(accessToken, assessment.id, assessment.title, assessment.type, teams) + ); + if (teamFormationOverviews !== null) { + yield put(actions.updateTeamFormationOverviews([...teamFormationOverviews])); + } } - }); + ); yield takeEvery( - bulkUploadTeam.type, + SessionActions.bulkUploadTeam.type, function* (action: ReturnType): any { const accessToken = yield select((state: OverallState) => state.session.accessToken); const { assessment, file } = action.payload; @@ -239,30 +220,36 @@ export function* mockBackendSaga(): SagaIterator { } ); - yield takeEvery(updateTeam.type, function* (action: ReturnType): any { - const accessToken = yield select((state: OverallState) => state.session.accessToken); - const { teamId, assessment, teams } = action.payload; + yield takeEvery( + SessionActions.updateTeam.type, + function* (action: ReturnType): any { + const accessToken = yield select((state: OverallState) => state.session.accessToken); + const { teamId, assessment, teams } = action.payload; - const teamFormationOverviews = yield call(() => - mockUpdateTeam(accessToken, teamId, assessment.id, assessment.title, assessment.type, teams) - ); - if (teamFormationOverviews !== null) { - yield put(actions.updateTeamFormationOverviews([...teamFormationOverviews])); + const teamFormationOverviews = yield call(() => + mockUpdateTeam(accessToken, teamId, assessment.id, assessment.title, assessment.type, teams) + ); + if (teamFormationOverviews !== null) { + yield put(actions.updateTeamFormationOverviews([...teamFormationOverviews])); + } } - }); + ); - yield takeEvery(deleteTeam.type, function* (action: ReturnType): any { - const accessToken = yield select((state: OverallState) => state.session.accessToken); - const { teamId } = action.payload; + yield takeEvery( + SessionActions.deleteTeam.type, + function* (action: ReturnType): any { + const accessToken = yield select((state: OverallState) => state.session.accessToken); + const { teamId } = action.payload; - const teamFormationOverviews = yield call(() => mockDeleteTeam(accessToken, teamId)); - if (teamFormationOverviews !== null) { - yield put(actions.updateTeamFormationOverviews([...teamFormationOverviews])); + const teamFormationOverviews = yield call(() => mockDeleteTeam(accessToken, teamId)); + if (teamFormationOverviews !== null) { + yield put(actions.updateTeamFormationOverviews([...teamFormationOverviews])); + } } - }); + ); yield takeEvery( - fetchStudents.type, + SessionActions.fetchStudents.type, function* (action: ReturnType): any { const accessToken = yield select((state: OverallState) => state.session.accessToken); const students = yield call(() => mockFetchStudents(accessToken)); @@ -273,7 +260,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - fetchGrading.type, + SessionActions.fetchGrading.type, function* (action: ReturnType): any { const submissionId = action.payload; const accessToken = yield select((state: OverallState) => state.session.accessToken); @@ -285,7 +272,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - unsubmitSubmission.type, + SessionActions.unsubmitSubmission.type, function* (action: ReturnType) { const { submissionId } = action.payload; const overviews: GradingOverviews = yield select( @@ -367,19 +354,19 @@ export function* mockBackendSaga(): SagaIterator { ); }; - yield takeEvery(submitGrading.type, sendGrade); + yield takeEvery(SessionActions.submitGrading.type, sendGrade); - yield takeEvery(submitGradingAndContinue.type, sendGradeAndContinue); + yield takeEvery(SessionActions.submitGradingAndContinue.type, sendGradeAndContinue); yield takeEvery( - fetchNotifications.type, + SessionActions.fetchNotifications.type, function* (action: ReturnType) { yield put(actions.updateNotifications([...mockNotifications])); } ); yield takeEvery( - acknowledgeNotifications.type, + SessionActions.acknowledgeNotifications.type, function* (action: ReturnType) { const notificationFilter: NotificationFilterFunction | undefined = action.payload.withFilter; @@ -408,7 +395,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - updateLatestViewedCourse.type, + SessionActions.updateLatestViewedCourse.type, function* (action: ReturnType) { const { courseId } = action.payload; const idx = courseId - 1; // zero-indexed @@ -434,7 +421,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - updateCourseConfig.type, + SessionActions.updateCourseConfig.type, function* (action: ReturnType) { const courseConfig = action.payload; @@ -444,7 +431,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - updateAssessmentConfigs.type, + SessionActions.updateAssessmentConfigs.type, function* (action: ReturnType): any { const assessmentConfig = action.payload; @@ -454,7 +441,7 @@ export function* mockBackendSaga(): SagaIterator { ); yield takeEvery( - fetchAdminPanelCourseRegistrations.type, + SessionActions.fetchAdminPanelCourseRegistrations.type, function* (action: ReturnType) { const courseRegistrations: AdminPanelCourseRegistration[] = [ ...mockAdminPanelCourseRegistrations From 89b07bff8795b1d28ad7a85b755df500126a1255 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 17:02:10 +0800 Subject: [PATCH 06/16] Update tests to use default import --- .../actions/__tests__/SessionActions.ts | 245 ++++++------- .../reducers/__tests__/SessionReducer.ts | 44 +-- src/commons/sagas/__tests__/BackendSaga.ts | 331 +++++++++--------- 3 files changed, 281 insertions(+), 339 deletions(-) diff --git a/src/commons/application/actions/__tests__/SessionActions.ts b/src/commons/application/actions/__tests__/SessionActions.ts index fafee13960..3c0fcebe98 100644 --- a/src/commons/application/actions/__tests__/SessionActions.ts +++ b/src/commons/application/actions/__tests__/SessionActions.ts @@ -21,58 +21,13 @@ import { UPDATE_COURSE_RESEARCH_AGREEMENT, User } from '../../types/SessionTypes'; -import { - acknowledgeNotifications, - deleteAssessmentConfig, - deleteUserCourseRegistration, - fetchAdminPanelCourseRegistrations, - fetchAssessment, - fetchAssessmentConfigs, - fetchAssessmentOverviews, - fetchAuth, - fetchCourseConfig, - fetchGrading, - fetchGradingOverviews, - fetchNotifications, - fetchStudents, - fetchTeamFormationOverviews, - fetchUserAndCourse, - login, - reautogradeAnswer, - reautogradeSubmission, - setAdminPanelCourseRegistrations, - setAssessmentConfigurations, - setCourseConfiguration, - setCourseRegistration, - setGitHubAccessToken, - setGitHubOctokitObject, - setTokens, - setUser, - submitAnswer, - submitAssessment, - submitGrading, - submitGradingAndContinue, - unsubmitSubmission, - updateAssessment, - updateAssessmentConfigs, - updateAssessmentOverviews, - updateCourseConfig, - updateCourseResearchAgreement, - updateGrading, - updateGradingOverviews, - updateLatestViewedCourse, - updateNotifications, - updateStudents, - updateTeamFormationOverview, - updateTeamFormationOverviews, - updateUserRole -} from '../SessionActions'; +import SessionActions, { updateAssessment, updateCourseResearchAgreement } from '../SessionActions'; test('acknowledgeNotifications generates correct action object', () => { - const action = acknowledgeNotifications(); + const action = SessionActions.acknowledgeNotifications(); expect(action).toEqual({ - type: acknowledgeNotifications.type, + type: SessionActions.acknowledgeNotifications.type, payload: { withFilter: undefined } @@ -81,59 +36,59 @@ test('acknowledgeNotifications generates correct action object', () => { test('fetchAuth generates correct action object', () => { const code = 'luminus-code-test'; - const action = fetchAuth(code); + const action = SessionActions.fetchAuth(code); expect(action).toEqual({ - type: fetchAuth.type, + type: SessionActions.fetchAuth.type, payload: { code } }); }); test('fetchUserAndCourse generates correct action object', () => { - const action = fetchUserAndCourse(); + const action = SessionActions.fetchUserAndCourse(); expect(action).toEqual({ - type: fetchUserAndCourse.type, + type: SessionActions.fetchUserAndCourse.type, payload: {} }); }); test('fetchCourseConfig generates correct action object', () => { - const action = fetchCourseConfig(); + const action = SessionActions.fetchCourseConfig(); expect(action).toEqual({ - type: fetchCourseConfig.type, + type: SessionActions.fetchCourseConfig.type, payload: {} }); }); test('fetchAssessment generates correct action object', () => { const id = 3; - const action = fetchAssessment(id); + const action = SessionActions.fetchAssessment(id); expect(action).toEqual({ - type: fetchAssessment.type, + type: SessionActions.fetchAssessment.type, payload: { assessmentId: id } }); }); test('fetchAssessmentOverviews generates correct action object', () => { - const action = fetchAssessmentOverviews(); + const action = SessionActions.fetchAssessmentOverviews(); expect(action).toEqual({ - type: fetchAssessmentOverviews.type, + type: SessionActions.fetchAssessmentOverviews.type, payload: {} }); }); test('fetchGrading generates correct action object', () => { const submissionId = 5; - const action = fetchGrading(submissionId); + const action = SessionActions.fetchGrading(submissionId); expect(action).toEqual({ - type: fetchGrading.type, + type: SessionActions.fetchGrading.type, payload: submissionId }); }); test('fetchGradingOverviews generates correct default action object', () => { - const action = fetchGradingOverviews(); + const action = SessionActions.fetchGradingOverviews(); expect(action).toEqual({ - type: fetchGradingOverviews.type, + type: SessionActions.fetchGradingOverviews.type, payload: { filterToGroup: true, publishedFilter: unpublishedToBackendParams(false), @@ -148,9 +103,14 @@ test('fetchGradingOverviews generates correct action object', () => { const publishedFilter = unpublishedToBackendParams(true); const pageParams = { offset: 123, pageSize: 456 }; const filterParams = { abc: 'xxx', def: 'yyy' }; - const action = fetchGradingOverviews(filterToGroup, publishedFilter, pageParams, filterParams); - expect(action).toEqual({ - type: fetchGradingOverviews.type, + const action = SessionActions.fetchGradingOverviews( + filterToGroup, + publishedFilter, + pageParams, + filterParams + ); + expect(action).toEqual({ + type: SessionActions.fetchGradingOverviews.type, payload: { filterToGroup: filterToGroup, publishedFilter: publishedFilter, @@ -161,43 +121,43 @@ test('fetchGradingOverviews generates correct action object', () => { }); test('fetchTeamFormationOverviews generates correct default action object', () => { - const action = fetchTeamFormationOverviews(); + const action = SessionActions.fetchTeamFormationOverviews(); expect(action).toEqual({ - type: fetchTeamFormationOverviews.type, + type: SessionActions.fetchTeamFormationOverviews.type, payload: true }); }); test('fetchTeamFormationOverviews generates correct action object', () => { const filterToGroup = false; - const action = fetchTeamFormationOverviews(filterToGroup); + const action = SessionActions.fetchTeamFormationOverviews(filterToGroup); expect(action).toEqual({ - type: fetchTeamFormationOverviews.type, + type: SessionActions.fetchTeamFormationOverviews.type, payload: filterToGroup }); }); test('fetchStudents generates correct action object', () => { - const action = fetchStudents(); + const action = SessionActions.fetchStudents(); expect(action).toEqual({ - type: fetchStudents.type, + type: SessionActions.fetchStudents.type, payload: {} }); }); test('fetchNotifications generates correct action object', () => { - const action = fetchNotifications(); + const action = SessionActions.fetchNotifications(); expect(action).toEqual({ - type: fetchNotifications.type, + type: SessionActions.fetchNotifications.type, payload: {} }); }); test('login action generates correct action object', () => { - const action = login('provider'); + const action = SessionActions.login('provider'); expect(action).toEqual({ - type: login.type, + type: SessionActions.login.type, payload: 'provider' }); }); @@ -205,9 +165,9 @@ test('login action generates correct action object', () => { test('setTokens generates correct action object', () => { const accessToken = 'access-token-test'; const refreshToken = 'refresh-token-test'; - const action = setTokens({ accessToken, refreshToken }); + const action = SessionActions.setTokens({ accessToken, refreshToken }); expect(action).toEqual({ - type: setTokens.type, + type: SessionActions.setTokens.type, payload: { accessToken, refreshToken @@ -237,9 +197,9 @@ test('setUser generates correct action object', () => { } ] }; - const action = setUser(user); + const action = SessionActions.setUser(user); expect(action).toEqual({ - type: setUser.type, + type: SessionActions.setUser.type, payload: user }); }); @@ -258,9 +218,9 @@ test('setCourseConfiguration generates correct action object', () => { moduleHelpText: 'Help text', assessmentTypes: ['Missions', 'Quests', 'Paths', 'Contests', 'Others'] }; - const action = setCourseConfiguration(courseConfig); + const action = SessionActions.setCourseConfiguration(courseConfig); expect(action).toEqual({ - type: setCourseConfiguration.type, + type: SessionActions.setCourseConfiguration.type, payload: courseConfig }); }); @@ -284,9 +244,9 @@ test('setCourseRegistration generates correct action object', () => { } as Story, agreedToResearch: true }; - const action = setCourseRegistration(courseRegistration); + const action = SessionActions.setCourseRegistration(courseRegistration); expect(action).toEqual({ - type: setCourseRegistration.type, + type: SessionActions.setCourseRegistration.type, payload: courseRegistration }); }); @@ -327,9 +287,9 @@ test('setAssessmentConfigurations generates correct action object', () => { earlySubmissionXp: 200 } ]; - const action = setAssessmentConfigurations(assesmentConfigurations); + const action = SessionActions.setAssessmentConfigurations(assesmentConfigurations); expect(action).toEqual({ - type: setAssessmentConfigurations.type, + type: SessionActions.setAssessmentConfigurations.type, payload: assesmentConfigurations }); }); @@ -351,17 +311,17 @@ test('setAdminPanelCourseRegistrations generates correct action object', async ( role: Role.Staff } ]; - const action = setAdminPanelCourseRegistrations(userCourseRegistrations); + const action = SessionActions.setAdminPanelCourseRegistrations(userCourseRegistrations); expect(action).toEqual({ - type: setAdminPanelCourseRegistrations.type, + type: SessionActions.setAdminPanelCourseRegistrations.type, payload: userCourseRegistrations }); }); test('setGitHubOctokitInstance generates correct action object', async () => { const authToken = 'testAuthToken12345'; - const action = setGitHubOctokitObject(authToken); - expect(action.type).toEqual(setGitHubOctokitObject.type); + const action = SessionActions.setGitHubOctokitObject(authToken); + expect(action.type).toEqual(SessionActions.setGitHubOctokitObject.type); const authObject = (await action.payload.auth()) as any; expect(authObject.token).toBe('testAuthToken12345'); @@ -370,9 +330,9 @@ test('setGitHubOctokitInstance generates correct action object', async () => { test('setGitHubAccessToken generates correct action object', () => { const authToken = 'testAuthToken12345'; - const action = setGitHubAccessToken(authToken); + const action = SessionActions.setGitHubAccessToken(authToken); expect(action).toEqual({ - type: setGitHubAccessToken.type, + type: SessionActions.setGitHubAccessToken.type, payload: authToken }); }); @@ -380,9 +340,9 @@ test('setGitHubAccessToken generates correct action object', () => { test('submitAnswer generates correct action object', () => { const id = 3; const answer = 'test-answer-here'; - const action = submitAnswer(id, answer); + const action = SessionActions.submitAnswer(id, answer); expect(action).toEqual({ - type: submitAnswer.type, + type: SessionActions.submitAnswer.type, payload: { id, answer @@ -392,9 +352,9 @@ test('submitAnswer generates correct action object', () => { test('submitAssessment generates correct action object', () => { const id = 7; - const action = submitAssessment(id); + const action = SessionActions.submitAssessment(id); expect(action).toEqual({ - type: submitAssessment.type, + type: SessionActions.submitAssessment.type, payload: id }); }); @@ -403,9 +363,9 @@ test('submitGrading generates correct action object with default values', () => const submissionId = 8; const questionId = 2; - const action = submitGrading(submissionId, questionId); + const action = SessionActions.submitGrading(submissionId, questionId); expect(action).toEqual({ - type: submitGrading.type, + type: SessionActions.submitGrading.type, payload: { submissionId, questionId, @@ -419,9 +379,9 @@ test('submitGradingAndContinue generates correct action object with default valu const submissionId = 8; const questionId = 2; - const action = submitGradingAndContinue(submissionId, questionId); + const action = SessionActions.submitGradingAndContinue(submissionId, questionId); expect(action).toEqual({ - type: submitGradingAndContinue.type, + type: SessionActions.submitGradingAndContinue.type, payload: { submissionId, questionId, @@ -436,9 +396,9 @@ test('submitGrading generates correct action object', () => { const questionId = 3; const xpAdjustment = 100; const comments = 'my comment'; - const action = submitGrading(submissionId, questionId, xpAdjustment, comments); + const action = SessionActions.submitGrading(submissionId, questionId, xpAdjustment, comments); expect(action).toEqual({ - type: submitGrading.type, + type: SessionActions.submitGrading.type, payload: { submissionId, questionId, @@ -453,9 +413,14 @@ test('submitGradingAndContinue generates correct action object', () => { const questionId = 7; const xpAdjustment = 55; const comments = 'another comment'; - const action = submitGradingAndContinue(submissionId, questionId, xpAdjustment, comments); - expect(action).toEqual({ - type: submitGradingAndContinue.type, + const action = SessionActions.submitGradingAndContinue( + submissionId, + questionId, + xpAdjustment, + comments + ); + expect(action).toEqual({ + type: SessionActions.submitGradingAndContinue.type, payload: { submissionId, questionId, @@ -467,9 +432,9 @@ test('submitGradingAndContinue generates correct action object', () => { test('reautogradeSubmission generates correct action object', () => { const submissionId = 123; - const action = reautogradeSubmission(submissionId); + const action = SessionActions.reautogradeSubmission(submissionId); expect(action).toEqual({ - type: reautogradeSubmission.type, + type: SessionActions.reautogradeSubmission.type, payload: submissionId }); }); @@ -477,18 +442,18 @@ test('reautogradeSubmission generates correct action object', () => { test('reautogradeAnswer generates correct action object', () => { const submissionId = 123; const questionId = 456; - const action = reautogradeAnswer(submissionId, questionId); + const action = SessionActions.reautogradeAnswer(submissionId, questionId); expect(action).toEqual({ - type: reautogradeAnswer.type, + type: SessionActions.reautogradeAnswer.type, payload: { submissionId, questionId } }); }); test('unsubmitSubmission generates correct action object', () => { const submissionId = 10; - const action = unsubmitSubmission(submissionId); + const action = SessionActions.unsubmitSubmission(submissionId); expect(action).toEqual({ - type: unsubmitSubmission.type, + type: SessionActions.unsubmitSubmission.type, payload: { submissionId } @@ -517,9 +482,9 @@ test('updateAssessmentOverviews generates correct action object', () => { hasVotingFeatures: false } ]; - const action = updateAssessmentOverviews(overviews); + const action = SessionActions.updateAssessmentOverviews(overviews); expect(action).toEqual({ - type: updateAssessmentOverviews.type, + type: SessionActions.updateAssessmentOverviews.type, payload: overviews }); }); @@ -573,9 +538,9 @@ test('updateGradingOverviews generates correct action object', () => { ] }; - const action = updateGradingOverviews(overviews); + const action = SessionActions.updateGradingOverviews(overviews); expect(action).toEqual({ - type: updateGradingOverviews.type, + type: SessionActions.updateGradingOverviews.type, payload: overviews }); }); @@ -583,9 +548,9 @@ test('updateGradingOverviews generates correct action object', () => { test('updateStudents generates correct action object', () => { const students: User[] = mockStudents; - const action = updateStudents(students); + const action = SessionActions.updateStudents(students); expect(action).toEqual({ - type: updateStudents.type, + type: SessionActions.updateStudents.type, payload: students }); }); @@ -600,9 +565,9 @@ test('updateTeamFormationOverview generates correct action object', () => { studentNames: ['Mark Henry'] }; - const action = updateTeamFormationOverview(overview); + const action = SessionActions.updateTeamFormationOverview(overview); expect(action).toEqual({ - type: updateTeamFormationOverview.type, + type: SessionActions.updateTeamFormationOverview.type, payload: overview }); }); @@ -619,9 +584,9 @@ test('updateTeamFormationOverviews generates correct action object', () => { } ]; - const action = updateTeamFormationOverviews(overviews); + const action = SessionActions.updateTeamFormationOverviews(overviews); expect(action).toEqual({ - type: updateTeamFormationOverviews.type, + type: SessionActions.updateTeamFormationOverviews.type, payload: overviews }); }); @@ -661,9 +626,9 @@ test('updateGrading generates correct action object', () => { } }; - const action = updateGrading(submissionId, grading); + const action = SessionActions.updateGrading(submissionId, grading); expect(action).toEqual({ - type: updateGrading.type, + type: SessionActions.updateGrading.type, payload: { submissionId, grading @@ -689,19 +654,19 @@ test('updateNotifications generates correct action object', () => { } ]; - const action = updateNotifications(notifications); + const action = SessionActions.updateNotifications(notifications); expect(action).toEqual({ - type: updateNotifications.type, + type: SessionActions.updateNotifications.type, payload: notifications }); }); test('updateLatestViewedCourse generates correct action object', () => { const courseId = 2; - const action = updateLatestViewedCourse(courseId); + const action = SessionActions.updateLatestViewedCourse(courseId); expect(action).toEqual({ - type: updateLatestViewedCourse.type, + type: SessionActions.updateLatestViewedCourse.type, payload: { courseId } }); }); @@ -720,17 +685,17 @@ test('updateCourseConfig generates correct action object', () => { moduleHelpText: 'Help text', assessmentTypes: ['Missions', 'Quests', 'Paths', 'Contests', 'Others'] }; - const action = updateCourseConfig(courseConfig); + const action = SessionActions.updateCourseConfig(courseConfig); expect(action).toEqual({ - type: updateCourseConfig.type, + type: SessionActions.updateCourseConfig.type, payload: courseConfig }); }); test('fetchAssessmentConfig generates correct action object', () => { - const action = fetchAssessmentConfigs(); + const action = SessionActions.fetchAssessmentConfigs(); expect(action).toEqual({ - type: fetchAssessmentConfigs.type, + type: SessionActions.fetchAssessmentConfigs.type, payload: {} }); }); @@ -804,9 +769,9 @@ test('updateAssessmentTypes generates correct action object', () => { earlySubmissionXp: 200 } ]; - const action = updateAssessmentConfigs(assessmentConfigs); + const action = SessionActions.updateAssessmentConfigs(assessmentConfigs); expect(action).toEqual({ - type: updateAssessmentConfigs.type, + type: SessionActions.updateAssessmentConfigs.type, payload: assessmentConfigs }); }); @@ -823,17 +788,17 @@ test('deleteAssessmentConfig generates correct action object', () => { hoursBeforeEarlyXpDecay: 48, earlySubmissionXp: 200 }; - const action = deleteAssessmentConfig(assessmentConfig); + const action = SessionActions.deleteAssessmentConfig(assessmentConfig); expect(action).toEqual({ - type: deleteAssessmentConfig.type, + type: SessionActions.deleteAssessmentConfig.type, payload: assessmentConfig }); }); test('fetchAdminPanelCourseRegistrations generates correct action object', () => { - const action = fetchAdminPanelCourseRegistrations(); + const action = SessionActions.fetchAdminPanelCourseRegistrations(); expect(action).toEqual({ - type: fetchAdminPanelCourseRegistrations.type, + type: SessionActions.fetchAdminPanelCourseRegistrations.type, payload: {} }); }); @@ -841,9 +806,9 @@ test('fetchAdminPanelCourseRegistrations generates correct action object', () => test('updateUserRole generates correct action object', () => { const courseRegId = 1; const role = Role.Staff; - const action = updateUserRole(courseRegId, role); + const action = SessionActions.updateUserRole(courseRegId, role); expect(action).toEqual({ - type: updateUserRole.type, + type: SessionActions.updateUserRole.type, payload: { courseRegId, role } }); }); @@ -859,9 +824,9 @@ test('updateCourseResearchAgreement generates correct action object', () => { test('deleteUserCourseRegistration generates correct action object', () => { const courseRegId = 1; - const action = deleteUserCourseRegistration(courseRegId); + const action = SessionActions.deleteUserCourseRegistration(courseRegId); expect(action).toEqual({ - type: deleteUserCourseRegistration.type, + type: SessionActions.deleteUserCourseRegistration.type, payload: { courseRegId } }); }); diff --git a/src/commons/application/reducers/__tests__/SessionReducer.ts b/src/commons/application/reducers/__tests__/SessionReducer.ts index fd45b94bcc..c884cbf2ab 100644 --- a/src/commons/application/reducers/__tests__/SessionReducer.ts +++ b/src/commons/application/reducers/__tests__/SessionReducer.ts @@ -8,19 +8,7 @@ import { ProgressStatuses } from '../../../assessment/AssessmentTypes'; import { Notification } from '../../../notificationBadge/NotificationBadgeTypes'; -import { - setAdminPanelCourseRegistrations, - setAssessmentConfigurations, - setCourseConfiguration, - setCourseRegistration, - setGitHubAccessToken, - setTokens, - setUser, - updateAssessmentOverviews, - updateGrading, - updateGradingOverviews, - updateNotifications -} from '../../actions/SessionActions'; +import SessionActions from '../../actions/SessionActions'; import { defaultSession, GameState, Role, Story } from '../../ApplicationTypes'; import { LOG_OUT } from '../../types/CommonsTypes'; import { SessionState, UPDATE_ASSESSMENT } from '../../types/SessionTypes'; @@ -41,7 +29,7 @@ test('SET_TOKEN sets accessToken and refreshToken correctly', () => { const refreshToken = 'refresh_token_test'; const action = { - type: setTokens.type, + type: SessionActions.setTokens.type, payload: { accessToken, refreshToken @@ -79,7 +67,7 @@ test('SET_USER works correctly', () => { }; const action = { - type: setUser.type, + type: SessionActions.setUser.type, payload } as const; const result: SessionState = SessionsReducer(defaultSession, action); @@ -105,7 +93,7 @@ test('SET_COURSE_CONFIGURATION works correctly', () => { assessmentTypes: ['Missions', 'Quests', 'Paths', 'Contests', 'Others'] }; const action = { - type: setCourseConfiguration.type, + type: SessionActions.setCourseConfiguration.type, payload } as const; const result: SessionState = SessionsReducer(defaultSession, action); @@ -135,7 +123,7 @@ test('SET_COURSE_REGISTRATION works correctly', () => { agreedToReseach: true }; const action = { - type: setCourseRegistration.type, + type: SessionActions.setCourseRegistration.type, payload } as const; const result: SessionState = SessionsReducer(defaultSession, action); @@ -193,7 +181,7 @@ test('SET_ASSESSMENT_CONFIGURATIONS works correctly', () => { ]; const action = { - type: setAssessmentConfigurations.type, + type: SessionActions.setAssessmentConfigurations.type, payload } as const; const result: SessionState = SessionsReducer(defaultSession, action); @@ -223,7 +211,7 @@ test('SET_ADMIN_PANEL_COURSE_REGISTRATIONS works correctly', () => { ]; const action = { - type: setAdminPanelCourseRegistrations.type, + type: SessionActions.setAdminPanelCourseRegistrations.type, payload } as const; const result: SessionState = SessionsReducer(defaultSession, action); @@ -237,7 +225,7 @@ test('SET_ADMIN_PANEL_COURSE_REGISTRATIONS works correctly', () => { test('SET_GITHUB_ACCESS_TOKEN works correctly', () => { const token = 'githubAccessToken'; const action = { - type: setGitHubAccessToken.type, + type: SessionActions.setGitHubAccessToken.type, payload: token } as const; const result: SessionState = SessionsReducer(defaultSession, action); @@ -376,7 +364,7 @@ const assessmentOverviewsTest2: AssessmentOverview[] = [ test('UPDATE_ASSESSMENT_OVERVIEWS works correctly in inserting assessment overviews', () => { const action = { - type: updateAssessmentOverviews.type, + type: SessionActions.updateAssessmentOverviews.type, payload: assessmentOverviewsTest1 } as const; @@ -395,7 +383,7 @@ test('UPDATE_ASSESSMENT_OVERVIEWS works correctly in updating assessment overvie }; const assessmentOverviewsPayload = [...assessmentOverviewsTest2, ...assessmentOverviewsTest1]; const action = { - type: updateAssessmentOverviews.type, + type: SessionActions.updateAssessmentOverviews.type, payload: assessmentOverviewsPayload } as const; @@ -467,7 +455,7 @@ const gradingTest2: GradingQuery = { test('UPDATE_GRADING works correctly in inserting gradings', () => { const submissionId = 23; const action = { - type: updateGrading.type, + type: SessionActions.updateGrading.type, payload: { submissionId, grading: gradingTest1 @@ -490,7 +478,7 @@ test('UPDATE_GRADING works correctly in inserting gradings and retains old data' }; const action = { - type: updateGrading.type, + type: SessionActions.updateGrading.type, payload: { submissionId: submissionId2, grading: gradingTest2 @@ -512,7 +500,7 @@ test('UPDATE_GRADING works correctly in updating gradings', () => { }; const action = { - type: updateGrading.type, + type: SessionActions.updateGrading.type, payload: { submissionId, grading: gradingTest2 @@ -578,7 +566,7 @@ const gradingOverviewTest2: GradingOverview[] = [ test('UPDATE_GRADING_OVERVIEWS works correctly in inserting grading overviews', () => { const action = { - type: updateGradingOverviews.type, + type: SessionActions.updateGradingOverviews.type, payload: { count: gradingOverviewTest1.length, data: gradingOverviewTest1 @@ -605,7 +593,7 @@ test('UPDATE_GRADING_OVERVIEWS works correctly in updating grading overviews', ( data: [...gradingOverviewTest2, ...gradingOverviewTest1] }; const action = { - type: updateGradingOverviews.type, + type: SessionActions.updateGradingOverviews.type, payload: gradingOverviewsPayload } as const; const result: SessionState = SessionsReducer(newDefaultSession, action); @@ -632,7 +620,7 @@ test('UPDATE_NOTIFICATIONS works correctly in updating notifications', () => { ]; const action = { - type: updateNotifications.type, + type: SessionActions.updateNotifications.type, payload: notifications } as const; diff --git a/src/commons/sagas/__tests__/BackendSaga.ts b/src/commons/sagas/__tests__/BackendSaga.ts index 9d8e630e0d..e45e82cd09 100644 --- a/src/commons/sagas/__tests__/BackendSaga.ts +++ b/src/commons/sagas/__tests__/BackendSaga.ts @@ -1,7 +1,7 @@ import { Chapter, Variant } from 'js-slang/dist/types'; import { createMemoryRouter } from 'react-router'; -import { call } from 'redux-saga/effects'; import { expectSaga } from 'redux-saga-test-plan'; +import { call } from 'redux-saga/effects'; import { mockTeamFormationOverviews } from 'src/commons/mocks/TeamFormationMocks'; import { addNewUsersToCourse, createCourse } from 'src/features/academy/AcademyActions'; import { UsernameRoleGroup } from 'src/pages/academy/adminPanel/subcomponents/AddUserPanel'; @@ -11,39 +11,7 @@ import { FETCH_GROUP_GRADING_SUMMARY, UPDATE_GROUP_GRADING_SUMMARY } from '../../../features/dashboard/DashboardTypes'; -import { - acknowledgeNotifications, - deleteUserCourseRegistration, - fetchAdminPanelCourseRegistrations, - fetchAssessment, - fetchAssessmentConfigs, - fetchAssessmentOverviews, - fetchAuth, - fetchCourseConfig, - fetchNotifications, - fetchStudents, - fetchTeamFormationOverviews, - fetchUserAndCourse, - reautogradeAnswer, - reautogradeSubmission, - setAdminPanelCourseRegistrations, - setAssessmentConfigurations, - setCourseConfiguration, - setCourseRegistration, - setTokens, - setUser, - submitAnswer, - submitAssessment, - updateAssessment, - updateAssessmentConfigs, - updateAssessmentOverviews, - updateCourseConfig, - updateLatestViewedCourse, - updateNotifications, - updateStudents, - updateTeamFormationOverviews, - updateUserRole -} from '../../application/actions/SessionActions'; +import SessionActions, { updateAssessment } from '../../application/actions/SessionActions'; import { GameState, Role, @@ -319,13 +287,13 @@ describe('Test FETCH_AUTH action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(postAuth, code, providerId, clientId, redirectUrl) - .put(setTokens(mockTokens)) + .put(SessionActions.setTokens(mockTokens)) .call(getUser, mockTokens) - .put(setUser(user)) - .not.put.actionType(updateLatestViewedCourse.type) - .put(setCourseRegistration(courseRegistration)) - .put(setCourseConfiguration(courseConfiguration)) - .put(setAssessmentConfigurations(assessmentConfigurations)) + .put(SessionActions.setUser(user)) + .not.put.actionType(SessionActions.updateLatestViewedCourse.type) + .put(SessionActions.setCourseRegistration(courseRegistration)) + .put(SessionActions.setCourseConfiguration(courseConfiguration)) + .put(SessionActions.setAssessmentConfigurations(assessmentConfigurations)) .provide([ [call(postAuth, code, providerId, clientId, redirectUrl), mockTokens], [ @@ -333,7 +301,7 @@ describe('Test FETCH_AUTH action', () => { { user, courseRegistration, courseConfiguration, assessmentConfigurations } ] ]) - .dispatch({ type: fetchAuth.type, payload: { code, providerId } }) + .dispatch({ type: SessionActions.fetchAuth.type, payload: { code, providerId } }) .silentRun(); }); @@ -342,14 +310,14 @@ describe('Test FETCH_AUTH action', () => { .withState(mockStates) .provide([[call(postAuth, code, providerId, clientId, redirectUrl), null]]) .call(postAuth, code, providerId, clientId, redirectUrl) - .not.put.actionType(setTokens.type) + .not.put.actionType(SessionActions.setTokens.type) .not.call.fn(getUser) - .not.put.actionType(setUser.type) - .not.put.actionType(updateLatestViewedCourse.type) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchAuth.type, payload: { code, providerId } }) + .not.put.actionType(SessionActions.setUser.type) + .not.put.actionType(SessionActions.updateLatestViewedCourse.type) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchAuth.type, payload: { code, providerId } }) .silentRun(); }); @@ -370,14 +338,14 @@ describe('Test FETCH_AUTH action', () => { ] ]) .call(postAuth, code, providerId, clientId, redirectUrl) - .put(setTokens(mockTokens)) + .put(SessionActions.setTokens(mockTokens)) .call(getUser, mockTokens) - .put(setUser(userWithNoCourse)) - .not.put.actionType(updateLatestViewedCourse.type) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchAuth.type, payload: { code, providerId } }) + .put(SessionActions.setUser(userWithNoCourse)) + .not.put.actionType(SessionActions.updateLatestViewedCourse.type) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchAuth.type, payload: { code, providerId } }) .silentRun(); }); @@ -397,14 +365,14 @@ describe('Test FETCH_AUTH action', () => { ]) .withState({ session: mockTokens }) // need to mock tokens for updateLatestViewedCourse call .call(postAuth, code, providerId, clientId, redirectUrl) - .put(setTokens(mockTokens)) + .put(SessionActions.setTokens(mockTokens)) .call(getUser, mockTokens) - .put(setUser(user)) - .put(updateLatestViewedCourse(user.courses[0].courseId)) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchAuth.type, payload: { code, providerId } }) + .put(SessionActions.setUser(user)) + .put(SessionActions.updateLatestViewedCourse(user.courses[0].courseId)) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchAuth.type, payload: { code, providerId } }) .silentRun(); }); @@ -423,14 +391,14 @@ describe('Test FETCH_AUTH action', () => { ] ]) .call(postAuth, code, providerId, clientId, redirectUrl) - .put(setTokens(mockTokens)) + .put(SessionActions.setTokens(mockTokens)) .call(getUser, mockTokens) - .not.put.actionType(setUser.type) - .not.put.actionType(updateLatestViewedCourse.type) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchAuth.type, payload: { code, providerId } }) + .not.put.actionType(SessionActions.setUser.type) + .not.put.actionType(SessionActions.updateLatestViewedCourse.type) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchAuth.type, payload: { code, providerId } }) .silentRun(); }); }); @@ -445,18 +413,18 @@ describe('Test FETCH_USER_AND_COURSE action', () => { return expectSaga(BackendSaga) .withState({ session: mockTokens }) .call(getUser, mockTokens) - .put(setUser(user)) - .not.put.actionType(updateLatestViewedCourse.type) - .put(setCourseRegistration(courseRegistration)) - .put(setCourseConfiguration(courseConfiguration)) - .put(setAssessmentConfigurations(assessmentConfigurations)) + .put(SessionActions.setUser(user)) + .not.put.actionType(SessionActions.updateLatestViewedCourse.type) + .put(SessionActions.setCourseRegistration(courseRegistration)) + .put(SessionActions.setCourseConfiguration(courseConfiguration)) + .put(SessionActions.setAssessmentConfigurations(assessmentConfigurations)) .provide([ [ call(getUser, mockTokens), { user, courseRegistration, courseConfiguration, assessmentConfigurations } ] ]) - .dispatch({ type: fetchUserAndCourse.type, payload: true }) + .dispatch({ type: SessionActions.fetchUserAndCourse.type, payload: true }) .silentRun(); }); @@ -475,12 +443,12 @@ describe('Test FETCH_USER_AND_COURSE action', () => { ] ]) .call(getUser, mockTokens) - .put(setUser(user)) - .put(updateLatestViewedCourse(user.courses[0].courseId)) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchUserAndCourse.type, payload: true }) + .put(SessionActions.setUser(user)) + .put(SessionActions.updateLatestViewedCourse(user.courses[0].courseId)) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchUserAndCourse.type, payload: true }) .silentRun(); }); @@ -500,12 +468,12 @@ describe('Test FETCH_USER_AND_COURSE action', () => { ] ]) .call(getUser, mockTokens) - .put(setUser(userWithNoCourse)) - .not.put.actionType(updateLatestViewedCourse.type) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchUserAndCourse.type, payload: true }) + .put(SessionActions.setUser(userWithNoCourse)) + .not.put.actionType(SessionActions.updateLatestViewedCourse.type) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchUserAndCourse.type, payload: true }) .silentRun(); }); @@ -524,12 +492,12 @@ describe('Test FETCH_USER_AND_COURSE action', () => { ] ]) .call(getUser, mockTokens) - .not.put.actionType(setUser.type) - .not.put.actionType(updateLatestViewedCourse.type) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchUserAndCourse.type, payload: true }) + .not.put.actionType(SessionActions.setUser.type) + .not.put.actionType(SessionActions.updateLatestViewedCourse.type) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchUserAndCourse.type, payload: true }) .silentRun(); }); }); @@ -539,8 +507,8 @@ describe('Test FETCH_COURSE_CONFIG action', () => { return expectSaga(BackendSaga) .withState(mockStates) .provide([[call(getCourseConfig, mockTokens), { config: mockCourseConfiguration1 }]]) - .put(setCourseConfiguration(mockCourseConfiguration1)) - .dispatch({ type: fetchCourseConfig.type }) + .put(SessionActions.setCourseConfiguration(mockCourseConfiguration1)) + .dispatch({ type: SessionActions.fetchCourseConfig.type }) .silentRun(); }); @@ -548,8 +516,8 @@ describe('Test FETCH_COURSE_CONFIG action', () => { return expectSaga(BackendSaga) .withState(mockStates) .provide([[call(getCourseConfig, mockTokens), { config: null }]]) - .not.put.actionType(setCourseConfiguration.type) - .dispatch({ type: fetchCourseConfig.type }) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .dispatch({ type: SessionActions.fetchCourseConfig.type }) .silentRun(); }); }); @@ -559,9 +527,9 @@ describe('Test FETCH_ASSESSMENT_OVERVIEWS action', () => { return expectSaga(BackendSaga) .withState({ session: mockTokens }) .provide([[call(getAssessmentOverviews, mockTokens), mockAssessmentOverviews]]) - .put(updateAssessmentOverviews(mockAssessmentOverviews)) + .put(SessionActions.updateAssessmentOverviews(mockAssessmentOverviews)) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchAssessmentOverviews.type }) + .dispatch({ type: SessionActions.fetchAssessmentOverviews.type }) .silentRun(); }); @@ -571,9 +539,9 @@ describe('Test FETCH_ASSESSMENT_OVERVIEWS action', () => { .withState({ session: mockTokens }) .provide([[call(getAssessmentOverviews, mockTokens), ret]]) .call(getAssessmentOverviews, mockTokens) - .not.put.actionType(updateAssessmentOverviews.type) + .not.put.actionType(SessionActions.updateAssessmentOverviews.type) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchAssessmentOverviews.type }) + .dispatch({ type: SessionActions.fetchAssessmentOverviews.type }) .silentRun(); }); }); @@ -583,9 +551,9 @@ describe('Test FETCH_TEAM_FORMATION_OVERVIEWS action', () => { return expectSaga(BackendSaga) .withState({ session: mockTokens }) .provide([[call(getTeamFormationOverviews, mockTokens), mockTeamFormationOverviews]]) - .put(updateTeamFormationOverviews(mockTeamFormationOverviews)) + .put(SessionActions.updateTeamFormationOverviews(mockTeamFormationOverviews)) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchTeamFormationOverviews.type }) + .dispatch({ type: SessionActions.fetchTeamFormationOverviews.type }) .silentRun(); }); @@ -594,9 +562,9 @@ describe('Test FETCH_TEAM_FORMATION_OVERVIEWS action', () => { .withState({ session: mockTokens }) .provide([[call(getTeamFormationOverviews, mockTokens), null]]) .call(getTeamFormationOverviews, mockTokens) - .not.put.actionType(updateTeamFormationOverviews.type) + .not.put.actionType(SessionActions.updateTeamFormationOverviews.type) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchTeamFormationOverviews.type }) + .dispatch({ type: SessionActions.fetchTeamFormationOverviews.type }) .silentRun(); }); }); @@ -606,9 +574,9 @@ describe('Test FETCH_STUDENTS action', () => { return expectSaga(BackendSaga) .withState({ session: mockTokens }) .provide([[call(getStudents, mockTokens), mockStudents]]) - .put(updateStudents(mockStudents)) + .put(SessionActions.updateStudents(mockStudents)) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchStudents.type }) + .dispatch({ type: SessionActions.fetchStudents.type }) .silentRun(); }); @@ -617,9 +585,9 @@ describe('Test FETCH_STUDENTS action', () => { .withState({ session: mockTokens }) .provide([[call(getStudents, mockTokens), null]]) .call(getStudents, mockTokens) - .not.put.actionType(updateStudents.type) + .not.put.actionType(SessionActions.updateStudents.type) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchStudents.type }) + .dispatch({ type: SessionActions.fetchStudents.type }) .silentRun(); }); }); @@ -632,7 +600,7 @@ describe('Test FETCH_ASSESSMENT action', () => { .provide([[call(getAssessment, mockId, mockTokens, undefined, undefined), mockAssessment]]) .put(updateAssessment(mockAssessment)) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchAssessment.type, payload: { assessmentId: mockId } }) + .dispatch({ type: SessionActions.fetchAssessment.type, payload: { assessmentId: mockId } }) .silentRun(); }); @@ -644,7 +612,7 @@ describe('Test FETCH_ASSESSMENT action', () => { .call(getAssessment, mockId, mockTokens, undefined, undefined) .not.put.actionType(UPDATE_ASSESSMENT) .hasFinalState({ session: mockTokens }) - .dispatch({ type: fetchAssessment.type, payload: { assessmentId: mockId } }) + .dispatch({ type: SessionActions.fetchAssessment.type, payload: { assessmentId: mockId } }) .silentRun(); }); }); @@ -684,7 +652,7 @@ describe('Test SUBMIT_ANSWER action', () => { .call(showSuccessMessage, 'Saved!', 1000) .put(updateAssessment(mockNewAssessment)) .put(updateHasUnsavedChanges('assessment' as WorkspaceLocation, false)) - .dispatch({ type: submitAnswer.type, payload: mockAnsweredAssessmentQuestion }) + .dispatch({ type: SessionActions.submitAnswer.type, payload: mockAnsweredAssessmentQuestion }) .silentRun(); // To make sure no changes in state return expect(mockStates.session.assessments[mockNewAssessment.id].questions[0].answer).toEqual( @@ -726,7 +694,7 @@ describe('Test SUBMIT_ANSWER action', () => { .call(showSuccessMessage, 'Saved!', 1000) .put(updateAssessment(mockNewAssessment)) .put(updateHasUnsavedChanges('assessment' as WorkspaceLocation, false)) - .dispatch({ type: submitAnswer.type, payload: mockAnsweredAssessmentQuestion }) + .dispatch({ type: SessionActions.submitAnswer.type, payload: mockAnsweredAssessmentQuestion }) .silentRun(); // To make sure no changes in state return expect(mockStates.session.assessments[mockNewAssessment.id].questions[0].answer).toEqual( @@ -760,7 +728,7 @@ describe('Test SUBMIT_ANSWER action', () => { .not.put.actionType(UPDATE_ASSESSMENT) .not.put.actionType(updateHasUnsavedChanges.type) .hasFinalState({ session: { ...mockTokens, role: Role.Student } }) - .dispatch({ type: submitAnswer.type, payload: mockAnsweredAssessmentQuestion }) + .dispatch({ type: SessionActions.submitAnswer.type, payload: mockAnsweredAssessmentQuestion }) .silentRun(); }); }); @@ -779,8 +747,8 @@ describe('Test SUBMIT_ASSESSMENT action', () => { .provide([[call(postAssessment, mockAssessmentId, mockTokens), okResp]]) .not.call(showWarningMessage) .call(showSuccessMessage, 'Submitted!', 2000) - .put(updateAssessmentOverviews(mockNewOverviews)) - .dispatch({ type: submitAssessment.type, payload: mockAssessmentId }) + .put(SessionActions.updateAssessmentOverviews(mockNewOverviews)) + .dispatch({ type: SessionActions.submitAssessment.type, payload: mockAssessmentId }) .silentRun(); expect(mockStates.session.assessmentOverviews[0].id).toEqual(mockAssessmentId); return expect(mockStates.session.assessmentOverviews[0].status).not.toEqual( @@ -794,9 +762,9 @@ describe('Test SUBMIT_ASSESSMENT action', () => { .provide([[call(postAssessment, 0, mockTokens), null]]) .call(postAssessment, 0, mockTokens) .call(showWarningMessage, "Couldn't reach our servers. Are you online?") - .not.put.actionType(updateAssessmentOverviews.type) + .not.put.actionType(SessionActions.updateAssessmentOverviews.type) .hasFinalState({ session: { ...mockTokens, role: Role.Student } }) - .dispatch({ type: submitAssessment.type, payload: 0 }) + .dispatch({ type: SessionActions.submitAssessment.type, payload: 0 }) .silentRun(); }); @@ -813,8 +781,8 @@ describe('Test SUBMIT_ASSESSMENT action', () => { .provide([[call(postAssessment, mockAssessmentId, mockTokens), okResp]]) .not.call(showWarningMessage) .call(showSuccessMessage, 'Submitted!', 2000) - .put(updateAssessmentOverviews(mockNewOverviews)) - .dispatch({ type: submitAssessment.type, payload: mockAssessmentId }) + .put(SessionActions.updateAssessmentOverviews(mockNewOverviews)) + .dispatch({ type: SessionActions.submitAssessment.type, payload: mockAssessmentId }) .silentRun(); expect(mockStates.session.assessmentOverviews[0].id).toEqual(mockAssessmentId); return expect(mockStates.session.assessmentOverviews[0].status).not.toEqual( @@ -828,8 +796,8 @@ describe('Test FETCH_NOTIFICATIONS action', () => { return expectSaga(BackendSaga) .withState(mockStates) .provide([[call(getNotifications, mockTokens), mockNotifications]]) - .put(updateNotifications(mockNotifications)) - .dispatch({ type: fetchNotifications.type }) + .put(SessionActions.updateNotifications(mockNotifications)) + .dispatch({ type: SessionActions.fetchNotifications.type }) .silentRun(); }); }); @@ -842,9 +810,9 @@ describe('Test ACKNOWLEDGE_NOTIFICATIONS action', () => { .withState(mockStates) .provide([[call(postAcknowledgeNotifications, mockTokens, ids), okResp]]) .not.call(showWarningMessage) - .put(updateNotifications(mockNewNotifications)) + .put(SessionActions.updateNotifications(mockNewNotifications)) .dispatch({ - type: acknowledgeNotifications.type, + type: SessionActions.acknowledgeNotifications.type, payload: { withFilter: (notifications: Notification[]) => notifications.filter(notification => ids.includes(notification.id)) @@ -871,7 +839,10 @@ describe('Test CHANGE_SUBLANGUAGE action', () => { sourceVariant: sublang.variant }) .put( - setCourseConfiguration({ sourceChapter: sublang.chapter, sourceVariant: sublang.variant }) + SessionActions.setCourseConfiguration({ + sourceChapter: sublang.chapter, + sourceVariant: sublang.variant + }) ) .provide([ [ @@ -895,9 +866,9 @@ describe('Test UPDATE_LATEST_VIEWED_COURSE action', () => { .withState(mockStates) .call(putLatestViewedCourse, mockTokens, courseId) .call(getLatestCourseRegistrationAndConfiguration, mockTokens) - .put(setCourseRegistration(mockCourseRegistration2)) - .put(setCourseConfiguration(mockCourseConfiguration2)) - .put(setAssessmentConfigurations(mockAssessmentConfigurations)) + .put(SessionActions.setCourseRegistration(mockCourseRegistration2)) + .put(SessionActions.setCourseConfiguration(mockCourseConfiguration2)) + .put(SessionActions.setAssessmentConfigurations(mockAssessmentConfigurations)) .provide([ [call(putLatestViewedCourse, mockTokens, courseId), okResp], [ @@ -909,7 +880,7 @@ describe('Test UPDATE_LATEST_VIEWED_COURSE action', () => { } ] ]) - .dispatch({ type: updateLatestViewedCourse.type, payload: { courseId } }) + .dispatch({ type: SessionActions.updateLatestViewedCourse.type, payload: { courseId } }) .silentRun(); }); @@ -919,9 +890,9 @@ describe('Test UPDATE_LATEST_VIEWED_COURSE action', () => { .provide([[call(putLatestViewedCourse, mockTokens, courseId), errorResp]]) .call(putLatestViewedCourse, mockTokens, courseId) .not.call.fn(getLatestCourseRegistrationAndConfiguration) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .dispatch({ type: updateLatestViewedCourse.type, payload: { courseId } }) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .dispatch({ type: SessionActions.updateLatestViewedCourse.type, payload: { courseId } }) .silentRun(); }); @@ -937,9 +908,9 @@ describe('Test UPDATE_LATEST_VIEWED_COURSE action', () => { ]) .call(putLatestViewedCourse, mockTokens, courseId) .call(getLatestCourseRegistrationAndConfiguration, mockTokens) - .not.put.actionType(setCourseRegistration.type) - .not.put.actionType(setCourseConfiguration.type) - .dispatch({ type: updateLatestViewedCourse.type, payload: { courseId } }) + .not.put.actionType(SessionActions.setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) + .dispatch({ type: SessionActions.updateLatestViewedCourse.type, payload: { courseId } }) .silentRun(); }); }); @@ -963,10 +934,10 @@ describe('Test UPDATE_COURSE_CONFIG action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(putCourseConfig, mockTokens, courseConfiguration) - .put(setCourseConfiguration(courseConfiguration)) + .put(SessionActions.setCourseConfiguration(courseConfiguration)) .call.fn(showSuccessMessage) .provide([[call(putCourseConfig, mockTokens, courseConfiguration), okResp]]) - .dispatch({ type: updateCourseConfig.type, payload: courseConfiguration }) + .dispatch({ type: SessionActions.updateCourseConfig.type, payload: courseConfiguration }) .silentRun(); }); @@ -975,9 +946,9 @@ describe('Test UPDATE_COURSE_CONFIG action', () => { .provide([[call(putCourseConfig, mockTokens, courseConfiguration), errorResp]]) .withState(mockStates) .call(putCourseConfig, mockTokens, courseConfiguration) - .not.put.actionType(setCourseConfiguration.type) + .not.put.actionType(SessionActions.setCourseConfiguration.type) .not.call.fn(showSuccessMessage) - .dispatch({ type: updateCourseConfig.type, payload: courseConfiguration }) + .dispatch({ type: SessionActions.updateCourseConfig.type, payload: courseConfiguration }) .silentRun(); }); }); @@ -987,9 +958,9 @@ describe('Test FETCH_ASSESSMENT_CONFIG action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(getAssessmentConfigs, mockTokens) - .put(setAssessmentConfigurations(mockAssessmentConfigurations)) + .put(SessionActions.setAssessmentConfigurations(mockAssessmentConfigurations)) .provide([[call(getAssessmentConfigs, mockTokens), mockAssessmentConfigurations]]) - .dispatch({ type: fetchAssessmentConfigs.type }) + .dispatch({ type: SessionActions.fetchAssessmentConfigs.type }) .silentRun(); }); @@ -998,8 +969,8 @@ describe('Test FETCH_ASSESSMENT_CONFIG action', () => { .withState(mockStates) .provide([[call(getAssessmentConfigs, mockTokens), null]]) .call(getAssessmentConfigs, mockTokens) - .not.put.actionType(setAssessmentConfigurations.type) - .dispatch({ type: fetchAssessmentConfigs.type }) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) + .dispatch({ type: SessionActions.fetchAssessmentConfigs.type }) .silentRun(); }); }); @@ -1025,9 +996,9 @@ describe('Test FETCH_ADMIN_PANEL_COURSE_REGISTRATIONS action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(getUserCourseRegistrations, mockTokens) - .put(setAdminPanelCourseRegistrations(userCourseRegistrations)) + .put(SessionActions.setAdminPanelCourseRegistrations(userCourseRegistrations)) .provide([[call(getUserCourseRegistrations, mockTokens), userCourseRegistrations]]) - .dispatch({ type: fetchAdminPanelCourseRegistrations.type }) + .dispatch({ type: SessionActions.fetchAdminPanelCourseRegistrations.type }) .silentRun(); }); @@ -1036,8 +1007,8 @@ describe('Test FETCH_ADMIN_PANEL_COURSE_REGISTRATIONS action', () => { .withState(mockStates) .provide([[call(getUserCourseRegistrations, mockTokens), null]]) .call(getUserCourseRegistrations, mockTokens) - .not.put.actionType(setAdminPanelCourseRegistrations.type) - .dispatch({ type: fetchAdminPanelCourseRegistrations.type }) + .not.put.actionType(SessionActions.setAdminPanelCourseRegistrations.type) + .dispatch({ type: SessionActions.fetchAdminPanelCourseRegistrations.type }) .silentRun(); }); }); @@ -1077,8 +1048,8 @@ describe('Test CREATE_COURSE action', () => { .withState(mockStates) .call(postCreateCourse, mockTokens, courseConfig) .call(getUser, mockTokens) - .put(setUser(mockUser)) - .put(setCourseRegistration({ role: Role.Student })) + .put(SessionActions.setUser(mockUser)) + .put(SessionActions.setCourseRegistration({ role: Role.Student })) .call( putAssessmentConfigs, mockTokens, @@ -1108,8 +1079,8 @@ describe('Test CREATE_COURSE action', () => { .withState(mockStates) .call(postCreateCourse, mockTokens, courseConfig) .not.call.fn(getUser) - .not.put.actionType(setUser.type) - .not.put.actionType(setCourseRegistration.type) + .not.put.actionType(SessionActions.setUser.type) + .not.put.actionType(SessionActions.setCourseRegistration.type) .not.call.fn(putAssessmentConfigs) .not.call.fn(showSuccessMessage) .provide([[call(postCreateCourse, mockTokens, courseConfig), errorResp]]) @@ -1146,7 +1117,7 @@ describe('Test ADD_NEW_USERS_TO_COURSE action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(putNewUsers, mockTokens, users, provider) - .put(fetchAdminPanelCourseRegistrations()) + .put(SessionActions.fetchAdminPanelCourseRegistrations()) .call.fn(showSuccessMessage) .provide([ [call(putNewUsers, mockTokens, users, provider), okResp], @@ -1160,7 +1131,7 @@ describe('Test ADD_NEW_USERS_TO_COURSE action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(putNewUsers, mockTokens, users, provider) - .not.put.actionType(fetchAdminPanelCourseRegistrations.type) + .not.put.actionType(SessionActions.fetchAdminPanelCourseRegistrations.type) .not.call.fn(showSuccessMessage) .provide([[call(putNewUsers, mockTokens, users, provider), errorResp]]) .dispatch({ type: addNewUsersToCourse.type, payload: { users, provider } }) @@ -1195,13 +1166,13 @@ describe('Test UPDATE_USER_ROLE action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(putUserRole, mockTokens, courseRegId, role) - .put(fetchAdminPanelCourseRegistrations()) + .put(SessionActions.fetchAdminPanelCourseRegistrations()) .call.fn(showSuccessMessage) .provide([ [call(putUserRole, mockTokens, courseRegId, role), okResp], [call(getUserCourseRegistrations, mockTokens), userCourseRegistrations] ]) - .dispatch({ type: updateUserRole.type, payload: { courseRegId, role } }) + .dispatch({ type: SessionActions.updateUserRole.type, payload: { courseRegId, role } }) .silentRun(); }); @@ -1209,10 +1180,10 @@ describe('Test UPDATE_USER_ROLE action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(putUserRole, mockTokens, courseRegId, role) - .not.put.actionType(fetchAdminPanelCourseRegistrations.type) + .not.put.actionType(SessionActions.fetchAdminPanelCourseRegistrations.type) .not.call.fn(showSuccessMessage) .provide([[call(putUserRole, mockTokens, courseRegId, role), errorResp]]) - .dispatch({ type: updateUserRole.type, payload: { courseRegId, role } }) + .dispatch({ type: SessionActions.updateUserRole.type, payload: { courseRegId, role } }) .silentRun(); }); }); @@ -1224,7 +1195,7 @@ describe('Test UPDATE_COURSE_RESEARCH_AGREEMENT', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(putCourseResearchAgreement, mockTokens, agreedToResearch) - .put(setCourseRegistration({ agreedToResearch })) + .put(SessionActions.setCourseRegistration({ agreedToResearch })) .call.fn(showSuccessMessage) .provide([[call(putCourseResearchAgreement, mockTokens, agreedToResearch), okResp]]) .dispatch({ type: UPDATE_COURSE_RESEARCH_AGREEMENT, payload: { agreedToResearch } }) @@ -1235,7 +1206,7 @@ describe('Test UPDATE_COURSE_RESEARCH_AGREEMENT', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(putCourseResearchAgreement, mockTokens, agreedToResearch) - .not.put.actionType(setCourseRegistration.type) + .not.put.actionType(SessionActions.setCourseRegistration.type) .not.call.fn(showSuccessMessage) .provide([[call(putCourseResearchAgreement, mockTokens, agreedToResearch), errorResp]]) .dispatch({ type: UPDATE_COURSE_RESEARCH_AGREEMENT, payload: { agreedToResearch } }) @@ -1260,13 +1231,16 @@ describe('Test DELETE_USER_COURSE_REGISTRATION action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(removeUserCourseRegistration, mockTokens, courseRegId) - .put(fetchAdminPanelCourseRegistrations()) + .put(SessionActions.fetchAdminPanelCourseRegistrations()) .call.fn(showSuccessMessage) .provide([ [call(removeUserCourseRegistration, mockTokens, courseRegId), okResp], [call(getUserCourseRegistrations, mockTokens), userCourseRegistrations] ]) - .dispatch({ type: deleteUserCourseRegistration.type, payload: { courseRegId } }) + .dispatch({ + type: SessionActions.deleteUserCourseRegistration.type, + payload: { courseRegId } + }) .silentRun(); }); @@ -1274,10 +1248,13 @@ describe('Test DELETE_USER_COURSE_REGISTRATION action', () => { return expectSaga(BackendSaga) .withState(mockStates) .call(removeUserCourseRegistration, mockTokens, courseRegId) - .not.put.actionType(fetchAdminPanelCourseRegistrations.type) + .not.put.actionType(SessionActions.fetchAdminPanelCourseRegistrations.type) .not.call.fn(showSuccessMessage) .provide([[call(removeUserCourseRegistration, mockTokens, courseRegId), errorResp]]) - .dispatch({ type: deleteUserCourseRegistration.type, payload: { courseRegId } }) + .dispatch({ + type: SessionActions.deleteUserCourseRegistration.type, + payload: { courseRegId } + }) .silentRun(); }); }); @@ -1288,13 +1265,16 @@ describe('Test UPDATE_ASSESSMENT_CONFIGS action', () => { .withState(mockStates) .call(putAssessmentConfigs, mockTokens, mockAssessmentConfigurations) .call(getAssessmentConfigs, mockTokens) - .put(setAssessmentConfigurations(mockAssessmentConfigurations)) + .put(SessionActions.setAssessmentConfigurations(mockAssessmentConfigurations)) .call.fn(showSuccessMessage) .provide([ [call(putAssessmentConfigs, mockTokens, mockAssessmentConfigurations), okResp], [call(getAssessmentConfigs, mockTokens), mockAssessmentConfigurations] ]) - .dispatch({ type: updateAssessmentConfigs.type, payload: mockAssessmentConfigurations }) + .dispatch({ + type: SessionActions.updateAssessmentConfigs.type, + payload: mockAssessmentConfigurations + }) .silentRun(); }); @@ -1304,9 +1284,12 @@ describe('Test UPDATE_ASSESSMENT_CONFIGS action', () => { .withState(mockStates) .call(putAssessmentConfigs, mockTokens, mockAssessmentConfigurations) .not.call(getAssessmentConfigs) - .not.put.actionType(setAssessmentConfigurations.type) + .not.put.actionType(SessionActions.setAssessmentConfigurations.type) .not.call.fn(showSuccessMessage) - .dispatch({ type: updateAssessmentConfigs.type, payload: mockAssessmentConfigurations }) + .dispatch({ + type: SessionActions.updateAssessmentConfigs.type, + payload: mockAssessmentConfigurations + }) .silentRun(); }); }); @@ -1343,7 +1326,7 @@ describe('Test REAUTOGRADE_SUBMISSION Action', () => { .call(postReautogradeSubmission, submissionId, mockTokens) .call.fn(showSuccessMessage) .not.call.fn(showWarningMessage) - .dispatch({ type: reautogradeSubmission.type, payload: submissionId }) + .dispatch({ type: SessionActions.reautogradeSubmission.type, payload: submissionId }) .silentRun(); }); @@ -1354,7 +1337,7 @@ describe('Test REAUTOGRADE_SUBMISSION Action', () => { .call(postReautogradeSubmission, submissionId, mockTokens) .not.call.fn(showSuccessMessage) .call.fn(showWarningMessage) - .dispatch({ type: reautogradeSubmission.type, payload: submissionId }) + .dispatch({ type: SessionActions.reautogradeSubmission.type, payload: submissionId }) .silentRun(); }); }); @@ -1370,7 +1353,10 @@ describe('Test REAUTOGRADE_ANSWER Action', () => { .call(postReautogradeAnswer, submissionId, questionId, mockTokens) .call.fn(showSuccessMessage) .not.call.fn(showWarningMessage) - .dispatch({ type: reautogradeAnswer.type, payload: { submissionId, questionId } }) + .dispatch({ + type: SessionActions.reautogradeAnswer.type, + payload: { submissionId, questionId } + }) .silentRun(); }); @@ -1382,7 +1368,10 @@ describe('Test REAUTOGRADE_ANSWER Action', () => { .call(postReautogradeAnswer, submissionId, questionId, mockTokens) .not.call.fn(showSuccessMessage) .call.fn(showWarningMessage) - .dispatch({ type: reautogradeAnswer.type, payload: { submissionId, questionId } }) + .dispatch({ + type: SessionActions.reautogradeAnswer.type, + payload: { submissionId, questionId } + }) .silentRun(); }); }); From 97fb802396851cc6fa382cf93e0067244160fa94 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 17:03:14 +0800 Subject: [PATCH 07/16] Remove named export for session action creators --- .../application/actions/SessionActions.ts | 74 ------------------- 1 file changed, 74 deletions(-) diff --git a/src/commons/application/actions/SessionActions.ts b/src/commons/application/actions/SessionActions.ts index 1292c661aa..80b98537b9 100644 --- a/src/commons/application/actions/SessionActions.ts +++ b/src/commons/application/actions/SessionActions.ts @@ -110,46 +110,6 @@ const newActions = createActions('session', { updateAssessmentOverviews: (overviews: AssessmentOverview[]) => overviews }); -// For compatibility with existing code (reducer) -export const { - fetchAuth, - fetchUserAndCourse, - fetchCourseConfig, - fetchAssessment, - fetchAssessmentAdmin, - fetchAssessmentOverviews, - fetchTotalXp, - fetchTotalXpAdmin, - fetchGrading, - fetchGradingOverviews, - fetchTeamFormationOverviews, - fetchStudents, - login, - logoutGoogle, - loginGitHub, - logoutGitHub, - setTokens, - setUser, - setCourseConfiguration, - setCourseRegistration, - setAssessmentConfigurations, - setConfigurableNotificationConfigs, - setNotificationConfigs, - setAdminPanelCourseRegistrations, - setGoogleUser, - setGitHubOctokitObject, - setGitHubAccessToken, - removeGitHubOctokitObjectAndAccessToken, - submitAnswer, - checkAnswerLastModifiedAt, - submitAssessment, - submitGrading, - submitGradingAndContinue, - reautogradeSubmission, - reautogradeAnswer, - updateAssessmentOverviews -} = newActions; - export const updateTotalXp = createAction(UPDATE_TOTAL_XP, (totalXp: number) => ({ payload: totalXp })); @@ -214,40 +174,6 @@ export const updateCourseResearchAgreement = createAction( (agreedToResearch: boolean) => ({ payload: { agreedToResearch } }) ); -// For compatibility with existing code (reducer) -export const { - updateGradingOverviews, - fetchTeamFormationOverview, - createTeam, - updateTeam, - deleteTeam, - bulkUploadTeam, - updateTeamFormationOverviews, - updateTeamFormationOverview, - updateStudents, - updateGrading, - unsubmitSubmission, - publishGrading, - unpublishGrading, - fetchNotifications, - acknowledgeNotifications, - updateNotifications, - updateLatestViewedCourse, - updateCourseConfig, - fetchAssessmentConfigs, - updateAssessmentConfigs, - updateNotificationConfigs, - updateNotificationPreferences, - deleteAssessmentConfig, - fetchAdminPanelCourseRegistrations, - fetchConfigurableNotificationConfigs, - fetchNotificationConfigs, - updateTimeOptions, - deleteTimeOptions, - updateUserRole, - deleteUserCourseRegistration -} = newActions2; - // For compatibility with existing code (actions helper) export default { ...newActions, From 34a30d63c7a4cbf9b3a7760ea725901eacac2e76 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 17:04:19 +0800 Subject: [PATCH 08/16] Fix format --- src/commons/sagas/GitHubPersistenceSaga.ts | 1 + src/commons/sagas/__tests__/BackendSaga.ts | 2 +- .../grading/subcomponents/GradingActions.tsx | 2 +- src/pages/login/Login.tsx | 2 +- src/pages/playground/Playground.tsx | 19 ++++++++++--------- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/commons/sagas/GitHubPersistenceSaga.ts b/src/commons/sagas/GitHubPersistenceSaga.ts index aac47c7978..75c7454e32 100644 --- a/src/commons/sagas/GitHubPersistenceSaga.ts +++ b/src/commons/sagas/GitHubPersistenceSaga.ts @@ -4,6 +4,7 @@ import { } from '@octokit/types'; import { call, put, select } from 'redux-saga/effects'; import GitHubActions from 'src/features/github/GitHubActions'; + import * as GitHubUtils from '../../features/github/GitHubUtils'; import { getGitHubOctokitInstance } from '../../features/github/GitHubUtils'; import { store } from '../../pages/createStore'; diff --git a/src/commons/sagas/__tests__/BackendSaga.ts b/src/commons/sagas/__tests__/BackendSaga.ts index e45e82cd09..9635b24e6d 100644 --- a/src/commons/sagas/__tests__/BackendSaga.ts +++ b/src/commons/sagas/__tests__/BackendSaga.ts @@ -1,7 +1,7 @@ import { Chapter, Variant } from 'js-slang/dist/types'; import { createMemoryRouter } from 'react-router'; -import { expectSaga } from 'redux-saga-test-plan'; import { call } from 'redux-saga/effects'; +import { expectSaga } from 'redux-saga-test-plan'; import { mockTeamFormationOverviews } from 'src/commons/mocks/TeamFormationMocks'; import { addNewUsersToCourse, createCourse } from 'src/features/academy/AcademyActions'; import { UsernameRoleGroup } from 'src/pages/academy/adminPanel/subcomponents/AddUserPanel'; diff --git a/src/pages/academy/grading/subcomponents/GradingActions.tsx b/src/pages/academy/grading/subcomponents/GradingActions.tsx index 1b123f3b06..a42d67f134 100644 --- a/src/pages/academy/grading/subcomponents/GradingActions.tsx +++ b/src/pages/academy/grading/subcomponents/GradingActions.tsx @@ -1,4 +1,4 @@ -import { Icon as BpIcon, Button } from '@blueprintjs/core'; +import { Button, Icon as BpIcon } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { Flex, Icon } from '@tremor/react'; import { useDispatch } from 'react-redux'; diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx index 4b7a2cb87b..d6d7e6cdba 100644 --- a/src/pages/login/Login.tsx +++ b/src/pages/login/Login.tsx @@ -16,11 +16,11 @@ import React, { useCallback, useEffect } from 'react'; import { Translation, useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { useLocation, useNavigate } from 'react-router'; +import SessionActions from 'src/commons/application/actions/SessionActions'; import { AuthProviderType } from 'src/commons/utils/AuthHelper'; import { useSession } from 'src/commons/utils/Hooks'; import classes from 'src/styles/Login.module.scss'; -import SessionActions from 'src/commons/application/actions/SessionActions'; import Constants from '../../commons/utils/Constants'; import { parseQuery } from '../../commons/utils/QueryHelper'; diff --git a/src/pages/playground/Playground.tsx b/src/pages/playground/Playground.tsx index f9a6905b31..5e1bba99f7 100644 --- a/src/pages/playground/Playground.tsx +++ b/src/pages/playground/Playground.tsx @@ -23,15 +23,15 @@ import { setSessionDetails, setSharedbConnected } from 'src/commons/collabEditing/CollabEditingActions'; -import { changeSideContentHeight } from 'src/commons/sideContent/SideContentActions'; -import { useSideContent } from 'src/commons/sideContent/SideContentHelper'; import makeCseMachineTabFrom from 'src/commons/sideContent/content/SideContentCseMachine'; import makeDataVisualizerTabFrom from 'src/commons/sideContent/content/SideContentDataVisualizer'; import makeHtmlDisplayTabFrom from 'src/commons/sideContent/content/SideContentHtmlDisplay'; +import { changeSideContentHeight } from 'src/commons/sideContent/SideContentActions'; +import { useSideContent } from 'src/commons/sideContent/SideContentHelper'; import { useResponsive, useTypedSelector } from 'src/commons/utils/Hooks'; import { - showFulTSWarningOnUrlLoad, showFullJSWarningOnUrlLoad, + showFulTSWarningOnUrlLoad, showHTMLDisclaimer } from 'src/commons/utils/WarningDialogHelper'; import { @@ -79,13 +79,14 @@ import { shortenURL, updateShortURL } from 'src/features/playground/PlaygroundActions'; + import { - OverallState, - ResultOutput, - SALanguage, getDefaultFilePath, getLanguageConfig, - isSourceLanguage + isSourceLanguage, + OverallState, + ResultOutput, + SALanguage } from '../../commons/application/ApplicationTypes'; import { ExternalLibraryName } from '../../commons/application/types/ExternalTypes'; import { ControlBarAutorunButtons } from '../../commons/controlBar/ControlBarAutorunButtons'; @@ -100,8 +101,8 @@ import { ControlBarStepLimit } from '../../commons/controlBar/ControlBarStepLimi import { ControlBarToggleFolderModeButton } from '../../commons/controlBar/ControlBarToggleFolderModeButton'; import { ControlBarGitHubButtons } from '../../commons/controlBar/github/ControlBarGitHubButtons'; import { - NormalEditorContainerProps, - convertEditorTabStateToProps + convertEditorTabStateToProps, + NormalEditorContainerProps } from '../../commons/editor/EditorContainer'; import { Position } from '../../commons/editor/EditorTypes'; import { overwriteFilesInWorkspace } from '../../commons/fileSystem/utils'; From 270213a9c22564093ecad9c0e80a60ddafd75803 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 17:07:14 +0800 Subject: [PATCH 09/16] Remove named export for GitHub action creators --- src/features/github/GitHubActions.ts | 3 --- src/pages/playground/Playground.tsx | 12 ++++-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/features/github/GitHubActions.ts b/src/features/github/GitHubActions.ts index 0c40154170..397a1030bd 100644 --- a/src/features/github/GitHubActions.ts +++ b/src/features/github/GitHubActions.ts @@ -6,9 +6,6 @@ const newActions = createActions('github', { githubSaveFileAs: () => ({}) }); -// For compatibility with existing code (reducer) -export const { githubOpenFile, githubSaveFile, githubSaveFileAs } = newActions; - // For compatibility with existing code (actions helper) export default { ...newActions diff --git a/src/pages/playground/Playground.tsx b/src/pages/playground/Playground.tsx index 5e1bba99f7..45db60fb85 100644 --- a/src/pages/playground/Playground.tsx +++ b/src/pages/playground/Playground.tsx @@ -62,11 +62,7 @@ import { updateReplValue } from 'src/commons/workspace/WorkspaceActions'; import { WorkspaceLocation } from 'src/commons/workspace/WorkspaceTypes'; -import { - githubOpenFile, - githubSaveFile, - githubSaveFileAs -} from 'src/features/github/GitHubActions'; +import GithubActions from 'src/features/github/GitHubActions'; import { persistenceInitialise, persistenceOpenPicker, @@ -606,9 +602,9 @@ const Playground: React.FC = props => { loggedInAs={githubOctokitObject.octokit} githubSaveInfo={githubSaveInfo} isDirty={githubPersistenceIsDirty} - onClickOpen={() => dispatch(githubOpenFile())} - onClickSaveAs={() => dispatch(githubSaveFileAs())} - onClickSave={() => dispatch(githubSaveFile())} + onClickOpen={() => dispatch(GithubActions.githubOpenFile())} + onClickSaveAs={() => dispatch(GithubActions.githubSaveFileAs())} + onClickSave={() => dispatch(GithubActions.githubSaveFile())} onClickLogIn={() => dispatch(SessionActions.loginGitHub())} onClickLogOut={() => dispatch(SessionActions.logoutGitHub())} /> From 63920e30c668c7665b5a7296c7ddaeed1e36637c Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 18:30:39 +0800 Subject: [PATCH 10/16] Migrate SourcereelReducer to RTK --- .../sourcereel/SourcereelReducer.ts | 127 +++++++----------- 1 file changed, 50 insertions(+), 77 deletions(-) diff --git a/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts b/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts index 70aee8d91b..fabc199df6 100644 --- a/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts +++ b/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts @@ -1,80 +1,53 @@ -import { SourceActionType } from '../../../commons/utils/ActionsHelper'; +import { createReducer } from '@reduxjs/toolkit'; +import { defaultWorkspaceManager } from 'src/commons/application/ApplicationTypes'; + import { RecordingStatus } from '../SourceRecorderTypes'; import { - RECORD_INIT, - RECORD_INPUT, - RESET_INPUTS, - SourcereelWorkspaceState, - TIMER_PAUSE, - TIMER_RESET, - TIMER_RESUME, - TIMER_START, - TIMER_STOP -} from './SourcereelTypes'; + recordInit, + recordInput, + resetInputs, + timerPause, + timerReset, + timerResume, + timerStart, + timerStop +} from './SourcereelActions'; -export const SourcereelReducer = (state: SourcereelWorkspaceState, action: SourceActionType) => { - switch (action.type) { - case RECORD_INIT: - return { - ...state, - playbackData: { - init: action.payload.initData, - inputs: [] - } - }; - case RECORD_INPUT: - return { - ...state, - playbackData: { - ...state.playbackData, - inputs: [...state.playbackData.inputs, action.payload.input] - } - }; - case RESET_INPUTS: - return { - ...state, - playbackData: { - ...state.playbackData, - inputs: action.payload.inputs - } - }; - case TIMER_PAUSE: - return { - ...state, - recordingStatus: RecordingStatus.paused, - timeElapsedBeforePause: - state.timeElapsedBeforePause + action.payload.timeNow - state.timeResumed - }; - case TIMER_RESET: - return { - ...state, - recordingStatus: RecordingStatus.notStarted, - timeElapsedBeforePause: 0, - timeResumed: 0 - }; - case TIMER_RESUME: - return { - ...state, - recordingStatus: RecordingStatus.recording, - timeElapsedBeforePause: - action.payload.timeBefore >= 0 ? action.payload.timeBefore : state.timeElapsedBeforePause, - timeResumed: action.payload.timeNow - }; - case TIMER_START: - return { - ...state, - recordingStatus: RecordingStatus.recording, - timeElapsedBeforePause: 0, - timeResumed: action.payload.timeNow - }; - case TIMER_STOP: - return { - ...state, - recordingStatus: RecordingStatus.finished, - timeElapsedBeforePause: 0, - timeResumed: 0 - }; - default: - return state; - } -}; +export const SourcereelReducer = createReducer(defaultWorkspaceManager.sourcereel, builder => { + builder + .addCase(recordInit, (state, action) => { + state.playbackData.init = action.payload.initData; + state.playbackData.inputs = []; + }) + .addCase(recordInput, (state, action) => { + state.playbackData.inputs.push(action.payload.input); + }) + .addCase(resetInputs, (state, action) => { + state.playbackData.inputs = action.payload.inputs; + }) + .addCase(timerPause, (state, action) => { + state.recordingStatus = RecordingStatus.paused; + state.timeElapsedBeforePause += action.payload.timeNow - state.timeResumed; + }) + .addCase(timerReset, (state, action) => { + state.recordingStatus = RecordingStatus.notStarted; + state.timeElapsedBeforePause = 0; + state.timeResumed = 0; + }) + .addCase(timerResume, (state, action) => { + state.recordingStatus = RecordingStatus.recording; + state.timeElapsedBeforePause = + action.payload.timeBefore >= 0 ? action.payload.timeBefore : state.timeElapsedBeforePause; + state.timeResumed = action.payload.timeNow; + }) + .addCase(timerStart, (state, action) => { + state.recordingStatus = RecordingStatus.recording; + state.timeElapsedBeforePause = 0; + state.timeResumed = action.payload.timeNow; + }) + .addCase(timerStop, (state, action) => { + state.recordingStatus = RecordingStatus.finished; + state.timeElapsedBeforePause = 0; + state.timeResumed = 0; + }); +}); From ef623c104f5a3013c1fc405a53853bead53568e8 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 5 May 2024 18:30:50 +0800 Subject: [PATCH 11/16] Migrate SourcecastReducer to RTK --- .../sourcecast/SourcecastReducer.ts | 114 ++++++++---------- 1 file changed, 47 insertions(+), 67 deletions(-) diff --git a/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts b/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts index dc517596f9..6b1f47bb6c 100644 --- a/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts +++ b/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts @@ -1,69 +1,49 @@ -import { SourceActionType } from '../../../commons/utils/ActionsHelper'; +import { createReducer } from '@reduxjs/toolkit'; +import { defaultWorkspaceManager } from 'src/commons/application/ApplicationTypes'; + import { - SAVE_SOURCECAST_DATA, - SET_CODE_DELTAS_TO_APPLY, - SET_CURRENT_PLAYER_TIME, - SET_INPUT_TO_APPLY, - SET_SOURCECAST_DATA, - SET_SOURCECAST_PLAYBACK_DURATION, - SET_SOURCECAST_PLAYBACK_STATUS -} from '../SourceRecorderTypes'; -import { SourcecastWorkspaceState, UPDATE_SOURCECAST_INDEX } from './SourcecastTypes'; + saveSourcecastData, + setCodeDeltasToApply, + setCurrentPlayerTime, + setInputToApply, + setSourcecastData, + setSourcecastDuration, + setSourcecastStatus +} from '../SourceRecorderActions'; +import { updateSourcecastIndex } from './SourcecastActions'; -export const SourcecastReducer = ( - state: SourcecastWorkspaceState, - action: SourceActionType -): SourcecastWorkspaceState => { - switch (action.type) { - case SAVE_SOURCECAST_DATA: - return { - ...state, - title: action.payload.title, - description: action.payload.description, - uid: action.payload.uid, - audioUrl: action.payload.audioUrl, - playbackData: action.payload.playbackData - }; - case SET_CURRENT_PLAYER_TIME: - return { - ...state, - currentPlayerTime: action.payload.playerTime - }; - case SET_CODE_DELTAS_TO_APPLY: - return { - ...state, - codeDeltasToApply: action.payload.deltas - }; - case SET_INPUT_TO_APPLY: - return { - ...state, - inputToApply: action.payload.inputToApply - }; - case SET_SOURCECAST_DATA: - return { - ...state, - title: action.payload.title, - description: action.payload.description, - uid: action.payload.uid, - audioUrl: action.payload.audioUrl, - playbackData: action.payload.playbackData - }; - case SET_SOURCECAST_PLAYBACK_DURATION: - return { - ...state, - playbackDuration: action.payload.duration - }; - case SET_SOURCECAST_PLAYBACK_STATUS: - return { - ...state, - playbackStatus: action.payload.playbackStatus - }; - case UPDATE_SOURCECAST_INDEX: - return { - ...state, - sourcecastIndex: action.payload.index - }; - default: - return state; - } -}; +export const SourcecastReducer = createReducer(defaultWorkspaceManager.sourcecast, builder => { + builder + .addCase(saveSourcecastData, (state, action) => { + state.title = action.payload.title; + state.description = action.payload.description; + state.uid = action.payload.uid; + state.audioUrl = action.payload.audioUrl; + state.playbackData = action.payload.playbackData; + }) + .addCase(setCurrentPlayerTime, (state, action) => { + state.currentPlayerTime = action.payload.playerTime; + }) + .addCase(setCodeDeltasToApply, (state, action) => { + state.codeDeltasToApply = action.payload.deltas; + }) + .addCase(setInputToApply, (state, action) => { + state.inputToApply = action.payload.inputToApply; + }) + .addCase(setSourcecastData, (state, action) => { + state.title = action.payload.title; + state.description = action.payload.description; + state.uid = action.payload.uid; + state.audioUrl = action.payload.audioUrl; + state.playbackData = action.payload.playbackData; + }) + .addCase(setSourcecastDuration, (state, action) => { + state.playbackDuration = action.payload.duration; + }) + .addCase(setSourcecastStatus, (state, action) => { + state.playbackStatus = action.payload.playbackStatus; + }) + .addCase(updateSourcecastIndex, (state, action) => { + state.sourcecastIndex = action.payload.index; + }); +}); From c3d5ff39d88d5a8997f93b8bae2cdeff9f863464 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Mon, 6 May 2024 17:22:10 +0800 Subject: [PATCH 12/16] Migrate CommonsReducer to RTK --- .../application/reducers/CommonsReducer.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/commons/application/reducers/CommonsReducer.ts b/src/commons/application/reducers/CommonsReducer.ts index 71f6aa9e62..cd6a716063 100644 --- a/src/commons/application/reducers/CommonsReducer.ts +++ b/src/commons/application/reducers/CommonsReducer.ts @@ -1,17 +1,10 @@ -import { Reducer } from 'redux'; -import { SourceActionType } from 'src/commons/utils/ActionsHelper'; +import { createReducer } from '@reduxjs/toolkit'; +import { updateReactRouter } from '../actions/CommonsActions'; import { defaultRouter } from '../ApplicationTypes'; -import { RouterState, UPDATE_REACT_ROUTER } from '../types/CommonsTypes'; -export const RouterReducer: Reducer = ( - state = defaultRouter, - action -) => { - switch (action.type) { - case UPDATE_REACT_ROUTER: - return action.payload; - default: - return state; - } -}; +export const RouterReducer = createReducer(defaultRouter, builder => { + builder.addCase(updateReactRouter, (state, action) => { + return action.payload; + }); +}); From fe7c3d9959fa6cad3c780021bb050126bb02ea8d Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Mon, 6 May 2024 22:58:37 +0800 Subject: [PATCH 13/16] Use namespace import in new reducers This is to ease the migration in future PRs where the named exports for action creators are removed. --- .../sourcecast/SourcecastReducer.ts | 24 ++++++----------- .../sourcereel/SourcereelReducer.ts | 27 +++++++------------ 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts b/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts index 6b1f47bb6c..f47bf97f78 100644 --- a/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts +++ b/src/features/sourceRecorder/sourcecast/SourcecastReducer.ts @@ -1,46 +1,38 @@ import { createReducer } from '@reduxjs/toolkit'; import { defaultWorkspaceManager } from 'src/commons/application/ApplicationTypes'; -import { - saveSourcecastData, - setCodeDeltasToApply, - setCurrentPlayerTime, - setInputToApply, - setSourcecastData, - setSourcecastDuration, - setSourcecastStatus -} from '../SourceRecorderActions'; +import * as SourceRecorderActions from '../SourceRecorderActions'; import { updateSourcecastIndex } from './SourcecastActions'; export const SourcecastReducer = createReducer(defaultWorkspaceManager.sourcecast, builder => { builder - .addCase(saveSourcecastData, (state, action) => { + .addCase(SourceRecorderActions.saveSourcecastData, (state, action) => { state.title = action.payload.title; state.description = action.payload.description; state.uid = action.payload.uid; state.audioUrl = action.payload.audioUrl; state.playbackData = action.payload.playbackData; }) - .addCase(setCurrentPlayerTime, (state, action) => { + .addCase(SourceRecorderActions.setCurrentPlayerTime, (state, action) => { state.currentPlayerTime = action.payload.playerTime; }) - .addCase(setCodeDeltasToApply, (state, action) => { + .addCase(SourceRecorderActions.setCodeDeltasToApply, (state, action) => { state.codeDeltasToApply = action.payload.deltas; }) - .addCase(setInputToApply, (state, action) => { + .addCase(SourceRecorderActions.setInputToApply, (state, action) => { state.inputToApply = action.payload.inputToApply; }) - .addCase(setSourcecastData, (state, action) => { + .addCase(SourceRecorderActions.setSourcecastData, (state, action) => { state.title = action.payload.title; state.description = action.payload.description; state.uid = action.payload.uid; state.audioUrl = action.payload.audioUrl; state.playbackData = action.payload.playbackData; }) - .addCase(setSourcecastDuration, (state, action) => { + .addCase(SourceRecorderActions.setSourcecastDuration, (state, action) => { state.playbackDuration = action.payload.duration; }) - .addCase(setSourcecastStatus, (state, action) => { + .addCase(SourceRecorderActions.setSourcecastStatus, (state, action) => { state.playbackStatus = action.payload.playbackStatus; }) .addCase(updateSourcecastIndex, (state, action) => { diff --git a/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts b/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts index fabc199df6..35eaa97092 100644 --- a/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts +++ b/src/features/sourceRecorder/sourcereel/SourcereelReducer.ts @@ -2,50 +2,41 @@ import { createReducer } from '@reduxjs/toolkit'; import { defaultWorkspaceManager } from 'src/commons/application/ApplicationTypes'; import { RecordingStatus } from '../SourceRecorderTypes'; -import { - recordInit, - recordInput, - resetInputs, - timerPause, - timerReset, - timerResume, - timerStart, - timerStop -} from './SourcereelActions'; +import * as SourcereelActions from './SourcereelActions'; export const SourcereelReducer = createReducer(defaultWorkspaceManager.sourcereel, builder => { builder - .addCase(recordInit, (state, action) => { + .addCase(SourcereelActions.recordInit, (state, action) => { state.playbackData.init = action.payload.initData; state.playbackData.inputs = []; }) - .addCase(recordInput, (state, action) => { + .addCase(SourcereelActions.recordInput, (state, action) => { state.playbackData.inputs.push(action.payload.input); }) - .addCase(resetInputs, (state, action) => { + .addCase(SourcereelActions.resetInputs, (state, action) => { state.playbackData.inputs = action.payload.inputs; }) - .addCase(timerPause, (state, action) => { + .addCase(SourcereelActions.timerPause, (state, action) => { state.recordingStatus = RecordingStatus.paused; state.timeElapsedBeforePause += action.payload.timeNow - state.timeResumed; }) - .addCase(timerReset, (state, action) => { + .addCase(SourcereelActions.timerReset, (state, action) => { state.recordingStatus = RecordingStatus.notStarted; state.timeElapsedBeforePause = 0; state.timeResumed = 0; }) - .addCase(timerResume, (state, action) => { + .addCase(SourcereelActions.timerResume, (state, action) => { state.recordingStatus = RecordingStatus.recording; state.timeElapsedBeforePause = action.payload.timeBefore >= 0 ? action.payload.timeBefore : state.timeElapsedBeforePause; state.timeResumed = action.payload.timeNow; }) - .addCase(timerStart, (state, action) => { + .addCase(SourcereelActions.timerStart, (state, action) => { state.recordingStatus = RecordingStatus.recording; state.timeElapsedBeforePause = 0; state.timeResumed = action.payload.timeNow; }) - .addCase(timerStop, (state, action) => { + .addCase(SourcereelActions.timerStop, (state, action) => { state.recordingStatus = RecordingStatus.finished; state.timeElapsedBeforePause = 0; state.timeResumed = 0; From 7b9dcc49d3f639fcbe73790ba54d974c8047c81c Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Mon, 6 May 2024 23:22:02 +0800 Subject: [PATCH 14/16] Use default import for InterpreterActions exports Removes all direct external references of the action creators, replacing their direct references with fully qualified names from the default export. --- .../actions/__tests__/InterpreterActions.ts | 53 ++++----- src/commons/sagas/RemoteExecutionSaga.ts | 4 +- .../sagas/WorkspaceSaga/helpers/evalCode.ts | 17 ++- .../WorkspaceSaga/helpers/evalTestCode.ts | 4 +- src/commons/sagas/WorkspaceSaga/index.ts | 6 +- src/commons/sagas/__tests__/WorkspaceSaga.ts | 102 +++++++++--------- src/commons/utils/CToWasmHelper.ts | 6 +- src/commons/utils/SourcerorHelper.ts | 6 +- src/commons/workspace/WorkspaceReducer.ts | 21 ++-- .../workspace/__tests__/WorkspaceReducer.ts | 55 +++++----- .../workspace/reducers/debuggerReducer.ts | 12 +-- src/pages/academy/sourcereel/Sourcereel.tsx | 16 ++- src/pages/playground/Playground.tsx | 16 ++- src/pages/sourcecast/Sourcecast.tsx | 16 ++- 14 files changed, 149 insertions(+), 185 deletions(-) diff --git a/src/commons/application/actions/__tests__/InterpreterActions.ts b/src/commons/application/actions/__tests__/InterpreterActions.ts index 038e17e76d..ef2b1b8175 100644 --- a/src/commons/application/actions/__tests__/InterpreterActions.ts +++ b/src/commons/application/actions/__tests__/InterpreterActions.ts @@ -1,16 +1,5 @@ import { WorkspaceLocation } from '../../../workspace/WorkspaceTypes'; -import { - beginDebuggerPause, - beginInterruptExecution, - debuggerReset, - debuggerResume, - endDebuggerPause, - endInterruptExecution, - evalInterpreterError, - evalInterpreterSuccess, - evalTestcaseSuccess, - handleConsoleLog -} from '../InterpreterActions'; +import InterpreterActions from '../InterpreterActions'; const assessmentWorkspace: WorkspaceLocation = 'assessment'; const gradingWorkspace: WorkspaceLocation = 'grading'; @@ -18,9 +7,9 @@ const playgroundWorkspace: WorkspaceLocation = 'playground'; test('handleConsoleLog generates correct action object', () => { const logString = 'test-log-string'; - const action = handleConsoleLog(assessmentWorkspace, logString); + const action = InterpreterActions.handleConsoleLog(assessmentWorkspace, logString); expect(action).toEqual({ - type: handleConsoleLog.type, + type: InterpreterActions.handleConsoleLog.type, payload: { logString: [logString], workspaceLocation: assessmentWorkspace @@ -30,9 +19,9 @@ test('handleConsoleLog generates correct action object', () => { test('evalInterpreterSuccess generates correct action object', () => { const value = 'value'; - const action = evalInterpreterSuccess(value, gradingWorkspace); + const action = InterpreterActions.evalInterpreterSuccess(value, gradingWorkspace); expect(action).toEqual({ - type: evalInterpreterSuccess.type, + type: InterpreterActions.evalInterpreterSuccess.type, payload: { type: 'result', value, @@ -44,9 +33,9 @@ test('evalInterpreterSuccess generates correct action object', () => { test('evalTestcaseSuccess generates correct action object', () => { const value = 'another value'; const index = 3; - const action = evalTestcaseSuccess(value, playgroundWorkspace, index); + const action = InterpreterActions.evalTestcaseSuccess(value, playgroundWorkspace, index); expect(action).toEqual({ - type: evalTestcaseSuccess.type, + type: InterpreterActions.evalTestcaseSuccess.type, payload: { type: 'result', value, @@ -58,9 +47,9 @@ test('evalTestcaseSuccess generates correct action object', () => { test('evalInterpreterError generates correct action object', () => { const errors: any = []; - const action = evalInterpreterError(errors, assessmentWorkspace); + const action = InterpreterActions.evalInterpreterError(errors, assessmentWorkspace); expect(action).toEqual({ - type: evalInterpreterError.type, + type: InterpreterActions.evalInterpreterError.type, payload: { type: 'errors', errors, @@ -70,9 +59,9 @@ test('evalInterpreterError generates correct action object', () => { }); test('beginInterruptExecution generates correct action object', () => { - const action = beginInterruptExecution(gradingWorkspace); + const action = InterpreterActions.beginInterruptExecution(gradingWorkspace); expect(action).toEqual({ - type: beginInterruptExecution.type, + type: InterpreterActions.beginInterruptExecution.type, payload: { workspaceLocation: gradingWorkspace } @@ -80,9 +69,9 @@ test('beginInterruptExecution generates correct action object', () => { }); test('endInterruptExecution generates correct action object', () => { - const action = endInterruptExecution(playgroundWorkspace); + const action = InterpreterActions.endInterruptExecution(playgroundWorkspace); expect(action).toEqual({ - type: endInterruptExecution.type, + type: InterpreterActions.endInterruptExecution.type, payload: { workspaceLocation: playgroundWorkspace } @@ -90,9 +79,9 @@ test('endInterruptExecution generates correct action object', () => { }); test('beginDebuggerPause generates correct action object', () => { - const action = beginDebuggerPause(assessmentWorkspace); + const action = InterpreterActions.beginDebuggerPause(assessmentWorkspace); expect(action).toEqual({ - type: beginDebuggerPause.type, + type: InterpreterActions.beginDebuggerPause.type, payload: { workspaceLocation: assessmentWorkspace } @@ -100,9 +89,9 @@ test('beginDebuggerPause generates correct action object', () => { }); test('endDebuggerPause generates correct action object', () => { - const action = endDebuggerPause(gradingWorkspace); + const action = InterpreterActions.endDebuggerPause(gradingWorkspace); expect(action).toEqual({ - type: endDebuggerPause.type, + type: InterpreterActions.endDebuggerPause.type, payload: { workspaceLocation: gradingWorkspace } @@ -110,9 +99,9 @@ test('endDebuggerPause generates correct action object', () => { }); test('debuggerResume generates correct action object', () => { - const action = debuggerResume(playgroundWorkspace); + const action = InterpreterActions.debuggerResume(playgroundWorkspace); expect(action).toEqual({ - type: debuggerResume.type, + type: InterpreterActions.debuggerResume.type, payload: { workspaceLocation: playgroundWorkspace } @@ -120,9 +109,9 @@ test('debuggerResume generates correct action object', () => { }); test('debuggerReset generates correct action object', () => { - const action = debuggerReset(assessmentWorkspace); + const action = InterpreterActions.debuggerReset(assessmentWorkspace); expect(action).toEqual({ - type: debuggerReset.type, + type: InterpreterActions.debuggerReset.type, payload: { workspaceLocation: assessmentWorkspace } diff --git a/src/commons/sagas/RemoteExecutionSaga.ts b/src/commons/sagas/RemoteExecutionSaga.ts index b939609fd2..5fbe1e2ce2 100644 --- a/src/commons/sagas/RemoteExecutionSaga.ts +++ b/src/commons/sagas/RemoteExecutionSaga.ts @@ -24,7 +24,7 @@ import { } from 'src/features/remoteExecution/RemoteExecutionTypes'; import { store } from 'src/pages/createStore'; -import { beginInterruptExecution } from '../application/actions/InterpreterActions'; +import InterpreterActions from '../application/actions/InterpreterActions'; import { OverallState } from '../application/ApplicationTypes'; import { ExternalLibraryName } from '../application/types/ExternalTypes'; import { actions } from '../utils/ActionsHelper'; @@ -304,7 +304,7 @@ export function* remoteExecutionSaga(): SagaIterator { client.sendRun(Buffer.from(assembled)); }); - yield takeEvery(beginInterruptExecution.type, function* () { + yield takeEvery(InterpreterActions.beginInterruptExecution.type, function* () { const session: DeviceSession | undefined = yield select( (state: OverallState) => state.session.remoteExecutionSession ); diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts index 535fc0b378..36f2ffa050 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts @@ -8,11 +8,7 @@ import { Chapter, ErrorSeverity, ErrorType, SourceError, Variant } from 'js-slan import { SagaIterator } from 'redux-saga'; import { call, put, race, select, take } from 'redux-saga/effects'; import * as Sourceror from 'sourceror'; -import { - beginDebuggerPause, - beginInterruptExecution, - debuggerResume -} from 'src/commons/application/actions/InterpreterActions'; +import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import { makeCCompilerConfig, specialCReturnObject } from 'src/commons/utils/CToWasmHelper'; import { javaRun } from 'src/commons/utils/JavaHelper'; import { notifyStoriesEvaluated } from 'src/features/stories/StoriesActions'; @@ -44,7 +40,8 @@ export function* evalCodeSaga( storyEnv?: string ): SagaIterator { context.runtime.debuggerOn = - (actionType === evalEditor.type || actionType === debuggerResume.type) && context.chapter > 2; + (actionType === evalEditor.type || actionType === InterpreterActions.debuggerResume.type) && + context.chapter > 2; const isStoriesBlock = actionType === actions.evalStory.type || workspaceLocation === 'stories'; // Logic for execution of substitution model visualizer @@ -258,7 +255,7 @@ export function* evalCodeSaga( const { result, interrupted, paused } = yield race({ result: - actionType === debuggerResume.type + actionType === InterpreterActions.debuggerResume.type ? call(resume, lastDebuggerResult) : isNonDet || isLazy || isWasm ? call_variant(context.variant) @@ -289,8 +286,8 @@ export function* evalCodeSaga( * A BEGIN_INTERRUPT_EXECUTION signals the beginning of an interruption, * i.e the trigger for the interpreter to interrupt execution. */ - interrupted: take(beginInterruptExecution.type), - paused: take(beginDebuggerPause.type) + interrupted: take(InterpreterActions.beginInterruptExecution.type), + paused: take(InterpreterActions.beginDebuggerPause.type) }); detachConsole(); @@ -401,7 +398,7 @@ export function* evalCodeSaga( if ( actionType === evalEditor.type || actionType === evalRepl.type || - actionType === debuggerResume.type + actionType === InterpreterActions.debuggerResume.type ) { if (context.errors.length > 0) { yield put(actions.addEvent([EventType.ERROR])); diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalTestCode.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalTestCode.ts index cdfd8e015f..e7c0cb43fa 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/evalTestCode.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/evalTestCode.ts @@ -1,7 +1,7 @@ import { Context, interrupt, runInContext } from 'js-slang'; import { InterruptedError } from 'js-slang/dist/errors/errors'; import { call, put, race, take } from 'redux-saga/effects'; -import { beginInterruptExecution } from 'src/commons/application/actions/InterpreterActions'; +import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import { TestcaseType, TestcaseTypes } from '../../../assessment/AssessmentTypes'; import { actions } from '../../../utils/ActionsHelper'; @@ -28,7 +28,7 @@ export function* evalTestCode( * A BEGIN_INTERRUPT_EXECUTION signals the beginning of an interruption, * i.e the trigger for the interpreter to interrupt execution. */ - interrupted: take(beginInterruptExecution.type) + interrupted: take(InterpreterActions.beginInterruptExecution.type) }); if (interrupted) { diff --git a/src/commons/sagas/WorkspaceSaga/index.ts b/src/commons/sagas/WorkspaceSaga/index.ts index c249967af7..a995e50463 100644 --- a/src/commons/sagas/WorkspaceSaga/index.ts +++ b/src/commons/sagas/WorkspaceSaga/index.ts @@ -3,9 +3,7 @@ import { Context, findDeclaration, getNames } from 'js-slang'; import { Chapter, Variant } from 'js-slang/dist/types'; import Phaser from 'phaser'; import { call, put, select } from 'redux-saga/effects'; -import InterpreterActions, { - debuggerResume -} from 'src/commons/application/actions/InterpreterActions'; +import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import { combineSagaHandlers } from 'src/commons/redux/utils'; import WorkspaceActions, { evalRepl } from 'src/commons/workspace/WorkspaceActions'; import CseMachine from 'src/features/cseMachine/CseMachine'; @@ -279,7 +277,7 @@ const WorkspaceSaga = combineSagaHandlers( context, execTime, workspaceLocation, - debuggerResume.type + InterpreterActions.debuggerResume.type ); }, debuggerReset: function* (action) { diff --git a/src/commons/sagas/__tests__/WorkspaceSaga.ts b/src/commons/sagas/__tests__/WorkspaceSaga.ts index 31e3de24a2..7a0f12dbfe 100644 --- a/src/commons/sagas/__tests__/WorkspaceSaga.ts +++ b/src/commons/sagas/__tests__/WorkspaceSaga.ts @@ -6,18 +6,7 @@ import { expectSaga } from 'redux-saga-test-plan'; import * as matchers from 'redux-saga-test-plan/matchers'; import { showFullJSDisclaimer, showFullTSDisclaimer } from 'src/commons/utils/WarningDialogHelper'; -import { - beginDebuggerPause, - beginInterruptExecution, - debuggerReset, - debuggerResume, - endDebuggerPause, - endInterruptExecution, - evalInterpreterError, - evalInterpreterSuccess, - evalTestcaseFailure, - evalTestcaseSuccess -} from '../../application/actions/InterpreterActions'; +import InterpreterActions from '../../application/actions/InterpreterActions'; import { defaultState, fullJSLanguage, @@ -169,7 +158,7 @@ describe('EVAL_EDITOR', () => { return ( expectSaga(workspaceSaga) .withState(newDefaultState) - .put(beginInterruptExecution(workspaceLocation)) + .put(InterpreterActions.beginInterruptExecution(workspaceLocation)) .put(beginClearContext(workspaceLocation, library, false)) .put(clearReplOutput(workspaceLocation)) // calls evalCode here with the prepend in elevated Context: silent run @@ -188,7 +177,7 @@ describe('EVAL_EDITOR', () => { ] }) // running the prepend block should return 'reeee', but silent run -> not written to REPL - .not.put(evalInterpreterSuccess('reeee', workspaceLocation)) + .not.put(InterpreterActions.evalInterpreterSuccess('reeee', workspaceLocation)) // Single call to evalCode made by blockExtraMethods .call.like({ fn: runFilesInContext }) // calls evalCode here with the student's program in normal Context @@ -208,7 +197,7 @@ describe('EVAL_EDITOR', () => { ] }) // running the student's program should return -1, which is written to REPL - .put(evalInterpreterSuccess(-1, workspaceLocation)) + .put(InterpreterActions.evalInterpreterSuccess(-1, workspaceLocation)) // should NOT attempt to execute the postpend block after above .not.call(runFilesInContext) .dispatch({ @@ -262,7 +251,7 @@ describe('EVAL_REPL', () => { return ( expectSaga(workspaceSaga) .withState(newState) - .put(beginInterruptExecution(workspaceLocation)) + .put(InterpreterActions.beginInterruptExecution(workspaceLocation)) .put(clearReplInput(workspaceLocation)) .put(sendReplInputToOutput(replValue, workspaceLocation)) // also calls evalCode here @@ -335,7 +324,7 @@ describe('DEBUG_RESUME', () => { } } }) - .put(beginInterruptExecution(workspaceLocation)) + .put(InterpreterActions.beginInterruptExecution(workspaceLocation)) .put(clearReplOutput(workspaceLocation)) // TODO: Hardcoded to make use of the first editor tab. Rewrite after editor tabs are added. .put(setEditorHighlightedLines(workspaceLocation, 0, [])) @@ -348,11 +337,11 @@ describe('DEBUG_RESUME', () => { {}, execTime, workspaceLocation, - debuggerResume.type + InterpreterActions.debuggerResume.type ] }) .dispatch({ - type: debuggerResume.type, + type: InterpreterActions.debuggerResume.type, payload: { workspaceLocation } }) .silentRun() @@ -374,7 +363,7 @@ describe('DEBUG_RESET', () => { // TODO: Hardcoded to make use of the first editor tab. Rewrite after editor tabs are added. .put(setEditorHighlightedLines(workspaceLocation, 0, [])) .dispatch({ - type: debuggerReset.type, + type: InterpreterActions.debuggerReset.type, payload: { workspaceLocation } }) .silentRun() @@ -440,7 +429,7 @@ describe('EVAL_TESTCASE', () => { expectSaga(workspaceSaga) .withState(newDefaultState) // Should interrupt execution and clear context but not clear REPL - .not.put(beginInterruptExecution(workspaceLocation)) + .not.put(InterpreterActions.beginInterruptExecution(workspaceLocation)) .put(beginClearContext(workspaceLocation, library, false)) .not.put.actionType(clearReplOutput.type) // Expect it to shard a new privileged context here and execute chunks in order @@ -454,7 +443,7 @@ describe('EVAL_TESTCASE', () => { ] }) // running the prepend block should return 'boink', but silent run -> not written to REPL - .not.put(evalInterpreterSuccess('boink', workspaceLocation)) + .not.put(InterpreterActions.evalInterpreterSuccess('boink', workspaceLocation)) // Single call to evalCode made by blockExtraMethods .call.like({ fn: runFilesInContext }) // calls evalCode here with the student's program in normal Context @@ -468,7 +457,7 @@ describe('EVAL_TESTCASE', () => { ] }) // running the student's program should return 69, which is NOT written to REPL (silent) - .not.put(evalInterpreterSuccess(69, workspaceLocation)) + .not.put(InterpreterActions.evalInterpreterSuccess(69, workspaceLocation)) // Single call to evalCode made by restoreExtraMethods to enable postpend to run in S4 .call.like({ fn: runFilesInContext }) // calls evalCode here again with the postpend now in elevated Context: silent run @@ -481,7 +470,7 @@ describe('EVAL_TESTCASE', () => { ] }) // running the postpend block should return true, but silent run -> not written to REPL - .not.put(evalInterpreterSuccess(true, workspaceLocation)) + .not.put(InterpreterActions.evalInterpreterSuccess(true, workspaceLocation)) // Single call to evalCode made by blockExtraMethods after postpend execution is complete .call.like({ fn: runFilesInContext }) // finally calls evalTestCode on the testcase @@ -490,8 +479,8 @@ describe('EVAL_TESTCASE', () => { args: [editorTestcases[0].program] }) // this testcase should execute fine in the elevated context and thus write result to REPL - .put(evalInterpreterSuccess(42, workspaceLocation)) - .put(evalTestcaseSuccess(42, workspaceLocation, testcaseId)) + .put(InterpreterActions.evalInterpreterSuccess(42, workspaceLocation)) + .put(InterpreterActions.evalTestcaseSuccess(42, workspaceLocation, testcaseId)) .dispatch({ type: evalTestcase.type, payload: { workspaceLocation, testcaseId } @@ -865,7 +854,7 @@ describe('evalCode', () => { throwInfiniteLoops: true, envSteps: -1 }) - .put(evalInterpreterSuccess(value, workspaceLocation)) + .put(InterpreterActions.evalInterpreterSuccess(value, workspaceLocation)) .silentRun(); }); @@ -891,8 +880,8 @@ describe('evalCode', () => { throwInfiniteLoops: true, envSteps: -1 }) - .put(endDebuggerPause(workspaceLocation)) - .put(evalInterpreterSuccess('Breakpoint hit!', workspaceLocation)) + .put(InterpreterActions.endDebuggerPause(workspaceLocation)) + .put(InterpreterActions.evalInterpreterSuccess('Breakpoint hit!', workspaceLocation)) .silentRun(); }); @@ -915,7 +904,7 @@ describe('evalCode', () => { throwInfiniteLoops: true, envSteps: -1 }) - .put.like({ action: { type: evalInterpreterError.type } }) + .put.like({ action: { type: InterpreterActions.evalInterpreterError.type } }) .silentRun(); }); @@ -950,7 +939,7 @@ describe('evalCode', () => { throwInfiniteLoops: true, envSteps: -1 }) - .put(evalInterpreterError(context.errors, workspaceLocation)) + .put(InterpreterActions.evalInterpreterError(context.errors, workspaceLocation)) .silentRun(); }); }); @@ -972,7 +961,7 @@ describe('evalCode', () => { }); test('calls resume, puts evalInterpreterSuccess when resume returns finished', () => { - actionType = debuggerResume.type; + actionType = InterpreterActions.debuggerResume.type; return expectSaga( evalCodeSaga, @@ -986,12 +975,12 @@ describe('evalCode', () => { .withState(state) .provide([[call(resume, lastDebuggerResult), { status: 'finished', value }]]) .call(resume, lastDebuggerResult) - .put(evalInterpreterSuccess(value, workspaceLocation)) + .put(InterpreterActions.evalInterpreterSuccess(value, workspaceLocation)) .silentRun(); }); test('calls resume, puts endDebuggerPause and evalInterpreterSuccess when resume returns suspended', () => { - actionType = debuggerResume.type; + actionType = InterpreterActions.debuggerResume.type; return expectSaga( evalCodeSaga, @@ -1005,13 +994,13 @@ describe('evalCode', () => { .withState(state) .provide([[call(resume, lastDebuggerResult), { status: 'suspended' }]]) .call(resume, lastDebuggerResult) - .put(endDebuggerPause(workspaceLocation)) - .put(evalInterpreterSuccess('Breakpoint hit!', workspaceLocation)) + .put(InterpreterActions.endDebuggerPause(workspaceLocation)) + .put(InterpreterActions.evalInterpreterSuccess('Breakpoint hit!', workspaceLocation)) .silentRun(); }); test('calls resume, puts evalInterpreterError when resume returns error', () => { - actionType = debuggerResume.type; + actionType = InterpreterActions.debuggerResume.type; return expectSaga( evalCodeSaga, @@ -1024,7 +1013,7 @@ describe('evalCode', () => { ) .withState(state) .call(resume, lastDebuggerResult) - .put.like({ action: { type: evalInterpreterError.type } }) + .put.like({ action: { type: InterpreterActions.evalInterpreterError.type } }) .silentRun(); }); }); @@ -1044,13 +1033,13 @@ describe('evalCode', () => { .provide({ race: () => ({ interrupted: { - type: beginInterruptExecution.type, + type: InterpreterActions.beginInterruptExecution.type, payload: { workspaceLocation } } }) }) - .put(debuggerReset(workspaceLocation)) - .put(endInterruptExecution(workspaceLocation)) + .put(InterpreterActions.debuggerReset(workspaceLocation)) + .put(InterpreterActions.endInterruptExecution(workspaceLocation)) .call(showWarningMessage, 'Execution aborted', 750) .silentRun() .then(result => { @@ -1074,12 +1063,12 @@ describe('evalCode', () => { .provide({ race: () => ({ paused: { - type: beginDebuggerPause.type, + type: InterpreterActions.beginDebuggerPause.type, payload: { workspaceLocation } } }) }) - .put(endDebuggerPause(workspaceLocation)) + .put(InterpreterActions.endDebuggerPause(workspaceLocation)) .call(showWarningMessage, 'Execution paused', 750) .silentRun(); }); @@ -1106,7 +1095,12 @@ describe('evalCode', () => { { status: 'error', value } ] ]) - .put(evalInterpreterSuccess('Program has been interrupted by module', workspaceLocation)) + .put( + InterpreterActions.evalInterpreterSuccess( + 'Program has been interrupted by module', + workspaceLocation + ) + ) .silentRun(); }); }); @@ -1144,8 +1138,8 @@ describe('evalTestCode', () => { return expectSaga(evalTestCode, code, context, execTime, workspaceLocation, index, type) .withState(state) .provide([[call(runInContext, code, context, options), { status: 'finished', value }]]) - .put(evalInterpreterSuccess(value, workspaceLocation)) - .put(evalTestcaseSuccess(value, workspaceLocation, index)) + .put(InterpreterActions.evalInterpreterSuccess(value, workspaceLocation)) + .put(InterpreterActions.evalTestcaseSuccess(value, workspaceLocation, index)) .not.put(clearReplOutputLast(workspaceLocation)) .silentRun(); }); @@ -1156,8 +1150,8 @@ describe('evalTestCode', () => { return expectSaga(evalTestCode, code, context, execTime, workspaceLocation, index, type) .withState(state) .provide([[call(runInContext, code, context, options), { status: 'finished', value }]]) - .put(evalInterpreterSuccess(value, workspaceLocation)) - .put(evalTestcaseSuccess(value, workspaceLocation, index)) + .put(InterpreterActions.evalInterpreterSuccess(value, workspaceLocation)) + .put(InterpreterActions.evalTestcaseSuccess(value, workspaceLocation, index)) .put(clearReplOutputLast(workspaceLocation)) .silentRun(); }); @@ -1166,8 +1160,8 @@ describe('evalTestCode', () => { return expectSaga(evalTestCode, code, context, execTime, workspaceLocation, index, type) .withState(state) .provide([[call(runInContext, code, context, options), { status: 'error' }]]) - .put(evalInterpreterError(context.errors, workspaceLocation)) - .put(evalTestcaseFailure(context.errors, workspaceLocation, index)) + .put(InterpreterActions.evalInterpreterError(context.errors, workspaceLocation)) + .put(InterpreterActions.evalTestcaseFailure(context.errors, workspaceLocation, index)) .not.put(clearReplOutputLast(workspaceLocation)) .silentRun(); }); @@ -1178,8 +1172,8 @@ describe('evalTestCode', () => { return expectSaga(evalTestCode, code, context, execTime, workspaceLocation, index, type) .withState(state) .provide([[call(runInContext, code, context, options), { status: 'error' }]]) - .put(evalInterpreterError(context.errors, workspaceLocation)) - .put(evalTestcaseFailure(context.errors, workspaceLocation, index)) + .put(InterpreterActions.evalInterpreterError(context.errors, workspaceLocation)) + .put(InterpreterActions.evalTestcaseFailure(context.errors, workspaceLocation, index)) .put(clearReplOutputLast(workspaceLocation)) .silentRun(); }); @@ -1200,12 +1194,12 @@ describe('evalTestCode', () => { .provide({ race: () => ({ interrupted: { - type: beginInterruptExecution.type, + type: InterpreterActions.beginInterruptExecution.type, payload: { workspaceLocation } } }) }) - .put(endInterruptExecution(workspaceLocation)) + .put(InterpreterActions.endInterruptExecution(workspaceLocation)) .call(showWarningMessage, `Execution of testcase ${index} aborted`, 750) .silentRun() .then(() => { diff --git a/src/commons/utils/CToWasmHelper.ts b/src/commons/utils/CToWasmHelper.ts index 7e5ba1bbb1..78d2983a9f 100644 --- a/src/commons/utils/CToWasmHelper.ts +++ b/src/commons/utils/CToWasmHelper.ts @@ -3,7 +3,7 @@ import loadSourceModules from 'js-slang/dist/modules/loader'; import type { ModuleFunctions } from 'js-slang/dist/modules/moduleTypes'; import type { Context } from 'js-slang/dist/types'; -import { handleConsoleLog } from '../application/actions/InterpreterActions'; +import InterpreterActions from '../application/actions/InterpreterActions'; export async function makeCCompilerConfig( program: string, @@ -13,7 +13,9 @@ export async function makeCCompilerConfig( return { printFunction: (v: string) => { if (typeof (window as any).__REDUX_STORE__ !== 'undefined') { - (window as any).__REDUX_STORE__.dispatch(handleConsoleLog(context.externalContext, v)); + (window as any).__REDUX_STORE__.dispatch( + InterpreterActions.handleConsoleLog(context.externalContext, v) + ); } }, externalFunctions diff --git a/src/commons/utils/SourcerorHelper.ts b/src/commons/utils/SourcerorHelper.ts index dbbaa25ea1..bd5c695190 100644 --- a/src/commons/utils/SourcerorHelper.ts +++ b/src/commons/utils/SourcerorHelper.ts @@ -1,12 +1,14 @@ import { Context } from 'js-slang/dist/types'; -import { handleConsoleLog } from '../application/actions/InterpreterActions'; +import InterpreterActions from '../application/actions/InterpreterActions'; export function makeExternalBuiltins(context: Context): any { return { display: (v: string) => { if (typeof (window as any).__REDUX_STORE__ !== 'undefined') { - (window as any).__REDUX_STORE__.dispatch(handleConsoleLog(context.externalContext, v)); + (window as any).__REDUX_STORE__.dispatch( + InterpreterActions.handleConsoleLog(context.externalContext, v) + ); } } }; diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index c88aa9c74b..142f4987c9 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -5,14 +5,7 @@ import { Reducer } from 'redux'; import { SourcecastReducer } from '../../features/sourceRecorder/sourcecast/SourcecastReducer'; import { SourcereelReducer } from '../../features/sourceRecorder/sourcereel/SourcereelReducer'; import { logOut } from '../application/actions/CommonsActions'; -import { - endInterruptExecution, - evalInterpreterError, - evalInterpreterSuccess, - evalTestcaseFailure, - evalTestcaseSuccess, - handleConsoleLog -} from '../application/actions/InterpreterActions'; +import InterpreterActions from '../application/actions/InterpreterActions'; import { createDefaultWorkspace, defaultWorkspaceManager, @@ -138,7 +131,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].externalLibrary = action.payload.newExternal; }) - .addCase(handleConsoleLog, (state, action) => { + .addCase(InterpreterActions.handleConsoleLog, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); /* Possible cases: * (1) state[workspaceLocation].output === [], i.e. state[workspaceLocation].output[-1] === undefined @@ -186,7 +179,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { state[workspaceLocation].isRunning = true; state[workspaceLocation].isDebugging = false; }) - .addCase(evalInterpreterSuccess, (state, action) => { + .addCase(InterpreterActions.evalInterpreterSuccess, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); const execType = state[workspaceLocation].context.executionMethod; const tokens = state[workspaceLocation].tokenCount; @@ -230,20 +223,20 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { state[workspaceLocation].output = newOutput; state[workspaceLocation].isRunning = false; }) - .addCase(evalTestcaseSuccess, (state, action) => { + .addCase(InterpreterActions.evalTestcaseSuccess, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); const testcase = state[workspaceLocation].editorTestcases[action.payload.index]; testcase.result = action.payload.value; testcase.errors = undefined; state[workspaceLocation].isRunning = false; }) - .addCase(evalTestcaseFailure, (state, action) => { + .addCase(InterpreterActions.evalTestcaseFailure, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); const testcase = state[workspaceLocation].editorTestcases[action.payload.index]; testcase.result = undefined; testcase.errors = action.payload.value; }) - .addCase(evalInterpreterError, (state, action) => { + .addCase(InterpreterActions.evalInterpreterError, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); const lastOutput: InterpreterOutput = state[workspaceLocation].output.slice(-1)[0]; @@ -272,7 +265,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { * i.e called after the interpreter is told to stop interruption, * to cause UI changes. */ - .addCase(endInterruptExecution, (state, action) => { + .addCase(InterpreterActions.endInterruptExecution, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); /** * Set the isRunning property of the diff --git a/src/commons/workspace/__tests__/WorkspaceReducer.ts b/src/commons/workspace/__tests__/WorkspaceReducer.ts index 0e185a754f..61c04903a9 100644 --- a/src/commons/workspace/__tests__/WorkspaceReducer.ts +++ b/src/commons/workspace/__tests__/WorkspaceReducer.ts @@ -1,16 +1,6 @@ import { Chapter, Variant } from 'js-slang/dist/types'; import { cloneDeep } from 'lodash'; -import { - debuggerReset, - debuggerResume, - endDebuggerPause, - endInterruptExecution, - evalInterpreterError, - evalInterpreterSuccess, - evalTestcaseFailure, - evalTestcaseSuccess, - handleConsoleLog -} from 'src/commons/application/actions/InterpreterActions'; +import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import { CodeOutput, @@ -353,7 +343,7 @@ describe('DEBUG_RESET', () => { isRunning, isDebugging }); - const actions = generateActions(debuggerReset.type); + const actions = generateActions(InterpreterActions.debuggerReset.type); actions.forEach(action => { const result = WorkspaceReducer(debugResetDefaultState, action); @@ -376,7 +366,7 @@ describe('DEBUG_RESUME', () => { const debugResumeDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ isDebugging }); - const actions = generateActions(debuggerResume.type); + const actions = generateActions(InterpreterActions.debuggerResume.type); actions.forEach(action => { const result = WorkspaceReducer(debugResumeDefaultState, action); @@ -447,7 +437,7 @@ describe('END_DEBUG_PAUSE', () => { test('sets isRunning to false and isDebugging to true', () => { const isRunning = true; const debugPauseDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ isRunning }); - const actions = generateActions(endDebuggerPause.type); + const actions = generateActions(InterpreterActions.endDebuggerPause.type); actions.forEach(action => { const result = WorkspaceReducer(debugPauseDefaultState, action); @@ -472,7 +462,7 @@ describe('END_INTERRUPT_EXECUTION', () => { isRunning, isDebugging }); - const actions = generateActions(endInterruptExecution.type); + const actions = generateActions(InterpreterActions.endInterruptExecution.type); actions.forEach(action => { const result = WorkspaceReducer(interruptExecutionDefaultState, action); @@ -533,7 +523,7 @@ describe('EVAL_INTERPRETER_ERROR', () => { isRunning, isDebugging }); - const actions = generateActions(evalInterpreterError.type); + const actions = generateActions(InterpreterActions.evalInterpreterError.type); actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); @@ -559,7 +549,7 @@ describe('EVAL_INTERPRETER_ERROR', () => { isDebugging }); - const actions = generateActions(evalInterpreterError.type); + const actions = generateActions(InterpreterActions.evalInterpreterError.type); actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); @@ -593,7 +583,7 @@ describe('EVAL_INTERPRETER_SUCCESS', () => { editorTabs: [{ highlightedLines, breakpoints }] }); - const actions = generateActions(evalInterpreterSuccess.type); + const actions = generateActions(InterpreterActions.evalInterpreterSuccess.type); actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); @@ -623,7 +613,7 @@ describe('EVAL_INTERPRETER_SUCCESS', () => { editorTabs: [{ highlightedLines, breakpoints }] }); - const actions = generateActions(evalInterpreterSuccess.type); + const actions = generateActions(InterpreterActions.evalInterpreterSuccess.type); actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); @@ -694,7 +684,10 @@ describe('EVAL_TESTCASE_FAILURE', () => { const evalFailureDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ editorTestcases }); - const actions = generateActions(evalTestcaseFailure.type, { value, index: 1 }); + const actions = generateActions(InterpreterActions.evalTestcaseFailure.type, { + value, + index: 1 + }); actions.forEach(action => { const result = WorkspaceReducer(evalFailureDefaultState, action); @@ -723,7 +716,10 @@ describe('EVAL_TESTCASE_SUCCESS', () => { editorTestcases }); - const actions = generateActions(evalTestcaseSuccess.type, { value, index: 1 }); + const actions = generateActions(InterpreterActions.evalTestcaseSuccess.type, { + value, + index: 1 + }); actions.forEach(action => { const result = WorkspaceReducer(testcaseSuccessDefaultState, action); @@ -752,7 +748,10 @@ describe('EVAL_TESTCASE_SUCCESS', () => { editorTestcases }); - const actions = generateActions(evalTestcaseSuccess.type, { value, index: 0 }); + const actions = generateActions(InterpreterActions.evalTestcaseSuccess.type, { + value, + index: 0 + }); actions.forEach(action => { const result = WorkspaceReducer(testcaseSuccessDefaultState, action); @@ -777,7 +776,9 @@ describe('HANDLE_CONSOLE_LOG', () => { test('works correctly with RunningOutput', () => { const logString = 'test-log-string'; const consoleLogDefaultState = generateDefaultWorkspace({ output: outputWithRunningOutput }); - const actions = generateActions(handleConsoleLog.type, { logString: [logString] }); + const actions = generateActions(InterpreterActions.handleConsoleLog.type, { + logString: [logString] + }); actions.forEach(action => { const result = WorkspaceReducer(cloneDeep(consoleLogDefaultState), action); const location: WorkspaceLocation = action.payload.workspaceLocation; @@ -803,7 +804,9 @@ describe('HANDLE_CONSOLE_LOG', () => { const consoleLogDefaultState = generateDefaultWorkspace({ output: outputWithRunningAndCodeOutput }); - const actions = generateActions(handleConsoleLog.type, { logString: [logString] }); + const actions = generateActions(InterpreterActions.handleConsoleLog.type, { + logString: [logString] + }); actions.forEach(action => { const result = WorkspaceReducer(consoleLogDefaultState, action); @@ -825,7 +828,9 @@ describe('HANDLE_CONSOLE_LOG', () => { const logString = 'test-log-string-3'; const consoleLogDefaultState = generateDefaultWorkspace({ output: [] }); - const actions = generateActions(handleConsoleLog.type, { logString: [logString] }); + const actions = generateActions(InterpreterActions.handleConsoleLog.type, { + logString: [logString] + }); actions.forEach(action => { const result = WorkspaceReducer(consoleLogDefaultState, action); diff --git a/src/commons/workspace/reducers/debuggerReducer.ts b/src/commons/workspace/reducers/debuggerReducer.ts index e03c67f893..1e7a339952 100644 --- a/src/commons/workspace/reducers/debuggerReducer.ts +++ b/src/commons/workspace/reducers/debuggerReducer.ts @@ -1,26 +1,22 @@ import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; -import { - debuggerReset, - debuggerResume, - endDebuggerPause -} from '../../application/actions/InterpreterActions'; +import InterpreterActions from '../../application/actions/InterpreterActions'; import { getWorkspaceLocation } from '../WorkspaceReducer'; import { WorkspaceManagerState } from '../WorkspaceTypes'; export const handleDebuggerActions = (builder: ActionReducerMapBuilder) => { builder - .addCase(debuggerReset, (state, action) => { + .addCase(InterpreterActions.debuggerReset, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].isRunning = false; state[workspaceLocation].isDebugging = false; }) - .addCase(debuggerResume, (state, action) => { + .addCase(InterpreterActions.debuggerResume, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].isRunning = true; state[workspaceLocation].isDebugging = false; }) - .addCase(endDebuggerPause, (state, action) => { + .addCase(InterpreterActions.endDebuggerPause, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].isRunning = false; state[workspaceLocation].isDebugging = true; diff --git a/src/pages/academy/sourcereel/Sourcereel.tsx b/src/pages/academy/sourcereel/Sourcereel.tsx index ad0d46a518..ccec7f46b4 100644 --- a/src/pages/academy/sourcereel/Sourcereel.tsx +++ b/src/pages/academy/sourcereel/Sourcereel.tsx @@ -4,12 +4,7 @@ import classNames from 'classnames'; import { Chapter, Variant } from 'js-slang/dist/types'; import React, { useEffect, useMemo } from 'react'; import { useDispatch } from 'react-redux'; -import { - beginDebuggerPause, - beginInterruptExecution, - debuggerReset, - debuggerResume -} from 'src/commons/application/actions/InterpreterActions'; +import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import { changeSideContentHeight } from 'src/commons/sideContent/SideContentActions'; import { fetchSourcecastIndex } from 'src/features/sourceRecorder/sourcecast/SourcecastActions'; import { @@ -208,10 +203,11 @@ const Sourcereel: React.FC = () => { }; const autorunButtonHandlers = useMemo(() => { return { - handleDebuggerPause: () => dispatch(beginDebuggerPause(workspaceLocation)), - handleDebuggerResume: () => dispatch(debuggerResume(workspaceLocation)), - handleDebuggerReset: () => dispatch(debuggerReset(workspaceLocation)), - handleInterruptEval: () => dispatch(beginInterruptExecution(workspaceLocation)), + handleDebuggerPause: () => dispatch(InterpreterActions.beginDebuggerPause(workspaceLocation)), + handleDebuggerResume: () => dispatch(InterpreterActions.debuggerResume(workspaceLocation)), + handleDebuggerReset: () => dispatch(InterpreterActions.debuggerReset(workspaceLocation)), + handleInterruptEval: () => + dispatch(InterpreterActions.beginInterruptExecution(workspaceLocation)), handleToggleEditorAutorun: () => dispatch(toggleEditorAutorun(workspaceLocation)) }; }, [dispatch]); diff --git a/src/pages/playground/Playground.tsx b/src/pages/playground/Playground.tsx index 45db60fb85..9d352d04e1 100644 --- a/src/pages/playground/Playground.tsx +++ b/src/pages/playground/Playground.tsx @@ -11,12 +11,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch, useStore } from 'react-redux'; import { useLocation, useNavigate } from 'react-router'; import { AnyAction, Dispatch } from 'redux'; -import { - beginDebuggerPause, - beginInterruptExecution, - debuggerReset, - debuggerResume -} from 'src/commons/application/actions/InterpreterActions'; +import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import SessionActions from 'src/commons/application/actions/SessionActions'; import { setEditorSessionId, @@ -460,11 +455,12 @@ const Playground: React.FC = props => { const autorunButtonHandlers = useMemo(() => { return { handleEditorEval: () => dispatch(evalEditor(workspaceLocation)), - handleInterruptEval: () => dispatch(beginInterruptExecution(workspaceLocation)), + handleInterruptEval: () => + dispatch(InterpreterActions.beginInterruptExecution(workspaceLocation)), handleToggleEditorAutorun: () => dispatch(toggleEditorAutorun(workspaceLocation)), - handleDebuggerPause: () => dispatch(beginDebuggerPause(workspaceLocation)), - handleDebuggerReset: () => dispatch(debuggerReset(workspaceLocation)), - handleDebuggerResume: () => dispatch(debuggerResume(workspaceLocation)) + handleDebuggerPause: () => dispatch(InterpreterActions.beginDebuggerPause(workspaceLocation)), + handleDebuggerReset: () => dispatch(InterpreterActions.debuggerReset(workspaceLocation)), + handleDebuggerResume: () => dispatch(InterpreterActions.debuggerResume(workspaceLocation)) }; }, [dispatch, workspaceLocation]); diff --git a/src/pages/sourcecast/Sourcecast.tsx b/src/pages/sourcecast/Sourcecast.tsx index af7b6d7a45..95ab3dd214 100644 --- a/src/pages/sourcecast/Sourcecast.tsx +++ b/src/pages/sourcecast/Sourcecast.tsx @@ -5,12 +5,7 @@ import { Chapter, Variant } from 'js-slang/dist/types'; import { useEffect, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { useParams } from 'react-router'; -import { - beginDebuggerPause, - beginInterruptExecution, - debuggerReset, - debuggerResume -} from 'src/commons/application/actions/InterpreterActions'; +import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import { Position } from 'src/commons/editor/EditorTypes'; import { changeSideContentHeight } from 'src/commons/sideContent/SideContentActions'; import { useSideContent } from 'src/commons/sideContent/SideContentHelper'; @@ -218,10 +213,11 @@ const Sourcecast: React.FC = () => { const autorunButtonHandlers = useMemo(() => { return { - handleDebuggerPause: () => dispatch(beginDebuggerPause(workspaceLocation)), - handleDebuggerReset: () => dispatch(debuggerReset(workspaceLocation)), - handleDebuggerResume: () => dispatch(debuggerResume(workspaceLocation)), - handleInterruptEval: () => dispatch(beginInterruptExecution(workspaceLocation)), + handleDebuggerPause: () => dispatch(InterpreterActions.beginDebuggerPause(workspaceLocation)), + handleDebuggerReset: () => dispatch(InterpreterActions.debuggerReset(workspaceLocation)), + handleDebuggerResume: () => dispatch(InterpreterActions.debuggerResume(workspaceLocation)), + handleInterruptEval: () => + dispatch(InterpreterActions.beginInterruptExecution(workspaceLocation)), handleToggleEditorAutorun: () => dispatch(toggleEditorAutorun(workspaceLocation)) }; }, [dispatch]); From f51111f8e4cfbdfcd1edb653bda98454a775f3ca Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Mon, 6 May 2024 23:25:42 +0800 Subject: [PATCH 15/16] Remove named exports for InterpreterActions --- .../application/actions/InterpreterActions.ts | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/commons/application/actions/InterpreterActions.ts b/src/commons/application/actions/InterpreterActions.ts index b99740be99..b84838f28c 100644 --- a/src/commons/application/actions/InterpreterActions.ts +++ b/src/commons/application/actions/InterpreterActions.ts @@ -3,7 +3,7 @@ import { createActions } from 'src/commons/redux/utils'; import { WorkspaceLocation } from '../../workspace/WorkspaceTypes'; -const newActions = createActions('interpreter', { +const InterpreterActions = createActions('interpreter', { handleConsoleLog: (workspaceLocation: WorkspaceLocation, ...logString: string[]) => ({ logString, workspaceLocation @@ -38,22 +38,5 @@ const newActions = createActions('interpreter', { debuggerReset: (workspaceLocation: WorkspaceLocation) => ({ workspaceLocation }) }); -// For compatibility with existing code (reducer) -export const { - handleConsoleLog, - evalInterpreterSuccess, - evalTestcaseSuccess, - evalTestcaseFailure, - evalInterpreterError, - beginInterruptExecution, - endInterruptExecution, - beginDebuggerPause, - endDebuggerPause, - debuggerResume, - debuggerReset -} = newActions; - // For compatibility with existing code (actions helper) -export default { - ...newActions -}; +export default InterpreterActions; From 2422a1ee088f9d4fd894044dce191d79c06af0de Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Mon, 6 May 2024 23:30:37 +0800 Subject: [PATCH 16/16] Use default import in WorkspaceReducer and test --- src/commons/workspace/WorkspaceReducer.ts | 63 ++--- .../workspace/__tests__/WorkspaceReducer.ts | 236 ++++++++++-------- 2 files changed, 154 insertions(+), 145 deletions(-) diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index 142f4987c9..006a13b549 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -25,28 +25,7 @@ import { handleCseAndStepperActions } from './reducers/cseReducer'; import { handleDebuggerActions } from './reducers/debuggerReducer'; import { handleEditorActions } from './reducers/editorReducer'; import { handleReplActions } from './reducers/replReducer'; -import { - changeExecTime, - changeExternalLibrary, - disableTokenCounter, - enableTokenCounter, - endClearContext, - evalEditor, - notifyProgramEvaluated, - resetTestcase, - resetWorkspace, - setIsEditorReadonly, - setTokenCount, - toggleEditorAutorun, - updateCurrentAssessmentId, - updateCurrentSubmissionId, - updateHasUnsavedChanges, - updateLastDebuggerResult, - updateLastNonDetResult, - updateSublanguage, - updateSubmissionsTableFilters, - updateWorkspace -} from './WorkspaceActions'; +import WorkspaceActions from './WorkspaceActions'; import { WorkspaceLocation, WorkspaceManagerState } from './WorkspaceTypes'; export const getWorkspaceLocation = (action: any): WorkspaceLocation => { @@ -100,15 +79,15 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { handleReplActions(builder); handleDebuggerActions(builder); builder - .addCase(setTokenCount, (state, action) => { + .addCase(WorkspaceActions.setTokenCount, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].tokenCount = action.payload.tokenCount; }) - .addCase(changeExecTime, (state, action) => { + .addCase(WorkspaceActions.changeExecTime, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].execTime = action.payload.execTime; }) - .addCase(endClearContext, (state, action) => { + .addCase(WorkspaceActions.endClearContext, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); // For some reason mutating the state directly results in type // errors, so we have to do it the old-fashioned way @@ -127,7 +106,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { } }; }) - .addCase(changeExternalLibrary, (state, action) => { + .addCase(WorkspaceActions.changeExternalLibrary, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].externalLibrary = action.payload.newExternal; }) @@ -166,15 +145,15 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { playground: playgroundWorkspace }; }) - .addCase(enableTokenCounter, (state, action) => { + .addCase(WorkspaceActions.enableTokenCounter, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].hasTokenCounter = true; }) - .addCase(disableTokenCounter, (state, action) => { + .addCase(WorkspaceActions.disableTokenCounter, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].hasTokenCounter = false; }) - .addCase(evalEditor, (state, action) => { + .addCase(WorkspaceActions.evalEditor, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].isRunning = true; state[workspaceLocation].isDebugging = false; @@ -277,7 +256,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { state[workspaceLocation].isRunning = false; state[workspaceLocation].isDebugging = false; }) - .addCase(resetTestcase, (state, action) => { + .addCase(WorkspaceActions.resetTestcase, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); const testcase = state[workspaceLocation].editorTestcases[action.payload.index]; testcase.result = undefined; @@ -288,7 +267,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { * including the js-slang Context. Apply * any specified settings (workspaceOptions) */ - .addCase(resetWorkspace, (state, action) => { + .addCase(WorkspaceActions.resetWorkspace, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); // For some reason mutating the state directly results in type // errors, so we have to do it the old-fashioned way @@ -305,7 +284,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { * Updates workspace without changing anything * which has not been specified */ - .addCase(updateWorkspace, (state, action) => { + .addCase(WorkspaceActions.updateWorkspace, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); // For some reason mutating the state directly results in type // errors, so we have to do it the old-fashioned way @@ -325,7 +304,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].sessionDetails = action.payload.sessionDetails; }) - .addCase(setIsEditorReadonly, (state, action) => { + .addCase(WorkspaceActions.setIsEditorReadonly, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].isEditorReadonly = action.payload.isEditorReadonly; }) @@ -333,22 +312,22 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].sharedbConnected = action.payload.connected; }) - .addCase(toggleEditorAutorun, (state, action) => { + .addCase(WorkspaceActions.toggleEditorAutorun, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].isEditorAutorun = !state[workspaceLocation].isEditorAutorun; }) - .addCase(updateSubmissionsTableFilters, (state, action) => { + .addCase(WorkspaceActions.updateSubmissionsTableFilters, (state, action) => { state.grading.submissionsTableFilters = action.payload.filters; }) - .addCase(updateCurrentAssessmentId, (state, action) => { + .addCase(WorkspaceActions.updateCurrentAssessmentId, (state, action) => { state.assessment.currentAssessment = action.payload.assessmentId; state.assessment.currentQuestion = action.payload.questionId; }) - .addCase(updateCurrentSubmissionId, (state, action) => { + .addCase(WorkspaceActions.updateCurrentSubmissionId, (state, action) => { state.grading.currentSubmission = action.payload.submissionId; state.grading.currentQuestion = action.payload.questionId; }) - .addCase(updateHasUnsavedChanges, (state, action) => { + .addCase(WorkspaceActions.updateHasUnsavedChanges, (state, action) => { // For some reason mutating the state directly results in type // errors, so we have to do it the old-fashioned way const workspaceLocation = getWorkspaceLocation(action); @@ -360,7 +339,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { } }; }) - .addCase(updateSublanguage, (state, action) => { + .addCase(WorkspaceActions.updateSublanguage, (state, action) => { // TODO: Mark for removal const { chapter, variant } = action.payload.sublang; state.playground.context.chapter = chapter; @@ -375,11 +354,11 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { // debuggerContext.context = action.payload.context; // debuggerContext.workspaceLocation = action.payload.workspaceLocation; // }) - .addCase(updateLastDebuggerResult, (state, action) => { + .addCase(WorkspaceActions.updateLastDebuggerResult, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].lastDebuggerResult = action.payload.lastDebuggerResult; }) - .addCase(updateLastNonDetResult, (state, action) => { + .addCase(WorkspaceActions.updateLastNonDetResult, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); state[workspaceLocation].lastNonDetResult = action.payload.lastNonDetResult; }); @@ -393,7 +372,7 @@ const oldWorkspaceReducer: Reducer = ( const workspaceLocation = getWorkspaceLocation(action); switch (action.type) { - case notifyProgramEvaluated.type: { + case WorkspaceActions.notifyProgramEvaluated.type: { const debuggerContext = { ...state[workspaceLocation].debuggerContext, result: action.payload.result, diff --git a/src/commons/workspace/__tests__/WorkspaceReducer.ts b/src/commons/workspace/__tests__/WorkspaceReducer.ts index 61c04903a9..e9e35af436 100644 --- a/src/commons/workspace/__tests__/WorkspaceReducer.ts +++ b/src/commons/workspace/__tests__/WorkspaceReducer.ts @@ -19,40 +19,7 @@ import { import { HighlightedLines, Position } from '../../editor/EditorTypes'; import Constants from '../../utils/Constants'; import { createContext } from '../../utils/JsSlangHelper'; -import { - addEditorTab, - browseReplHistoryDown, - browseReplHistoryUp, - changeExternalLibrary, - clearReplInput, - clearReplOutput, - clearReplOutputLast, - endClearContext, - evalEditor, - evalRepl, - moveCursor, - removeEditorTab, - removeEditorTabForFile, - removeEditorTabsForDirectory, - renameEditorTabForFile, - renameEditorTabsForDirectory, - resetTestcase, - resetWorkspace, - sendReplInputToOutput, - setEditorBreakpoint, - setEditorHighlightedLines, - setFolderMode, - shiftEditorTab, - toggleEditorAutorun, - toggleUsingSubst, - updateActiveEditorTab, - updateActiveEditorTabIndex, - updateCurrentAssessmentId, - updateCurrentSubmissionId, - updateEditorValue, - updateHasUnsavedChanges, - updateReplValue -} from '../WorkspaceActions'; +import WorkspaceActions from '../WorkspaceActions'; import { WorkspaceReducer } from '../WorkspaceReducer'; import { EditorTabState, @@ -124,7 +91,7 @@ describe('BROWSE_REPL_HISTORY_DOWN', () => { }; const replDownDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ replHistory }); - const actions = generateActions(browseReplHistoryDown.type, { replHistory }); + const actions = generateActions(WorkspaceActions.browseReplHistoryDown.type, { replHistory }); actions.forEach(action => { let result = WorkspaceReducer(replDownDefaultState, action); @@ -168,7 +135,7 @@ describe('BROWSE_REPL_HISTORY_DOWN', () => { }; const replDownDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ replHistory }); - const actions = generateActions(browseReplHistoryDown.type, { replHistory }); + const actions = generateActions(WorkspaceActions.browseReplHistoryDown.type, { replHistory }); actions.forEach(action => { const result = WorkspaceReducer(replDownDefaultState, action); @@ -191,7 +158,7 @@ describe('BROWSE_REPL_HISTORY_UP', () => { replHistory, replValue }); - const actions = generateActions(browseReplHistoryUp.type, { replHistory }); + const actions = generateActions(WorkspaceActions.browseReplHistoryUp.type, { replHistory }); actions.forEach(action => { let result = WorkspaceReducer(replUpDefaultState, action); @@ -246,7 +213,7 @@ describe('CHANGE_EXTERNAL_LIBRARY', () => { test('sets externalLibrary correctly', () => { const newExternal = 'new_external_test' as ExternalLibraryName; const playgroundAction = { - type: changeExternalLibrary.type, + type: WorkspaceActions.changeExternalLibrary.type, payload: { newExternal, workspaceLocation: playgroundWorkspace @@ -268,7 +235,7 @@ describe('CLEAR_REPL_INPUT', () => { test('clears replValue', () => { const replValue = 'test repl value'; const clearReplDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ replValue }); - const actions = generateActions(clearReplInput.type); + const actions = generateActions(WorkspaceActions.clearReplInput.type); actions.forEach(action => { const result = WorkspaceReducer(clearReplDefaultState, action); @@ -288,7 +255,7 @@ describe('CLEAR_REPL_OUTPUT', () => { test('clears output', () => { const output: InterpreterOutput[] = [{ type: 'code', value: 'test repl input' }]; const clearReplDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ output }); - const actions = generateActions(clearReplOutput.type); + const actions = generateActions(WorkspaceActions.clearReplOutput.type); actions.forEach(action => { const result = WorkspaceReducer(clearReplDefaultState, action); @@ -319,7 +286,7 @@ describe('CLEAR_REPL_OUTPUT_LAST', () => { } ]; const clearReplLastPriorState: WorkspaceManagerState = generateDefaultWorkspace({ output }); - const actions = generateActions(clearReplOutputLast.type); + const actions = generateActions(WorkspaceActions.clearReplOutputLast.type); actions.forEach(action => { const result = WorkspaceReducer(clearReplLastPriorState, action); @@ -404,7 +371,7 @@ describe('END_CLEAR_CONTEXT', () => { globals: mockGlobals }; - const actions = generateActions(endClearContext.type, { library }); + const actions = generateActions(WorkspaceActions.endClearContext.type, { library }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); @@ -485,7 +452,7 @@ describe('EVAL_EDITOR', () => { const evalEditorDefaultState: WorkspaceManagerState = generateDefaultWorkspace({ isDebugging }); - const actions = generateActions(evalEditor.type); + const actions = generateActions(WorkspaceActions.evalEditor.type); actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); @@ -636,7 +603,7 @@ describe('EVAL_INTERPRETER_SUCCESS', () => { describe('EVAL_REPL', () => { test('sets isRunning to true', () => { - const actions = generateActions(evalRepl.type); + const actions = generateActions(WorkspaceActions.evalRepl.type); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); @@ -896,7 +863,7 @@ describe('RESET_TESTCASE', () => { editorTestcases }); - const actions = generateActions(resetTestcase.type, { + const actions = generateActions(WorkspaceActions.resetTestcase.type, { index: 1 }); @@ -931,7 +898,7 @@ describe('RESET_WORKSPACE', () => { replValue: 'test repl value' }; - const actions = generateActions(resetWorkspace.type, { workspaceOptions }); + const actions = generateActions(WorkspaceActions.resetWorkspace.type, { workspaceOptions }); actions.forEach(action => { const result = WorkspaceReducer(resetWorkspaceDefaultState, action); @@ -966,7 +933,7 @@ describe('SEND_REPL_INPUT_TO_OUTPUT', () => { }); const newOutput = 'new-output-test'; - const actions = generateActions(sendReplInputToOutput.type, { + const actions = generateActions(WorkspaceActions.sendReplInputToOutput.type, { type: 'code', value: newOutput }); @@ -1007,7 +974,10 @@ describe('SEND_REPL_INPUT_TO_OUTPUT', () => { }); const newOutput = ''; - const actions = generateActions(sendReplInputToOutput.type, { type: 'code', value: newOutput }); + const actions = generateActions(WorkspaceActions.sendReplInputToOutput.type, { + type: 'code', + value: newOutput + }); actions.forEach(action => { const result = WorkspaceReducer(inputToOutputDefaultState, action); @@ -1064,7 +1034,7 @@ describe('SET_SHAREDB_CONNECTED', () => { describe('TOGGLE_EDITOR_AUTORUN', () => { test('toggles isEditorAutorun correctly', () => { - const actions = generateActions(toggleEditorAutorun.type); + const actions = generateActions(WorkspaceActions.toggleEditorAutorun.type); actions.forEach(action => { let result = WorkspaceReducer(defaultWorkspaceManager, action); @@ -1094,7 +1064,7 @@ describe('UPDATE_CURRENT_ASSESSMENT_ID', () => { const assessmentId = 3; const questionId = 7; const assessmentAction = { - type: updateCurrentAssessmentId.type, + type: WorkspaceActions.updateCurrentAssessmentId.type, payload: { assessmentId, questionId } } as const; @@ -1115,7 +1085,7 @@ describe('UPDATE_CURRENT_SUBMISSION_ID', () => { const submissionId = 5; const questionId = 8; const assessmentAction = { - type: updateCurrentSubmissionId.type, + type: WorkspaceActions.updateCurrentSubmissionId.type, payload: { submissionId, questionId } } as const; @@ -1134,7 +1104,7 @@ describe('UPDATE_CURRENT_SUBMISSION_ID', () => { describe('SET_FOLDER_MODE', () => { test('sets isFolderModeEnabled correctly', () => { const isFolderModeEnabled = true; - const actions = generateActions(setFolderMode.type, { isFolderModeEnabled }); + const actions = generateActions(WorkspaceActions.setFolderMode.type, { isFolderModeEnabled }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); @@ -1171,7 +1141,9 @@ describe('UPDATE_ACTIVE_EDITOR_TAB_INDEX', () => { editorTabs }); - const actions = generateActions(updateActiveEditorTabIndex.type, { activeEditorTabIndex }); + const actions = generateActions(WorkspaceActions.updateActiveEditorTabIndex.type, { + activeEditorTabIndex + }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1186,7 +1158,9 @@ describe('UPDATE_ACTIVE_EDITOR_TAB_INDEX', () => { editorTabs }); - const actions = generateActions(updateActiveEditorTabIndex.type, { activeEditorTabIndex }); + const actions = generateActions(WorkspaceActions.updateActiveEditorTabIndex.type, { + activeEditorTabIndex + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1213,7 +1187,9 @@ describe('UPDATE_ACTIVE_EDITOR_TAB_INDEX', () => { editorTabs }); - const actions = generateActions(updateActiveEditorTabIndex.type, { activeEditorTabIndex }); + const actions = generateActions(WorkspaceActions.updateActiveEditorTabIndex.type, { + activeEditorTabIndex + }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1244,7 +1220,9 @@ describe('UPDATE_ACTIVE_EDITOR_TAB', () => { ] }); - const actions = generateActions(updateActiveEditorTab.type, { activeEditorTabOptions }); + const actions = generateActions(WorkspaceActions.updateActiveEditorTab.type, { + activeEditorTabOptions + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1284,7 +1262,9 @@ describe('UPDATE_ACTIVE_EDITOR_TAB', () => { editorTabs: [] }); - const actions = generateActions(updateActiveEditorTab.type, { activeEditorTabOptions }); + const actions = generateActions(WorkspaceActions.updateActiveEditorTab.type, { + activeEditorTabOptions + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1314,7 +1294,10 @@ describe('UPDATE_EDITOR_VALUE', () => { editorTabs }); - const actions = generateActions(updateEditorValue.type, { editorTabIndex, newEditorValue }); + const actions = generateActions(WorkspaceActions.updateEditorValue.type, { + editorTabIndex, + newEditorValue + }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1329,7 +1312,10 @@ describe('UPDATE_EDITOR_VALUE', () => { editorTabs }); - const actions = generateActions(updateEditorValue.type, { editorTabIndex, newEditorValue }); + const actions = generateActions(WorkspaceActions.updateEditorValue.type, { + editorTabIndex, + newEditorValue + }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1344,7 +1330,10 @@ describe('UPDATE_EDITOR_VALUE', () => { editorTabs }); - const actions = generateActions(updateEditorValue.type, { editorTabIndex, newEditorValue }); + const actions = generateActions(WorkspaceActions.updateEditorValue.type, { + editorTabIndex, + newEditorValue + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1381,7 +1370,10 @@ describe('UPDATE_EDITOR_BREAKPOINTS', () => { editorTabs }); - const actions = generateActions(setEditorBreakpoint.type, { editorTabIndex, newBreakpoints }); + const actions = generateActions(WorkspaceActions.setEditorBreakpoint.type, { + editorTabIndex, + newBreakpoints + }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1396,7 +1388,10 @@ describe('UPDATE_EDITOR_BREAKPOINTS', () => { editorTabs }); - const actions = generateActions(setEditorBreakpoint.type, { editorTabIndex, newBreakpoints }); + const actions = generateActions(WorkspaceActions.setEditorBreakpoint.type, { + editorTabIndex, + newBreakpoints + }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1411,7 +1406,10 @@ describe('UPDATE_EDITOR_BREAKPOINTS', () => { editorTabs }); - const actions = generateActions(setEditorBreakpoint.type, { editorTabIndex, newBreakpoints }); + const actions = generateActions(WorkspaceActions.setEditorBreakpoint.type, { + editorTabIndex, + newBreakpoints + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1451,7 +1449,7 @@ describe('UPDATE_EDITOR_HIGHLIGHTED_LINES', () => { editorTabs }); - const actions = generateActions(setEditorHighlightedLines.type, { + const actions = generateActions(WorkspaceActions.setEditorHighlightedLines.type, { editorTabIndex, newHighlightedLines }); @@ -1469,7 +1467,7 @@ describe('UPDATE_EDITOR_HIGHLIGHTED_LINES', () => { editorTabs }); - const actions = generateActions(setEditorHighlightedLines.type, { + const actions = generateActions(WorkspaceActions.setEditorHighlightedLines.type, { editorTabIndex, newHighlightedLines }); @@ -1487,7 +1485,7 @@ describe('UPDATE_EDITOR_HIGHLIGHTED_LINES', () => { editorTabs }); - const actions = generateActions(setEditorHighlightedLines.type, { + const actions = generateActions(WorkspaceActions.setEditorHighlightedLines.type, { editorTabIndex, newHighlightedLines }); @@ -1533,7 +1531,7 @@ describe('MOVE_CURSOR', () => { editorTabs }); - const actions = generateActions(moveCursor.type, { + const actions = generateActions(WorkspaceActions.moveCursor.type, { editorTabIndex, newCursorPosition }); @@ -1551,7 +1549,7 @@ describe('MOVE_CURSOR', () => { editorTabs }); - const actions = generateActions(moveCursor.type, { + const actions = generateActions(WorkspaceActions.moveCursor.type, { editorTabIndex, newCursorPosition }); @@ -1569,7 +1567,7 @@ describe('MOVE_CURSOR', () => { editorTabs }); - const actions = generateActions(moveCursor.type, { + const actions = generateActions(WorkspaceActions.moveCursor.type, { editorTabIndex, newCursorPosition }); @@ -1611,7 +1609,7 @@ describe('ADD_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(addEditorTab.type, { filePath, editorValue }); + const actions = generateActions(WorkspaceActions.addEditorTab.type, { filePath, editorValue }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1643,7 +1641,7 @@ describe('ADD_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(addEditorTab.type, { filePath, editorValue }); + const actions = generateActions(WorkspaceActions.addEditorTab.type, { filePath, editorValue }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1699,7 +1697,7 @@ describe('SHIFT_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(shiftEditorTab.type, { + const actions = generateActions(WorkspaceActions.shiftEditorTab.type, { previousEditorTabIndex, newEditorTabIndex }); @@ -1718,7 +1716,7 @@ describe('SHIFT_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(shiftEditorTab.type, { + const actions = generateActions(WorkspaceActions.shiftEditorTab.type, { previousEditorTabIndex, newEditorTabIndex }); @@ -1739,7 +1737,7 @@ describe('SHIFT_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(shiftEditorTab.type, { + const actions = generateActions(WorkspaceActions.shiftEditorTab.type, { previousEditorTabIndex, newEditorTabIndex }); @@ -1758,7 +1756,7 @@ describe('SHIFT_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(shiftEditorTab.type, { + const actions = generateActions(WorkspaceActions.shiftEditorTab.type, { previousEditorTabIndex, newEditorTabIndex }); @@ -1777,7 +1775,7 @@ describe('SHIFT_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(shiftEditorTab.type, { + const actions = generateActions(WorkspaceActions.shiftEditorTab.type, { previousEditorTabIndex, newEditorTabIndex }); @@ -1807,7 +1805,7 @@ describe('SHIFT_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(shiftEditorTab.type, { + const actions = generateActions(WorkspaceActions.shiftEditorTab.type, { previousEditorTabIndex, newEditorTabIndex }); @@ -1851,7 +1849,7 @@ describe('REMOVE_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(removeEditorTab.type, { editorTabIndex }); + const actions = generateActions(WorkspaceActions.removeEditorTab.type, { editorTabIndex }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1866,7 +1864,7 @@ describe('REMOVE_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(removeEditorTab.type, { editorTabIndex }); + const actions = generateActions(WorkspaceActions.removeEditorTab.type, { editorTabIndex }); actions.forEach(action => { const resultThunk = () => WorkspaceReducer(defaultWorkspaceState, action); @@ -1881,7 +1879,7 @@ describe('REMOVE_EDITOR_TAB', () => { editorTabs: [zerothEditorTab] }); - const actions = generateActions(removeEditorTab.type, { editorTabIndex }); + const actions = generateActions(WorkspaceActions.removeEditorTab.type, { editorTabIndex }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1908,7 +1906,7 @@ describe('REMOVE_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(removeEditorTab.type, { editorTabIndex }); + const actions = generateActions(WorkspaceActions.removeEditorTab.type, { editorTabIndex }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1935,7 +1933,7 @@ describe('REMOVE_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(removeEditorTab.type, { editorTabIndex }); + const actions = generateActions(WorkspaceActions.removeEditorTab.type, { editorTabIndex }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1962,7 +1960,7 @@ describe('REMOVE_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(removeEditorTab.type, { editorTabIndex }); + const actions = generateActions(WorkspaceActions.removeEditorTab.type, { editorTabIndex }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -1989,7 +1987,7 @@ describe('REMOVE_EDITOR_TAB', () => { editorTabs }); - const actions = generateActions(removeEditorTab.type, { editorTabIndex }); + const actions = generateActions(WorkspaceActions.removeEditorTab.type, { editorTabIndex }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2032,7 +2030,9 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { editorTabs }); - const actions = generateActions(removeEditorTabForFile.type, { removedFilePath }); + const actions = generateActions(WorkspaceActions.removeEditorTabForFile.type, { + removedFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2049,7 +2049,9 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { editorTabs: [zerothEditorTab] }); - const actions = generateActions(removeEditorTabForFile.type, { removedFilePath }); + const actions = generateActions(WorkspaceActions.removeEditorTabForFile.type, { + removedFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2076,7 +2078,9 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { editorTabs }); - const actions = generateActions(removeEditorTabForFile.type, { removedFilePath }); + const actions = generateActions(WorkspaceActions.removeEditorTabForFile.type, { + removedFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2103,7 +2107,9 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { editorTabs }); - const actions = generateActions(removeEditorTabForFile.type, { removedFilePath }); + const actions = generateActions(WorkspaceActions.removeEditorTabForFile.type, { + removedFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2130,7 +2136,9 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { editorTabs }); - const actions = generateActions(removeEditorTabForFile.type, { removedFilePath }); + const actions = generateActions(WorkspaceActions.removeEditorTabForFile.type, { + removedFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2157,7 +2165,9 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { editorTabs }); - const actions = generateActions(removeEditorTabForFile.type, { removedFilePath }); + const actions = generateActions(WorkspaceActions.removeEditorTabForFile.type, { + removedFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2217,7 +2227,9 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(removeEditorTabsForDirectory.type, { removedDirectoryPath }); + const actions = generateActions(WorkspaceActions.removeEditorTabsForDirectory.type, { + removedDirectoryPath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2234,7 +2246,9 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(removeEditorTabsForDirectory.type, { removedDirectoryPath }); + const actions = generateActions(WorkspaceActions.removeEditorTabsForDirectory.type, { + removedDirectoryPath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2261,7 +2275,9 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(removeEditorTabsForDirectory.type, { removedDirectoryPath }); + const actions = generateActions(WorkspaceActions.removeEditorTabsForDirectory.type, { + removedDirectoryPath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2288,7 +2304,9 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(removeEditorTabsForDirectory.type, { removedDirectoryPath }); + const actions = generateActions(WorkspaceActions.removeEditorTabsForDirectory.type, { + removedDirectoryPath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2315,7 +2333,9 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(removeEditorTabsForDirectory.type, { removedDirectoryPath }); + const actions = generateActions(WorkspaceActions.removeEditorTabsForDirectory.type, { + removedDirectoryPath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2342,7 +2362,9 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(removeEditorTabsForDirectory.type, { removedDirectoryPath }); + const actions = generateActions(WorkspaceActions.removeEditorTabsForDirectory.type, { + removedDirectoryPath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2385,7 +2407,10 @@ describe('RENAME_EDITOR_TAB_FOR_FILE', () => { editorTabs }); - const actions = generateActions(renameEditorTabForFile.type, { oldFilePath, newFilePath }); + const actions = generateActions(WorkspaceActions.renameEditorTabForFile.type, { + oldFilePath, + newFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2403,7 +2428,10 @@ describe('RENAME_EDITOR_TAB_FOR_FILE', () => { editorTabs }); - const actions = generateActions(renameEditorTabForFile.type, { oldFilePath, newFilePath }); + const actions = generateActions(WorkspaceActions.renameEditorTabForFile.type, { + oldFilePath, + newFilePath + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); @@ -2450,7 +2478,7 @@ describe('RENAME_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(renameEditorTabsForDirectory.type, { + const actions = generateActions(WorkspaceActions.renameEditorTabsForDirectory.type, { oldDirectoryPath, newDirectoryPath }); @@ -2470,7 +2498,7 @@ describe('RENAME_EDITOR_TABS_FOR_DIRECTORY', () => { editorTabs }); - const actions = generateActions(renameEditorTabsForDirectory.type, { + const actions = generateActions(WorkspaceActions.renameEditorTabsForDirectory.type, { oldDirectoryPath, newDirectoryPath }); @@ -2503,7 +2531,9 @@ describe('RENAME_EDITOR_TABS_FOR_DIRECTORY', () => { describe('UPDATE_HAS_UNSAVED_CHANGES', () => { test('sets hasUnsavedChanges correctly', () => { const hasUnsavedChanges = true; - const actions = generateActions(updateHasUnsavedChanges.type, { hasUnsavedChanges }); + const actions = generateActions(WorkspaceActions.updateHasUnsavedChanges.type, { + hasUnsavedChanges + }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); @@ -2522,7 +2552,7 @@ describe('UPDATE_HAS_UNSAVED_CHANGES', () => { describe('UPDATE_REPL_VALUE', () => { test('sets replValue correctly', () => { const newReplValue = 'test new repl value'; - const actions = generateActions(updateReplValue.type, { newReplValue }); + const actions = generateActions(WorkspaceActions.updateReplValue.type, { newReplValue }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); @@ -2541,7 +2571,7 @@ describe('UPDATE_REPL_VALUE', () => { describe('TOGGLE_USING_SUBST', () => { test('sets usingSubst correctly', () => { const usingSubst = true; - const actions = generateActions(toggleUsingSubst.type, { usingSubst }); + const actions = generateActions(WorkspaceActions.toggleUsingSubst.type, { usingSubst }); actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action);