diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 3f221aca..d72ffa15 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -12,7 +12,7 @@ jobs: name: Run Python Tests and Coverage runs-on: ubuntu-latest strategy: - fail-fast: true + fail-fast: false matrix: python-version: ["3.11", "3.12"] toxenv: [quality, docs, django42, django52] diff --git a/xblocks_contrib/video/tests/__init__.py b/xblocks_contrib/video/tests/__init__.py index 6fa36e42..3c6d65a1 100644 --- a/xblocks_contrib/video/tests/__init__.py +++ b/xblocks_contrib/video/tests/__init__.py @@ -1,3 +1,9 @@ """ Tests for the video xblock. """ + +from unittest.mock import Mock +from xblocks_contrib.video.video import VideoBlock + + +VideoBlock.add_aside = Mock() diff --git a/xblocks_contrib/video/tests/test_utils.py b/xblocks_contrib/video/tests/test_utils.py new file mode 100644 index 00000000..25c9ba0a --- /dev/null +++ b/xblocks_contrib/video/tests/test_utils.py @@ -0,0 +1,64 @@ +from unittest.mock import Mock + +from web_fragments.fragment import Fragment +from xblock.core import XBlockAside +from xblock.fields import Scope, String +from xblock.runtime import DictKeyValueStore, KvsFieldData, Runtime +from xblock.test.tools import TestRuntime + +EXPORT_IMPORT_STATIC_DIR = 'static' +VALIDATION_MESSAGE_WARNING = "warning" + + +class DummyRuntime(TestRuntime, Runtime): + """ + Construct a test DummyRuntime instance. + """ + + def __init__(self, render_template=None, **kwargs): + services = kwargs.setdefault('services', {}) + services['field-data'] = KvsFieldData(DictKeyValueStore()) + video_config_mock = Mock(name='video_config') + video_config_mock.available_translations = lambda block, transcripts_info, verify_assets=True: [] + services['video_config'] = video_config_mock + + # Ignore load_error_blocks as it's not supported by modern TestRuntime + kwargs.pop('load_error_blocks', None) + + super().__init__(**kwargs) + self._resources_fs = Mock(name='DummyRuntime.resources_fs', root_path='.') + + def parse_asides(self, node, def_id, usage_id, id_generator): + """pull the asides out of the xml payload and instantiate them""" + aside_children = [] + for child in node.iterchildren(): + # get xblock-family from node + xblock_family = child.attrib.pop('xblock-family', None) + if xblock_family: + xblock_family = self._family_id_to_superclass(xblock_family) + if issubclass(xblock_family, XBlockAside): + aside_children.append(child) + # now process them & remove them from the xml payload + for child in aside_children: + self._aside_from_xml(child, def_id, usage_id) + node.remove(child) + return aside_children + + @property + def resources_fs(self): + return self._resources_fs + + +class AsideTestType(XBlockAside): + """ + Test Aside type + """ + FRAG_CONTENT = "

Aside rendered

" + + content = String(default="default_content", scope=Scope.content) + data_field = String(default="default_data", scope=Scope.settings) + + @XBlockAside.aside_for('student_view') + def student_view_aside(self, block, context): # pylint: disable=unused-argument + """Add to the student view""" + return Fragment(self.FRAG_CONTENT) diff --git a/xblocks_contrib/video/tests/test_video.py b/xblocks_contrib/video/tests/test_video.py new file mode 100644 index 00000000..4b6cd4e0 --- /dev/null +++ b/xblocks_contrib/video/tests/test_video.py @@ -0,0 +1,1164 @@ +# pylint: disable=protected-access +"""Test for Video XBlock functional logic. +These test data read from xml, not from mongo. + +We have a ModuleStoreTestCase class defined in +xmodule/modulestore/tests/django_utils.py. You can +search for usages of this in the cms and lms tests for examples. You use +this so that it will do things like point the modulestore setting to mongo, +flush the contentstore before and after, load the templates, etc. +You can then use the CourseFactory and BlockFactory as defined +in xmodule/modulestore/tests/factories.py to create +the course, section, subsection, unit, etc. +""" + + +import datetime +import shutil +import unittest +from tempfile import mkdtemp +from unittest.mock import ANY, MagicMock, Mock, patch +from uuid import uuid4 + +from django.test.utils import override_settings +import pytest +import ddt +from django.conf import settings +from django.test import TestCase +from fs.osfs import OSFS +from lxml import etree +from opaque_keys.edx.keys import CourseKey +from opaque_keys.edx.locator import CourseLocator +from xblock.field_data import DictFieldData +from xblock.fields import ScopeIds + +from xblocks_contrib.video.tests.test_utils import ( + VALIDATION_MESSAGE_WARNING, + AsideTestType, + DummyRuntime, +) +from xblocks_contrib.video.video import ( + EXPORT_IMPORT_STATIC_DIR, + VideoBlock, + create_youtube_string, +) +from xblock.core import XBlockAside + + +SRT_FILEDATA = ''' +0 +00:00:00,270 --> 00:00:02,720 +sprechen sie deutsch? + +1 +00:00:02,720 --> 00:00:05,430 +Ja, ich spreche Deutsch +''' + +CRO_SRT_FILEDATA = ''' +0 +00:00:00,270 --> 00:00:02,720 +Dobar dan! + +1 +00:00:02,720 --> 00:00:05,430 +Kako ste danas? +''' + +YOUTUBE_SUBTITLES = ( + "Sample trascript line 1. " + "Sample trascript line 2. " + "Sample trascript line 3." +) + +MOCKED_YOUTUBE_TRANSCRIPT_API_RESPONSE = ''' + + Sample trascript line 1. + Sample trascript line 2. + Sample trascript line 3. + +''' + +ALL_LANGUAGES = ( + ["en", "English"], + ["eo", "Esperanto"], + ["ur", "Urdu"] +) + + +def instantiate_block(**field_data): + """ + Instantiate block with most properties. + """ + if field_data.get('data', None): + field_data = VideoBlock.parse_video_xml(field_data['data']) + system = DummyRuntime() + course_key = CourseLocator('org', 'course', 'run') + usage_key = course_key.make_usage_key('video', 'SampleProblem') + return system.construct_xblock_from_class( + VideoBlock, + scope_ids=ScopeIds(None, None, usage_key, usage_key), + field_data=DictFieldData(field_data), + ) + + +# Because of the way xblocks_contrib.video.video imports edxval.api, we +# must mock the entire module, which requires making mock exception classes. + +class _MockValVideoNotFoundError(Exception): + """Mock ValVideoNotFoundError exception""" + pass # lint-amnesty, pylint: disable=unnecessary-pass + + +class _MockValCannotCreateError(Exception): + """Mock ValCannotCreateError exception""" + pass # lint-amnesty, pylint: disable=unnecessary-pass + + +class VideoBlockTest(unittest.TestCase): + """Logic tests for Video XBlock.""" + + raw_field_data = { + 'data': '