Add frontend video image validations - EDUCATOR-447
This commit is contained in:
@@ -25,7 +25,8 @@ define(
|
||||
);
|
||||
var view = new PreviousVideoUploadListView({
|
||||
collection: collection,
|
||||
videoHandlerUrl: videoHandlerUrl
|
||||
videoHandlerUrl: videoHandlerUrl,
|
||||
videoImageSettings: {}
|
||||
});
|
||||
return view.render().$el;
|
||||
},
|
||||
|
||||
@@ -14,7 +14,8 @@ define(
|
||||
},
|
||||
view = new PreviousVideoUploadView({
|
||||
model: new Backbone.Model($.extend({}, defaultData, modelData)),
|
||||
videoHandlerUrl: '/videos/course-v1:org.0+course_0+Run_0'
|
||||
videoHandlerUrl: '/videos/course-v1:org.0+course_0+Run_0',
|
||||
videoImageSettings: {}
|
||||
});
|
||||
return view.render().$el;
|
||||
};
|
||||
|
||||
@@ -1,36 +1,77 @@
|
||||
define(
|
||||
['jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
|
||||
'js/views/video_thumbnail', 'common/js/spec_helpers/template_helpers'],
|
||||
function($, _, Backbone, AjaxHelpers, VideoThumbnailView, TemplateHelpers) {
|
||||
'js/views/video_thumbnail', 'js/views/previous_video_upload_list', 'common/js/spec_helpers/template_helpers'],
|
||||
function($, _, Backbone, AjaxHelpers, VideoThumbnailView, PreviousVideoUploadListView, TemplateHelpers) {
|
||||
'use strict';
|
||||
describe('VideoThumbnailView', function() {
|
||||
var IMAGE_UPLOAD_URL = '/videos/upload/image',
|
||||
UPLOADED_IMAGE_URL = 'images/upload_success.jpg',
|
||||
VIDEO_IMAGE_MAX_BYTES = 2 * 1024 * 1024,
|
||||
VIDEO_IMAGE_MIN_BYTES = 2 * 1024,
|
||||
VIDEO_IMAGE_MAX_WIDTH = 1280,
|
||||
VIDEO_IMAGE_MAX_HEIGHT = 720,
|
||||
VIDEO_IMAGE_SUPPORTED_FILE_FORMATS = {
|
||||
'.bmp': 'image/bmp',
|
||||
'.bmp2': 'image/x-ms-bmp', // PIL gives x-ms-bmp format
|
||||
'.gif': 'image/gif',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.png': 'image/png'
|
||||
},
|
||||
videoListView,
|
||||
videoThumbnailView,
|
||||
$videoListEl,
|
||||
$videoThumbnailEl,
|
||||
createVideoListView,
|
||||
createFakeImageFile,
|
||||
verifyStateInfo,
|
||||
render = function(modelData) {
|
||||
var defaultData = {
|
||||
verifyStateInfo;
|
||||
|
||||
/**
|
||||
* Creates a list of video records.
|
||||
*
|
||||
* @param {Object} modelData Model data for video records.
|
||||
* @param {Integer} numVideos Number of video elements to create.
|
||||
* @param {Integer} videoViewIndex Index of video on which videoThumbnailView would be based.
|
||||
*/
|
||||
createVideoListView = function(modelData, numVideos, videoViewIndex) {
|
||||
var modelData = modelData || {}, // eslint-disable-line no-redeclare
|
||||
numVideos = numVideos || 1, // eslint-disable-line no-redeclare
|
||||
videoViewIndex = videoViewIndex || 0, // eslint-disable-line no-redeclare,
|
||||
defaultData = {
|
||||
client_video_id: 'foo.mp4',
|
||||
duration: 42,
|
||||
created: '2014-11-25T23:13:05',
|
||||
edx_video_id: 'dummy_id',
|
||||
status: 'uploading',
|
||||
thumbnail_url: null
|
||||
};
|
||||
videoThumbnailView = new VideoThumbnailView({
|
||||
model: new Backbone.Model($.extend({}, defaultData, modelData)),
|
||||
imageUploadURL: IMAGE_UPLOAD_URL
|
||||
});
|
||||
return videoThumbnailView.render().$el;
|
||||
};
|
||||
status: 'uploading'
|
||||
},
|
||||
collection = new Backbone.Collection(_.map(_.range(numVideos), function(num, index) {
|
||||
return new Backbone.Model(
|
||||
_.extend({}, defaultData, {edx_video_id: 'dummy_id_' + index}, modelData)
|
||||
);
|
||||
}));
|
||||
videoListView = new PreviousVideoUploadListView({
|
||||
collection: collection,
|
||||
videoHandlerUrl: '/videos/course-v1:org.0+course_0+Run_0',
|
||||
videoImageUploadURL: IMAGE_UPLOAD_URL,
|
||||
videoImageSettings: {
|
||||
max_size: VIDEO_IMAGE_MAX_BYTES,
|
||||
min_size: VIDEO_IMAGE_MIN_BYTES,
|
||||
max_width: VIDEO_IMAGE_MAX_WIDTH,
|
||||
max_height: VIDEO_IMAGE_MAX_HEIGHT,
|
||||
supported_file_formats: VIDEO_IMAGE_SUPPORTED_FILE_FORMATS
|
||||
}
|
||||
});
|
||||
$videoListEl = videoListView.render().$el;
|
||||
videoThumbnailView = videoListView.itemViews[videoViewIndex].videoThumbnailView;
|
||||
$videoThumbnailEl = videoThumbnailView.render().$el;
|
||||
return videoListView;
|
||||
};
|
||||
|
||||
createFakeImageFile = function(size) {
|
||||
var fileFakeData = 'i63ljc6giwoskyb9x5sw0169bdcmcxr3cdz8boqv0lik971972cmd6yknvcxr5sw0nvc169bdcmcxsdf';
|
||||
return new Blob(
|
||||
[fileFakeData.substr(0, size)],
|
||||
{type: 'image/jpg'}
|
||||
);
|
||||
|
||||
createFakeImageFile = function(size, type) {
|
||||
var size = size || VIDEO_IMAGE_MIN_BYTES, // eslint-disable-line no-redeclare
|
||||
type = type || 'image/jpeg'; // eslint-disable-line no-redeclare
|
||||
return new Blob([Array(size + 1).join('i')], {type: type});
|
||||
};
|
||||
|
||||
verifyStateInfo = function($thumbnail, state, onHover, additionalSRText) {
|
||||
@@ -50,9 +91,12 @@ define(
|
||||
).toEqual(additionalSRText);
|
||||
}
|
||||
|
||||
expect($thumbnail.find('.action-icon').html().trim()).toEqual(
|
||||
videoThumbnailView.actionsInfo[state].icon
|
||||
);
|
||||
if (state !== 'error') {
|
||||
expect($thumbnail.find('.action-icon').html().trim()).toEqual(
|
||||
videoThumbnailView.actionsInfo[state].icon
|
||||
);
|
||||
}
|
||||
|
||||
expect($thumbnail.find('.action-text').html().trim()).toEqual(
|
||||
videoThumbnailView.actionsInfo[state].text
|
||||
);
|
||||
@@ -68,22 +112,22 @@ define(
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="page-prompt"></div><div id="page-notification"></div>');
|
||||
TemplateHelpers.installTemplate('video-thumbnail');
|
||||
TemplateHelpers.installTemplate('previous-video-upload-list');
|
||||
createVideoListView();
|
||||
});
|
||||
|
||||
it('renders as expected', function() {
|
||||
var $el = render({});
|
||||
expect($el.find('.thumbnail-wrapper')).toExist();
|
||||
expect($el.find('.upload-image-input')).toExist();
|
||||
expect($videoThumbnailEl.find('.thumbnail-wrapper')).toExist();
|
||||
expect($videoThumbnailEl.find('.upload-image-input')).toExist();
|
||||
});
|
||||
|
||||
it('does not show duration if not available', function() {
|
||||
var $el = render({duration: 0});
|
||||
expect($el.find('.thumbnail-wrapper .video-duration')).not.toExist();
|
||||
createVideoListView({duration: 0});
|
||||
expect($videoThumbnailEl.find('.thumbnail-wrapper .video-duration')).not.toExist();
|
||||
});
|
||||
|
||||
it('shows the duration if available', function() {
|
||||
var $el = render({}),
|
||||
$duration = $el.find('.thumbnail-wrapper .video-duration');
|
||||
var $duration = $videoThumbnailEl.find('.thumbnail-wrapper .video-duration');
|
||||
expect($duration).toExist();
|
||||
expect($duration.find('.duration-text-machine').text().trim()).toEqual('0:42');
|
||||
expect($duration.find('.duration-text-human').text().trim()).toEqual('Video duration is 42 seconds');
|
||||
@@ -114,8 +158,8 @@ define(
|
||||
});
|
||||
|
||||
it('can upload image', function() {
|
||||
var $el = render({}),
|
||||
$thumbnail = $el.find('.thumbnail-wrapper'),
|
||||
var videoViewIndex = 0,
|
||||
$thumbnail = $videoThumbnailEl.find('.thumbnail-wrapper'),
|
||||
requests = AjaxHelpers.requests(this),
|
||||
additionalSRText = videoThumbnailView.getSRText();
|
||||
|
||||
@@ -125,12 +169,17 @@ define(
|
||||
verifyStateInfo($thumbnail, 'requirements', true, additionalSRText);
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$el.find('.upload-image-input').fileupload('add', {files: [createFakeImageFile(60)]});
|
||||
$videoThumbnailEl.find('.upload-image-input').fileupload('add', {files: [createFakeImageFile()]});
|
||||
|
||||
verifyStateInfo($thumbnail, 'progress');
|
||||
|
||||
// Verify if POST request received for image upload
|
||||
AjaxHelpers.expectRequest(requests, 'POST', IMAGE_UPLOAD_URL + '/dummy_id', new FormData());
|
||||
AjaxHelpers.expectRequest(
|
||||
requests,
|
||||
'POST',
|
||||
IMAGE_UPLOAD_URL + '/dummy_id_' + videoViewIndex,
|
||||
new FormData()
|
||||
);
|
||||
|
||||
// Send successful upload response
|
||||
AjaxHelpers.respondWithJson(requests, {image_url: UPLOADED_IMAGE_URL});
|
||||
@@ -142,45 +191,136 @@ define(
|
||||
});
|
||||
|
||||
it('shows error state correctly', function() {
|
||||
var $el = render({}),
|
||||
$thumbnail = $el.find('.thumbnail-wrapper'),
|
||||
var $thumbnail = $videoThumbnailEl.find('.thumbnail-wrapper'),
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$el.find('.upload-image-input').fileupload('add', {files: [createFakeImageFile(60)]});
|
||||
$videoThumbnailEl.find('.upload-image-input').fileupload('add', {files: [createFakeImageFile()]});
|
||||
|
||||
AjaxHelpers.respondWithError(requests, 400);
|
||||
|
||||
verifyStateInfo($thumbnail, 'error');
|
||||
});
|
||||
|
||||
it('should show error notification in case of server error', function() {
|
||||
var $el = render({}),
|
||||
requests = AjaxHelpers.requests(this);
|
||||
it('calls readMessage with correct message', function() {
|
||||
var errorMessage = 'This image file type is not supported. Supported file types are ' +
|
||||
videoThumbnailView.getVideoImageSupportedFileFormats().humanize + '.',
|
||||
successData = {
|
||||
files: [createFakeImageFile()],
|
||||
submit: function() {}
|
||||
},
|
||||
failureData = {
|
||||
jqXHR: {
|
||||
responseText: JSON.stringify({
|
||||
error: errorMessage
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
spyOn(videoThumbnailView, 'readMessages');
|
||||
|
||||
videoThumbnailView.imageSelected({}, successData);
|
||||
expect(videoThumbnailView.readMessages).toHaveBeenCalledWith(['Video image upload started']);
|
||||
videoThumbnailView.imageUploadSucceeded({}, {result: {image_url: UPLOADED_IMAGE_URL}});
|
||||
expect(videoThumbnailView.readMessages).toHaveBeenCalledWith(['Video image upload completed']);
|
||||
videoThumbnailView.imageUploadFailed({}, failureData);
|
||||
expect(videoThumbnailView.readMessages).toHaveBeenCalledWith(
|
||||
['Could not upload the video image file', errorMessage]
|
||||
);
|
||||
});
|
||||
|
||||
it('should show error message in case of server error', function() {
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$el.find('.upload-image-input').fileupload('add', {files: [createFakeImageFile(60)]});
|
||||
$videoThumbnailEl.find('.upload-image-input').fileupload('add', {files: [createFakeImageFile()]});
|
||||
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
|
||||
expect($('#notification-error-title').text().trim()).toEqual(
|
||||
"Studio's having trouble saving your work"
|
||||
// Verify error message is present
|
||||
expect($videoListEl.find('.thumbnail-error-wrapper')).toExist();
|
||||
});
|
||||
|
||||
it('should show error message when file is smaller than minimum size', function() {
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$videoThumbnailEl.find('.upload-image-input')
|
||||
.fileupload('add', {files: [createFakeImageFile(VIDEO_IMAGE_MIN_BYTES - 10)]});
|
||||
|
||||
// Verify error message
|
||||
expect($videoListEl.find('.thumbnail-error-wrapper').find('.action-text').html()
|
||||
.trim()).toEqual(
|
||||
'The selected image must be larger than ' +
|
||||
videoThumbnailView.getVideoImageMinSize().humanize + '.'
|
||||
);
|
||||
});
|
||||
|
||||
it('calls readMessage with correct message', function() {
|
||||
spyOn(videoThumbnailView, 'readMessages');
|
||||
it('should show error message when file is larger than maximum size', function() {
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
videoThumbnailView.imageSelected({}, {submit: function() {}});
|
||||
expect(videoThumbnailView.readMessages).toHaveBeenCalledWith(['Video image upload started']);
|
||||
videoThumbnailView.imageUploadSucceeded({}, {result: {image_url: UPLOADED_IMAGE_URL}});
|
||||
expect(videoThumbnailView.readMessages).toHaveBeenCalledWith(['Video image upload completed']);
|
||||
videoThumbnailView.imageUploadFailed();
|
||||
expect(videoThumbnailView.readMessages).toHaveBeenCalledWith(['Video image upload failed']);
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$videoThumbnailEl.find('.upload-image-input')
|
||||
.fileupload('add', {files: [createFakeImageFile(VIDEO_IMAGE_MAX_BYTES + 10)]});
|
||||
|
||||
// Verify error message
|
||||
expect($videoListEl.find('.thumbnail-error-wrapper').find('.action-text').html()
|
||||
.trim()).toEqual(
|
||||
'The selected image must be smaller than ' +
|
||||
videoThumbnailView.getVideoImageMaxSize().humanize + '.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not show error message when file size is equals to minimum file size', function() {
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$videoThumbnailEl.find('.upload-image-input')
|
||||
.fileupload('add', {files: [createFakeImageFile(VIDEO_IMAGE_MIN_BYTES)]});
|
||||
|
||||
// Verify error not present.
|
||||
expect($videoListEl.find('.thumbnail-error-wrapper')).not.toExist();
|
||||
});
|
||||
|
||||
it('should not show error message when file size is equals to maximum file size', function() {
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$videoThumbnailEl.find('.upload-image-input')
|
||||
.fileupload('add', {files: [createFakeImageFile(VIDEO_IMAGE_MAX_BYTES)]});
|
||||
|
||||
// Verify error not present.
|
||||
expect($videoListEl.find('.thumbnail-error-wrapper')).not.toExist();
|
||||
});
|
||||
|
||||
it('should show error message when file has unsupported content type', function() {
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$videoThumbnailEl.find('.upload-image-input')
|
||||
.fileupload('add', {files: [createFakeImageFile(VIDEO_IMAGE_MIN_BYTES, 'mov/mp4')]});
|
||||
|
||||
// Verify error message
|
||||
expect($videoListEl.find('.thumbnail-error-wrapper').find('.action-text').html()
|
||||
.trim()).toEqual(
|
||||
'This image file type is not supported. Supported file types are ' +
|
||||
videoThumbnailView.getVideoImageSupportedFileFormats().humanize + '.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not show error message when file has supported content type', function() {
|
||||
videoThumbnailView.chooseFile();
|
||||
|
||||
// Add image to upload queue and send POST request to upload image
|
||||
$videoThumbnailEl.find('.upload-image-input')
|
||||
.fileupload('add', {files: [createFakeImageFile(VIDEO_IMAGE_MIN_BYTES)]});
|
||||
|
||||
// Verify error message is not present
|
||||
expect($videoListEl.find('.thumbnail-error-wrapper')).not.toExist();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user