Commit 4dae2267 by Afzal Wali

The remaining time is now independent of the browser's system clock.

Fixed the failing jasmine tests

JS test to verify that the client remaining time is updated after the server responds with an updated remaining time.
parent 617db2b2
...@@ -16,32 +16,24 @@ ...@@ -16,32 +16,24 @@
course_id: null, course_id: null,
lastFetched: new Date() lastFetched: new Date()
}, },
getRemainingSeconds: function () { getFormattedRemainingTime: function (secondsLeft) {
var currentTime = (new Date()).getTime();
var lastFetched = this.get('lastFetched').getTime();
var totalSeconds = this.get('time_remaining_seconds') - (currentTime - lastFetched) / 1000;
return totalSeconds;
},
getFormattedRemainingTime: function () {
var totalSeconds = this.getRemainingSeconds();
/* since we can have a small grace period, we can end in the negative numbers */ /* since we can have a small grace period, we can end in the negative numbers */
if (totalSeconds < 0) if (secondsLeft < 0)
totalSeconds = 0; secondsLeft = 0;
var hours = parseInt(totalSeconds / 3600) % 24; var hours = parseInt(secondsLeft / 3600) % 24;
var minutes = parseInt(totalSeconds / 60) % 60; var minutes = parseInt(secondsLeft / 60) % 60;
var seconds = Math.floor(totalSeconds % 60); var seconds = Math.floor(secondsLeft % 60);
return hours + ":" + (minutes < 10 ? "0" + minutes : minutes) return hours + ":" + (minutes < 10 ? "0" + minutes : minutes)
+ ":" + (seconds < 10 ? "0" + seconds : seconds); + ":" + (seconds < 10 ? "0" + seconds : seconds);
}, },
getRemainingTimeState: function () { getRemainingTimeState: function (secondsLeft) {
var totalSeconds = this.getRemainingSeconds(); if (secondsLeft > this.get('low_threshold_sec')) {
if (totalSeconds > this.get('low_threshold_sec')) {
return ""; return "";
} }
else if (totalSeconds <= this.get('low_threshold_sec') && totalSeconds > this.get('critically_low_threshold_sec')) { else if (secondsLeft <= this.get('low_threshold_sec') && secondsLeft > this.get('critically_low_threshold_sec')) {
// returns the class name that has some css properties // returns the class name that has some css properties
// and it displays the user with the waring message if // and it displays the user with the waring message if
// total seconds is less than the low_threshold value. // total seconds is less than the low_threshold value.
......
...@@ -14,6 +14,7 @@ var edx = edx || {}; ...@@ -14,6 +14,7 @@ var edx = edx || {};
this.template = null; this.template = null;
this.timerId = null; this.timerId = null;
this.timerTick = 0; this.timerTick = 0;
this.secondsLeft = 0;
/* give an extra 5 seconds where the timer holds at 00:00 before page refreshes */ /* give an extra 5 seconds where the timer holds at 00:00 before page refreshes */
this.grace_period_secs = 5; this.grace_period_secs = 5;
...@@ -48,6 +49,7 @@ var edx = edx || {}; ...@@ -48,6 +49,7 @@ var edx = edx || {};
// should not be navigating around the courseware // should not be navigating around the courseware
var taking_as_proctored = this.model.get('taking_as_proctored'); var taking_as_proctored = this.model.get('taking_as_proctored');
var time_left = this.model.get('time_remaining_seconds') > 0; var time_left = this.model.get('time_remaining_seconds') > 0;
this.secondsLeft = this.model.get('time_remaining_seconds');
var status = this.model.get('attempt_status'); var status = this.model.get('attempt_status');
var in_courseware = document.location.href.indexOf('/courses/' + this.model.get('course_id') + '/courseware/') > -1; var in_courseware = document.location.href.indexOf('/courses/' + this.model.get('course_id') + '/courseware/') > -1;
...@@ -104,6 +106,7 @@ var edx = edx || {}; ...@@ -104,6 +106,7 @@ var edx = edx || {};
}, },
updateRemainingTime: function (self) { updateRemainingTime: function (self) {
self.timerTick ++; self.timerTick ++;
self.secondsLeft --;
if (self.timerTick % 5 === 0){ if (self.timerTick % 5 === 0){
var url = self.model.url + '/' + self.model.get('attempt_id'); var url = self.model.url + '/' + self.model.get('attempt_id');
$.ajax(url).success(function(data) { $.ajax(url).success(function(data) {
...@@ -115,12 +118,15 @@ var edx = edx || {}; ...@@ -115,12 +118,15 @@ var edx = edx || {};
// refresh the page when the timer expired // refresh the page when the timer expired
location.reload(); location.reload();
} }
else {
self.secondsLeft = data.time_remaining_seconds;
}
}); });
} }
self.$el.find('div.exam-timer').removeClass("low-time warning critical"); self.$el.find('div.exam-timer').removeClass("low-time warning critical");
self.$el.find('div.exam-timer').addClass(self.model.getRemainingTimeState()); self.$el.find('div.exam-timer').addClass(self.model.getRemainingTimeState(self.secondsLeft));
self.$el.find('span#time_remaining_id b').html(self.model.getFormattedRemainingTime()); self.$el.find('span#time_remaining_id b').html(self.model.getFormattedRemainingTime(self.secondsLeft));
if (self.model.getRemainingSeconds() <= -self.grace_period_secs) { if (self.secondsLeft <= -self.grace_period_secs) {
clearInterval(self.timerId); // stop the timer once the time finishes. clearInterval(self.timerId); // stop the timer once the time finishes.
$(window).unbind('beforeunload', this.unloadMessage); $(window).unbind('beforeunload', this.unloadMessage);
// refresh the page when the timer expired // refresh the page when the timer expired
......
...@@ -46,31 +46,36 @@ describe('ProctoredExamView', function () { ...@@ -46,31 +46,36 @@ describe('ProctoredExamView', function () {
expect(this.proctored_exam_view.$el.find('a')).toContainHtml(this.model.get('exam_display_name')); expect(this.proctored_exam_view.$el.find('a')).toContainHtml(this.model.get('exam_display_name'));
}); });
it('changes behavior when clock time decreases low threshold', function () { it('changes behavior when clock time decreases low threshold', function () {
spyOn(this.model, 'getRemainingSeconds').and.callFake(function() { this.proctored_exam_view.secondsLeft = 25;
return 25;
});
expect(this.model.getRemainingSeconds()).toEqual(25);
expect(this.proctored_exam_view.$el.find('div.exam-timer')).not.toHaveClass('low-time warning');
this.proctored_exam_view.render(); this.proctored_exam_view.render();
expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time warning'); expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time warning');
}); });
it('changes behavior when clock time decreases critically low threshold', function () { it('changes behavior when clock time decreases critically low threshold', function () {
spyOn(this.model, 'getRemainingSeconds').and.callFake(function () { this.proctored_exam_view.secondsLeft = 5;
return 5;
});
expect(this.model.getRemainingSeconds()).toEqual(5);
expect(this.proctored_exam_view.$el.find('div.exam-timer')).not.toHaveClass('low-time critical');
this.proctored_exam_view.render(); this.proctored_exam_view.render();
expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time critical'); expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time critical');
}); });
it("reload the page when the exam time finishes", function(){ it("reload the page when the exam time finishes", function(){
spyOn(this.model, 'getRemainingSeconds').and.callFake(function() { this.proctored_exam_view.secondsLeft = -10;
return -10;
});
expect(this.model.getRemainingSeconds()).toEqual(-10);
var reloadPage = spyOn(this.proctored_exam_view, 'reloadPage'); var reloadPage = spyOn(this.proctored_exam_view, 'reloadPage');
this.proctored_exam_view.render(); this.proctored_exam_view.updateRemainingTime(this.proctored_exam_view);
expect(reloadPage).toHaveBeenCalled();
});
it("resets the remainig exam time after the ajax response", function(){
this.server.respondWith("GET", "/api/edx_proctoring/v1/proctored_exam/attempt/" + this.proctored_exam_view.model.get('attempt_id'),
[
200,
{"Content-Type": "application/json"},
JSON.stringify({
time_remaining_seconds: -10
})
]
);
this.proctored_exam_view.timerTick = 4; // to make the ajax call.
var reloadPage = spyOn(this.proctored_exam_view, 'reloadPage');
this.proctored_exam_view.updateRemainingTime(this.proctored_exam_view);
this.server.respond();
this.proctored_exam_view.updateRemainingTime(this.proctored_exam_view);
expect(reloadPage).toHaveBeenCalled(); expect(reloadPage).toHaveBeenCalled();
}); });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment