def test_block_helper_inverted_sections(self): def list(this, options, context): if len(context): out = "<ul>" for thing in context: out += "<li>" out += str_class(options['fn'](thing)) out += "</li>" out += "</ul>" return out else: return "<p>" + str_class(options['inverse'](this)) + "</p>" context = {'people': [{'name': "Alan"}, {'name': "Yehuda"}]} empty = {'people': []} rootMessage = {'people': [], 'message': "Nobody's here"} src1 = u"{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}" src2 = u"{{#list people}}Hello{{^}}{{message}}{{/list}}" src3 = u"{{#list people}}{{name}}{{else}}<em>Nobody's here</em>{{/list}}" helpers = {'list': list} # inverse not executed by helper: self.assertEqual("<ul><li>Alan</li><li>Yehuda</li></ul>", render(src1, context, helpers)) # inverse can be called by a helper self.assertEqual("<p><em>Nobody's here</em></p>", render(src1, empty, helpers)) # the expected context of the inverse is the this parameter to block # helpers. self.assertEqual("<p>Nobody's here</p>", render(src2, rootMessage, helpers=helpers)) # inverse can also be denoted by 'else': self.assertEqual("<ul><li>Alan</li><li>Yehuda</li></ul>", render(src3, context, helpers)) self.assertEqual("<p><em>Nobody's here</em></p>", render(src3, empty, helpers))
def test_negative_int_literal(self): self.assertEqual("str int int", render(u"{{type \"string\"}} {{type 1}} {{type -1}}", None, helpers={'type': lambda s, v: type(v).__name__})) self.assertEqual("string 1 -1", render(u"{{echo \"string\"}} {{echo 1}} {{echo -1}}", None, helpers={'echo': lambda s, v: str(v)}))
def test_zeros(self): self.assertEqual("num1: 42, num2: 0", render(u"num1: {{num1}}, num2: {{num2}}", {'num1': 42, 'num2': 0})) self.assertEqual("num: 0", render(u"num: {{.}}", 0)) self.assertEqual("num: 0", render(u"num: {{num1/num2}}", {'num1': {'num2': 0}}))
def test_escaping_expressions(self): self.assertEqual('&\"\\<>', render(u"{{{awesome}}}", {'awesome': "&\"\\<>"})) self.assertEqual('&\"\\<>', render(u"{{&awesome}}", {'awesome': "&\"\\<>"})) self.assertEqual(u'&"'`\\<>', render(u"{{awesome}}", {'awesome': "&\"'`\\<>"}))
def test_if_with_function_argument(self): source = u"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!" self.assertEqual( u"GOODBYE cruel world!", render(source, { 'goodbye': lambda this: True, 'world': "world" })) self.assertEqual( u"GOODBYE cruel world!", render(source, { 'goodbye': lambda this: this['world'], 'world': "world" })) self.assertEqual( u"cruel world!", render(source, { 'goodbye': lambda this: False, 'world': "world" })) self.assertEqual( u"cruel world!", render(source, { 'goodbye': lambda this: None, 'world': "world" }))
def test_booleans(self): template = u"{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!" self.assertEqual( "GOODBYE cruel world!", render(template, {'goodbye': True, 'world': 'world'})) self.assertEqual( "cruel world!", render(template, {'goodbye': False, 'world': 'world'}))
def test_providing_a_helpers_hash(self): self.assertEqual("Goodbye cruel world!", render(u"Goodbye {{cruel}} {{world}}!", {'cruel': "cruel"}, helpers={'world': "world"})) self.assertEqual("Goodbye cruel world!", render(u"Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!", {'iter': [{'cruel': "cruel"}]}, helpers={'world': "world"}))
def test_empty_block(self): source = u"{{#goodbyes}}{{/goodbyes}}cruel {{world}}!" context = { 'goodbyes': [ {'text': "goodbye"}, {'text': "Goodbye"}, {'text': "GOODBYE"}], 'world': "world"} self.assertEqual("cruel world!", render(source, context)) self.assertEqual("cruel world!", render(source, {'goodbyes': [], 'world': "world"}))
def test_functions_can_take_context_arguments(self): def awesome(this, context): return context self.assertEqual("Frank", render(u"{{awesome frank}}", {'awesome': awesome, 'frank': 'Frank'})) self.assertEqual("Frank", render(u"{{{awesome frank}}}", {'awesome': awesome, 'frank': 'Frank'}))
def test_inverted_sections(self): source = (u"{{#goodbyes}}{{this}}{{/goodbyes}}" u"{{^goodbyes}}Right On!{{/goodbyes}}") # Unset value self.assertEqual("Right On!", render(source, {})) # False value self.assertEqual("Right On!", render(source, {'goodbyes': False})) # Empty list self.assertEqual("Right On!", render(source, {'goodbyes': []}))
def test_each(self): source = u"{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!" context = {'goodbyes': [{'text': "goodbye"}, {'text': "Goodbye"}, {'text': "GOODBYE"}], 'world': "world"} self.assertEqual("goodbye! Goodbye! GOODBYE! cruel world!", render(source, context)) self.assertEqual("cruel world!", render(source, {'goodbyes': [], 'world': "world"}))
def test_in_cases_of_conflict_helpers_before_context(self): self.assertEqual("helpers", render(u"{{lookup}}", {'lookup': 'Explicit'}, helpers={'lookup': 'helpers'})) self.assertEqual("helpers", render(u"{{{lookup}}}", {'lookup': 'Explicit'}, helpers={'lookup': 'helpers'})) self.assertEqual("Explicit", render(u"{{#lookup}}Explicit{{/lookup}}", {'lookup': []}, helpers={'lookup': [{}]}))
def test_inverted_sections(self): source = ( u"{{#goodbyes}}{{this}}{{/goodbyes}}" u"{{^goodbyes}}Right On!{{/goodbyes}}") # Unset value self.assertEqual("Right On!", render(source, {})) # False value self.assertEqual("Right On!", render(source, {'goodbyes': False})) # Empty list self.assertEqual("Right On!", render(source, {'goodbyes': []}))
def test_if_with_function_argument(self): source = u"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!" self.assertEqual(u"GOODBYE cruel world!", render(source, {'goodbye': lambda this: True, 'world': "world"})) self.assertEqual(u"GOODBYE cruel world!", render(source, {'goodbye': lambda this: this['world'], 'world': "world"})) self.assertEqual(u"cruel world!", render(source, {'goodbye': lambda this: False, 'world': "world"})) self.assertEqual(u"cruel world!", render(source, {'goodbye': lambda this: None, 'world': "world"}))
def test_providing_a_helpers_hash(self): self.assertEqual( "Goodbye cruel world!", render(u"Goodbye {{cruel}} {{world}}!", {'cruel': "cruel"}, helpers={'world': "world"})) self.assertEqual( "Goodbye cruel world!", render(u"Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!", {'iter': [{ 'cruel': "cruel" }]}, helpers={'world': "world"}))
def test_zeros(self): self.assertEqual( "num1: 42, num2: 0", render(u"num1: {{num1}}, num2: {{num2}}", { 'num1': 42, 'num2': 0 })) self.assertEqual("num: 0", render(u"num: {{.}}", 0)) self.assertEqual("num: 0", render(u"num: {{num1/num2}}", {'num1': { 'num2': 0 }}))
def test_in_cases_of_conflict_helpers_before_context(self): self.assertEqual( "helpers", render(u"{{lookup}}", {'lookup': 'Explicit'}, helpers={'lookup': 'helpers'})) self.assertEqual( "helpers", render(u"{{{lookup}}}", {'lookup': 'Explicit'}, helpers={'lookup': 'helpers'})) self.assertEqual( "Explicit", render(u"{{#lookup}}Explicit{{/lookup}}", {'lookup': []}, helpers={'lookup': [{}]}))
def test_booleans(self): template = u"{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!" self.assertEqual("GOODBYE cruel world!", render(template, { 'goodbye': True, 'world': 'world' })) self.assertEqual( "cruel world!", render(template, { 'goodbye': False, 'world': 'world' }))
def test_block_helpers_can_take_an_optional_hash_with_booleans(self): def goodbye(this, options, cruel, _print): if _print is True: return "GOODBYE " + cruel + " " + str_class(options['fn'](this)) elif _print is False: return "NOT PRINTING" else: return "THIS SHOULD NOT HAPPEN" helpers = {'goodbye': goodbye} self.assertEqual("GOODBYE CRUEL world", render(u'{{#goodbye cruel="CRUEL" _print=true}}world{{/goodbye}}', {}, helpers=helpers)) self.assertEqual("NOT PRINTING", render(u'{{#goodbye cruel="CRUEL" _print=false}}world{{/goodbye}}', {}, helpers=helpers))
def test_helpers_can_take_an_optional_hash_with_booleans(self): def goodbye(this, cruel, world, _print): if _print is True: return "GOODBYE " + cruel + " " + world elif _print is False: return "NOT PRINTING" else: return "THIS SHOULD NOT HAPPEN" helpers = {'goodbye': goodbye} self.assertEqual("GOODBYE CRUEL WORLD", render(u'{{goodbye cruel="CRUEL" world="WORLD" _print=true}}', {}, helpers=helpers)) self.assertEqual("NOT PRINTING", render(u'{{goodbye cruel="CRUEL" world="WORLD" _print=false}}', {}, helpers=helpers))
def test_negative_int_literal(self): self.assertEqual( "str int int", render(u"{{type \"string\"}} {{type 1}} {{type -1}}", None, helpers={ 'type': lambda s, v: type(v).__name__ })) self.assertEqual( "string 1 -1", render(u"{{echo \"string\"}} {{echo 1}} {{echo -1}}", None, helpers={ 'echo': lambda s, v: str(v) }))
def test_simple_multi_params_work(self): source = u'Message: {{goodbye cruel world}}' context = {'cruel': "cruel", 'world': "world"} def goodbye(this, cruel, world): return "Goodbye " + cruel + " " + world self.assertEqual("Message: Goodbye cruel world", render(source, context, helpers=dict(goodbye=goodbye)))
def test_escaping_text(self): self.assertThat(u"Awesome's", RendersItself()) self.assertThat(u"Awesome\\", RendersItself()) self.assertThat(u"Awesome\\\\ foo", RendersItself()) self.assertEqual(u"Awesome \\", render(u"Awesome {{foo}}", {'foo': '\\'})) self.assertThat(u' " " ', RendersItself())
def test_lambdas_are_resolved_by_blockHelperMissing_not_handlebars_proper(self): # Probably should be called 'lambdas in the context are called as # though for a simple block' - it wants to check moustache # compatibility which allows all block stuff to be overridden via # blockHelperMissing source = u"{{#truthy}}yep{{/truthy}}" self.assertEqual("yep", render(source, {'truthy': lambda this: True}))
def test_block_helpers_can_take_an_optional_hash(self): source = u'{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}' def goodbye(this, options, times, cruel): return "GOODBYE " + cruel + " " + str_class(options['fn'](this)) + " " + str(times) + " TIMES" helpers = {'goodbye': goodbye} self.assertEqual("GOODBYE CRUEL world 12 TIMES", render(source, {}, helpers=helpers))
def test_parent_lookup(self): source = u"{{#goodbyes}}{{text}} cruel {{@_parent.name}}! {{/goodbyes}}" context = {'name': "Alan", 'goodbyes': [ {'text': "goodbye"}, {'text': "Goodbye"}, {'text': "GOODBYE"}]} self.assertEqual( "goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ", render(source, context))
def test_log(self): source = u"{{log blah}}" context = {'blah': "whee"} log = [] self.patch(pybars, 'log', log.append) self.assertEqual("", render(source, context)) self.assertEqual(["whee"], log)
def test_block_helper_passing_a_complex_path_context(self): source = u"{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}" def form(this, options, context): return u"<form>" + str_class(options['fn'](context)) + u"</form>" context = {'yehuda': {'name': "Yehuda", 'cat': {'name': "Harold"}}} self.assertEqual("<form><p>Harold</p></form>", render(source, context, helpers={'form': form}))
def test_each_this(self): source = u"{{#each name}}{{capitalize this}} {{/each}}" helpers = {'capitalize': lambda this, value: value.upper()} context = {'name': ['John', 'James']} self.assertEqual("JOHN JAMES ", render(source, context, helpers=helpers))
def test_nested_paths_to_empty_string_renders(self): self.assertEqual( u"Goodbye world!", render(u"Goodbye {{alan/expression}} world!", {'alan': { 'expression': '' }}))
def test_nested_paths_access_nested_objects(self): self.assertEqual( u"Goodbye beautiful world!", render(u"Goodbye {{alan/expression}} world!", {'alan': { 'expression': 'beautiful' }}))
def test_literal_paths_can_be_used(self): self.assertEqual( u"Goodbye beautiful world!", render(u"Goodbye {{[@alan]/expression}} world!", {'@alan': { 'expression': 'beautiful' }}))
def test_functions_can_take_context_arguments(self): def awesome(this, context): return context self.assertEqual( "Frank", render(u"{{awesome frank}}", { 'awesome': awesome, 'frank': 'Frank' })) self.assertEqual( "Frank", render(u"{{{awesome frank}}}", { 'awesome': awesome, 'frank': 'Frank' }))
def test_comments_ignored(self): self.assertEqual( "Goodbye\ncruel\nworld!", render(u"{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!", { 'cruel': "cruel", 'world': "world" }))
def test_block_inverted_sections_with_empty_arrays(self): self.assertEqual( "No people", render(u"{{#people}}{{name}}{{^}}{{none}}{{/people}}", { 'none': "No people", 'people': [] }))
def test_basic_context(self): self.assertEqual( "Goodbye\ncruel\nworld!", render(u"Goodbye\n{{cruel}}\n{{world}}!", { 'cruel': "cruel", 'world': "world" }))
def test_Known_helper_should_render_helper(self): source = u"{{hello}}" self.assertEqual( "foo", render(source, {}, helpers=dict(hello=lambda this: "foo"), knownHelpers=set(['hello'])))
def test_if_else(self): source = u"{{#if goodbye}}GOODBYE{{else}}Hello{{/if}} cruel {{world}}!" self.assertEqual(u"Hello cruel world!", render(source, { 'goodbye': False, 'world': "world" }))
def test_each_this(self): source = u"{{#each name}}{{capitalize this}} {{/each}}" helpers = {'capitalize': lambda this, value: value.upper()} context = { 'name': ['John', 'James'] } self.assertEqual("JOHN JAMES ", render(source, context, helpers=helpers))
def test_with(self): source = u"{{#with person}}{{first}} {{last}}{{/with}}" self.assertEqual( "Alan Johnson", render(source, {'person': { 'first': "Alan", 'last': "Johnson" }}))
def test_it_works_with_single_quote_marks(self): source = u'Message: {{{hello "Alan\'s world"}}}' def hello(this, param): return "Hello " + param self.assertEqual("Message: Hello Alan's world", render(source, {}, helpers=dict(hello=hello)))
def test_if_a_context_is_not_found_helperMissing_is_used(self): def link_to(this, helpername, context): if helpername == 'link_to': return strlist(("<a>", context, "</a>")) source = u"{{hello}} {{link_to world}}" context = {'hello': "Hello", 'world': "world"} self.assertEqual("Hello <a>world</a>", render(source, context, helpers=dict(helperMissing=link_to)))
def test_lambdas_are_resolved_by_blockHelperMissing_not_handlebars_proper( self): # Probably should be called 'lambdas in the context are called as # though for a simple block' - it wants to check moustache # compatibility which allows all block stuff to be overridden via # blockHelperMissing source = u"{{#truthy}}yep{{/truthy}}" self.assertEqual("yep", render(source, {'truthy': lambda this: True}))
def test_block_helper_passing_a_new_context(self): template = u"{{#form yehuda}}<p>{{name}}</p>{{/form}}" context = {'yehuda': {'name': "Yehuda"}} expected = u"<form><p>Yehuda</p></form>" def form(this, options, context): return "<form>" + str_class(options['fn'](context)) + '</form>' helpers = {'form': form} self.assertEqual(expected, render(template, context, helpers=helpers))
def test_escaping_a_String_is_possible(self): source = u'Message: {{{hello "\\"world\\""}}}' def hello(this, param): return "Hello " + param self.assertEqual("Message: Hello \"world\"", render(source, {}, helpers=dict(hello=hello)))
def test_each_with_nested_index(self): source = u"{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!" context = { 'goodbyes': [{'text': "goodbye"}, {'text': "Goodbye"}, {'text': "GOODBYE"}], 'world': "world"} self.assertEqual( "0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!", render(source, context))
def test_block_helper_staying_in_the_same_context(self): template = u"{{#form}}<p>{{name}}</p>{{/form}}" helpers = { 'form': lambda this, options: strlist( [u"<form>", options['fn'](this), u"</form>"]) } self.assertEqual("<form><p>Yehuda</p></form>", render(template, {'name': "Yehuda"}, helpers=helpers))
def test_block_multi_params_work(self): source = u'Message: {{#goodbye cruel world}}'\ u'{{greeting}} {{adj}} {{noun}}{{/goodbye}}' context = {'cruel': "cruel", 'world': "world"} def goodbye(this, options, cruel, world): return options['fn']( {'greeting': "Goodbye", 'adj': cruel, 'noun': world}) self.assertEqual("Message: Goodbye cruel world", render(source, context, helpers=dict(goodbye=goodbye)))
def test_GH_94_Cannot_read_property_of_undefined(self): context = {"books": [ {"title": "The origin of species", "author": {"name": "Charles Darwin"}}, {"title": "Lazarillo de Tormes"}]} source = u"{{#books}}{{title}}{{author.name}}{{/books}}" self.assertEqual( "The origin of speciesCharles DarwinLazarillo de Tormes", render(source, context))
def test_helpers_take_precedence_over_same_named_context_properties(self): source = u"{{goodbye}} {{cruel world}}" helpers = {'goodbye': lambda this: this['goodbye'].upper()} context = {'cruel': lambda this, world: "cruel " + world.upper(), 'goodbye': "goodbye", 'world': "world", } self.assertEqual("GOODBYE cruel WORLD", render(source, context, helpers=helpers))
def test_Scoped_names_take_precedence_over_helpers(self): source = u"{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}" helpers = {'goodbye': lambda this: this['goodbye'].upper()} context = {'cruel': lambda this, world: "cruel " + world.upper(), 'goodbye': "goodbye", 'world': "world", } self.assertEqual(u"goodbye cruel WORLD cruel GOODBYE", render(source, context, helpers=helpers))
def test_helpers_can_take_an_optional_hash_with_booleans(self): def goodbye(this, cruel, world, _print): if _print is True: return "GOODBYE " + cruel + " " + world elif _print is False: return "NOT PRINTING" else: return "THIS SHOULD NOT HAPPEN" helpers = {'goodbye': goodbye} self.assertEqual( "GOODBYE CRUEL WORLD", render(u'{{goodbye cruel="CRUEL" world="WORLD" _print=true}}', {}, helpers=helpers)) self.assertEqual( "NOT PRINTING", render(u'{{goodbye cruel="CRUEL" world="WORLD" _print=false}}', {}, helpers=helpers))
def test_context_with_attrs(self): class TestContext(): @property def text(self): return 'Goodbye' source = u"{{#each .}}{{text}}! {{/each}}cruel world!" context = [TestContext()] self.assertEqual("Goodbye! cruel world!", render(source, context))
def test_helpers_can_take_an_optional_hash(self): source = u'{{goodbye cruel="CRUEL" world="WORLD" times=12}}' # Note: the order is a rotation on the template order to avoid *args # processing generating a false pass def goodbye(this, times, cruel, world): return "GOODBYE " + cruel + " " + world + " " + str(times) + " TIMES" helpers = {'goodbye': goodbye} self.assertEquals(u"GOODBYE CRUEL WORLD 12 TIMES", render(source, {}, helpers=helpers))
def test_if_a_context_is_not_found_helperMissing_is_used(self): def link_to(this, helpername, context): if helpername == 'link_to': return strlist(("<a>", context, "</a>")) source = u"{{hello}} {{link_to world}}" context = {'hello': "Hello", 'world': "world"} self.assertEqual( "Hello <a>world</a>", render(source, context, helpers=dict(helperMissing=link_to)))