diff --git a/cms/djangoapps/contentstore/management/commands/tests/test_migrate_transcripts.py b/cms/djangoapps/contentstore/management/commands/tests/test_migrate_transcripts.py index 446c5703b625..c458b7791160 100644 --- a/cms/djangoapps/contentstore/management/commands/tests/test_migrate_transcripts.py +++ b/cms/djangoapps/contentstore/management/commands/tests/test_migrate_transcripts.py @@ -261,10 +261,7 @@ def test_migrate_transcripts_exception_logging(self): u'[Transcript migration] process for ge transcript started'), (LOGGER_NAME, 'ERROR', - '[Transcript migration] Exception: u"SON([' - '(\'category\', \'asset\'), (\'name\', u\'not_found.srt\'),' - ' (\'course\', u\'{}\'), (\'tag\', \'c4x\'), (\'org\', u\'{}\'),' - ' (\'revision\', None)])"'.format(self.course_2.id.course, self.course_2.id.org)), + "[Transcript migration] Exception: u'No transcript for `ge` language'"), (LOGGER_NAME, 'INFO', u'[Transcript migration] process for course {} ended. Processed 1 transcripts'.format( @@ -272,11 +269,8 @@ def test_migrate_transcripts_exception_logging(self): )), (LOGGER_NAME, 'INFO', - "[Transcript migration] Result: Failed: language ge of video test_edx_video_id_2 with exception SON([" - "('category', 'asset'), ('name', u'not_found.srt'), ('course', u'{}')," - " ('tag', 'c4x'), ('org', u'{}'), ('revision', None)])".format( - self.course_2.id.course, self.course_2.id.org) - ) + "[Transcript migration] Result: Failed: language ge of video test_edx_video_id_2 with exception " + "No transcript for `ge` language") ) with LogCapture(LOGGER_NAME, level=logging.INFO) as logger: diff --git a/cms/djangoapps/contentstore/tests/test_transcripts_utils.py b/cms/djangoapps/contentstore/tests/test_transcripts_utils.py index bc67a6aad2fc..2a004433f814 100644 --- a/cms/djangoapps/contentstore/tests/test_transcripts_utils.py +++ b/cms/djangoapps/contentstore/tests/test_transcripts_utils.py @@ -191,7 +191,9 @@ def setUpClass(cls): @override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE) class TestDownloadYoutubeSubs(TestYoutubeSubsBase): - """Tests for `download_youtube_subs` function.""" + """ + Tests for `download_youtube_subs` function. + """ org = 'MITx' number = '999' @@ -238,13 +240,6 @@ def test_success_downloading_subs(self): mock_get.assert_any_call('http://video.google.com/timedtext', params={'lang': 'en', 'v': 'good_id_2'}) - # Check asset status after import of transcript. - filename = 'subs_{0}.srt.sjson'.format(good_youtube_sub) - content_location = StaticContent.compute_location(self.course.id, filename) - self.assertTrue(contentstore().find(content_location)) - - self.clear_sub_content(good_youtube_sub) - def test_subs_for_html5_vid_with_periods(self): """ This is to verify a fix whereby subtitle files uploaded against @@ -269,16 +264,6 @@ def test_fail_downloading_subs(self, mock_get): with self.assertRaises(transcripts_utils.GetTranscriptsFromYouTubeException): transcripts_utils.download_youtube_subs(bad_youtube_sub, self.course, settings) - # Check asset status after import of transcript. - filename = 'subs_{0}.srt.sjson'.format(bad_youtube_sub) - content_location = StaticContent.compute_location( - self.course.id, filename - ) - with self.assertRaises(NotFoundError): - contentstore().find(content_location) - - self.clear_sub_content(bad_youtube_sub) - def test_success_downloading_chinese_transcripts(self): # Disabled 11/14/13 @@ -367,13 +352,6 @@ def test_downloading_subs_using_transcript_name(self, mock_get): params={'lang': 'en', 'v': 'good_id_2', 'name': 'Custom'} ) - # Check asset status after import of transcript. - filename = 'subs_{0}.srt.sjson'.format(good_youtube_sub) - content_location = StaticContent.compute_location(self.course.id, filename) - self.assertTrue(contentstore().find(content_location)) - - self.clear_sub_content(good_youtube_sub) - class TestGenerateSubsFromSource(TestDownloadYoutubeSubs): """Tests for `generate_subs_from_source` function.""" @@ -766,7 +744,7 @@ def setUp(self): edx_video_id=u'1234-5678-90' ) - def create_transcript(self, subs_id, language=u'en', filename='video.srt'): + def create_transcript(self, subs_id, language=u'en', filename='video.srt', youtube_id_1_0='', html5_sources=None): """ create transcript. """ @@ -774,21 +752,26 @@ def create_transcript(self, subs_id, language=u'en', filename='video.srt'): if language != u'en': transcripts = {language: filename} + html5_sources = html5_sources or [] self.video = ItemFactory.create( category='video', parent_location=self.vertical.location, sub=subs_id, + youtube_id_1_0=youtube_id_1_0, transcripts=transcripts, - edx_video_id=u'1234-5678-90' + edx_video_id=u'1234-5678-90', + html5_sources=html5_sources ) - if subs_id: - transcripts_utils.save_subs_to_store( - self.subs_sjson, - subs_id, - self.video, - language=language, - ) + possible_subs = [subs_id, youtube_id_1_0] + transcripts_utils.get_html5_ids(html5_sources) + for possible_sub in possible_subs: + if possible_sub: + transcripts_utils.save_subs_to_store( + self.subs_sjson, + possible_sub, + self.video, + language=language, + ) def create_srt_file(self, content): """ @@ -834,31 +817,69 @@ def test_get_transcript_not_found(self, lang): ) @ddt.data( + # video.sub transcript { 'language': u'en', 'subs_id': 'video_101', - 'filename': 'en_video_101.srt', + 'youtube_id_1_0': '', + 'html5_sources': [], + 'expected_filename': 'en_video_101.srt', + }, + # if video.sub is present, rest will be skipped. + { + 'language': u'en', + 'subs_id': 'video_101', + 'youtube_id_1_0': 'test_yt_id', + 'html5_sources': ['www.abc.com/foo.mp4'], + 'expected_filename': 'en_video_101.srt', }, + # video.youtube_id_1_0 transcript + { + 'language': u'en', + 'subs_id': '', + 'youtube_id_1_0': 'test_yt_id', + 'html5_sources': [], + 'expected_filename': 'en_test_yt_id.srt', + }, + # video.html5_sources transcript + { + 'language': u'en', + 'subs_id': '', + 'youtube_id_1_0': '', + 'html5_sources': ['www.abc.com/foo.mp4'], + 'expected_filename': 'en_foo.srt', + }, + # non-english transcript { 'language': u'ur', 'subs_id': '', - 'filename': 'ur_video_101.srt', + 'youtube_id_1_0': '', + 'html5_sources': [], + 'expected_filename': 'ur_video_101.srt', }, ) @ddt.unpack - def test_get_transcript_from_content_store(self, language, subs_id, filename): + def test_get_transcript_from_contentstore( + self, + language, + subs_id, + youtube_id_1_0, + html5_sources, + expected_filename + ): """ Verify that `get_transcript` function returns correct data when transcript is in content store. """ - self.upload_file(self.create_srt_file(self.subs_srt), self.video.location, filename) - self.create_transcript(subs_id, language, filename) - content, filename, mimetype = transcripts_utils.get_transcript( + base_filename = 'video_101.srt' + self.upload_file(self.create_srt_file(self.subs_srt), self.video.location, base_filename) + self.create_transcript(subs_id, language, base_filename, youtube_id_1_0, html5_sources) + content, file_name, mimetype = transcripts_utils.get_transcript( self.video, language ) self.assertEqual(content, self.subs[language]) - self.assertEqual(filename, filename) + self.assertEqual(file_name, expected_filename) self.assertEqual(mimetype, self.srt_mime_type) def test_get_transcript_from_content_store_for_ur(self): @@ -938,3 +959,43 @@ def test_get_transcript_no_en_transcript(self): exception_message = text_type(no_en_transcript_exception.exception) self.assertEqual(exception_message, 'No transcript for `en` language') + + @ddt.data( + transcripts_utils.TranscriptsGenerationException, + UnicodeDecodeError('aliencodec', b'\x02\x01', 1, 2, 'alien codec found!') + ) + @patch('xmodule.video_module.transcripts_utils.Transcript') + def test_get_transcript_val_exceptions(self, exception_to_raise, mock_Transcript): + """ + Verify that `get_transcript_from_val` function raises `NotFoundError` when specified exceptions raised. + """ + mock_Transcript.convert.side_effect = exception_to_raise + transcripts_info = self.video.get_transcripts_info() + lang = self.video.get_default_transcript_language(transcripts_info) + edx_video_id = transcripts_utils.clean_video_id(self.video.edx_video_id) + with self.assertRaises(NotFoundError): + transcripts_utils.get_transcript_from_val( + edx_video_id, + lang=lang, + output_format=transcripts_utils.Transcript.SRT + ) + + @ddt.data( + transcripts_utils.TranscriptsGenerationException, + UnicodeDecodeError('aliencodec', b'\x02\x01', 1, 2, 'alien codec found!') + ) + @patch('xmodule.video_module.transcripts_utils.Transcript') + def test_get_transcript_content_store_exceptions(self, exception_to_raise, mock_Transcript): + """ + Verify that `get_transcript_from_contentstore` function raises `NotFoundError` when specified exceptions raised. + """ + mock_Transcript.asset.side_effect = exception_to_raise + transcripts_info = self.video.get_transcripts_info() + lang = self.video.get_default_transcript_language(transcripts_info) + with self.assertRaises(NotFoundError): + transcripts_utils.get_transcript_from_contentstore( + self.video, + language=lang, + output_format=transcripts_utils.Transcript.SRT, + transcripts_info=transcripts_info + ) diff --git a/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py b/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py index 443131af6b62..da838b180684 100644 --- a/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py +++ b/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py @@ -5,6 +5,7 @@ from mock import Mock, patch, ANY from django.test.testcases import TestCase +from django.core.urlresolvers import reverse from edxval import api from contentstore.tests.utils import CourseTestCase @@ -177,26 +178,24 @@ def test_invalid_credentials(self, provider, credentials, expected_error_message @ddt.ddt -@patch( - 'openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled', - Mock(return_value=True) -) class TranscriptDownloadTest(CourseTestCase): """ Tests for transcript download handler. """ - VIEW_NAME = 'transcript_download_handler' - def get_url_for_course_key(self, course_id): - return reverse_course_url(self.VIEW_NAME, course_id) + @property + def view_url(self): + """ + Returns url for this view + """ + return reverse('transcript_download_handler') def test_302_with_anonymous_user(self): """ Verify that redirection happens in case of unauthorized request. """ self.client.logout() - transcript_download_url = self.get_url_for_course_key(self.course.id) - response = self.client.get(transcript_download_url, content_type='application/json') + response = self.client.get(self.view_url, content_type='application/json') self.assertEqual(response.status_code, 302) def test_405_with_not_allowed_request_method(self): @@ -204,26 +203,14 @@ def test_405_with_not_allowed_request_method(self): Verify that 405 is returned in case of not-allowed request methods. Allowed request methods include GET. """ - transcript_download_url = self.get_url_for_course_key(self.course.id) - response = self.client.post(transcript_download_url, content_type='application/json') + response = self.client.post(self.view_url, content_type='application/json') self.assertEqual(response.status_code, 405) - def test_404_with_feature_disabled(self): - """ - Verify that 404 is returned if the corresponding feature is disabled. - """ - transcript_download_url = self.get_url_for_course_key(self.course.id) - with patch('openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled') as feature: - feature.return_value = False - response = self.client.get(transcript_download_url, content_type='application/json') - self.assertEqual(response.status_code, 404) - @patch('contentstore.views.transcript_settings.get_video_transcript_data') def test_transcript_download_handler(self, mock_get_video_transcript_data): """ Tests that transcript download handler works as expected. """ - transcript_download_url = self.get_url_for_course_key(self.course.id) mock_get_video_transcript_data.return_value = { 'content': json.dumps({ "start": [10], @@ -235,7 +222,7 @@ def test_transcript_download_handler(self, mock_get_video_transcript_data): # Make request to transcript download handler response = self.client.get( - transcript_download_url, + self.view_url, data={ 'edx_video_id': '123', 'language_code': 'en' @@ -277,34 +264,30 @@ def test_transcript_download_handler_missing_attrs(self, request_payload, expect Tests that transcript download handler with missing attributes. """ # Make request to transcript download handler - transcript_download_url = self.get_url_for_course_key(self.course.id) - response = self.client.get(transcript_download_url, data=request_payload) + response = self.client.get(self.view_url, data=request_payload) # Assert the response self.assertEqual(response.status_code, 400) self.assertEqual(json.loads(response.content)['error'], expected_error_message) @ddt.ddt -@patch( - 'openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled', - Mock(return_value=True) -) class TranscriptUploadTest(CourseTestCase): """ Tests for transcript upload handler. """ - VIEW_NAME = 'transcript_upload_handler' - - def get_url_for_course_key(self, course_id): - return reverse_course_url(self.VIEW_NAME, course_id) + @property + def view_url(self): + """ + Returns url for this view + """ + return reverse('transcript_upload_handler') def test_302_with_anonymous_user(self): """ Verify that redirection happens in case of unauthorized request. """ self.client.logout() - transcript_upload_url = self.get_url_for_course_key(self.course.id) - response = self.client.post(transcript_upload_url, content_type='application/json') + response = self.client.post(self.view_url, content_type='application/json') self.assertEqual(response.status_code, 302) def test_405_with_not_allowed_request_method(self): @@ -312,31 +295,19 @@ def test_405_with_not_allowed_request_method(self): Verify that 405 is returned in case of not-allowed request methods. Allowed request methods include POST. """ - transcript_upload_url = self.get_url_for_course_key(self.course.id) - response = self.client.get(transcript_upload_url, content_type='application/json') + response = self.client.get(self.view_url, content_type='application/json') self.assertEqual(response.status_code, 405) - def test_404_with_feature_disabled(self): - """ - Verify that 404 is returned if the corresponding feature is disabled. - """ - transcript_upload_url = self.get_url_for_course_key(self.course.id) - with patch('openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled') as feature: - feature.return_value = False - response = self.client.post(transcript_upload_url, content_type='application/json') - self.assertEqual(response.status_code, 404) - @patch('contentstore.views.transcript_settings.create_or_update_video_transcript') @patch('contentstore.views.transcript_settings.get_available_transcript_languages', Mock(return_value=['en'])) def test_transcript_upload_handler(self, mock_create_or_update_video_transcript): """ Tests that transcript upload handler works as expected. """ - transcript_upload_url = self.get_url_for_course_key(self.course.id) transcript_file_stream = BytesIO('0\n00:00:00,010 --> 00:00:00,100\nПривіт, edX вітає вас.\n\n') # Make request to transcript upload handler response = self.client.post( - transcript_upload_url, + self.view_url, { 'edx_video_id': '123', 'language_code': 'en', @@ -395,9 +366,8 @@ def test_transcript_upload_handler_missing_attrs(self, request_payload, expected """ Tests the transcript upload handler when the required attributes are missing. """ - transcript_upload_url = self.get_url_for_course_key(self.course.id) # Make request to transcript upload handler - response = self.client.post(transcript_upload_url, request_payload, format='multipart') + response = self.client.post(self.view_url, request_payload, format='multipart') self.assertEqual(response.status_code, 400) self.assertEqual(json.loads(response.content)['error'], expected_error_message) @@ -407,14 +377,13 @@ def test_transcript_upload_handler_existing_transcript(self): Tests that upload handler do not update transcript's language if a transcript with the same language already present for an edx_video_id. """ - transcript_upload_url = self.get_url_for_course_key(self.course.id) # Make request to transcript upload handler request_payload = { 'edx_video_id': '1234', 'language_code': 'en', 'new_language_code': 'es' } - response = self.client.post(transcript_upload_url, request_payload, format='multipart') + response = self.client.post(self.view_url, request_payload, format='multipart') self.assertEqual(response.status_code, 400) self.assertEqual( json.loads(response.content)['error'], @@ -427,10 +396,9 @@ def test_transcript_upload_handler_with_image(self): Tests the transcript upload handler with an image file. """ with make_image_file() as image_file: - transcript_upload_url = self.get_url_for_course_key(self.course.id) # Make request to transcript upload handler response = self.client.post( - transcript_upload_url, + self.view_url, { 'edx_video_id': '123', 'language_code': 'en', @@ -451,11 +419,10 @@ def test_transcript_upload_handler_with_invalid_transcript(self): """ Tests the transcript upload handler with an invalid transcript file. """ - transcript_upload_url = self.get_url_for_course_key(self.course.id) transcript_file_stream = BytesIO('An invalid transcript SubRip file content') # Make request to transcript upload handler response = self.client.post( - transcript_upload_url, + self.view_url, { 'edx_video_id': '123', 'language_code': 'en', @@ -473,11 +440,7 @@ def test_transcript_upload_handler_with_invalid_transcript(self): @ddt.ddt -@patch( - 'openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled', - Mock(return_value=True) -) -class TranscriptUploadTest(CourseTestCase): +class TranscriptDeleteTest(CourseTestCase): """ Tests for transcript deletion handler. """ @@ -504,16 +467,6 @@ def test_405_with_not_allowed_request_method(self): response = self.client.post(transcript_delete_url) self.assertEqual(response.status_code, 405) - def test_404_with_feature_disabled(self): - """ - Verify that 404 is returned if the corresponding feature is disabled. - """ - transcript_delete_url = self.get_url_for_course_key(self.course.id, edx_video_id='test_id', language_code='en') - with patch('openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled') as feature: - feature.return_value = False - response = self.client.delete(transcript_delete_url) - self.assertEqual(response.status_code, 404) - def test_404_with_non_staff_user(self): """ Verify that 404 is returned if the user doesn't have studio write access. diff --git a/cms/djangoapps/contentstore/views/tests/test_transcripts.py b/cms/djangoapps/contentstore/views/tests/test_transcripts.py index 9d99c072d9af..0fae175fd917 100644 --- a/cms/djangoapps/contentstore/views/tests/test_transcripts.py +++ b/cms/djangoapps/contentstore/views/tests/test_transcripts.py @@ -1,9 +1,10 @@ """Tests for items views.""" import copy +from codecs import BOM_UTF8 import ddt import json -import os +from mock import patch, Mock import tempfile import textwrap from uuid import uuid4 @@ -11,7 +12,7 @@ from django.conf import settings from django.core.urlresolvers import reverse from django.test.utils import override_settings -from mock import patch, Mock +from edxval.api import create_video from opaque_keys.edx.keys import UsageKey from contentstore.tests.utils import CourseTestCase, mock_requests_get @@ -20,11 +21,32 @@ from xmodule.contentstore.django import contentstore from xmodule.exceptions import NotFoundError from xmodule.modulestore.django import modulestore -from xmodule.video_module import transcripts_utils +from xmodule.video_module.transcripts_utils import ( + GetTranscriptsFromYouTubeException, + get_video_transcript_content, + remove_subs_from_store, + Transcript, +) TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE) TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex +SRT_TRANSCRIPT_CONTENT = """0 +00:00:10,500 --> 00:00:13,000 +Elephant's Dream + +1 +00:00:15,000 --> 00:00:18,000 +At the left we can see... + +""" + +SJSON_TRANSCRIPT_CONTENT = Transcript.convert( + SRT_TRANSCRIPT_CONTENT, + Transcript.SRT, + Transcript.SJSON, +) + @override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE) class BaseTranscripts(CourseTestCase): @@ -95,498 +117,716 @@ def get_youtube_ids(self): 1.5: item.youtube_id_1_5 } + def create_non_video_module(self): + """ + Setup non video module for tests. + """ + data = { + 'parent_locator': unicode(self.course.location), + 'category': 'non_video', + 'type': 'non_video' + } + response = self.client.ajax_post('/xblock/', data) + usage_key = self._get_usage_key(response) + item = modulestore().get_item(usage_key) + item.data = '' + modulestore().update_item(item, self.user.id) + + return usage_key + + def assert_response(self, response, expected_status_code, expected_message): + response_content = json.loads(response.content) + self.assertEqual(response.status_code, expected_status_code) + self.assertEqual(response_content['status'], expected_message) + +@ddt.ddt class TestUploadTranscripts(BaseTranscripts): """ - Tests for '/transcripts/upload' url. + Tests for '/transcripts/upload' endpoint. """ def setUp(self): - """Create initial data.""" super(TestUploadTranscripts, self).setUp() + self.contents = { + 'good': SRT_TRANSCRIPT_CONTENT, + 'bad': 'Some BAD data', + } + # Create temporary transcript files + self.good_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.srt') + self.bad_data_srt_file = self.create_transcript_file(content=self.contents['bad'], suffix='.srt') + self.bad_name_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.bad') + self.bom_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.srt', include_bom=True) + + # Setup a VEDA produced video and persist `edx_video_id` in VAL. + create_video({ + 'edx_video_id': u'123-456-789', + 'status': 'upload', + 'client_video_id': u'Test Video', + 'duration': 0, + 'encoded_videos': [], + 'courses': [unicode(self.course.id)] + }) - self.good_srt_file = tempfile.NamedTemporaryFile(suffix='.srt') - self.good_srt_file.write(textwrap.dedent(""" - 1 - 00:00:10,500 --> 00:00:13,000 - Elephant's Dream - - 2 - 00:00:15,000 --> 00:00:18,000 - At the left we can see... - """)) - self.good_srt_file.seek(0) - - self.bad_data_srt_file = tempfile.NamedTemporaryFile(suffix='.srt') - self.bad_data_srt_file.write('Some BAD data') - self.bad_data_srt_file.seek(0) - - self.bad_name_srt_file = tempfile.NamedTemporaryFile(suffix='.BAD') - self.bad_name_srt_file.write(textwrap.dedent(""" - 1 - 00:00:10,500 --> 00:00:13,000 - Elephant's Dream - - 2 - 00:00:15,000 --> 00:00:18,000 - At the left we can see... - """)) - self.bad_name_srt_file.seek(0) - - self.ufeff_srt_file = tempfile.NamedTemporaryFile(suffix='.srt') - - def test_success_video_module_source_subs_uploading(self): - self.item.data = textwrap.dedent(""" - - """) - modulestore().update_item(self.item, self.user.id) + # Add clean up handler + self.addCleanup(self.clean_temporary_transcripts) - link = reverse('upload_transcripts') - filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0] - resp = self.client.post(link, { - 'locator': self.video_usage_key, - 'transcript-file': self.good_srt_file, - 'video_list': json.dumps([{ - 'type': 'html5', - 'video': filename, - 'mode': 'mp4', - }]) - }) - self.assertEqual(resp.status_code, 200) - self.assertEqual(json.loads(resp.content).get('status'), 'Success') + def create_transcript_file(self, content, suffix, include_bom=False): + """ + Setup a transcript file with suffix and content. + """ + transcript_file = tempfile.NamedTemporaryFile(suffix=suffix) + wrapped_content = textwrap.dedent(content) + if include_bom: + wrapped_content = wrapped_content.encode('utf-8-sig') + # Verify that ufeff(BOM) character is in content. + self.assertIn(BOM_UTF8, wrapped_content) - item = modulestore().get_item(self.video_usage_key) - self.assertEqual(item.sub, filename) + transcript_file.write(wrapped_content) + transcript_file.seek(0) - content_location = StaticContent.compute_location( - self.course.id, 'subs_{0}.srt.sjson'.format(filename)) - self.assertTrue(contentstore().find(content_location)) + return transcript_file - def test_fail_data_without_id(self): - link = reverse('upload_transcripts') - resp = self.client.post(link, {'transcript-file': self.good_srt_file}) - self.assertEqual(resp.status_code, 400) - self.assertEqual(json.loads(resp.content).get('status'), 'POST data without "locator" form data.') + def clean_temporary_transcripts(self): + """ + Close transcript files gracefully. + """ + self.good_srt_file.close() + self.bad_data_srt_file.close() + self.bad_name_srt_file.close() + self.bom_srt_file.close() - def test_fail_data_without_file(self): - link = reverse('upload_transcripts') - resp = self.client.post(link, {'locator': self.video_usage_key}) - self.assertEqual(resp.status_code, 400) - self.assertEqual(json.loads(resp.content).get('status'), 'POST data without "file" form data.') + def upload_transcript(self, locator, transcript_file, edx_video_id=None): + """ + Uploads a transcript for a video + """ + payload = {} + if locator: + payload.update({'locator': locator}) - def test_fail_data_with_bad_locator(self): - # Test for raising `InvalidLocationError` exception. - link = reverse('upload_transcripts') - filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0] - resp = self.client.post(link, { - 'locator': 'BAD_LOCATOR', - 'transcript-file': self.good_srt_file, - 'video_list': json.dumps([{ - 'type': 'html5', - 'video': filename, - 'mode': 'mp4', - }]) - }) - self.assertEqual(resp.status_code, 400) - self.assertEqual(json.loads(resp.content).get('status'), "Can't find item by locator.") + if edx_video_id is not None: + payload.update({'edx_video_id': edx_video_id}) - # Test for raising `ItemNotFoundError` exception. - link = reverse('upload_transcripts') - filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0] - resp = self.client.post(link, { - 'locator': '{0}_{1}'.format(self.video_usage_key, 'BAD_LOCATOR'), - 'transcript-file': self.good_srt_file, - 'video_list': json.dumps([{ - 'type': 'html5', - 'video': filename, - 'mode': 'mp4', - }]) - }) - self.assertEqual(resp.status_code, 400) - self.assertEqual(json.loads(resp.content).get('status'), "Can't find item by locator.") + if transcript_file: + payload.update({'transcript-file': transcript_file}) - def test_fail_for_non_video_module(self): - # non_video module: setup - data = { - 'parent_locator': unicode(self.course.location), - 'category': 'non_video', - 'type': 'non_video' - } - resp = self.client.ajax_post('/xblock/', data) - usage_key = self._get_usage_key(resp) - item = modulestore().get_item(usage_key) - item.data = '' - modulestore().update_item(item, self.user.id) + upload_url = reverse('upload_transcripts') + response = self.client.post(upload_url, payload) - # non_video module: testing + return response - link = reverse('upload_transcripts') - filename = os.path.splitext(os.path.basename(self.good_srt_file.name))[0] - resp = self.client.post(link, { - 'locator': unicode(usage_key), - 'transcript-file': self.good_srt_file, - 'video_list': json.dumps([{ - 'type': 'html5', - 'video': filename, - 'mode': 'mp4', - }]) - }) - self.assertEqual(resp.status_code, 400) - self.assertEqual(json.loads(resp.content).get('status'), 'Transcripts are supported only for "video" modules.') + @ddt.data( + (u'123-456-789', False), + (u'', False), + (u'123-456-789', True) + ) + @ddt.unpack + def test_transcript_upload_success(self, edx_video_id, include_bom): + """ + Tests transcript file upload to video component works as + expected in case of following: - def test_fail_bad_xml(self): - self.item.data = '<<