Bug 774234: Add tests for l10n blocks and langs support.

Refactor template handling and parsing.
This commit is contained in:
Paul McLanahan 2013-01-25 18:16:31 -05:00
Родитель 106ba2120f
Коммит 872e98b11e
12 изменённых файлов: 465 добавлений и 202 удалений

Просмотреть файл

@ -6,5 +6,5 @@ from django.conf.urls.defaults import *
from mozorg.util import page
urlpatterns = patterns('',
page('eula', 'eula/index.html'),
)
page('eula', 'legal/eula.html'),
)

Просмотреть файл

@ -23,3 +23,18 @@ def secure_required(view_func):
def server_error_view(request, template_name='500.html'):
"""500 error handler that runs context processors."""
return l10n_utils.render(request, template_name)
# backported from Django 1.4
# https://github.com/django/django/blob/master/django/utils/functional.py#L34
class cached_property(object):
"""
Decorator that converts a method with a single self argument into a
property cached on the instance.
"""
def __init__(self, func):
self.func = func
def __get__(self, instance, type):
res = instance.__dict__[self.func.__name__] = self.func(instance)
return res

Просмотреть файл

@ -3,35 +3,27 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import datetime
import errno
import itertools
import re
import os, errno
import os
from os import path
from optparse import make_option
import codecs
from contextlib import closing
from StringIO import StringIO
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import BaseCommand
from django.conf import settings
from jinja2 import Environment, TemplateNotFound
from jinja2.parser import Parser
from jinja2 import Environment
from bedrock_util import cached_property
def l10n_file(*args):
return path.join(settings.ROOT, 'locale', *args)
def l10n_tmpl(tmpl, lang):
return l10n_file(lang, 'templates', tmpl)
def app_tmpl(tmpl):
app = tmpl[:tmpl.index('/')]
return path.join(settings.ROOT, 'apps', app, 'templates', tmpl)
def list_templates():
"""List all the templates in all the installed apps"""
@ -45,27 +37,16 @@ def list_templates():
name, ext = os.path.splitext(filename)
if ext in ['.txt', '.html']:
full_path = os.path.join(root, filename)
yield full_path.replace(tmpl_dir, '').lstrip('/')
yield os.path.join(root, filename)
def update_templates(langs):
"""List templates with outdated/incorrect l10n blocks"""
for tmpl in list_templates():
print "%s..." % tmpl
# Parse the reference template that will provide new content
# and only get the blocks from it
parser = L10nParser()
blocks = list(parser.parse_template(app_tmpl(tmpl),
only_blocks=True))
for lang in langs:
if path.exists(l10n_tmpl(tmpl, lang)):
update_template(tmpl, blocks, lang)
else:
copy_template(tmpl, blocks, lang)
template = L10nTemplate(tmpl)
print "%s..." % template.rel_path
template.process(langs)
def get_todays_version():
@ -82,22 +63,102 @@ def ensure_dir_exists(path):
except OSError as exc:
if exc.errno == errno.EEXIST:
pass
else: raise
else:
raise
def update_template(tmpl, blocks, lang):
"""Detect outdated l10n blocks and update the template"""
def write_block(block, dest, force_was=False):
"""Write out a block to an l10n template"""
dest.write('{%% l10n %s %%}\n' % block['name'])
dest.write(block['main'])
if block['was'] or force_was:
dest.write('\n{% was %}')
dest.write('\n%s' % block['was'] if block['was'] else '')
dest.write('\n{% endl10n %}')
dest.write('\n\n')
def get_ref_block(name):
class L10nTemplate(object):
def __init__(self, template=None, source=None):
"""
Initialize the template class.
:param template:
Full path to a template file.
:param source:
Template text as a string if there is no template file.
"""
self.full_path = template
self.source = source
self.parser = L10nParser()
@cached_property
def rel_path(self):
"""
Return the part of the template path after the 'templates' directory.
"""
args = self.full_path.split(path.sep)
args = args[args.index('templates') + 1:]
return path.join(*args)
def l10n_path(self, lang):
"""
Return the path to the localized version of the template for lang.
"""
return l10n_file(lang, 'templates', self.rel_path)
@cached_property
def blocks(self):
if self.full_path:
blocks = self.parser.parse_template(self.full_path,
only_blocks=True)
elif self.source:
blocks = self.parser.parse(self.source, only_blocks=True)
return tuple(blocks)
def blocks_for_lang(self, lang):
"""Filter blocks to only those that allow this locale or all locales."""
return tuple(b for b in self.blocks
if not b['locales'] or lang in b['locales'])
def process(self, langs):
"""
Update existing templates and create new ones for specified langs.
"""
for lang in langs:
if path.exists(self.l10n_path(lang)):
self.update(lang)
else:
self.copy(lang)
def copy(self, lang):
"""Create a new l10n template by copying the l10n blocks"""
blocks = self.blocks_for_lang(lang)
if not blocks:
return
dest_file = self.l10n_path(lang)
# Make sure the templates directory for this locale and app exists
ensure_dir_exists(os.path.dirname(dest_file))
with codecs.open(dest_file, 'w', 'utf-8') as dest:
dest.write('{# Version: %s #}\n\n' % get_todays_version())
dest.write('{%% extends "%s" %%}\n\n' % self.rel_path)
for block in blocks:
write_block(block, dest)
print '%s: %s' % (lang, self.rel_path)
def _get_ref_block(self, name, blocks=None):
"""Return the reference block"""
blocks = blocks or self.blocks
return next((b for b in blocks if b['name'] == name), None)
for block in blocks:
if block['name'] == name:
return block
def transfer_content(l10n_block, ref_block):
def _transfer_content(self, l10n_block, ref_block):
"""Transfer any new content from the reference block"""
if ref_block:
# Update if the l10n file is older than this block
if l10n_block['version'] < ref_block['version']:
@ -108,100 +169,62 @@ def update_template(tmpl, blocks, lang):
l10n_block['main'] = ref_block['main']
l10n_block['locales'] = ref_block['locales']
return l10n_block
def update(self, lang):
"""Detect outdated l10n blocks and update the template"""
blocks = self.blocks_for_lang(lang)
if not blocks:
return
parser = L10nParser()
file_version = None
dest_tmpl = l10n_tmpl(tmpl, lang)
halted = False
written_blocks = []
file_version = None
parser = L10nParser()
dest_tmpl = self.l10n_path(lang)
written_blocks = []
# Make sure the templates directory for this locale and app exists
ensure_dir_exists(os.path.dirname(dest_tmpl))
# Make sure the templates directory for this locale and app exists
ensure_dir_exists(os.path.dirname(dest_tmpl))
# Parse the l10n template, run through it and update it where
# appropriate into a new template file
with closing(StringIO()) as buffer:
for token in parser.parse_template(dest_tmpl, strict=False,
halt_on_content=True):
if not token:
# If False is returned, that means a content block
# exists so we don't do anything to the template since
# it's customized
return
elif token[0] == 'content':
buffer.write(token[1])
elif token[0] == 'version':
buffer.write('{# Version: %s #}' % get_todays_version())
file_version = token[1]
elif token[0] == 'block':
if not file_version:
raise Exception('l10n file version tag does not exist '
'before initial l10n block')
# Parse the l10n template, run through it and update it where
# appropriate into a new template file
with closing(StringIO()) as buffer:
for token in parser.parse_template(dest_tmpl, strict=False,
halt_on_content=True):
if not token:
# If False is returned, that means a content block
# exists so we don't do anything to the template since
# it's customized
return
elif token[0] == 'content':
buffer.write(token[1])
elif token[0] == 'version':
buffer.write('{# Version: %s #}' % get_todays_version())
file_version = token[1]
elif token[0] == 'block':
if not file_version:
raise Exception('l10n file version tag does not exist '
'before initial l10n block')
# We have an l10n block, set its version and keep
# track of it for later use
l10n_block = token[1]
l10n_block['version'] = file_version
name = l10n_block['name']
written_blocks.append(name)
# We have an l10n block, set its version and keep
# track of it for later use
l10n_block = token[1]
l10n_block['version'] = file_version
name = l10n_block['name']
written_blocks.append(name)
# Update the block and write it out
l10n_block = transfer_content(l10n_block,
get_ref_block(name))
locales = l10n_block['locales']
if not locales or lang in locales:
# Update the block and write it out
self._transfer_content(l10n_block,
self._get_ref_block(name, blocks))
write_block(l10n_block, buffer)
# Check for any missing blocks
for block in blocks:
if block['name'] not in written_blocks:
locales = block['locales']
if not locales or lang in locales:
buffer.write('\n\n')
# Check for any missing blocks
for block in blocks:
if block['name'] not in written_blocks:
write_block(block, buffer)
# Write out the result to the l10n template
with codecs.open(dest_tmpl, 'w', 'utf-8') as dest:
dest.write(buffer.getvalue())
# Write out the result to the l10n template
with codecs.open(dest_tmpl, 'w', 'utf-8') as dest:
dest.write(buffer.getvalue())
print '%s: %s' % (lang, tmpl)
def write_block(block, dest, force_was=False):
"""Write out a block to an l10n template"""
dest.write('{%% l10n %s %%}\n' % block['name'])
dest.write(block['main'])
if block['was'] or force_was:
dest.write('\n{% was %}')
dest.write('\n%s' % block['was'] if block['was'] else '')
dest.write('\n{% endl10n %}')
def copy_template(tmpl, blocks, lang):
"""Create a new l10n template by copying the l10n blocks"""
dest_file = l10n_tmpl(tmpl, lang)
#If there is only one block and not for current lang
# no need to create the file.
if len(blocks) == 1 and not lang in blocks[0]['locales']:
return
if blocks:
# Make sure the templates directory for this locale and app exists
ensure_dir_exists(os.path.dirname(dest_file))
with codecs.open(dest_file, 'w', 'utf-8') as dest:
dest.write('{# Version: %s #}\n\n' % get_todays_version())
dest.write('{%% extends "%s" %%}\n\n' % tmpl)
for block in blocks:
#Copy only for the allowed locales
locales = block['locales']
if not locales or lang in locales:
write_block(block, dest)
dest.write('\n\n')
print '%s: %s' % (lang, self.rel_path)
class L10nParser():
@ -273,7 +296,7 @@ class L10nParser():
if not version:
raise Exception('Invalid version metadata in '
'template: %s '% self.tmpl)
'template: %s' % self.tmpl)
yield ('version', version)
self.scan_until('comment_end')
@ -341,7 +364,7 @@ class L10nParser():
if not block_version:
raise Exception("Invalid l10n block declaration: "
"bad version '%s' in %s"
% (block_name, self.tmpl))
% (block_version, self.tmpl))
return block_version
def parse_block(self, strict=True):
@ -353,48 +376,31 @@ class L10nParser():
self.scan_ignore('whitespace')
operator = self.scan_next('operator')
if operator == ',' or operator == ';':
self.scan_ignore('whitespace')
version_str = self.scan_next('integer')
if version_str:
block_version = self.get_block_version(version_str)
else: #We have new template
self.scan_ignore('whitespace')
locale_flag = self.scan_next('name')
if self.scan_next('operator') == '=':
past_token = None
while True:
token = self.scan_next('name')
operator = self.scan_next('operator')
if operator == ',':
if past_token:
token = '%s-%s' % (past_token, token)
past_token = None
locales.append(token)
continue
elif operator == '-':
past_token = token
continue
elif not operator:
if past_token:
token = '%s-%s' % (past_token, token)
past_token = None
locales.append(token)
break
self.scan_ignore('whitespace')
self.scan_next('operator')
self.scan_ignore('whitespace')
version_str = self.scan_next('integer')
block_version = self.get_block_version(version_str)
# Grab the locales if provided
prev_sub = False
for _, token_type, token_value in self.tokens:
if token_type in ['integer', 'block_end']:
break
if token_type == 'operator' and token_value in [',', '=']:
continue
if token_type == 'name' and token_value != 'locales':
if prev_sub:
locales[-1] += token_value
prev_sub = False
else:
locales.append(token_value)
if token_type == 'operator' and token_value == '-':
locales[-1] += '-'
prev_sub = True
if token_type == 'integer':
block_version = self.get_block_version(token_value)
self.scan_until('block_end')
elif strict:
raise Exception("Invalid l10n block declaration: "
"missing date for block '%s' in %s"
% (block_name, self.tmpl))
self.scan_until('block_end')
(main, was_) = self.block_content()
yield ('block', {'name': block_name,
'version': block_version,
@ -467,6 +473,6 @@ class Command(BaseCommand):
langs = args
else:
langs = os.listdir(l10n_file())
langs = filter(lambda x: x[0] != '.' , langs)
langs = filter(lambda x: x[0] != '.', langs)
update_templates(langs)

Просмотреть файл

@ -30,10 +30,32 @@ class L10nBlockExtension(Extension):
# Block name is mandatory.
name = parser.stream.expect('name').value
locales = []
# Comma optional.
parser.stream.skip_if('comma')
# Grab the locales if provided
if parser.stream.current.type == 'name':
parser.stream.skip() # locales
parser.stream.skip() # assign (=)
prev_sub = False
while parser.stream.current.type not in ['integer', 'block_end']:
parser.stream.skip_if('comma')
parser.stream.skip_if('assign')
token = parser.stream.current
if token.type in ['integer', 'block_end']:
break
if token.type == 'name':
if prev_sub:
locales[-1] += token.value
prev_sub = False
else:
locales.append(token.value)
if token.type == 'sub':
locales[-1] += '-'
prev_sub = True
parser.stream.next()
# Add version if provided.
if parser.stream.current.type == 'integer':
version = int(parser.parse_expression().value)
@ -55,6 +77,7 @@ class L10nBlockExtension(Extension):
node.set_lineno(lineno)
node.name = '__l10n__{0}'.format(name)
node.version = version # For debugging only, for now.
node.locales = locales
node.body = body
# I *think*, `true` would mean that variable assignments inside this
# block do not persist beyond this block (like a `with` block).

Просмотреть файл

@ -0,0 +1,12 @@
from tempfile import TemporaryFile
from textwrap import dedent
class TempFileMixin(object):
"""Provide a method for getting a temp file that is removed when closed."""
def tempfile(self, data=None):
tempf = TemporaryFile()
if data:
tempf.write(dedent(data))
tempf.seek(0)
return tempf

Просмотреть файл

@ -1,27 +1,42 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import codecs
from os import path
from StringIO import StringIO
from textwrap import dedent
from django.utils import unittest
from l10n_utils.management.commands.l10n_check import (list_templates,
L10nParser)
from mock import MagicMock, patch
from l10n_utils.management.commands.l10n_check import (
get_todays_version,
L10nParser,
L10nTemplate,
list_templates,
update_templates,
)
ROOT = path.join(path.dirname(path.abspath(__file__)), 'test_files')
TEMPLATE_DIRS = (path.join(ROOT, 'templates'),)
class TestL10nCheck(unittest.TestCase):
def _get_block(self, blocks, name):
"""Out of all blocks, grab the one with the specified name."""
try:
return filter(lambda b: b['name'] == name, blocks)[0]
except IndexError:
return None
for b in blocks:
if b['name'] == name:
return b
return None
def test_list_templates(self):
"""Make sure we capture both html and txt templates."""
TEMPLATES = ['mozorg/home.html',
'mozorg/emails/other.txt']
tmpls = filter(lambda tmpl: tmpl in TEMPLATES,
list_templates())
tmpls = [t for t in list_templates()
if L10nTemplate(t).rel_path in TEMPLATES]
assert len(tmpls) == len(TEMPLATES)
def test_parse_templates(self):
@ -29,14 +44,15 @@ class TestL10nCheck(unittest.TestCase):
correctly."""
parser = L10nParser()
blocks = parser.parse('foo bar bizzle what? '
'{% l10n baz, 20110914 %}'
'mumble'
'{% was %}'
'wased'
'{% endl10n %}'
'qux',
only_blocks=True)
blocks = parser.parse("""
foo bar bizzle what?
{% l10n baz, 20110914 %}
mumble
{% was %}
wased
{% endl10n %}
qux
""", only_blocks=True)
baz = self._get_block(blocks, 'baz')
@ -44,16 +60,18 @@ class TestL10nCheck(unittest.TestCase):
self.assertEqual(baz['was'], 'wased')
self.assertEqual(baz['version'], 20110914)
blocks = parser.parse('foo bar bizzle what? '
'{% l10n baz ; locales=ru,bn-IN ; 20110914 %}'
'mumble'
'{% endl10n %}'
'qux',
only_blocks=True)
blocks = parser.parse("""
foo bar bizzle what?
{% l10n baz locales=ru,bn-IN,fr 20110914 %}
mumble
{% endl10n %}
qux
""", only_blocks=True)
baz = self._get_block(blocks, 'baz')
self.assertEqual(baz['main'], 'mumble')
self.assertEqual(baz['locales'], ['ru', 'bn-IN'])
self.assertEqual(baz['locales'], ['ru', 'bn-IN', 'fr'])
self.assertEqual(baz['version'], 20110914)
def test_content_halt(self):
"""Make sure the parser will halt on the content block if told
@ -68,6 +86,160 @@ class TestL10nCheck(unittest.TestCase):
self.assertEqual(last_token, False)
# I need help writing tests for copy_template and update_template,
# which read files from the filesystem and write to it. I think I
# need to mock those somehow.
def test_filter_blocks(self):
"""Should return a list of blocks appropriate for a given lang"""
template = L10nTemplate(source="""
{% l10n dude locales=fr,es-ES,ru 20121212 %}
This aggression will not stand, man.
{% endl10n %}
{% l10n walter, locales=es-ES,ru 20121212 %}
I'm stayin'. Finishin' my coffee.
{% endl10n %}
{% l10n donnie 20121212 %}
Phone's ringing Dude.
{% endl10n %}
""")
lang_blocks = template.blocks_for_lang('fr')
self.assertEqual(len(lang_blocks), 2)
self.assertEqual(lang_blocks[0]['name'], 'dude')
self.assertEqual(lang_blocks[1]['name'], 'donnie')
lang_blocks = template.blocks_for_lang('es-ES')
self.assertEqual(len(lang_blocks), 3)
self.assertEqual(lang_blocks[0]['name'], 'dude')
self.assertEqual(lang_blocks[1]['name'], 'walter')
self.assertEqual(lang_blocks[2]['name'], 'donnie')
lang_blocks = template.blocks_for_lang('pt-BR')
self.assertEqual(len(lang_blocks), 1)
self.assertEqual(lang_blocks[0]['name'], 'donnie')
@patch('l10n_utils.management.commands.l10n_check.settings.ROOT', ROOT)
@patch('l10n_utils.management.commands.l10n_check.list_templates')
@patch('l10n_utils.management.commands.l10n_check.L10nTemplate.copy')
@patch('l10n_utils.management.commands.l10n_check.L10nTemplate.update')
def test_process_template(self, update_mock, copy_mock, lt_mock):
"""
template.process() should update existing templates and create missing
ones. It should only do so for the right locales.
"""
lt_mock.return_value = [
path.join(TEMPLATE_DIRS[0], 'l10n_blocks_with_langs.html'),
path.join(TEMPLATE_DIRS[0], 'l10n_blocks_without_langs.html'),
]
update_templates(['de'])
copy_mock.assert_called_once_with('de')
update_mock.assert_called_once_with('de')
def test_blocks_called_once(self):
"""
Test that the cached_property decorator really works in our situation.
"""
template = L10nTemplate(source="""
{% l10n donnie 20121212 %}
Phone's ringing Dude.
{% endl10n %}
""")
with patch.object(template, 'parser') as mock_parser:
template.blocks
template.blocks_for_lang('de')
template.blocks
self.assertEqual(mock_parser.parse.call_count, 1)
def test_update_template_no_lang(self):
"""
template.update() should skip files without blocks for the given locale.
"""
template = L10nTemplate(path.join(TEMPLATE_DIRS[0],
'l10n_blocks_with_langs.html'))
# cause the template to be read and parsed before mocking open
template.blocks
codecs_open = 'l10n_utils.management.commands.l10n_check.codecs.open'
open_mock = MagicMock(spec=file)
with patch(codecs_open, open_mock):
template.update('zh-TW')
file_handle = open_mock.return_value.__enter__.return_value
assert not file_handle.write.called
template.update('de')
assert file_handle.write.called
@patch('l10n_utils.management.commands.l10n_check.settings.ROOT', ROOT)
def test_update_template(self):
"""
template.update() should update lang specific templates.
"""
template = L10nTemplate(path.join(TEMPLATE_DIRS[0],
'l10n_blocks_with_langs.html'))
# cause the template to be read and parsed before mocking open
template.blocks
codecs_open = 'l10n_utils.management.commands.l10n_check.codecs.open'
open_mock = MagicMock(spec=file)
open_buffer = StringIO()
# for writing the new file
open_mock.return_value.__enter__.return_value = open_buffer
# for reading the old file
open_mock().read.return_value = codecs.open(
template.l10n_path('de')).read()
with patch(codecs_open, open_mock):
template.update('de')
# braces doubled for .format()
good_value = dedent("""\
{{# Version: {0} #}}
{{% extends "l10n_blocks_with_langs.html" %}}
{{% l10n donnie %}}
Phone's ringing Dude.
{{% was %}}
I am the walrus.
{{% endl10n %}}\n\n
""".format(get_todays_version()))
self.assertEqual(open_buffer.getvalue(), good_value)
def test_copy_template_no_lang(self):
"""
template.copy() should skip files with no blocks for the given locale.
:return:
"""
template = L10nTemplate(path.join(TEMPLATE_DIRS[0],
'l10n_blocks_with_langs.html'))
# cause the template to be read and parsed before mocking open
template.blocks
codecs_open = 'l10n_utils.management.commands.l10n_check.codecs.open'
open_mock = MagicMock(spec=file)
with patch(codecs_open, open_mock):
template.copy('zh-TW')
file_handle = open_mock.return_value.__enter__.return_value
assert not file_handle.write.called
template.copy('de')
assert file_handle.write.called
def test_copy_template(self):
"""
template.copy() should create missing lang specific templates.
"""
template = L10nTemplate(path.join(TEMPLATE_DIRS[0],
'l10n_blocks_without_langs.html'))
# cause the template to be read and parsed before mocking open
template.blocks
codecs_open = 'l10n_utils.management.commands.l10n_check.codecs.open'
open_mock = MagicMock(spec=file)
open_buffer = StringIO()
open_mock.return_value.__enter__.return_value = open_buffer
with patch(codecs_open, open_mock):
template.copy('de')
# braces doubled for .format()
good_value = dedent("""\
{{# Version: {0} #}}
{{% extends "l10n_blocks_without_langs.html" %}}
{{% l10n donnie %}}
Phone's ringing Dude.
{{% endl10n %}}\n
""".format(get_todays_version()))
self.assertEqual(open_buffer.getvalue(), good_value)

Просмотреть файл

@ -0,0 +1,8 @@
{# Version: 20120101 #}
{% extends "l10n_blocks_with_langs.html" %}
{% l10n donnie %}
I am the walrus.
{% endl10n %}

Просмотреть файл

@ -0,0 +1,11 @@
{% l10n dude locales=fr,es-ES,ru 20121212 %}
This aggression will not stand, man.
{% endl10n %}
{% l10n walter, locales=es-ES,ru 20121212 %}
I'm stayin'. Finishin' my coffee.
{% endl10n %}
{% l10n donnie locales=ru,de 20121212 %}
Phone's ringing Dude.
{% endl10n %}

Просмотреть файл

@ -0,0 +1,11 @@
{% l10n dude locales=fr,es-ES,ru 20121212 %}
This aggression will not stand, man.
{% endl10n %}
{% l10n walter, locales=es-ES,ru 20121212 %}
I'm stayin'. Finishin' my coffee.
{% endl10n %}
{% l10n donnie 20121212 %}
Phone's ringing Dude.
{% endl10n %}

Просмотреть файл

@ -3,8 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
from tempfile import TemporaryFile
from textwrap import dedent
from django.conf import settings
@ -12,6 +10,7 @@ from mock import patch
from nose.tools import eq_
from l10n_utils.gettext import langfiles_for_path, parse_python, parse_template
from l10n_utils.tests import TempFileMixin
from mozorg.tests import TestCase
@ -19,15 +18,6 @@ ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_files')
TEMPLATE_DIRS = (os.path.join(ROOT, 'templates'))
class TempFileMixin(object):
"""Provide a method for getting a temp file that is removed when closed."""
def tempfile(self, data):
tempf = TemporaryFile()
tempf.write(dedent(data))
tempf.seek(0)
return tempf
class TestParseTemplate(TempFileMixin, TestCase):
@patch('l10n_utils.gettext.codecs')
def test_single_lang_file_added(self, codecs_mock):

Просмотреть файл

@ -10,6 +10,7 @@ from django.test.client import Client
from jingo import env
from jinja2 import FileSystemLoader
from jinja2.nodes import Block
from mock import patch
from nose.plugins.skip import SkipTest
from nose.tools import eq_, ok_
@ -22,6 +23,20 @@ ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_files')
TEMPLATE_DIRS = (os.path.join(ROOT, 'templates'),)
class TestL10nBlocks(TestCase):
def test_l10n_block_locales(self):
"""
Parsing an l10n block with locales info should put that info
on the node.
"""
tree = env.parse("""{% l10n dude locales=ru,es-ES,fr 20121212 %}
This stuff is totally translated.
{% endl10n %}""")
l10n_block = tree.find(Block)
self.assertEqual(l10n_block.locales, ['ru', 'es-ES', 'fr'])
self.assertEqual(l10n_block.version, 20121212)
@patch.object(env, 'loader', FileSystemLoader(TEMPLATE_DIRS))
@patch.object(settings, 'ROOT_URLCONF', 'l10n_utils.tests.test_files.urls')
@patch.object(settings, 'ROOT', ROOT)