Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,23 @@ describe 'CombinedOpenEnded', ->
expect(@combined.task_count).toEqual 2
expect(@combined.task_number).toEqual 1

it 'subelements are made collapsible', ->
it 'subelements are made collapsible', ->
expect(Collapsible.setCollapsibles).toHaveBeenCalled()


describe 'poll', ->
nativeSetTimeout = null

beforeEach =>
nativeSetTimeout = window.setTimeout
# setup the spies
@combined = new CombinedOpenEnded @element
spyOn(@combined, 'reload').andCallFake -> return 0
window.setTimeout = jasmine.createSpy().andCallFake (callback, timeout) -> return 5

afterEach =>
window.setTimeout = nativeSetTimeout

it 'polls at the correct intervals', =>
fakeResponseContinue = state: 'not done'
spyOn($, 'postWithPrefix').andCallFake (url, callback) -> callback(fakeResponseContinue)
Expand All @@ -67,39 +73,45 @@ describe 'CombinedOpenEnded', ->
expect(window.queuePollerID).toBe(5)

it 'polling stops properly', =>
fakeResponseDone = state: "done"
fakeResponseDone = state: "done"
spyOn($, 'postWithPrefix').andCallFake (url, callback) -> callback(fakeResponseDone)
@combined.poll()
expect(window.queuePollerID).toBeUndefined()
expect(window.setTimeout).not.toHaveBeenCalled()

describe 'rebind', ->
nativeSetTimeout = null

beforeEach ->
nativeSetTimeout = window.setTimeout
@combined = new CombinedOpenEnded @element
spyOn(@combined, 'queueing').andCallFake -> return 0
spyOn(@combined, 'skip_post_assessment').andCallFake -> return 0
window.setTimeout = jasmine.createSpy().andCallFake (callback, timeout) -> return 5

afterEach =>
window.setTimeout = nativeSetTimeout

it 'when our child is in an assessing state', ->
@combined.child_state = 'assessing'
@combined.rebind()
expect(@combined.answer_area.attr("disabled")).toBe("disabled")
expect(@combined.submit_button.val()).toBe("Submit assessment")
expect(@combined.queueing).toHaveBeenCalled()

it 'when our child state is initial', ->
it 'when our child state is initial', ->
@combined.child_state = 'initial'
@combined.rebind()
expect(@combined.answer_area.attr("disabled")).toBeUndefined()
expect(@combined.submit_button.val()).toBe("Submit")

it 'when our child state is post_assessment', ->
it 'when our child state is post_assessment', ->
@combined.child_state = 'post_assessment'
@combined.rebind()
expect(@combined.answer_area.attr("disabled")).toBe("disabled")
expect(@combined.submit_button.val()).toBe("Submit post-assessment")

it 'when our child state is done', ->
it 'when our child state is done', ->
spyOn(@combined, 'next_problem').andCallFake ->
@combined.child_state = 'done'
@combined.rebind()
Expand All @@ -112,7 +124,7 @@ describe 'CombinedOpenEnded', ->
@combined.child_state = 'done'

it 'handling a successful call', ->
fakeResponse =
fakeResponse =
success: true
html: "dummy html"
allow_reset: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

beforeEach(function() {
oldOTBD = window.onTouchBasedDevice;
spyOn(window, 'setTimeout').andReturn(100);
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
initialize();
});
Expand Down Expand Up @@ -130,8 +131,6 @@

describe('mouse movement', function() {
beforeEach(function() {
window.setTimeout = jasmine.createSpy().andCallFake(function(callback, timeout) { return 5; })
window.setTimeout.andReturn(100);
spyOn(window, 'clearTimeout');
});

Expand Down Expand Up @@ -446,7 +445,7 @@
});

// Temporarily disabled due to intermittent failures
// Fails with error: "InvalidStateError: An attempt was made to
// Fails with error: "InvalidStateError: An attempt was made to
// use an object that is not, or is no longer, usable
// Expected 0 to equal 14.91."
// on Firefox
Expand Down
53 changes: 53 additions & 0 deletions common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,59 @@
});
});

describe('Check heartbeats logging', function() {
beforeEach(function() {
initialize();
spyOn(videoPlayer, 'log').andCallThrough();
state.config.heartbeatsLoggingDelay = 1000;

videoPlayer.onStateChange({
data: YT.PlayerState.PLAYING
});
});

function initHeartbets(expectation, waitsForTime) {
var runsTimer = waitsForTime - 100,
flag;

runs(function() {
flag = false;
var timer = window.setTimeout(function(){
flag = true;
}, runsTimer);
});

waitsFor(function() {
return flag;
}, "The Flag should be True", waitsForTime);

runs(expectation);

}

it('heartbeat log called twice', function() {
initHeartbets(function() {
var heartbeatsLogsArray = videoPlayer.log.calls.slice(-2);

$.each(heartbeatsLogsArray, function(index, log){
expect(log.args[0]).toEqual('is_video_playing');
});
}, 2200);
});

it('heartbeat log did\'t called after pausing', function() {
videoPlayer.onStateChange({
data: YT.PlayerState.PAUSED
});

initHeartbets(function() {
var log = videoPlayer.log.mostRecentCall.args[0];
expect(log).not.toEqual('is_video_playing');
}, 1200);
});

});

describe('when the video is playing', function() {
var oldState;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@

describe('onStop', function() {
beforeEach(function() {
spyOn(window, 'setTimeout');
initialize();
spyOn(videoPlayer, 'onSlideSeek').andCallThrough();
videoProgressSlider.onStop({}, {
Expand Down
25 changes: 13 additions & 12 deletions common/lib/xmodule/xmodule/js/src/video/01_initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,22 +214,23 @@ function (VideoPlayer) {
this.config = {
element: element,

start: this.el.data('start'),
end: this.el.data('end'),
start: this.el.data('start'),
end: this.el.data('end'),

caption_data_dir: this.el.data('caption-data-dir'),
caption_asset_path: this.el.data('caption-asset-path'),
show_captions: (this.el.data('show-captions').toString().toLowerCase() === 'true'),
youtubeStreams: this.el.data('streams'),
caption_data_dir: this.el.data('caption-data-dir'),
caption_asset_path: this.el.data('caption-asset-path'),
show_captions: (this.el.data('show-captions').toString().toLowerCase() === 'true'),
youtubeStreams: this.el.data('streams'),

sub: this.el.data('sub'),
mp4Source: this.el.data('mp4-source'),
webmSource: this.el.data('webm-source'),
oggSource: this.el.data('ogg-source'),
sub: this.el.data('sub'),
mp4Source: this.el.data('mp4-source'),
webmSource: this.el.data('webm-source'),
oggSource: this.el.data('ogg-source'),

fadeOutTimeout: 1400,
fadeOutTimeout: 1400,

availableQualities: ['hd720', 'hd1080', 'highres']
availableQualities: ['hd720', 'hd1080', 'highres'],
heartbeatsLoggingDelay: 30*1000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why you shouldn't try to line up the values in an object: this should have been a one-line change.

};

if (!(_parseYouTubeIDs(this))) {
Expand Down
22 changes: 22 additions & 0 deletions common/lib/xmodule/xmodule/js/src/video/03_video_player.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,25 @@ function (HTML5Video) {
});
}

// Start heartbeats logging.
// Send logs to the server each state.config.heartbeatsLoggingDelay seconds
// and provide an indication of whether students still have a video playing
// on their screen so that we have a better idea of whether they are getting
// the educational benefit of watching the lecture.
function _startHeartbeatLogging(state) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment about what these function do and why they are necessary. To a person who never has anything to do with heartbeats this will be helpful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comments.

if (!state.videoPlayer.heartbeatsInterval) {
state.videoPlayer.heartbeatsInterval = window.setInterval(
_.bind(state.videoPlayer.log, state, 'is_video_playing'),
state.config.heartbeatsLoggingDelay
);
}
}

// Stop heartbeats logging.
function _stopHeartbeatLogging(state) {
window.clearInterval(state.videoPlayer.heartbeatsInterval);
}

// ***************************************************************
// Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object.
Expand Down Expand Up @@ -267,6 +286,7 @@ function (HTML5Video) {
}
);

_stopHeartbeatLogging(this);
clearInterval(this.videoPlayer.updateInterval);
delete this.videoPlayer.updateInterval;

Expand All @@ -285,6 +305,8 @@ function (HTML5Video) {
}
);

_startHeartbeatLogging(this);

if (!this.videoPlayer.updateInterval) {
this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200);
}
Expand Down