Merge pull request #17718 from edx/transcripts-phase-2
Transcripts phase 2
This commit is contained in:
@@ -55,8 +55,7 @@ define([
|
||||
videoImageSettings: videoImageSettings,
|
||||
videoTranscriptSettings: videoTranscriptSettings,
|
||||
transcriptAvailableLanguages: transcriptAvailableLanguages,
|
||||
videoSupportedFileFormats: videoSupportedFileFormats,
|
||||
isVideoTranscriptEnabled: isVideoTranscriptEnabled
|
||||
videoSupportedFileFormats: videoSupportedFileFormats
|
||||
});
|
||||
$contentWrapper.find('.wrapper-assets').replaceWith(updatedView.render().$el);
|
||||
});
|
||||
@@ -71,8 +70,7 @@ define([
|
||||
videoImageSettings: videoImageSettings,
|
||||
videoTranscriptSettings: videoTranscriptSettings,
|
||||
transcriptAvailableLanguages: transcriptAvailableLanguages,
|
||||
videoSupportedFileFormats: videoSupportedFileFormats,
|
||||
isVideoTranscriptEnabled: isVideoTranscriptEnabled
|
||||
videoSupportedFileFormats: videoSupportedFileFormats
|
||||
});
|
||||
$contentWrapper.append(activeView.render().$el);
|
||||
$contentWrapper.append(previousView.render().$el);
|
||||
|
||||
@@ -11,7 +11,8 @@ define(['backbone'], function(Backbone) {
|
||||
explicitly_set: null,
|
||||
default_value: null,
|
||||
options: null,
|
||||
type: null
|
||||
type: null,
|
||||
custom: false // Used only for non-metadata fields
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
@@ -24,6 +25,11 @@ define(['backbone'], function(Backbone) {
|
||||
* property has changed.
|
||||
*/
|
||||
isModified: function() {
|
||||
// A non-metadata field will handle itself
|
||||
if (this.get('custom') === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.get('explicitly_set') && !this.original_explicitly_set) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ function($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCol
|
||||
field_name: 'edx_video_id',
|
||||
help: 'Specifies the video ID.',
|
||||
options: [],
|
||||
type: MetadataModel.GENERIC_TYPE,
|
||||
type: 'VideoID',
|
||||
value: 'basic tab video id'
|
||||
},
|
||||
models = [DisplayNameEntry, VideoListEntry, VideoIDEntry],
|
||||
@@ -51,7 +51,8 @@ function($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCol
|
||||
object: testData,
|
||||
string: JSON.stringify(testData)
|
||||
},
|
||||
transcripts, $container;
|
||||
component_locator = 'component_locator',
|
||||
transcripts, $container, waitForEvent, editor;
|
||||
|
||||
var waitsForDisplayName = function(collection) {
|
||||
return jasmine.waitUntil(function() {
|
||||
@@ -76,15 +77,109 @@ function($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCol
|
||||
Utils.Storage.remove('sub');
|
||||
});
|
||||
|
||||
describe('Events', function() {
|
||||
beforeEach(function() {
|
||||
Utils.command.and.callThrough();
|
||||
spyOn(Backbone, 'trigger').and.callThrough();
|
||||
spyOn(Editor.prototype, 'destroy').and.callThrough();
|
||||
spyOn(Editor.prototype, 'handleFieldChanged').and.callThrough();
|
||||
spyOn(Editor.prototype, 'getLocator').and.returnValue(component_locator);
|
||||
|
||||
appendSetFixtures(
|
||||
sandbox({ // eslint-disable-line no-undef
|
||||
class: 'wrapper-comp-settings basic_metadata_edit',
|
||||
'data-metadata': JSON.stringify({video_url: VideoListEntry, edx_video_id: VideoIDEntry})
|
||||
})
|
||||
);
|
||||
|
||||
appendSetFixtures(
|
||||
$('<script>',
|
||||
{
|
||||
id: 'metadata-videolist-entry',
|
||||
type: 'text/template'
|
||||
}
|
||||
).text(readFixtures('video/transcripts/metadata-videolist-entry.underscore'))
|
||||
);
|
||||
|
||||
appendSetFixtures(
|
||||
$('<script>',
|
||||
{
|
||||
id: 'metadata-string-entry',
|
||||
type: 'text/template'
|
||||
}
|
||||
).text(readFixtures('metadata-string-entry.underscore'))
|
||||
);
|
||||
|
||||
editor = new Editor({
|
||||
el: $('.basic_metadata_edit')
|
||||
});
|
||||
|
||||
// reset the already triggered events
|
||||
Backbone.trigger.calls.reset();
|
||||
// reset the manual call to `handleFieldChanged` we made in the `editor.js::initialize`
|
||||
Editor.prototype.handleFieldChanged.calls.reset();
|
||||
});
|
||||
|
||||
waitForEvent = function(eventName) {
|
||||
var triggerCallArgs;
|
||||
return jasmine.waitUntil(function() {
|
||||
triggerCallArgs = Backbone.trigger.calls.mostRecent().args;
|
||||
return Backbone.trigger.calls.count() === 1 && triggerCallArgs[0] === eventName;
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(function() {
|
||||
Backbone.trigger.calls.reset();
|
||||
Editor.prototype.destroy.calls.reset();
|
||||
Editor.prototype.handleFieldChanged.calls.reset();
|
||||
});
|
||||
|
||||
it('handles transcripts:basicTabFieldChanged', function(done) {
|
||||
var event = 'transcripts:basicTabFieldChanged';
|
||||
|
||||
Backbone.trigger(event);
|
||||
waitForEvent(event)
|
||||
.then(function() {
|
||||
expect(Editor.prototype.handleFieldChanged).toHaveBeenCalled();
|
||||
expect(Utils.command).toHaveBeenCalledWith(
|
||||
'check',
|
||||
component_locator,
|
||||
[
|
||||
{ mode: 'youtube', video: '12345678901', type: 'youtube' },
|
||||
{ mode: 'html5', video: 'video', type: 'mp4' },
|
||||
{ mode: 'html5', video: 'video', type: 'webm' },
|
||||
{ mode: 'edx_video_id', type: 'edx_video_id', video: 'basic tab video id' }
|
||||
]
|
||||
);
|
||||
}).always(done);
|
||||
});
|
||||
|
||||
it('handles xblock:editorModalHidden', function(done) {
|
||||
var event = 'xblock:editorModalHidden';
|
||||
|
||||
Backbone.trigger(event);
|
||||
waitForEvent(event)
|
||||
.then(function() {
|
||||
expect(Editor.prototype.destroy).toHaveBeenCalled();
|
||||
}).always(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test initialization', function() {
|
||||
beforeEach(function() {
|
||||
spyOn(MetadataView, 'Editor');
|
||||
spyOn(Editor.prototype, 'handleFieldChanged');
|
||||
|
||||
transcripts = new Editor({
|
||||
el: $container
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
MetadataView.Editor.calls.reset();
|
||||
Editor.prototype.handleFieldChanged.calls.reset();
|
||||
});
|
||||
|
||||
$.each(metadataDict, function(index, val) {
|
||||
it('toModels with argument as ' + index, function() {
|
||||
expect(transcripts.toModels(val)).toEqual(models);
|
||||
@@ -159,6 +254,7 @@ function($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCol
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(MetadataView, 'Editor');
|
||||
spyOn(Editor.prototype, 'handleFieldChanged');
|
||||
|
||||
transcripts = new Editor({
|
||||
el: $container
|
||||
@@ -182,6 +278,11 @@ function($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCol
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
MetadataView.Editor.calls.reset();
|
||||
Editor.prototype.handleFieldChanged.calls.reset();
|
||||
});
|
||||
|
||||
describe('Test Advanced to Basic synchronization', function() {
|
||||
it('Correct data', function(done) {
|
||||
transcripts.syncBasicTab(metadataCollection, metadataView);
|
||||
@@ -362,31 +463,6 @@ function($, Backbone, _, Utils, Editor, MetadataView, MetadataModel, MetadataCol
|
||||
}).getValue();
|
||||
expect(youtubeValue).toEqual('');
|
||||
});
|
||||
|
||||
it('Timed Transcript field is updated', function() {
|
||||
Utils.Storage.set('sub', 'test_value');
|
||||
|
||||
transcripts.syncAdvancedTab(metadataCollection);
|
||||
|
||||
var collection = metadataCollection.models,
|
||||
subValue = collection[1].getValue();
|
||||
|
||||
expect(subValue).toEqual('test_value');
|
||||
});
|
||||
|
||||
it('Timed Transcript field is updated just once', function() {
|
||||
Utils.Storage.set('sub', 'test_value');
|
||||
|
||||
var collection = metadataCollection.models,
|
||||
subModel = collection[1];
|
||||
|
||||
spyOn(subModel, 'setValue');
|
||||
|
||||
transcripts.syncAdvancedTab(metadataCollection);
|
||||
transcripts.syncAdvancedTab(metadataCollection);
|
||||
transcripts.syncAdvancedTab(metadataCollection);
|
||||
expect(subModel.setValue.calls.count()).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
define(
|
||||
[
|
||||
'jquery', 'underscore',
|
||||
'jquery', 'underscore', 'backbone',
|
||||
'js/views/video/transcripts/utils', 'js/views/video/transcripts/file_uploader',
|
||||
'xmodule', 'jquery.form'
|
||||
],
|
||||
function($, _, Utils, FileUploader) {
|
||||
function($, _, Backbone, TranscriptUtils, FileUploader) {
|
||||
'use strict';
|
||||
|
||||
describe('Transcripts.FileUploader', function() {
|
||||
@@ -34,10 +34,6 @@ function($, _, Utils, FileUploader) {
|
||||
'MessageManager',
|
||||
['render', 'showError', 'hideError']
|
||||
),
|
||||
videoListObject = jasmine.createSpyObj(
|
||||
'MetadataView.VideoList',
|
||||
['render', 'getVideoObjectsList']
|
||||
),
|
||||
$container = $('.transcripts-status');
|
||||
|
||||
$container
|
||||
@@ -49,7 +45,6 @@ function($, _, Utils, FileUploader) {
|
||||
view = new FileUploader({
|
||||
el: $container,
|
||||
messenger: messenger,
|
||||
videoListObject: videoListObject,
|
||||
component_locator: 'component_locator'
|
||||
});
|
||||
});
|
||||
@@ -100,6 +95,12 @@ function($, _, Utils, FileUploader) {
|
||||
});
|
||||
|
||||
describe('Upload', function() {
|
||||
var videoId = '123-456-789-0';
|
||||
|
||||
beforeEach(function() {
|
||||
TranscriptUtils.Storage.set('edx_video_id', videoId);
|
||||
});
|
||||
|
||||
it('File is not chosen', function() {
|
||||
spyOn($.fn, 'ajaxSubmit');
|
||||
view.upload();
|
||||
@@ -114,6 +115,9 @@ function($, _, Utils, FileUploader) {
|
||||
view.upload();
|
||||
|
||||
expect(view.$form.ajaxSubmit).toHaveBeenCalled();
|
||||
expect(view.$form.ajaxSubmit).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||
data: {'edx_video_id': videoId}
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -196,31 +200,25 @@ function($, _, Utils, FileUploader) {
|
||||
status: 200,
|
||||
responseText: JSON.stringify({
|
||||
status: 'Success',
|
||||
subs: 'test'
|
||||
edx_video_id: 'test_video_id'
|
||||
})
|
||||
};
|
||||
spyOn(Utils.Storage, 'set');
|
||||
spyOn(Backbone, 'trigger');
|
||||
view.xhrCompleteHandler(xhr);
|
||||
|
||||
expect(view.$progress).toHaveClass('is-invisible');
|
||||
expect(view.options.messenger.render.calls.mostRecent().args[0])
|
||||
.toEqual('uploaded');
|
||||
expect(Utils.Storage.set)
|
||||
.toHaveBeenCalledWith('sub', 'test');
|
||||
expect(Backbone.trigger)
|
||||
.toHaveBeenCalledWith('transcripts:basicTabUpdateEdxVideoId', 'test_video_id');
|
||||
});
|
||||
|
||||
var assertAjaxError = function(xhr) {
|
||||
spyOn(Utils.Storage, 'set');
|
||||
view.xhrCompleteHandler(xhr);
|
||||
|
||||
expect(view.options.messenger.showError).toHaveBeenCalled();
|
||||
expect(view.$progress).toHaveClass('is-invisible');
|
||||
expect(view.options.messenger.render)
|
||||
.not
|
||||
.toHaveBeenCalled();
|
||||
expect(Utils.Storage.set)
|
||||
.not
|
||||
.toHaveBeenCalledWith('sub', 'test');
|
||||
expect(view.options.messenger.render).not.toHaveBeenCalled();
|
||||
};
|
||||
|
||||
it('Ajax transport Error', function() {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
define(
|
||||
[
|
||||
'jquery', 'underscore',
|
||||
'jquery', 'underscore', 'backbone',
|
||||
'js/views/video/transcripts/utils', 'js/views/video/transcripts/message_manager',
|
||||
'js/views/video/transcripts/file_uploader', 'sinon',
|
||||
'xmodule'
|
||||
],
|
||||
function($, _, Utils, MessageManager, FileUploader, sinon) {
|
||||
function($, _, Backbone, Utils, MessageManager, FileUploader, sinon) {
|
||||
'use strict';
|
||||
|
||||
describe('Transcripts.MessageManager', function() {
|
||||
@@ -61,8 +61,7 @@ function($, _, Utils, MessageManager, FileUploader, sinon) {
|
||||
expect(fileUploader.initialize).toHaveBeenCalledWith({
|
||||
el: view.$el,
|
||||
messenger: view,
|
||||
component_locator: view.component_locator,
|
||||
videoListObject: view.options.parent
|
||||
component_locator: view.component_locator
|
||||
});
|
||||
});
|
||||
|
||||
@@ -185,12 +184,15 @@ function($, _, Utils, MessageManager, FileUploader, sinon) {
|
||||
};
|
||||
|
||||
it('Invoke without extraParamas', function(done) {
|
||||
|
||||
spyOn(Backbone, 'trigger');
|
||||
|
||||
sinonXhr.respondWith([
|
||||
200,
|
||||
{'Content-Type': 'application/json'},
|
||||
JSON.stringify({
|
||||
status: 'Success',
|
||||
subs: 'video_id'
|
||||
edx_video_id: 'video_id'
|
||||
})
|
||||
]);
|
||||
|
||||
@@ -203,20 +205,23 @@ function($, _, Utils, MessageManager, FileUploader, sinon) {
|
||||
void(0)
|
||||
);
|
||||
expect(view.showError).not.toHaveBeenCalled();
|
||||
expect(view.render.calls.mostRecent().args[0])
|
||||
.toEqual('found');
|
||||
expect(Utils.Storage.set).toHaveBeenCalled();
|
||||
expect(view.render.calls.mostRecent().args[0]).toEqual('found');
|
||||
expect(Backbone.trigger)
|
||||
.toHaveBeenCalledWith('transcripts:basicTabUpdateEdxVideoId', 'video_id');
|
||||
})
|
||||
.always(done);
|
||||
});
|
||||
|
||||
it('Invoke with extraParamas', function(done) {
|
||||
|
||||
spyOn(Backbone, 'trigger');
|
||||
|
||||
sinonXhr.respondWith([
|
||||
200,
|
||||
{'Content-Type': 'application/json'},
|
||||
JSON.stringify({
|
||||
status: 'Success',
|
||||
subs: 'video_id'
|
||||
edx_video_id: 'video_id'
|
||||
})
|
||||
]);
|
||||
|
||||
@@ -234,7 +239,8 @@ function($, _, Utils, MessageManager, FileUploader, sinon) {
|
||||
);
|
||||
expect(view.showError).not.toHaveBeenCalled();
|
||||
expect(view.render.calls.mostRecent().args[0]).toEqual('found');
|
||||
expect(Utils.Storage.set).toHaveBeenCalled();
|
||||
expect(Backbone.trigger)
|
||||
.toHaveBeenCalledWith('transcripts:basicTabUpdateEdxVideoId', 'video_id');
|
||||
})
|
||||
.always(done);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
define(
|
||||
[
|
||||
'jquery', 'underscore',
|
||||
'jquery', 'underscore', 'backbone',
|
||||
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
|
||||
'js/views/video/transcripts/utils',
|
||||
'js/views/video/transcripts/editor',
|
||||
'js/views/video/transcripts/metadata_videolist', 'js/models/metadata',
|
||||
'js/views/abstract_editor',
|
||||
'js/views/video/transcripts/message_manager',
|
||||
'xmodule'
|
||||
],
|
||||
function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
function($, _, Backbone, AjaxHelpers, Utils, Editor, VideoList, MetadataModel, AbstractEditor, MessageManager) {
|
||||
'use strict';
|
||||
describe('CMS.Views.Metadata.VideoList', function() {
|
||||
var videoListEntryTemplate = readFixtures(
|
||||
@@ -46,12 +48,23 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
'video.webm'
|
||||
]
|
||||
},
|
||||
videoIDStub = {
|
||||
default_value: 'test default value',
|
||||
display_name: 'Video ID',
|
||||
explicitly_set: true,
|
||||
field_name: 'edx_video_id',
|
||||
help: 'Specifies the video ID.',
|
||||
options: [],
|
||||
type: 'VideoID',
|
||||
value: 'advanced tab video id'
|
||||
},
|
||||
response = JSON.stringify({
|
||||
command: 'found',
|
||||
status: 'Success',
|
||||
subs: 'video_id'
|
||||
}),
|
||||
MessageManager, messenger;
|
||||
waitForEvent,
|
||||
createVideoListView;
|
||||
|
||||
|
||||
var createMockAjaxServer = function() {
|
||||
@@ -67,7 +80,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
var tpl = sandbox({
|
||||
var tpl = sandbox({ // eslint-disable-line no-undef
|
||||
class: 'component',
|
||||
'data-locator': component_locator
|
||||
});
|
||||
@@ -86,24 +99,17 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
// create mock server
|
||||
this.mockServer = createMockAjaxServer();
|
||||
|
||||
spyOn($.fn, 'on').and.callThrough();
|
||||
spyOn(Backbone, 'trigger').and.callThrough();
|
||||
spyOn(Utils, 'command').and.callThrough();
|
||||
spyOn(abstractEditor, 'initialize').and.callThrough();
|
||||
spyOn(abstractEditor, 'render').and.callThrough();
|
||||
spyOn(console, 'error');
|
||||
|
||||
messenger = jasmine.createSpyObj('MessageManager', [
|
||||
'initialize', 'render', 'showError', 'hideError'
|
||||
]);
|
||||
|
||||
$.each(messenger, function(index, method) {
|
||||
method.and.returnValue(messenger);
|
||||
});
|
||||
|
||||
MessageManager = function() {
|
||||
messenger.initialize();
|
||||
|
||||
return messenger;
|
||||
};
|
||||
spyOn(MessageManager.prototype, 'initialize').and.callThrough();
|
||||
spyOn(MessageManager.prototype, 'render').and.callThrough();
|
||||
spyOn(MessageManager.prototype, 'showError').and.callThrough();
|
||||
spyOn(MessageManager.prototype, 'hideError').and.callThrough();
|
||||
|
||||
jasmine.addMatchers({
|
||||
assertValueInView: function() {
|
||||
@@ -154,13 +160,49 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
this.mockServer.restore();
|
||||
});
|
||||
|
||||
var createVideoListView = function() {
|
||||
var model = new MetadataModel(modelStub);
|
||||
return new VideoList({
|
||||
waitForEvent = function() {
|
||||
var triggerCallArgs;
|
||||
return jasmine.waitUntil(function() {
|
||||
triggerCallArgs = Backbone.trigger.calls.mostRecent().args;
|
||||
return Backbone.trigger.calls.count() === 1 &&
|
||||
triggerCallArgs[0] === 'transcripts:basicTabFieldChanged';
|
||||
});
|
||||
};
|
||||
|
||||
createVideoListView = function(mockServer) {
|
||||
var $container, editor, model, videoListView;
|
||||
|
||||
appendSetFixtures(
|
||||
sandbox({ // eslint-disable-line no-undef
|
||||
class: 'wrapper-comp-settings basic_metadata_edit',
|
||||
'data-metadata': JSON.stringify({video_url: modelStub, edx_video_id: videoIDStub})
|
||||
})
|
||||
);
|
||||
|
||||
$container = $('.basic_metadata_edit');
|
||||
editor = new Editor({
|
||||
el: $container
|
||||
});
|
||||
|
||||
spyOn(editor, 'getLocator').and.returnValue(component_locator);
|
||||
|
||||
// reset
|
||||
Backbone.trigger.calls.reset();
|
||||
mockServer.requests.length = 0;
|
||||
|
||||
model = new MetadataModel(modelStub);
|
||||
videoListView = new VideoList({
|
||||
el: $('.component'),
|
||||
model: model,
|
||||
MessageManager: MessageManager
|
||||
});
|
||||
|
||||
waitForEvent()
|
||||
.then(function() {
|
||||
return true;
|
||||
});
|
||||
|
||||
return videoListView;
|
||||
};
|
||||
|
||||
var waitsForResponse = function(mockServer) {
|
||||
@@ -174,36 +216,46 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
|
||||
|
||||
it('Initialize', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer), callArgs;
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
expect(abstractEditor.initialize).toHaveBeenCalled();
|
||||
expect(messenger.initialize).toHaveBeenCalled();
|
||||
expect(view.component_locator).toBe(component_locator);
|
||||
expect(view.$el).toHandle('input');
|
||||
}).always(done);
|
||||
.then(function() {
|
||||
expect(abstractEditor.initialize).toHaveBeenCalled();
|
||||
expect(MessageManager.prototype.initialize).toHaveBeenCalled();
|
||||
expect(view.component_locator).toBe(component_locator);
|
||||
expect(view.$el).toHandle('input');
|
||||
callArgs = view.$el.on.calls.mostRecent().args;
|
||||
expect(callArgs[0]).toEqual('input');
|
||||
expect(callArgs[1]).toEqual('.videolist-settings-item input');
|
||||
}).always(done);
|
||||
});
|
||||
|
||||
describe('Render', function() {
|
||||
var assertToHaveBeenRendered = function(videoList) {
|
||||
expect(abstractEditor.render).toHaveBeenCalled();
|
||||
expect(Utils.command).toHaveBeenCalledWith(
|
||||
'check',
|
||||
component_locator,
|
||||
videoList
|
||||
);
|
||||
var assertToHaveBeenRendered = function(expectedVideoList) {
|
||||
var commandCallArgs = Utils.command.calls.mostRecent().args,
|
||||
actualVideoList = commandCallArgs[2].slice(0, expectedVideoList.length);
|
||||
|
||||
expect(messenger.render).toHaveBeenCalled();
|
||||
expect(commandCallArgs[0]).toEqual('check');
|
||||
expect(commandCallArgs[1]).toEqual(component_locator);
|
||||
_.each([0, 1, 2], function(index) {
|
||||
expect(_.isEqual(expectedVideoList[index], actualVideoList[index])).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(abstractEditor.render).toHaveBeenCalled();
|
||||
expect(MessageManager.prototype.render).toHaveBeenCalled();
|
||||
},
|
||||
resetSpies = function(mockServer) {
|
||||
abstractEditor.render.calls.reset();
|
||||
Utils.command.calls.reset();
|
||||
messenger.render.calls.reset();
|
||||
mockServer.requests.length = 0;
|
||||
MessageManager.prototype.render.calls.reset();
|
||||
mockServer.requests.length = 0; // eslint-disable-line no-param-reassign
|
||||
};
|
||||
|
||||
afterEach(function() {
|
||||
Backbone.trigger('xblock:editorModalHidden');
|
||||
});
|
||||
|
||||
it('is rendered in correct way', function(done) {
|
||||
createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
assertToHaveBeenRendered(videoList);
|
||||
@@ -212,7 +264,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('is rendered with opened extra videos bar', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
var videoListLength = [
|
||||
{
|
||||
mode: 'youtube',
|
||||
@@ -233,8 +285,8 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
}
|
||||
];
|
||||
|
||||
spyOn(view, 'getVideoObjectsList').and.returnValue(videoListLength);
|
||||
spyOn(view, 'openExtraVideosBar');
|
||||
spyOn(VideoList.prototype, 'getVideoObjectsList').and.returnValue(videoListLength);
|
||||
spyOn(VideoList.prototype, 'openExtraVideosBar');
|
||||
|
||||
resetSpies(this.mockServer);
|
||||
view.render();
|
||||
@@ -260,7 +312,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('is rendered without opened extra videos bar', function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
videoList = [
|
||||
{
|
||||
mode: 'youtube',
|
||||
@@ -269,8 +321,8 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
}
|
||||
];
|
||||
|
||||
spyOn(view, 'getVideoObjectsList').and.returnValue(videoList);
|
||||
spyOn(view, 'closeExtraVideosBar');
|
||||
spyOn(VideoList.prototype, 'getVideoObjectsList').and.returnValue(videoList);
|
||||
spyOn(VideoList.prototype, 'closeExtraVideosBar');
|
||||
|
||||
resetSpies(this.mockServer);
|
||||
view.render();
|
||||
@@ -286,7 +338,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
|
||||
describe('isUniqOtherVideos', function() {
|
||||
it('Unique data - return true', function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
data = videoList.concat([{
|
||||
mode: 'html5',
|
||||
type: 'other',
|
||||
@@ -302,7 +354,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('Not Unique data - return false', function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
data = [
|
||||
{
|
||||
mode: 'html5',
|
||||
@@ -342,7 +394,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
|
||||
describe('isUniqVideoTypes', function() {
|
||||
it('Unique data - return true', function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
data = videoList;
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
@@ -354,7 +406,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('Not Unique data - return false', function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
data = [
|
||||
{
|
||||
mode: 'html5',
|
||||
@@ -389,7 +441,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
|
||||
describe('checkIsUniqVideoTypes', function() {
|
||||
it('Error is shown', function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
data = [
|
||||
{
|
||||
mode: 'html5',
|
||||
@@ -417,14 +469,14 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
.then(function() {
|
||||
var result = view.checkIsUniqVideoTypes(data);
|
||||
|
||||
expect(messenger.showError).toHaveBeenCalled();
|
||||
expect(MessageManager.prototype.showError).toHaveBeenCalled();
|
||||
expect(result).toBe(false);
|
||||
})
|
||||
.always(done);
|
||||
});
|
||||
|
||||
it('All works okay if arguments are not passed', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
spyOn(view, 'getVideoObjectsList').and.returnValue(videoList);
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
@@ -432,7 +484,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
var result = view.checkIsUniqVideoTypes();
|
||||
|
||||
expect(view.getVideoObjectsList).toHaveBeenCalled();
|
||||
expect(messenger.showError).not.toHaveBeenCalled();
|
||||
expect(MessageManager.prototype.showError).not.toHaveBeenCalled();
|
||||
expect(result).toBe(true);
|
||||
})
|
||||
.always(done);
|
||||
@@ -441,7 +493,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
|
||||
describe('checkValidity', function() {
|
||||
it('Error message is shown', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
spyOn(view, 'checkIsUniqVideoTypes').and.returnValue(true);
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
@@ -449,7 +501,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
var data = {mode: 'incorrect'},
|
||||
result = view.checkValidity(data, true);
|
||||
|
||||
expect(messenger.showError).toHaveBeenCalled();
|
||||
expect(MessageManager.prototype.showError).toHaveBeenCalled();
|
||||
expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
|
||||
expect(result).toBe(false);
|
||||
})
|
||||
@@ -457,7 +509,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('Error message is shown when flag is not passed', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
spyOn(view, 'checkIsUniqVideoTypes').and.returnValue(true);
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
@@ -465,14 +517,14 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
var data = {mode: 'incorrect'},
|
||||
result = view.checkValidity(data);
|
||||
|
||||
expect(messenger.showError).not.toHaveBeenCalled();
|
||||
expect(MessageManager.prototype.showError).not.toHaveBeenCalled();
|
||||
expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
|
||||
expect(result).toBe(true);
|
||||
}).always(done);
|
||||
});
|
||||
|
||||
it('All works okay if correct data is passed', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
spyOn(view, 'checkIsUniqVideoTypes').and.returnValue(true);
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
@@ -480,7 +532,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
var data = videoList,
|
||||
result = view.checkValidity(data);
|
||||
|
||||
expect(messenger.showError).not.toHaveBeenCalled();
|
||||
expect(MessageManager.prototype.showError).not.toHaveBeenCalled();
|
||||
expect(view.checkIsUniqVideoTypes).toHaveBeenCalled();
|
||||
expect(result).toBe(true);
|
||||
})
|
||||
@@ -489,7 +541,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('openExtraVideosBar', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
view.$extraVideosBar.removeClass('is-visible');
|
||||
@@ -500,7 +552,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('closeExtraVideosBar', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
view.$extraVideosBar.addClass('is-visible');
|
||||
@@ -512,7 +564,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('toggleExtraVideosBar', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
view.$extraVideosBar.addClass('is-visible');
|
||||
@@ -525,7 +577,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('getValueFromEditor', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
expect(view).assertValueInView(modelStub.value);
|
||||
@@ -534,7 +586,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('setValueInEditor', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
expect(view).assertCanUpdateView(['abc.mp4']);
|
||||
@@ -543,7 +595,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
});
|
||||
|
||||
it('getVideoObjectsList', function(done) {
|
||||
var view = createVideoListView();
|
||||
var view = createVideoListView(this.mockServer);
|
||||
var value = [
|
||||
{
|
||||
mode: 'youtube',
|
||||
@@ -577,7 +629,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
|
||||
describe('getPlaceholders', function() {
|
||||
it('All works okay if empty values are passed', function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
defaultPlaceholders = view.placeholders;
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
@@ -593,7 +645,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
it('On filling less than 3 fields, remaining fields should have ' +
|
||||
'placeholders for video types that were not filled yet',
|
||||
function(done) {
|
||||
var view = createVideoListView(),
|
||||
var view = createVideoListView(this.mockServer),
|
||||
defaultPlaceholders = view.placeholders;
|
||||
var dataDict = {
|
||||
youtube: {
|
||||
@@ -640,7 +692,7 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
var eventObject;
|
||||
|
||||
var resetSpies = function(view) {
|
||||
messenger.hideError.calls.reset();
|
||||
MessageManager.prototype.hideError.calls.reset();
|
||||
view.updateModel.calls.reset();
|
||||
view.closeExtraVideosBar.calls.reset();
|
||||
};
|
||||
@@ -660,100 +712,77 @@ function($, _, AjaxHelpers, Utils, VideoList, MetadataModel, AbstractEditor) {
|
||||
resetSpies(view);
|
||||
};
|
||||
|
||||
it('Field has invalid value - nothing should happen',
|
||||
function(done) {
|
||||
var view = createVideoListView();
|
||||
setUp(view);
|
||||
$.fn.hasClass.and.returnValue(false);
|
||||
view.checkValidity.and.returnValue(false);
|
||||
var videoListView = function() {
|
||||
return new VideoList({
|
||||
el: $('.component'),
|
||||
model: new MetadataModel(modelStub),
|
||||
MessageManager: MessageManager
|
||||
});
|
||||
};
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
view.inputHandler(eventObject);
|
||||
expect(messenger.hideError).not.toHaveBeenCalled();
|
||||
expect(view.updateModel).not.toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith(
|
||||
'disabled', true
|
||||
);
|
||||
expect($.fn.addClass).toHaveBeenCalledWith(
|
||||
'is-disabled'
|
||||
);
|
||||
})
|
||||
.always(done);
|
||||
}
|
||||
);
|
||||
beforeEach(function() {
|
||||
MessageManager.prototype.render.and.callFake(function() { return true; });
|
||||
});
|
||||
|
||||
it('Main field has invalid value - extra Videos Bar is closed',
|
||||
function(done) {
|
||||
var view = createVideoListView();
|
||||
setUp(view);
|
||||
$.fn.hasClass.and.returnValue(true);
|
||||
view.checkValidity.and.returnValue(false);
|
||||
afterEach(function() {
|
||||
MessageManager.prototype.render.and.callThrough();
|
||||
});
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
view.inputHandler(eventObject);
|
||||
expect(messenger.hideError).not.toHaveBeenCalled();
|
||||
expect(view.updateModel).not.toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith(
|
||||
'disabled', true
|
||||
);
|
||||
expect($.fn.addClass).toHaveBeenCalledWith(
|
||||
'is-disabled'
|
||||
);
|
||||
})
|
||||
.always(done);
|
||||
}
|
||||
);
|
||||
it('Field has invalid value - nothing should happen', function() {
|
||||
var view = videoListView();
|
||||
setUp(view);
|
||||
$.fn.hasClass.and.returnValue(false);
|
||||
view.checkValidity.and.returnValue(false);
|
||||
|
||||
it('Model is updated if value is valid',
|
||||
function(done) {
|
||||
var view = createVideoListView();
|
||||
setUp(view);
|
||||
view.checkValidity.and.returnValue(true);
|
||||
_.isEqual.and.returnValue(false);
|
||||
view.inputHandler(eventObject);
|
||||
expect(MessageManager.prototype.hideError).not.toHaveBeenCalled();
|
||||
expect(view.updateModel).not.toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith('disabled', true);
|
||||
expect($.fn.addClass).toHaveBeenCalledWith('is-disabled');
|
||||
});
|
||||
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
view.inputHandler(eventObject);
|
||||
expect(messenger.hideError).not.toHaveBeenCalled();
|
||||
expect(view.updateModel).toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith(
|
||||
'disabled', false
|
||||
);
|
||||
expect($.fn.removeClass).toHaveBeenCalledWith(
|
||||
'is-disabled'
|
||||
);
|
||||
})
|
||||
.always(done);
|
||||
}
|
||||
);
|
||||
it('Main field has invalid value - extra Videos Bar is closed', function() {
|
||||
var view = videoListView();
|
||||
setUp(view);
|
||||
$.fn.hasClass.and.returnValue(true);
|
||||
view.checkValidity.and.returnValue(false);
|
||||
|
||||
it('Corner case: Error is hided',
|
||||
function(done) {
|
||||
var view = createVideoListView();
|
||||
setUp(view);
|
||||
view.checkValidity.and.returnValue(true);
|
||||
_.isEqual.and.returnValue(true);
|
||||
waitsForResponse(this.mockServer)
|
||||
.then(function() {
|
||||
view.inputHandler(eventObject);
|
||||
expect(messenger.hideError).toHaveBeenCalled();
|
||||
expect(view.updateModel).not.toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith(
|
||||
'disabled', false
|
||||
);
|
||||
expect($.fn.removeClass).toHaveBeenCalledWith(
|
||||
'is-disabled'
|
||||
);
|
||||
})
|
||||
.always(done);
|
||||
}
|
||||
);
|
||||
view.inputHandler(eventObject);
|
||||
expect(MessageManager.prototype.hideError).not.toHaveBeenCalled();
|
||||
expect(view.updateModel).not.toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith('disabled', true);
|
||||
expect($.fn.addClass).toHaveBeenCalledWith('is-disabled');
|
||||
});
|
||||
|
||||
it('Model is updated if value is valid', function() {
|
||||
var view = videoListView();
|
||||
setUp(view);
|
||||
view.checkValidity.and.returnValue(true);
|
||||
_.isEqual.and.returnValue(false);
|
||||
|
||||
view.inputHandler(eventObject);
|
||||
expect(MessageManager.prototype.hideError).not.toHaveBeenCalled();
|
||||
expect(view.updateModel).toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith('disabled', false);
|
||||
expect($.fn.removeClass).toHaveBeenCalledWith('is-disabled');
|
||||
});
|
||||
|
||||
it('Corner case: Error is hided', function() {
|
||||
var view = videoListView();
|
||||
setUp(view);
|
||||
view.checkValidity.and.returnValue(true);
|
||||
_.isEqual.and.returnValue(true);
|
||||
|
||||
view.inputHandler(eventObject);
|
||||
expect(MessageManager.prototype.hideError).toHaveBeenCalled();
|
||||
expect(view.updateModel).not.toHaveBeenCalled();
|
||||
expect(view.closeExtraVideosBar).not.toHaveBeenCalled();
|
||||
expect($.fn.prop).toHaveBeenCalledWith('disabled', false);
|
||||
expect($.fn.removeClass).toHaveBeenCalledWith('is-disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
define(["js/models/metadata", "js/collections/metadata", "js/views/metadata", "cms/js/main"],
|
||||
function(MetadataModel, MetadataCollection, MetadataView, main) {
|
||||
define(["underscore", "js/models/metadata", "js/collections/metadata", "js/views/metadata", "cms/js/main",
|
||||
"js/views/video/transcripts/utils", 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'],
|
||||
function(_, MetadataModel, MetadataCollection, MetadataView, main, TranscriptUtils, AjaxHelpers) {
|
||||
const verifyInputType = function(input, expectedType) {
|
||||
// Some browsers (e.g. FireFox) do not support the "number"
|
||||
// input type. We can accept a "text" input instead
|
||||
@@ -43,6 +44,8 @@ function(MetadataModel, MetadataCollection, MetadataView, main) {
|
||||
value: "Word cloud"
|
||||
};
|
||||
|
||||
const videoIDEntry = _.extend({}, genericEntry, {field_name: "edx_video_id", type: "VideoID"});
|
||||
|
||||
const selectEntry = {
|
||||
default_value: "answered",
|
||||
display_name: "Show Answer",
|
||||
@@ -271,6 +274,51 @@ function(MetadataModel, MetadataCollection, MetadataView, main) {
|
||||
});
|
||||
});
|
||||
|
||||
describe("MetadataView.VideoID", function() {
|
||||
var waitForMock;
|
||||
|
||||
waitForMock = function(mock) {
|
||||
return jasmine.waitUntil(function() {
|
||||
return mock.calls.count() === 1;
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
const model = new MetadataModel(videoIDEntry);
|
||||
spyOn(TranscriptUtils.Storage, 'set');
|
||||
this.view = new MetadataView.VideoID({model});
|
||||
spyOn(Backbone, 'trigger');
|
||||
expect(TranscriptUtils.Storage.set).toHaveBeenCalledWith('edx_video_id', this.view.getValueFromEditor());
|
||||
});
|
||||
|
||||
it("triggers correct event on input change", function(done) {
|
||||
// change value and trigger input event
|
||||
this.view.$el.find('input').val("1234-5678-90").trigger('input');
|
||||
waitForMock(Backbone.trigger)
|
||||
.then(function() {
|
||||
expect(Backbone.trigger).toHaveBeenCalledWith('transcripts:basicTabFieldChanged');
|
||||
})
|
||||
.always(done);
|
||||
});
|
||||
|
||||
it("triggers correct event on clear", function(done) {
|
||||
this.view.clear();
|
||||
waitForMock(Backbone.trigger)
|
||||
.then(function() {
|
||||
expect(Backbone.trigger).toHaveBeenCalledWith('transcripts:basicTabFieldChanged');
|
||||
})
|
||||
.always(done);
|
||||
});
|
||||
|
||||
it("constructs correct data", function() {
|
||||
expect(
|
||||
this.view.getData()
|
||||
).toEqual(
|
||||
[{mode: 'edx_video_id', type: 'edx_video_id', video: this.view.getValueFromEditor()}]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("MetadataView.Option is an option input type with clear functionality", function() {
|
||||
beforeEach(function() {
|
||||
const model = new MetadataModel(selectEntry);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/spec_helpers/edit_helpers',
|
||||
'js/views/modals/edit_xblock', 'js/models/xblock_info'],
|
||||
function($, _, AjaxHelpers, EditHelpers, EditXBlockModal, XBlockInfo) {
|
||||
define(['jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
|
||||
'js/spec_helpers/edit_helpers', 'js/views/modals/edit_xblock', 'js/models/xblock_info'],
|
||||
function($, _, Backbone, AjaxHelpers, EditHelpers, EditXBlockModal, XBlockInfo) {
|
||||
'use strict';
|
||||
describe('EditXBlockModal', function() {
|
||||
var model, modal, showModal;
|
||||
|
||||
@@ -30,6 +31,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
|
||||
beforeEach(function() {
|
||||
EditHelpers.installMockXBlock();
|
||||
spyOn(Backbone, 'trigger').and.callThrough();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
@@ -74,6 +76,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
modal.editorView.notifyRuntime('save', {state: 'end'});
|
||||
expect(EditHelpers.isShowingModal(modal)).toBeFalsy();
|
||||
expect(refreshed).toBeTruthy();
|
||||
expect(Backbone.trigger).toHaveBeenCalledWith('xblock:editorModalHidden');
|
||||
});
|
||||
|
||||
it('hides itself and does not refresh after cancel notification', function() {
|
||||
@@ -86,6 +89,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
modal.editorView.notifyRuntime('cancel');
|
||||
expect(EditHelpers.isShowingModal(modal)).toBeFalsy();
|
||||
expect(refreshed).toBeFalsy();
|
||||
expect(Backbone.trigger).toHaveBeenCalledWith('xblock:editorModalHidden');
|
||||
});
|
||||
|
||||
describe('Custom Buttons', function() {
|
||||
|
||||
@@ -11,7 +11,8 @@ define(
|
||||
duration: 42,
|
||||
created: '2014-11-25T23:13:05',
|
||||
edx_video_id: 'dummy_id',
|
||||
status: 'uploading'
|
||||
status: 'uploading',
|
||||
transcripts: []
|
||||
};
|
||||
var collection = new Backbone.Collection(
|
||||
_.map(
|
||||
@@ -26,6 +27,9 @@ define(
|
||||
var view = new PreviousVideoUploadListView({
|
||||
collection: collection,
|
||||
videoHandlerUrl: videoHandlerUrl,
|
||||
transcriptAvailableLanguages: [],
|
||||
videoSupportedFileFormats: [],
|
||||
videoTranscriptSettings: {},
|
||||
videoImageSettings: {}
|
||||
});
|
||||
return view.render().$el;
|
||||
|
||||
@@ -10,11 +10,15 @@ define(
|
||||
duration: 42,
|
||||
created: '2014-11-25T23:13:05',
|
||||
edx_video_id: 'dummy_id',
|
||||
status: 'uploading'
|
||||
status: 'uploading',
|
||||
transcripts: []
|
||||
},
|
||||
view = new PreviousVideoUploadView({
|
||||
model: new Backbone.Model($.extend({}, defaultData, modelData)),
|
||||
videoHandlerUrl: '/videos/course-v1:org.0+course_0+Run_0',
|
||||
transcriptAvailableLanguages: [],
|
||||
videoSupportedFileFormats: [],
|
||||
videoTranscriptSettings: {},
|
||||
videoImageSettings: {}
|
||||
});
|
||||
return view.render().$el;
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
define(["sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter",
|
||||
define(["underscore", "sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter",
|
||||
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/spec_helpers/modal_helpers"],
|
||||
(sinon, FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) =>
|
||||
(_, sinon, FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) =>
|
||||
|
||||
describe("UploadDialog", function() {
|
||||
const tpl = readFixtures("upload-dialog.underscore");
|
||||
const tpl = readFixtures("upload-dialog.underscore"),
|
||||
uploadData = {
|
||||
edx_video_id: '123-456-789-0',
|
||||
language_code: 'en',
|
||||
new_language_code: 'ur'
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
let dialogResponse;
|
||||
@@ -27,7 +32,8 @@ define(["sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter",
|
||||
url: CMS.URL.UPLOAD_ASSET,
|
||||
onSuccess: response => {
|
||||
return test.dialogResponse.push(response.response);
|
||||
}
|
||||
},
|
||||
uploadData: uploadData
|
||||
});
|
||||
spyOn(view, 'remove').and.callThrough();
|
||||
|
||||
@@ -37,6 +43,7 @@ define(["sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter",
|
||||
const jqMockFileInput = jasmine.createSpyObj('jqMockFileInput', ['get', 'replaceWith']);
|
||||
jqMockFileInput.get.and.returnValue(mockFileInput);
|
||||
const originalView$ = view.$;
|
||||
spyOn($.fn, 'ajaxSubmit').and.callThrough();
|
||||
spyOn(view, "$").and.callFake(function(selector) {
|
||||
if (selector === "input[type=file]") {
|
||||
return jqMockFileInput;
|
||||
@@ -126,6 +133,9 @@ define(["sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter",
|
||||
view.upload();
|
||||
expect(this.model.get("uploading")).toBeTruthy();
|
||||
AjaxHelpers.expectRequest(requests, "POST", "/upload");
|
||||
expect($.fn.ajaxSubmit.calls.mostRecent().args[0].data).toEqual(
|
||||
_.extend({}, uploadData, {notifyOnError: false})
|
||||
);
|
||||
AjaxHelpers.respondWithJson(requests, { response: "dummy_response"});
|
||||
expect(this.model.get("uploading")).toBeFalsy();
|
||||
expect(this.model.get("finished")).toBeTruthy();
|
||||
|
||||
@@ -43,7 +43,8 @@ define(
|
||||
duration: 42,
|
||||
created: '2014-11-25T23:13:05',
|
||||
edx_video_id: 'dummy_id',
|
||||
status: 'uploading'
|
||||
status: 'uploading',
|
||||
transcripts: []
|
||||
},
|
||||
collection = new Backbone.Collection(_.map(_.range(numVideos), function(num, index) {
|
||||
return new Backbone.Model(
|
||||
@@ -61,7 +62,10 @@ define(
|
||||
max_height: VIDEO_IMAGE_MAX_HEIGHT,
|
||||
supported_file_formats: VIDEO_IMAGE_SUPPORTED_FILE_FORMATS,
|
||||
video_image_upload_enabled: videoImageUploadEnabled
|
||||
}
|
||||
},
|
||||
transcriptAvailableLanguages: [],
|
||||
videoSupportedFileFormats: [],
|
||||
videoTranscriptSettings: {}
|
||||
});
|
||||
$videoListEl = videoListView.render().$el;
|
||||
|
||||
|
||||
@@ -93,9 +93,8 @@ define(
|
||||
return new File([new Blob([Array(size).join('i')], {type: type})], transcriptFileName);
|
||||
};
|
||||
|
||||
renderView = function(availableTranscripts, isVideoTranscriptEnabled) {
|
||||
renderView = function(availableTranscripts) {
|
||||
var videoViewIndex = 0,
|
||||
isVideoTranscriptEnabled = isVideoTranscriptEnabled || _.isUndefined(isVideoTranscriptEnabled), // eslint-disable-line max-len, no-redeclare
|
||||
videoData = {
|
||||
client_video_id: clientVideoID,
|
||||
edx_video_id: edxVideoID,
|
||||
@@ -109,8 +108,7 @@ define(
|
||||
videoImageSettings: {},
|
||||
videoTranscriptSettings: videoTranscriptSettings,
|
||||
transcriptAvailableLanguages: transcriptAvailableLanguages,
|
||||
videoSupportedFileFormats: videoSupportedFileFormats,
|
||||
isVideoTranscriptEnabled: isVideoTranscriptEnabled
|
||||
videoSupportedFileFormats: videoSupportedFileFormats
|
||||
});
|
||||
videoListView.setElement($('.wrapper-assets'));
|
||||
videoListView.render();
|
||||
@@ -139,18 +137,6 @@ define(
|
||||
expect(_.isUndefined(videoTranscriptsView)).toEqual(false);
|
||||
});
|
||||
|
||||
it('does not render transcripts view if feature is disabled', function() {
|
||||
renderView(transcripts, false);
|
||||
// Verify transcript container is not present.
|
||||
expect(videoListView.$el.find('.video-transcripts-header')).not.toExist();
|
||||
// Veirfy transcript column header is not present.
|
||||
expect(videoListView.$el.find('.js-table-head .video-head-col.transcripts-col')).not.toExist();
|
||||
// Verify transcript data column is not present.
|
||||
expect(videoListView.$el.find('.js-table-body .transcripts-col')).not.toExist();
|
||||
// Verify view has not initiallized.
|
||||
expect(_.isUndefined(videoTranscriptsView)).toEqual(true);
|
||||
});
|
||||
|
||||
it('does not show list of transcripts initially', function() {
|
||||
expect(
|
||||
videoTranscriptsView.$el.find('.video-transcripts-wrapper').hasClass('hidden')
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'js/views/baseview', 'underscore', 'js/models/metadata', 'js/views/abstract_editor',
|
||||
'js/models/uploads', 'js/views/uploads',
|
||||
'js/models/license', 'js/views/license',
|
||||
'js/views/video/transcripts/utils',
|
||||
'js/views/video/transcripts/metadata_videolist',
|
||||
'js/views/video/translations_editor'
|
||||
],
|
||||
function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog,
|
||||
LicenseModel, LicenseView, VideoList, VideoTranslations) {
|
||||
function(Backbone, BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog,
|
||||
LicenseModel, LicenseView, TranscriptUtils, VideoList, VideoTranslations) {
|
||||
'use strict';
|
||||
var Metadata = {};
|
||||
|
||||
Metadata.Editor = BaseView.extend({
|
||||
// Store rendered view references
|
||||
views: {},
|
||||
|
||||
// Model is CMS.Models.MetadataCollection,
|
||||
initialize: function() {
|
||||
@@ -42,10 +47,10 @@ function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog,
|
||||
}
|
||||
|
||||
if (_.isFunction(Metadata[type])) {
|
||||
new Metadata[type](data);
|
||||
self.views[data.model.getFieldName()] = new Metadata[type](data);
|
||||
} else {
|
||||
// Everything else is treated as GENERIC_TYPE, which uses String editor.
|
||||
new Metadata.String(data);
|
||||
self.views[data.model.getFieldName()] = new Metadata.String(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -120,6 +125,40 @@ function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog,
|
||||
}
|
||||
});
|
||||
|
||||
Metadata.VideoID = Metadata.String.extend({
|
||||
// Delay between check_transcript requests
|
||||
requestDelay: 300,
|
||||
|
||||
initialize: function() {
|
||||
Metadata.String.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.$el.on(
|
||||
'input',
|
||||
'input',
|
||||
_.debounce(_.bind(this.inputChange, this), this.requestDelay)
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
Metadata.String.prototype.render.apply(this, arguments);
|
||||
TranscriptUtils.Storage.set('edx_video_id', this.getValueFromEditor());
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.model.setValue('');
|
||||
this.inputChange();
|
||||
},
|
||||
|
||||
getData: function() {
|
||||
return [{mode: 'edx_video_id', type: 'edx_video_id', video: this.getValueFromEditor()}];
|
||||
},
|
||||
|
||||
inputChange: function() {
|
||||
TranscriptUtils.Storage.set('edx_video_id', this.getValueFromEditor());
|
||||
Backbone.trigger('transcripts:basicTabFieldChanged');
|
||||
}
|
||||
});
|
||||
|
||||
Metadata.Number = AbstractEditor.extend({
|
||||
|
||||
events: {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* It is invoked using the edit method which is passed an existing rendered xblock,
|
||||
* and upon save an optional refresh function can be invoked to update the display.
|
||||
*/
|
||||
define(['jquery', 'underscore', 'gettext', 'js/views/modals/base_modal', 'common/js/components/utils/view_utils',
|
||||
'js/views/utils/xblock_utils', 'js/views/xblock_editor'],
|
||||
function($, _, gettext, BaseModal, ViewUtils, XBlockViewUtils, XBlockEditorView) {
|
||||
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/modals/base_modal',
|
||||
'common/js/components/utils/view_utils', 'js/views/utils/xblock_utils', 'js/views/xblock_editor'],
|
||||
function($, _, Backbone, gettext, BaseModal, ViewUtils, XBlockViewUtils, XBlockEditorView) {
|
||||
'use strict';
|
||||
|
||||
var EditXBlockModal = BaseModal.extend({
|
||||
@@ -181,6 +181,9 @@ define(['jquery', 'underscore', 'gettext', 'js/views/modals/base_modal', 'common
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
// Notify child views to stop listening events
|
||||
Backbone.trigger('xblock:editorModalHidden');
|
||||
|
||||
BaseModal.prototype.hide.call(this);
|
||||
|
||||
// Notify the runtime that the modal has been hidden
|
||||
|
||||
@@ -20,7 +20,6 @@ define(
|
||||
this.template = HtmlUtils.template(previousVideoUploadTemplate);
|
||||
this.videoHandlerUrl = options.videoHandlerUrl;
|
||||
this.videoImageUploadEnabled = options.videoImageSettings.video_image_upload_enabled;
|
||||
this.isVideoTranscriptEnabled = options.isVideoTranscriptEnabled;
|
||||
|
||||
if (this.videoImageUploadEnabled) {
|
||||
this.videoThumbnailView = new VideoThumbnailView({
|
||||
@@ -30,22 +29,19 @@ define(
|
||||
videoImageSettings: options.videoImageSettings
|
||||
});
|
||||
}
|
||||
if (this.isVideoTranscriptEnabled) {
|
||||
this.videoTranscriptsView = new VideoTranscriptsView({
|
||||
transcripts: this.model.get('transcripts'),
|
||||
edxVideoID: this.model.get('edx_video_id'),
|
||||
clientVideoID: this.model.get('client_video_id'),
|
||||
transcriptAvailableLanguages: options.transcriptAvailableLanguages,
|
||||
videoSupportedFileFormats: options.videoSupportedFileFormats,
|
||||
videoTranscriptSettings: options.videoTranscriptSettings
|
||||
});
|
||||
}
|
||||
this.videoTranscriptsView = new VideoTranscriptsView({
|
||||
transcripts: this.model.get('transcripts'),
|
||||
edxVideoID: this.model.get('edx_video_id'),
|
||||
clientVideoID: this.model.get('client_video_id'),
|
||||
transcriptAvailableLanguages: options.transcriptAvailableLanguages,
|
||||
videoSupportedFileFormats: options.videoSupportedFileFormats,
|
||||
videoTranscriptSettings: options.videoTranscriptSettings
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var renderedAttributes = {
|
||||
videoImageUploadEnabled: this.videoImageUploadEnabled,
|
||||
isVideoTranscriptEnabled: this.isVideoTranscriptEnabled,
|
||||
created: DateUtils.renderDate(this.model.get('created')),
|
||||
status: this.model.get('status')
|
||||
};
|
||||
@@ -59,9 +55,7 @@ define(
|
||||
if (this.videoImageUploadEnabled) {
|
||||
this.videoThumbnailView.setElement(this.$('.thumbnail-col')).render();
|
||||
}
|
||||
if (this.isVideoTranscriptEnabled) {
|
||||
this.videoTranscriptsView.setElement(this.$('.transcripts-col')).render();
|
||||
}
|
||||
this.videoTranscriptsView.setElement(this.$('.transcripts-col')).render();
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ define(
|
||||
this.template = HtmlUtils.template(previousVideoUploadListTemplate);
|
||||
this.encodingsDownloadUrl = options.encodingsDownloadUrl;
|
||||
this.videoImageUploadEnabled = options.videoImageSettings.video_image_upload_enabled;
|
||||
this.isVideoTranscriptEnabled = options.isVideoTranscriptEnabled;
|
||||
this.itemViews = this.collection.map(function(model) {
|
||||
return new PreviousVideoUploadView({
|
||||
videoImageUploadURL: options.videoImageUploadURL,
|
||||
@@ -21,8 +20,7 @@ define(
|
||||
videoTranscriptSettings: options.videoTranscriptSettings,
|
||||
model: model,
|
||||
transcriptAvailableLanguages: options.transcriptAvailableLanguages,
|
||||
videoSupportedFileFormats: options.videoSupportedFileFormats,
|
||||
isVideoTranscriptEnabled: options.isVideoTranscriptEnabled
|
||||
videoSupportedFileFormats: options.videoSupportedFileFormats
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -35,8 +33,7 @@ define(
|
||||
this.$el,
|
||||
this.template({
|
||||
encodingsDownloadUrl: this.encodingsDownloadUrl,
|
||||
videoImageUploadEnabled: this.videoImageUploadEnabled,
|
||||
isVideoTranscriptEnabled: this.isVideoTranscriptEnabled
|
||||
videoImageUploadEnabled: this.videoImageUploadEnabled
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -13,11 +13,14 @@ define(['jquery', 'underscore', 'gettext', 'js/views/modals/base_modal', 'jquery
|
||||
viewSpecificClasses: 'confirm'
|
||||
}),
|
||||
|
||||
initialize: function() {
|
||||
initialize: function(options) {
|
||||
BaseModal.prototype.initialize.call(this);
|
||||
this.template = this.loadTemplate('upload-dialog');
|
||||
this.listenTo(this.model, 'change', this.renderContents);
|
||||
this.options.title = this.model.get('title');
|
||||
// `uploadData` can contain extra data that
|
||||
// can be POSTed along with the file.
|
||||
this.uploadData = _.extend({}, options.uploadData);
|
||||
},
|
||||
|
||||
addActionButtons: function() {
|
||||
@@ -73,17 +76,19 @@ define(['jquery', 'underscore', 'gettext', 'js/views/modals/base_modal', 'jquery
|
||||
},
|
||||
|
||||
upload: function(e) {
|
||||
|
||||
var uploadAjaxData = _.extend({}, this.uploadData);
|
||||
// don't show the generic error notification; we're in a modal,
|
||||
// and we're better off modifying it instead.
|
||||
uploadAjaxData.notifyOnError = false;
|
||||
|
||||
if (e && e.preventDefault) { e.preventDefault(); }
|
||||
this.model.set('uploading', true);
|
||||
this.$('form').ajaxSubmit({
|
||||
success: _.bind(this.success, this),
|
||||
error: _.bind(this.error, this),
|
||||
uploadProgress: _.bind(this.progress, this),
|
||||
data: {
|
||||
// don't show the generic error notification; we're in a modal,
|
||||
// and we're better off modifying it instead.
|
||||
notifyOnError: false
|
||||
}
|
||||
data: uploadAjaxData
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
|
||||
initialize: function() {
|
||||
// prepare data for MetadataView.Editor
|
||||
|
||||
var metadata = this.$el.data('metadata'),
|
||||
models = this.toModels(metadata);
|
||||
|
||||
@@ -23,6 +22,23 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
el: this.$el,
|
||||
collection: this.collection
|
||||
});
|
||||
|
||||
// Listen to edx_video_id update
|
||||
this.listenTo(Backbone, 'transcripts:basicTabUpdateEdxVideoId', this.handleUpdateEdxVideoId);
|
||||
|
||||
// Listen to `video_url` and `edx_video_id` updates
|
||||
this.listenTo(Backbone, 'transcripts:basicTabFieldChanged', this.handleFieldChanged);
|
||||
|
||||
// Listen to modal hidden event
|
||||
this.listenTo(Backbone, 'xblock:editorModalHidden', this.destroy);
|
||||
|
||||
// Now `video_url` and `edx_video_id` viwes are rendered so
|
||||
// send a `check_transcript` request to get transctip status
|
||||
// This is needed because we need to update the transcrript status
|
||||
// when basic tabs renders. We trigger `basicTabFieldChanged` event
|
||||
// in `video_url` field but that event triggers before event is
|
||||
// actually binded
|
||||
this.handleFieldChanged();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -72,7 +88,6 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
var result = [],
|
||||
getField = Utils.getField,
|
||||
component_locator = this.$el.closest('[data-locator]').data('locator'),
|
||||
subs = getField(metadataCollection, 'sub'),
|
||||
values = {},
|
||||
videoUrl, metadata, modifiedValues;
|
||||
|
||||
@@ -86,37 +101,6 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
|
||||
modifiedValues = metadataView.getModifiedMetadataValues();
|
||||
|
||||
var isSubsModified = (function(values) {
|
||||
var isSubsChanged = subs.hasChanged('value');
|
||||
|
||||
return Boolean(
|
||||
isSubsChanged &&
|
||||
(
|
||||
// If the user changes the field, `values.sub` contains
|
||||
// string value;
|
||||
// If the user clicks `clear` button, the field contains
|
||||
// null value.
|
||||
// Otherwise, undefined.
|
||||
_.isString(values.sub) || _.isNull(subs.getValue())
|
||||
)
|
||||
);
|
||||
}(modifiedValues));
|
||||
|
||||
// When we change value of `sub` field in the `Advanced`,
|
||||
// we update data on backend. That provides possibility to remove
|
||||
// transcripts.
|
||||
if (isSubsModified) {
|
||||
metadata = $.extend(true, {}, modifiedValues);
|
||||
// Save module state
|
||||
Utils.command('save', component_locator, null, {
|
||||
metadata: metadata,
|
||||
current_subs: _.pluck(
|
||||
Utils.getVideoList(videoUrl.getDisplayValue()),
|
||||
'video'
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
// Get values from `Advanced` tab fields (`html5_sources`,
|
||||
// `youtube_id_1_0`) that should be synchronized.
|
||||
var html5Sources = getField(metadataCollection, 'html5_sources').getDisplayValue();
|
||||
@@ -148,17 +132,6 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
|
||||
// Synchronize other fields that has the same `field_name` property.
|
||||
Utils.syncCollections(metadataCollection, this.collection);
|
||||
|
||||
if (isSubsModified) {
|
||||
// When `sub` field is changed, clean Storage to avoid overwriting.
|
||||
Utils.Storage.remove('sub');
|
||||
|
||||
// Trigger `change` event manually if `video_url` model
|
||||
// isn't changed.
|
||||
if (!videoUrl.hasChanged()) {
|
||||
videoUrl.trigger('change');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -174,8 +147,6 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
*/
|
||||
syncAdvancedTab: function(metadataCollection, metadataView) {
|
||||
var getField = Utils.getField,
|
||||
subsValue = Utils.Storage.get('sub'),
|
||||
subs = getField(metadataCollection, 'sub'),
|
||||
html5Sources, youtube, videoUrlValue, result;
|
||||
|
||||
// if metadataCollection is not passed, just exit.
|
||||
@@ -231,18 +202,45 @@ function($, Backbone, _, Utils, MetadataView, MetadataCollection) {
|
||||
youtube.setValue(result);
|
||||
}
|
||||
|
||||
// If Utils.Storage contain some subtitles, update them.
|
||||
if (_.isString(subsValue)) {
|
||||
subs.setValue(subsValue);
|
||||
// After updating should be removed, because it might overwrite
|
||||
// subtitles added by user manually.
|
||||
Utils.Storage.remove('sub');
|
||||
}
|
||||
|
||||
// Synchronize other fields that has the same `field_name` property.
|
||||
Utils.syncCollections(this.collection, metadataCollection);
|
||||
}
|
||||
},
|
||||
|
||||
handleUpdateEdxVideoId: function(edxVideoId) {
|
||||
var edxVideoIdField = Utils.getField(this.collection, 'edx_video_id');
|
||||
edxVideoIdField.setValue(edxVideoId);
|
||||
},
|
||||
|
||||
getLocator: function() {
|
||||
return this.$el.closest('[data-locator]').data('locator');
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for `transcripts:basicTabFieldChanged` event.
|
||||
*/
|
||||
handleFieldChanged: function() {
|
||||
var views = this.settingsView.views,
|
||||
videoURLSView = views.video_url,
|
||||
edxVideoIdView = views.edx_video_id,
|
||||
edxVideoIdData = edxVideoIdView.getData(),
|
||||
videoURLsData = videoURLSView.getVideoObjectsList(),
|
||||
data = videoURLsData.concat(edxVideoIdData),
|
||||
locator = this.getLocator();
|
||||
|
||||
Utils.command('check', locator, data)
|
||||
.done(function(response) {
|
||||
videoURLSView.updateOnCheckTranscriptSuccess(videoURLsData, response);
|
||||
})
|
||||
.fail(function(response) {
|
||||
videoURLSView.showServerError(response);
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.stopListening();
|
||||
this.undelegateEvents();
|
||||
this.$el.empty();
|
||||
}
|
||||
});
|
||||
|
||||
return Editor;
|
||||
|
||||
@@ -3,7 +3,7 @@ define(
|
||||
'jquery', 'backbone', 'underscore',
|
||||
'js/views/video/transcripts/utils'
|
||||
],
|
||||
function($, Backbone, _, Utils) {
|
||||
function($, Backbone, _, TranscriptUtils) {
|
||||
var FileUploader = Backbone.View.extend({
|
||||
invisibleClass: 'is-invisible',
|
||||
|
||||
@@ -29,8 +29,7 @@ function($, Backbone, _, Utils) {
|
||||
|
||||
render: function() {
|
||||
var tpl = $(this.uploadTpl).text(),
|
||||
tplContainer = this.$el.find('.transcripts-file-uploader'),
|
||||
videoList = this.options.videoListObject.getVideoObjectsList();
|
||||
tplContainer = this.$el.find('.transcripts-file-uploader');
|
||||
|
||||
if (tplContainer.length) {
|
||||
if (!tpl) {
|
||||
@@ -42,8 +41,7 @@ function($, Backbone, _, Utils) {
|
||||
|
||||
tplContainer.html(this.template({
|
||||
ext: this.validFileExtensions,
|
||||
component_locator: this.options.component_locator,
|
||||
video_list: videoList
|
||||
component_locator: this.options.component_locator
|
||||
}));
|
||||
|
||||
this.$form = this.$el.find('.file-chooser');
|
||||
@@ -59,6 +57,10 @@ function($, Backbone, _, Utils) {
|
||||
*
|
||||
*/
|
||||
upload: function() {
|
||||
var data = {
|
||||
'edx_video_id': TranscriptUtils.Storage.get('edx_video_id') || ''
|
||||
};
|
||||
|
||||
if (!this.file) {
|
||||
return;
|
||||
}
|
||||
@@ -66,7 +68,8 @@ function($, Backbone, _, Utils) {
|
||||
this.$form.ajaxSubmit({
|
||||
beforeSend: this.xhrResetProgressBar,
|
||||
uploadProgress: this.xhrProgressHandler,
|
||||
complete: this.xhrCompleteHandler
|
||||
complete: this.xhrCompleteHandler,
|
||||
data: data
|
||||
});
|
||||
},
|
||||
|
||||
@@ -186,14 +189,14 @@ function($, Backbone, _, Utils) {
|
||||
xhrCompleteHandler: function(xhr) {
|
||||
var resp = JSON.parse(xhr.responseText),
|
||||
err = resp.status || gettext('Error: Uploading failed.'),
|
||||
sub = resp.subs;
|
||||
edxVideoId = resp.edx_video_id;
|
||||
|
||||
this.$progress
|
||||
.addClass(this.invisibleClass);
|
||||
|
||||
if (xhr.status === 200) {
|
||||
this.options.messenger.render('uploaded', resp);
|
||||
Utils.Storage.set('sub', sub);
|
||||
Backbone.trigger('transcripts:basicTabUpdateEdxVideoId', edxVideoId);
|
||||
} else {
|
||||
this.options.messenger.showError(err);
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ function($, Backbone, _, Utils, FileUploader, gettext) {
|
||||
this.fileUploader = new FileUploader({
|
||||
el: this.$el,
|
||||
messenger: this,
|
||||
component_locator: this.component_locator,
|
||||
videoListObject: this.options.parent
|
||||
component_locator: this.component_locator
|
||||
});
|
||||
},
|
||||
|
||||
@@ -218,10 +217,10 @@ function($, Backbone, _, Utils, FileUploader, gettext) {
|
||||
|
||||
xhr = Utils.command(action, component_locator, videoList, extraParam)
|
||||
.done(function(resp) {
|
||||
var sub = resp.subs;
|
||||
var edxVideoID = resp.edx_video_id;
|
||||
|
||||
self.render('found', resp);
|
||||
Utils.Storage.set('sub', sub);
|
||||
Backbone.trigger('transcripts:basicTabUpdateEdxVideoId', edxVideoID);
|
||||
})
|
||||
.fail(function(resp) {
|
||||
var message = resp.status || errorMessage;
|
||||
|
||||
@@ -43,7 +43,7 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager) {
|
||||
.apply(this, arguments);
|
||||
|
||||
this.$el.on(
|
||||
'input', 'input',
|
||||
'input', '.videolist-settings-item input',
|
||||
_.debounce(_.bind(this.inputHandler, this), this.inputDelay)
|
||||
);
|
||||
|
||||
@@ -56,57 +56,45 @@ function($, Backbone, _, AbstractEditor, Utils, MessageManager) {
|
||||
AbstractEditor.prototype.render
|
||||
.apply(this, arguments);
|
||||
|
||||
var self = this,
|
||||
component_locator = this.$el.closest('[data-locator]')
|
||||
.data('locator'),
|
||||
videoList = this.getVideoObjectsList(),
|
||||
|
||||
showServerError = function(response) {
|
||||
var errorMessage = response.status ||
|
||||
gettext('Error: Connection with server failed.');
|
||||
|
||||
self.messenger
|
||||
.render('not_found')
|
||||
.showError(
|
||||
errorMessage,
|
||||
true // hide buttons
|
||||
);
|
||||
};
|
||||
|
||||
this.$extraVideosBar = this.$el.find('.videolist-extra-videos');
|
||||
|
||||
if (videoList.length === 0) {
|
||||
this.messenger
|
||||
.render('not_found')
|
||||
.showError(
|
||||
gettext('No sources'),
|
||||
true // hide buttons
|
||||
);
|
||||
// Check current state of Timed Transcripts.
|
||||
Backbone.trigger('transcripts:basicTabFieldChanged');
|
||||
},
|
||||
|
||||
return void(0);
|
||||
updateOnCheckTranscriptSuccess: function(videoList, response) {
|
||||
var params = response,
|
||||
len = videoList.length,
|
||||
mode = (len === 1) ? videoList[0].mode : false;
|
||||
|
||||
// If there are more than 1 video or just html5 source is
|
||||
// passed, video sources box should expand
|
||||
if (len > 1 || mode === 'html5') {
|
||||
this.openExtraVideosBar();
|
||||
} else {
|
||||
this.closeExtraVideosBar();
|
||||
}
|
||||
|
||||
// Check current state of Timed Transcripts.
|
||||
Utils.command('check', component_locator, videoList)
|
||||
.done(function(resp) {
|
||||
var params = resp,
|
||||
len = videoList.length,
|
||||
mode = (len === 1) ? videoList[0].mode : false;
|
||||
this.messenger.render(response.command, params);
|
||||
this.checkIsUniqVideoTypes();
|
||||
},
|
||||
|
||||
// If there are more than 1 video or just html5 source is
|
||||
// passed, video sources box should expand
|
||||
if (len > 1 || mode === 'html5') {
|
||||
self.openExtraVideosBar();
|
||||
} else {
|
||||
self.closeExtraVideosBar();
|
||||
}
|
||||
/**
|
||||
* Updates the message with error.
|
||||
*/
|
||||
showServerError: function(response) {
|
||||
var errorMessage = gettext('Error: Connection with server failed.');
|
||||
|
||||
self.messenger.render(resp.command, params);
|
||||
self.checkIsUniqVideoTypes();
|
||||
// Synchronize transcripts field in the `Advanced` tab.
|
||||
Utils.Storage.set('sub', resp.subs);
|
||||
})
|
||||
.fail(showServerError);
|
||||
if (response.responseJSON !== undefined) {
|
||||
errorMessage = response.responseJSON.status;
|
||||
}
|
||||
|
||||
this.messenger
|
||||
.render('not_found')
|
||||
.showError(
|
||||
errorMessage,
|
||||
true // hide buttons
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
define(
|
||||
[
|
||||
'jquery', 'underscore',
|
||||
'js/views/abstract_editor', 'js/models/uploads', 'js/views/uploads'
|
||||
|
||||
'jquery', 'underscore', 'edx-ui-toolkit/js/utils/html-utils', 'js/views/video/transcripts/utils',
|
||||
'js/views/abstract_editor', 'common/js/components/utils/view_utils', 'js/models/uploads', 'js/views/uploads'
|
||||
],
|
||||
function($, _, AbstractEditor, FileUpload, UploadDialog) {
|
||||
function($, _, HtmlUtils, TranscriptUtils, AbstractEditor, ViewUtils, FileUpload, UploadDialog) {
|
||||
'use strict';
|
||||
|
||||
var VideoUploadDialog = UploadDialog.extend({
|
||||
@@ -19,7 +18,6 @@ function($, _, AbstractEditor, FileUpload, UploadDialog) {
|
||||
|
||||
var Translations = AbstractEditor.extend({
|
||||
events: {
|
||||
'click .setting-clear': 'clear',
|
||||
'click .create-setting': 'addEntry',
|
||||
'click .remove-setting': 'removeEntry',
|
||||
'click .upload-setting': 'upload',
|
||||
@@ -29,15 +27,29 @@ function($, _, AbstractEditor, FileUpload, UploadDialog) {
|
||||
templateName: 'metadata-translations-entry',
|
||||
templateItemName: 'metadata-translations-item',
|
||||
|
||||
validFileFormats: ['srt'],
|
||||
|
||||
initialize: function() {
|
||||
var templateName = _.result(this, 'templateItemName'),
|
||||
tpl = document.getElementById(templateName).text;
|
||||
tpl = document.getElementById(templateName).text,
|
||||
languageMap = {};
|
||||
|
||||
if (!tpl) {
|
||||
console.error("Couldn't load template for item: " + templateName);
|
||||
}
|
||||
|
||||
this.templateItem = _.template(tpl);
|
||||
|
||||
// Initialize language map. This maps original language to the newly selected language.
|
||||
// Keys in this map represent language codes present on server, they don't change when
|
||||
// user selects a language while values represent currently selected language.
|
||||
// Initially, the map will look like {'ar': 'ar', 'zh': 'zh'} i.e {'original_lang': 'original_lang'}
|
||||
// and corresponding dropdowns will show language names Arabic and Chinese. If user changes
|
||||
// Chinese to Russian then map will become {'ar': 'ar', 'zh': 'ru'} i.e {'original_lang': 'new_lang'}
|
||||
_.each(this.model.getDisplayValue(), function(value, lang) {
|
||||
languageMap[lang] = lang;
|
||||
});
|
||||
TranscriptUtils.Storage.set('languageMap', languageMap);
|
||||
AbstractEditor.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
@@ -111,14 +123,16 @@ function($, _, AbstractEditor, FileUpload, UploadDialog) {
|
||||
setValueInEditor: function(values) {
|
||||
var self = this,
|
||||
frag = document.createDocumentFragment(),
|
||||
dropdown = self.getDropdown(values);
|
||||
dropdown = self.getDropdown(values),
|
||||
languageMap = TranscriptUtils.Storage.get('languageMap');
|
||||
|
||||
_.each(values, function(value, key) {
|
||||
_.each(values, function(value, newLang) {
|
||||
var html = $(self.templateItem({
|
||||
lang: key,
|
||||
newLang: newLang,
|
||||
originalLang: _.findKey(languageMap, function(lang) { return lang === newLang; }) || '',
|
||||
value: value,
|
||||
url: self.model.get('urlRoot') + '/' + key
|
||||
})).prepend(dropdown.clone().val(key))[0];
|
||||
url: self.model.get('urlRoot')
|
||||
})).prepend(dropdown.clone().val(newLang))[0];
|
||||
|
||||
frag.appendChild(html);
|
||||
});
|
||||
@@ -130,63 +144,166 @@ function($, _, AbstractEditor, FileUpload, UploadDialog) {
|
||||
event.preventDefault();
|
||||
// We don't call updateModel here since it's bound to the
|
||||
// change event
|
||||
var dict = $.extend(true, {}, this.model.get('value'));
|
||||
dict[''] = '';
|
||||
this.setValueInEditor(dict);
|
||||
this.setValueInEditor(this.getAllLanguageDropdownElementsData(true));
|
||||
this.$el.find('.create-setting').addClass('is-disabled').attr('aria-disabled', true);
|
||||
},
|
||||
|
||||
removeEntry: function(event) {
|
||||
var self = this,
|
||||
$currentListItemEl = $(event.currentTarget).parent(),
|
||||
originalLang = $currentListItemEl.data('original-lang'),
|
||||
selectedLang = $currentListItemEl.find('select option:selected').val(),
|
||||
languageMap = TranscriptUtils.Storage.get('languageMap'),
|
||||
edxVideoIdField = TranscriptUtils.getField(self.model.collection, 'edx_video_id');
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var entry = $(event.currentTarget).data('lang');
|
||||
this.setValueInEditor(_.omit(this.model.get('value'), entry));
|
||||
this.updateModel();
|
||||
/*
|
||||
There is a scenario when a user adds an empty video translation item and
|
||||
removes it. In such cases, omitting will have no harm on the model
|
||||
values or languages map.
|
||||
*/
|
||||
if (originalLang) {
|
||||
ViewUtils.confirmThenRunOperation(
|
||||
gettext('Are you sure you want to remove this transcript?'),
|
||||
gettext('If you remove this transcript, the transcript will not be available for this component.'),
|
||||
gettext('Remove Transcript'),
|
||||
function() {
|
||||
ViewUtils.runOperationShowingMessage(
|
||||
gettext('Removing'),
|
||||
function() {
|
||||
return $.ajax({
|
||||
url: self.model.get('urlRoot'),
|
||||
type: 'DELETE',
|
||||
data: JSON.stringify({lang: originalLang, edx_video_id: edxVideoIdField.getValue()})
|
||||
}).done(function() {
|
||||
self.setValueInEditor(self.getAllLanguageDropdownElementsData(false, selectedLang));
|
||||
TranscriptUtils.Storage.set('languageMap', _.omit(languageMap, originalLang));
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.setValueInEditor(this.getAllLanguageDropdownElementsData(false, selectedLang));
|
||||
}
|
||||
|
||||
this.$el.find('.create-setting').removeClass('is-disabled').attr('aria-disabled', false);
|
||||
},
|
||||
|
||||
upload: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var self = this,
|
||||
$target = $(event.currentTarget),
|
||||
lang = $target.data('lang'),
|
||||
model = new FileUpload({
|
||||
title: gettext('Upload translation'),
|
||||
fileFormats: ['srt']
|
||||
}),
|
||||
view = new VideoUploadDialog({
|
||||
model: model,
|
||||
url: self.model.get('urlRoot') + '/' + lang,
|
||||
parentElement: $target.closest('.xblock-editor'),
|
||||
onSuccess: function(response) {
|
||||
if (!response.filename) { return; }
|
||||
$listItem = $target.parents('li.list-settings-item'),
|
||||
originalLang = $listItem.data('original-lang'),
|
||||
newLang = $listItem.find(':selected').val(),
|
||||
edxVideoIdField = TranscriptUtils.getField(self.model.collection, 'edx_video_id'),
|
||||
fileUploadModel,
|
||||
uploadData,
|
||||
videoUploadDialog;
|
||||
|
||||
var dict = $.extend(true, {}, self.model.get('value'));
|
||||
event.preventDefault();
|
||||
|
||||
dict[lang] = response.filename;
|
||||
self.model.setValue(dict);
|
||||
}
|
||||
});
|
||||
// That's the case when an author is
|
||||
// uploading a new transcript.
|
||||
if (!originalLang) {
|
||||
originalLang = newLang;
|
||||
}
|
||||
|
||||
view.show();
|
||||
// Transcript data payload
|
||||
uploadData = {
|
||||
edx_video_id: edxVideoIdField.getValue(),
|
||||
language_code: originalLang,
|
||||
new_language_code: newLang
|
||||
};
|
||||
|
||||
fileUploadModel = new FileUpload({
|
||||
title: gettext('Upload translation'),
|
||||
fileFormats: this.validFileFormats
|
||||
});
|
||||
|
||||
videoUploadDialog = new VideoUploadDialog({
|
||||
model: fileUploadModel,
|
||||
url: this.model.get('urlRoot'),
|
||||
parentElement: $target.closest('.xblock-editor'),
|
||||
uploadData: uploadData,
|
||||
onSuccess: function(response) {
|
||||
var languageMap = TranscriptUtils.Storage.get('languageMap'),
|
||||
newLangObject = {};
|
||||
|
||||
// new language entry to be added to languageMap
|
||||
newLangObject[newLang] = newLang;
|
||||
|
||||
// Update edx-video-id
|
||||
edxVideoIdField.setValue(response.edx_video_id);
|
||||
|
||||
// Update language map by omitting original lang and adding new lang
|
||||
// if languageMap is empty then newLang will be added
|
||||
// if an original lang is replaced with new lang then omit the original lang and the add new lang
|
||||
languageMap = _.extend(_.omit(languageMap, originalLang), newLangObject);
|
||||
TranscriptUtils.Storage.set('languageMap', languageMap);
|
||||
|
||||
// re-render the whole view
|
||||
self.setValueInEditor(self.getAllLanguageDropdownElementsData());
|
||||
}
|
||||
});
|
||||
videoUploadDialog.show();
|
||||
},
|
||||
|
||||
enableAdd: function() {
|
||||
this.$el.find('.create-setting').removeClass('is-disabled').attr('aria-disabled', false);
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
AbstractEditor.prototype.clear.apply(this, arguments);
|
||||
if (_.isNull(this.model.getValue())) {
|
||||
this.$el.find('.create-setting').removeClass('is-disabled').attr('aria-disabled', false);
|
||||
onChangeHandler: function(event) {
|
||||
var $target = $(event.currentTarget),
|
||||
$listItem = $target.parents('li.list-settings-item'),
|
||||
originalLang = $listItem.data('original-lang'),
|
||||
newLang = $listItem.find('select option:selected').val(),
|
||||
languageMap = TranscriptUtils.Storage.get('languageMap');
|
||||
|
||||
// To protect against any new/unsaved language code in the map.
|
||||
if (originalLang in languageMap) {
|
||||
languageMap[originalLang] = newLang;
|
||||
TranscriptUtils.Storage.set('languageMap', languageMap);
|
||||
|
||||
// an existing saved lang is changed, no need to re-render the whole view
|
||||
return;
|
||||
}
|
||||
|
||||
this.enableAdd();
|
||||
this.setValueInEditor(this.getAllLanguageDropdownElementsData());
|
||||
},
|
||||
|
||||
onChangeHandler: function(event) {
|
||||
this.showClearButton();
|
||||
this.enableAdd();
|
||||
this.updateModel();
|
||||
/**
|
||||
* Constructs data extracted from each dropdown. This will be used to re-render the whole view.
|
||||
*/
|
||||
getAllLanguageDropdownElementsData: function(isNew, omittedLanguage) {
|
||||
var data = {},
|
||||
languageDropdownElements = this.$el.find('select'),
|
||||
languageMap = TranscriptUtils.Storage.get('languageMap');
|
||||
|
||||
// data object will mirror the languageMap. `data` will contain lang to lang map as explained below
|
||||
// {originalLang: originalLang}; original lang not changed
|
||||
// {newLang: originalLang}; original lang changed to a new lang
|
||||
// {selectedLang: ''}; new lang to be added, no entry in languageMap
|
||||
_.each(languageDropdownElements, function(languageDropdown) {
|
||||
var language = $(languageDropdown).find(':selected').val();
|
||||
data[language] = _.findKey(languageMap, function(lang) { return lang === language; }) || '';
|
||||
});
|
||||
|
||||
// This is needed to render an empty item that
|
||||
// will be further used to upload a transcript.
|
||||
if (isNew) {
|
||||
data[''] = '';
|
||||
}
|
||||
|
||||
// This Omits a language from the dropdown's data. It is
|
||||
// needed when an item is going to be removed.
|
||||
if (typeof(omittedLanguage) !== 'undefined') {
|
||||
data = _.omit(data, omittedLanguage);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user