Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions xblocks_contrib/test_settings.py → test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
INSTALLED_APPS = [
"django.contrib.auth",
"django.contrib.contenttypes",
"edxval",
]

DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
}
}

TRANSCRIPT_LANG_CACHE_TIMEOUT = 60 * 60 * 24 # 24 hours
5 changes: 4 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ max-line-length = 120
match-dir = (?!migrations)

[pytest]
DJANGO_SETTINGS_MODULE = xblocks_contrib.test_settings
DJANGO_SETTINGS_MODULE = test_settings
django_find_project = false
addopts = --cov xblocks_contrib --cov-report term-missing --cov-report xml
norecursedirs = .* docs requirements site-packages

[testenv]
setenv =
PYTHONPATH = {toxinidir}
DJANGO_SETTINGS_MODULE = test_settings
deps =
django42: Django>=4.2,<5.0
django52: Django>=5.2,<6.0
Expand Down
113 changes: 113 additions & 0 deletions xblocks_contrib/video/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@

from collections import defaultdict
from unittest.mock import Mock

from web_fragments.fragment import Fragment
from xblock.core import XBlockAside
from xblock.field_data import DictFieldData
from xblock.fields import Scope, ScopeIds, String
from xblock.test.tools import TestRuntime

EXPORT_IMPORT_STATIC_DIR = 'static'
VALIDATION_MESSAGE_WARNING = "warning"

# Mock for openedx save_to_store (openedx.core.djangoapps.video_config.transcripts_utils);
# not available in standalone xblocks-contrib. Use in tests that need to "save" transcript files.
save_to_store = Mock(name='save_to_store')

# Transcript text used by video_config mock for index_dictionary / get_transcript (lang key).
_TRANSCRIPT_TEXT_BY_LANG = {
'ge': 'sprechen sie deutsch? Ja, ich spreche Deutsch',
'hr': 'Dobar dan! Kako ste danas?',
}


def _mock_available_translations(block, transcripts_info, verify_assets=True):
"""Behave like openedx video_config: return transcript langs or ['en'] when empty."""
transcripts = (
transcripts_info.get('transcripts', {})
if isinstance(transcripts_info, dict) else {}
)
if transcripts:
return list(transcripts.keys())
try:
from django.conf import settings
features = getattr(settings, 'FEATURES', {})
if features.get('FALLBACK_TO_ENGLISH_TRANSCRIPTS', True):
return ['en']
except Exception:
return ['en']
return []


def _mock_get_transcript(block, lang=None, output_format=None):
"""Return (transcript_text,) so index_dictionary can use [0].replace(...)."""
text = _TRANSCRIPT_TEXT_BY_LANG.get(lang, '')
return (text,)


class DummyRuntime(TestRuntime):
"""
Construct a test DummyRuntime instance.
"""

def __init__(self, render_template=None, **kwargs):
services = kwargs.setdefault('services', {})
services['field-data'] = DictFieldData({})
video_config_mock = Mock(name='video_config')
video_config_mock.available_translations = Mock(side_effect=_mock_available_translations)
video_config_mock.get_transcript = Mock(side_effect=_mock_get_transcript)
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)
# Use a Mock with root_path so edxval.api create_transcript_objects works
# (it does resource_fs.root_path.split('/drafts')[0]). MemoryFS has no root_path.
self._resources_fs = Mock(name='DummyRuntime.resources_fs', root_path='.')
self._asides = defaultdict(list)

# TODO: Need to look into all asided code, do we need to add any functionality related to it in edx-platform?
def get_asides(self, block):
return self._asides.get(block.scope_ids.usage_id, [])

def get_aside_of_type(self, block, aside_type):
for aside in self._asides.get(block.scope_ids.usage_id, []):
if getattr(aside.scope_ids, 'block_type', None) == aside_type:
return aside
return super().get_aside_of_type(block, aside_type)

def parse_asides(self, node, definition_id, usage_id, id_generator):
asides = []
for child in node:
if child.get('xblock-family') == 'xblock_asides.v1':
# Simplified mock parser for tests
aside_scope_ids = ScopeIds(None, child.tag, definition_id, usage_id)
aside = AsideTestType(runtime=self, scope_ids=aside_scope_ids)
aside.tag = child.tag
for attr, val in child.attrib.items():
if attr in aside.fields:
setattr(aside, attr, val)
asides.append(aside)
self._asides[usage_id].append(aside)
return asides

@property
def resources_fs(self):
return self._resources_fs


class AsideTestType(XBlockAside):
"""
Test Aside type
"""
FRAG_CONTENT = "<p>Aside rendered</p>"

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)
Loading
Loading