dugway 1.2.0 → 1.3.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 +4 -4
- data/.github/workflows/main.yml +1 -1
- data/README.md +32 -7
- data/dugway.gemspec +11 -9
- data/lib/dugway/cli/server.rb +67 -7
- data/lib/dugway/liquid/drops/artists_drop.rb +12 -0
- data/lib/dugway/liquid/drops/base_drop.rb +27 -2
- data/lib/dugway/liquid/drops/categories_drop.rb +12 -0
- data/lib/dugway/liquid/drops/pages_drop.rb +30 -2
- data/lib/dugway/liquid/drops/product_drop.rb +11 -0
- data/lib/dugway/liquid/drops/products_drop.rb +39 -0
- data/lib/dugway/liquid/drops/theme_drop.rb +29 -6
- data/lib/dugway/liquid/filters/core_filters.rb +169 -7
- data/lib/dugway/liquid/filters/font_filters.rb +1 -0
- data/lib/dugway/liquid/filters/util_filters.rb +1 -10
- data/lib/dugway/liquid/tags/get.rb +6 -6
- data/lib/dugway/liquid/tags/paginate.rb +61 -11
- data/lib/dugway/store.rb +39 -1
- data/lib/dugway/theme.rb +72 -33
- data/lib/dugway/version.rb +1 -1
- data/lib/dugway.rb +24 -1
- data/locales/storefront.de.yml +2 -0
- data/locales/storefront.en-CA.yml +2 -0
- data/locales/storefront.en-GB.yml +2 -0
- data/locales/storefront.en-US.yml +2 -0
- data/locales/storefront.es-ES.yml +2 -0
- data/locales/storefront.es-MX.yml +2 -0
- data/locales/storefront.fr-CA.yml +2 -0
- data/locales/storefront.fr-FR.yml +2 -0
- data/locales/storefront.id.yml +2 -0
- data/locales/storefront.it.yml +2 -0
- data/locales/storefront.ja.yml +2 -0
- data/locales/storefront.ko.yml +2 -0
- data/locales/storefront.nl.yml +2 -0
- data/locales/storefront.pl.yml +2 -0
- data/locales/storefront.pt-BR.yml +2 -0
- data/locales/storefront.pt-PT.yml +2 -0
- data/locales/storefront.ro.yml +2 -0
- data/locales/storefront.sv.yml +2 -0
- data/locales/storefront.tr.yml +2 -0
- data/locales/storefront.zh-CN.yml +2 -0
- data/locales/storefront.zh-TW.yml +2 -0
- data/mise.toml +2 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/units/dugway/liquid/drops/pages_drop_spec.rb +186 -7
- data/spec/units/dugway/liquid/drops/product_drop_spec.rb +17 -0
- data/spec/units/dugway/liquid/filters/core_filters_spec.rb +301 -3
- data/spec/units/dugway/store_spec.rb +18 -0
- data/spec/units/dugway/theme_spec.rb +246 -0
- metadata +54 -25
@@ -1,6 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Dugway::Drops::PagesDrop do
|
4
|
+
before(:each) do
|
5
|
+
# Reset store state before each test
|
6
|
+
Dugway.store.instance_variable_set(:@pages, nil)
|
7
|
+
Dugway.store.instance_variable_set(:@external_pages, nil)
|
8
|
+
end
|
9
|
+
|
4
10
|
let(:pages) do
|
5
11
|
Dugway::Drops::PagesDrop.new(
|
6
12
|
Dugway.store.pages.map do |p|
|
@@ -14,15 +20,58 @@ describe Dugway::Drops::PagesDrop do
|
|
14
20
|
)
|
15
21
|
end
|
16
22
|
|
23
|
+
def create_pages_with_external(external_pages_data)
|
24
|
+
# Mock the store before creating pages
|
25
|
+
allow(Dugway.store).to receive(:external_pages).and_return(external_pages_data)
|
26
|
+
# Clear memoized pages
|
27
|
+
Dugway.store.instance_variable_set(:@pages, nil)
|
28
|
+
|
29
|
+
Dugway::Drops::PagesDrop.new(
|
30
|
+
Dugway.store.pages.map do |p|
|
31
|
+
case p["permalink"]
|
32
|
+
when "cart"
|
33
|
+
Dugway::Drops::CartDrop.new(p)
|
34
|
+
else
|
35
|
+
Dugway::Drops::PageDrop.new(p)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
17
41
|
describe "#all" do
|
18
|
-
|
19
|
-
all
|
20
|
-
|
21
|
-
|
42
|
+
context "with only custom pages" do
|
43
|
+
it "should return an array of all custom pages" do
|
44
|
+
all = pages.all
|
45
|
+
all.should be_an_instance_of(Array)
|
46
|
+
all.size.should == 1
|
22
47
|
|
23
|
-
|
24
|
-
|
25
|
-
|
48
|
+
page = all.first
|
49
|
+
page.should be_an_instance_of(Dugway::Drops::PageDrop)
|
50
|
+
page.name.should == 'About Us'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "with custom and external pages" do
|
55
|
+
it "should return an array of all pages (custom + external)" do
|
56
|
+
pages_with_external = create_pages_with_external([
|
57
|
+
{
|
58
|
+
'name' => 'Subscribe',
|
59
|
+
'permalink' => 'subscribe',
|
60
|
+
'url' => 'https://example.com/subscribe',
|
61
|
+
'category' => 'external'
|
62
|
+
}
|
63
|
+
])
|
64
|
+
|
65
|
+
all = pages_with_external.all
|
66
|
+
all.should be_an_instance_of(Array)
|
67
|
+
all.size.should == 2
|
68
|
+
|
69
|
+
custom_page = all.find { |p| p.name == 'About Us' }
|
70
|
+
custom_page.should be_an_instance_of(Dugway::Drops::PageDrop)
|
71
|
+
|
72
|
+
external_page = all.find { |p| p.name == 'Subscribe' }
|
73
|
+
external_page.should be_an_instance_of(Dugway::Drops::PageDrop)
|
74
|
+
end
|
26
75
|
end
|
27
76
|
end
|
28
77
|
|
@@ -46,6 +95,136 @@ describe Dugway::Drops::PagesDrop do
|
|
46
95
|
end
|
47
96
|
end
|
48
97
|
|
98
|
+
describe "#subscribe_page" do
|
99
|
+
context "when subscribe_url is configured" do
|
100
|
+
it "should return the subscribe page" do
|
101
|
+
allow(Dugway.store).to receive(:subscribe_url).and_return('https://example.com/subscribe')
|
102
|
+
pages_with_subscribe = create_pages_with_external([
|
103
|
+
{
|
104
|
+
'name' => 'Subscribe',
|
105
|
+
'permalink' => 'subscribe',
|
106
|
+
'url' => 'https://example.com/subscribe',
|
107
|
+
'category' => 'external'
|
108
|
+
}
|
109
|
+
])
|
110
|
+
|
111
|
+
subscribe_page = pages_with_subscribe.subscribe_page
|
112
|
+
subscribe_page.should be_an_instance_of(Dugway::Drops::PageDrop)
|
113
|
+
subscribe_page.name.should == 'Subscribe'
|
114
|
+
subscribe_page.url.should == 'https://example.com/subscribe'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when subscribe_url is not configured" do
|
119
|
+
it "should return nil" do
|
120
|
+
allow(Dugway.store).to receive(:subscribe_url).and_return(nil)
|
121
|
+
pages_without_subscribe = create_pages_with_external([])
|
122
|
+
|
123
|
+
pages_without_subscribe.subscribe_page.should be_nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#custom_pages" do
|
129
|
+
it "should return an array of custom pages" do
|
130
|
+
custom_pages = pages.custom_pages
|
131
|
+
custom_pages.should be_an_instance_of(Array)
|
132
|
+
custom_pages.size.should == 1
|
133
|
+
custom_pages.first.should be_an_instance_of(Dugway::Drops::PageDrop)
|
134
|
+
custom_pages.first.name.should == 'About Us'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "#external_pages" do
|
139
|
+
context "when subscribe_url is configured" do
|
140
|
+
it "should return an array with the subscribe page" do
|
141
|
+
pages_with_subscribe = create_pages_with_external([
|
142
|
+
{
|
143
|
+
'name' => 'Subscribe',
|
144
|
+
'permalink' => 'subscribe',
|
145
|
+
'url' => 'https://example.com/subscribe',
|
146
|
+
'category' => 'external'
|
147
|
+
}
|
148
|
+
])
|
149
|
+
|
150
|
+
external_pages = pages_with_subscribe.external_pages
|
151
|
+
external_pages.should be_an_instance_of(Array)
|
152
|
+
external_pages.size.should == 1
|
153
|
+
external_pages.first.should be_an_instance_of(Dugway::Drops::PageDrop)
|
154
|
+
external_pages.first.name.should == 'Subscribe'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when subscribe_url is not configured" do
|
159
|
+
it "should return an empty array" do
|
160
|
+
pages_without_subscribe = create_pages_with_external([])
|
161
|
+
|
162
|
+
pages_without_subscribe.external_pages.should == []
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "#required_pages" do
|
168
|
+
def create_pages_with_required(required_pages_data)
|
169
|
+
# Mock the store before creating pages
|
170
|
+
allow(Dugway.store).to receive(:required_pages).and_return(required_pages_data)
|
171
|
+
# Clear memoized pages
|
172
|
+
Dugway.store.instance_variable_set(:@pages, nil)
|
173
|
+
|
174
|
+
Dugway::Drops::PagesDrop.new(
|
175
|
+
Dugway.store.pages.map do |p|
|
176
|
+
case p["permalink"]
|
177
|
+
when "cart"
|
178
|
+
Dugway::Drops::CartDrop.new(p)
|
179
|
+
else
|
180
|
+
Dugway::Drops::PageDrop.new(p)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
context "when required pages are configured" do
|
187
|
+
it "should return an array with the required pages" do
|
188
|
+
pages_with_required = create_pages_with_required([
|
189
|
+
{
|
190
|
+
'name' => 'Terms of Service',
|
191
|
+
'permalink' => 'terms-of-service',
|
192
|
+
'url' => 'https://example.com/terms',
|
193
|
+
'category' => 'custom',
|
194
|
+
'required' => true
|
195
|
+
},
|
196
|
+
{
|
197
|
+
'name' => 'Privacy Policy',
|
198
|
+
'permalink' => 'privacy-policy',
|
199
|
+
'url' => 'https://example.com/privacy',
|
200
|
+
'category' => 'custom',
|
201
|
+
'required' => true
|
202
|
+
}
|
203
|
+
])
|
204
|
+
|
205
|
+
required_pages = pages_with_required.required_pages
|
206
|
+
required_pages.should be_an_instance_of(Array)
|
207
|
+
required_pages.size.should == 2
|
208
|
+
|
209
|
+
terms_page = required_pages.find { |p| p.name == 'Terms of Service' }
|
210
|
+
terms_page.should be_an_instance_of(Dugway::Drops::PageDrop)
|
211
|
+
terms_page.url.should == 'https://example.com/terms'
|
212
|
+
|
213
|
+
privacy_page = required_pages.find { |p| p.name == 'Privacy Policy' }
|
214
|
+
privacy_page.should be_an_instance_of(Dugway::Drops::PageDrop)
|
215
|
+
privacy_page.url.should == 'https://example.com/privacy'
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "when no required pages are configured" do
|
220
|
+
it "should return an empty array" do
|
221
|
+
pages_without_required = create_pages_with_required([])
|
222
|
+
|
223
|
+
pages_without_required.required_pages.should == []
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
49
228
|
private
|
50
229
|
|
51
230
|
def rendered_template(template, assigns={}, registers={})
|
@@ -302,4 +302,21 @@ describe Dugway::Drops::ProductDrop do
|
|
302
302
|
expect(related_products).to eq(related_products_mock)
|
303
303
|
end
|
304
304
|
end
|
305
|
+
|
306
|
+
describe "#price_suffix" do
|
307
|
+
it "should return nil when no price_suffix is configured" do
|
308
|
+
allow(Dugway.store).to receive(:price_suffix).and_return(nil)
|
309
|
+
product.price_suffix.should be_nil
|
310
|
+
end
|
311
|
+
|
312
|
+
it "should return the price_suffix from store configuration" do
|
313
|
+
allow(Dugway.store).to receive(:price_suffix).and_return(' USD')
|
314
|
+
product.price_suffix.should == ' USD'
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should return the price_suffix from store options" do
|
318
|
+
allow(Dugway.store).to receive(:price_suffix).and_return('+ HI')
|
319
|
+
product.price_suffix.should == '+ HI'
|
320
|
+
end
|
321
|
+
end
|
305
322
|
end
|
@@ -34,12 +34,251 @@ describe Dugway::Filters::CoreFilters do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
it "should support the 'code' format argument" do
|
37
|
-
rendered_template("{{ 1234.56 | money: 'code' }}").should == '
|
37
|
+
rendered_template("{{ 1234.56 | money: 'code' }}").should == '<span class="currency_code">USD</span>1,234.56'
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should support the 'sign_and_code' format argument" do
|
41
41
|
rendered_template("{{ 1234.56 | money: 'sign_and_code' }}").should == '<span class="currency_sign">$</span>1,234.56 <span class="currency_code">USD</span>'
|
42
42
|
end
|
43
|
+
|
44
|
+
it "should use theme.money_format when explicitly passed as parameter" do
|
45
|
+
theme_drop = Dugway::Drops::ThemeDrop.new({ 'money_format' => 'sign' }, {})
|
46
|
+
rendered_template("{{ 1234.56 | money: theme.money_format }}", { 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>1,234.56'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should use theme.money_format = 'code' when explicitly passed as parameter" do
|
50
|
+
theme_drop = Dugway::Drops::ThemeDrop.new({ 'money_format' => 'code' }, {})
|
51
|
+
rendered_template("{{ 1234.56 | money: theme.money_format }}", { 'theme' => theme_drop }).should == '<span class="currency_code">USD</span>1,234.56'
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should use theme.money_format = 'sign_and_code' when explicitly passed as parameter" do
|
55
|
+
theme_drop = Dugway::Drops::ThemeDrop.new({ 'money_format' => 'sign_and_code' }, {})
|
56
|
+
rendered_template("{{ 1234.56 | money: theme.money_format }}", { 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>1,234.56 <span class="currency_code">USD</span>'
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should use default format when no format argument is provided" do
|
60
|
+
theme_drop = Dugway::Drops::ThemeDrop.new({ 'money_format' => 'sign' }, {})
|
61
|
+
rendered_template("{{ 1234.56 | money }}", { 'theme' => theme_drop }).should == '1,234.56'
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should fall back to default format when theme.money_format is not set" do
|
65
|
+
rendered_template("{{ 1234.56 | money }}").should == '1,234.56'
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should handle money_format from dugway.json customization when explicitly passed" do
|
69
|
+
# Simulate how the theme would be loaded with dugway.json customization
|
70
|
+
customization = { 'money_format' => 'sign' }
|
71
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
72
|
+
rendered_template("{{ 1234.56 | money: theme.money_format }}", { 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>1,234.56'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should handle money: theme.money_format syntax like storefront" do
|
76
|
+
# Test the exact syntax the user is using: {{ price | money: theme.money_format }}
|
77
|
+
customization = { 'money_format' => 'sign' }
|
78
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
79
|
+
rendered_template("{{ 1234.56 | money: theme.money_format }}", { 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>1,234.56'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should handle money_format 'none' correctly returning just the number" do
|
83
|
+
customization = { 'money_format' => 'none' }
|
84
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
85
|
+
rendered_template("{{ 1234.56 | money: theme.money_format }}", { 'theme' => theme_drop }).should == '1,234.56'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should support the 'rounded' format argument" do
|
89
|
+
rendered_template("{{ 1234.56 | money: 'rounded' }}").should == '1,235'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should support the 'sign_rounded' format argument" do
|
93
|
+
rendered_template("{{ 1234.56 | money: 'sign_rounded' }}").should == '<span class="currency_sign">$</span>1,235'
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should support the 'code_rounded' format argument" do
|
97
|
+
rendered_template("{{ 1234.56 | money: 'code_rounded' }}").should == '<span class="currency_code">USD</span>1,235'
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should round up 10.99 to 11 with rounded format" do
|
101
|
+
rendered_template("{{ 10.99 | money: 'rounded' }}").should == '11'
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should round up 10.99 to $11 with sign_rounded format" do
|
105
|
+
rendered_template("{{ 10.99 | money: 'sign_rounded' }}").should == '<span class="currency_sign">$</span>11'
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should round up 10.99 to 11 USD with code_rounded format" do
|
109
|
+
rendered_template("{{ 10.99 | money: 'code_rounded' }}").should == '<span class="currency_code">USD</span>11'
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should handle exact amounts with rounded formats" do
|
113
|
+
rendered_template("{{ 10.00 | money: 'rounded' }}").should == '10'
|
114
|
+
rendered_template("{{ 10.00 | money: 'sign_rounded' }}").should == '<span class="currency_sign">$</span>10'
|
115
|
+
rendered_template("{{ 10.00 | money: 'code_rounded' }}").should == '<span class="currency_code">USD</span>10'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#product_price" do
|
120
|
+
it "should handle fixed pricing products" do
|
121
|
+
product = { 'default_price' => 1234.56, 'variable_pricing' => false }
|
122
|
+
rendered_template("{{ product | product_price }}", { 'product' => product }).should == '1,234.56'
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should handle fixed pricing products with format" do
|
126
|
+
product = { 'default_price' => 1234.56, 'variable_pricing' => false }
|
127
|
+
rendered_template("{{ product | product_price: 'sign' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>1,234.56'
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should handle variable pricing products with default format" do
|
131
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
132
|
+
rendered_template("{{ product | product_price }}", { 'product' => product }).should == '10.00 - 20.00'
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should handle variable pricing products with sign format" do
|
136
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
137
|
+
rendered_template("{{ product | product_price: 'sign' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>10.00 - <span class="currency_sign">$</span>20.00'
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should handle variable pricing products with code format" do
|
141
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
142
|
+
rendered_template("{{ product | product_price: 'code' }}", { 'product' => product }).should == '<span class="currency_code">USD</span>10.00 - 20.00'
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should return empty for nil product" do
|
146
|
+
rendered_template("{{ nil | product_price }}").should == ''
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should fallback to price field if default_price is not present" do
|
150
|
+
product = { 'price' => 1234.56, 'variable_pricing' => false }
|
151
|
+
rendered_template("{{ product | product_price }}", { 'product' => product }).should == '1,234.56'
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should work with theme.money_format for fixed pricing" do
|
155
|
+
product = { 'default_price' => 1234.56, 'variable_pricing' => false }
|
156
|
+
customization = { 'money_format' => 'sign' }
|
157
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
158
|
+
rendered_template("{{ product | product_price: theme.money_format }}", { 'product' => product, 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>1,234.56'
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should work with theme.money_format for variable pricing" do
|
162
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
163
|
+
customization = { 'money_format' => 'sign' }
|
164
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
165
|
+
rendered_template("{{ product | product_price: theme.money_format }}", { 'product' => product, 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>10.00 - <span class="currency_sign">$</span>20.00'
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should handle locale-specific currency formatting" do
|
169
|
+
# Test that the currency code position respects locale formatting
|
170
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
171
|
+
rendered_template("{{ product | product_price: 'code' }}", { 'product' => product }).should == '<span class="currency_code">USD</span>10.00 - 20.00'
|
172
|
+
end
|
173
|
+
|
174
|
+
# Tests for range_format parameter
|
175
|
+
it "should show min price only when range_format is 'min_only'" do
|
176
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
177
|
+
rendered_template("{{ product | product_price: 'sign', 'min_only' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>10.00'
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should show min price only with code format when range_format is 'min_only'" do
|
181
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
182
|
+
rendered_template("{{ product | product_price: 'code', 'min_only' }}", { 'product' => product }).should == '<span class="currency_code">USD</span>10.00'
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should show max price only when range_format is 'max_only'" do
|
186
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
187
|
+
rendered_template("{{ product | product_price: 'sign', 'max_only' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>20.00'
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should show max price only with code format when range_format is 'max_only'" do
|
191
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
192
|
+
rendered_template("{{ product | product_price: 'code', 'max_only' }}", { 'product' => product }).should == '<span class="currency_code">USD</span>20.00'
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should show range when range_format is 'default'" do
|
196
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
197
|
+
rendered_template("{{ product | product_price: 'sign', 'default' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>10.00 - <span class="currency_sign">$</span>20.00'
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should show range when range_format is nil" do
|
201
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
202
|
+
rendered_template("{{ product | product_price: 'sign', nil }}", { 'product' => product }).should == '<span class="currency_sign">$</span>10.00 - <span class="currency_sign">$</span>20.00'
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should show range when range_format is an invalid value" do
|
206
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
207
|
+
rendered_template("{{ product | product_price: 'sign', 'invalid' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>10.00 - <span class="currency_sign">$</span>20.00'
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should show range by default when range_format is not specified" do
|
211
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
212
|
+
rendered_template("{{ product | product_price: 'sign' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>10.00 - <span class="currency_sign">$</span>20.00'
|
213
|
+
end
|
214
|
+
|
215
|
+
# Test ThemeDrop integration for real-world usage
|
216
|
+
it "should work with theme-based range format when 'min_only'" do
|
217
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
218
|
+
customization = { 'price_range_format' => 'min_only' }
|
219
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
220
|
+
rendered_template("{{ product | product_price: 'sign', theme.price_range_format }}", { 'product' => product, 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>10.00'
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should work with theme-based range format when 'max_only'" do
|
224
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
225
|
+
customization = { 'price_range_format' => 'max_only' }
|
226
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
227
|
+
rendered_template("{{ product | product_price: 'sign', theme.price_range_format }}", { 'product' => product, 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>20.00'
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should work with both theme.money_format and theme-based range format" do
|
231
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
232
|
+
customization = { 'money_format' => 'sign', 'price_range_format' => 'min_only' }
|
233
|
+
theme_drop = Dugway::Drops::ThemeDrop.new(customization, {})
|
234
|
+
rendered_template("{{ product | product_price: theme.money_format, theme.price_range_format }}", { 'product' => product, 'theme' => theme_drop }).should == '<span class="currency_sign">$</span>10.00'
|
235
|
+
end
|
236
|
+
|
237
|
+
# Tests for rounded formats
|
238
|
+
it "should handle fixed pricing products with rounded format" do
|
239
|
+
product = { 'default_price' => 1234.56, 'variable_pricing' => false }
|
240
|
+
rendered_template("{{ product | product_price: 'rounded' }}", { 'product' => product }).should == '1,235'
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should handle fixed pricing products with sign_rounded format" do
|
244
|
+
product = { 'default_price' => 1234.56, 'variable_pricing' => false }
|
245
|
+
rendered_template("{{ product | product_price: 'sign_rounded' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>1,235'
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should handle fixed pricing products with code_rounded format" do
|
249
|
+
product = { 'default_price' => 1234.56, 'variable_pricing' => false }
|
250
|
+
rendered_template("{{ product | product_price: 'code_rounded' }}", { 'product' => product }).should == '<span class="currency_code">USD</span>1,235'
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should handle variable pricing products with rounded format" do
|
254
|
+
product = { 'min_price' => 10.99, 'max_price' => 20.99, 'variable_pricing' => true }
|
255
|
+
rendered_template("{{ product | product_price: 'rounded' }}", { 'product' => product }).should == '11 - 21'
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should handle variable pricing products with sign_rounded format" do
|
259
|
+
product = { 'min_price' => 10.99, 'max_price' => 20.99, 'variable_pricing' => true }
|
260
|
+
rendered_template("{{ product | product_price: 'sign_rounded' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>11 - <span class="currency_sign">$</span>21'
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should handle variable pricing products with code_rounded format" do
|
264
|
+
product = { 'min_price' => 10.99, 'max_price' => 20.99, 'variable_pricing' => true }
|
265
|
+
rendered_template("{{ product | product_price: 'code_rounded' }}", { 'product' => product }).should == '<span class="currency_code">USD</span>11 - 21'
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should handle rounded formats with range_format min_only" do
|
269
|
+
product = { 'min_price' => 10.99, 'max_price' => 20.99, 'variable_pricing' => true }
|
270
|
+
rendered_template("{{ product | product_price: 'sign_rounded', 'min_only' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>11'
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should handle rounded formats with range_format max_only" do
|
274
|
+
product = { 'min_price' => 10.99, 'max_price' => 20.99, 'variable_pricing' => true }
|
275
|
+
rendered_template("{{ product | product_price: 'code_rounded', 'max_only' }}", { 'product' => product }).should == '<span class="currency_code">USD</span>21'
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should handle exact amounts with rounded variable pricing" do
|
279
|
+
product = { 'min_price' => 10.00, 'max_price' => 20.00, 'variable_pricing' => true }
|
280
|
+
rendered_template("{{ product | product_price: 'sign_rounded' }}", { 'product' => product }).should == '<span class="currency_sign">$</span>10 - <span class="currency_sign">$</span>20'
|
281
|
+
end
|
43
282
|
end
|
44
283
|
|
45
284
|
describe "#money_with_sign" do
|
@@ -50,7 +289,62 @@ describe Dugway::Filters::CoreFilters do
|
|
50
289
|
|
51
290
|
describe "#money_with_code" do
|
52
291
|
it "should convert a number to currency format with a code" do
|
53
|
-
rendered_template("{{ 1234.56 | money_with_code }}").should == '
|
292
|
+
rendered_template("{{ 1234.56 | money_with_code }}").should == '<span class="currency_code">USD</span>1,234.56'
|
293
|
+
end
|
294
|
+
|
295
|
+
# Tests for different currencies to ensure locale-specific formatting
|
296
|
+
context "with EUR currency" do
|
297
|
+
# TODO: The 'eu' locale is invalid and needs to be fixed in the bigcartel-currency-locales gem.
|
298
|
+
# Once the gem is updated to use a valid locale like 'de-DE' for EUR, we can update these tests
|
299
|
+
# to expect the correct European number formatting (1.200,99 instead of 1,200.99).
|
300
|
+
# For now, we're expecting the fallback US formatting due to the invalid locale.
|
301
|
+
it "should display currency code after number with space for EUR" do
|
302
|
+
eur_currency = { 'sign' => '€', 'name' => 'Euro', 'id' => 4, 'code' => 'EUR', 'locale' => 'eu' }
|
303
|
+
# Expected: '1.200,99 <span class="currency_code">EUR</span>' once locale is fixed
|
304
|
+
rendered_template_with_currency("{{ 1200.99 | money_with_code }}", eur_currency).should == '1,200.99 <span class="currency_code">EUR</span>'
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should work with money: 'code' format for EUR" do
|
308
|
+
eur_currency = { 'sign' => '€', 'name' => 'Euro', 'id' => 4, 'code' => 'EUR', 'locale' => 'eu' }
|
309
|
+
# Expected: '1.200,99 <span class="currency_code">EUR</span>' once locale is fixed
|
310
|
+
rendered_template_with_currency("{{ 1200.99 | money: 'code' }}", eur_currency).should == '1,200.99 <span class="currency_code">EUR</span>'
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
context "with CHF currency" do
|
315
|
+
it "should display currency code before number with space for CHF" do
|
316
|
+
chf_currency = { 'sign' => 'CHF', 'name' => 'Swiss Franc', 'id' => 10, 'code' => 'CHF', 'locale' => 'gsw-CH' }
|
317
|
+
rendered_template_with_currency("{{ 1200.99 | money_with_code }}", chf_currency).should == '<span class="currency_code">CHF</span> 1\'200.99'
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should work with money: 'code' format for CHF" do
|
321
|
+
chf_currency = { 'sign' => 'CHF', 'name' => 'Swiss Franc', 'id' => 10, 'code' => 'CHF', 'locale' => 'gsw-CH' }
|
322
|
+
rendered_template_with_currency("{{ 1200.99 | money: 'code' }}", chf_currency).should == '<span class="currency_code">CHF</span> 1\'200.99'
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context "with JPY currency" do
|
327
|
+
it "should display currency code before number with no decimals for JPY" do
|
328
|
+
jpy_currency = { 'sign' => '¥', 'name' => 'Japanese Yen', 'id' => 6, 'code' => 'JPY', 'locale' => 'ja' }
|
329
|
+
rendered_template_with_currency("{{ 1200.99 | money_with_code }}", jpy_currency).should == '<span class="currency_code">JPY</span>1,201'
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should work with money: 'code' format for JPY" do
|
333
|
+
jpy_currency = { 'sign' => '¥', 'name' => 'Japanese Yen', 'id' => 6, 'code' => 'JPY', 'locale' => 'ja' }
|
334
|
+
rendered_template_with_currency("{{ 1200.99 | money: 'code' }}", jpy_currency).should == '<span class="currency_code">JPY</span>1,201'
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context "with GBP currency" do
|
339
|
+
it "should display currency code before number with no space for GBP" do
|
340
|
+
gbp_currency = { 'sign' => '£', 'name' => 'Pound Sterling', 'id' => 5, 'code' => 'GBP', 'locale' => 'en-GB' }
|
341
|
+
rendered_template_with_currency("{{ 1200.99 | money_with_code }}", gbp_currency).should == '<span class="currency_code">GBP</span>1,200.99'
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should work with money: 'code' format for GBP" do
|
345
|
+
gbp_currency = { 'sign' => '£', 'name' => 'Pound Sterling', 'id' => 5, 'code' => 'GBP', 'locale' => 'en-GB' }
|
346
|
+
rendered_template_with_currency("{{ 1200.99 | money: 'code' }}", gbp_currency).should == '<span class="currency_code">GBP</span>1,200.99'
|
347
|
+
end
|
54
348
|
end
|
55
349
|
end
|
56
350
|
|
@@ -63,6 +357,10 @@ describe Dugway::Filters::CoreFilters do
|
|
63
357
|
private
|
64
358
|
|
65
359
|
def rendered_template(template, assigns={})
|
66
|
-
Liquid::Template.parse(template).render(assigns, :registers => { :currency => Dugway.store.currency })
|
360
|
+
Liquid::Template.parse(template).render(assigns, :registers => { :currency => Dugway.store.currency, :settings => {} })
|
361
|
+
end
|
362
|
+
|
363
|
+
def rendered_template_with_currency(template, currency, assigns={})
|
364
|
+
Liquid::Template.parse(template).render(assigns, :registers => { :currency => currency, :settings => {} })
|
67
365
|
end
|
68
366
|
end
|
@@ -226,4 +226,22 @@ describe Dugway::Store do
|
|
226
226
|
end
|
227
227
|
end
|
228
228
|
end
|
229
|
+
|
230
|
+
describe "#price_suffix" do
|
231
|
+
context "when price_suffix is in store_options" do
|
232
|
+
let(:store) { Dugway::Store.new('dugway', { price_suffix: '+ HI' }) }
|
233
|
+
|
234
|
+
it "returns the price_suffix from store_options" do
|
235
|
+
store.price_suffix.should == '+ HI'
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context "when price_suffix is not in store_options" do
|
240
|
+
let(:store) { Dugway::Store.new('dugway', {}) }
|
241
|
+
|
242
|
+
it "returns nil" do
|
243
|
+
store.price_suffix.should be_nil
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
229
247
|
end
|