diff --git a/common/lib/xmodule/xmodule/css/video/display.scss b/common/lib/xmodule/xmodule/css/video/display.scss
index a826ea365a..79bdf9f09f 100644
--- a/common/lib/xmodule/xmodule/css/video/display.scss
+++ b/common/lib/xmodule/xmodule/css/video/display.scss
@@ -21,7 +21,7 @@
.video {
@include clearfix();
- background: rgb(240, 243, 245); // UXPL grayscale-cool xx-light;
+ background: rgb(245, 245, 245); // UXPL grayscale x-back
display: block;
margin: 0 -12px;
padding: 12px;
@@ -74,71 +74,69 @@
width: 0px;
height: 0px;
}
-
- .wrapper-downloads {
- margin: 0;
- padding: 0;
-
- .video-download-button {
- display: inline-block;
- vertical-align: top;
- margin: ($baseline*0.75) ($baseline/2) 0 0;
-
- > a {
- @include transition(all $tmg-f2 ease-in-out 0s);
- @include font-size(14);
- line-height : 14px;
- float: left;
- border-radius: 3px;
- background-color: $very-light-text;
- padding: ($baseline*0.75);
- color: $lighter-base-font-color;
-
- &:hover,
- &:focus {
- background-color: $action-primary-active-bg;
- color: $very-light-text;
+
+ .downloads-heading {
+ margin: 1em 0 0 0;
+ }
+
+ .wrapper-downloads {
+ display: flex;
+
+ .hd {
+ margin: 0;
+ }
+
+ .wrapper-download-video,
+ .wrapper-download-transcripts,
+ .wrapper-handouts,
+ .branding {
+ flex: 1;
+ margin-top: $baseline;
+ @include padding-right($baseline);
+ vertical-align: top;
+ }
+
+ .wrapper-download-video {
+
+ .video-sources {
+ margin: 0;
+ }
+ }
+
+ .wrapper-download-transcripts {
+
+ .list-download-transcripts {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+
+ .transcript-option {
+ margin: 0;
+ }
+ }
+ }
+
+ .branding {
+ @include padding-right(0);
+
+ .host-tag {
+ position: absolute;
+ left: -9999em;
+ display: inline-block;
+ vertical-align: middle;
+ color: $base-font-color;
+ }
+
+ .brand-logo {
+ display: inline-block;
+ max-width: 100%;
+ max-height: ($baseline*2);
+ padding: ($baseline/4) 0;
+ vertical-align: middle;
+ }
}
- }
}
- .video-tracks {
-
- > a {
- border-radius: 3px 0 0 3px;
- }
-
- > a.external-track {
- border-radius: 3px;
- }
- }
-
- .branding {
- display: inline-block;
- float: right;
- margin: 15px 0 0 10px;
- vertical-align: top;
-
- .host-tag {
- @include margin-right($baseline/2);
- position: absolute;
- left: -9999em;
- display: inline-block;
- vertical-align: middle;
- font-size: 70%;
- color: #777;
- }
-
- .brand-logo {
- display: inline-block;
- max-width: 100%;
- max-height: ($baseline*2);
- padding: ($baseline/4) 0;
- vertical-align: middle;
- }
- }
- }
-
.video-wrapper {
@include float(left);
@include margin-right(flex-gutter(9));
diff --git a/common/lib/xmodule/xmodule/js/fixtures/video_all.html b/common/lib/xmodule/xmodule/js/fixtures/video_all.html
index 4850a5e9e5..d5d50b5709 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/video_all.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/video_all.html
@@ -10,8 +10,8 @@
-
-
- -
-
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js
deleted file mode 100644
index f7dc49b238..0000000000
--- a/common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js
+++ /dev/null
@@ -1,290 +0,0 @@
-(function(undefined) {
- describe('Video Accessible Menu', function() {
- var state;
-
- afterEach(function() {
- $('source').remove();
- state.storage.clear();
- state.videoPlayer.destroy();
- });
-
- describe('constructor', function() {
- describe('always', function() {
- var videoTracks, container, button, menu, menuItems,
- menuItemsLinks;
-
- beforeEach(function() {
- state = jasmine.initializePlayer();
- videoTracks = $('li.video-tracks');
- container = videoTracks.children('div.a11y-menu-container');
- button = container.children('a.a11y-menu-button');
- menuList = container.children('ol.a11y-menu-list');
- menuItems = menuList.children('li.a11y-menu-item');
- menuItemsLinks = menuItems.children('a.a11y-menu-item-link');
- });
-
- it('add the accessible menu', function() {
- var activeMenuItem;
- // Make sure we have the expected HTML structure:
- // Menu container exists
- expect(container.length).toBe(1);
- // Only one button and one menu list per menu container.
- expect(button.length).toBe(1);
- expect(menuList.length).toBe(1);
- // At least one menu item and one menu link per menu
- // container. Exact length test?
- expect(menuItems.length).toBeGreaterThan(0);
- expect(menuItemsLinks.length).toBeGreaterThan(0);
- expect(menuItems.length).toBe(menuItemsLinks.length);
- // And one menu item is active
- activeMenuItem = menuItems.filter('.active');
- expect(activeMenuItem.length).toBe(1);
-
- expect(activeMenuItem.children('a.a11y-menu-item-link'))
- .toHaveData('value', 'srt');
-
- expect(activeMenuItem.children('a.a11y-menu-item-link'))
- .toHaveHtml('SubRip (.srt) file');
-
- /* TO DO: Check that all the anchors contain correct text.
- $.each(li.toArray().reverse(), function (index, link) {
- expect($(link)).toHaveData(
- 'speed', state.videoSpeedControl.speeds[index]
- );
- expect($(link).find('a').text()).toBe(
- state.videoSpeedControl.speeds[index] + 'x'
- );
- });
- */
- });
- });
-
- describe('when running', function() {
- var videoTracks, container, button, menu, menuItems,
- menuItemsLinks, KEY = $.ui.keyCode,
-
- keyPressEvent = function(key) {
- return $.Event('keydown', {keyCode: key});
- },
-
- tabBackPressEvent = function() {
- return $.Event('keydown',
- {keyCode: KEY.TAB, shiftKey: true});
- },
-
- tabForwardPressEvent = function() {
- return $.Event('keydown',
- {keyCode: KEY.TAB, shiftKey: false});
- },
-
- // Get previous element in array or cyles back to the last
- // if it is the first.
- previousSpeed = function(index) {
- return speedEntries.eq(index < 1 ?
- speedEntries.length - 1 :
- index - 1);
- },
-
- // Get next element in array or cyles back to the first if
- // it is the last.
- nextSpeed = function(index) {
- return speedEntries.eq(index >= speedEntries.length - 1 ?
- 0 :
- index + 1);
- };
-
- beforeEach(function() {
- state = jasmine.initializePlayer();
- videoTracks = $('li.video-tracks');
- container = videoTracks.children('div.a11y-menu-container');
- button = container.children('a.a11y-menu-button');
- menuList = container.children('ol.a11y-menu-list');
- menuItems = menuList.children('li.a11y-menu-item');
- menuItemsLinks = menuItems.children('a.a11y-menu-item-link');
- spyOn($.fn, 'focus').and.callThrough();
- });
-
- it('open/close the menu on mouseenter/mouseleave', function() {
- container.mouseenter();
- expect(container).toHaveClass('open');
- container.mouseleave();
- expect(container).not.toHaveClass('open');
- });
-
- it('do not close the menu on mouseleave if a menu item has ' +
- 'focus', function() {
- // Open menu. Focus is on last menu item.
- container.trigger(keyPressEvent(KEY.ENTER));
- container.mouseenter().mouseleave();
- expect(container).toHaveClass('open');
- });
-
- it('close the menu on click', function() {
- container.mouseenter().click();
- expect(container).not.toHaveClass('open');
- });
-
- it('close the menu on outside click', function() {
- container.trigger(keyPressEvent(KEY.ENTER));
- $(window).click();
- expect(container).not.toHaveClass('open');
- });
-
- it('open the menu on ENTER keydown', function() {
- container.trigger(keyPressEvent(KEY.ENTER));
- expect(container).toHaveClass('open');
- expect(menuItemsLinks.last().focus).toHaveBeenCalled();
- });
-
- it('open the menu on SPACE keydown', function() {
- container.trigger(keyPressEvent(KEY.SPACE));
- expect(container).toHaveClass('open');
- expect(menuItemsLinks.last().focus).toHaveBeenCalled();
- });
-
- it('open the menu on UP keydown', function() {
- container.trigger(keyPressEvent(KEY.UP));
- expect(container).toHaveClass('open');
- expect(menuItemsLinks.last().focus).toHaveBeenCalled();
- });
-
- it('close the menu on ESCAPE keydown', function() {
- container.trigger(keyPressEvent(KEY.ESCAPE));
- expect(container).not.toHaveClass('open');
- });
-
- it('UP and DOWN keydown function as expected on menu items',
- function() {
- // Iterate through list in both directions and check if
- // things wrap up correctly.
- var lastEntry = menuItemsLinks.length - 1, i;
-
- // First open menu
- container.trigger(keyPressEvent(KEY.UP));
-
- // Iterate with UP key until we have looped.
- for (i = lastEntry; i >= 0; i--) {
- menuItemsLinks.eq(i).trigger(keyPressEvent(KEY.UP));
- }
-
- // Iterate with DOWN key until we have looped.
- for (i = 0; i <= lastEntry; i++) {
- menuItemsLinks.eq(i).trigger(keyPressEvent(KEY.DOWN));
- }
-
- // Test if each element has been called twice.
- expect($.fn.focus.calls.count())
- .toEqual(2 * menuItemsLinks.length + 1);
- });
-
- it('ESC keydown on menu item closes menu', function() {
- // First open menu. Focus is on last speed entry.
- container.trigger(keyPressEvent(KEY.UP));
- menuItemsLinks.last().trigger(keyPressEvent(KEY.ESCAPE));
-
- // Menu is closed and focus has been returned to speed
- // control.
- expect(container).not.toHaveClass('open');
- expect(container.focus).toHaveBeenCalled();
- });
-
- it('ENTER keydown on menu item selects its data and closes menu',
- function() {
- // First open menu.
- container.trigger(keyPressEvent(KEY.UP));
- // Focus on '.txt'
- menuItemsLinks.eq(0).focus();
- menuItemsLinks.eq(0).trigger(keyPressEvent(KEY.ENTER));
-
- // Menu is closed, focus has been returned to container
- // and file format is '.txt'.
- /* TO DO
- expect(container.focus).toHaveBeenCalled();
- expect($('.video_speeds li[data-speed="1.50"]'))
- .toHaveClass('active');
- expect($('.speeds p.active')).toHaveHtml('1.50x');
- */
- });
-
- it('SPACE keydown on menu item selects its data and closes menu',
- function() {
- // First open menu.
- container.trigger(keyPressEvent(KEY.UP));
- // Focus on '.txt'
- menuItemsLinks.eq(0).focus();
- menuItemsLinks.eq(0).trigger(keyPressEvent(KEY.SPACE));
-
- // Menu is closed, focus has been returned to container
- // and file format is '.txt'.
- /* TO DO
- expect(speedControl.focus).toHaveBeenCalled();
- expect($('.video_speeds li[data-speed="1.50"]'))
- .toHaveClass('active');
- expect($('.speeds p.active')).toHaveHtml('1.50x');
- */
- });
-
- // TO DO? No such behavior implemented.
- xit('TAB + SHIFT keydown on speed entry closes menu and gives ' +
- 'focus to Play/Pause control', function() {
- // First open menu. Focus is on last speed entry.
- speedControl.trigger(keyPressEvent(KEY.UP));
- speedEntries.last().trigger(tabBackPressEvent());
-
- // Menu is closed and focus has been given to Play/Pause
- // control.
- expect(state.videoControl.playPauseEl.focus)
- .toHaveBeenCalled();
- });
-
- // TO DO? No such behavior implemented.
- xit('TAB keydown on speed entry closes menu and gives focus ' +
- 'to Volume control', function() {
- // First open menu. Focus is on last speed entry.
- speedControl.trigger(keyPressEvent(KEY.UP));
- speedEntries.last().trigger(tabForwardPressEvent());
-
- // Menu is closed and focus has been given to Volume
- // control.
- expect(state.videoVolumeControl.buttonEl.focus)
- .toHaveBeenCalled();
- });
- });
- });
-
- // TODO
- xdescribe('change file format', function() {
- describe('when new file format is not the same', function() {
- beforeEach(function() {
- state = jasmine.initializePlayer();
- state.videoSpeedControl.setSpeed(1.0);
- spyOn(state.videoPlayer, 'onSpeedChange').and.callThrough();
-
- $('li[data-speed="0.75"] .speed-link').click();
- });
-
- it('trigger speedChange event', function() {
- expect(state.videoPlayer.onSpeedChange).toHaveBeenCalled();
- expect(state.videoSpeedControl.currentSpeed).toEqual(0.75);
- });
- });
- });
-
- // TODO
- xdescribe('onSpeedChange', function() {
- beforeEach(function() {
- state = jasmine.initializePlayer();
- $('li[data-speed="1.0"] .speed-link').addClass('active');
- state.videoSpeedControl.setSpeed(0.75);
- });
-
- it('set the new speed as active', function() {
- expect($('.video_speeds li[data-speed="1.0"]'))
- .not.toHaveClass('active');
- expect($('.video_speeds li[data-speed="0.75"]'))
- .toHaveClass('active');
- expect($('.speeds p.active')).toHaveHtml('0.75x');
- });
- });
- });
-}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js b/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js
index 246cfff2dd..7b47debd63 100644
--- a/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js
+++ b/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js
@@ -1,9 +1,9 @@
(function(define) {
'use strict';
-// VideoAccessibleMenu module.
+// VideoTranscriptDownloadHandler module.
define(
-'video/035_video_accessible_menu.js', [],
-function() {
+'video/035_video_accessible_menu.js', ['underscore'],
+function(_) {
/**
* Video Download Transcript control module.
* @exports video/035_video_accessible_menu.js
@@ -11,231 +11,59 @@ function() {
* @param {jquery Element} element
* @param {Object} options
*/
- var VideoAccessibleMenu = function(element, options) {
- if (!(this instanceof VideoAccessibleMenu)) {
- return new VideoAccessibleMenu(element, options);
+ var VideoTranscriptDownloadHandler = function(element, options) {
+ if (!(this instanceof VideoTranscriptDownloadHandler)) {
+ return new VideoTranscriptDownloadHandler(element, options);
}
- _.bindAll(this, 'openMenu', 'openMenuHandler', 'closeMenu', 'closeMenuHandler', 'toggleMenuHandler',
- 'clickHandler', 'keyDownHandler', 'render', 'menuItemsLinksFocused', 'changeFileType', 'setValue'
- );
+ _.bindAll(this, 'clickHandler');
this.container = element;
this.options = options || {};
- if (this.container.find('.video-tracks')) {
+ if (this.container.find('.wrapper-downloads .wrapper-download-transcripts')) {
this.initialize();
}
+
+ return false;
};
- VideoAccessibleMenu.prototype = {
- /** Initializes the module. */
+ VideoTranscriptDownloadHandler.prototype = {
+ // Initializes the module.
initialize: function() {
this.value = this.options.storage.getItem('transcript_download_format');
- this.el = this.container.find('.video-tracks .a11y-menu-container');
- this.render();
- this.bindHandlers();
+ this.el = this.container.find('.list-download-transcripts');
+ this.el.on('click', '.btn-link', this.clickHandler);
},
- /**
- * Creates any necessary DOM elements, attach them, and set their,
- * initial configuration.
- */
- render: function() {
- var value, msg;
- // For the time being, we assume that the menu structure is present in
- // the template HTML. In the future accessible menu plugin, everything
- // inside will be generated in this
- // file.
- this.button = this.el.children('.a11y-menu-button');
- this.menuList = this.el.children('.a11y-menu-list');
- this.menuItems = this.menuList.children('.a11y-menu-item');
- this.menuItemsLinks = this.menuItems.children('.a11y-menu-item-link');
- value = (function(val, activeElement) {
- return val || activeElement.find('a').data('value') || 'srt';
- }(this.value, this.menuItems.filter('.active')));
- msg = '.' + value;
-
- if (value) {
- this.setValue(value);
- this.button.text(gettext(msg));
- }
- },
-
- /** Bind any necessary function callbacks to DOM events. */
- bindHandlers: function() {
- // Attach various events handlers to menu container.
- this.el.on({
- 'mouseenter': this.openMenuHandler,
- 'mouseleave': this.closeMenuHandler,
- 'click': this.toggleMenuHandler,
- 'keydown': this.keyDownHandler
- });
-
- // Attach click and keydown event handlers to individual menu items.
- this.menuItems
- .on('click', 'a.a11y-menu-item-link', this.clickHandler)
- .on('keydown', 'a.a11y-menu-item-link', this.keyDownHandler);
- },
-
- // Get previous element in array or cyles back to the last if it is the
- // first.
- previousMenuItemLink: function(links, index) {
- return index < 1 ? links.last() : links.eq(index - 1);
- },
-
- // Get next element in array or cyles back to the first if it is the last.
- nextMenuItemLink: function(links, index) {
- return index >= links.length - 1 ? links.first() : links.eq(index + 1);
- },
-
- menuItemsLinksFocused: function() {
- return this.menuItemsLinks.is(':focus');
- },
-
- openMenu: function(withoutHandler) {
- // When menu items have focus, the menu stays open on
- // mouseleave. A closeMenuHandler is added to the window
- // element to have clicks close the menu when they happen
- // outside of it. We namespace the click event to easily remove it (and
- // only it) in closeMenu.
- this.el.addClass('open');
- this.button.text('...');
- if (!withoutHandler) {
- $(window).on('click.currentMenu', this.closeMenuHandler);
- }
- // @TODO: onOpen callback
- },
-
- closeMenu: function(withoutHandler) {
- // Remove the previously added clickHandler from window element.
- var msg = '.' + this.value;
-
- this.el.removeClass('open');
- this.button.text(gettext(msg));
- if (!withoutHandler) {
- $(window).off('click.currentMenu');
- }
- // @TODO: onClose callback
- },
-
- openMenuHandler: function() {
- this.openMenu(true);
- return false;
- },
-
- closeMenuHandler: function(event) {
- // Only close the menu if no menu item link has focus or `click` event.
- if (!this.menuItemsLinksFocused() || event.type === 'click') {
- this.closeMenu(true);
- }
- return false;
- },
-
- toggleMenuHandler: function() {
- if (this.el.hasClass('open')) {
- this.closeMenu(true);
- } else {
- this.openMenu(true);
- }
- return false;
- },
-
- // Various event handlers. They all return false to stop propagation and
- // prevent default behavior.
+ // Event handler. We delay link clicks until the file type is set
clickHandler: function(event) {
- this.changeFileType.call(this, event);
- this.closeMenu(true);
- return false;
- },
+ var that = this,
+ fileType,
+ data,
+ downloadUrl;
- keyDownHandler: function(event) {
- var KEY = $.ui.keyCode,
- keyCode = event.keyCode,
- target = $(event.currentTarget),
- index;
+ event.preventDefault();
- if (target.is('a.a11y-menu-item-link')) {
- index = target.parent().index();
- switch (keyCode) {
- // Scroll up menu, wrapping at the top. Keep menu open.
- case KEY.UP:
- this.previousMenuItemLink(this.menuItemsLinks, index).focus();
- break;
- // Scroll down menu, wrapping at the bottom. Keep menu
- // open.
- case KEY.DOWN:
- this.nextMenuItemLink(this.menuItemsLinks, index).focus();
- break;
- // Close menu.
- case KEY.TAB:
- this.closeMenu();
- // TODO
- // What has to happen here? In speed menu, tabbing backward
- // will give focus to Play/Pause button and tabbing
- // forward to Volume button.
- break;
- // Close menu, give focus to button and change
- // file type.
- case KEY.ENTER:
- case KEY.SPACE:
- this.button.focus();
- this.changeFileType.call(this, event);
- this.closeMenu();
- break;
- // Close menu and give focus to speed control.
- case KEY.ESCAPE:
- this.closeMenu();
- this.button.focus();
- break;
- }
- return false;
- }
- else {
- switch (keyCode) {
- // Open menu and focus on last element of list above it.
- case KEY.ENTER:
- case KEY.SPACE:
- case KEY.UP:
- this.openMenu();
- this.menuItemsLinks.last().focus();
- break;
- // Close menu.
- case KEY.ESCAPE:
- this.closeMenu();
- break;
- }
- // We do not stop propagation and default behavior on a TAB
- // keypress.
- return event.keyCode === KEY.TAB;
- }
- },
-
- setValue: function(value) {
- this.value = value;
- this.menuItems
- .removeClass('active')
- .find("a[data-value='" + value + "']")
- .parent()
- .addClass('active');
- },
-
- changeFileType: function(event) {
- var fileType = $(event.currentTarget).data('value'),
- data = {'transcript_download_format': fileType};
-
- this.setValue(fileType);
- this.options.storage.setItem('transcript_download_format', fileType);
+ fileType = $(event.target).data('value');
+ data = {transcript_download_format: fileType};
+ downloadUrl = $(event.target).attr('href');
$.ajax({
url: this.options.saveStateUrl,
type: 'POST',
dataType: 'json',
- data: data
+ data: data,
+ success: function() {
+ that.options.storage.setItem('transcript_download_format', fileType);
+ },
+ complete: function() {
+ document.location.href = downloadUrl;
+ }
});
}
};
- return VideoAccessibleMenu;
+ return VideoTranscriptDownloadHandler;
});
}(RequireJS.define));
diff --git a/common/test/acceptance/pages/lms/video/video.py b/common/test/acceptance/pages/lms/video/video.py
index 86bf546942..0848e8f5e1 100644
--- a/common/test/acceptance/pages/lms/video/video.py
+++ b/common/test/acceptance/pages/lms/video/video.py
@@ -57,7 +57,10 @@ VIDEO_MENUS = {
'language': '.lang .menu',
'speed': '.speed .menu',
'download_transcript': '.video-tracks .a11y-menu-list',
- 'transcript-format': '.video-tracks .a11y-menu-button',
+ 'transcript-format': {
+ 'srt': '.wrapper-download-transcripts .list-download-transcripts .btn-link[data-value="srt"]',
+ 'txt': '.wrapper-download-transcripts .list-download-transcripts .btn-link[data-value="txt"]'
+ },
'transcript-skip': '.sr-is-focusable.transcript-start',
}
@@ -584,7 +587,7 @@ class VideoPage(PageObject):
bool: Transcript download result.
"""
- transcript_selector = self.get_element_selector(VIDEO_MENUS['transcript-format'])
+ transcript_selector = self.get_element_selector(VIDEO_MENUS['transcript-format'][transcript_format])
# check if we have a transcript with correct format
if '.' + transcript_format not in self.q(css=transcript_selector).text[0]:
@@ -595,16 +598,15 @@ class VideoPage(PageObject):
'txt': 'text/plain',
}
- transcript_url_selector = self.get_element_selector(VIDEO_BUTTONS['download_transcript'])
- url = self.q(css=transcript_url_selector).attrs('href')[0]
+ link = self.q(css=transcript_selector)
+ url = link.attrs('href')[0]
+ link.click()
+
result, headers, content = self._get_transcript(url)
if result is False:
return False
- if formats[transcript_format] not in headers.get('content-type', ''):
- return False
-
if text_to_search not in content.decode('utf-8'):
return False
@@ -674,45 +676,6 @@ class VideoPage(PageObject):
selector = self.get_element_selector(VIDEO_MENUS[menu_name])
return self.q(css=selector).present
- def select_transcript_format(self, transcript_format):
- """
- Select transcript with format `transcript_format`.
-
- Arguments:
- transcript_format (st): Transcript file format `srt` or `txt`.
-
- Returns:
- bool: Selection Result.
-
- """
- button_selector = self.get_element_selector(VIDEO_MENUS['transcript-format'])
-
- button = self.q(css=button_selector).results[0]
-
- hover = ActionChains(self.browser).move_to_element(button)
- hover.perform()
-
- if '...' not in self.q(css=button_selector).text[0]:
- return False
-
- menu_selector = self.get_element_selector(VIDEO_MENUS['download_transcript'])
- menu_items = self.q(css=menu_selector + ' a').results
- for item in menu_items:
- if item.get_attribute('data-value') == transcript_format:
- ActionChains(self.browser).move_to_element(item).click().perform()
- self.wait_for_ajax()
- break
-
- self.browser.execute_script("window.scrollTo(0, 0);")
-
- if self.q(css=menu_selector + ' .active a').attrs('data-value')[0] != transcript_format:
- return False
-
- if '.' + transcript_format not in self.q(css=button_selector).text[0]:
- return False
-
- return True
-
@property
def sources(self):
"""
diff --git a/common/test/acceptance/pages/studio/video/video.py b/common/test/acceptance/pages/studio/video/video.py
index bb6ebf0b48..73d9cb747c 100644
--- a/common/test/acceptance/pages/studio/video/video.py
+++ b/common/test/acceptance/pages/studio/video/video.py
@@ -32,7 +32,7 @@ CLASS_SELECTORS = {
BUTTON_SELECTORS = {
'create_video': 'button[data-category="video"]',
- 'handout_download': '.video-handout.video-download-button a',
+ 'handout_download': '.wrapper-handouts .btn-link',
'handout_download_editor': '.wrapper-comp-setting.file-uploader .download-action',
'upload_asset': '.upload-action',
'asset_submit': '.action-upload',
diff --git a/common/test/acceptance/tests/video/test_video_module.py b/common/test/acceptance/tests/video/test_video_module.py
index 56e18a198b..6d300d30e5 100644
--- a/common/test/acceptance/tests/video/test_video_module.py
+++ b/common/test/acceptance/tests/video/test_video_module.py
@@ -4,6 +4,7 @@
Acceptance tests for Video.
"""
import os
+from ddt import ddt, unpack, data
from mock import patch
from nose.plugins.attrib import attr
@@ -199,6 +200,7 @@ class VideoBaseTest(UniqueCourseTest):
@attr(shard=4)
+@ddt
class YouTubeVideoTest(VideoBaseTest):
""" Test YouTube Video Player """
@@ -491,15 +493,16 @@ class YouTubeVideoTest(VideoBaseTest):
self.assertTrue(self.video.is_button_shown('transcript_button'))
self._verify_caption_text('Welcome to edX.')
- def test_download_transcript_button_works_correctly(self):
+ @data(('srt', '00:00:00,260'), ('txt', 'Welcome to edX.'))
+ @unpack
+ def test_download_transcript_links_work_correctly(self, file_type, search_text):
"""
- Scenario: Download Transcript button works correctly
+ Scenario: Download 'srt' transcript link works correctly.
+ Download 'txt' transcript link works correctly.
Given the course has Video components A and B in "Youtube" mode
And Video component C in "HTML5" mode
And I have defined downloadable transcripts for the videos
Then I can download a transcript for Video A in "srt" format
- And I can download a transcript for Video A in "txt" format
- And I can download a transcript for Video B in "txt" format
And the Download Transcript menu does not exist for Video C
"""
@@ -524,19 +527,7 @@ class YouTubeVideoTest(VideoBaseTest):
self.navigate_to_video()
# check if we can download transcript in "srt" format that has text "00:00:00,260"
- self.assertTrue(self.video.downloaded_transcript_contains_text('srt', '00:00:00,260'))
-
- # select the transcript format "txt"
- self.assertTrue(self.video.select_transcript_format('txt'))
-
- # check if we can download transcript in "txt" format that has text "Welcome to edX."
- self.assertTrue(self.video.downloaded_transcript_contains_text('txt', 'Welcome to edX.'))
-
- # open vertical containing video "B"
- self.course_nav.go_to_vertical('Test Vertical-1')
-
- # check if we can download transcript in "txt" format that has text "Equal transcripts"
- self.assertTrue(self.video.downloaded_transcript_contains_text('txt', 'Equal transcripts'))
+ self.assertTrue(self.video.downloaded_transcript_contains_text(file_type, search_text))
# open vertical containing video "C"
self.course_nav.go_to_vertical('Test Vertical-2')
diff --git a/lms/templates/video.html b/lms/templates/video.html
index a9a9ed86fc..0c0a0484f0 100644
--- a/lms/templates/video.html
+++ b/lms/templates/video.html
@@ -39,52 +39,49 @@ from openedx.core.djangolib.js_utils import js_escaped_string
-
% if cdn_eval: