diff --git a/app/assets/javascripts/mumuki_laboratory/application/bridge.js b/app/assets/javascripts/mumuki_laboratory/application/bridge.js index 7a8fa7a34..7e2f9efe3 100644 --- a/app/assets/javascripts/mumuki_laboratory/application/bridge.js +++ b/app/assets/javascripts/mumuki_laboratory/application/bridge.js @@ -1,25 +1,36 @@ /** - * @typedef {{status: SubmissionStatus, test_results: [{status: SubmissionStatus, title: string}]}} SubmissionClientResult + * @typedef {"errored"|"failed"|"passed_with_warnings"|"passed"|"pending"|"aborted"} SubmissionStatus */ /** * @typedef {{ * status: SubmissionStatus, - * class_for_progress_list_item?: string, - * guide_finished_by_solution?: boolean - * }} SubmissionResult + * test_results: [{status: SubmissionStatus, title: string}] + * }} SubmissionClientResult */ /** - * @typedef {{solution: object, client_result?: SubmissionClientResult}} Submission + * @typedef {{ + * status: SubmissionStatus, + * class_for_progress_list_item?: string, + * guide_finished_by_solution?: boolean + * }} SubmissionResult + */ + +/** + * @typedef {object} Solution */ /** - * @typedef {"errored"|"failed"|"passed_with_warnings"|"passed"|"pending"|"aborted"} SubmissionStatus + * @typedef {{ + * "solution[content]"?:string, + * solution?: Solution, + * client_result?: SubmissionClientResult + * }} Submission */ /** - * @typedef {{content?: {solution: object}, result?: SubmissionResult}} SubmissionAndResult + * @typedef {{submission?: Submission, result?: SubmissionResult}} SubmissionAndResult */ mumuki.bridge = (() => { @@ -51,26 +62,18 @@ mumuki.bridge = (() => { */ _submitSolution(submission) { const lastSubmission = mumuki.SubmissionsStore.getCachedResultFor(mumuki.currentExerciseId, submission); - if(lastSubmission){ + if (lastSubmission) { return $.Deferred().resolve(lastSubmission); } else { return this._sendNewSolution(submission).done((result) => { - mumuki.SubmissionsStore.setLastSubmission(mumuki.currentExerciseId, this._buildLastSubmission(submission, result)); + mumuki.SubmissionsStore.setLastSubmission(mumuki.currentExerciseId, {submission, result}); }); } } - /** - * @param {Submission} submission - * @param {SubmissionResult} result - * @returns {SubmissionAndResult} - */ - _buildLastSubmission(submission, result) { - return { content: {solution: submission.solution}, result: result } - } - /** * @param {Submission} submission the submission object + * @returns {JQuery.Promise} */ _sendNewSolution(submission){ var token = new mumuki.CsrfToken(); diff --git a/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js b/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js index 85d8e20e1..f1ed2d703 100644 --- a/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js +++ b/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js @@ -1,4 +1,4 @@ -(() => { +mumuki.SubmissionsStore = (() => { const SubmissionsStore = new class { /** * @param {number} exerciseId @@ -14,35 +14,62 @@ * @returns {SubmissionAndResult} */ getLastSubmission(exerciseId) { - const submission = window.localStorage.getItem(this._keyFor(exerciseId)); - if (!submission) return null; - return JSON.parse(submission); + const submissionAndResult = window.localStorage.getItem(this._keyFor(exerciseId)); + if (!submissionAndResult) return null; + return JSON.parse(submissionAndResult); } /** * @param {number} exerciseId - * @param {SubmissionAndResult} submission + * @param {SubmissionAndResult} submissionAndResult */ - setLastSubmission(exerciseId, submission) { - window.localStorage.setItem(this._keyFor(exerciseId), this._asString(submission)); + setLastSubmission(exerciseId, submissionAndResult) { + window.localStorage.setItem(this._keyFor(exerciseId), this._asString(submissionAndResult)); } /** + * Retrieves the last cached, non-aborted result for the given submission * * @param {number} exerciseId - * @param {*} newSolution - * @returns {SubmissionResult} + * @param {Submission} submission + * @returns {SubmissionResult} the cached result for this submission */ - getCachedResultFor(exerciseId, newSolution) { + getCachedResultFor(exerciseId, submission) { const lastSubmission = this.getLastSubmission(exerciseId); if (!lastSubmission || lastSubmission.result.status === 'aborted' - || !this._solutionEquals(lastSubmission, newSolution)) { + || !this.submissionSolutionEquals(lastSubmission.submission, submission)) { return null; } return lastSubmission.result; } + /** + * Extract the submission's solution content + * + * @param {Submission} submission + * @returns {string} + */ + submissionSolutionContent(submission) { + if (submission.solution) { + return submission.solution.content; + } else { + return submission['solution[content]']; + } + } + + /** + * Compares two solutions to determine if they are equivalent + * from the point of view of the code evaluation + * + * @param {Submission} one + * @param {Submission} other + * @returns {boolean} + */ + submissionSolutionEquals(one, other) { + return this.submissionSolutionContent(one) === this.submissionSolutionContent(other); + } + // private API _asString(object) { @@ -52,10 +79,7 @@ _keyFor(exerciseId) { return `/exercise/${exerciseId}/submission`; } - - _solutionEquals(submission, solution) { - return this._asString(submission.content) === this._asString(solution); - } }; - mumuki.SubmissionsStore = SubmissionsStore; + + return SubmissionsStore; })();