Skip to content

Commit 05b6ed9

Browse files
NocaxeRichDom2185
andauthored
Versioning and history (#3659)
* Implement Versioning and History of Editor Code for Assessment Workspaces * Create tests for versioning and history feature * Prevent submit answer from displaying success message, as submit status is the same as version history save status. * Fix updateVersionName API route and getVersionHistory return value * Fix bug causing double saving, now only submits answer, and allows the submission to save code to backend * Change restored versions to be named "(name)-restored" instead of "(timestamp)-restored" * Prevent autosave in team assessments * Implement UI for Versioning and History feature * Implement version preview before restoring * Fix bug causing duplicate versions to be saved * Prevent save status indicator from showing in team assessments * Update test to not check for success message upon autosave * Update snapshots to align with changes * Reformatted imports * Fix bug where save status is not correctly checked and updated, always showing saved even on fail * Refactor restoreVersionSaga and prevent restoring from auto-submitting in team assessments * Fix bug where renaming updates the timestamp * Fix bug where restoring was renaming the wrong version * update snapshots to match changes * Fix bug where new UI is not updated for mobile view * Change return type of sagas for better type safety * Fix bug where restoring will rename an existing version if the code is not updated * Memoize sorted array of versions to improve performance * Fix bug where save button appears incorrectly for individual assessments during loading * Update snapshot to match bug fixes * Fix formatting * Fix bug where the restoreVersion action did not take into account the SagaIterator return type of restoreVersionSaga * Fix bug where one auto-save can prematurely unblock another, causing an incorrectly timed version history refresh * Fix bug where UI would show a stale name if the rename API fails * Change all early returns in performAutoSave to return false instead of undefined * Change onClick prop to match how other control-bar buttons are typed * Fix bug where backend saga mock never updates save status * Change saving status watcher to only work in assessment workspaces * Fix bug where restoring a version always shows saved even while in the process of saving * Add check to ensure that timestamp is a naive datetime before appending 'Z" * Add safeguard to prevent blanking the editor when restoring a malformed version * Edited clickable versions in the panel to be keyboard-focusable and announced correctly by assistive tech * Add safeguard to prevent slicing the inserted_at when it is null/undefined * Fix bug where save button for team assessments is enabled even when a student has not joined a team * Edit autosaves to only fetch version history if the panel is open to improve performance * Remove redundant code * Use .reverse() to sort the versions by timestamp instead of .sort() to improve performance * Update field to match backend change * Fix bug where wrong version is renamed after restoring * Add tests for coverage * Implement diff viewer when previewing version to restore * Prevent race condition by making concurrent auto saves wait for the first one to complete * Fix bug where performAutoSave returns true even if save failed * Fix bug where admin panel configuration switches were not animating * Fix bug where hasUnsavedCode is not set to false when restoring identical code * Make code save when history panel is opened * Update tests * Implement configuration setting for enabling and disabling autosave per assessment group and per assessment overrides * Update tests for configuration of autosave * Update snapshots * Fix bug causing duplicate versions when restoring * Fix bug causing hasUnsavedChanges to be false even when save failed from restoring * Prevent autosaving from fetch and restore when disabled * Update tests * Fix bug where autosave was enabled for MCQ * Change deduplication check for better performance * Change restoreVersionSaga to return earlier if autosave is disabled * Make auto save return earlier if it is a team assessment * Update tests * Improve performance of restoring version by removing redundant search * Update tests * Improve performance of renaming versions when restoring * Change default to enable autosave for assessments created before this feature was implemented * Implement lazy loading of versions for previewing * Implement collapsible grouping of versions in history panel * Remove redundant work following backend changes * Edit reset confirmation message * Update tests * Prevent race condition from hanging and blocking future autosaves * Change direct calls to fetchVersionHistorySaga to action dispatches instead * Prevent possible indefinite hanging of saga * Add onSelectVersion to the useEffect dependency array * Fix bug where rapid selection of versions can cause incorrect code to be displayed * Update lockfile post-merge * Remove duplicate dependency * Remove unnecessary type assertion * Refactor to use SCSS module for scoping * Fix global * Update test snapshots * Remove unnecessary type assertion --------- Co-authored-by: Richard Dominick <34370238+RichDom2185@users.noreply.github.com>
1 parent 06478a0 commit 05b6ed9

39 files changed

Lines changed: 2636 additions & 97 deletions

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"@szhsin/react-menu": "^4.0.0",
5252
"@tanstack/react-table": "^8.9.3",
5353
"ace-builds": "^1.42.1",
54+
"ace-diff": "^4.0.0",
5455
"acorn": "^8.9.0",
5556
"ag-grid-community": "^35.0.0",
5657
"ag-grid-react": "^35.0.0",

src/bootstrap/agGrid.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
type Module,
1010
ModuleRegistry,
1111
PaginationModule,
12+
RowApiModule,
1213
RowDragModule,
1314
TextEditorModule,
1415
TextFilterModule,
@@ -24,6 +25,7 @@ const productionModules: readonly Module[] = [
2425
DateFilterModule,
2526
InfiniteRowModelModule,
2627
PaginationModule,
28+
RowApiModule,
2729
RowDragModule,
2830
TextEditorModule,
2931
TextFilterModule,

src/commons/application/ApplicationTypes.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,17 @@ export const createDefaultWorkspace = (workspaceLocation: WorkspaceLocation): Wo
392392
debuggerContext: {} as DebuggerContext,
393393
lastDebuggerResult: undefined,
394394
files: {},
395-
updateUserRoleCallback: () => {}
395+
updateUserRoleCallback: () => {},
396+
versionHistory: {
397+
versions: [],
398+
selectedVersion: null,
399+
selectedVersionCode: null,
400+
isLoadingCode: false,
401+
isLoading: false,
402+
isHistoryPanelOpen: false,
403+
isAutoSaving: false
404+
},
405+
saveStatus: 'idle'
396406
});
397407

398408
const defaultFileName = 'program.js';

src/commons/application/actions/__tests__/SessionActions.test.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@ test('setAssessmentConfigurations generates correct action object', () => {
268268
hasTokenCounter: false,
269269
hasVotingFeatures: false,
270270
hoursBeforeEarlyXpDecay: 48,
271-
earlySubmissionXp: 200
271+
earlySubmissionXp: 200,
272+
isAutosaveEnabled: true
272273
},
273274
{
274275
assessmentConfigId: 2,
@@ -280,7 +281,8 @@ test('setAssessmentConfigurations generates correct action object', () => {
280281
hasTokenCounter: false,
281282
hasVotingFeatures: false,
282283
hoursBeforeEarlyXpDecay: 48,
283-
earlySubmissionXp: 200
284+
earlySubmissionXp: 200,
285+
isAutosaveEnabled: true
284286
},
285287
{
286288
assessmentConfigId: 3,
@@ -292,7 +294,8 @@ test('setAssessmentConfigurations generates correct action object', () => {
292294
hasTokenCounter: false,
293295
hasVotingFeatures: false,
294296
hoursBeforeEarlyXpDecay: 48,
295-
earlySubmissionXp: 200
297+
earlySubmissionXp: 200,
298+
isAutosaveEnabled: true
296299
}
297300
];
298301
const action = SessionActions.setAssessmentConfigurations(assesmentConfigurations);
@@ -726,7 +729,8 @@ test('updateAssessmentTypes generates correct action object', () => {
726729
hasTokenCounter: false,
727730
hasVotingFeatures: false,
728731
hoursBeforeEarlyXpDecay: 48,
729-
earlySubmissionXp: 200
732+
earlySubmissionXp: 200,
733+
isAutosaveEnabled: true
730734
},
731735
{
732736
assessmentConfigId: 2,
@@ -738,7 +742,8 @@ test('updateAssessmentTypes generates correct action object', () => {
738742
hasTokenCounter: false,
739743
hasVotingFeatures: false,
740744
hoursBeforeEarlyXpDecay: 48,
741-
earlySubmissionXp: 200
745+
earlySubmissionXp: 200,
746+
isAutosaveEnabled: true
742747
},
743748
{
744749
assessmentConfigId: 3,
@@ -750,7 +755,8 @@ test('updateAssessmentTypes generates correct action object', () => {
750755
hasTokenCounter: false,
751756
hasVotingFeatures: false,
752757
hoursBeforeEarlyXpDecay: 48,
753-
earlySubmissionXp: 200
758+
earlySubmissionXp: 200,
759+
isAutosaveEnabled: true
754760
},
755761
{
756762
assessmentConfigId: 4,
@@ -762,7 +768,8 @@ test('updateAssessmentTypes generates correct action object', () => {
762768
hasTokenCounter: false,
763769
hasVotingFeatures: false,
764770
hoursBeforeEarlyXpDecay: 48,
765-
earlySubmissionXp: 200
771+
earlySubmissionXp: 200,
772+
isAutosaveEnabled: true
766773
},
767774
{
768775
assessmentConfigId: 5,
@@ -774,7 +781,8 @@ test('updateAssessmentTypes generates correct action object', () => {
774781
hasTokenCounter: false,
775782
hasVotingFeatures: false,
776783
hoursBeforeEarlyXpDecay: 0,
777-
earlySubmissionXp: 0
784+
earlySubmissionXp: 0,
785+
isAutosaveEnabled: true
778786
},
779787
{
780788
assessmentConfigId: 6,
@@ -786,7 +794,8 @@ test('updateAssessmentTypes generates correct action object', () => {
786794
hasTokenCounter: false,
787795
hasVotingFeatures: false,
788796
hoursBeforeEarlyXpDecay: 48,
789-
earlySubmissionXp: 200
797+
earlySubmissionXp: 200,
798+
isAutosaveEnabled: true
790799
}
791800
];
792801
const action = SessionActions.updateAssessmentConfigs(assessmentConfigs);
@@ -807,7 +816,8 @@ test('deleteAssessmentConfig generates correct action object', () => {
807816
hasTokenCounter: false,
808817
hasVotingFeatures: false,
809818
hoursBeforeEarlyXpDecay: 48,
810-
earlySubmissionXp: 200
819+
earlySubmissionXp: 200,
820+
isAutosaveEnabled: true
811821
};
812822
const action = SessionActions.deleteAssessmentConfig(assessmentConfig);
813823
expect(action).toEqual({

src/commons/application/reducers/__tests__/SessionReducer.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ test('SET_ASSESSMENT_CONFIGURATIONS works correctly', () => {
149149
displayInDashboard: true,
150150
isMinigame: false,
151151
hasTokenCounter: false,
152-
hasVotingFeatures: false
152+
hasVotingFeatures: false,
153+
isAutosaveEnabled: true
153154
},
154155
{
155156
assessmentConfigId: 1,
@@ -164,7 +165,8 @@ test('SET_ASSESSMENT_CONFIGURATIONS works correctly', () => {
164165
displayInDashboard: true,
165166
isMinigame: false,
166167
hasTokenCounter: false,
167-
hasVotingFeatures: false
168+
hasVotingFeatures: false,
169+
isAutosaveEnabled: true
168170
},
169171
{
170172
assessmentConfigId: 1,
@@ -179,7 +181,8 @@ test('SET_ASSESSMENT_CONFIGURATIONS works correctly', () => {
179181
displayInDashboard: true,
180182
isMinigame: false,
181183
hasTokenCounter: false,
182-
hasVotingFeatures: false
184+
hasVotingFeatures: false,
185+
isAutosaveEnabled: true
183186
}
184187
];
185188

src/commons/assessment/AssessmentTypes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export type AssessmentOverview = {
6868
isPublished?: boolean; // refers to assessment as a whole being published
6969
hasVotingFeatures: boolean;
7070
hasTokenCounter?: boolean;
71+
isAutosaveEnabled?: boolean;
7172
isVotingPublished?: boolean;
7273
maxXp: number;
7374
earlySubmissionXp: number;
@@ -112,6 +113,7 @@ export type AssessmentConfiguration = {
112113
earlySubmissionXp: number;
113114
hasTokenCounter: boolean;
114115
hasVotingFeatures: boolean;
116+
isAutosaveEnabled: boolean;
115117
};
116118

117119
export interface IProgrammingQuestion extends BaseQuestion {

src/commons/assessment/__tests__/Assessment.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const mockAssessmentConfig: AssessmentConfiguration = {
2020
hasTokenCounter: false,
2121
hasVotingFeatures: false,
2222
hoursBeforeEarlyXpDecay: 48,
23-
earlySubmissionXp: 200
23+
earlySubmissionXp: 200,
24+
isAutosaveEnabled: true
2425
};
2526

2627
const getOverriddenStore = ({

0 commit comments

Comments
 (0)