Commit 0ebc9047 by Chris Rodriguez

AC-571 updating video download/transcript area

parent 087acb8a
......@@ -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;
......@@ -75,58 +75,56 @@
height: 0px;
}
.downloads-heading {
margin: 1em 0 0 0;
}
.wrapper-downloads {
display: flex;
.hd {
margin: 0;
padding: 0;
}
.video-download-button {
display: inline-block;
.wrapper-download-video,
.wrapper-download-transcripts,
.wrapper-handouts,
.branding {
flex: 1;
margin-top: $baseline;
@include padding-right($baseline);
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;
}
.wrapper-download-video {
.video-sources {
margin: 0;
}
}
.video-tracks {
.wrapper-download-transcripts {
> a {
border-radius: 3px 0 0 3px;
}
.list-download-transcripts {
margin: 0;
padding: 0;
list-style: none;
> a.external-track {
border-radius: 3px;
.transcript-option {
margin: 0;
}
}
}
.branding {
display: inline-block;
float: right;
margin: 15px 0 0 10px;
vertical-align: top;
@include padding-right(0);
.host-tag {
@include margin-right($baseline/2);
position: absolute;
left: -9999em;
display: inline-block;
vertical-align: middle;
font-size: 70%;
color: #777;
color: $base-font-color;
}
.brand-logo {
......
......@@ -10,8 +10,8 @@
<div class="tc-wrapper">
<article class="video-wrapper">
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
<span tabindex="-1" class="btn-play is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="Loading video player"></span>
<span tabindex="-1" class="btn-play is-hidden" aria-hidden="true" aria-label="Play video"></span>
<div class="video-player-pre"></div>
<section class="video-player">
<div id="id"></div>
......@@ -30,21 +30,31 @@
<div class="focus_grabber last"></div>
<ul class="wrapper-downloads">
<li class="video-tracks">
<div class="a11y-menu-container">
<a class="a11y-menu-button" href="#" title=".srt">.srt</a>
<ol class="a11y-menu-list">
<li class="a11y-menu-item">
<a class="a11y-menu-item-link" href="#txt" title="Text (.txt) file" data-value="txt">Text (.txt) file</a>
</li>
<li class="a11y-menu-item active">
<a class="a11y-menu-item-link" href="#srt" title="SubRip (.srt) file" data-value="srt">SubRip (.srt) file</a>
</li>
</ol>
<h3 class="hd hd-4 downloads-heading sr" id="video-download-transcripts">Downloads and transcripts</h3>
<div class="wrapper-downloads" role="region" aria-labelledby="video-download-transcripts">
<div class="wrapper-download-video">
<h4 class="hd hd-5">Video</h4>
<a class="btn-link video-sources video-download-button" href="#">
Download video file
</a>
</div>
<div class="wrapper-download-transcripts">
<h4 class="hd hd-5">Transcripts</h4>
<ul class="list-download-transcripts">
<li class="transcript-option">
<a href="#" class="btn btn-link" data-href="txt">Download Text (.txt) file</a>
</li>
<li class="transcript-option">
<a href="#" class="btn btn-link" data-href="srt">Download SubRip (.srt) file</a>
</li>
</ul>
<a href="#" class="external-track">Download transcript</a>
</div>
<div class="wrapper-handouts">
<h4 class="hd hd-5">Handouts</h4>
<a href="#">Download Handout</a>
</div>
</div>
</div>
</div>
......
(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);
(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();
},
/**
* 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 <div class='menu-container'></div> 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
this.el = this.container.find('.list-download-transcripts');
this.el.on('click', '.btn-link', this.clickHandler);
},
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;
},
keyDownHandler: function(event) {
var KEY = $.ui.keyCode,
keyCode = event.keyCode,
target = $(event.currentTarget),
index;
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');
},
var that = this,
fileType,
data,
downloadUrl;
changeFileType: function(event) {
var fileType = $(event.currentTarget).data('value'),
data = {'transcript_download_format': fileType};
event.preventDefault();
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));
......@@ -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):
"""
......
......@@ -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',
......
......@@ -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')
......
......@@ -39,52 +39,49 @@ from openedx.core.djangolib.js_utils import js_escaped_string
</div>
<div class="focus_grabber last"></div>
<ul class="wrapper-downloads">
% if download_video_link or track or handout or branding_info:
<h3 class="hd hd-4 downloads-heading sr" id="video-download-transcripts_${id}">${_('Downloads and transcripts')}</h3>
<div class="wrapper-downloads" role="region" aria-labelledby="video-download-transcripts_${id}">
% if download_video_link:
<li class="video-sources video-download-button">
<a href="${download_video_link}">${_('Download video')}</a>
</li>
<div class="wrapper-download-video">
<h4 class="hd hd-5">${_('Video')}</h4>
<a class="btn-link video-sources video-download-button" href="${download_video_link}">
${_('Download video file')}
</a>
</div>
% endif
% if track:
<li class="video-tracks video-download-button">
<div class="wrapper-download-transcripts">
<h4 class="hd hd-5">${_('Transcripts')}</h4>
% if transcript_download_format:
<a href="${track}">${_('Download transcript')}</a>
<div class="a11y-menu-container">
<a class="a11y-menu-button" href="#" title="${'.' + transcript_download_format}" role="button" aria-disabled="false">${'.' + transcript_download_format}</a>
<ol class="a11y-menu-list" role="menu">
<ul class="list-download-transcripts">
% for item in transcript_download_formats_list:
% if item['value'] == transcript_download_format:
<li class="a11y-menu-item active">
% else:
<li class="a11y-menu-item">
% endif
## This is necessary so we don't scrape 'display_name' as a string.
<% dname = item['display_name'] %>
<a class="a11y-menu-item-link" href="#${item['value']}" title="${_(dname)}" data-value="${item['value']}" role="menuitem" aria-disabled="false">
${_(dname)}
</a>
<li class="transcript-option">
<% dname = _("Download {file}").format(file=item['display_name']) %>
<a class="btn btn-link" href="${track}" data-value="${item['value']}">${dname}</a>
</li>
% endfor
</ol>
</div>
</ul>
% else:
<a href="${track}" class="external-track">${_('Download transcript')}</a>
<a class="btn-link external-track" href="${track}">${_('Download transcript')}</a>
% endif
</li>
</div>
% endif
% if handout:
<li class="video-handout video-download-button">
<a href="${handout}" target="_blank">${_('Download Handout')}</a>
</li>
<div class="wrapper-handouts">
<h4 class="hd hd-5">${_('Handouts')}</h4>
<a class="btn-link" href="${handout}">${_('Download Handout')}</a>
</div>
% endif
% if branding_info:
<li id="branding" class="branding">
<div class="branding">
<span class="host-tag">${branding_info['logo_tag']}</span>
<a href="${branding_info['url']}" target="_blank" title="${branding_info['logo_tag']}"><img class="brand-logo" src="${branding_info['logo_src']}" alt="${branding_info['logo_tag']}" /></a>
</li>
<a href="${branding_info['url']}"><img class="brand-logo" src="${branding_info['logo_src']}" alt="${branding_info['logo_tag']}" /></a>
</div>
% endif
</div>
% endif
</ul>
</div>
% if cdn_eval:
<script>
......
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