solidus_frontend 2.7.4 → 2.8.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.
Potentially problematic release.
This version of solidus_frontend might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/app/assets/images/favicon.ico +0 -0
- data/app/assets/javascripts/spree/frontend/checkout/coupon-code.js +1 -1
- data/app/assets/stylesheets/spree/frontend/screen.css.scss +3 -0
- data/app/controllers/spree/checkout_controller.rb +19 -0
- data/app/controllers/spree/coupon_codes_controller.rb +35 -0
- data/app/controllers/spree/locale_controller.rb +1 -1
- data/app/controllers/spree/orders_controller.rb +1 -0
- data/app/views/spree/checkout/_coupon_code.html.erb +1 -1
- data/app/views/spree/checkout/_delivery.html.erb +6 -2
- data/app/views/spree/coupon_codes/new.html.erb +6 -0
- data/app/views/spree/orders/_form.html.erb +1 -1
- data/app/views/spree/orders/_line_item.html.erb +3 -5
- data/app/views/spree/orders/edit.html.erb +5 -6
- data/app/views/spree/products/_image.html.erb +4 -1
- data/app/views/spree/products/_thumbnails.html.erb +10 -8
- data/app/views/spree/shared/_image.html.erb +4 -4
- data/app/views/spree/shared/_locale_selector.html.erb +1 -1
- data/app/views/spree/shared/_order_details.html.erb +3 -5
- data/app/views/spree/shared/_products.html.erb +1 -1
- data/config/routes.rb +1 -0
- data/lib/spree/frontend.rb +1 -1
- data/solidus_frontend.gemspec +1 -1
- data/spec/controllers/spree/checkout_controller_spec.rb +50 -6
- data/spec/controllers/spree/orders_controller_ability_spec.rb +0 -3
- data/spec/controllers/spree/orders_controller_spec.rb +4 -0
- data/spec/controllers/spree/products_controller_spec.rb +3 -3
- data/spec/controllers/spree/taxons_controller_spec.rb +1 -1
- data/spec/features/checkout_confirm_insufficient_stock_spec.rb +71 -0
- data/spec/features/checkout_spec.rb +1 -1
- data/spec/features/coupon_code_spec.rb +14 -14
- data/spec/features/promotion_code_invalidation_spec.rb +2 -2
- data/spec/features/quantity_promotions_spec.rb +9 -9
- data/spec/spec_helper.rb +1 -0
- data/spec/support/features/fill_in_with_force.rb +12 -0
- metadata +13 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b78e6782c7aaf5de2799b93c100eb97e8431a5e9b865fcc202886e8c28cc008
|
4
|
+
data.tar.gz: 1e17148b86ae7d09454ecb6f8fe5b4589324d75b35cdc5aa410a33318df27db4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5267cc2ab0c37977c64c5aad4be7643a7bb45fc6368826632c012b3945fd3f1dfe4f47586c8a805c6e229b0317c505f663a44d685b788efae1df9f9784e768bf
|
7
|
+
data.tar.gz: 61d8f2a41bdacb457fd500965e9308996b5f005d049a2ee5a4b012d437b649510509d54ec87be3bbdb7f829e88d03ba5de6001d0614357bef01311c6c5cb1634
|
data/README.md
CHANGED
@@ -11,19 +11,19 @@ Solidus provides a generator to help with copying the right view into your host
|
|
11
11
|
|
12
12
|
Simply call the generator to copy all views into your host app.
|
13
13
|
|
14
|
-
```
|
14
|
+
```bash
|
15
15
|
$ bundle exec rails g solidus:views:override
|
16
16
|
```
|
17
17
|
|
18
18
|
If you only want to copy certain views into your host app, you can provide the `--only` argument:
|
19
19
|
|
20
|
-
```
|
20
|
+
```bash
|
21
21
|
$ bundle exec rails g solidus:views:override --only products/show
|
22
22
|
```
|
23
23
|
|
24
24
|
The argument to `--only` can also be a substring of the name of the view from the `app/views/spree` folder:
|
25
25
|
|
26
|
-
```
|
26
|
+
```bash
|
27
27
|
$ bundle exec rails g solidus:views:override --only product
|
28
28
|
```
|
29
29
|
|
@@ -31,10 +31,12 @@ This will copy all views whose directory or filename contains the string "produc
|
|
31
31
|
|
32
32
|
### Handle upgrades
|
33
33
|
|
34
|
-
After upgrading
|
34
|
+
After upgrading Solidus to a new version run the generator again and follow on screen instructions.
|
35
35
|
|
36
36
|
## Testing
|
37
37
|
|
38
38
|
Run the tests
|
39
39
|
|
40
|
-
|
40
|
+
```bash
|
41
|
+
bundle exec rspec
|
42
|
+
```
|
Binary file
|
@@ -14,7 +14,7 @@ Spree.onCouponCodeApply = function(e) {
|
|
14
14
|
coupon_code: couponCode
|
15
15
|
};
|
16
16
|
var req = Spree.ajax({
|
17
|
-
method:
|
17
|
+
method: 'POST',
|
18
18
|
url: Spree.routes.apply_coupon_code(Spree.current_order_id),
|
19
19
|
data: JSON.stringify(data),
|
20
20
|
contentType: "application/json"
|
@@ -24,6 +24,7 @@ module Spree
|
|
24
24
|
helper 'spree/orders'
|
25
25
|
|
26
26
|
rescue_from Spree::Core::GatewayError, with: :rescue_from_spree_gateway_error
|
27
|
+
rescue_from Spree::Order::InsufficientStock, with: :insufficient_stock_error
|
27
28
|
|
28
29
|
# Updates the order and advances to the next state (when possible.)
|
29
30
|
def update
|
@@ -168,6 +169,7 @@ module Spree
|
|
168
169
|
|
169
170
|
def apply_coupon_code
|
170
171
|
if update_params[:coupon_code].present?
|
172
|
+
Spree::Deprecation.warn('This endpoint is deprecated. Please use `Spree::CouponCodesController#create` endpoint instead.')
|
171
173
|
@order.coupon_code = update_params[:coupon_code]
|
172
174
|
|
173
175
|
handler = PromotionHandler::Coupon.new(@order).apply
|
@@ -232,5 +234,22 @@ module Spree
|
|
232
234
|
def check_authorization
|
233
235
|
authorize!(:edit, current_order, cookies.signed[:guest_token])
|
234
236
|
end
|
237
|
+
|
238
|
+
def insufficient_stock_error
|
239
|
+
packages = @order.shipments.map(&:to_package)
|
240
|
+
if packages.empty?
|
241
|
+
flash[:error] = I18n.t('spree.insufficient_stock_for_order')
|
242
|
+
redirect_to cart_path
|
243
|
+
else
|
244
|
+
availability_validator = Spree::Stock::AvailabilityValidator.new
|
245
|
+
unavailable_items = @order.line_items.reject { |line_item| availability_validator.validate(line_item) }
|
246
|
+
if unavailable_items.any?
|
247
|
+
item_names = unavailable_items.map(&:name).to_sentence
|
248
|
+
flash[:error] = t('spree.inventory_error_flash_for_insufficient_shipment_quantity', unavailable_items: item_names)
|
249
|
+
@order.restart_checkout_flow
|
250
|
+
redirect_to spree.checkout_state_path(@order.state)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
235
254
|
end
|
236
255
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
class CouponCodesController < Spree::StoreController
|
5
|
+
before_action :load_order, only: :create
|
6
|
+
around_action :lock_order, only: :create
|
7
|
+
|
8
|
+
def create
|
9
|
+
authorize! :update, @order, cookies.signed[:guest_token]
|
10
|
+
|
11
|
+
if params[:coupon_code].present?
|
12
|
+
@order.coupon_code = params[:coupon_code]
|
13
|
+
handler = PromotionHandler::Coupon.new(@order).apply
|
14
|
+
|
15
|
+
respond_with(@order) do |format|
|
16
|
+
format.html do
|
17
|
+
if handler.successful?
|
18
|
+
flash[:success] = handler.success
|
19
|
+
redirect_to cart_path
|
20
|
+
else
|
21
|
+
flash.now[:error] = handler.error
|
22
|
+
render 'spree/coupon_codes/new'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def load_order
|
32
|
+
@order = current_order
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -7,7 +7,7 @@ module Spree
|
|
7
7
|
requested_locale = params[:switch_to_locale] || params[:locale]
|
8
8
|
|
9
9
|
if requested_locale && available_locales.map(&:to_s).include?(requested_locale)
|
10
|
-
session[
|
10
|
+
session[set_user_language_locale_key] = requested_locale
|
11
11
|
I18n.locale = requested_locale
|
12
12
|
flash.notice = t('spree.locale_changed')
|
13
13
|
else
|
@@ -122,6 +122,7 @@ module Spree
|
|
122
122
|
|
123
123
|
def apply_coupon_code
|
124
124
|
if order_params[:coupon_code].present?
|
125
|
+
Spree::Deprecation.warn('This endpoint is deprecated. Please use `Spree::CouponCodesController#create` endpoint instead.')
|
125
126
|
@order.coupon_code = order_params[:coupon_code]
|
126
127
|
|
127
128
|
handler = PromotionHandler::Coupon.new(@order).apply
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<div class="coupon-code" data-hook='coupon_code'>
|
2
2
|
<%= form_for order, url: update_checkout_path(order.state) do |form| %>
|
3
3
|
<%= form.label :coupon_code %>
|
4
|
-
<%= form.text_field :coupon_code, placeholder:
|
4
|
+
<%= form.text_field :coupon_code, placeholder: true %>
|
5
5
|
|
6
6
|
<button type="submit" class="button coupon-code-apply-button" id="coupon-code-apply-button">
|
7
7
|
<%= t('spree.apply_code') %>
|
@@ -27,7 +27,9 @@
|
|
27
27
|
<% ship_form.object.manifest.each do |item| %>
|
28
28
|
<tr class="stock-item">
|
29
29
|
<td class="item-image">
|
30
|
-
<%= render 'spree/shared/image',
|
30
|
+
<%= render 'spree/shared/image',
|
31
|
+
image: (item.variant.gallery.images.first || item.variant.product.gallery.images.first),
|
32
|
+
size: :mini %>
|
31
33
|
</td>
|
32
34
|
<td class="item-name"><%= item.variant.name %></td>
|
33
35
|
<td class="item-qty"><%= item.quantity %></td>
|
@@ -75,7 +77,9 @@
|
|
75
77
|
<% @differentiator.missing.each do |variant, quantity| %>
|
76
78
|
<tr class="stock-item">
|
77
79
|
<td class="item-image">
|
78
|
-
<%= render 'spree/shared/image',
|
80
|
+
<%= render 'spree/shared/image',
|
81
|
+
image: (variant.gallery.images.first || variant.product.gallery.images.first),
|
82
|
+
size: :mini %>
|
79
83
|
</td>
|
80
84
|
<td class="item-name"><%= variant.name %></td>
|
81
85
|
<td class="item-qty"><%= quantity %></td>
|
@@ -15,7 +15,7 @@
|
|
15
15
|
</tbody>
|
16
16
|
<% if order.adjustments.nonzero.exists? || order.line_item_adjustments.nonzero.exists? || order.shipment_adjustments.nonzero.exists? || order.shipments.any? %>
|
17
17
|
<tr class="cart-subtotal">
|
18
|
-
<td colspan="4" align='right'><h5><%= t('spree.cart_subtotal', count: order.line_items.sum(:quantity)) %></h5></
|
18
|
+
<td colspan="4" align='right'><h5><%= t('spree.cart_subtotal', count: order.line_items.sum(:quantity)) %></h5></td>
|
19
19
|
<td colspan><h5><%= order.display_item_total %></h5></td>
|
20
20
|
<td></td>
|
21
21
|
</tr>
|
@@ -2,11 +2,9 @@
|
|
2
2
|
<%= order_form.fields_for :line_items, line_item do |item_form| -%>
|
3
3
|
<tr class="<%= cycle('', 'alt') %> line-item">
|
4
4
|
<td class="cart-item-image" data-hook="cart_item_image">
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
<%= link_to(render('spree/shared/image', image: variant.images.first, size: :small), variant.product) %>
|
9
|
-
<% end %>
|
5
|
+
<%= link_to(render('spree/shared/image',
|
6
|
+
image: (variant.gallery.images.first || variant.product.gallery.images.first),
|
7
|
+
size: :small), variant.product) %>
|
10
8
|
</td>
|
11
9
|
<td class="cart-item-description" data-hook="cart_item_description">
|
12
10
|
<h4><%= link_to line_item.name, product_path(variant.product) %></h4>
|
@@ -16,19 +16,16 @@
|
|
16
16
|
<div data-hook="inside_cart_form">
|
17
17
|
|
18
18
|
<div data-hook="cart_items">
|
19
|
-
<%= render 'form', order_form: order_form %>
|
19
|
+
<%= render 'spree/orders/form', order_form: order_form %>
|
20
20
|
</div>
|
21
21
|
|
22
22
|
<div class="links columns sixteen alpha omega" data-hook="cart_buttons">
|
23
|
-
<%=
|
24
|
-
|
25
|
-
<%= t('spree.update') %>
|
26
|
-
<% end %>
|
23
|
+
<%= button_tag t("spree.update"), class: "primary", id: "update-button" %>
|
24
|
+
|
27
25
|
<%= button_tag class: 'button checkout primary', id: 'checkout-link', name: 'checkout' do %>
|
28
26
|
<%= t('spree.checkout') %>
|
29
27
|
<% end %>
|
30
28
|
</div>
|
31
|
-
|
32
29
|
</div>
|
33
30
|
<% end %>
|
34
31
|
</div>
|
@@ -41,6 +38,8 @@
|
|
41
38
|
<%= link_to t('spree.continue_shopping'), products_path, class: 'continue button gray' %>
|
42
39
|
</p>
|
43
40
|
<% end %>
|
41
|
+
|
42
|
+
<%= render template: 'spree/coupon_codes/new' %>
|
44
43
|
</div>
|
45
44
|
|
46
45
|
<% end %>
|
@@ -1,5 +1,8 @@
|
|
1
1
|
<% if defined?(image) && image %>
|
2
2
|
<%= render 'spree/shared/image', image: image, size: :product, itemprop: "image" %>
|
3
3
|
<% else %>
|
4
|
-
<%= render 'spree/shared/image',
|
4
|
+
<%= render 'spree/shared/image',
|
5
|
+
image: @product.gallery.images.first,
|
6
|
+
size: :product,
|
7
|
+
itemprop: "image" %>
|
5
8
|
<% end %>
|
@@ -1,17 +1,19 @@
|
|
1
1
|
<%# no need for thumbnails unless there is more than one image %>
|
2
|
-
<% if
|
2
|
+
<% if @product.gallery.images.size > 1 %>
|
3
3
|
<ul id="product-thumbnails" class="thumbnails inline" data-hook>
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
<% @product.gallery.images.each do |image| %>
|
6
|
+
<% next if image.viewable_id != @product.master.id %>
|
7
|
+
<li class='tmb-all tmb-<%= image.viewable_id %>'>
|
8
|
+
<%= link_to(image_tag(image.url(:mini)), image.url(:product)) %>
|
7
9
|
</li>
|
8
10
|
<% end %>
|
9
11
|
|
10
12
|
<% if @product.has_variants? %>
|
11
|
-
<% @product.
|
12
|
-
<% next if @product.
|
13
|
-
<li class='vtmb tmb-<%=
|
14
|
-
<%= link_to(image_tag(
|
13
|
+
<% @product.gallery.images.each do |image| %>
|
14
|
+
<% next if image.viewable_id == @product.master.id %>
|
15
|
+
<li class='vtmb tmb-<%= image.viewable_id %>'>
|
16
|
+
<%= link_to(image_tag(image.url(:mini)), image.url(:product)) %>
|
15
17
|
</li>
|
16
18
|
<% end %>
|
17
19
|
<% end %>
|
@@ -1,11 +1,11 @@
|
|
1
1
|
<% size ||= :mini %>
|
2
2
|
<% itemprop ||= nil %>
|
3
3
|
|
4
|
-
<% if
|
5
|
-
<% if
|
6
|
-
<%= image_tag
|
4
|
+
<% if image_url = image.try(:url, size) %>
|
5
|
+
<% if image_alt = image.try(:alt) %>
|
6
|
+
<%= image_tag image_url, alt: image_alt, itemprop: itemprop %>
|
7
7
|
<% else %>
|
8
|
-
<%= image_tag
|
8
|
+
<%= image_tag image_url, itemprop: itemprop %>
|
9
9
|
<% end %>
|
10
10
|
<% else %>
|
11
11
|
<span class="image-placeholder <%= size %>"></span>
|
@@ -62,11 +62,9 @@
|
|
62
62
|
<% order.line_items.each do |item| %>
|
63
63
|
<tr data-hook="order_details_line_item_row">
|
64
64
|
<td data-hook="order_item_image">
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
<%= link_to(render('spree/shared/image', image: item.variant.images.first, size: :small), item.variant.product) %>
|
69
|
-
<% end %>
|
65
|
+
<%= link_to(render('spree/shared/image',
|
66
|
+
image: (item.variant.gallery.images.first || item.variant.product.gallery.images.first),
|
67
|
+
size: :small), item.variant.product) %>
|
70
68
|
</td>
|
71
69
|
<td data-hook="order_item_description">
|
72
70
|
<h4><%= item.variant.product.name %></h4>
|
@@ -28,7 +28,7 @@
|
|
28
28
|
<li id="product_<%= product.id %>" class="columns three <%= cycle("alpha", "secondary", "", "omega secondary", name: "classes") %>" data-hook="products_list_item" itemscope itemtype="http://schema.org/Product">
|
29
29
|
<% cache(@taxon.present? ? [I18n.locale, current_pricing_options, @taxon, product] : [I18n.locale, current_pricing_options, product]) do %>
|
30
30
|
<div class="product-image">
|
31
|
-
<%= link_to(render('spree/shared/image', image: product.
|
31
|
+
<%= link_to(render('spree/shared/image', image: product.gallery.images.first, size: :small, itemprop: "image"), url, itemprop: 'url') %>
|
32
32
|
</div>
|
33
33
|
<%= link_to truncate(product.name, length: 50), url, class: 'info', itemprop: "name", title: product.name %>
|
34
34
|
<span itemprop="offers" itemscope itemtype="http://schema.org/Offer">
|
data/config/routes.rb
CHANGED
data/lib/spree/frontend.rb
CHANGED
data/solidus_frontend.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.add_dependency 'font-awesome-rails', '~> 4.0'
|
29
29
|
s.add_dependency 'jquery-rails'
|
30
30
|
s.add_dependency 'kaminari', '~> 1.1'
|
31
|
-
s.add_dependency '
|
31
|
+
s.add_dependency 'sassc-rails'
|
32
32
|
s.add_dependency 'truncate_html', '~> 0.9', '>= 0.9.2'
|
33
33
|
|
34
34
|
s.add_development_dependency 'capybara-accessible'
|
@@ -14,7 +14,6 @@ describe Spree::CheckoutController, type: :controller do
|
|
14
14
|
|
15
15
|
before do
|
16
16
|
allow(controller).to receive_messages try_spree_current_user: user
|
17
|
-
allow(controller).to receive_messages spree_current_user: user
|
18
17
|
allow(controller).to receive_messages current_order: order
|
19
18
|
end
|
20
19
|
|
@@ -90,7 +89,7 @@ describe Spree::CheckoutController, type: :controller do
|
|
90
89
|
order.line_items << FactoryBot.create(:line_item)
|
91
90
|
end
|
92
91
|
|
93
|
-
context "with the order in the cart state" do
|
92
|
+
context "with the order in the cart state", partial_double_verification: false do
|
94
93
|
before do
|
95
94
|
order.update_attributes! user: user
|
96
95
|
order.update_column(:state, "cart")
|
@@ -139,7 +138,7 @@ describe Spree::CheckoutController, type: :controller do
|
|
139
138
|
end
|
140
139
|
end
|
141
140
|
|
142
|
-
context "with the order in the address state" do
|
141
|
+
context "with the order in the address state", partial_double_verification: false do
|
143
142
|
before do
|
144
143
|
order.update_attributes! user: user
|
145
144
|
order.update_columns(ship_address_id: create(:address).id, state: "address")
|
@@ -152,7 +151,7 @@ describe Spree::CheckoutController, type: :controller do
|
|
152
151
|
end
|
153
152
|
end
|
154
153
|
|
155
|
-
context "with a billing and shipping address" do
|
154
|
+
context "with a billing and shipping address", partial_double_verification: false do
|
156
155
|
subject do
|
157
156
|
post :update, params: {
|
158
157
|
state: "address",
|
@@ -184,7 +183,7 @@ describe Spree::CheckoutController, type: :controller do
|
|
184
183
|
# the same thing here.
|
185
184
|
# Perhaps we can just remove 'set_payment_parameters_amount' entirely at
|
186
185
|
# some point?
|
187
|
-
context "when there is a checkout step between payment and confirm" do
|
186
|
+
context "when there is a checkout step between payment and confirm", partial_double_verification: false do
|
188
187
|
before do
|
189
188
|
@old_checkout_flow = Spree::Order.checkout_flow
|
190
189
|
Spree::Order.class_eval do
|
@@ -227,7 +226,7 @@ describe Spree::CheckoutController, type: :controller do
|
|
227
226
|
end
|
228
227
|
end
|
229
228
|
|
230
|
-
context "when in the payment state" do
|
229
|
+
context "when in the payment state", partial_double_verification: false do
|
231
230
|
let(:order) { create(:order_with_line_items) }
|
232
231
|
let(:payment_method) { create(:credit_card_payment_method) }
|
233
232
|
|
@@ -420,6 +419,45 @@ describe Spree::CheckoutController, type: :controller do
|
|
420
419
|
expect(flash[:error]).to eq(I18n.t('spree.payment_processing_failed'))
|
421
420
|
end
|
422
421
|
end
|
422
|
+
|
423
|
+
context "when InsufficientStock error is raised" do
|
424
|
+
before do
|
425
|
+
allow(controller).to receive_messages current_order: order
|
426
|
+
allow(controller).to receive_messages check_authorization: true
|
427
|
+
allow(controller).to receive_messages ensure_sufficient_stock_lines: true
|
428
|
+
end
|
429
|
+
|
430
|
+
context "when the order has no shipments" do
|
431
|
+
let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:address) }
|
432
|
+
|
433
|
+
before do
|
434
|
+
allow(order).to receive_messages shipments: []
|
435
|
+
# Order#next is the tipical failure point here:
|
436
|
+
allow(order).to receive(:next).and_raise(Spree::Order::InsufficientStock)
|
437
|
+
end
|
438
|
+
|
439
|
+
it "redirects the customer to the cart page with an error message" do
|
440
|
+
put :update, params: { state: order.state, order: {} }
|
441
|
+
expect(flash[:error]).to eq(I18n.t('spree.insufficient_stock_for_order'))
|
442
|
+
expect(response).to redirect_to(spree.cart_path)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
context "when the order has shipments" do
|
447
|
+
let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }
|
448
|
+
|
449
|
+
context "when items become somehow not available anymore" do
|
450
|
+
before { Spree::StockItem.update_all backorderable: false }
|
451
|
+
|
452
|
+
it "redirects the customer to the address checkout page with an error message" do
|
453
|
+
put :update, params: { state: order.state, order: {} }
|
454
|
+
error = I18n.t('spree.inventory_error_flash_for_insufficient_shipment_quantity', unavailable_items: order.products.first.name)
|
455
|
+
expect(flash[:error]).to eq(error)
|
456
|
+
expect(response).to redirect_to(spree.checkout_state_path(state: :address))
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
end
|
423
461
|
end
|
424
462
|
|
425
463
|
context "When last inventory item has been purchased" do
|
@@ -501,6 +539,8 @@ describe Spree::CheckoutController, type: :controller do
|
|
501
539
|
let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: nil, success: 'Coupon Applied!') }
|
502
540
|
|
503
541
|
it "continues checkout flow normally" do
|
542
|
+
expect(Spree::Deprecation).to receive(:warn)
|
543
|
+
|
504
544
|
expect(Spree::PromotionHandler::Coupon)
|
505
545
|
.to receive_message_chain(:new, :apply)
|
506
546
|
.and_return(promotion_handler)
|
@@ -515,6 +555,8 @@ describe Spree::CheckoutController, type: :controller do
|
|
515
555
|
let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: 'Some error', success: false) }
|
516
556
|
|
517
557
|
it "setups the current step correctly before rendering" do
|
558
|
+
expect(Spree::Deprecation).to receive(:warn)
|
559
|
+
|
518
560
|
expect(Spree::PromotionHandler::Coupon)
|
519
561
|
.to receive_message_chain(:new, :apply)
|
520
562
|
.and_return(promotion_handler)
|
@@ -524,6 +566,8 @@ describe Spree::CheckoutController, type: :controller do
|
|
524
566
|
end
|
525
567
|
|
526
568
|
it "render cart with coupon error" do
|
569
|
+
expect(Spree::Deprecation).to receive(:warn)
|
570
|
+
|
527
571
|
expect(Spree::PromotionHandler::Coupon)
|
528
572
|
.to receive_message_chain(:new, :apply)
|
529
573
|
.and_return(promotion_handler)
|
@@ -7,8 +7,6 @@ module Spree
|
|
7
7
|
ORDER_TOKEN = 'ORDER_TOKEN'
|
8
8
|
|
9
9
|
let!(:store) { create(:store) }
|
10
|
-
let(:user) { create(:user) }
|
11
|
-
let(:guest_user) { create(:user) }
|
12
10
|
let(:order) { Spree::Order.create }
|
13
11
|
let(:variant) { create(:variant) }
|
14
12
|
|
@@ -21,7 +19,6 @@ module Spree
|
|
21
19
|
|
22
20
|
before do
|
23
21
|
allow(controller).to receive_messages current_order: order
|
24
|
-
allow(controller).to receive_messages spree_current_user: user
|
25
22
|
end
|
26
23
|
|
27
24
|
context '#populate' do
|
@@ -149,6 +149,8 @@ describe Spree::OrdersController, type: :controller do
|
|
149
149
|
let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: nil, success: 'Coupon Applied!') }
|
150
150
|
|
151
151
|
it "continues checkout flow normally" do
|
152
|
+
expect(Spree::Deprecation).to receive(:warn)
|
153
|
+
|
152
154
|
expect(Spree::PromotionHandler::Coupon)
|
153
155
|
.to receive_message_chain(:new, :apply)
|
154
156
|
.and_return(promotion_handler)
|
@@ -163,6 +165,8 @@ describe Spree::OrdersController, type: :controller do
|
|
163
165
|
let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: 'Some error', success: false) }
|
164
166
|
|
165
167
|
it "render cart with coupon error" do
|
168
|
+
expect(Spree::Deprecation).to receive(:warn)
|
169
|
+
|
166
170
|
expect(Spree::PromotionHandler::Coupon)
|
167
171
|
.to receive_message_chain(:new, :apply)
|
168
172
|
.and_return(promotion_handler)
|
@@ -7,7 +7,7 @@ describe Spree::ProductsController, type: :controller do
|
|
7
7
|
|
8
8
|
# Regression test for https://github.com/spree/spree/issues/1390
|
9
9
|
it "allows admins to view non-active products" do
|
10
|
-
allow(controller).to receive_messages
|
10
|
+
allow(controller).to receive_messages try_spree_current_user: mock_model(Spree.user_class, has_spree_role?: true, last_incomplete_spree_order: nil, spree_api_key: 'fake')
|
11
11
|
get :show, params: { id: product.to_param }
|
12
12
|
expect(response.status).to eq(200)
|
13
13
|
end
|
@@ -20,7 +20,7 @@ describe Spree::ProductsController, type: :controller do
|
|
20
20
|
|
21
21
|
it "should provide the current user to the searcher class" do
|
22
22
|
user = mock_model(Spree.user_class, last_incomplete_spree_order: nil, spree_api_key: 'fake')
|
23
|
-
allow(controller).to receive_messages
|
23
|
+
allow(controller).to receive_messages try_spree_current_user: user
|
24
24
|
expect_any_instance_of(Spree::Config.searcher_class).to receive(:current_user=).with(user)
|
25
25
|
get :index
|
26
26
|
expect(response.status).to eq(200)
|
@@ -29,7 +29,7 @@ describe Spree::ProductsController, type: :controller do
|
|
29
29
|
# Regression test for https://github.com/spree/spree/issues/2249
|
30
30
|
it "doesn't error when given an invalid referer" do
|
31
31
|
current_user = mock_model(Spree.user_class, has_spree_role?: true, last_incomplete_spree_order: nil, generate_spree_api_key!: nil)
|
32
|
-
allow(controller).to receive_messages
|
32
|
+
allow(controller).to receive_messages try_spree_current_user: current_user
|
33
33
|
request.env['HTTP_REFERER'] = "not|a$url"
|
34
34
|
|
35
35
|
# Previously a URI::InvalidURIError exception was being thrown
|
@@ -6,7 +6,7 @@ describe Spree::TaxonsController, type: :controller do
|
|
6
6
|
it "should provide the current user to the searcher class" do
|
7
7
|
taxon = create(:taxon, permalink: "test")
|
8
8
|
user = mock_model(Spree.user_class, last_incomplete_spree_order: nil, spree_api_key: 'fake')
|
9
|
-
allow(controller).to receive_messages
|
9
|
+
allow(controller).to receive_messages try_spree_current_user: user
|
10
10
|
expect_any_instance_of(Spree::Config.searcher_class).to receive(:current_user=).with(user)
|
11
11
|
get :show, params: { id: taxon.permalink }
|
12
12
|
expect(response.status).to eq(200)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Checkout confirm page submission", type: :feature do
|
6
|
+
include_context 'checkout setup'
|
7
|
+
|
8
|
+
context "when the product from the order is not backorderable but has enough stock quantity" do
|
9
|
+
let(:user) { create(:user) }
|
10
|
+
|
11
|
+
let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }
|
12
|
+
let(:order_product) { order.products.first }
|
13
|
+
let(:order_stock_item) { order_product.stock_items.first }
|
14
|
+
|
15
|
+
before do
|
16
|
+
order_stock_item.update! backorderable: false
|
17
|
+
order_stock_item.set_count_on_hand(1)
|
18
|
+
allow_any_instance_of(Spree::CheckoutController).to receive_messages(current_order: order)
|
19
|
+
allow_any_instance_of(Spree::CheckoutController).to receive_messages(try_spree_current_user: user)
|
20
|
+
allow_any_instance_of(Spree::OrdersController).to receive_messages(try_spree_current_user: user)
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when there are not other backorderable stock locations' do
|
24
|
+
context 'when the customer is on the confirm page and the availabilty drops to zero' do
|
25
|
+
before do
|
26
|
+
visit spree.checkout_state_path(:confirm)
|
27
|
+
order_stock_item.set_count_on_hand(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'redirects to cart page and shows an unavailable product message' do
|
31
|
+
click_button "Place Order"
|
32
|
+
expect(page).to have_content "#{order_product.name} became unavailable"
|
33
|
+
expect(page).to have_current_path spree.cart_path
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when there is another backorderable stock location' do
|
39
|
+
before do
|
40
|
+
create :stock_location, backorderable_default: true, default: false
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when the customer is on the confirm page and the availabilty drops to zero' do
|
44
|
+
before do
|
45
|
+
visit spree.checkout_state_path(:confirm)
|
46
|
+
order_stock_item.set_count_on_hand(0)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "redirects to the address checkout page and shows an availability changed message" do
|
50
|
+
click_button "Place Order"
|
51
|
+
error_message = "Quantity selected of #{order_product.name} is not available. Still, items may be available from another stock location, please try again."
|
52
|
+
expect(page).to have_content error_message
|
53
|
+
expect(page).to have_current_path spree.checkout_state_path(:address)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can still complete the order using the backorderable stock location by restarting the checkout" do
|
57
|
+
click_button "Place Order"
|
58
|
+
expect(page).to have_current_path spree.checkout_state_path(:address)
|
59
|
+
click_button "Save and Continue"
|
60
|
+
expect(page).to have_current_path spree.checkout_state_path(:delivery)
|
61
|
+
click_button "Save and Continue"
|
62
|
+
expect(page).to have_current_path spree.checkout_state_path(:payment)
|
63
|
+
click_button "Save and Continue"
|
64
|
+
expect(page).to have_current_path spree.checkout_state_path(:confirm)
|
65
|
+
click_button "Place Order"
|
66
|
+
expect(page).to have_content 'Your order has been processed successfully'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -350,7 +350,7 @@ describe "Checkout", type: :feature, inaccessible: true do
|
|
350
350
|
choose "use_existing_card_no"
|
351
351
|
|
352
352
|
fill_in "Name on card", with: 'Spree Commerce'
|
353
|
-
|
353
|
+
fill_in_with_force "Card Number", with: '4111 1111 1111 1111'
|
354
354
|
fill_in "card_expiry", with: '04 / 20'
|
355
355
|
fill_in "Card Code", with: '123'
|
356
356
|
|
@@ -130,22 +130,22 @@ describe "Coupon code promotions", type: :feature, js: true do
|
|
130
130
|
end
|
131
131
|
|
132
132
|
it "can enter a coupon code and receives success notification" do
|
133
|
-
fill_in "
|
134
|
-
click_button "
|
133
|
+
fill_in "coupon_code", with: "onetwo"
|
134
|
+
click_button "Apply Code"
|
135
135
|
expect(page).to have_content(I18n.t('spree.coupon_code_applied'))
|
136
136
|
end
|
137
137
|
|
138
138
|
it "can enter a promotion code with both upper and lower case letters" do
|
139
|
-
fill_in "
|
140
|
-
click_button "
|
139
|
+
fill_in "coupon_code", with: "ONETwO"
|
140
|
+
click_button "Apply Code"
|
141
141
|
expect(page).to have_content(I18n.t('spree.coupon_code_applied'))
|
142
142
|
end
|
143
143
|
|
144
144
|
it "informs the user about a coupon code which has exceeded its usage" do
|
145
145
|
expect_any_instance_of(Spree::Promotion).to receive(:usage_limit_exceeded?).and_return(true)
|
146
146
|
|
147
|
-
fill_in "
|
148
|
-
click_button "
|
147
|
+
fill_in "coupon_code", with: "onetwo"
|
148
|
+
click_button "Apply Code"
|
149
149
|
expect(page).to have_content(I18n.t('spree.coupon_code_max_usage'))
|
150
150
|
end
|
151
151
|
|
@@ -160,8 +160,8 @@ describe "Coupon code promotions", type: :feature, js: true do
|
|
160
160
|
specify do
|
161
161
|
visit spree.cart_path
|
162
162
|
|
163
|
-
fill_in "
|
164
|
-
click_button "
|
163
|
+
fill_in "coupon_code", with: "onetwo"
|
164
|
+
click_button "Apply Code"
|
165
165
|
expect(page).to have_content(I18n.t(:item_total_less_than_or_equal, scope: [:spree, :eligibility_errors, :messages], amount: "$100.00"))
|
166
166
|
end
|
167
167
|
end
|
@@ -170,8 +170,8 @@ describe "Coupon code promotions", type: :feature, js: true do
|
|
170
170
|
promotion.expires_at = Date.today.beginning_of_week
|
171
171
|
promotion.starts_at = Date.today.beginning_of_week.advance(day: 3)
|
172
172
|
promotion.save!
|
173
|
-
fill_in "
|
174
|
-
click_button "
|
173
|
+
fill_in "coupon_code", with: "onetwo"
|
174
|
+
click_button "Apply Code"
|
175
175
|
expect(page).to have_content(I18n.t('spree.coupon_code_expired'))
|
176
176
|
end
|
177
177
|
|
@@ -191,8 +191,8 @@ describe "Coupon code promotions", type: :feature, js: true do
|
|
191
191
|
click_button "add-to-cart-button"
|
192
192
|
|
193
193
|
visit spree.cart_path
|
194
|
-
fill_in "
|
195
|
-
click_button "
|
194
|
+
fill_in "coupon_code", with: "onetwo"
|
195
|
+
click_button "Apply Code"
|
196
196
|
|
197
197
|
fill_in "order_line_items_attributes_0_quantity", with: 2
|
198
198
|
fill_in "order_line_items_attributes_1_quantity", with: 2
|
@@ -237,8 +237,8 @@ describe "Coupon code promotions", type: :feature, js: true do
|
|
237
237
|
expect(page).to have_content("$30.00")
|
238
238
|
end
|
239
239
|
|
240
|
-
fill_in "
|
241
|
-
click_button "
|
240
|
+
fill_in "coupon_code", with: "onetwo"
|
241
|
+
click_button "Apply Code"
|
242
242
|
|
243
243
|
within '#cart_adjustments' do
|
244
244
|
expect(page).to have_content("Promotion (Onetwo) -$30.00")
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
RSpec.feature "Promotion Code Invalidation" do
|
5
|
+
RSpec.feature "Promotion Code Invalidation", js: true do
|
6
6
|
given!(:promotion) do
|
7
7
|
FactoryBot.create(
|
8
8
|
:promotion_with_item_adjustment,
|
@@ -28,7 +28,7 @@ RSpec.feature "Promotion Code Invalidation" do
|
|
28
28
|
|
29
29
|
scenario "adding the promotion to a cart with two applicable items" do
|
30
30
|
fill_in "Coupon code", with: "PROMO"
|
31
|
-
click_button "
|
31
|
+
click_button "Apply Code"
|
32
32
|
|
33
33
|
expect(page).to have_content("The coupon code was successfully applied to your order")
|
34
34
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
RSpec.feature "Quantity Promotions" do
|
5
|
+
RSpec.feature "Quantity Promotions", js: true do
|
6
6
|
given(:action) do
|
7
7
|
Spree::Promotion::Actions::CreateQuantityAdjustments.create(
|
8
8
|
calculator: calculator,
|
@@ -26,8 +26,8 @@ RSpec.feature "Quantity Promotions" do
|
|
26
26
|
|
27
27
|
scenario "adding and removing items from the cart" do
|
28
28
|
# Attempt to use the code with too few items.
|
29
|
-
fill_in "
|
30
|
-
click_button "
|
29
|
+
fill_in "coupon_code", with: "PROMO"
|
30
|
+
click_button "Apply Code"
|
31
31
|
expect(page).to have_content("This coupon code could not be applied to the cart at this time")
|
32
32
|
|
33
33
|
# Add another item to our cart.
|
@@ -36,8 +36,8 @@ RSpec.feature "Quantity Promotions" do
|
|
36
36
|
click_button "Add To Cart"
|
37
37
|
|
38
38
|
# Using the code should now succeed.
|
39
|
-
fill_in "
|
40
|
-
click_button "
|
39
|
+
fill_in "coupon_code", with: "PROMO"
|
40
|
+
click_button "Apply Code"
|
41
41
|
expect(page).to have_content("The coupon code was successfully applied to your order")
|
42
42
|
within("#cart_adjustments") do
|
43
43
|
expect(page).to have_content("-$10.00")
|
@@ -70,8 +70,8 @@ RSpec.feature "Quantity Promotions" do
|
|
70
70
|
click_button "Update"
|
71
71
|
|
72
72
|
# Apply the promo code and see a $10 discount (for 2 of the 3 items)
|
73
|
-
fill_in "
|
74
|
-
click_button "
|
73
|
+
fill_in "coupon_code", with: "PROMO"
|
74
|
+
click_button "Apply Code"
|
75
75
|
expect(page).to have_content("The coupon code was successfully applied to your order")
|
76
76
|
within("#cart_adjustments") do
|
77
77
|
expect(page).to have_content("-$10.00")
|
@@ -104,8 +104,8 @@ RSpec.feature "Quantity Promotions" do
|
|
104
104
|
click_button "Update"
|
105
105
|
|
106
106
|
# Apply the promo code and see a $15 discount
|
107
|
-
fill_in "
|
108
|
-
click_button "
|
107
|
+
fill_in "coupon_code", with: "PROMO"
|
108
|
+
click_button "Apply Code"
|
109
109
|
expect(page).to have_content("The coupon code was successfully applied to your order")
|
110
110
|
within("#cart_adjustments") do
|
111
111
|
expect(page).to have_content("-$15.00")
|
data/spec/spec_helper.rb
CHANGED
@@ -26,6 +26,7 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
|
26
26
|
|
27
27
|
require 'database_cleaner'
|
28
28
|
|
29
|
+
require 'spree/testing_support/partial_double_verification'
|
29
30
|
require 'spree/testing_support/authorization_helpers'
|
30
31
|
require 'spree/testing_support/capybara_ext'
|
31
32
|
require 'spree/testing_support/factories'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FillInWithForce
|
4
|
+
def fill_in_with_force(locator, with:)
|
5
|
+
field_id = find_field(locator)[:id]
|
6
|
+
page.execute_script "document.getElementById('#{field_id}').value = '#{with}';"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.include FillInWithForce, type: :feature
|
12
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solidus_frontend
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Solidus Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: solidus_api
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
19
|
+
version: 2.8.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.
|
26
|
+
version: 2.8.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: solidus_core
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.
|
33
|
+
version: 2.8.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.
|
40
|
+
version: 2.8.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: canonical-rails
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '1.1'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: sassc-rails
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -195,6 +195,7 @@ files:
|
|
195
195
|
- app/assets/stylesheets/spree/frontend/screen.css.scss
|
196
196
|
- app/controllers/spree/checkout_controller.rb
|
197
197
|
- app/controllers/spree/content_controller.rb
|
198
|
+
- app/controllers/spree/coupon_codes_controller.rb
|
198
199
|
- app/controllers/spree/home_controller.rb
|
199
200
|
- app/controllers/spree/locale_controller.rb
|
200
201
|
- app/controllers/spree/orders_controller.rb
|
@@ -218,6 +219,7 @@ files:
|
|
218
219
|
- app/views/spree/checkout/payment/_check.html.erb
|
219
220
|
- app/views/spree/checkout/payment/_gateway.html.erb
|
220
221
|
- app/views/spree/content/cvv.html.erb
|
222
|
+
- app/views/spree/coupon_codes/new.html.erb
|
221
223
|
- app/views/spree/home/index.html.erb
|
222
224
|
- app/views/spree/layouts/spree_application.html.erb
|
223
225
|
- app/views/spree/orders/_adjustment_row.html.erb
|
@@ -286,6 +288,7 @@ files:
|
|
286
288
|
- spec/features/caching/products_spec.rb
|
287
289
|
- spec/features/caching/taxons_spec.rb
|
288
290
|
- spec/features/cart_spec.rb
|
291
|
+
- spec/features/checkout_confirm_insufficient_stock_spec.rb
|
289
292
|
- spec/features/checkout_spec.rb
|
290
293
|
- spec/features/checkout_unshippable_spec.rb
|
291
294
|
- spec/features/coupon_code_spec.rb
|
@@ -304,6 +307,7 @@ files:
|
|
304
307
|
- spec/helpers/order_helper_spec.rb
|
305
308
|
- spec/helpers/taxon_filters_helper_spec.rb
|
306
309
|
- spec/spec_helper.rb
|
310
|
+
- spec/support/features/fill_in_with_force.rb
|
307
311
|
- spec/support/shared_contexts/checkout_setup.rb
|
308
312
|
- spec/support/shared_contexts/custom_products.rb
|
309
313
|
- spec/support/shared_contexts/locales.rb
|
@@ -328,7 +332,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
328
332
|
version: 1.8.23
|
329
333
|
requirements:
|
330
334
|
- none
|
331
|
-
|
335
|
+
rubyforge_project:
|
336
|
+
rubygems_version: 2.7.3
|
332
337
|
signing_key:
|
333
338
|
specification_version: 4
|
334
339
|
summary: Cart and storefront for the Solidus e-commerce project.
|