mirror of
https://github.com/Hopiu/django-rosetta.git
synced 2026-05-26 13:04:02 +00:00
Previously, state was extensively persisted in the cache. This large-scale rewrite uses the cache only to persist changes to the catalog file when it is read-only. State is now conveyed through the url structure and through query string arguments. The primary downside to this new approach is that there's no explicit way of purging a cached catalog from memory, or refreshing it from what's on disk. The user is forced (untransparently!) to wait until the cached file falls out of the cache (untransparently!), which is currently 24 hours after the last save (or after if the session ends). Addressing this would require some design considerations, and probably some new text that would require its own translation.
944 lines
39 KiB
Python
944 lines
39 KiB
Python
# -*- coding: utf-8 -*-
|
|
import filecmp
|
|
import hashlib
|
|
import os
|
|
import shutil
|
|
|
|
from django.conf import settings
|
|
from django.dispatch import receiver
|
|
try:
|
|
from django.urls import reverse, resolve
|
|
except ImportError:
|
|
from django.core.urlresolvers import reverse, resolve
|
|
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
|
|
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_user('test_admin', 'test@test.com', 'test_password')
|
|
user2 = User.objects.create_user('test_admin2', 'test@test2.com', 'test_password')
|
|
user3 = User.objects.create_user('test_admin3', 'test@test2.com', 'test_password')
|
|
|
|
user.is_superuser, user2.is_superuser, user3.is_superuser = True, True, True
|
|
user.is_staff, user2.is_staff, user3.is_staff = True, True, False
|
|
|
|
user.save()
|
|
user2.save()
|
|
user3.save()
|
|
|
|
self.user = user
|
|
self.client2 = Client()
|
|
|
|
self.client.login(username='test_admin', password='test_password')
|
|
self.client2.login(username='test_admin2', 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)'),
|
|
)
|
|
|
|
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, '<td class="ch-messages r">1</td>')
|
|
self.assertContains(r, '<td class="ch-progress r">0%</td>')
|
|
self.assertContains(r, '<td class="ch-obsolete r">1</td>')
|
|
|
|
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_CLASSES)
|
|
|
|
# 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, '<li class="active"><a href="/rosetta/files/third-party/">')
|
|
|
|
def test_29_unsupported_p3_django_16_storage(self):
|
|
if VERSION[0:2] < (2, 0):
|
|
self.assertTrue('django.contrib.sessions.middleware.SessionMiddleware'
|
|
in settings.MIDDLEWARE_CLASSES)
|
|
|
|
settings.SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
|
|
rosetta_settings.STORAGE_CLASS = 'rosetta.storage.SessionRosettaStorage'
|
|
|
|
# Force caching to be used by making the pofile read-only
|
|
os.chmod(self.dest_file, 292) # 0444
|
|
|
|
# (Have to log in again, since our session engine changed)
|
|
self.client.login(username='test_admin', password='test_password')
|
|
|
|
with self.assertRaises(ImproperlyConfigured):
|
|
self.client.get(self.xx_form_url)
|
|
|
|
# Cleanup
|
|
os.chmod(self.dest_file, 420) # 0644
|
|
|
|
def test_30_pofile_names(self):
|
|
ORIG_POFILENAMES = rosetta_settings.POFILENAMES
|
|
rosetta_settings.POFILENAMES = ('pr44.po', )
|
|
|
|
os.unlink(self.dest_file)
|
|
destfile = os.path.normpath(
|
|
os.path.join(self.curdir, '../locale/xx/LC_MESSAGES/pr44.po')
|
|
)
|
|
shutil.copy(
|
|
os.path.normpath(os.path.join(self.curdir, './pr44.po.template')),
|
|
destfile
|
|
)
|
|
|
|
r = self.client.get(self.third_party_file_list_url)
|
|
self.assertTrue('xx/LC_MESSAGES/pr44.po' in str(r.content))
|
|
|
|
r = self.client.get(self.xx_form_url)
|
|
self.assertTrue('dummy language' in str(r.content))
|
|
|
|
# (Clean up)
|
|
os.unlink(destfile)
|
|
rosetta_settings.POFILENAMES = ORIG_POFILENAMES
|
|
|
|
def test_31_pr_102__exclude_paths(self):
|
|
ORIG_ROSETTA_EXCLUDED_PATHS = rosetta_settings.ROSETTA_EXCLUDED_PATHS
|
|
r = self.client.get(self.third_party_file_list_url)
|
|
self.assertContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
|
|
|
|
exclude_path = os.path.normpath(os.path.join(self.curdir, '../locale'))
|
|
rosetta_settings.ROSETTA_EXCLUDED_PATHS = [exclude_path, ]
|
|
|
|
r = self.client.get(self.third_party_file_list_url)
|
|
self.assertNotContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
|
|
|
|
rosetta_settings.ROSETTA_EXCLUDED_PATHS = ORIG_ROSETTA_EXCLUDED_PATHS
|
|
|
|
def test_32_pr_103__language_groups(self):
|
|
from django.contrib.auth.models import User, Group
|
|
|
|
ORIG_ROSETTA_LANGUAGE_GROUPS = rosetta_settings.ROSETTA_LANGUAGE_GROUPS
|
|
rosetta_settings.ROSETTA_LANGUAGE_GROUPS = False
|
|
|
|
# Default behavior: non-admins need to be in a translators group; they
|
|
# see all catalogs
|
|
translators = Group.objects.create(name='translators')
|
|
translators_xx = Group.objects.create(name='translators-xx')
|
|
|
|
user4 = User.objects.create_user('test_admin4', 'test@test3.com', 'test_password')
|
|
user4.groups.add(translators)
|
|
user4.is_superuser = False
|
|
user4.is_staff = True
|
|
user4.save()
|
|
self.client.login(username='test_admin4', password='test_password')
|
|
|
|
r = self.client.get(self.third_party_file_list_url)
|
|
self.assertContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
|
|
|
|
# Activate the option, user doesn't see the XX catalog
|
|
rosetta_settings.ROSETTA_LANGUAGE_GROUPS = True
|
|
|
|
r = self.client.get(self.third_party_file_list_url)
|
|
self.assertNotContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
|
|
|
|
# Now add them to the custom group
|
|
user4.groups.add(translators_xx)
|
|
|
|
r = self.client.get(self.third_party_file_list_url)
|
|
self.assertContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po')
|
|
|
|
# Clean up
|
|
rosetta_settings.ROSETTA_LANGUAGE_GROUPS = ORIG_ROSETTA_LANGUAGE_GROUPS
|
|
|
|
def test_33_reflang(self):
|
|
ORIG_ENABLE_REFLANG = rosetta_settings.ENABLE_REFLANG
|
|
rosetta_settings.ENABLE_REFLANG = True
|
|
self.copy_po_file_from_template('./django.po.issue60.template')
|
|
r = self.client.get(self.xx_form_url)
|
|
|
|
# Verify that there's an option to select a reflang
|
|
self.assertTrue('<option value="?ref_lang=xx">dummy language</option>' in str(r.content))
|
|
|
|
r = self.client.get(self.xx_form_url + '?ref_lang=xx')
|
|
# The translated string in the test PO file ends up in the "Reference" column
|
|
self.assertTrue('<span class="message">translated-string1</span>' in str(r.content))
|
|
rosetta_settings.ENABLE_REFLANG = ORIG_ENABLE_REFLANG
|
|
|
|
def test_34_issue_113_app_configs(self):
|
|
r = self.client.get(reverse('rosetta-file-list', kwargs={'po_filter': 'all'}))
|
|
self.assertTrue('rosetta/files/all/xx/1/">Test_App' in str(r.content))
|
|
|
|
def test_35_issue_135_display_exception_messages(self):
|
|
# Note: the old version of this test looked for a 'Permission denied'
|
|
# message reflected in the response. That behavior has now changed so
|
|
# that changes that can't be persisted through the filesystem .po file
|
|
# are saved to the cached version of the .po file.
|
|
self.copy_po_file_from_template('./django.po.template')
|
|
rosetta_settings.STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'
|
|
|
|
r = self.client.get(self.xx_form_url + '?msg_filter=untranslated')
|
|
self.assertContains(r, 'm_e48f149a8b2e8baa81b816c0edf93890')
|
|
|
|
# make the pofile read-only
|
|
os.chmod(self.dest_file, 292) # 0444
|
|
|
|
# post a translation
|
|
data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'}
|
|
self.client.post(self.xx_form_url, data, follow=True)
|
|
|
|
# Confirm that the filesystem file hasn't changed
|
|
tmpl_path = os.path.normpath(os.path.join(self.curdir, 'django.po.template'))
|
|
self.assertTrue(filecmp.cmp(tmpl_path, self.dest_file))
|
|
|
|
# Confirm that the cached version has been updated
|
|
cache_key = 'po-file-%s' % self.dest_file
|
|
request = RequestFactory().get(self.xx_form_url)
|
|
request.user = self.user
|
|
request.session = self.client.session
|
|
storage = get_storage(request)
|
|
|
|
po_file = storage.get(cache_key)
|
|
entry = po_file.find('String 2')
|
|
self.assertEqual(entry.msgstr, 'Hello, world')
|
|
|
|
# cleanup
|
|
os.chmod(self.dest_file, 420) # 0644
|
|
|
|
def test_36_issue_142_complex_locales(self):
|
|
r = self.client.get(reverse('rosetta-file-list', kwargs={'po_filter': 'all'}))
|
|
self.assertContains(r, 'locale/bs-Cyrl-BA/LC_MESSAGES/django.po')
|
|
|
|
def test_37_issue_133_complex_locales(self):
|
|
r = self.client.get(reverse('rosetta-file-list', kwargs={'po_filter': 'all'}))
|
|
self.assertContains(r, 'locale/yy_Anot/LC_MESSAGES/django.po')
|
|
|
|
def test_38_issue_161_more_weird_locales(self):
|
|
r = self.client.get(reverse('rosetta-file-list', kwargs={'po_filter': 'all'}))
|
|
self.assertTrue(r, 'locale/zh_Hans/LC_MESSAGES/django.po')
|
|
|
|
def test_39_invalid_get_page(self):
|
|
url = self.xx_form_url + '?filter=untranslated'
|
|
|
|
r = self.client.get(url) # Page not specified
|
|
self.assertEqual(r.context['page'], 1)
|
|
|
|
r = self.client.get(url + '&page=') # No number given
|
|
self.assertEqual(r.context['page'], 1)
|
|
|
|
r = self.client.get(url + '&page=9999') # Too-high number given
|
|
self.assertEqual(r.context['page'], 1)
|
|
|
|
r = self.client.get(url + '&page=x') # Non-number given
|
|
self.assertEqual(r.context['page'], 1)
|
|
|
|
def test_40_issue_155_auto_compile(self):
|
|
|
|
def file_hash(file_string):
|
|
if six.PY3:
|
|
with open(file_string, encoding="latin-1") as file:
|
|
file_content = file.read().encode('utf-8')
|
|
else:
|
|
with open(file_string) as file:
|
|
file_content = file.read()
|
|
return hashlib.md5(file_content).hexdigest()
|
|
|
|
def message_hashes():
|
|
r = self.client.get(self.xx_form_url)
|
|
return {m.msgid: 'm_' + m.md5hash for m in r.context['rosetta_messages']}
|
|
|
|
po_file = self.dest_file
|
|
mo_file = self.dest_file[:-3] + '.mo'
|
|
|
|
# MO file will be compiled by default.
|
|
# Get PO and MO files into an initial reference state (MO will be
|
|
# created or updated)
|
|
msg_hashes = message_hashes()
|
|
data = {msg_hashes['String 1']: 'Translation 1'}
|
|
self.client.post(self.xx_form_url, data)
|
|
po_file_hash_before, mo_file_hash_before = file_hash(po_file), file_hash(mo_file)
|
|
|
|
# Make a change to the translations
|
|
msg_hashes = message_hashes()
|
|
data = {msg_hashes['String 1']: 'Translation 2'}
|
|
self.client.post(self.xx_form_url, data)
|
|
|
|
# Get the new hashes of the PO and MO file contents
|
|
po_file_hash_after, mo_file_hash_after = file_hash(po_file), file_hash(mo_file)
|
|
|
|
# Both the PO and MO should have changed
|
|
self.assertNotEqual(po_file_hash_before, po_file_hash_after)
|
|
self.assertNotEqual(mo_file_hash_before, mo_file_hash_after)
|
|
|
|
# Disable auto-compilation of the MO when the PO is saved
|
|
rosetta_settings.AUTO_COMPILE = False
|
|
|
|
# Make a change to the translations
|
|
po_file_hash_before, mo_file_hash_before = po_file_hash_after, mo_file_hash_after
|
|
msg_hashes = message_hashes()
|
|
data = {msg_hashes['String 1']: "Translation 3"}
|
|
self.client.post(self.xx_form_url, data)
|
|
po_file_hash_after, mo_file_hash_after = file_hash(po_file), file_hash(mo_file)
|
|
|
|
# Only the PO should have changed, the MO should be unchanged
|
|
self.assertNotEqual(po_file_hash_before, po_file_hash_after)
|
|
self.assertEqual(mo_file_hash_before, mo_file_hash_after)
|
|
|
|
# Verify that translating another string also leaves the MO unchanged
|
|
po_file_hash_before, mo_file_hash_before = po_file_hash_after, mo_file_hash_after
|
|
msg_hashes = message_hashes()
|
|
data = {msg_hashes['String 2']: "Translation 4"}
|
|
self.client.post(self.xx_form_url, data)
|
|
po_file_hash_after, mo_file_hash_after = file_hash(po_file), file_hash(mo_file)
|
|
|
|
self.assertNotEqual(po_file_hash_before, po_file_hash_after)
|
|
self.assertEqual(mo_file_hash_before, mo_file_hash_after)
|
|
|
|
# Double check that switching back to auto compilation updates the MO
|
|
rosetta_settings.AUTO_COMPILE = True
|
|
|
|
po_file_hash_before, mo_file_hash_before = po_file_hash_after, mo_file_hash_after
|
|
msg_hashes = message_hashes()
|
|
data = {msg_hashes['String 2']: "Translation 5"}
|
|
self.client.post(self.xx_form_url, data)
|
|
po_file_hash_after, mo_file_hash_after = file_hash(po_file), file_hash(mo_file)
|
|
|
|
self.assertNotEqual(po_file_hash_before, po_file_hash_after)
|
|
self.assertNotEqual(mo_file_hash_before, mo_file_hash_after)
|
|
|
|
def test_41_pr_176_embed_in_admin(self):
|
|
resp = self.client.get(reverse('admin:index'))
|
|
self.assertContains(resp, 'app-rosetta module')
|
|
|
|
def _setup_view(self, view, request, *args, **kwargs):
|
|
"""Mimic as_view() returned callable, but returns view instance.
|
|
|
|
args and kwargs are the same you would pass to ``reverse()``
|
|
(From http://tech.novapost.fr/django-unit-test-your-views-en.html.)
|
|
"""
|
|
view.request = request
|
|
view.args = args
|
|
view.kwargs = kwargs
|
|
return view
|
|
|
|
def test_42_view_property_po_file_is_writable(self):
|
|
"""Confirm that we're accurately determining the filesystem write-perms
|
|
on our .po file.
|
|
"""
|
|
self.copy_po_file_from_template('./django.po.template')
|
|
|
|
# By default, we're writable
|
|
request = RequestFactory().get(self.xx_form_url)
|
|
request.user = self.user
|
|
kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 0}
|
|
view = self._setup_view(
|
|
view=views.TranslationFormView(),
|
|
request=request,
|
|
**kwargs
|
|
)
|
|
self.assertTrue(view.po_file_is_writable)
|
|
|
|
# Now try again with the file not writable. (Regenerate the view, since
|
|
# this po_file_is_writable is cached for the life of the request.)
|
|
# make the pofile read-only
|
|
os.chmod(self.dest_file, 292) # 0444
|
|
view = self._setup_view(
|
|
view=views.TranslationFormView(),
|
|
request=request,
|
|
**kwargs
|
|
)
|
|
self.assertFalse(view.po_file_is_writable)
|
|
|
|
# Cleanup
|
|
os.chmod(self.dest_file, 420) # 0644
|
|
|
|
def test_43_view_property_po_file_path(self):
|
|
"""Confirm our class-based views properly parse/validate the path of the
|
|
.po file in question derived from the url kwargs.
|
|
"""
|
|
self.copy_po_file_from_template('./django.po.template')
|
|
|
|
# By default, when all goes well, we get our existing .po file path
|
|
request = RequestFactory().get(self.xx_form_url)
|
|
request.user = self.user
|
|
kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 0}
|
|
view = self._setup_view(
|
|
view=views.TranslationFormView(),
|
|
request=request,
|
|
**kwargs
|
|
)
|
|
self.assertEqual(view.po_file_path, self.dest_file)
|
|
|
|
# But if the language isn't an option, we get a 404
|
|
ORIG_LANGUAGES = settings.LANGUAGES
|
|
settings.LANGUAGES = [l for l in settings.LANGUAGES if l[0] != 'xx']
|
|
view = self._setup_view(
|
|
view=views.TranslationFormView(),
|
|
request=request,
|
|
**kwargs
|
|
)
|
|
with self.assertRaises(Http404):
|
|
view.po_file_path
|
|
|
|
settings.LANGUAGES = ORIG_LANGUAGES # (Clean up)
|
|
|
|
# And if the index doesn't correspond with a file, we get a 404
|
|
new_kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 9}
|
|
view = self._setup_view(
|
|
view=views.TranslationFormView(),
|
|
# Recycle request, even though url kwargs conflict with ones below.
|
|
request=request,
|
|
**new_kwargs
|
|
)
|
|
with self.assertRaises(Http404):
|
|
view.po_file_path
|
|
|
|
def test_44_message_search_function(self):
|
|
"""Confirm that search of the .po file works across the various message
|
|
fields.
|
|
"""
|
|
self.copy_po_file_from_template('./django.po.test44.template')
|
|
url = self.xx_form_url + '?query=%s'
|
|
|
|
# Here's the message entry we're considering:
|
|
# #. Translators: consectetur adipisicing
|
|
# #: templates/eiusmod/tempor.html:43
|
|
# msgid "Lorem ipsum"
|
|
# msgstr "dolor sit amet"
|
|
# It is buried at the end of the file, so without searching for it, it
|
|
# shouldn't be on the page
|
|
r = self.client.get(url % '')
|
|
self.assertNotContains(r, 'Lorem')
|
|
|
|
# Search msgid
|
|
r = self.client.get(url % 'ipsum')
|
|
self.assertContains(r, 'Lorem')
|
|
|
|
# Search msgstr
|
|
r = self.client.get(url % 'dolor')
|
|
self.assertContains(r, 'Lorem')
|
|
|
|
# Search occurences
|
|
r = self.client.get(url % 'tempor')
|
|
self.assertContains(r, 'Lorem')
|
|
|
|
# Search comments
|
|
r = self.client.get(url % 'adipisicing')
|
|
self.assertContains(r, 'Lorem')
|
|
|
|
|
|
# Stubbed access control function
|
|
def no_access(user):
|
|
return False
|