From 9409dd8f4a0d7985eeed2739736c65260ef76630 Mon Sep 17 00:00:00 2001 From: Julia Boutin Date: Mon, 20 Oct 2025 12:57:36 -0600 Subject: [PATCH] Render arguments should maintain correct precedence --- lib/liquid.rb | 1 + lib/liquid/tags/render.rb | 42 +- test/integration/tags/snippet_test.rb | 1531 ++++++++++++++++++------- 3 files changed, 1125 insertions(+), 449 deletions(-) diff --git a/lib/liquid.rb b/lib/liquid.rb index 09cafe94..2bc0b4a7 100644 --- a/lib/liquid.rb +++ b/lib/liquid.rb @@ -40,6 +40,7 @@ module Liquid QuotedString = /"[^"]*"|'[^']*'/ QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o TagAttributes = /(\w[\w-]*)\s*\:\s*(#{QuotedFragment})/o + ContextInheritance = /\.\.\./ AnyStartingTag = /#{TagStart}|#{VariableStart}/o PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om diff --git a/lib/liquid/tags/render.rb b/lib/liquid/tags/render.rb index db8fa768..dd1a7a18 100644 --- a/lib/liquid/tags/render.rb +++ b/lib/liquid/tags/render.rb @@ -42,10 +42,6 @@ module Liquid @is_for_loop end - def inherit_context? - @inherit_context - end - def render_to_output_buffer(context, output) render_tag(context, output) end @@ -77,20 +73,21 @@ module Liquid inner_context.partial = true end - if is_inline && inherit_context? - context.scopes.each do |scope| - scope.each do |key, value| - inner_context[key] = value + inner_context['forloop'] = forloop if forloop + + @attributes.each do |key, value| + if key == "..." && is_inline + context.scopes.each do |scope| + scope.each do |k, v| + inner_context[k] = v + end end + else + inner_context[key] = context.evaluate(value) end end - @attributes.each do |key, value| - inner_context[key] = context.evaluate(value) - end - inner_context[context_variable_name] = var unless var.nil? - inner_context['forloop'] = forloop if forloop partial.render_to_output_buffer(inner_context, output) forloop&.send(:increment!) @@ -119,14 +116,6 @@ module Liquid p.consume?(:comma) - @inherit_context = false - # ... inline snippets syntax - if p.consume?(:dotdotdot) - p.consume?(:comma) - - @inherit_context = true - end - @attributes = {} while p.look(:id) key = p.consume @@ -162,11 +151,16 @@ module Liquid @variable_name_expr = variable_name ? parse_expression(variable_name) : nil @template_name_expr = parse_expression(template_name) @is_for_loop = (with_or_for == FOR) - @inherit_context = markup.include?('...') @attributes = {} - markup.scan(TagAttributes) do |key, value| - @attributes[key] = parse_expression(value) + markup.scan(/(#{ContextInheritance})|#{TagAttributes.source}/) do |context_marker, key, value| + if context_marker + @attributes.delete("...") + @attributes["..."] = true + elsif key && value + @attributes.delete(key) + @attributes[key] = parse_expression(value) + end end end diff --git a/test/integration/tags/snippet_test.rb b/test/integration/tags/snippet_test.rb index 8699b359..38fe599a 100644 --- a/test/integration/tags/snippet_test.rb +++ b/test/integration/tags/snippet_test.rb @@ -5,556 +5,1237 @@ require 'test_helper' class SnippetTest < Minitest::Test include Liquid - def test_valid_inline_snippet - template = <<~LIQUID.strip - {% snippet input %} + class LaxMode < SnippetTest + def test_valid_inline_snippet + template = <<~LIQUID.strip + {% snippet input %} + Hey + {% endsnippet %} + LIQUID + expected = '' + + assert_template_result(expected, template) + end + + def test_render_inline_snippet + template = <<~LIQUID.strip + {% snippet hey %} Hey - {% endsnippet %} - LIQUID - expected = '' + {% endsnippet %} - assert_template_result(expected, template) - end + {%- render hey -%} + LIQUID + expected = <<~OUTPUT - def test_render_inline_snippet - template = <<~LIQUID.strip - {% snippet hey %} - Hey - {% endsnippet %} + Hey + OUTPUT - {%- render hey -%} - LIQUID - expected = <<~OUTPUT + assert_template_result(expected, template) + end - Hey - OUTPUT + def test_render_inline_snippet_with_variable + template = <<~LIQUID.strip + {% snippet hey %} +

Today is {{ "hello" | capitalize }}

+ {% endsnippet %} - assert_template_result(expected, template) - end + {%- render hey -%} + LIQUID + expected = <<~OUTPUT - def test_render_inline_snippet_with_variable - template = <<~LIQUID.strip - {% snippet hey %} -

Today is {{ "hello" | capitalize }}

- {% endsnippet %} +

Today is Hello

+ OUTPUT - {%- render hey -%} - LIQUID - expected = <<~OUTPUT + assert_template_result(expected, template) + end -

Today is Hello

- OUTPUT + def test_render_multiple_inline_snippets + template = <<~LIQUID.strip + {% snippet input %} + + {% endsnippet %} - assert_template_result(expected, template) - end + {% snippet banner %} + + Welcome to my store! + + {% endsnippet %} - def test_render_multiple_inline_snippets - template = <<~LIQUID.strip - {% snippet input %} - - {% endsnippet %} + {%- render input -%} + {%- render banner -%} + LIQUID + expected = <<~OUTPUT - {% snippet banner %} - - Welcome to my store! - - {% endsnippet %} - {%- render input -%} - {%- render banner -%} - LIQUID - expected = <<~OUTPUT + + + Welcome to my store! + + OUTPUT - + assert_template_result(expected, template) + end - - Welcome to my store! - - OUTPUT + def test_render_inline_snippet_with_argument + template = <<~LIQUID.strip + {% snippet input %} + + {% endsnippet %} - assert_template_result(expected, template) - end + {%- render input, type: "text" -%} + LIQUID + expected = <<~OUTPUT - def test_render_inline_snippet_with_argument - template = <<~LIQUID.strip - {% snippet input %} - - {% endsnippet %} + + OUTPUT - {%- render input, type: "text" -%} - LIQUID - expected = <<~OUTPUT + assert_template_result(expected, template) + end - - OUTPUT + def test_render_inline_snippet_with_doc_tag + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + {% enddoc %} - assert_template_result(expected, template) - end + + {% endsnippet %} - def test_render_inline_snippet_with_doc_tag - template = <<~LIQUID.strip - {% snippet input %} - {% doc %} - @param {string} type - Input type. - {% enddoc %} + {%- render input, type: "text" -%} + LIQUID + expected = <<~OUTPUT - - {% endsnippet %} - {%- render input, type: "text" -%} - LIQUID - expected = <<~OUTPUT + + OUTPUT + assert_template_result(expected, template) + end - - OUTPUT + def test_render_inline_snippet_with_evaluated_assign + template = <<~LIQUID.strip + {% snippet input %} +

{{ greeting }}

+ {% endsnippet %} - assert_template_result(expected, template) - end + {%- assign greeting = "hello" | upcase -%} + {%- render input, greeting: greeting -%} + LIQUID + expected = <<~OUTPUT - def test_render_inline_snippet_with_evaluated_assign - template = <<~LIQUID.strip - {% snippet input %} -

{{ greeting }}

- {% endsnippet %} +

HELLO

+ OUTPUT - {%- assign greeting = "hello" | upcase -%} - {%- render input, greeting: greeting -%} - LIQUID - expected = <<~OUTPUT + assert_template_result(expected, template) + end -

HELLO

- OUTPUT + def test_render_inline_snippet_with_multiple_arguments + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} - assert_template_result(expected, template) - end + + {% endsnippet %} - def test_render_inline_snippet_with_multiple_arguments - template = <<~LIQUID.strip - {% snippet input %} - {% doc %} - @param {string} type - Input type. - @param {string} value - Input value. - {% enddoc %} + {%- render input, type: "text", value: "Hello" -%} + LIQUID + expected = <<~OUTPUT - - {% endsnippet %} - {%- render input, type: "text", value: "Hello" -%} - LIQUID - expected = <<~OUTPUT + + OUTPUT + assert_template_result(expected, template) + end - - OUTPUT + def test_render_inline_snippets_using_same_argument_name + template = <<~LIQUID.strip + {% snippet input %} + + {% endsnippet %} - assert_template_result(expected, template) - end + {% snippet inputs %} + + {% endsnippet %} - def test_render_inline_snippets_using_same_argument_name - template = <<~LIQUID.strip - {% snippet input %} - - {% endsnippet %} + {%- render input, type: "text" -%} + {%- render inputs, type: "password", value: "pass" -%} + LIQUID - {% snippet inputs %} - - {% endsnippet %} + expected = <<~OUTPUT - {%- render input, type: "text" -%} - {%- render inputs, type: "password", value: "pass" -%} - LIQUID - expected = <<~OUTPUT + + + OUTPUT - + assert_template_result(expected, template) + end - - OUTPUT + def test_render_inline_snippet_empty_string_when_missing_argument + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} - assert_template_result(expected, template) - end + + {% endsnippet %} - def test_render_inline_snippet_empty_string_when_missing_argument - template = <<~LIQUID.strip - {% snippet input %} - {% doc %} - @param {string} type - Input type. - @param {string} value - Input value. - {% enddoc %} + {%- render input, type: "text" -%} + LIQUID + expected = <<~OUTPUT - - {% endsnippet %} - {%- render input, type: "text" -%} - LIQUID - expected = <<~OUTPUT + + OUTPUT + assert_template_result(expected, template) + end - - OUTPUT + def test_render_inline_snippet_shouldnt_leak_context + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} - assert_template_result(expected, template) - end + + {% endsnippet %} - def test_render_inline_snippet_shouldnt_leak_context - template = <<~LIQUID.strip - {% snippet input %} - {% doc %} - @param {string} type - Input type. - @param {string} value - Input value. - {% enddoc %} + {%- render input, type: "text", value: "Hello" -%} - - {% endsnippet %} + {{ type }} + {{ value }} + LIQUID + expected = <<~OUTPUT - {%- render input, type: "text", value: "Hello" -%} - {{ type }} - {{ value }} - LIQUID - expected = <<~OUTPUT + + OUTPUT - + assert_template_result(expected, template) + end - OUTPUT + def test_render_multiple_inline_snippets_without_leaking_context + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + {% enddoc %} - assert_template_result(expected, template) - end + + {% endsnippet %} - def test_render_multiple_inline_snippets_without_leaking_context - template = <<~LIQUID.strip - {% snippet input %} - {% doc %} - @param {string} type - Input type. - {% enddoc %} + {% snippet no_leak %} + + {% endsnippet %} - - {% endsnippet %} + {%- render input, type: "text" -%} + {%- render no_leak -%} + LIQUID + expected = <<~OUTPUT - {% snippet no_leak %} - - {% endsnippet %} - {%- render input, type: "text" -%} - {%- render no_leak -%} - LIQUID - expected = <<~OUTPUT + + + OUTPUT - + assert_template_result(expected, template) + end - - OUTPUT + def test_render_inline_snippet_without_outside_context + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} - assert_template_result(expected, template) - end - - def test_render_inline_snippet_without_outside_context - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, message: 'Welcome!' %} - LIQUID - expected = <<~OUTPUT - - - - - - -
- Welcome! -
- OUTPUT - - assert_template_result(expected, template) - end - - def test_render_inline_snippet_with_outside_context - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, ..., message: 'Welcome!' %} - LIQUID - expected = <<~OUTPUT - - - - - - -
- Welcome! -
- OUTPUT - - assert_template_result(expected, template) - end - - def test_render_inline_snippet_with_outside_context_rigid - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, ..., message: 'Welcome!' %} - LIQUID - expected = <<~OUTPUT - - - - - - -
- Welcome! -
- OUTPUT - - assert_template_result(expected, template, error_mode: :rigid) - end - - def test_inline_snippet_local_scope_takes_precedence - template = <<~LIQUID - {% assign color_scheme = 'dark' %} - - {% snippet header %} - {% assign color_scheme = 'light' %} -
- {{ message }} -
- {% endsnippet %} - - {{ color_scheme }} - - {% render header, ..., message: 'Welcome!' %} - - {{ color_scheme }} - LIQUID - expected = <<~OUTPUT - - - - - dark - - - -
- Welcome! -
- - - dark - OUTPUT - - assert_template_result(expected, template) - end - - def test_render_captured_snippet - template = <<~LIQUID - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - {% capture up_header %} - {%- render header, ..., message: 'Welcome!' -%} - {% endcapture %} - - {{ up_header | upcase }} - - {{ header | upcase }} - - {{ header }} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- WELCOME! -
- - - SNIPPETDROP - - SnippetDrop - OUTPUT - - assert_template_result(expected, template) - end - - def test_render_snippets_as_arguments - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - - {% snippet header %} + {% snippet header %}
{{ message }}
- {% endsnippet %} + {% endsnippet %} - {% snippet main %} - {% assign color_scheme = 'auto' %} -
- {% render header, ..., message: 'Welcome!' %} -
- {% endsnippet %} - - {% render main, header: header %} - LIQUID - - expected = <<~OUTPUT + {% render header, message: 'Welcome!' %} + LIQUID + expected = <<~OUTPUT +
+ Welcome! +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_with_outside_context + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + + {% render header, ..., message: 'Welcome!' %} + LIQUID + expected = <<~OUTPUT -
-
+ + +
+ Welcome! +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_inline_snippet_local_scope_takes_precedence + template = <<~LIQUID + {% assign color_scheme = 'dark' %} + + {% snippet header %} + {% assign color_scheme = 'light' %} +
+ {{ message }} +
+ {% endsnippet %} + + {{ color_scheme }} + + {% render header, ..., message: 'Welcome!' %} + + {{ color_scheme }} + LIQUID + expected = <<~OUTPUT + + + + + dark + + + +
Welcome!
-
- OUTPUT - assert_template_result(expected, template) + dark + OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_with_correct_argument_precedence + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign message = 'Goodbye!' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + + {% render header, message: 'Welcome!', ... %} + LIQUID + expected = <<~OUTPUT + + + + + + + +
+ Goodbye! +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_with_correct_argument_order + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign message = 'Goodbye!' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + + {% render header, ..., message: 'Welcome!' %} + LIQUID + expected = <<~OUTPUT + + + + + + + +
+ Welcome! +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_with_correct_duplicate_argument_precedence + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign message = 'Goodbye!' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + + {% render header, message: 'Welcome!', ..., message: 'Hi!' %} + LIQUID + expected = <<~OUTPUT + + + + + + + +
+ Hi! +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_render_captured_snippet + template = <<~LIQUID + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + {% capture up_header %} + {%- render header, ..., message: 'Welcome!' -%} + {% endcapture %} + + {{ up_header | upcase }} + + {{ header | upcase }} + + {{ header }} + LIQUID + expected = <<~OUTPUT + + + + + + + +
+ WELCOME! +
+ + + SNIPPETDROP + + SnippetDrop + OUTPUT + + assert_template_result(expected, template) + end + + def test_render_snippets_as_arguments + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + {% snippet main %} + {% assign color_scheme = 'auto' %} + +
+ {% render header, ..., message: 'Welcome!' %} +
+ {% endsnippet %} + + {% render main, header: header %} + LIQUID + + expected = <<~OUTPUT + + + + + + + + + +
+ +
+ Welcome! +
+ +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_forloop + template = <<~LIQUID.strip + {% snippet item %} +
  • {{ forloop.index }}: {{ item }}
  • + {% endsnippet %} + + {% assign items = "A,B,C" | split: "," %} + {%- render item for items -%} + LIQUID + expected = <<~OUTPUT + + + +
  • 1: A
  • + +
  • 2: B
  • + +
  • 3: C
  • + OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_with + template = <<~LIQUID.strip + {% snippet header %} +
    {{ header }}
    + {% endsnippet %} + + {% assign product = "Apple" %} + {%- render header with product -%} + LIQUID + expected = <<~OUTPUT + + + +
    Apple
    + OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_alias + template = <<~LIQUID.strip + {% snippet product_card %} +
    {{ item }}
    + {% endsnippet %} + + {% assign featured = "Apple" %} + {%- render product_card with featured as item -%} + LIQUID + expected = <<~OUTPUT + + + +
    Apple
    + OUTPUT + + assert_template_result(expected, template) + end + + def test_render_inline_snippet_inside_loop + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign array = '1,2,3' | split: ',' %} + + {% for i in array %} + {% snippet header %} +
    + {{ message }} {{ i }} +
    + {% endsnippet %} + {% endfor %} + + {% render header, ..., message: '👉' %} + LIQUID + expected = <<~OUTPUT + + + + + + +
    + 👉#{" "} +
    + OUTPUT + + assert_template_result(expected, template) + end end - def test_render_inline_snippet_forloop - template = <<~LIQUID.strip - {% snippet item %} -
  • {{ forloop.index }}: {{ item }}
  • - {% endsnippet %} + class RigidMode < SnippetTest + def test_valid_inline_snippet + template = <<~LIQUID.strip + {% snippet input %} + Hey + {% endsnippet %} + LIQUID + expected = '' - {% assign items = "A,B,C" | split: "," %} - {%- render item for items -%} - LIQUID - expected = <<~OUTPUT + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet + template = <<~LIQUID.strip + {% snippet hey %} + Hey + {% endsnippet %} + + {%- render hey -%} + LIQUID + expected = <<~OUTPUT + + Hey + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with_variable + template = <<~LIQUID.strip + {% snippet hey %} +

    Today is {{ "hello" | capitalize }}

    + {% endsnippet %} + + {%- render hey -%} + LIQUID + expected = <<~OUTPUT + +

    Today is Hello

    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_multiple_inline_snippets + template = <<~LIQUID.strip + {% snippet input %} + + {% endsnippet %} + + {% snippet banner %} + + Welcome to my store! + + {% endsnippet %} + + {%- render input -%} + {%- render banner -%} + LIQUID + expected = <<~OUTPUT -
  • 1: A
  • + -
  • 2: B
  • + + Welcome to my store! + + OUTPUT -
  • 3: C
  • - OUTPUT + assert_template_result(expected, template, error_mode: :rigid) + end - assert_template_result(expected, template) - end + def test_render_inline_snippet_with_argument + template = <<~LIQUID.strip + {% snippet input %} + + {% endsnippet %} - def test_render_inline_snippet_with - template = <<~LIQUID.strip - {% snippet header %} -
    {{ header }}
    - {% endsnippet %} + {%- render input, type: "text" -%} + LIQUID + expected = <<~OUTPUT - {% assign product = "Apple" %} - {%- render header with product -%} - LIQUID - expected = <<~OUTPUT + + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with_doc_tag + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + {% enddoc %} + + + {% endsnippet %} + + {%- render input, type: "text" -%} + LIQUID + expected = <<~OUTPUT -
    Apple
    - OUTPUT + + OUTPUT - assert_template_result(expected, template) - end + assert_template_result(expected, template, error_mode: :rigid) + end - def test_render_inline_snippet_alias - template = <<~LIQUID.strip - {% snippet product_card %} -
    {{ item }}
    - {% endsnippet %} + def test_render_inline_snippet_with_evaluated_assign + template = <<~LIQUID.strip + {% snippet input %} +

    {{ greeting }}

    + {% endsnippet %} - {% assign featured = "Apple" %} - {%- render product_card with featured as item -%} - LIQUID - expected = <<~OUTPUT + {%- assign greeting = "hello" | upcase -%} + {%- render input, greeting: greeting -%} + LIQUID + expected = <<~OUTPUT + +

    HELLO

    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with_multiple_arguments + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} + + + {% endsnippet %} + + {%- render input, type: "text", value: "Hello" -%} + LIQUID + expected = <<~OUTPUT -
    Apple
    - OUTPUT + + OUTPUT - assert_template_result(expected, template) - end + assert_template_result(expected, template, error_mode: :rigid) + end - def test_render_inline_snippet_inside_loop - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign array = '1,2,3' | split: ',' %} + def test_render_inline_snippets_using_same_argument_name + template = <<~LIQUID.strip + {% snippet input %} + + {% endsnippet %} - {% for i in array %} - {% snippet header %} -
    - {{ message }} {{ i }} -
    - {% endsnippet %} - {% endfor %} + {% snippet inputs %} + + {% endsnippet %} - {% render header, ..., message: '👉' %} - LIQUID - expected = <<~OUTPUT + {%- render input, type: "text" -%} + {%- render inputs, type: "password", value: "pass" -%} + LIQUID + + expected = <<~OUTPUT + + + + + + + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_empty_string_when_missing_argument + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} + + + {% endsnippet %} + + {%- render input, type: "text" -%} + LIQUID + expected = <<~OUTPUT + + + + + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_shouldnt_leak_context + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} + + + {% endsnippet %} + + {%- render input, type: "text", value: "Hello" -%} + + {{ type }} + {{ value }} + LIQUID + expected = <<~OUTPUT + + + + + + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_multiple_inline_snippets_without_leaking_context + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + {% enddoc %} + + + {% endsnippet %} + + {% snippet no_leak %} + + {% endsnippet %} + + {%- render input, type: "text" -%} + {%- render no_leak -%} + LIQUID + expected = <<~OUTPUT + + + + + + + + + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_without_outside_context + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
    + {{ message }} +
    + {% endsnippet %} + + + {% render header, message: 'Welcome!' %} + LIQUID + expected = <<~OUTPUT -
    - 👉#{" "} -
    - OUTPUT +
    + Welcome! +
    + OUTPUT - assert_template_result(expected, template) + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with_outside_context + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
    + {{ message }} +
    + {% endsnippet %} + + + {% render header, ..., message: 'Welcome!' %} + LIQUID + expected = <<~OUTPUT + + + + + + +
    + Welcome! +
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_inline_snippet_local_scope_takes_precedence + template = <<~LIQUID + {% assign color_scheme = 'dark' %} + + {% snippet header %} + {% assign color_scheme = 'light' %} +
    + {{ message }} +
    + {% endsnippet %} + + {{ color_scheme }} + + {% render header, ..., message: 'Welcome!' %} + + {{ color_scheme }} + LIQUID + expected = <<~OUTPUT + + + + + dark + + + +
    + Welcome! +
    + + + dark + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with_correct_argument_precedence + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign message = 'Goodbye!' %} + + {% snippet header %} +
    + {{ message }} +
    + {% endsnippet %} + + + {% render header, message: 'Welcome!', ... %} + LIQUID + expected = <<~OUTPUT + + + + + + + +
    + Goodbye! +
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with_correct_argument_order + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign message = 'Goodbye!' %} + + {% snippet header %} +
    + {{ message }} +
    + {% endsnippet %} + + + {% render header, ..., message: 'Welcome!' %} + LIQUID + expected = <<~OUTPUT + + + + + + + +
    + Welcome! +
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with_correct_duplicate_argument_precedence + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign message = 'Goodbye!' %} + + {% snippet header %} +
    + {{ message }} +
    + {% endsnippet %} + + + {% render header, message: 'Welcome!', ..., message: 'Hi!' %} + LIQUID + expected = <<~OUTPUT + + + + + + + +
    + Hi! +
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_captured_snippet + template = <<~LIQUID + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
    + {{ message }} +
    + {% endsnippet %} + + {% capture up_header %} + {%- render header, ..., message: 'Welcome!' -%} + {% endcapture %} + + {{ up_header | upcase }} + + {{ header | upcase }} + + {{ header }} + LIQUID + expected = <<~OUTPUT + + + + + + + +
    + WELCOME! +
    + + + SNIPPETDROP + + SnippetDrop + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_snippets_as_arguments + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
    + {{ message }} +
    + {% endsnippet %} + + {% snippet main %} + {% assign color_scheme = 'auto' %} + +
    + {% render header, ..., message: 'Welcome!' %} +
    + {% endsnippet %} + + {% render main, header: header %} + LIQUID + + expected = <<~OUTPUT + + + + + + + + + +
    + +
    + Welcome! +
    + +
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_forloop + template = <<~LIQUID.strip + {% snippet item %} +
  • {{ forloop.index }}: {{ item }}
  • + {% endsnippet %} + + {% assign items = "A,B,C" | split: "," %} + {%- render item for items -%} + LIQUID + expected = <<~OUTPUT + + + +
  • 1: A
  • + +
  • 2: B
  • + +
  • 3: C
  • + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_with + template = <<~LIQUID.strip + {% snippet header %} +
    {{ header }}
    + {% endsnippet %} + + {% assign product = "Apple" %} + {%- render header with product -%} + LIQUID + expected = <<~OUTPUT + + + +
    Apple
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_alias + template = <<~LIQUID.strip + {% snippet product_card %} +
    {{ item }}
    + {% endsnippet %} + + {% assign featured = "Apple" %} + {%- render product_card with featured as item -%} + LIQUID + expected = <<~OUTPUT + + + +
    Apple
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + + def test_render_inline_snippet_inside_loop + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + {% assign array = '1,2,3' | split: ',' %} + + {% for i in array %} + {% snippet header %} +
    + {{ message }} {{ i }} +
    + {% endsnippet %} + {% endfor %} + + {% render header, ..., message: '👉' %} + LIQUID + expected = <<~OUTPUT + + + + + + +
    + 👉#{" "} +
    + OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end end end