From 0ebc90472c382bb81a43f752b95fea9e7ee0a6e9 Mon Sep 17 00:00:00 2001 From: Chris Rodriguez Date: Fri, 26 Aug 2016 09:48:18 -0400 Subject: [PATCH] AC-571 updating video download/transcript area --- .../xmodule/xmodule/css/video/display.scss | 124 ++++---- .../xmodule/js/fixtures/video_all.html | 44 ++- .../spec/video/video_accessible_menu_spec.js | 290 ------------------ .../js/src/video/035_video_accessible_menu.js | 234 ++------------ .../test/acceptance/pages/lms/video/video.py | 55 +--- .../acceptance/pages/studio/video/video.py | 2 +- .../tests/video/test_video_module.py | 25 +- lms/templates/video.html | 83 +++-- 8 files changed, 177 insertions(+), 680 deletions(-) delete mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js 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 @@
- - + +
@@ -29,22 +29,32 @@
- - +
+

Transcripts

+ + Download transcript +
+
+

Handouts

+ Download Handout +
+ 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
- + % else: + ${_('Download transcript')} + % endif + + % endif + % if handout: +
+

${_('Handouts')}

+ ${_('Download Handout')} +
+ % endif + % if branding_info: +
+ ${branding_info['logo_tag']} + +
+ % endif + % endif - % if handout: -
  • - ${_('Download Handout')} -
  • - % endif - - % if branding_info: -
  • - ${branding_info['logo_tag']} - -
  • - % endif - % if cdn_eval: