Back to Repositories

Validating Liquid Tag Processing and Error Handling in Shopify/liquid

This test suite validates the Liquid tag functionality in Shopify’s Liquid templating engine, focusing on syntax, error handling, and nested tag behavior. The tests ensure proper template rendering and error reporting for various Liquid tag configurations.

Test Coverage Overview

The test suite provides comprehensive coverage of Liquid tag functionality:

  • Basic template rendering with array joins and loops
  • Variable assignment and manipulation within tags
  • Nested tag structures and scoping
  • Error handling for invalid syntax and unclosed tags
  • Line number accuracy in error reporting
  • Raw tag interaction with Liquid tags

Implementation Analysis

The testing approach uses Minitest framework with custom assertions for template validation. It implements systematic verification of Liquid tag parsing, rendering, and error handling using heredoc syntax for clean test case organization. The suite leverages custom assertion methods like assert_template_result and assert_match_syntax_error for consistent validation.

Technical Details

  • Testing Framework: Minitest
  • Custom Assertions: assert_template_result, assert_match_syntax_error
  • Test Helper Integration
  • Liquid Module Integration
  • Heredoc Syntax for Template Definition
  • Frozen String Literal Pragma

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test cases for specific functionality
  • Comprehensive error case coverage
  • Clear test naming conventions
  • Structured test organization
  • Edge case handling
  • Consistent assertion patterns

shopify/liquid

test/integration/tags/liquid_tag_test.rb

            
# frozen_string_literal: true

require 'test_helper'

class LiquidTagTest < Minitest::Test
  include Liquid

  def test_liquid_tag
    assert_template_result('1 2 3', <<~LIQUID, { 'array' => [1, 2, 3] })
      {%- liquid
        echo array | join: " "
      -%}
    LIQUID

    assert_template_result('1 2 3', <<~LIQUID, { 'array' => [1, 2, 3] })
      {%- liquid
        for value in array
          echo value
          unless forloop.last
            echo " "
          endunless
        endfor
      -%}
    LIQUID

    assert_template_result('4 8 12 6', <<~LIQUID, { 'array' => [1, 2, 3] })
      {%- liquid
        for value in array
          assign double_value = value | times: 2
          echo double_value | times: 2
          unless forloop.last
            echo " "
          endunless
        endfor

        echo " "
        echo double_value
      -%}
    LIQUID

    assert_template_result('abc', <<~LIQUID)
      {%- liquid echo "a" -%}
      b
      {%- liquid echo "c" -%}
    LIQUID
  end

  def test_liquid_tag_errors
    assert_match_syntax_error("syntax error (line 1): Unknown tag 'error'", <<~LIQUID)
      {%- liquid error no such tag -%}
    LIQUID

    assert_match_syntax_error("syntax error (line 7): Unknown tag 'error'", <<~LIQUID)
      {{ test }}

      {%-
      liquid
        for value in array

          error no such tag
        endfor
      -%}
    LIQUID

    assert_match_syntax_error("syntax error (line 2): Unknown tag '!!! the guards are vigilant'", <<~LIQUID)
      {%- liquid
        !!! the guards are vigilant
      -%}
    LIQUID

    assert_match_syntax_error("syntax error (line 4): 'for' tag was never closed", <<~LIQUID)
      {%- liquid
        for value in array
          echo 'forgot to close the for tag'
      -%}
    LIQUID
  end

  def test_line_number_is_correct_after_a_blank_token
    assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n\n error %}")
    assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n  \n error %}")
  end

  def test_nested_liquid_tag
    assert_template_result('good', <<~LIQUID)
      {%- if true %}
        {%- liquid
          echo "good"
        %}
      {%- endif -%}
    LIQUID
  end

  def test_cannot_open_blocks_living_past_a_liquid_tag
    assert_match_syntax_error("syntax error (line 3): 'if' tag was never closed", <<~LIQUID)
      {%- liquid
        if true
      -%}
      {%- endif -%}
    LIQUID
  end

  def test_cannot_close_blocks_created_before_a_liquid_tag
    assert_match_syntax_error("syntax error (line 3): 'endif' is not a valid delimiter for liquid tags. use %}", <<~LIQUID)
      {%- if true -%}
      42
      {%- liquid endif -%}
    LIQUID
  end

  def test_liquid_tag_in_raw
    assert_template_result("{% liquid echo 'test' %}\n", <<~LIQUID)
      {% raw %}{% liquid echo 'test' %}{% endraw %}
    LIQUID
  end

  def test_nested_liquid_tags
    assert_template_result('good', <<~LIQUID)
      {%- liquid
        liquid
          if true
            echo "good"
          endif
      -%}
    LIQUID
  end

  def test_nested_liquid_tags_on_same_line
    assert_template_result('good', <<~LIQUID)
      {%- liquid liquid liquid echo "good" -%}
    LIQUID
  end

  def test_nested_liquid_liquid_is_not_skipped_if_used_in_non_tag_position
    assert_template_result('liquid', <<~LIQUID, { 'liquid' => 'liquid' })
      {%- liquid liquid liquid echo liquid -%}
    LIQUID
  end

  def test_next_liquid_with_unclosed_if_tag
    assert_match_syntax_error("Liquid syntax error (line 2): 'if' tag was never closed", <<~LIQUID)
      {%- liquid
        liquid if true
          echo "good"
        endif
      -%}
    LIQUID
  end
end