# -*- coding: utf-8 -*-
import filecmp
import hashlib
import os
import shutil
import vcr
try:
# Python 3
from urllib.parse import urlencode
except ImportError:
# Python 2
from urllib import urlencode
from django.conf import settings
from django.dispatch import receiver
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.test import TestCase, RequestFactory
from django.test.client import Client
from django import VERSION
from django.urls import reverse, resolve
from django.utils.encoding import force_bytes
import six
from rosetta.conf import settings as rosetta_settings
from rosetta.signals import entry_changed, post_save
from rosetta.storage import get_storage
from rosetta import views
class RosettaTestCase(TestCase):
def __init__(self, *args, **kwargs):
super(RosettaTestCase, self).__init__(*args, **kwargs)
self.curdir = os.path.dirname(__file__)
self.dest_file = os.path.normpath(
os.path.join(self.curdir, '../locale/xx/LC_MESSAGES/django.po')
)
def setUp(self):
from django.contrib.auth.models import User
user = User.objects.create_superuser('test_admin', 'test@test.com', 'test_password')
user2 = User.objects.create_superuser('test_admin2', 'test@test2.com', 'test_password')
user3 = User.objects.create_superuser('test_admin3', 'test@test2.com', 'test_password')
user3.is_staff = False
user3.save()
self.user = user
self.client2 = Client()
self.client.login(username=user.username, password='test_password')
self.client2.login(username=user2.username, password='test_password')
self.__old_settings_languages = settings.LANGUAGES
settings.LANGUAGES = (
('xx', 'dummy language'),
('fr_FR.utf8', 'French (France), UTF8'),
('bs-Cyrl-BA', u'Bosnian (Cyrillic) (Bosnia and Herzegovina)'),
('yy-Anot', u'Yet Another dummy language'),
('zh_Hans', u'Chinese (simplified)'),
)
# This stinks. We'd rather use `override_settings`, but because of
# this bug, it can't be done: https://code.djangoproject.com/ticket/25911
# We're relying on settings.SETTINGS_MODULE to be there
# to get the projetct's base dir
self.__session_engine = settings.SESSION_ENGINE
self.__storage_class = rosetta_settings.STORAGE_CLASS
self.__require_auth = rosetta_settings.ROSETTA_REQUIRES_AUTH
self.__enable_translation = rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS
self.__auto_compile = rosetta_settings.AUTO_COMPILE
self.__admin_panel_embed = rosetta_settings.SHOW_AT_ADMIN_PANEL
shutil.copy(self.dest_file, self.dest_file + '.orig')
def tearDown(self):
settings.LANGUAGES = self.__old_settings_languages
settings.SESSION_ENGINE = self.__session_engine
rosetta_settings.STORAGE_CLASS = self.__storage_class
rosetta_settings.ROSETTA_REQUIRES_AUTH = self.__require_auth
rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS = self.__enable_translation
rosetta_settings.AUTO_COMPILE = self.__auto_compile
rosetta_settings.SHOW_AT_ADMIN_PANEL = self.__admin_panel_embed
shutil.move(self.dest_file + '.orig', self.dest_file)
def copy_po_file_from_template(self, template_path):
"""Utility method to handle swapping a template po file in place for
testing.
"""
src_path = os.path.normpath(os.path.join(self.curdir, template_path))
shutil.copy(src_path, self.dest_file)
@property
def xx_form_url(self):
kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 0}
return reverse('rosetta-form', kwargs=kwargs)
@property
def third_party_file_list_url(self):
return reverse('rosetta-file-list', kwargs={'po_filter': 'third-party'})
def test_1_ListLoading(self):
r = self.client.get(self.third_party_file_list_url)
self.assertTrue(
os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content)
)
def test_2_PickFile(self):
r = self.client.get(self.xx_form_url)
self.assertTrue('dummy language' in str(r.content))
def test_3_DownloadZIP(self):
kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 0}
url = reverse('rosetta-download-file', kwargs=kwargs)
r = self.client.get(url)
self.assertTrue('content-type' in r._headers.keys())
self.assertTrue('application/x-zip' in r._headers.get('content-type'))
def test_4_DoChanges(self):
self.copy_po_file_from_template('./django.po.template')
untranslated_url = self.xx_form_url + '?msg_filter=untranslated'
translated_url = self.xx_form_url + '?msg_filter=translated'
# Load the template file
r = self.client.get(untranslated_url)
# make sure both strings are untranslated
self.assertTrue('dummy language' in str(r.content))
self.assertTrue('String 1' in str(r.content))
self.assertTrue('String 2' in str(r.content))
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
# post a translation
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
r = self.client.post(untranslated_url, data, follow=True)
# reload all untranslated strings
r = self.client.get(untranslated_url)
# the translated string no longer is up for translation
self.assertTrue('String 1' in str(r.content))
self.assertTrue('String 2' not in str(r.content))
# display only translated strings
r = self.client.get(translated_url)
# The translation was persisted
self.assertTrue('String 1' not in str(r.content))
self.assertTrue('String 2' in str(r.content))
self.assertTrue('Hello, world' in str(r.content))
def test_5_TestIssue67(self):
# issue 67: http://code.google.com/p/django-rosetta/issues/detail?id=67
self.copy_po_file_from_template('./django.po.issue67.template')
# Make sure the plurals string is valid
with open(self.dest_file, 'rb') as f_:
content = f_.read()
self.assertTrue('Hello, world' not in six.text_type(content))
self.assertTrue('|| n%100>=20) ? 1 : 2)' in six.text_type(content))
del(content)
r = self.client.get(self.xx_form_url + '?msg_filter=untranslated')
# make sure all strings are untranslated
self.assertTrue('dummy language' in str(r.content))
self.assertTrue('String 1' in str(r.content))
self.assertTrue('String 2' in str(r.content))
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
# post a translation
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
self.client.post(self.xx_form_url + '?msg_filter=untranslated', data)
# Make sure the plurals string is still valid
with open(self.dest_file, 'rb') as f_:
content = f_.read()
self.assertTrue('Hello, world' in str(content))
self.assertTrue('|| n%100>=20) ? 1 : 2)' in str(content))
self.assertTrue('or n%100>=20) ? 1 : 2)' not in str(content))
del(content)
def test_6_ExcludedApps(self):
rosetta_settings.EXCLUDED_APPLICATIONS = ('rosetta',)
r = self.client.get(self.third_party_file_list_url)
self.assertNotContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
rosetta_settings.EXCLUDED_APPLICATIONS = ()
r = self.client.get(self.third_party_file_list_url)
self.assertContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
def test_7_selfInApplist(self):
r = self.client.get(self.third_party_file_list_url)
self.assertContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
url = reverse('rosetta-file-list', kwargs={'po_filter': 'project'})
r = self.client.get(url)
self.assertNotContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
def test_8_hideObsoletes(self):
r = self.client.get(self.xx_form_url)
# not in listing
for p in range(1, 5):
r = self.client.get(self.xx_form_url + '?page=%d' % p)
self.assertTrue('dummy language' in str(r.content))
self.assertTrue('Les deux' not in str(r.content))
r = self.client.get(self.xx_form_url + '?query=Les%20Deux')
self.assertContains(r, 'dummy language')
self.assertNotContains(r, 'Les deux')
def test_9_concurrency(self):
self.copy_po_file_from_template('./django.po.template')
translated_url = self.xx_form_url + '?msg_filter=translated'
untranslated_url = self.xx_form_url + '?msg_filter=untranslated'
# Load the template file
r = self.client.get(untranslated_url)
r2 = self.client2.get(untranslated_url)
self.assertContains(r, 'String 1')
self.assertContains(r2, 'String 1')
self.assertContains(r, 'm_08e4e11e2243d764fc45a5a4fba5d0f2')
data = {'m_08e4e11e2243d764fc45a5a4fba5d0f2': 'Hello, world'}
r = self.client.post(untranslated_url, data, follow=True)
# Client 2 reloads, forces a reload of the catalog; untranslated
# string1 is now translated
r2 = self.client2.get(untranslated_url, follow=True)
self.assertNotContains(r, 'String 1')
self.assertContains(r, 'String 2')
self.assertNotContains(r2, 'String 1')
self.assertContains(r2, 'String 2')
r = self.client.get(untranslated_url)
r2 = self.client2.get(untranslated_url)
self.assertContains(r2, 'String 2')
self.assertContains(r2, 'm_e48f149a8b2e8baa81b816c0edf93890')
self.assertContains(r, 'String 2')
self.assertContains(r, 'm_e48f149a8b2e8baa81b816c0edf93890')
# client 2 posts!
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world, from client two!'}
r2 = self.client2.post(untranslated_url, data, follow=True)
self.assertNotContains(r2, 'save-conflict')
# uh-oh here comes client 1
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world, from client one!'}
r = self.client.post(untranslated_url, data, follow=True)
# An error message is displayed
self.assertContains(r, 'save-conflict')
# client 2 won
with open(self.dest_file, 'r') as po_file:
pofile_content = po_file.read()
self.assertTrue('Hello, world, from client two!' in pofile_content)
# Both clients show all strings, error messages are gone
r = self.client.get(translated_url)
self.assertNotContains(r, 'save-conflict')
r2 = self.client2.get(translated_url)
self.assertNotContains(r2, 'save-conflict')
r = self.client.get(self.xx_form_url)
self.assertNotContains(r, 'save-conflict')
r2 = self.client2.get(self.xx_form_url)
self.assertNotContains(r2, 'save-conflict')
# Both have client's two version
self.assertContains(r, 'Hello, world, from client two!')
self.assertContains(r2, 'Hello, world, from client two!')
def test_10_issue_79_num_entries(self):
self.copy_po_file_from_template('./django.po.issue79.template')
r = self.client.get(self.third_party_file_list_url)
self.assertContains(r, '
1
')
self.assertContains(r, '
0%
')
self.assertContains(r, '
1
')
def test_11_issue_80_tab_indexes(self):
r = self.client.get(self.xx_form_url)
self.assertTrue('tabindex="3"' in str(r.content))
def test_12_issue_82_staff_user(self):
self.client3 = Client()
self.client3.login(username='test_admin3', password='test_password')
# When auth is required, we get an empty response (and a redirect) with
# this user.
settings.ROSETTA_REQUIRES_AUTH = True
r = self.client3.get(self.xx_form_url)
self.assertFalse(r.content)
self.assertEqual(r.status_code, 302)
# When it's not required, we sail through.
settings.ROSETTA_REQUIRES_AUTH = False
r = self.client3.get(self.xx_form_url)
self.assertTrue(r.content)
self.assertEqual(r.status_code, 200)
def test_13_catalog_filters(self):
settings.LANGUAGES = (('fr', 'French'), ('xx', 'Dummy Language'),)
r = self.client.get(self.third_party_file_list_url)
self.assertTrue(
os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content)
)
self.assertTrue(('contrib') not in str(r.content))
url = reverse('rosetta-file-list', kwargs={'po_filter': 'django'})
r = self.client.get(url)
self.assertTrue(
os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in str(r.content)
)
self.assertTrue(('contrib') in str(r.content))
url = reverse('rosetta-file-list', kwargs={'po_filter': 'all'})
r = self.client.get(url)
self.assertTrue(
os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content)
)
self.assertTrue(('contrib') in str(r.content))
url = reverse('rosetta-file-list', kwargs={'po_filter': 'project'})
r = self.client.get(url)
self.assertTrue(
os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in str(r.content)
)
self.assertTrue(('contrib') not in str(r.content))
def test_14_issue_99_context_and_comments(self):
r = self.client.get(self.xx_form_url)
self.assertTrue('This is a text of the base template' in str(r.content))
self.assertTrue('Context hint' in str(r.content))
def test_15_issue_87_entry_changed_signal(self):
self.copy_po_file_from_template('./django.po.template')
r = self.client.get(self.xx_form_url)
@receiver(entry_changed)
def test_receiver(sender, **kwargs):
self.test_old_msgstr = kwargs.get('old_msgstr')
self.test_new_msgstr = sender.msgstr
self.test_msg_id = sender.msgid
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
# post a translation
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
self.client.post(self.xx_form_url, data)
self.assertTrue(self.test_old_msgstr == '')
self.assertTrue(self.test_new_msgstr == 'Hello, world')
self.assertTrue(self.test_msg_id == 'String 2')
del(self.test_old_msgstr, self.test_new_msgstr, self.test_msg_id)
def test_16_issue_101_post_save_signal(self):
self.copy_po_file_from_template('./django.po.template')
r = self.client.get(self.xx_form_url)
@receiver(post_save)
def test_receiver(sender, **kwargs):
self.test_sig_lang = kwargs.get('language_code')
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
# post a translation
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
self.client.post(self.xx_form_url, data)
self.assertTrue(self.test_sig_lang == 'xx')
del(self.test_sig_lang)
def test_17_issue_103_post_save_signal_has_request(self):
self.copy_po_file_from_template('./django.po.template')
r = self.client.get(self.xx_form_url)
@receiver(post_save)
def test_receiver(sender, **kwargs):
self.test_16_has_request = 'request' in kwargs
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
# post a translation
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
r = self.client.post(self.xx_form_url, data)
self.assertTrue(self.test_16_has_request)
del(self.test_16_has_request)
def test_18_Test_Issue_gh24(self):
self.copy_po_file_from_template('./django.po.issue24gh.template')
r = self.client.get(self.xx_form_url)
self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in str(r.content))
# Post a translation, it should have properly wrapped lines
data = {'m_bb9d8fe6159187b9ea494c1b313d23d4':
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean '
'commodo ligula eget dolor. Aenean massa. Cum sociis natoque '
'penatibus et magnis dis parturient montes, nascetur ridiculus '
'mus. Donec quam felis, ultricies nec, pellentesque eu, pretium '
'quis, sem. Nulla consequat massa quis enim. Donec pede justo, '
'fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, '
'rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum '
'felis eu pede mollis pretium.'}
r = self.client.post(self.xx_form_url, data)
with open(self.dest_file, 'r') as po_file:
pofile_content = po_file.read()
self.assertTrue('"pede mollis pretium."' in pofile_content)
# Again, with unwrapped lines
self.copy_po_file_from_template('./django.po.issue24gh.template')
rosetta_settings.POFILE_WRAP_WIDTH = 0
r = self.client.get(self.xx_form_url)
self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in str(r.content))
r = self.client.post(self.xx_form_url, data)
with open(self.dest_file, 'r') as po_file:
pofile_content = po_file.read()
self.assertTrue('felis eu pede mollis pretium."' in pofile_content)
def test_19_Test_Issue_gh34(self):
self.copy_po_file_from_template('./django.po.issue34gh.template')
r = self.client.get(self.xx_form_url)
self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_1' in str(r.content))
self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_0' in str(r.content))
self.assertTrue('m_09f7e02f1290be211da707a266f153b3' in str(r.content))
# post a translation, it should have properly wrapped lines
data = {
'm_ff7060c1a9aae9c42af4d54ac8551f67_0': 'Foo %s',
'm_ff7060c1a9aae9c42af4d54ac8551f67_1': 'Bar %s',
'm_09f7e02f1290be211da707a266f153b3': 'Salut',
}
r = self.client.post(self.xx_form_url, data)
with open(self.dest_file, 'r') as po_file:
pofile_content = po_file.read()
self.assertTrue('msgstr "Salut\\n"' in pofile_content)
self.assertTrue('msgstr[0] ""\n"\\n"\n"Foo %s\\n"' in pofile_content)
self.assertTrue('msgstr[1] ""\n"\\n"\n"Bar %s\\n"' in pofile_content)
def test_20_Test_Issue_gh38(self):
# Set up
settings.SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
# (Have to log in again, since our session engine changed)
self.client.login(username='test_admin', password='test_password')
self.assertTrue('django.contrib.sessions.middleware.SessionMiddleware'
in settings.MIDDLEWARE)
# Only one backend to test: cache backend
rosetta_settings.STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'
self.copy_po_file_from_template('./django.po.issue38gh.template')
r = self.client.get(self.xx_form_url)
self.assertFalse(len(str(self.client.cookies.get('sessionid'))) > 4096)
self.assertTrue('m_9efd113f7919952523f06e0d88da9c54' in str(r.content))
data = {'m_9efd113f7919952523f06e0d88da9c54': 'Testing cookie length'}
r = self.client.post(self.xx_form_url, data)
with open(self.dest_file, 'r') as po_file:
pofile_content = po_file.read()
self.assertTrue('Testing cookie length' in pofile_content)
r = self.client.get(self.xx_form_url + '?filter=translated')
self.assertTrue('Testing cookie length' in str(r.content))
self.assertTrue('m_9f6c442c6d579707440ba9dada0fb373' in str(r.content))
def test_21_concurrency_of_cache_backend(self):
rosetta_settings.STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'
self.copy_po_file_from_template('./django.po.issue38gh.template')
# Force caching into play by making .po file read-only
os.chmod(self.dest_file, 292) # 0444
self.client.get(self.xx_form_url)
self.client2.get(self.xx_form_url)
self.assertNotEqual(
self.client.session.get('rosetta_cache_storage_key_prefix'),
self.client2.session.get('rosetta_cache_storage_key_prefix')
)
# Clean up (restore perms)
os.chmod(self.dest_file, 420) # 0644
def test_22_Test_Issue_gh39(self):
self.copy_po_file_from_template('./django.po.issue39gh.template')
r = self.client.get(self.xx_form_url)
# We have distinct hashes, even though the msgid and msgstr are identical
self.assertTrue('m_4765f7de94996d3de5975fa797c3451f' in str(r.content))
self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in str(r.content))
def test_23_save_header_data(self):
from django.contrib.auth.models import User
self.copy_po_file_from_template('./django.po.template')
unicode_user = User.objects.create_user(
'test_unicode', 'save_header_data@test.com', 'test_unicode'
)
unicode_user.first_name = "aéaéaé aàaàaàa"
unicode_user.last_name = "aâââ üüüü"
unicode_user.is_superuser, unicode_user.is_staff = True, True
unicode_user.save()
self.client.login(username='test_unicode', password='test_unicode')
# Load the template file
r = self.client.get(self.xx_form_url + '?filter=untranslated')
# make sure both strings are untranslated
self.assertTrue('dummy language' in str(r.content))
self.assertTrue('String 1' in str(r.content))
self.assertTrue('String 2' in str(r.content))
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
# post a translation
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
r = self.client.post(self.xx_form_url + '?filter=untranslated', data)
# read the result
with open(self.dest_file, 'rb') as f_:
content = six.text_type(f_.read())
# make sure unicode data was properly converted to ascii
self.assertTrue('Hello, world' in content)
self.assertTrue('save_header_data@test.com' in content)
self.assertTrue('aeaeae aaaaaaa aaaa uuuu' in content)
def test_24_percent_translation(self):
self.copy_po_file_from_template('./django.po.template')
# Load the template file
r = self.client.get(self.xx_form_url)
self.assertTrue('Progress: 0%' in str(r.content))
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
r = self.client.post(self.xx_form_url, data, follow=True)
self.assertTrue('Progress: 25%' in str(r.content))
def test_25_replace_access_control(self):
# Test default access control allows access
url = reverse('rosetta-file-list', kwargs={'po_filter': 'project'})
response = self.client.get(url)
self.assertEqual(200, response.status_code)
# Now replace access control, and check we get redirected
settings.ROSETTA_ACCESS_CONTROL_FUNCTION = 'rosetta.tests.no_access'
response = self.client.get(url)
self.assertEqual(302, response.status_code)
# Restore setting to default
settings.ROSETTA_ACCESS_CONTROL_FUNCTION = None
def test_26_urlconf_accept_dots_and_underscores(self):
resolver_match = resolve('/rosetta/files/all/fr_FR.utf8/0/')
self.assertEqual(resolver_match.url_name, 'rosetta-form')
self.assertEqual(resolver_match.kwargs['lang_id'], 'fr_FR.utf8')
def test_27_extended_urlconf_language_code_loads_file(self):
url = reverse(
'rosetta-form',
kwargs={'po_filter': 'all', 'lang_id': 'fr_FR.utf8', 'idx': 0}
)
r = self.client.get(url)
self.assertTrue('French (France), UTF8' in str(r.content))
self.assertTrue('m_03a603523bd75b00414a413657acdeb2' in str(r.content))
def test_28_issue_gh87(self):
"""Make sure that rosetta_i18n_catalog_filter is passed into the context."""
r = self.client.get(self.third_party_file_list_url)
self.assertContains(r, '