Back to Repositories

Testing Liquid Template Output Processing in Shopify/liquid

This integration test suite examines Liquid template output processing, focusing on variable handling and filter operations. The test cases validate variable traversal, piping functionality, and custom filter implementations within the Liquid templating system.

Test Coverage Overview

The test suite provides comprehensive coverage of Liquid’s output functionality:

  • Variable rendering and traversal testing
  • Filter chaining and argument handling
  • Custom filter implementation validation
  • Complex nested variable access scenarios
  • HTML tag generation and manipulation tests

Implementation Analysis

The testing approach utilizes Minitest framework with a modular structure, implementing the FunnyFilter module for custom filter testing. The suite employs assertion-based verification patterns to validate template rendering outcomes, with particular attention to variable scope and filter chain processing.

Technical Details

  • Testing Framework: Minitest
  • Custom Filter Module: FunnyFilter
  • Setup Configuration: Pre-defined assigns hash with nested data
  • Assertion Method: assert_template_result
  • Template Parsing: Template.parse with render validation

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test cases with clear, single-responsibility focus
  • Comprehensive edge case coverage for filter arguments
  • Consistent setup and teardown patterns
  • Clear test naming conventions
  • Modular filter implementation for reusability

shopify/liquid

test/integration/output_test.rb

            
# frozen_string_literal: true

require 'test_helper'

module FunnyFilter
  def make_funny(_input)
    'LOL'
  end

  def cite_funny(input)
    "LOL: #{input}"
  end

  def add_smiley(input, smiley = ":-)")
    "#{input} #{smiley}"
  end

  def add_tag(input, tag = "p", id = "foo")
    %(<#{tag} id="#{id}">#{input}</#{tag}>)
  end

  def paragraph(input)
    "<p>#{input}</p>"
  end

  def link_to(name, url)
    %(<a href="#{url}">#{name}</a>)
  end
end

class OutputTest < Minitest::Test
  include Liquid

  def setup
    @assigns = {
      'car' => { 'bmw' => 'good', 'gm' => 'bad' },
    }
  end

  def test_variable
    assert_template_result(" bmw ", " {{best_cars}} ", { "best_cars" => "bmw" })
  end

  def test_variable_traversing_with_two_brackets
    source = "{{ site.data.menu[include.menu][include.locale] }}"
    assert_template_result("it works!", source, {
      "site" => { "data" => { "menu" => { "foo" => { "bar" => "it works!" } } } },
      "include" => { "menu" => "foo", "locale" => "bar" },
    })
  end

  def test_variable_traversing
    source = " {{car.bmw}} {{car.gm}} {{car.bmw}} "
    assert_template_result(" good bad good ", source, @assigns)
  end

  def test_variable_piping
    text     = %( {{ car.gm | make_funny }} )
    expected = %( LOL )

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end

  def test_variable_piping_with_input
    text     = %( {{ car.gm | cite_funny }} )
    expected = %( LOL: bad )

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end

  def test_variable_piping_with_args
    text     = %! {{ car.gm | add_smiley : ':-(' }} !
    expected = %| bad :-( |

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end

  def test_variable_piping_with_no_args
    text     = %( {{ car.gm | add_smiley }} )
    expected = %| bad :-) |

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end

  def test_multiple_variable_piping_with_args
    text     = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
    expected = %| bad :-( :-( |

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end

  def test_variable_piping_with_multiple_args
    text     = %( {{ car.gm | add_tag : 'span', 'bar'}} )
    expected = %( <span id="bar">bad</span> )

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end

  def test_variable_piping_with_variable_args
    text     = %( {{ car.gm | add_tag : 'span', car.bmw}} )
    expected = %( <span id="good">bad</span> )

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end

  def test_multiple_pipings
    assigns = { 'best_cars' => 'bmw' }
    text     = %( {{ best_cars | cite_funny | paragraph }} )
    expected = %( <p>LOL: bmw</p> )

    assert_equal(expected, Template.parse(text).render!(assigns, filters: [FunnyFilter]))
  end

  def test_link_to
    text     = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
    expected = %( <a href="http://typo.leetsoft.com">Typo</a> )

    assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
  end
end # OutputTest