From 450dfe17b04b59d950fd772da12ef5ac9e0136db Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 9 Apr 2014 05:19:13 +0000 Subject: [PATCH] TESTS: Add tests for fix of end-time in the video player. BLD-662 --- .../xmodule/js/spec/video/html5_video_spec.js | 2 +- .../js/spec/video/video_player_spec.js | 38 ++-- .../xmodule/js/src/video/01_initialize.js | 5 + .../xmodule/js/src/video/02_html5_video.js | 3 +- .../xmodule/js/src/video/03_video_player.js | 140 +++++++++++---- .../js/src/video/06_video_progress_slider.js | 15 +- lms/djangoapps/courseware/features/common.py | 2 +- .../courseware/features/video.feature | 164 +++++++++++++----- lms/djangoapps/courseware/features/video.py | 37 ++-- 9 files changed, 302 insertions(+), 104 deletions(-) diff --git a/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js b/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js index 75f8a6e9d3..baefb4dea1 100644 --- a/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js +++ b/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js @@ -117,7 +117,7 @@ runs(function () { expect(state.videoPlayer.player.getPlayerState()) - .toBe(STATUS.PLAYING); + .toBe(STATUS.BUFFERING); }); }); diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js index c0d48bd95d..f404a47d0a 100644 --- a/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js +++ b/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js @@ -366,19 +366,22 @@ function (VideoPlayer) { }); it('Slider event causes log update', function () { - runs(function () { - var currentTime = state.videoPlayer.currentTime; - spyOn(state.videoPlayer, 'log'); state.videoProgressSlider.onSlide( jQuery.Event('slide'), { value: 2 } ); + }); + waitsFor(function () { + return state.videoPlayer.currentTime >= 2; + }, 'currentTime is less than 2 seconds', WAIT_TIMEOUT); + + runs(function () { expect(state.videoPlayer.log).toHaveBeenCalledWith( 'seek_video', { - old_time: currentTime, + old_time: jasmine.any(Number), new_time: 2, type: 'onSlideSeek' } @@ -388,25 +391,37 @@ function (VideoPlayer) { it('seek the player', function () { runs(function () { - spyOn(state.videoPlayer.player, 'seekTo'); + spyOn(state.videoPlayer.player, 'seekTo').andCallThrough(); state.videoProgressSlider.onSlide( - jQuery.Event('slide'), { value: 60 } + jQuery.Event('slide'), { value: 30 } ); + }); + waitsFor(function () { + return state.videoPlayer.currentTime >= 30; + }, 'currentTime is less than 30 seconds', WAIT_TIMEOUT); + + runs(function () { expect(state.videoPlayer.player.seekTo) - .toHaveBeenCalledWith(60, true); + .toHaveBeenCalledWith(30, true); }); }); it('call updatePlayTime on player', function () { runs(function () { - spyOn(state.videoPlayer, 'updatePlayTime'); + spyOn(state.videoPlayer, 'updatePlayTime').andCallThrough(); state.videoProgressSlider.onSlide( - jQuery.Event('slide'), { value: 60 } + jQuery.Event('slide'), { value: 30 } ); + }); + waitsFor(function () { + return state.videoPlayer.currentTime >= 30; + }, 'currentTime is less than 30 seconds', WAIT_TIMEOUT); + + runs(function () { expect(state.videoPlayer.updatePlayTime) - .toHaveBeenCalledWith(60); + .toHaveBeenCalledWith(jasmine.any(Number)); }); }); @@ -1070,6 +1085,7 @@ function (VideoPlayer) { youtubeId: jasmine.createSpy().andReturn('videoId'), isFlashMode: jasmine.createSpy().andReturn(false), isHtml5Mode: jasmine.createSpy().andReturn(true), + isYoutubeType: jasmine.createSpy().andReturn(true), setPlayerMode: jasmine.createSpy(), videoPlayer: { currentTime: 60, @@ -1104,12 +1120,12 @@ function (VideoPlayer) { }); it('in HTML5 mode', function () { + state.isYoutubeType.andReturn(false); VideoPlayer.prototype.setPlaybackRate.call(state, '0.75'); expect(state.videoPlayer.player.setPlaybackRate).toHaveBeenCalledWith('0.75'); }); it('Youtube video in FF, with new speed equal 1.0', function () { - state.videoType = 'youtube'; state.browserIsFirefox = true; state.videoPlayer.isPlaying.andReturn(false); diff --git a/common/lib/xmodule/xmodule/js/src/video/01_initialize.js b/common/lib/xmodule/xmodule/js/src/video/01_initialize.js index f1a63b9c35..a865c8655f 100644 --- a/common/lib/xmodule/xmodule/js/src/video/01_initialize.js +++ b/common/lib/xmodule/xmodule/js/src/video/01_initialize.js @@ -68,6 +68,7 @@ function (VideoPlayer, VideoStorage) { initialize: initialize, isHtml5Mode: isHtml5Mode, isFlashMode: isFlashMode, + isYoutubeType: isYoutubeType, parseSpeed: parseSpeed, parseVideoSources: parseVideoSources, parseYoutubeStreams: parseYoutubeStreams, @@ -847,6 +848,10 @@ function (VideoPlayer, VideoStorage) { return this.getPlayerMode() === 'html5'; } + function isYoutubeType() { + return this.videoType === 'youtube'; + } + function speedToString(speed) { return parseFloat(speed).toFixed(2).replace(/\.00$/, '.0'); } diff --git a/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js b/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js index bcd1529207..324ca8c965 100644 --- a/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js +++ b/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js @@ -275,12 +275,13 @@ function () { // Register the 'play' event. this.video.addEventListener('play', function () { - _this.playerState = HTML5Video.PlayerState.PLAYING; + _this.playerState = HTML5Video.PlayerState.BUFFERING; _this.callStateChangeCallback(); }, false); this.video.addEventListener('playing', function () { _this.playerState = HTML5Video.PlayerState.PLAYING; + _this.callStateChangeCallback(); }, false); // Register the 'pause' event. diff --git a/common/lib/xmodule/xmodule/js/src/video/03_video_player.js b/common/lib/xmodule/xmodule/js/src/video/03_video_player.js index 42086fb4c0..cda33180c3 100644 --- a/common/lib/xmodule/xmodule/js/src/video/03_video_player.js +++ b/common/lib/xmodule/xmodule/js/src/video/03_video_player.js @@ -17,13 +17,24 @@ function (HTML5Video, Resizer) { methodsDict = { duration: duration, handlePlaybackQualityChange: handlePlaybackQualityChange, + + // Added for finer graded seeking control. + // Please see: + // https://developers.google.com/youtube/js_api_reference#Events + isBuffering: isBuffering, + // https://developers.google.com/youtube/js_api_reference#cueVideoById + isCued: isCued, + isEnded: isEnded, isPlaying: isPlaying, + isUnstarted: isUnstarted, log: log, onCaptionSeek: onSeek, onEnded: onEnded, onPause: onPause, onPlay: onPlay, + runTimer: runTimer, + stopTimer: stopTimer, onPlaybackQualityChange: onPlaybackQualityChange, onReady: onReady, onSlideSeek: onSeek, @@ -54,7 +65,17 @@ function (HTML5Video, Resizer) { // Functions which will be accessible via 'state' object. When called, // these functions will get the 'state' object as a context. function _makeFunctionsPublic(state) { + var debouncedF = _.debounce( + function (params) { + return onSeek.call(this, params); + }.bind(state), + 300 + ); + state.bindTo(methodsDict, state.videoPlayer, state); + + state.videoPlayer.onSlideSeek = debouncedF; + state.videoPlayer.onCaptionSeek = debouncedF; } // function _initialize(state) @@ -79,10 +100,10 @@ function (HTML5Video, Resizer) { state.videoPlayer.player.setVolume(state.currentVolume); }); - if (state.videoType === 'youtube') { + if (state.isYoutubeType()) { state.videoPlayer.PlayerState = YT.PlayerState; state.videoPlayer.PlayerState.UNSTARTED = -1; - } else { // if (state.videoType === 'html5') { + } else { state.videoPlayer.PlayerState = HTML5Video.PlayerState; } @@ -139,7 +160,7 @@ function (HTML5Video, Resizer) { _updateVcrAndRegion(state); }, false); - } else { // if (state.videoType === 'youtube') { + } else { youTubeId = state.youtubeId(); state.videoPlayer.player = new YT.Player(state.id, { @@ -305,8 +326,8 @@ function (HTML5Video, Resizer) { // This function gets the video's current play position in time // (currentTime) and its duration. // It is called at a regular interval when the video is playing. - function update() { - this.videoPlayer.currentTime = this.videoPlayer.player.getCurrentTime(); + function update(time) { + this.videoPlayer.currentTime = time || this.videoPlayer.player.getCurrentTime(); if (isFinite(this.videoPlayer.currentTime)) { this.videoPlayer.updatePlayTime(this.videoPlayer.currentTime); @@ -339,7 +360,7 @@ function (HTML5Video, Resizer) { !( this.browserIsFirefox && newSpeed === '1.0' && - this.videoType === 'youtube' + this.isYoutubeType() ) ) { this.videoPlayer.player.setPlaybackRate(newSpeed); @@ -404,6 +425,7 @@ function (HTML5Video, Resizer) { return; } + this.el.off('play.seek'); this.videoPlayer.log( 'seek_video', { @@ -420,24 +442,49 @@ function (HTML5Video, Resizer) { this.videoPlayer.stopAtEndTime = false; } - this.videoPlayer.player.seekTo(newTime, true); - if (this.videoPlayer.isPlaying()) { - clearInterval(this.videoPlayer.updateInterval); - this.videoPlayer.updateInterval = setInterval( - this.videoPlayer.update, 200 - ); - - setTimeout(this.videoPlayer.update, 0); + this.videoPlayer.stopTimer(); } else { this.videoPlayer.currentTime = newTime; } + var isUnplayed = this.videoPlayer.isUnstarted() || + this.videoPlayer.isCued(); - this.videoPlayer.updatePlayTime(newTime); + // Use `cueVideoById` method for youtube video that is not played before. + if (isUnplayed && this.isYoutubeType()) { + this.videoPlayer.player.cueVideoById(this.youtubeId(), newTime); + } else { + // Youtube video cannot be rewinded during bufferization, so wait to + // finish bufferization and then rewind the video. + if (this.isYoutubeType() && this.videoPlayer.isBuffering()) { + this.el.on('play.seek', function () { + this.videoPlayer.player.seekTo(newTime, true); + }.bind(this)); + } else { + // Otherwise, just seek the video + this.videoPlayer.player.seekTo(newTime, true); + } + } + this.videoPlayer.updatePlayTime(newTime, true); this.el.trigger('seek', arguments); } + function runTimer() { + if (!this.videoPlayer.updateInterval) { + this.videoPlayer.updateInterval = window.setInterval( + this.videoPlayer.update, 200 + ); + + this.videoPlayer.update(); + } + } + + function stopTimer() { + window.clearInterval(this.videoPlayer.updateInterval); + delete this.videoPlayer.updateInterval; + } + function onEnded() { var time = this.videoPlayer.duration(); @@ -466,8 +513,7 @@ function (HTML5Video, Resizer) { } ); - clearInterval(this.videoPlayer.updateInterval); - delete this.videoPlayer.updateInterval; + this.videoPlayer.stopTimer(); this.trigger('videoControl.pause', null); this.saveState(true); @@ -482,14 +528,7 @@ function (HTML5Video, Resizer) { } ); - if (!this.videoPlayer.updateInterval) { - this.videoPlayer.updateInterval = setInterval( - this.videoPlayer.update, 200 - ); - - this.videoPlayer.update(); - } - + this.videoPlayer.runTimer(); this.trigger('videoControl.play', null); this.trigger('videoProgressSlider.notifyThroughHandleEnd', { end: false @@ -558,7 +597,7 @@ function (HTML5Video, Resizer) { // https://github.com/edx/edx-platform/pull/2841 if ( (this.isHtml5Mode() || availablePlaybackRates.length > 1) && - this.videoType === 'youtube' + this.isYoutubeType() ) { if (availablePlaybackRates.length === 1 && !this.isTouch) { // This condition is needed in cases when Firefox version is @@ -620,20 +659,34 @@ function (HTML5Video, Resizer) { } function onStateChange(event) { + this.el.removeClass([ + 'is-unstarted', 'is-playing', 'is-paused', 'is-buffered', + 'is-ended', 'is-cued' + ].join(' ')); + switch (event.data) { case this.videoPlayer.PlayerState.UNSTARTED: + this.el.addClass('is-unstarted'); this.videoPlayer.onUnstarted(); break; case this.videoPlayer.PlayerState.PLAYING: + this.el.addClass('is-playing'); this.videoPlayer.onPlay(); break; case this.videoPlayer.PlayerState.PAUSED: + this.el.addClass('is-paused'); this.videoPlayer.onPause(); break; + case this.videoPlayer.PlayerState.BUFFERING: + this.el.addClass('is-buffered'); + this.el.trigger('buffering'); + break; case this.videoPlayer.PlayerState.ENDED: + this.el.addClass('is-ended'); this.videoPlayer.onEnded(); break; case this.videoPlayer.PlayerState.CUED: + this.el.addClass('is-cued'); this.videoPlayer.player.seekTo(this.videoPlayer.seekToTimeOnCued, true); // We need to call play() explicitly because after the call // to functions cueVideoById() followed by seekTo() the video @@ -711,12 +764,12 @@ function (HTML5Video, Resizer) { return time; } - function updatePlayTime(time) { + function updatePlayTime(time, skip_seek) { var videoPlayer = this.videoPlayer, duration = this.videoPlayer.duration(), youTubeId; - if (duration > 0 && videoPlayer.goToStartTime) { + if (duration > 0 && videoPlayer.goToStartTime && !skip_seek) { videoPlayer.goToStartTime = false; // The duration might have changed. Update the start-end time region to @@ -746,7 +799,7 @@ function (HTML5Video, Resizer) { // // HTML5 video sources play fine from start-time in both Chrome // and Firefox. - if (this.browserIsFirefox && this.videoType === 'youtube') { + if (this.browserIsFirefox && this.isYoutubeType()) { youTubeId = this.youtubeId(); // When we will call cueVideoById() for some strange reason @@ -794,10 +847,27 @@ function (HTML5Video, Resizer) { } function isPlaying() { - var playerState = this.videoPlayer.player.getPlayerState(), - PLAYING = this.videoPlayer.PlayerState.PLAYING; + var playerState = this.videoPlayer.player.getPlayerState(); - return playerState === PLAYING; + return playerState === this.videoPlayer.PlayerState.PLAYING; + } + + function isBuffering() { + var playerState = this.videoPlayer.player.getPlayerState(); + + return playerState === this.videoPlayer.PlayerState.BUFFERING; + } + + function isCued() { + var playerState = this.videoPlayer.player.getPlayerState(); + + return playerState === this.videoPlayer.PlayerState.CUED; + } + + function isUnstarted() { + var playerState = this.videoPlayer.player.getPlayerState(); + + return playerState === this.videoPlayer.PlayerState.UNSTARTED; } /* @@ -844,7 +914,7 @@ function (HTML5Video, Resizer) { // might differ by one or two seconds against the actual time as will // be reported later on by the player.getDuration() API function. if (!isFinite(dur) || dur <= 0) { - if (this.videoType === 'youtube') { + if (this.isYoutubeType()) { dur = this.getDuration(); } } @@ -873,9 +943,9 @@ function (HTML5Video, Resizer) { }); } - if (this.videoType === 'youtube') { + if (this.isYoutubeType()) { logInfo.code = this.youtubeId(); - } else if (this.videoType === 'html5') { + } else { logInfo.code = 'html5'; } diff --git a/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js b/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js index fadc23cb91..aa506dc25c 100644 --- a/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js +++ b/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js @@ -177,15 +177,26 @@ function () { } function onSlide(event, ui) { + var time = ui.value, + duration = this.videoPlayer.duration(); + this.videoProgressSlider.frozen = true; // Remember the seek to value so that we don't repeat ourselves on the // 'stop' slider event. - this.videoProgressSlider.lastSeekValue = ui.value; + this.videoProgressSlider.lastSeekValue = time; + + this.trigger( + 'videoControl.updateVcrVidTime', + { + time: time, + duration: duration + } + ); this.trigger( 'videoPlayer.onSlideSeek', - {'type': 'onSlideSeek', 'time': ui.value} + {'type': 'onSlideSeek', 'time': time} ); // ARIA diff --git a/lms/djangoapps/courseware/features/common.py b/lms/djangoapps/courseware/features/common.py index 99501bfd5a..1cdec7629b 100644 --- a/lms/djangoapps/courseware/features/common.py +++ b/lms/djangoapps/courseware/features/common.py @@ -35,7 +35,7 @@ def configure_screenshots_for_all_steps(_step, action): else: raise ValueError('Parameter `action` should be one of "enable" or "disable".') - +@world.absorb def capture_screenshot_before_after(func): """ A decorator that will take a screenshot before and after the applied diff --git a/lms/djangoapps/courseware/features/video.feature b/lms/djangoapps/courseware/features/video.feature index fe37999568..2f1e9e1c69 100644 --- a/lms/djangoapps/courseware/features/video.feature +++ b/lms/djangoapps/courseware/features/video.feature @@ -2,18 +2,6 @@ Feature: LMS.Video component As a student, I want to view course videos in LMS - # 1 Disabled 4/8/14 after intermittent failures in master - #Scenario: Video component stores position correctly when page is reloaded - # Given the course has a Video component in "Youtube" mode - # When the video has rendered in "Youtube" mode - # And I click video button "play" - # And I click video button "pause" - # Then I seek video to "10" seconds - # And I click video button "play" - # And I click video button "pause" - # And I reload the page with video - # Then I see video slider at "10" seconds - # 1 Scenario: Video component is fully rendered in the LMS in HTML5 mode Given the course has a Video component in "HTML5" mode @@ -267,36 +255,87 @@ Feature: LMS.Video component Then the video has rendered in "HTML5" mode And the video does not show the captions - # 20 Disabled 4/8/14 after intermittent failures in master - #Scenario: Transcripts are available on different speeds of Flash mode - # Given I am registered for the course "test_course" - # And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets - # And it has a video in "Flash" mode - # Then the video has rendered in "Flash" mode - # And I make sure captions are opened - # And I see "Hi, welcome to Edx." text in the captions - # Then I select the "1.50" speed - # And I see "Hi, welcome to Edx." text in the captions - # Then I select the "0.75" speed - # And I see "Hi, welcome to Edx." text in the captions - # Then I select the "1.25" speed - # And I see "Hi, welcome to Edx." text in the captions + # 20 + Scenario: Start time works for Youtube video + Given I am registered for the course "test_course" + And it has a video in "Youtube" mode: + | start_time | + | 00:00:10 | + And I click video button "play" + Then I see video slider at "0:10" position - # 21 Disabled 4/8/14 after intermittent failures in master - #Scenario: Elapsed time calculates correctly on different speeds of Flash mode - # Given I am registered for the course "test_course" - # And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets - # And it has a video in "Flash" mode - # And I make sure captions are opened - # Then I select the "1.50" speed - # And I click video button "pause" - # And I click on caption line "4", video module shows elapsed time "7" - # Then I select the "0.75" speed - # And I click video button "pause" - # And I click on caption line "3", video module shows elapsed time "9" - # Then I select the "1.25" speed - # And I click video button "pause" - # And I click on caption line "2", video module shows elapsed time "4" + # 21 + Scenario: End time works for Youtube video + Given I am registered for the course "test_course" + And it has a video in "Youtube" mode: + | end_time | + | 00:00:02 | + And I click video button "play" + And I wait "5" seconds + Then I see video slider at "0:02" position + + # 22 + Scenario: Youtube video with end-time at 1:00 and the video starts playing at 0:58 + Given I am registered for the course "test_course" + And it has a video in "Youtube" mode: + | end_time | + | 00:01:00 | + And I wait for video controls appear + And I seek video to "0:58" position + And I click video button "play" + And I wait "5" seconds + Then I see video slider at "1:00" position + + # 23 + Scenario: Start time and end time work together for Youtube video + Given I am registered for the course "test_course" + And it has a video in "Youtube" mode: + | start_time | end_time | + | 00:00:10 | 00:00:12 | + And I click video button "play" + Then I see video slider at "0:10" position + And I wait "5" seconds + Then I see video slider at "0:12" position + + # 24 + Scenario: Youtube video after pausing at end time video plays to the end from end time + Given I am registered for the course "test_course" + And it has a video in "Youtube" mode: + | start_time | end_time | + | 00:01:51 | 00:01:52 | + And I click video button "play" + And I wait "5" seconds + # The end time is 00:01:52. + Then I see video slider at "1:52" position + And I click video button "play" + And I wait "8" seconds + # The default video length is 00:01:55. + Then I see video slider at "1:55" position + + # 25 + Scenario: Youtube video with end-time at 0:32 and start-time at 0:30, the video starts playing from 0:28 + Given I am registered for the course "test_course" + And it has a video in "Youtube" mode: + | start_time | end_time | + | 00:00:30 | 00:00:32 | + And I wait for video controls appear + And I seek video to "0:28" position + And I click video button "play" + And I wait "8" seconds + Then I see video slider at "0:32" position + + # 26 + Scenario: Youtube video with end-time at 1:00, the video starts playing from 1:52 + Given I am registered for the course "test_course" + And it has a video in "Youtube" mode: + | end_time | + | 00:01:00 | + And I wait for video controls appear + And I seek video to "1:52" position + And I click video button "play" + And I wait "5" seconds + # Video stops at the end. + Then I see video slider at "1:55" position # 27 @skip_firefox @@ -314,3 +353,46 @@ Feature: LMS.Video component And I see video button "quality" is inactive And I click video button "quality" Then I see video button "quality" is active + + # 29 Disabled 4/8/14 after intermittent failures in master + #Scenario: Transcripts are available on different speeds of Flash mode + # Given I am registered for the course "test_course" + # And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets + # And it has a video in "Flash" mode + # Then the video has rendered in "Flash" mode + # And I make sure captions are opened + # And I see "Hi, welcome to Edx." text in the captions + # Then I select the "1.50" speed + # And I see "Hi, welcome to Edx." text in the captions + # Then I select the "0.75" speed + # And I see "Hi, welcome to Edx." text in the captions + # Then I select the "1.25" speed + # And I see "Hi, welcome to Edx." text in the captions + + # 30 Disabled 4/8/14 after intermittent failures in master + #Scenario: Elapsed time calculates correctly on different speeds of Flash mode + # Given I am registered for the course "test_course" + # And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets + # And it has a video in "Flash" mode + # And I make sure captions are opened + # Then I select the "1.50" speed + # And I click video button "pause" + # And I click on caption line "4", video module shows elapsed time "7" + # Then I select the "0.75" speed + # And I click video button "pause" + # And I click on caption line "3", video module shows elapsed time "9" + # Then I select the "1.25" speed + # And I click video button "pause" + # And I click on caption line "2", video module shows elapsed time "4" + + # 31 Disabled 4/8/14 after intermittent failures in master + #Scenario: Video component stores position correctly when page is reloaded + # Given the course has a Video component in "Youtube" mode + # When the video has rendered in "Youtube" mode + # And I click video button "play" + # And I click video button "pause" + # Then I seek video to "0:10" position + # And I click video button "play" + # And I click video button "pause" + # And I reload the page with video + # Then I see video slider at "0:10" position diff --git a/lms/djangoapps/courseware/features/video.py b/lms/djangoapps/courseware/features/video.py index fb81325099..6bdcadf8d8 100644 --- a/lms/djangoapps/courseware/features/video.py +++ b/lms/djangoapps/courseware/features/video.py @@ -482,6 +482,7 @@ def check_captions(_step): @step('I select language with code "([^"]*)"$') def select_language(_step, code): + world.wait_for_visible('.video-controls') # Make sure that all ajax requests that affects the language menu are finished. # For example, request to get new translation etc. world.wait_for_ajax_complete() @@ -506,16 +507,19 @@ def select_language(_step, code): @step('I click video button "([^"]*)"$') def click_button(_step, button): world.css_click(VIDEO_BUTTONS[button]) + if button == "play": + # Needs to wait for video buffrization + world.wait_for( + func=lambda _: world.css_has_class('.video', 'is-playing') and world.is_css_present(VIDEO_BUTTONS['pause']), + timeout=30 + ) + world.wait_for_ajax_complete() -@step('I see video slider at "([^"]*)" seconds$') -def start_playing_video_from_n_seconds(_step, position): - world.wait_for( - func=lambda _: elapsed_time() > 0, - timeout=30 - ) - +@step('I see video slider at "([^"]*)" position$') +def start_playing_video_from_n_seconds(_step, time_str): + position = parse_time_str(time_str) actual_position = elapsed_time() assert_equal(actual_position, int(position), "Current position is {}, but should be {}".format(actual_position, position)) @@ -530,12 +534,21 @@ def i_see_duration(_step, position): assert duration() == parse_time_str(position) -@step('I seek video to "([^"]*)" seconds$') -def seek_video_to_n_seconds(_step, seconds): - time = float(seconds.strip()) - jsCode = "$('.video').data('video-player-state').videoPlayer.onSlideSeek({{time: {0:f}}})".format(time) +@step('I wait for video controls appear$') +def controls_appear(_step): + world.wait_for_visible('.video-controls') + + +@step('I seek video to "([^"]*)" position$') +def seek_video_to_n_seconds(_step, time_str): + time = parse_time_str(time_str) + jsCode = "$('.video').data('video-player-state').videoPlayer.onSlideSeek({{time: {0}}})".format(time) world.browser.execute_script(jsCode) - _step.given('I see video slider at "{}" seconds'.format(seconds)) + world.wait_for( + func=lambda _: world.retry_on_exception(lambda: elapsed_time() == time and not world.css_has_class('.video', 'is-buffering')), + timeout=30 + ) + _step.given('I see video slider at "{0}" position'.format(time_str)) @step('I have a "([^"]*)" transcript file in assets$')