dugway 1.0.14 → 1.2.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/.github/workflows/main.yml +1 -1
  4. data/.gitignore +1 -0
  5. data/README.md +9 -0
  6. data/lib/dugway/application.rb +5 -3
  7. data/lib/dugway/assets/big_cartel_logo.svg +4 -0
  8. data/lib/dugway/cli/build.rb +18 -1
  9. data/lib/dugway/cli/server.rb +2 -2
  10. data/lib/dugway/cli/templates/source/settings.json +8 -0
  11. data/lib/dugway/cli/validate.rb +20 -2
  12. data/lib/dugway/controller.rb +5 -1
  13. data/lib/dugway/liquid/drops/account_drop.rb +4 -0
  14. data/lib/dugway/liquid/drops/features_drop.rb +144 -0
  15. data/lib/dugway/liquid/drops/product_drop.rb +8 -0
  16. data/lib/dugway/liquid/drops/products_drop.rb +1 -1
  17. data/lib/dugway/liquid/drops/related_products_drop.rb +88 -0
  18. data/lib/dugway/liquid/drops/theme_drop.rb +23 -0
  19. data/lib/dugway/liquid/drops/translations_drop.rb +122 -0
  20. data/lib/dugway/liquifier.rb +44 -8
  21. data/lib/dugway/store.rb +7 -2
  22. data/lib/dugway/theme.rb +169 -3
  23. data/lib/dugway/version.rb +1 -1
  24. data/lib/dugway.rb +31 -1
  25. data/locales/storefront.de.yml +79 -0
  26. data/locales/storefront.en-CA.yml +79 -0
  27. data/locales/storefront.en-GB.yml +79 -0
  28. data/locales/storefront.en-US.yml +79 -0
  29. data/locales/storefront.es-ES.yml +79 -0
  30. data/locales/storefront.es-MX.yml +79 -0
  31. data/locales/storefront.fr-CA.yml +79 -0
  32. data/locales/storefront.fr-FR.yml +79 -0
  33. data/locales/storefront.id.yml +79 -0
  34. data/locales/storefront.it.yml +79 -0
  35. data/locales/storefront.ja.yml +79 -0
  36. data/locales/storefront.ko.yml +79 -0
  37. data/locales/storefront.nl.yml +79 -0
  38. data/locales/storefront.pl.yml +79 -0
  39. data/locales/storefront.pt-BR.yml +79 -0
  40. data/locales/storefront.pt-PT.yml +79 -0
  41. data/locales/storefront.ro.yml +79 -0
  42. data/locales/storefront.sv.yml +79 -0
  43. data/locales/storefront.tr.yml +79 -0
  44. data/locales/storefront.zh-CN.yml +79 -0
  45. data/locales/storefront.zh-TW.yml +79 -0
  46. data/log/dugway.log +1 -0
  47. data/spec/features/page_rendering_spec.rb +4 -4
  48. data/spec/fixtures/theme/layout.html +2 -0
  49. data/spec/fixtures/theme/settings.json +6 -0
  50. data/spec/spec_helper.rb +4 -0
  51. data/spec/units/dugway/liquid/drops/features_drop_spec.rb +182 -0
  52. data/spec/units/dugway/liquid/drops/product_drop_spec.rb +36 -0
  53. data/spec/units/dugway/liquid/drops/related_products_drop_spec.rb +80 -0
  54. data/spec/units/dugway/liquid/drops/theme_drop_spec.rb +45 -0
  55. data/spec/units/dugway/liquid/drops/translations_drop_spec.rb +292 -0
  56. data/spec/units/dugway/store_spec.rb +37 -0
  57. data/spec/units/dugway/theme_spec.rb +456 -0
  58. metadata +35 -2
@@ -4,18 +4,30 @@ describe Dugway::Theme do
4
4
  let(:theme) { Dugway.theme }
5
5
 
6
6
  describe "#layout" do
7
+ before(:each) do
8
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
9
+ end
10
+
7
11
  it "should return the theme's layout" do
8
12
  theme.layout.should == read_file('layout.html')
9
13
  end
10
14
  end
11
15
 
12
16
  describe "#settings" do
17
+ before(:each) do
18
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
19
+ end
20
+
13
21
  it "should return a hash of the theme's settings" do
14
22
  theme.settings.should == JSON.parse(read_file('settings.json'))
15
23
  end
16
24
  end
17
25
 
18
26
  describe "#fonts" do
27
+ before(:each) do
28
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
29
+ end
30
+
19
31
  it "should return a hash of font settings values" do
20
32
  theme.fonts.should == {
21
33
  'text_font' => 'Georgia',
@@ -25,6 +37,10 @@ describe Dugway::Theme do
25
37
  end
26
38
 
27
39
  describe "#images" do
40
+ before(:each) do
41
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
42
+ end
43
+
28
44
  it "should return a hash of the image settings" do
29
45
  theme.images.should == {
30
46
  'logo' => { :url => 'images/logo_bc.png', :width => 1, :height => 1 }
@@ -33,6 +49,10 @@ describe Dugway::Theme do
33
49
  end
34
50
 
35
51
  describe "#image_sets" do
52
+ before(:each) do
53
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
54
+ end
55
+
36
56
  it "should return a hash of the image set settings" do
37
57
  theme.image_sets.should == {
38
58
  'slideshow_images' => [
@@ -47,6 +67,10 @@ describe Dugway::Theme do
47
67
  end
48
68
 
49
69
  describe "#customization" do
70
+ before(:each) do
71
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
72
+ end
73
+
50
74
  it "should return a hash of font, color, option, images and image sets settings values" do
51
75
  theme.customization.should == {
52
76
  'background_color' => 'white',
@@ -76,6 +100,8 @@ describe Dugway::Theme do
76
100
  Dugway.stub(:theme) {
77
101
  Dugway::Theme.new(:fixed_sidebar => false, :link_text_color => 'blue')
78
102
  }
103
+
104
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
79
105
  end
80
106
 
81
107
  it "should merge those values into the defaults" do
@@ -105,18 +131,30 @@ describe Dugway::Theme do
105
131
  end
106
132
 
107
133
  describe "#name" do
134
+ before(:each) do
135
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
136
+ end
137
+
108
138
  it "should return the theme's name" do
109
139
  theme.name.should == 'Test Theme'
110
140
  end
111
141
  end
112
142
 
113
143
  describe "#version" do
144
+ before(:each) do
145
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
146
+ end
147
+
114
148
  it "should return the theme's version" do
115
149
  theme.version.should == '1.2.3'
116
150
  end
117
151
  end
118
152
 
119
153
  describe "#file_content" do
154
+ before(:each) do
155
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
156
+ end
157
+
120
158
  it "should return the file content for most files" do
121
159
  %w( home.html products.html screenshot.jpg ).each { |file_name|
122
160
  theme.file_content(file_name).should == read_file(file_name)
@@ -133,6 +171,10 @@ describe Dugway::Theme do
133
171
  end
134
172
 
135
173
  describe "#build_file" do
174
+ before(:each) do
175
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
176
+ end
177
+
136
178
  it "should return the file content for most files" do
137
179
  %w( home.html products.html screenshot.jpg ).each { |file_name|
138
180
  theme.build_file(file_name).should == read_file(file_name)
@@ -149,25 +191,55 @@ describe Dugway::Theme do
149
191
  end
150
192
 
151
193
  describe "#files" do
194
+ before(:each) do
195
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
196
+ end
197
+
152
198
  it "should return an array of all files" do
153
199
  theme.files.should =~ ["cart.html", "contact.html", "home.html", "layout.html", "maintenance.html", "product.html", "products.html", "screenshot.jpg", "settings.json", "theme.css", "theme.js", "images/logo_bc.png", "images/slideshow/1.gif", "images/slideshow/2.gif", "images/slideshow/3.gif", "images/slideshow/4.gif", "images/slideshow/5.gif", "images/small.svg", "fonts/icons.ttf", "fonts/icons.woff"]
154
200
  end
155
201
  end
156
202
 
157
203
  describe "#image_files" do
204
+ before(:each) do
205
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
206
+ end
207
+
158
208
  it "should return an array of all image files" do
159
209
  theme.image_files.should =~ ["images/logo_bc.png", "images/slideshow/1.gif", "images/slideshow/2.gif", "images/slideshow/3.gif", "images/slideshow/4.gif", "images/slideshow/5.gif", "images/small.svg"]
160
210
  end
161
211
  end
162
212
 
163
213
  describe "#font_files" do
214
+ before(:each) do
215
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
216
+ end
217
+
164
218
  it "should return an array of all font files" do
165
219
  theme.font_files.should include("fonts/icons.ttf", "fonts/icons.woff")
166
220
  end
167
221
  end
168
222
 
169
223
  describe "#valid?" do
224
+ let(:required_colors) {[
225
+ 'background_color',
226
+ 'primary_text_color',
227
+ 'link_text_color',
228
+ 'link_hover_color',
229
+ 'button_background_color',
230
+ 'button_text_color',
231
+ 'button_hover_background_color'
232
+ ]}
233
+
170
234
  it "should return true when a theme has everything it needs" do
235
+ allow(theme).to receive(:validate_required_color_settings) { true }
236
+
237
+ allow(theme).to receive(:settings).and_return({
238
+ 'name' => 'Test Theme',
239
+ 'version' => '1.2.3',
240
+ 'colors' => required_colors.map { |color| {'variable' => color} }
241
+ })
242
+
171
243
  theme.valid?.should be(true)
172
244
  theme.errors.should be_empty
173
245
  end
@@ -175,6 +247,7 @@ describe Dugway::Theme do
175
247
  describe "when missing a required file" do
176
248
  before(:each) do
177
249
  theme.stub(:read_source_file).with('cart.html') { theme.unstub(:read_source_file); nil }
250
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
178
251
  end
179
252
 
180
253
  it "should not be valid" do
@@ -187,6 +260,7 @@ describe Dugway::Theme do
187
260
  describe "when missing a name" do
188
261
  before(:each) do
189
262
  theme.stub(:name) { nil }
263
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
190
264
  end
191
265
 
192
266
  it "should not be valid" do
@@ -199,6 +273,7 @@ describe Dugway::Theme do
199
273
  describe "when missing a version" do
200
274
  before(:each) do
201
275
  theme.stub(:version) { nil }
276
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
202
277
  end
203
278
 
204
279
  it "should not be valid" do
@@ -211,6 +286,7 @@ describe Dugway::Theme do
211
286
  describe "when having an invalid version" do
212
287
  before(:each) do
213
288
  theme.stub(:version) { '1.3' }
289
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
214
290
  end
215
291
 
216
292
  it "should not be valid" do
@@ -224,6 +300,7 @@ describe Dugway::Theme do
224
300
  before(:each) do
225
301
  theme.stub(:name) { nil }
226
302
  theme.stub(:version) { nil }
303
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
227
304
  end
228
305
 
229
306
  it "should return all of them" do
@@ -273,6 +350,7 @@ describe Dugway::Theme do
273
350
  end
274
351
 
275
352
  before(:each) do
353
+ allow(theme).to receive(:validate_required_color_settings) { true }
276
354
  allow(theme).to receive(:name) { "Test Theme" }
277
355
  allow(theme).to receive(:version) { "1.2.3" }
278
356
  end
@@ -293,6 +371,384 @@ describe Dugway::Theme do
293
371
  theme.errors.should include('Style in group \'Classic\' - Missing style_name')
294
372
  end
295
373
  end
374
+
375
+ describe "when missing option descriptions" do
376
+ before(:each) do
377
+ allow_any_instance_of(Dugway::Theme).to receive(:validate_required_color_settings) { true }
378
+ theme.stub(:settings) { {
379
+ 'name' => 'Test Theme',
380
+ 'version' => '1.2.3',
381
+ 'colors' => [
382
+ {'variable' => 'background_color'},
383
+ {'variable' => 'primary_text_color'},
384
+ {'variable' => 'link_text_color'},
385
+ {'variable' => 'link_hover_color'},
386
+ {'variable' => 'button_background_color'},
387
+ {'variable' => 'button_text_color'},
388
+ {'variable' => 'button_hover_background_color'}
389
+ ],
390
+ 'options' => [{ 'variable' => 'show_search', 'section' => 'global_navigation', 'label' => 'Show search' }]
391
+ } }
392
+ end
393
+
394
+ it "should not be valid" do
395
+ theme.valid?.should be(false)
396
+ theme.errors.size.should == 1
397
+ theme.errors.first.should == 'Missing descriptions for settings: show_search'
398
+ end
399
+
400
+ it "should be valid when skipping options validation" do
401
+ theme.valid?(validate_options: false).should be(true)
402
+ end
403
+ end
404
+
405
+ describe "when validating option requirements" do
406
+ let(:required_colors) {[
407
+ 'background_color',
408
+ 'primary_text_color',
409
+ 'link_text_color',
410
+ 'link_hover_color',
411
+ 'button_background_color',
412
+ 'button_text_color',
413
+ 'button_hover_background_color'
414
+ ]}
415
+
416
+ it "allows options with no requires" do
417
+ settings = theme.settings.merge({
418
+ 'options' => [{
419
+ 'variable' => 'show_search',
420
+ 'section' => 'global_navigation',
421
+ 'label' => 'Show search',
422
+ 'description' => 'Show search in header'
423
+ }]
424
+ })
425
+ allow(theme).to receive(:settings).and_return(settings)
426
+
427
+ theme.valid?.should be(true)
428
+ theme.errors.should be_empty
429
+ end
430
+
431
+ it "should not be valid when colors section is missing" do
432
+ allow(theme).to receive(:settings).and_return({
433
+ 'name' => 'Test Theme',
434
+ 'version' => '1.2.3',
435
+ 'options' => []
436
+ })
437
+
438
+ theme.valid?.should be(false)
439
+ theme.errors.should include("Missing colors section in theme settings")
440
+ end
441
+
442
+ it "should not be valid when required colors are missing" do
443
+
444
+ allow(theme).to receive(:settings).and_return({
445
+ 'name' => 'Test Theme',
446
+ 'version' => '1.2.3',
447
+ 'colors' => [
448
+ { 'variable' => 'background_color' }
449
+ ],
450
+ 'options' => []
451
+ })
452
+
453
+ theme.valid?.should be(false)
454
+ theme.errors.first.should match(/Missing required color settings:/)
455
+ end
456
+
457
+ it "validates feature flag format and values" do
458
+ settings = theme.settings.merge({
459
+ 'options' => [{
460
+ 'variable' => 'show_foo',
461
+ 'section' => 'general',
462
+ 'description' => 'Show foo thing',
463
+ 'requires' => [
464
+ 'feature:', # invalid: empty
465
+ 'feature:foo_flag gt visible', # invalid operator
466
+ 'feature:foo_flag eq invalid_value', # invalid value
467
+ 'feature:foo_flag neq visible', # valid
468
+ 'feature:foo_flag eq visible' # valid
469
+ ]
470
+ }]
471
+ })
472
+ allow(theme).to receive(:settings).and_return(settings)
473
+
474
+ theme.valid?.should be(false)
475
+ theme.errors.should include("Option 'show_foo' has invalid feature flag format")
476
+ theme.errors.should include("Option 'show_foo' has invalid operator 'gt'. Feature flags can only use `eq` or `neq`.")
477
+ theme.errors.should include("Option 'show_foo' has invalid comparison value 'invalid_value'. Feature flags can only check for `visible`.")
478
+
479
+ end
480
+ end
481
+
482
+ describe "when validating option sections" do
483
+ let(:valid_sections_string) { Dugway::Theme::VALID_SECTIONS.join(', ') }
484
+ let(:base_settings) do
485
+ {
486
+ 'name' => 'Test Theme',
487
+ 'version' => '1.2.3',
488
+ 'colors' => required_colors.map { |color| {'variable' => color} },
489
+ 'options' => []
490
+ }
491
+ end
492
+
493
+ # Define required_colors here if not already available in this scope
494
+ let(:required_colors) {[
495
+ 'background_color',
496
+ 'primary_text_color',
497
+ 'link_text_color',
498
+ 'link_hover_color',
499
+ 'button_background_color',
500
+ 'button_text_color',
501
+ 'button_hover_background_color'
502
+ ]}
503
+
504
+ # Fix for the failing tests - define constant if needed
505
+ before(:each) do
506
+ unless defined?(Dugway::Theme::REQUIRED_FILES)
507
+ stub_const("Dugway::Theme::REQUIRED_FILES", [
508
+ "cart.html", "contact.html", "home.html", "layout.html", "maintenance.html",
509
+ "product.html", "products.html", "screenshot.jpg", "settings.json", "theme.css", "theme.js"
510
+ ])
511
+ end
512
+ end
513
+
514
+ it "passes with a valid section" do
515
+ settings = base_settings.merge({
516
+ 'options' => [{
517
+ 'variable' => 'valid_option',
518
+ 'description' => 'Valid option',
519
+ 'section' => 'homepage'
520
+ }]
521
+ })
522
+ # Stub settings
523
+ allow(theme).to receive(:settings).and_return(settings)
524
+
525
+ # Stub ALL required files first
526
+ Dugway::Theme::REQUIRED_FILES.each do |file|
527
+ allow(theme).to receive(:read_source_file).with(file).and_return("some content")
528
+ end
529
+
530
+ # Then override the specific stub for layout.html
531
+ allow(theme).to receive(:read_source_file).with('layout.html')
532
+ .and_return('<body data-bc-page-type="home"><div data-bc-hook="header"></div><div data-bc-hook="footer"></div></body>')
533
+
534
+ # Stub color validation
535
+ allow(theme).to receive(:validate_required_color_settings).and_return(true)
536
+
537
+ expect(Kernel).not_to receive(:warn)
538
+ theme.valid?.should be(true)
539
+ end
540
+
541
+ it "warns appropriately for multiple options with missing or invalid sections" do
542
+ settings = base_settings.merge({
543
+ 'options' => [
544
+ { 'variable' => 'option_1_valid', 'description' => 'Valid', 'section' => 'homepage' },
545
+ { 'variable' => 'option_2_missing', 'description' => 'Missing section' }, # Missing section
546
+ { 'variable' => 'option_3_invalid', 'description' => 'Invalid section', 'section' => 'bad_section' }, # Invalid section
547
+ { 'variable' => 'option_4_valid', 'description' => 'Valid again', 'section' => 'general' },
548
+ { 'variable' => 'option_5_missing', 'description' => 'Missing section again' }, # Missing section again
549
+ { 'description' => 'Option 6 missing variable and section' }, # Missing variable and section
550
+ { 'variable' => 'option_7_invalid', 'description' => 'Invalid section again', 'section' => 'another_bad_one' } # Invalid section again
551
+ ]
552
+ })
553
+ # Stub settings
554
+ allow(theme).to receive(:settings).and_return(settings)
555
+
556
+ # Stub ALL required files first
557
+ Dugway::Theme::REQUIRED_FILES.each do |file|
558
+ allow(theme).to receive(:read_source_file).with(file).and_return("some content")
559
+ end
560
+
561
+ # Then override the specific stub for layout.html
562
+ allow(theme).to receive(:read_source_file).with('layout.html')
563
+ .and_return('<body data-bc-page-type="home"><div data-bc-hook="header"></div><div data-bc-hook="footer"></div></body>')
564
+
565
+ # Stub color validation
566
+ allow(theme).to receive(:validate_required_color_settings).and_return(true)
567
+
568
+ # Allow warn to be called so we can spy on it
569
+ allow(Kernel).to receive(:warn)
570
+
571
+ # Call valid? and assert true (since warnings don't invalidate)
572
+ theme.valid?.should be(true)
573
+
574
+ # Verify calls
575
+ expect(Kernel).to have_received(:warn).with("Warning: Theme setting 'option_2_missing' is missing the 'section' property.")
576
+ expect(Kernel).to have_received(:warn).with("Warning: Theme setting 'option_3_invalid' has an invalid 'section' value: 'bad_section'. Allowed values are: #{valid_sections_string}.")
577
+ expect(Kernel).to have_received(:warn).with("Warning: Theme setting 'option_5_missing' is missing the 'section' property.")
578
+ expect(Kernel).to have_received(:warn).with("Warning: Theme setting 'unknown' is missing the 'section' property.")
579
+ expect(Kernel).to have_received(:warn).with("Warning: Theme setting 'option_7_invalid' has an invalid 'section' value: 'another_bad_one'. Allowed values are: #{valid_sections_string}.")
580
+ end
581
+
582
+ it "handles options without a variable name gracefully when section is invalid" do
583
+ settings = base_settings.merge({
584
+ 'options' => [{
585
+ # 'variable' key is missing
586
+ 'description' => 'Option without variable',
587
+ 'section' => 'invalid_area' # Invalid section to trigger warning
588
+ }]
589
+ })
590
+
591
+ # Stub all necessary methods
592
+ allow(theme).to receive(:settings).and_return(settings)
593
+ allow(theme).to receive(:validate_required_color_settings).and_return(true)
594
+ allow(theme).to receive(:validate_required_layout_attributes).and_return(true)
595
+ allow(theme).to receive(:validate_options_requires).and_return(true)
596
+ allow(theme).to receive(:validate_options_descriptions).and_return(true)
597
+ allow(theme).to receive(:validate_option_defaults).and_return(true)
598
+
599
+ # Stub file checks - this is crucial
600
+ Dugway::Theme::REQUIRED_FILES.each do |file|
601
+ allow(theme).to receive(:read_source_file).with(file).and_return("some content")
602
+ end
603
+
604
+ # Allow warn to be called
605
+ allow(Kernel).to receive(:warn)
606
+
607
+ # Run the validation
608
+ theme.valid?.should be(true)
609
+
610
+ # Check that the warning was issued
611
+ expect(Kernel).to have_received(:warn).with("Warning: Theme setting 'unknown' has an invalid 'section' value: 'invalid_area'. Allowed values are: #{valid_sections_string}.")
612
+ end
613
+
614
+ it "handles options without a variable name gracefully when section is missing" do
615
+ settings = base_settings.merge({
616
+ 'options' => [{
617
+ # 'variable' key is missing
618
+ 'description' => 'Option without variable or section'
619
+ # 'section' key is missing
620
+ }]
621
+ })
622
+
623
+ # Stub all necessary methods
624
+ allow(theme).to receive(:settings).and_return(settings)
625
+ allow(theme).to receive(:validate_required_color_settings).and_return(true)
626
+ allow(theme).to receive(:validate_required_layout_attributes).and_return(true)
627
+ allow(theme).to receive(:validate_options_requires).and_return(true)
628
+ allow(theme).to receive(:validate_options_descriptions).and_return(true)
629
+ allow(theme).to receive(:validate_option_defaults).and_return(true)
630
+
631
+ # Stub file checks
632
+ Dugway::Theme::REQUIRED_FILES.each do |file|
633
+ allow(theme).to receive(:read_source_file).with(file).and_return("some content")
634
+ end
635
+
636
+ # Allow warn to be called
637
+ allow(Kernel).to receive(:warn)
638
+
639
+ # Run the validation
640
+ theme.valid?.should be(true)
641
+
642
+ # Check that the warning was issued
643
+ expect(Kernel).to have_received(:warn).with("Warning: Theme setting 'unknown' is missing the 'section' property.")
644
+ end
645
+ end
646
+
647
+ end
648
+
649
+ shared_examples "option value validation" do |value_type|
650
+ context "when type is 'select' with explicit options" do
651
+ let(:options) do
652
+ [{
653
+ 'type' => 'select',
654
+ 'variable' => 'hero_overlay_opacity',
655
+ 'options' => [['Bright', 'bright'], ['Dark', 'dark']],
656
+ value_type => 'bright'
657
+ }]
658
+ end
659
+
660
+ it "passes when #{value_type} matches a valid option" do
661
+ theme.instance_variable_set(:@errors, [])
662
+ theme.send(:validate_option_defaults)
663
+ expect(theme.errors).to be_empty
664
+ end
665
+
666
+ it "flags invalid #{value_type} values" do
667
+ options.first[value_type] = 'invalid'
668
+ theme.instance_variable_set(:@errors, [])
669
+ theme.send(:validate_option_defaults)
670
+ expect(theme.errors).to include("#{value_type.capitalize} 'invalid' is not a valid option for hero_overlay_opacity")
671
+ end
672
+ end
673
+
674
+ context "when type is 'select' with numeric range" do
675
+ let(:options) do
676
+ [{
677
+ 'type' => 'select',
678
+ 'variable' => 'featured_products',
679
+ 'options' => '0..100',
680
+ value_type => 50
681
+ }]
682
+ end
683
+
684
+ it "passes when #{value_type} is within range" do
685
+ theme.instance_variable_set(:@errors, [])
686
+ theme.send(:validate_option_defaults)
687
+ expect(theme.errors).to be_empty
688
+ end
689
+
690
+ it "flags #{value_type} outside the range" do
691
+ options.first[value_type] = 101
692
+ theme.instance_variable_set(:@errors, [])
693
+ theme.send(:validate_option_defaults)
694
+ expect(theme.errors).to include("#{value_type.capitalize} '101' is out of range for featured_products")
695
+ end
696
+ end
697
+
698
+ context "when type is 'boolean'" do
699
+ let(:options) do
700
+ [{
701
+ 'type' => 'boolean',
702
+ 'variable' => 'show_home_page_categories',
703
+ value_type => true
704
+ }]
705
+ end
706
+
707
+ it "passes when #{value_type} is a valid boolean" do
708
+ theme.instance_variable_set(:@errors, [])
709
+ theme.send(:validate_option_defaults)
710
+ expect(theme.errors).to be_empty
711
+ end
712
+
713
+ it "flags non-boolean #{value_type}" do
714
+ options.first[value_type] = 'true'
715
+ theme.instance_variable_set(:@errors, [])
716
+ theme.send(:validate_option_defaults)
717
+ expect(theme.errors).to include("#{value_type.capitalize} 'true' is not a boolean for show_home_page_categories")
718
+ end
719
+ end
720
+
721
+ context "when type is 'select' with product_orders" do
722
+ let(:options) do
723
+ [{
724
+ 'type' => 'select',
725
+ 'variable' => 'related_products_order',
726
+ 'options' => 'product_orders',
727
+ value_type => 'top-selling'
728
+ }]
729
+ end
730
+
731
+ it "passes when #{value_type} matches a valid product_orders value" do
732
+ theme.instance_variable_set(:@errors, [])
733
+ theme.send(:validate_option_defaults)
734
+ expect(theme.errors).to be_empty
735
+ end
736
+
737
+ it "flags invalid #{value_type} values" do
738
+ options.first[value_type] = 'invalid'
739
+ theme.instance_variable_set(:@errors, [])
740
+ theme.send(:validate_option_defaults)
741
+ expect(theme.errors).to include("#{value_type.capitalize} 'invalid' is not a valid option for related_products_order")
742
+ end
743
+ end
744
+ end
745
+
746
+ describe "#validate_option_defaults" do
747
+ let(:settings) { { 'options' => options } }
748
+ before { allow(theme).to receive(:settings).and_return(settings) }
749
+
750
+ it_behaves_like "option value validation", 'default'
751
+ it_behaves_like "option value validation", 'upgrade_default'
296
752
  end
297
753
 
298
754
  def read_file(file_name)