Files
Feanil Patel b6b9599f18 test: Drop useless tests.
We had a set of i18n tests that testing basic english is working on a
page that's going away.  It's not necessary. And then we have a skipped
test that tests i18n. It would be good to have actual i18n tests and
there are some other ones in this file at the block level that do test
i18n but since this test isn't actually testing anything novel there's
no need to keep it.
2025-10-17 10:25:03 -04:00

165 lines
6.9 KiB
Python

"""
Tests for validate Internationalization and XBlock i18n service.
"""
import gettext
from unittest import mock
from django.utils import translation
from django.utils.translation import get_language
from xblock.core import XBlock
from xmodule.modulestore.django import XBlockI18nService
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory
from xmodule.tests.test_export import PureXBlock
from cms.djangoapps.contentstore.views.preview import _prepare_runtime_for_preview
class FakeTranslations(XBlockI18nService):
"""A test GNUTranslations class that takes a map of msg -> translations."""
def __init__(self, translations): # pylint: disable=super-init-not-called
self.translations = translations
def ugettext(self, msgid):
"""
Mock override for ugettext translation operation
"""
return self.translations.get(msgid, msgid)
gettext = ugettext
@staticmethod
def translator(locales_map): # pylint: disable=method-hidden
"""Build mock translator for the given locales.
Returns a mock gettext.translation function that uses
individual TestTranslations to translate in the given locales.
:param locales_map: A map from locale name to a translations map.
{
'es': {'Hi': 'Hola', 'Bye': 'Adios'},
'zh': {'Hi': 'Ni Hao', 'Bye': 'Zaijian'}
}
"""
def _translation(domain, localedir=None, languages=None): # pylint: disable=unused-argument
"""
return gettext.translation for given language
"""
if languages:
language = languages[0]
if language in locales_map:
return FakeTranslations(locales_map[language])
return gettext.NullTranslations()
return _translation
class TestXBlockI18nService(ModuleStoreTestCase):
""" Test XBlockI18nService """
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
@XBlock.register_temp_plugin(PureXBlock, 'pure')
def setUp(self):
""" Setting up tests """
super().setUp()
self.test_language = 'dummy language'
self.request = mock.Mock()
self.course = CourseFactory.create()
self.block = BlockFactory(category="pure", parent=self.course)
_prepare_runtime_for_preview(self.request, self.block)
self.addCleanup(translation.deactivate)
def get_block_i18n_service(self, block):
"""
return the block i18n service.
"""
i18n_service = self.block.runtime.service(block, 'i18n')
self.assertIsNotNone(i18n_service)
self.assertIsInstance(i18n_service, XBlockI18nService)
return i18n_service
def test_django_service_translation_works(self):
"""
Test django translation service works fine.
"""
class wrap_ugettext_with_xyz: # pylint: disable=invalid-name
"""
A context manager function that just adds 'XYZ ' to the front
of all strings of the module ugettext function.
"""
def __init__(self, module):
self.module = module
self.old_ugettext = module.gettext
def __enter__(self):
def new_ugettext(*args, **kwargs):
""" custom function """
output = self.old_ugettext(*args, **kwargs)
return "XYZ " + output
self.module.ugettext = new_ugettext
self.module.gettext = new_ugettext
def __exit__(self, _type, _value, _traceback):
self.module.ugettext = self.old_ugettext
self.module.gettext = self.old_ugettext
i18n_service = self.get_block_i18n_service(self.block)
# Activate french, so that if the fr files haven't been loaded, they will be loaded now.
with translation.override("fr"):
french_translation = translation.trans_real._active.value # pylint: disable=protected-access
# wrap the ugettext functions so that 'XYZ ' will prefix each translation
with wrap_ugettext_with_xyz(french_translation):
self.assertEqual(i18n_service.ugettext(self.test_language), 'XYZ dummy language')
# Check that the old ugettext has been put back into place
self.assertEqual(i18n_service.ugettext(self.test_language), 'dummy language')
@mock.patch('django.utils.translation.gettext', mock.Mock(return_value='XYZ-TEST-LANGUAGE'))
def test_django_translator_in_use_with_empty_block(self):
"""
Test: Django default translator should in use if we have an empty block
"""
i18n_service = XBlockI18nService(None)
self.assertEqual(i18n_service.ugettext(self.test_language), 'XYZ-TEST-LANGUAGE')
@mock.patch('django.utils.translation.gettext', mock.Mock(return_value='XYZ-TEST-LANGUAGE'))
def test_message_catalog_translations(self):
"""
Test: Message catalog from FakeTranslation should return required translations.
"""
_translator = FakeTranslations.translator(
{
'es': {'Hello': 'es-hello-world'},
'fr': {'Hello': 'fr-hello-world'},
},
)
localedir = '/translations'
translation.activate("es")
with mock.patch('gettext.translation', return_value=_translator(domain='text', localedir=localedir,
languages=[get_language()])):
i18n_service = self.get_block_i18n_service(self.block)
self.assertEqual(i18n_service.ugettext('Hello'), 'es-hello-world')
translation.activate("ar")
with mock.patch('gettext.translation', return_value=_translator(domain='text', localedir=localedir,
languages=[get_language()])):
i18n_service = self.get_block_i18n_service(self.block)
self.assertEqual(i18n_service.gettext('Hello'), 'Hello')
self.assertNotEqual(i18n_service.gettext('Hello'), 'fr-hello-world')
self.assertNotEqual(i18n_service.gettext('Hello'), 'es-hello-world')
translation.activate("fr")
with mock.patch('gettext.translation', return_value=_translator(domain='text', localedir=localedir,
languages=[get_language()])):
i18n_service = self.get_block_i18n_service(self.block)
self.assertEqual(i18n_service.ugettext('Hello'), 'fr-hello-world')
def test_i18n_service_callable(self):
"""
Test: i18n service should be callable in studio.
"""
self.assertTrue(callable(self.block.runtime._services.get('i18n'))) # pylint: disable=protected-access