-
Notifications
You must be signed in to change notification settings - Fork 9
Adds test case for studio_metadata_mixin class #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
farhan
wants to merge
1
commit into
main
Choose a base branch
from
farhan/add-unit-test-video-block
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
180 changes: 180 additions & 0 deletions
180
xblocks_contrib/video/tests/test_studio_metadata_mixin.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| """Tests for StudioMetadataMixin.editable_metadata_fields (VideoBlock).""" | ||
| from unittest.mock import Mock, patch | ||
|
|
||
| from django.test import SimpleTestCase | ||
| from django.test.utils import override_settings | ||
| from opaque_keys.edx.locator import CourseLocator | ||
| from xblock.field_data import DictFieldData | ||
| from xblock.fields import ScopeIds | ||
|
|
||
| from xblocks_contrib.video.exceptions import TranscriptNotFoundError | ||
| from xblocks_contrib.video.tests.test_utils import DummyRuntime | ||
| from xblocks_contrib.video.video import VideoBlock | ||
|
|
||
| ALL_LANGUAGES = ( | ||
| ("en", "English"), | ||
| ("eo", "Esperanto"), | ||
| ("ur", "Urdu"), | ||
| ) | ||
|
|
||
|
|
||
| @override_settings(ALL_LANGUAGES=ALL_LANGUAGES) | ||
| class TestEditableMetadataFieldsProperty(SimpleTestCase): | ||
| """ | ||
| Unit tests for VideoBlock.editable_metadata_fields property. | ||
|
|
||
| The property enriches the raw editable fields returned by | ||
| _get_editable_metadata_fields with video-specific customisations: | ||
| transcript language lists, special types for certain fields, etc. | ||
| """ | ||
|
|
||
| @staticmethod | ||
| def _instantiate_block(**field_data): | ||
| """Instantiate a VideoBlock with a DummyRuntime.""" | ||
| 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), | ||
| ) | ||
|
|
||
| def setUp(self): | ||
| super().setUp() | ||
| self.block = self._instantiate_block() | ||
|
|
||
| def _base_fields(self, include_license=True): | ||
| """Return a minimal editable-fields dict that satisfies the property's expectations.""" | ||
| fields = { | ||
| 'sub': {'type': 'Generic', 'value': ''}, | ||
| 'transcripts': {'type': 'Dict', 'value': {}}, | ||
| 'edx_video_id': {'type': 'Generic', 'value': ''}, | ||
| 'public_access': {'type': 'Select', 'value': False}, | ||
| 'handout': {'type': 'Generic', 'value': ''}, | ||
| } | ||
| if include_license: | ||
| fields['license'] = {'type': 'License', 'value': ''} | ||
| return fields | ||
|
|
||
| @staticmethod | ||
| def _service_stub(service_name, service_obj): | ||
| """Return a runtime.service stub that serves service_obj only for service_name.""" | ||
| return lambda _block, name: service_obj if name == service_name else None | ||
|
|
||
| def _get_fields(self, include_license=False, public_url=None, transcripts=None, service=None): | ||
| """ | ||
| Call editable_metadata_fields with standard mocks applied. | ||
|
|
||
| Pass service=<callable> to simulate a real runtime service (e.g. for | ||
| licensing or video_config tests); omit it to have all service calls | ||
| return None (skipping license and English-transcript logic). | ||
| """ | ||
| service_kwargs = {'side_effect': service} if service else {'return_value': None} | ||
| with ( | ||
| patch.object(self.block, '_get_editable_metadata_fields', | ||
| return_value=self._base_fields(include_license)), | ||
| patch.object(self.block, 'get_transcripts_info', | ||
| return_value={'sub': self.block.sub, 'transcripts': transcripts or {}}), | ||
| patch.object(self.block, 'get_public_video_url', return_value=public_url), | ||
| patch.object(self.block.runtime, 'service', **service_kwargs), | ||
| ): | ||
| return self.block.editable_metadata_fields | ||
|
|
||
| # ------------------------------------------------------------------ | ||
| # Field-level modifications | ||
| # ------------------------------------------------------------------ | ||
|
|
||
| def test_default_field_modifications(self): | ||
| """Verify all field-level changes made by editable_metadata_fields with default args.""" | ||
| fields = self._get_fields() | ||
| self.assertNotIn('sub', fields) | ||
| self.assertTrue(fields['transcripts']['custom']) | ||
| self.assertEqual(fields['transcripts']['type'], 'VideoTranslations') | ||
| self.assertEqual(fields['transcripts']['languages'], [ | ||
| {'label': 'English', 'code': 'en'}, | ||
| {'label': 'Esperanto', 'code': 'eo'}, | ||
| {'label': 'Urdu', 'code': 'ur'}, | ||
| ]) | ||
| # DummyRuntime.handler_url returns a fixed path used by the transcript upload widget | ||
| self.assertEqual(fields['transcripts']['urlRoot'], '/handler/block/handler') | ||
| self.assertEqual(fields['edx_video_id']['type'], 'VideoID') | ||
| self.assertEqual(fields['handout']['type'], 'FileUploader') | ||
| # public_access type is always set; url is None when no public video URL is available | ||
| self.assertEqual(fields['public_access']['type'], 'PublicAccess') | ||
| self.assertIsNone(fields['public_access']['url']) | ||
|
|
||
| def test_transcripts_value_passthrough(self): | ||
| """Verify transcripts.value is passed through from get_transcripts_info.""" | ||
| fields = self._get_fields(transcripts={'fr': 'french.srt'}) | ||
| self.assertEqual(fields['transcripts']['value'], {'fr': 'french.srt'}) | ||
|
|
||
| def test_public_access_enriched_when_url_present(self): | ||
| """Verify public_access url is populated when a public video URL is available.""" | ||
| fields = self._get_fields(public_url='https://example.com/video') | ||
| self.assertEqual(fields['public_access']['type'], 'PublicAccess') | ||
| self.assertEqual(fields['public_access']['url'], 'https://example.com/video') | ||
|
|
||
| # ------------------------------------------------------------------ | ||
| # License field handling | ||
| # ------------------------------------------------------------------ | ||
|
|
||
| def test_license_removed_when_licensing_disabled(self): | ||
| """'license' is removed when the settings service reports licensing_enabled=False.""" | ||
| settings_service = Mock() | ||
| settings_service.get_settings_bucket.return_value = {'licensing_enabled': False} | ||
| fields = self._get_fields( | ||
| include_license=True, | ||
| service=self._service_stub('settings', settings_service), | ||
| ) | ||
| self.assertNotIn('license', fields) | ||
|
|
||
| def test_license_kept_when_licensing_enabled(self): | ||
| """'license' is kept when the settings service reports licensing_enabled=True.""" | ||
| settings_service = Mock() | ||
| settings_service.get_settings_bucket.return_value = {'licensing_enabled': True} | ||
| fields = self._get_fields( | ||
| include_license=True, | ||
| service=self._service_stub('settings', settings_service), | ||
| ) | ||
| self.assertIn('license', fields) | ||
|
|
||
| def test_license_retained_when_no_settings_service(self): | ||
| """'license' is retained when no settings service is available (service returns None).""" | ||
| fields = self._get_fields(include_license=True) | ||
| self.assertIn('license', fields) | ||
|
|
||
| # ------------------------------------------------------------------ | ||
| # English transcript lookup via video_config service | ||
| # ------------------------------------------------------------------ | ||
|
|
||
| def test_english_transcript_found_added_to_value(self): | ||
| """When video_config returns an English transcript, it is added to transcripts.value.""" | ||
| video_config = Mock() | ||
| video_config.get_transcript.return_value = ('content', 'en_subs_id', 'txt') | ||
| fields = self._get_fields( | ||
| transcripts={}, | ||
| service=self._service_stub('video_config', video_config), | ||
| ) | ||
| self.assertEqual(fields['transcripts']['value'], {'en': 'en_subs_id'}) | ||
|
|
||
| def test_english_transcript_merged_with_existing_transcripts(self): | ||
| """English transcript from video_config is merged with, not replacing, existing transcripts.""" | ||
| video_config = Mock() | ||
| video_config.get_transcript.return_value = ('content', 'en_subs_id', 'txt') | ||
| fields = self._get_fields( | ||
| transcripts={'fr': 'french.srt'}, | ||
| service=self._service_stub('video_config', video_config), | ||
| ) | ||
| self.assertEqual(fields['transcripts']['value'], {'fr': 'french.srt', 'en': 'en_subs_id'}) | ||
|
|
||
| def test_transcript_not_found_leaves_value_unchanged(self): | ||
| """When video_config raises TranscriptNotFoundError, transcripts.value is not modified.""" | ||
| video_config = Mock() | ||
| video_config.get_transcript.side_effect = TranscriptNotFoundError | ||
| fields = self._get_fields( | ||
| transcripts={'fr': 'french.srt'}, | ||
| service=self._service_stub('video_config', video_config), | ||
| ) | ||
| self.assertNotIn('en', fields['transcripts']['value']) | ||
| self.assertEqual(fields['transcripts']['value'], {'fr': 'french.srt'}) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.