shopify_liquid_test_helper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 78ab883c518d4cb5d8e2bee3c6cb8c4f65781724f3d5e7e093f55b5e108e041c
4
+ data.tar.gz: 1d1d92e1d5e717b655152a320bcbb69cc72eebc111756d0e4f969af1a97c5e45
5
+ SHA512:
6
+ metadata.gz: 6559b4229c6fa3ef893499b7bc4d397d9dc270d840b1b33e9871004b84549857e1e646d272f0c2a9719d0db71f324d5b4eb06940702b126f49f8f9bf1992b3c3
7
+ data.tar.gz: a1aa6a1ed00f58fc7648aec4e625417ad59f2fe9fefd64f93c05535cda798af44d1eeb599439f7cc98ab27d2b89eaa8c2216f04811f4ced877e922aec4101053
data/README.md ADDED
@@ -0,0 +1,215 @@
1
+ # ShopifyLiquidTestHelper
2
+
3
+ ShopifyLiquidTestHelper is a Ruby gem designed to assist in testing Shopify Liquid templates. It provides custom Liquid tags and helper methods that simulate Shopify-specific functionality, making it easier to test your Liquid templates in isolation.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'shopify_liquid_test_helper'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```
22
+ $ gem install shopify_liquid_test_helper
23
+ ```
24
+
25
+ ## Usage with RSpec
26
+
27
+ ### Setup
28
+
29
+ In your `spec_helper.rb` or at the beginning of your test file, require and configure the helper:
30
+
31
+ ```ruby
32
+ require 'shopify_liquid_test_helper'
33
+
34
+ RSpec.configure do |config|
35
+ config.before(:each) do
36
+ ShopifyLiquidTestHelper.register_custom_tags
37
+ end
38
+ end
39
+ ```
40
+
41
+ ### Testing Liquid Templates
42
+
43
+ Here are some examples of how you can use ShopifyLiquidTestHelper in your RSpec tests:
44
+
45
+ 1. Testing a simple render tag:
46
+
47
+ ```ruby
48
+ RSpec.describe "Product template" do
49
+ before do
50
+ ShopifyLiquidTestHelper.register_snippet('product_title', '<h1>{{ product.title }}</h1>')
51
+ end
52
+
53
+ it "renders the product title" do
54
+ template = "{% render 'product_title' %}"
55
+ assigns = { 'product' => { 'title' => 'Awesome T-Shirt' } }
56
+ result = ShopifyLiquidTestHelper.render_template(Liquid::Template.parse(template), assigns)
57
+ expect(result).to eq '<h1>Awesome T-Shirt</h1>'
58
+ end
59
+ end
60
+ ```
61
+
62
+ 2. Testing a for loop in a render tag:
63
+
64
+ ```ruby
65
+ RSpec.describe "Collection template" do
66
+ before do
67
+ ShopifyLiquidTestHelper.register_snippet('product_list', '{% for product in products %}{{ product.title }}{% endfor %}')
68
+ end
69
+
70
+ it "renders a list of product titles" do
71
+ template = "{% render 'product_list' for products %}"
72
+ assigns = { 'products' => [{ 'title' => 'Shirt' }, { 'title' => 'Pants' }] }
73
+ result = ShopifyLiquidTestHelper.render_template(Liquid::Template.parse(template), assigns)
74
+ expect(result).to eq 'ShirtPants'
75
+ end
76
+ end
77
+ ```
78
+
79
+ 3. Testing the capture tag:
80
+
81
+ ```ruby
82
+ RSpec.describe "Capture tag" do
83
+ it "captures content into a variable" do
84
+ template = "{% capture my_variable %}Hello, {{ name }}!{% endcapture %}{{ my_variable }}"
85
+ assigns = { 'name' => 'World' }
86
+ result = ShopifyLiquidTestHelper.render_template(Liquid::Template.parse(template), assigns)
87
+ expect(result).to eq 'Hello, World!'
88
+ end
89
+ end
90
+ ```
91
+
92
+ ### Loading Snippets from Directory
93
+
94
+ ShopifyLiquidTestHelper can load snippets from a specific directory. By default, it looks for snippets in a `snippets` directory relative to your current working directory. You can use this feature as follows:
95
+
96
+ 1. Create a `snippets` directory in your project:
97
+
98
+ ```
99
+ mkdir snippets
100
+ ```
101
+
102
+ 2. Add your Liquid snippet files to this directory. For example, create a file named `product_card.liquid`:
103
+
104
+ ```liquid
105
+ <!-- snippets/product_card.liquid -->
106
+ <div class="product-card">
107
+ <h2>{{ product.title }}</h2>
108
+ <p>Price: {{ product.price | money }}</p>
109
+ </div>
110
+ ```
111
+
112
+ 3. In your tests, you can now render this snippet:
113
+
114
+ ```ruby
115
+ RSpec.describe "Product listing" do
116
+ it "renders a product card" do
117
+ template = "{% render 'product_card' %}"
118
+ assigns = {
119
+ 'product' => {
120
+ 'title' => 'Awesome T-Shirt',
121
+ 'price' => 1999
122
+ }
123
+ }
124
+ result = ShopifyLiquidTestHelper.render_template(Liquid::Template.parse(template), assigns)
125
+ expect(result).to include('Awesome T-Shirt')
126
+ expect(result).to include('Price: $19.99')
127
+ end
128
+ end
129
+ ```
130
+
131
+ ShopifyLiquidTestHelper will automatically load the `product_card` snippet from the `snippets` directory when you use the `render` tag in your Liquid template.
132
+
133
+ ### Customizing the Snippets Directory
134
+
135
+ If your snippets are located in a different directory, you can specify the path when initializing ShopifyLiquidTestHelper:
136
+
137
+ ```ruby
138
+ ShopifyLiquidTestHelper.snippets_dir = 'path/to/your/snippets'
139
+ ```
140
+
141
+ This allows you to organize your snippets in a way that best fits your project structure.
142
+
143
+ ### Combining Registered and File-based Snippets
144
+
145
+ You can use both registered snippets (using `ShopifyLiquidTestHelper.register_snippet`) and file-based snippets in the same test suite. ShopifyLiquidTestHelper will first check for registered snippets, and if not found, it will look for a matching file in the snippets directory.
146
+
147
+ ```ruby
148
+ RSpec.describe "Mixed snippet sources" do
149
+ before do
150
+ ShopifyLiquidTestHelper.register_snippet('inline_snippet', 'This is an inline snippet')
151
+ end
152
+
153
+ it "renders both registered and file-based snippets" do
154
+ template = """
155
+ {% render 'inline_snippet' %}
156
+ {% render 'product_card' %}
157
+ """
158
+ assigns = {
159
+ 'product' => {
160
+ 'title' => 'Cool Product',
161
+ 'price' => 2499
162
+ }
163
+ }
164
+ result = ShopifyLiquidTestHelper.render_template(Liquid::Template.parse(template), assigns)
165
+ expect(result).to include('This is an inline snippet')
166
+ expect(result).to include('Cool Product')
167
+ expect(result).to include('Price: $24.99')
168
+ end
169
+ end
170
+ ```
171
+
172
+ ### Advanced Usage
173
+
174
+ For more complex scenarios, you can create helper methods in your specs to simplify template rendering:
175
+
176
+ ```ruby
177
+ def render_template(template, assigns = {})
178
+ Liquid::Template.parse(template).render(assigns)
179
+ end
180
+
181
+ RSpec.describe "Complex template" do
182
+ it "renders a complex structure" do
183
+ template = """
184
+ {% render 'header' %}
185
+ {% for product in collection.products %}
186
+ {% render 'product_card' %}
187
+ {% endfor %}
188
+ {% render 'footer' %}
189
+ """
190
+ assigns = {
191
+ 'collection' => {
192
+ 'products' => [
193
+ { 'title' => 'Product 1', 'price' => 19.99 },
194
+ { 'title' => 'Product 2', 'price' => 24.99 }
195
+ ]
196
+ }
197
+ }
198
+ result = render_template(template, assigns)
199
+ expect(result).to include('Product 1')
200
+ expect(result).to include('Product 2')
201
+ expect(result).to include('19.99')
202
+ expect(result).to include('24.99')
203
+ end
204
+ end
205
+ ```
206
+
207
+ This gem allows you to test your Shopify Liquid templates thoroughly, ensuring they render correctly with various inputs and scenarios.
208
+
209
+ ## Contributing
210
+
211
+ Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/shopify_liquid_test_helper.
212
+
213
+ ## License
214
+
215
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ module ShopifyLiquidTestHelper
2
+ class CaptureTag < Liquid::Block
3
+ def initialize(tag_name, markup, tokens)
4
+ super
5
+ @variable_name = markup.strip
6
+ end
7
+
8
+ def render(context)
9
+ result = super
10
+ context.scopes.last[@variable_name] = result
11
+ ''
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,107 @@
1
+ module ShopifyLiquidTestHelper
2
+ class RenderTag < Liquid::Tag
3
+ SYNTAX = /(#{Liquid::QuotedFragment}+)(\s+(?:with|for)\s+(#{Liquid::QuotedFragment}+))?(\s+as\s+(#{Liquid::QuotedFragment}+))?/o
4
+
5
+ def initialize(tag_name, markup, tokens)
6
+ super
7
+ parse_markup(markup)
8
+ @params = extract_params(markup)
9
+ end
10
+
11
+ def render(context)
12
+ snippet_name = resolve_snippet_name(context)
13
+ snippet = fetch_snippet(snippet_name)
14
+
15
+ temp_context = create_isolated_context(context)
16
+ variable = resolve_variable(context)
17
+
18
+ if @for_loop
19
+ render_for_loop(snippet, variable, temp_context)
20
+ else
21
+ render_with_variable(snippet, variable, temp_context)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def parse_markup(markup)
28
+ unless markup =~ SYNTAX
29
+ raise Liquid::SyntaxError,
30
+ "Syntax Error in 'render' - Valid syntax: render 'snippet' [with object|for array] [as alias]"
31
+ end
32
+
33
+ @snippet_name = ::Regexp.last_match(1)
34
+ @variable_name = ::Regexp.last_match(3)
35
+ @alias = ::Regexp.last_match(5)
36
+ @for_loop = ::Regexp.last_match(2) =~ /\s+for\s+/
37
+ end
38
+
39
+ def extract_params(markup)
40
+ params = {}
41
+ markup.scan(Liquid::TagAttributes) { |key, value| params[key] = value }
42
+ params
43
+ end
44
+
45
+ def resolve_snippet_name(context)
46
+ snippet_name = context[@snippet_name] || @snippet_name
47
+ snippet_name.gsub(/['"]/, '')
48
+ end
49
+
50
+ def fetch_snippet(snippet_name)
51
+ snippet = ShopifyLiquidTestHelper.get_snippet(snippet_name)
52
+ raise Liquid::SyntaxError, "Unknown snippet '#{snippet_name}'" unless snippet
53
+
54
+ snippet
55
+ end
56
+
57
+ def resolve_variable(context)
58
+ @variable_name ? context[@variable_name] : nil
59
+ end
60
+
61
+ def render_for_loop(snippet, array, context)
62
+ return unless array.respond_to?(:each)
63
+
64
+ array.each_with_index.map do |item, index|
65
+ item_context = create_item_context(context, item, index, array.size)
66
+ Liquid::Template.parse(snippet).render(item_context)
67
+ end.join
68
+ end
69
+
70
+ def create_item_context(context, item, index, array_size)
71
+ item_context = context.new_isolated_subcontext
72
+ item_context[@alias || 'item'] = item
73
+
74
+ item_context['forloop'] = {
75
+ 'first' => index.zero?,
76
+ 'index' => index + 1,
77
+ 'index0' => index,
78
+ 'last' => index == array_size - 1,
79
+ 'length' => array_size,
80
+ 'rindex' => array_size - index,
81
+ 'rindex0' => array_size - index - 1
82
+ }
83
+
84
+ @params.each do |key, value|
85
+ item_context[key] = context[value] || context.evaluate(value) || value
86
+ end
87
+
88
+ item_context
89
+ end
90
+
91
+ def render_with_variable(snippet, variable, context)
92
+ context[@alias || 'object'] = variable if @variable_name
93
+
94
+ @params.each do |key, value|
95
+ context[key] = context[value] || context.evaluate(value) || value
96
+ end
97
+
98
+ Liquid::Template.parse(snippet).render(context)
99
+ end
100
+
101
+ def create_isolated_context(context)
102
+ new_context = context.new_isolated_subcontext
103
+ @params.each { |key, value| new_context[key] = context[value] || context.evaluate(value) || value }
104
+ new_context
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,50 @@
1
+ require 'liquid'
2
+ require 'shopify_liquid_test_helper/render_tag'
3
+ require 'shopify_liquid_test_helper/capture_tag'
4
+
5
+ module ShopifyLiquidTestHelper
6
+ class << self
7
+ attr_accessor :snippets_dir
8
+
9
+ def parse_template(template_name)
10
+ Liquid::Template.parse(File.read(template_name))
11
+ end
12
+
13
+ def render_template(template, assigns)
14
+ template.render(assigns).strip
15
+ end
16
+
17
+ def register_custom_tags
18
+ Liquid::Template.register_tag('render', RenderTag)
19
+ Liquid::Template.register_tag('capture', CaptureTag)
20
+ end
21
+
22
+ def register_snippet(name, content)
23
+ snippets[name] = content
24
+ end
25
+
26
+ def get_snippet(name)
27
+ snippets[name] || load_snippet(name)
28
+ end
29
+
30
+ private
31
+
32
+ def snippets
33
+ @snippets ||= {}
34
+ end
35
+
36
+ def load_snippet(name)
37
+ snippet_path = File.join(snippets_dir || 'snippets', "#{name}.liquid")
38
+ if File.exist?(snippet_path)
39
+ snippet = File.read(snippet_path)
40
+ snippets[name] = snippet
41
+ else
42
+ puts "Snippet not found: #{snippet_path}"
43
+ nil
44
+ end
45
+ end
46
+ end
47
+
48
+ # デフォルトのsnippetsディレクトリを設定
49
+ @snippets_dir = 'snippets'
50
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ShopifyLiquidTestHelper do
4
+ before(:all) do
5
+ ShopifyLiquidTestHelper.register_custom_tags
6
+ ShopifyLiquidTestHelper.register_snippet('simple', 'Hello, {{ name }}!')
7
+ ShopifyLiquidTestHelper.register_snippet('for_loop', 'Item: {{ item }}')
8
+ ShopifyLiquidTestHelper.register_snippet('with_object', 'Name: {{ object.name }}')
9
+ end
10
+
11
+ def render(template, assigns = {})
12
+ Liquid::Template.parse(template).render(assigns)
13
+ end
14
+
15
+ describe 'render tag' do
16
+ it 'renders a simple snippet' do
17
+ template = "{% render 'simple', name: 'World' %}"
18
+ expect(render(template)).to eq 'Hello, World!'
19
+ end
20
+
21
+ it 'renders a snippet with a for loop' do
22
+ template = "{% render 'for_loop' for items as item %}"
23
+ assigns = { 'items' => %w[A B C] }
24
+ expect(render(template, assigns)).to eq 'Item: AItem: BItem: C'
25
+ end
26
+
27
+ it 'renders a snippet with an object' do
28
+ template = "{% render 'with_object' with user as object %}"
29
+ assigns = { 'user' => { 'name' => 'John' } }
30
+ expect(render(template, assigns)).to eq 'Name: John'
31
+ end
32
+
33
+ it 'does not allow access to variables outside the snippet' do
34
+ ShopifyLiquidTestHelper.register_snippet('isolated', '{{ outside_var }}')
35
+ template = "{% assign outside_var = 'Outside' %}{% render 'isolated' %}"
36
+ expect(render(template)).to eq ''
37
+ end
38
+
39
+ it 'provides all forloop variables in for loop rendering' do
40
+ ShopifyLiquidTestHelper.register_snippet('forloop_vars',
41
+ '{{ forloop.index }},{{ forloop.index0 }},{{ forloop.first }},{{ forloop.last }},{{ forloop.length }},{{ forloop.rindex }},{{ forloop.rindex0 }}|')
42
+ template = "{% render 'forloop_vars' for items as item %}"
43
+ assigns = { 'items' => %w[A B C] }
44
+ expect(render(template, assigns)).to eq '1,0,true,false,3,3,2|2,1,false,false,3,2,1|3,2,false,true,3,1,0|'
45
+ end
46
+
47
+ it 'allows using an alias for the rendered variable' do
48
+ template = "{% render 'simple' with 'Alias' as name %}"
49
+ expect(render(template)).to eq 'Hello, Alias!'
50
+ end
51
+
52
+ it 'does not pollute the outer scope' do
53
+ template = "{% render 'simple' with 'Inner' as name %}{{ name }}"
54
+ assigns = { 'name' => 'Outer' }
55
+ expect(render(template, assigns)).to eq 'Hello, Inner!Outer'
56
+ end
57
+ end
58
+
59
+ describe 'capture tag' do
60
+ it 'captures content into a variable' do
61
+ template = "{% capture my_variable %}Hello, Capture!{% endcapture %}{{ my_variable }}"
62
+ expect(render(template)).to eq 'Hello, Capture!'
63
+ end
64
+
65
+ it 'captures complex content with Liquid logic' do
66
+ template = "{% capture complex %}{% for i in (1..3) %}{{ i }}{% endfor %}{% endcapture %}{{ complex }}"
67
+ expect(render(template)).to eq '123'
68
+ end
69
+
70
+ it 'overwrites previously captured variables' do
71
+ template = "{% capture var %}First{% endcapture %}{% capture var %}Second{% endcapture %}{{ var }}"
72
+ expect(render(template)).to eq 'Second'
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,7 @@
1
+ require 'shopify_liquid_test_helper'
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:all) do
5
+ ShopifyLiquidTestHelper.register_custom_tags
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shopify_liquid_test_helper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ken Takagiwa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-08-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: liquid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ description: This gem provides helper methods and custom tags for testing Shopify
56
+ Liquid templates
57
+ email:
58
+ - ugw.gi.world@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - README.md
64
+ - Rakefile
65
+ - lib/shopify_liquid_test_helper.rb
66
+ - lib/shopify_liquid_test_helper/capture_tag.rb
67
+ - lib/shopify_liquid_test_helper/render_tag.rb
68
+ - spec/shopify_liquid_test_helper_spec.rb
69
+ - spec/spec_helper.rb
70
+ homepage: https://github.com/yourusername/shopify_liquid_test_helper
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 3.3.7
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: A helper for testing Shopify Liquid templates
93
+ test_files: []