mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-14 16:30:40 -07:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b1b9f649a | ||
|
|
474315c6bb | ||
|
|
667664bb22 | ||
|
|
1cdd1f0834 | ||
|
|
1cd5ec54f0 | ||
|
|
29732f4305 | ||
|
|
abed47547c | ||
|
|
ace3fe15ac | ||
|
|
db8e85ab31 | ||
|
|
f484b868d0 | ||
|
|
3e8994c258 | ||
|
|
7ccac29688 | ||
|
|
96b5325f87 | ||
|
|
b8f05267a9 | ||
|
|
6ce4ec1011 | ||
|
|
a39422feac | ||
|
|
2c2f5826d5 | ||
|
|
c99c93255d | ||
|
|
c0c191cabd | ||
|
|
6765d93938 | ||
|
|
4f17abfb4a | ||
|
|
eff2a63204 | ||
|
|
fbab19ac8c | ||
|
|
b14bf94c98 | ||
|
|
951be6c1f5 | ||
|
|
c8f3cfa8fc | ||
|
|
f21d1c5d9d | ||
|
|
456be2f75e | ||
|
|
ff1c35b986 | ||
|
|
cab08cfe57 | ||
|
|
bb7027138e | ||
|
|
34512df8e9 | ||
|
|
bca01e8944 | ||
|
|
d3647e280d | ||
|
|
c0f565ce7b |
@@ -109,14 +109,22 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
private def parse_for_document(tokenizer, parse_context)
|
||||
private def handle_invalid_tag_token(token, parse_context)
|
||||
if token.end_with?('%}')
|
||||
yield token, token
|
||||
else
|
||||
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
||||
end
|
||||
end
|
||||
|
||||
private def parse_for_document(tokenizer, parse_context, &block)
|
||||
while (token = tokenizer.shift)
|
||||
next if token.empty?
|
||||
case
|
||||
when token.start_with?(TAGSTART)
|
||||
whitespace_handler(token, parse_context)
|
||||
unless token =~ FullToken
|
||||
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
||||
return handle_invalid_tag_token(token, parse_context, &block)
|
||||
end
|
||||
tag_name = Regexp.last_match(2)
|
||||
markup = Regexp.last_match(4)
|
||||
|
||||
@@ -26,7 +26,7 @@ module Liquid
|
||||
@environments = [environments]
|
||||
@environments.flatten!
|
||||
|
||||
@static_environments = [static_environments].flat_map(&:freeze).freeze
|
||||
@static_environments = [static_environments].flatten(1).freeze
|
||||
@scopes = [(outer_scope || {})]
|
||||
@registers = registers.is_a?(Registers) ? registers : Registers.new(registers)
|
||||
@errors = []
|
||||
|
||||
@@ -5,7 +5,7 @@ module Liquid
|
||||
# @liquid_type object
|
||||
# @liquid_name forloop
|
||||
# @liquid_summary
|
||||
# Information about a parent [`for` loop](/api/liquid/tags#for).
|
||||
# Information about a parent [`for` loop](/api/liquid/tags/for).
|
||||
class ForloopDrop < Drop
|
||||
def initialize(name, length, parentloop)
|
||||
@name = name
|
||||
@@ -30,10 +30,7 @@ module Liquid
|
||||
# @liquid_return [forloop]
|
||||
attr_reader :parentloop
|
||||
|
||||
def name
|
||||
Usage.increment('forloop_drop_name')
|
||||
@name
|
||||
end
|
||||
attr_reader :name
|
||||
|
||||
# @liquid_public_docs
|
||||
# @liquid_summary
|
||||
|
||||
@@ -6,7 +6,14 @@ require 'bigdecimal'
|
||||
|
||||
module Liquid
|
||||
module StandardFilters
|
||||
MAX_INT = (1 << 31) - 1
|
||||
MAX_I32 = (1 << 31) - 1
|
||||
private_constant :MAX_I32
|
||||
|
||||
MIN_I64 = -(1 << 63)
|
||||
MAX_I64 = (1 << 63) - 1
|
||||
I64_RANGE = MIN_I64..MAX_I64
|
||||
private_constant :MIN_I64, :MAX_I64, :I64_RANGE
|
||||
|
||||
HTML_ESCAPE = {
|
||||
'&' => '&',
|
||||
'>' => '>',
|
||||
@@ -73,7 +80,7 @@ module Liquid
|
||||
# @liquid_type filter
|
||||
# @liquid_category string
|
||||
# @liquid_summary
|
||||
# Escapes a string.
|
||||
# Escapes special characters in HTML, such as `<>`, `'`, and `&`, and converts characters into escape sequences. The filter doesn't effect characters within the string that don’t have a corresponding escape sequence.".
|
||||
# @liquid_syntax string | escape
|
||||
# @liquid_return [string]
|
||||
def escape(input)
|
||||
@@ -186,10 +193,19 @@ module Liquid
|
||||
offset = Utils.to_integer(offset)
|
||||
length = length ? Utils.to_integer(length) : 1
|
||||
|
||||
if input.is_a?(Array)
|
||||
input.slice(offset, length) || []
|
||||
else
|
||||
input.to_s.slice(offset, length) || ''
|
||||
begin
|
||||
if input.is_a?(Array)
|
||||
input.slice(offset, length) || []
|
||||
else
|
||||
input.to_s.slice(offset, length) || ''
|
||||
end
|
||||
rescue RangeError
|
||||
if I64_RANGE.cover?(length) && I64_RANGE.cover?(offset)
|
||||
raise # unexpected error
|
||||
end
|
||||
offset = offset.clamp(I64_RANGE)
|
||||
length = length.clamp(I64_RANGE)
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
@@ -239,9 +255,9 @@ module Liquid
|
||||
wordlist = begin
|
||||
input.split(" ", words + 1)
|
||||
rescue RangeError
|
||||
raise if words + 1 < MAX_INT
|
||||
# e.g. integer #{words} too big to convert to `int'
|
||||
raise Liquid::ArgumentError, "integer #{words} too big for truncatewords"
|
||||
# integer too big for String#split, but we can semantically assume no truncation is needed
|
||||
return input if words + 1 > MAX_I32
|
||||
raise # unexpected error
|
||||
end
|
||||
return input if wordlist.length <= words
|
||||
|
||||
@@ -599,7 +615,7 @@ module Liquid
|
||||
# @liquid_description
|
||||
# > Note:
|
||||
# > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
|
||||
# > [`uniq` filter](/api/liquid/filters#uniq).
|
||||
# > [`uniq` filter](/api/liquid/filters/uniq).
|
||||
# @liquid_syntax array | concat: array
|
||||
# @liquid_return [array[untyped]]
|
||||
def concat(input, array)
|
||||
|
||||
@@ -5,7 +5,7 @@ module Liquid
|
||||
# @liquid_type object
|
||||
# @liquid_name tablerowloop
|
||||
# @liquid_summary
|
||||
# Information about a parent [`tablerow` loop](/api/liquid/tags#tablerow).
|
||||
# Information about a parent [`tablerow` loop](/api/liquid/tags/tablerow).
|
||||
class TablerowloopDrop < Drop
|
||||
def initialize(length, cols)
|
||||
@length = length
|
||||
|
||||
+8
-2
@@ -14,12 +14,18 @@ module Liquid
|
||||
end
|
||||
|
||||
def disable_tags(*tag_names)
|
||||
@disabled_tags ||= []
|
||||
@disabled_tags.concat(tag_names)
|
||||
tag_names += disabled_tags
|
||||
define_singleton_method(:disabled_tags) { tag_names }
|
||||
prepend(Disabler)
|
||||
end
|
||||
|
||||
private :new
|
||||
|
||||
protected
|
||||
|
||||
def disabled_tags
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, parse_context)
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
module Liquid
|
||||
class Tag
|
||||
module Disabler
|
||||
module ClassMethods
|
||||
attr_reader :disabled_tags
|
||||
end
|
||||
|
||||
def self.prepended(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.with_disabled_tags(self.class.disabled_tags) do
|
||||
super
|
||||
|
||||
@@ -15,7 +15,7 @@ module Liquid
|
||||
# @liquid_category iteration
|
||||
# @liquid_name break
|
||||
# @liquid_summary
|
||||
# Stops a [`for` loop](/api/liquid/tags#for) from iterating.
|
||||
# Stops a [`for` loop](/api/liquid/tags/for) from iterating.
|
||||
# @liquid_syntax
|
||||
# {% break %}
|
||||
class Break < Tag
|
||||
|
||||
@@ -8,7 +8,7 @@ module Liquid
|
||||
# @liquid_summary
|
||||
# Prevents an expression from being rendered or output.
|
||||
# @liquid_description
|
||||
# Any text inside `comment` tags won't be output, and any Liquid code won't be rendered.
|
||||
# Any text inside `comment` tags won't be output, and any Liquid code will be parsed, but not executed.
|
||||
# @liquid_syntax
|
||||
# {% comment %}
|
||||
# content
|
||||
|
||||
@@ -6,7 +6,7 @@ module Liquid
|
||||
# @liquid_category iteration
|
||||
# @liquid_name continue
|
||||
# @liquid_summary
|
||||
# Causes a [`for` loop](/api/liquid/tags#for) to skip to the next iteration.
|
||||
# Causes a [`for` loop](/api/liquid/tags/for) to skip to the next iteration.
|
||||
# @liquid_syntax
|
||||
# {% continue %}
|
||||
class Continue < Tag
|
||||
|
||||
@@ -6,7 +6,7 @@ module Liquid
|
||||
# @liquid_category iteration
|
||||
# @liquid_name cycle
|
||||
# @liquid_summary
|
||||
# Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/api/liquid/tags#for).
|
||||
# Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/api/liquid/tags/for).
|
||||
# @liquid_description
|
||||
# The `cycle` tag must be used inside a `for` loop.
|
||||
#
|
||||
|
||||
@@ -12,8 +12,8 @@ module Liquid
|
||||
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
||||
# [snippets](/themes/architecture#snippets) included in the file.
|
||||
#
|
||||
# Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/api/liquid/tags#assign)
|
||||
# and [`capture`](/api/liquid/tags#capture). However, `decrement` and [`increment`](/api/liquid/tags#increment) share
|
||||
# Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/api/liquid/tags/assign)
|
||||
# and [`capture`](/api/liquid/tags/capture). However, `decrement` and [`increment`](/api/liquid/tags/increment) share
|
||||
# variables.
|
||||
# @liquid_syntax
|
||||
# {% decrement variable_name %}
|
||||
|
||||
@@ -9,7 +9,7 @@ module Liquid
|
||||
# Outputs an expression.
|
||||
# @liquid_description
|
||||
# Using the `echo` tag is the same as wrapping an expression in curly brackets (`{{` and `}}`). However, unlike the curly
|
||||
# bracket method, you can use the `echo` tag inside [`liquid` tags](/api/liquid/tags#liquid).
|
||||
# bracket method, you can use the `echo` tag inside [`liquid` tags](/api/liquid/tags/liquid).
|
||||
#
|
||||
# > Tip:
|
||||
# > You can use [filters](/api/liquid/filters) on expressions inside `echo` tags.
|
||||
|
||||
@@ -9,10 +9,10 @@ module Liquid
|
||||
# Renders an expression for every item in an array.
|
||||
# @liquid_description
|
||||
# You can do a maximum of 50 iterations with a `for` loop. If you need to iterate over more than 50 items, then use the
|
||||
# [`paginate` tag](/api/liquid/tags#paginate) to split the items over multiple pages.
|
||||
# [`paginate` tag](/api/liquid/tags/paginate) to split the items over multiple pages.
|
||||
#
|
||||
# > Tip:
|
||||
# > Every `for` loop has an associated [`forloop` object](/api/liquid/objects#forloop) with information about the loop.
|
||||
# > Every `for` loop has an associated [`forloop` object](/api/liquid/objects/forloop) with information about the loop.
|
||||
# @liquid_syntax
|
||||
# {% for variable in array %}
|
||||
# expression
|
||||
@@ -177,7 +177,6 @@ module Liquid
|
||||
case key
|
||||
when 'offset'
|
||||
@from = if expr == 'continue'
|
||||
Usage.increment('for_offset_continue')
|
||||
:continue
|
||||
else
|
||||
parse_expression(expr)
|
||||
|
||||
@@ -8,7 +8,7 @@ module Liquid
|
||||
# @liquid_summary
|
||||
# Renders a [snippet](/themes/architecture#snippets).
|
||||
# @liquid_description
|
||||
# Inside the snippet, you can access and alter variables that are [created](/api/liquid/tags#variable-tags) outside of the
|
||||
# Inside the snippet, you can access and alter variables that are [created](/api/liquid/tags/variable-tags) outside of the
|
||||
# snippet.
|
||||
# @liquid_syntax
|
||||
# {% include 'filename' %}
|
||||
@@ -16,7 +16,7 @@ module Liquid
|
||||
# @liquid_deprecated
|
||||
# Deprecated because the way that variables are handled reduces performance and makes code harder to both read and maintain.
|
||||
#
|
||||
# The `include` tag has been replaced by [`render`](/api/liquid/tags#render).
|
||||
# The `include` tag has been replaced by [`render`](/api/liquid/tags/render).
|
||||
class Include < Tag
|
||||
prepend Tag::Disableable
|
||||
|
||||
@@ -52,7 +52,7 @@ module Liquid
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
template_name = context.evaluate(@template_name_expr)
|
||||
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
||||
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name.is_a?(String)
|
||||
|
||||
partial = PartialCache.load(
|
||||
template_name,
|
||||
|
||||
@@ -12,8 +12,8 @@ module Liquid
|
||||
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
||||
# [snippets](/themes/architecture#snippets) included in the file.
|
||||
#
|
||||
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/api/liquid/tags#assign)
|
||||
# and [`capture`](/api/liquid/tags#capture). However, `increment` and [`decrement`](/api/liquid/tags#decrement) share
|
||||
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/api/liquid/tags/assign)
|
||||
# and [`capture`](/api/liquid/tags/capture). However, `increment` and [`decrement`](/api/liquid/tags/decrement) share
|
||||
# variables.
|
||||
# @liquid_syntax
|
||||
# {% increment variable_name %}
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# @liquid_public_docs
|
||||
# @liquid_type tag
|
||||
# @liquid_category syntax
|
||||
# @liquid_name inline_comment
|
||||
# @liquid_summary
|
||||
# Prevents an expression from being rendered or output.
|
||||
# @liquid_description
|
||||
# Any text inside an `inline_comment` tag won't be rendered or output.
|
||||
#
|
||||
# You can create multi-line inline comments. However, each line must begin with a `#`.
|
||||
# @liquid_syntax
|
||||
# {% # content %}
|
||||
# @liquid_syntax_keyword content The content of the comment.
|
||||
class InlineComment < Tag
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
@@ -8,19 +8,19 @@ module Liquid
|
||||
# @liquid_summary
|
||||
# Renders a [snippet](/themes/architecture#snippets) or [app block](/themes/architecture/sections/section-schema#render-app-blocks).
|
||||
# @liquid_description
|
||||
# Inside snippets and app blocks, you can't directly access variables that are [created](/api/liquid/tags#variable-tags) outside
|
||||
# of the snippet or app block. However, you can [specify variables as parameters](/api/liquid/tags#render-passing-variables-to-snippets)
|
||||
# Inside snippets and app blocks, you can't directly access variables that are [created](/api/liquid/tags/variable-tags) outside
|
||||
# of the snippet or app block. However, you can [specify variables as parameters](/api/liquid/tags/render#render-passing-variables-to-a-snippet)
|
||||
# to pass outside variables to snippets.
|
||||
#
|
||||
# While you can't directly access created variables, you can access global objects, as well as any objects that are
|
||||
# directly accessible outside the snippet or app block. For example, a snippet or app block inside the [product template](/themes/architecture/templates/product)
|
||||
# can access the [`product` object](/api/liquid/objects#product), and a snippet or app block inside a [section](/themes/architecture/sections)
|
||||
# can access the [`section` object](/api/liquid/objects#section).
|
||||
# can access the [`product` object](/api/liquid/objects/product), and a snippet or app block inside a [section](/themes/architecture/sections)
|
||||
# can access the [`section` object](/api/liquid/objects/section).
|
||||
#
|
||||
# Outside a snippet or app block, you can't access variables created inside the snippet or app block.
|
||||
#
|
||||
# > Note:
|
||||
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/api/liquid/tags#include)
|
||||
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/api/liquid/tags/include)
|
||||
# > inside the snippet.
|
||||
# @liquid_syntax
|
||||
# {% render 'filename' %}
|
||||
@@ -33,10 +33,6 @@ module Liquid
|
||||
|
||||
attr_reader :template_name_expr, :variable_name_expr, :attributes, :alias_name
|
||||
|
||||
def for_loop?
|
||||
@is_for_loop
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
@@ -57,6 +53,10 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def for_loop?
|
||||
@is_for_loop
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
render_tag(context, output)
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module Liquid
|
||||
# The `tablerow` tag must be wrapped in HTML `<table>` and `</table>` tags.
|
||||
#
|
||||
# > Tip:
|
||||
# > Every `tablerow` loop has an associated [`tablerowloop` object](/api/liquid/objects#tablerowloop) with information about the loop.
|
||||
# > Every `tablerow` loop has an associated [`tablerowloop` object](/api/liquid/objects/tablerowloop) with information about the loop.
|
||||
# @liquid_syntax
|
||||
# {% tablerow variable in array %}
|
||||
# expression
|
||||
@@ -51,7 +51,7 @@ module Liquid
|
||||
collection = Utils.slice_collection(collection, from, to)
|
||||
length = collection.length
|
||||
|
||||
cols = context.evaluate(@attributes['cols']).to_i
|
||||
cols = @attributes.key?('cols') ? context.evaluate(@attributes['cols']).to_i : length
|
||||
|
||||
output << "<tr class=\"row1\">\n"
|
||||
context.stack do
|
||||
|
||||
@@ -11,7 +11,7 @@ module Liquid
|
||||
# Renders an expression unless a specific condition is `true`.
|
||||
# @liquid_description
|
||||
# > Tip:
|
||||
# > Similar to the [`if` tag](/api/liquid/tags#if), you can use `elsif` to add more conditions to an `unless` tag.
|
||||
# > Similar to the [`if` tag](/api/liquid/tags/if), you can use `elsif` to add more conditions to an `unless` tag.
|
||||
# @liquid_syntax
|
||||
# {% unless condition %}
|
||||
# expression
|
||||
|
||||
@@ -8,11 +8,15 @@ module Liquid
|
||||
@source = source.to_s.to_str
|
||||
@line_number = line_number || (line_numbers ? 1 : nil)
|
||||
@for_liquid_tag = for_liquid_tag
|
||||
@offset = 0
|
||||
@tokens = tokenize
|
||||
end
|
||||
|
||||
def shift
|
||||
(token = @tokens.shift) || return
|
||||
token = @tokens[@offset]
|
||||
return nil unless token
|
||||
|
||||
@offset += 1
|
||||
|
||||
if @line_number
|
||||
@line_number += @for_liquid_tag ? 1 : token.count("\n")
|
||||
@@ -31,7 +35,9 @@ module Liquid
|
||||
tokens = @source.split(TemplateParser)
|
||||
|
||||
# removes the rogue empty element at the beginning of the array
|
||||
tokens.shift if tokens[0]&.empty?
|
||||
if tokens[0]&.empty?
|
||||
@offset += 1
|
||||
end
|
||||
|
||||
tokens
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@ class BlankTest < Minitest::Test
|
||||
|
||||
def test_new_tags_are_not_blank_by_default
|
||||
with_custom_tag('foobar', FoobarTag) do
|
||||
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
||||
assert_equal(" " * N, Liquid::Template.parse(wrap_in_for("{% foobar %}")).render!)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_raises(Liquid::ArgumentError) do
|
||||
@filters.slice('foobar', 0, "")
|
||||
end
|
||||
assert_equal("", @filters.slice("foobar", 0, -(1 << 64)))
|
||||
assert_equal("foobar", @filters.slice("foobar", 0, 1 << 63))
|
||||
assert_equal("", @filters.slice("foobar", 1 << 63, 6))
|
||||
assert_equal("", @filters.slice("foobar", -(1 << 63), 6))
|
||||
end
|
||||
|
||||
def test_slice_on_arrays
|
||||
@@ -123,6 +127,10 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal(%w(r), @filters.slice(input, -1))
|
||||
assert_equal(%w(), @filters.slice(input, 100, 10))
|
||||
assert_equal(%w(), @filters.slice(input, -100, 10))
|
||||
assert_equal([], @filters.slice(input, 0, -(1 << 64)))
|
||||
assert_equal(input, @filters.slice(input, 0, 1 << 63))
|
||||
assert_equal([], @filters.slice(input, 1 << 63, 6))
|
||||
assert_equal([], @filters.slice(input, -(1 << 63), 6))
|
||||
end
|
||||
|
||||
def test_truncate
|
||||
@@ -132,6 +140,8 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal('1234567890', @filters.truncate('1234567890'))
|
||||
assert_equal("测试...", @filters.truncate("测试测试测试测试", 5))
|
||||
assert_equal('12341', @filters.truncate("1234567890", 5, 1))
|
||||
assert_equal("foobar", @filters.truncate("foobar", 1 << 63))
|
||||
assert_equal("...", @filters.truncate("foobar", -(1 << 63)))
|
||||
end
|
||||
|
||||
def test_split
|
||||
@@ -227,10 +237,8 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal('one two three...', @filters.truncatewords("one two\tthree\nfour", 3))
|
||||
assert_equal('one two...', @filters.truncatewords("one two three four", 2))
|
||||
assert_equal('one...', @filters.truncatewords("one two three four", 0))
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
@filters.truncatewords("one two three four", 1 << 31)
|
||||
end
|
||||
assert_equal("Liquid error: integer #{1 << 31} too big for truncatewords", exception.message)
|
||||
assert_equal('one two three four', @filters.truncatewords("one two three four", 1 << 31))
|
||||
assert_equal('one...', @filters.truncatewords("one two three four", -(1 << 32)))
|
||||
end
|
||||
|
||||
def test_strip_html
|
||||
@@ -469,8 +477,8 @@ class StandardFiltersTest < Minitest::Test
|
||||
def test_map_over_proc
|
||||
drop = TestDrop.new(value: "testfoo")
|
||||
p = proc { drop }
|
||||
templ = '{{ procs | map: "value" }}'
|
||||
assert_template_result("testfoo", templ, { "procs" => [p] })
|
||||
output = Liquid::Template.parse('{{ procs | map: "value" }}').render!({ "procs" => [p] })
|
||||
assert_equal("testfoo", output)
|
||||
end
|
||||
|
||||
def test_map_over_drops_returning_procs
|
||||
@@ -482,12 +490,13 @@ class StandardFiltersTest < Minitest::Test
|
||||
"proc" => -> { "bar" },
|
||||
},
|
||||
]
|
||||
templ = '{{ drops | map: "proc" }}'
|
||||
assert_template_result("foobar", templ, { "drops" => drops })
|
||||
output = Liquid::Template.parse('{{ drops | map: "proc" }}').render!({ "drops" => drops })
|
||||
assert_equal("foobar", output)
|
||||
end
|
||||
|
||||
def test_map_works_on_enumerables
|
||||
assert_template_result("123", '{{ foo | map: "foo" }}', { "foo" => TestEnumerable.new })
|
||||
output = Liquid::Template.parse('{{ foo | map: "foo" }}').render!({ "foo" => TestEnumerable.new })
|
||||
assert_equal("123", output)
|
||||
end
|
||||
|
||||
def test_map_returns_empty_on_2d_input_array
|
||||
|
||||
@@ -9,8 +9,8 @@ class BreakTagTest < Minitest::Test
|
||||
# block
|
||||
def test_break_with_no_block
|
||||
assigns = { 'i' => 1 }
|
||||
markup = '{% break %}'
|
||||
expected = ''
|
||||
markup = 'before{% break %}after'
|
||||
expected = 'before'
|
||||
|
||||
assert_template_result(expected, markup, assigns)
|
||||
end
|
||||
|
||||
@@ -263,6 +263,19 @@ HERE
|
||||
assert_template_result(expected, markup, assigns)
|
||||
end
|
||||
|
||||
def test_for_with_break_after_nested_loop
|
||||
source = <<~LIQUID.chomp
|
||||
{% for i in (1..2) -%}
|
||||
{% for j in (1..2) -%}
|
||||
{{ i }}-{{ j }},
|
||||
{%- endfor -%}
|
||||
{% break -%}
|
||||
{% endfor -%}
|
||||
after
|
||||
LIQUID
|
||||
assert_template_result("1-1,1-2,after", source)
|
||||
end
|
||||
|
||||
def test_for_with_continue
|
||||
assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
|
||||
|
||||
@@ -437,30 +450,4 @@ HERE
|
||||
|
||||
assert(context.registers[:for_stack].empty?)
|
||||
end
|
||||
|
||||
def test_instrument_for_offset_continue
|
||||
assert_usage_increment('for_offset_continue') do
|
||||
Template.parse('{% for item in items offset:continue %}{{item}}{% endfor %}')
|
||||
end
|
||||
|
||||
assert_usage_increment('for_offset_continue', times: 0) do
|
||||
Template.parse('{% for item in items offset:2 %}{{item}}{% endfor %}')
|
||||
end
|
||||
end
|
||||
|
||||
def test_instrument_forloop_drop_name
|
||||
assigns = { 'items' => [1, 2, 3, 4, 5] }
|
||||
|
||||
assert_usage_increment('forloop_drop_name', times: 5) do
|
||||
Template.parse('{% for item in items %}{{forloop.name}}{% endfor %}').render!(assigns)
|
||||
end
|
||||
|
||||
assert_usage_increment('forloop_drop_name', times: 0) do
|
||||
Template.parse('{% for item in items %}{{forloop.index}}{% endfor %}').render!(assigns)
|
||||
end
|
||||
|
||||
assert_usage_increment('forloop_drop_name', times: 0) do
|
||||
Template.parse('{% for item in items %}{{item}}{% endfor %}').render!(assigns)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,44 +3,13 @@
|
||||
require 'test_helper'
|
||||
|
||||
class TestFileSystem
|
||||
PARTIALS = {
|
||||
"nested_template" => "{% include 'header' %} {% include 'body' %} {% include 'footer' %}",
|
||||
"body" => "body {% include 'body_detail' %}",
|
||||
}
|
||||
|
||||
def read_template_file(template_path)
|
||||
case template_path
|
||||
when "product"
|
||||
"Product: {{ product.title }} "
|
||||
|
||||
when "product_alias"
|
||||
"Product: {{ product.title }} "
|
||||
|
||||
when "locale_variables"
|
||||
"Locale: {{echo1}} {{echo2}}"
|
||||
|
||||
when "variant"
|
||||
"Variant: {{ variant.title }}"
|
||||
|
||||
when "nested_template"
|
||||
"{% include 'header' %} {% include 'body' %} {% include 'footer' %}"
|
||||
|
||||
when "body"
|
||||
"body {% include 'body_detail' %}"
|
||||
|
||||
when "nested_product_template"
|
||||
"Product: {{ nested_product_template.title }} {%include 'details'%} "
|
||||
|
||||
when "recursively_nested_template"
|
||||
"-{% include 'recursively_nested_template' %}"
|
||||
|
||||
when "pick_a_source"
|
||||
"from TestFileSystem"
|
||||
|
||||
when 'assignments'
|
||||
"{% assign foo = 'bar' %}"
|
||||
|
||||
when 'break'
|
||||
"{% break %}"
|
||||
|
||||
else
|
||||
template_path
|
||||
end
|
||||
PARTIALS[template_path] || template_path
|
||||
end
|
||||
end
|
||||
|
||||
@@ -81,7 +50,11 @@ class IncludeTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def setup
|
||||
Liquid::Template.file_system = TestFileSystem.new
|
||||
@default_file_system = Liquid::Template.file_system
|
||||
end
|
||||
|
||||
def teardown
|
||||
Liquid::Template.file_system = @default_file_system
|
||||
end
|
||||
|
||||
def test_include_tag_looks_for_file_system_in_registers_first
|
||||
@@ -92,63 +65,85 @@ class IncludeTagTest < Minitest::Test
|
||||
def test_include_tag_with
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% include 'product' with products[0] %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
end
|
||||
|
||||
def test_include_tag_with_alias
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% include 'product_alias' with products[0] as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product_alias" => "Product: {{ product.title }} " })
|
||||
end
|
||||
|
||||
def test_include_tag_for_alias
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% include 'product_alias' for products as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product_alias" => "Product: {{ product.title }} " })
|
||||
end
|
||||
|
||||
def test_include_tag_with_default_name
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% include 'product' %}", { "product" => { 'title' => 'Draft 151cm' } })
|
||||
"{% include 'product' %}", { "product" => { 'title' => 'Draft 151cm' } },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
end
|
||||
|
||||
def test_include_tag_for
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% include 'product' for products %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
end
|
||||
|
||||
def test_include_tag_with_local_variables
|
||||
assert_template_result("Locale: test123 ", "{% include 'locale_variables' echo1: 'test123' %}")
|
||||
assert_template_result("Locale: test123 ", "{% include 'locale_variables' echo1: 'test123' %}",
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" })
|
||||
end
|
||||
|
||||
def test_include_tag_with_multiple_local_variables
|
||||
assert_template_result("Locale: test123 test321",
|
||||
"{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}")
|
||||
"{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}",
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" })
|
||||
end
|
||||
|
||||
def test_include_tag_with_multiple_local_variables_from_context
|
||||
assert_template_result("Locale: test123 test321",
|
||||
"{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}",
|
||||
{ 'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321' } })
|
||||
{ 'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321' } },
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" })
|
||||
end
|
||||
|
||||
def test_included_templates_assigns_variables
|
||||
assert_template_result("bar", "{% include 'assignments' %}{{ foo }}")
|
||||
assert_template_result("bar", "{% include 'assignments' %}{{ foo }}",
|
||||
partials: { 'assignments' => "{% assign foo = 'bar' %}" })
|
||||
end
|
||||
|
||||
def test_nested_include_tag
|
||||
assert_template_result("body body_detail", "{% include 'body' %}")
|
||||
partials = { "body" => "body {% include 'body_detail' %}", "body_detail" => "body_detail" }
|
||||
assert_template_result("body body_detail", "{% include 'body' %}", partials: partials)
|
||||
|
||||
assert_template_result("header body body_detail footer", "{% include 'nested_template' %}")
|
||||
partials = partials.merge({
|
||||
"nested_template" => "{% include 'header' %} {% include 'body' %} {% include 'footer' %}",
|
||||
"header" => "header",
|
||||
"footer" => "footer",
|
||||
})
|
||||
assert_template_result("header body body_detail footer", "{% include 'nested_template' %}", partials: partials)
|
||||
end
|
||||
|
||||
def test_nested_include_with_variable
|
||||
partials = {
|
||||
"nested_product_template" => "Product: {{ nested_product_template.title }} {%include 'details'%} ",
|
||||
"details" => "details",
|
||||
}
|
||||
|
||||
assert_template_result("Product: Draft 151cm details ",
|
||||
"{% include 'nested_product_template' with product %}", { "product" => { "title" => 'Draft 151cm' } })
|
||||
"{% include 'nested_product_template' with product %}", { "product" => { "title" => 'Draft 151cm' } },
|
||||
partials: partials)
|
||||
|
||||
assert_template_result("Product: Draft 151cm details Product: Element 155cm details ",
|
||||
"{% include 'nested_product_template' for products %}", { "products" => [{ "title" => 'Draft 151cm' }, { "title" => 'Element 155cm' }] })
|
||||
"{% include 'nested_product_template' for products %}", { "products" => [{ "title" => 'Draft 151cm' }, { "title" => 'Element 155cm' }] },
|
||||
partials: partials)
|
||||
end
|
||||
|
||||
def test_recursively_included_template_does_not_produce_endless_loop
|
||||
@@ -166,11 +161,15 @@ class IncludeTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_dynamically_choosen_template
|
||||
assert_template_result("Test123", "{% include template %}", { "template" => 'Test123' })
|
||||
assert_template_result("Test321", "{% include template %}", { "template" => 'Test321' })
|
||||
assert_template_result("Test123", "{% include template %}", { "template" => 'Test123' },
|
||||
partials: { "Test123" => "Test123" })
|
||||
|
||||
assert_template_result("Test321", "{% include template %}", { "template" => 'Test321' },
|
||||
partials: { "Test321" => "Test321" })
|
||||
|
||||
assert_template_result("Product: Draft 151cm ", "{% include template for product %}",
|
||||
{ "template" => 'product', 'product' => { 'title' => 'Draft 151cm' } })
|
||||
{ "template" => 'product', 'product' => { 'title' => 'Draft 151cm' } },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
end
|
||||
|
||||
def test_include_tag_caches_second_read_of_same_partial
|
||||
@@ -192,7 +191,8 @@ class IncludeTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_include_tag_within_if_statement
|
||||
assert_template_result("foo_if_true", "{% if true %}{% include 'foo_if_true' %}{% endif %}")
|
||||
assert_template_result("foo_if_true", "{% if true %}{% include 'foo_if_true' %}{% endif %}",
|
||||
partials: { "foo_if_true" => "foo_if_true" })
|
||||
end
|
||||
|
||||
def test_custom_include_tag
|
||||
@@ -226,6 +226,7 @@ class IncludeTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_passing_options_to_included_templates
|
||||
Liquid::Template.file_system = TestFileSystem.new
|
||||
assert_raises(Liquid::SyntaxError) do
|
||||
Template.parse("{% include template %}", error_mode: :strict).render!("template" => '{{ "X" || downcase }}')
|
||||
end
|
||||
@@ -241,27 +242,35 @@ class IncludeTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_render_raise_argument_error_when_template_is_undefined
|
||||
assert_raises(Liquid::ArgumentError) do
|
||||
template = Liquid::Template.parse('{% include undefined_variable %}')
|
||||
template.render!
|
||||
end
|
||||
assert_raises(Liquid::ArgumentError) do
|
||||
template = Liquid::Template.parse('{% include nil %}')
|
||||
template.render!
|
||||
end
|
||||
assert_template_result("Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include undefined_variable %}", render_errors: true)
|
||||
|
||||
assert_template_result("Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include nil %}", render_errors: true)
|
||||
end
|
||||
|
||||
def test_render_raise_argument_error_when_template_is_not_a_string
|
||||
assert_template_result("Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include 123 %}", render_errors: true)
|
||||
end
|
||||
|
||||
def test_including_via_variable_value
|
||||
assert_template_result("from TestFileSystem", "{% assign page = 'pick_a_source' %}{% include page %}")
|
||||
assert_template_result("from TestFileSystem", "{% assign page = 'pick_a_source' %}{% include page %}",
|
||||
partials: { "pick_a_source" => "from TestFileSystem" })
|
||||
|
||||
partials = { "product" => "Product: {{ product.title }} " }
|
||||
|
||||
assert_template_result("Product: Draft 151cm ", "{% assign page = 'product' %}{% include page %}",
|
||||
{ "product" => { 'title' => 'Draft 151cm' } })
|
||||
{ "product" => { 'title' => 'Draft 151cm' } },
|
||||
partials: partials)
|
||||
|
||||
assert_template_result("Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}",
|
||||
{ "foo" => { 'title' => 'Draft 151cm' } })
|
||||
{ "foo" => { 'title' => 'Draft 151cm' } },
|
||||
partials: partials)
|
||||
end
|
||||
|
||||
def test_including_with_strict_variables
|
||||
Liquid::Template.file_system = StubFileSystem.new({ "simple" => "simple" })
|
||||
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
|
||||
template.render(nil, strict_variables: true)
|
||||
|
||||
@@ -270,6 +279,7 @@ class IncludeTagTest < Minitest::Test
|
||||
|
||||
def test_break_through_include
|
||||
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}")
|
||||
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}")
|
||||
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}",
|
||||
partials: { 'break' => "{% break %}" })
|
||||
end
|
||||
end # IncludeTagTest
|
||||
|
||||
@@ -6,20 +6,21 @@ class IncrementTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_inc
|
||||
assert_template_result('0', '{%increment port %}', {})
|
||||
assert_template_result('0 1', '{%increment port %} {%increment port%}', {})
|
||||
assert_template_result('0 1', '{%increment port %} {{ port }}')
|
||||
assert_template_result(' 0 1 2', '{{port}} {%increment port %} {%increment port%} {{port}}')
|
||||
assert_template_result('0 0 1 2 1',
|
||||
'{%increment port %} {%increment starboard%} ' \
|
||||
'{%increment port %} {%increment port%} ' \
|
||||
'{%increment starboard %}', {})
|
||||
'{%increment starboard %}')
|
||||
end
|
||||
|
||||
def test_dec
|
||||
assert_template_result('9', '{%decrement port %}', { 'port' => 10 })
|
||||
assert_template_result('-1 -2', '{%decrement port %} {%decrement port%}', {})
|
||||
assert_template_result('1 5 2 2 5',
|
||||
assert_template_result('-1 -1', '{%decrement port %} {{ port }}', { 'port' => 10 })
|
||||
assert_template_result(' -1 -2 -2', '{{port}} {%decrement port %} {%decrement port%} {{port}}')
|
||||
assert_template_result('0 1 2 0 3 1 1 3',
|
||||
'{%increment starboard %} {%increment starboard%} {%increment starboard%} ' \
|
||||
'{%increment port %} {%increment starboard%} ' \
|
||||
'{%increment port %} {%decrement port%} ' \
|
||||
'{%decrement starboard %}', { 'port' => 1, 'starboard' => 5 })
|
||||
'{%decrement starboard %}')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,53 +6,52 @@ class RenderTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_render_with_no_arguments
|
||||
Liquid::Template.file_system = StubFileSystem.new('source' => 'rendered content')
|
||||
assert_template_result('rendered content', '{% render "source" %}')
|
||||
assert_template_result('rendered content', '{% render "source" %}',
|
||||
partials: { 'source' => 'rendered content' })
|
||||
end
|
||||
|
||||
def test_render_tag_looks_for_file_system_in_registers_first
|
||||
file_system = StubFileSystem.new('pick_a_source' => 'from register file system')
|
||||
assert_equal('from register file system',
|
||||
Template.parse('{% render "pick_a_source" %}').render!({}, registers: { file_system: file_system }))
|
||||
assert_template_result('from register file system', '{% render "pick_a_source" %}',
|
||||
partials: { 'pick_a_source' => 'from register file system' })
|
||||
end
|
||||
|
||||
def test_render_passes_named_arguments_into_inner_scope
|
||||
Liquid::Template.file_system = StubFileSystem.new('product' => '{{ inner_product.title }}')
|
||||
assert_template_result('My Product', '{% render "product", inner_product: outer_product %}',
|
||||
{ 'outer_product' => { 'title' => 'My Product' } })
|
||||
{ 'outer_product' => { 'title' => 'My Product' } },
|
||||
partials: { 'product' => '{{ inner_product.title }}' })
|
||||
end
|
||||
|
||||
def test_render_accepts_literals_as_arguments
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ price }}')
|
||||
assert_template_result('123', '{% render "snippet", price: 123 %}')
|
||||
assert_template_result('123', '{% render "snippet", price: 123 %}',
|
||||
partials: { 'snippet' => '{{ price }}' })
|
||||
end
|
||||
|
||||
def test_render_accepts_multiple_named_arguments
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ one }} {{ two }}')
|
||||
assert_template_result('1 2', '{% render "snippet", one: 1, two: 2 %}')
|
||||
assert_template_result('1 2', '{% render "snippet", one: 1, two: 2 %}',
|
||||
partials: { 'snippet' => '{{ one }} {{ two }}' })
|
||||
end
|
||||
|
||||
def test_render_does_not_inherit_parent_scope_variables
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ outer_variable }}')
|
||||
assert_template_result('', '{% assign outer_variable = "should not be visible" %}{% render "snippet" %}')
|
||||
assert_template_result('', '{% assign outer_variable = "should not be visible" %}{% render "snippet" %}',
|
||||
partials: { 'snippet' => '{{ outer_variable }}' })
|
||||
end
|
||||
|
||||
def test_render_does_not_inherit_variable_with_same_name_as_snippet
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ snippet }}')
|
||||
assert_template_result('', "{% assign snippet = 'should not be visible' %}{% render 'snippet' %}")
|
||||
assert_template_result('', "{% assign snippet = 'should not be visible' %}{% render 'snippet' %}",
|
||||
partials: { 'snippet' => '{{ snippet }}' })
|
||||
end
|
||||
|
||||
def test_render_does_not_mutate_parent_scope
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{% assign inner = 1 %}')
|
||||
assert_template_result('', "{% render 'snippet' %}{{ inner }}")
|
||||
assert_template_result('', "{% render 'snippet' %}{{ inner }}",
|
||||
partials: { 'snippet' => '{% assign inner = 1 %}' })
|
||||
end
|
||||
|
||||
def test_nested_render_tag
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'one' => "one {% render 'two' %}",
|
||||
'two' => 'two'
|
||||
)
|
||||
assert_template_result('one two', "{% render 'one' %}")
|
||||
assert_template_result('one two', "{% render 'one' %}",
|
||||
partials: {
|
||||
'one' => "one {% render 'two' %}",
|
||||
'two' => 'two',
|
||||
})
|
||||
end
|
||||
|
||||
def test_recursively_rendered_template_does_not_produce_endless_loop
|
||||
@@ -73,11 +72,7 @@ class RenderTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_dynamically_choosen_templates_are_not_allowed
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'should not be rendered')
|
||||
|
||||
assert_raises(Liquid::SyntaxError) do
|
||||
Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}")
|
||||
end
|
||||
assert_syntax_error("{% assign name = 'snippet' %}{% render name %}")
|
||||
end
|
||||
|
||||
def test_include_tag_caches_second_read_of_same_partial
|
||||
@@ -101,36 +96,36 @@ class RenderTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_render_tag_within_if_statement
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'my message')
|
||||
assert_template_result('my message', '{% if true %}{% render "snippet" %}{% endif %}')
|
||||
assert_template_result('my message', '{% if true %}{% render "snippet" %}{% endif %}',
|
||||
partials: { 'snippet' => 'my message' })
|
||||
end
|
||||
|
||||
def test_break_through_render
|
||||
Liquid::Template.file_system = StubFileSystem.new('break' => '{% break %}')
|
||||
assert_template_result('1', '{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}')
|
||||
assert_template_result('112233', '{% for i in (1..3) %}{{ i }}{% render "break" %}{{ i }}{% endfor %}')
|
||||
options = { partials: { 'break' => '{% break %}' } }
|
||||
assert_template_result('1', '{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}', **options)
|
||||
assert_template_result('112233', '{% for i in (1..3) %}{{ i }}{% render "break" %}{{ i }}{% endfor %}', **options)
|
||||
end
|
||||
|
||||
def test_increment_is_isolated_between_renders
|
||||
Liquid::Template.file_system = StubFileSystem.new('incr' => '{% increment %}')
|
||||
assert_template_result('010', '{% increment %}{% increment %}{% render "incr" %}')
|
||||
assert_template_result('010', '{% increment %}{% increment %}{% render "incr" %}',
|
||||
partials: { 'incr' => '{% increment %}' })
|
||||
end
|
||||
|
||||
def test_decrement_is_isolated_between_renders
|
||||
Liquid::Template.file_system = StubFileSystem.new('decr' => '{% decrement %}')
|
||||
assert_template_result('-1-2-1', '{% decrement %}{% decrement %}{% render "decr" %}')
|
||||
assert_template_result('-1-2-1', '{% decrement %}{% decrement %}{% render "decr" %}',
|
||||
partials: { 'decr' => '{% decrement %}' })
|
||||
end
|
||||
|
||||
def test_includes_will_not_render_inside_render_tag
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'foo' => 'bar',
|
||||
'test_include' => '{% include "foo" %}'
|
||||
assert_template_result(
|
||||
'Liquid error (test_include line 1): include usage is not allowed in this context',
|
||||
'{% render "test_include" %}',
|
||||
render_errors: true,
|
||||
partials: {
|
||||
'foo' => 'bar',
|
||||
'test_include' => '{% include "foo" %}',
|
||||
}
|
||||
)
|
||||
|
||||
exc = assert_raises(Liquid::DisabledError) do
|
||||
Liquid::Template.parse('{% render "test_include" %}').render!
|
||||
end
|
||||
assert_equal('Liquid error: include usage is not allowed in this context', exc.message)
|
||||
end
|
||||
|
||||
def test_includes_will_not_render_inside_nested_sibling_tags
|
||||
@@ -148,74 +143,67 @@ class RenderTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_render_tag_with
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% render 'product' with products[0] %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
end
|
||||
|
||||
def test_render_tag_with_alias
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% render 'product_alias' with products[0] as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
end
|
||||
|
||||
def test_render_tag_for_alias
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% render 'product_alias' for products as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
end
|
||||
|
||||
def test_render_tag_for
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% render 'product' for products %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
end
|
||||
|
||||
def test_render_tag_forloop
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'product' => "Product: {{ product.title }} {% if forloop.first %}first{% endif %} {% if forloop.last %}last{% endif %} index:{{ forloop.index }} ",
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm first index:1 Product: Element 155cm last index:2 ",
|
||||
"{% render 'product' for products %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} {% if forloop.first %}first{% endif %} {% if forloop.last %}last{% endif %} index:{{ forloop.index }} ",
|
||||
})
|
||||
end
|
||||
|
||||
def test_render_tag_for_drop
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'loop' => "{{ value.foo }}",
|
||||
)
|
||||
|
||||
assert_template_result("123",
|
||||
"{% render 'loop' for loop as value %}", { "loop" => TestEnumerable.new })
|
||||
"{% render 'loop' for loop as value %}", { "loop" => TestEnumerable.new },
|
||||
partials: {
|
||||
'loop' => "{{ value.foo }}",
|
||||
})
|
||||
end
|
||||
|
||||
def test_render_tag_with_drop
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'loop' => "{{ value }}",
|
||||
)
|
||||
|
||||
assert_template_result("TestEnumerable",
|
||||
"{% render 'loop' with loop as value %}", { "loop" => TestEnumerable.new })
|
||||
"{% render 'loop' with loop as value %}", { "loop" => TestEnumerable.new },
|
||||
partials: {
|
||||
'loop' => "{{ value }}",
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,6 +36,8 @@ class StandardTagTest < Minitest::Test
|
||||
assert_template_result('', '{%comment%}{% endif %}{%endcomment%}')
|
||||
assert_template_result('', '{% comment %}{% endwhatever %}{% endcomment %}')
|
||||
assert_template_result('', '{% comment %}{% raw %} {{%%%%}} }} { {% endcomment %} {% comment {% endraw %} {% endcomment %}')
|
||||
assert_template_result('', '{% comment %}{% " %}{% endcomment %}')
|
||||
assert_template_result('', '{% comment %}{%%}{% endcomment %}')
|
||||
|
||||
assert_template_result('foobar', 'foo{%comment%}comment{%endcomment%}bar')
|
||||
assert_template_result('foobar', 'foo{% comment %}comment{% endcomment %}bar')
|
||||
|
||||
@@ -65,4 +65,70 @@ class TableRowTest < Minitest::Test
|
||||
"{% tablerow char in characters cols:3 %}I WILL NOT BE OUTPUT{% endtablerow %}",
|
||||
{ 'characters' => '' })
|
||||
end
|
||||
|
||||
def test_cols_nil_constant_same_as_evaluated_nil_expression
|
||||
expect = "<tr class=\"row1\">\n" \
|
||||
"<td class=\"col1\">false</td>" \
|
||||
"<td class=\"col2\">false</td>" \
|
||||
"</tr>\n"
|
||||
|
||||
assert_template_result(expect,
|
||||
"{% tablerow i in (1..2) cols:nil %}{{ tablerowloop.col_last }}{% endtablerow %}")
|
||||
|
||||
assert_template_result(expect,
|
||||
"{% tablerow i in (1..2) cols:var %}{{ tablerowloop.col_last }}{% endtablerow %}",
|
||||
{ "var" => nil })
|
||||
end
|
||||
|
||||
def test_tablerow_loop_drop_attributes
|
||||
template = <<~LIQUID.chomp
|
||||
{% tablerow i in (1...2) %}
|
||||
col: {{ tablerowloop.col }}
|
||||
col0: {{ tablerowloop.col0 }}
|
||||
col_first: {{ tablerowloop.col_first }}
|
||||
col_last: {{ tablerowloop.col_last }}
|
||||
first: {{ tablerowloop.first }}
|
||||
index: {{ tablerowloop.index }}
|
||||
index0: {{ tablerowloop.index0 }}
|
||||
last: {{ tablerowloop.last }}
|
||||
length: {{ tablerowloop.length }}
|
||||
rindex: {{ tablerowloop.rindex }}
|
||||
rindex0: {{ tablerowloop.rindex0 }}
|
||||
row: {{ tablerowloop.row }}
|
||||
{% endtablerow %}
|
||||
LIQUID
|
||||
|
||||
expected_output = <<~OUTPUT
|
||||
<tr class="row1">
|
||||
<td class="col1">
|
||||
col: 1
|
||||
col0: 0
|
||||
col_first: true
|
||||
col_last: false
|
||||
first: true
|
||||
index: 1
|
||||
index0: 0
|
||||
last: false
|
||||
length: 2
|
||||
rindex: 2
|
||||
rindex0: 1
|
||||
row: 1
|
||||
</td><td class="col2">
|
||||
col: 2
|
||||
col0: 1
|
||||
col_first: false
|
||||
col_last: true
|
||||
first: false
|
||||
index: 2
|
||||
index0: 1
|
||||
last: true
|
||||
length: 2
|
||||
rindex: 1
|
||||
rindex0: 0
|
||||
row: 1
|
||||
</td></tr>
|
||||
OUTPUT
|
||||
|
||||
assert_template_result(expected_output, template)
|
||||
end
|
||||
end
|
||||
|
||||
+6
-2
@@ -42,9 +42,9 @@ module Minitest
|
||||
message: nil, partials: nil, error_mode: nil, render_errors: false
|
||||
)
|
||||
template = Liquid::Template.parse(template, line_numbers: true, error_mode: error_mode&.to_sym)
|
||||
file_system = StubFileSystem.new(partials) if partials
|
||||
file_system = StubFileSystem.new(partials || {})
|
||||
registers = Liquid::Registers.new(file_system: file_system)
|
||||
context = Liquid::Context.build(environments: assigns, rethrow_errors: !render_errors, registers: registers)
|
||||
context = Liquid::Context.build(static_environments: assigns, rethrow_errors: !render_errors, registers: registers)
|
||||
output = template.render(context)
|
||||
assert_equal(expected, output, message)
|
||||
end
|
||||
@@ -56,6 +56,10 @@ module Minitest
|
||||
assert_match(match, exception.message)
|
||||
end
|
||||
|
||||
def assert_syntax_error(template, error_mode: nil)
|
||||
assert_match_syntax_error("", template, error_mode: error_mode)
|
||||
end
|
||||
|
||||
def assert_usage_increment(name, times: 1)
|
||||
old_method = Liquid::Usage.method(:increment)
|
||||
calls = 0
|
||||
|
||||
Reference in New Issue
Block a user