Merge pull request #10913 from abdellani/remove-split-checkout-toggle-and-legacy-checkout

Remove split checkout toggle and legacy checkout
This commit is contained in:
Filipe
2023-06-13 17:00:07 +01:00
committed by GitHub
77 changed files with 324 additions and 2345 deletions

View File

@@ -628,11 +628,16 @@ Metrics/BlockLength:
- 'spec/factories/user_factory.rb'
- 'spec/factories/variant_factory.rb'
- 'spec/requests/api/orders_spec.rb'
- 'spec/requests/checkout/failed_checkout_spec.rb'
- 'spec/requests/checkout/stripe_sca_spec.rb'
- 'spec/support/cancan_helper.rb'
- 'spec/support/matchers/select2_matchers.rb'
- 'spec/support/matchers/table_matchers.rb'
- 'spec/swagger_helper.rb'
- 'spec/system/consumer/shopping/checkout_spec.rb'
- 'spec/system/consumer/shopping/checkout_stripe_spec.rb'
- 'spec/system/consumer/shopping/variant_overrides_spec.rb'
- 'spec/system/consumer/split_checkout_tax_not_incl_spec.rb'
# Offense count: 1
# Configuration parameters: CountBlocks, Max.

View File

@@ -1,22 +0,0 @@
angular.module('Darkswarm').controller "AccordionCtrl", ($scope, localStorageService, $timeout, $document, CurrentHub) ->
$scope.accordionSections = ["details", "billing", "shipping", "payment"]
$scope.accordion = { details: true, billing: true, shipping: true, payment: true }
$scope.show = (section) ->
$scope.accordion[section] = true
$scope.scrollTo = (section) ->
# Scrolling is confused by our position:fixed top bar - add an offset to scroll
# to the correct location, plus 5px buffer
offset_height = $("nav.top-bar").height() + 5
$document.scrollTo($("##{section}"), offset_height, 400)
$scope.$on 'purchaseFormInvalid', (event, form) ->
# Scroll to first invalid section
for section in $scope.accordionSections
if not form[section].$valid
$scope.show section
$timeout ->
$scope.scrollTo(section)
, 50
break

View File

@@ -1,12 +0,0 @@
angular.module('Darkswarm').controller "BillingCtrl", ($scope, $timeout, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.name = "billing"
$scope.nextPanel = "shipping"
$scope.summary = ->
[$scope.order.bill_address.address1,
$scope.order.bill_address.city,
$scope.order.bill_address.zipcode]
$timeout $scope.onTimeout

View File

@@ -1,43 +0,0 @@
angular.module('Darkswarm').controller "CheckoutCtrl", ($scope, localStorageService, Checkout, CurrentUser, CurrentHub, $http) ->
$scope.Checkout = Checkout
$scope.submitted = false
# Bind to local storage
$scope.fieldsToBind = ["bill_address", "email", "payment_method_id", "shipping_method_id", "ship_address"]
prefix = "order_#{Checkout.order.id}#{CurrentUser.id or ""}#{CurrentHub.hub.id}"
for field in $scope.fieldsToBind
localStorageService.bind $scope, "Checkout.order.#{field}", Checkout.order[field], "#{prefix}_#{field}"
localStorageService.bind $scope, "Checkout.ship_address_same_as_billing", true, "#{prefix}_sameasbilling"
localStorageService.bind $scope, "Checkout.default_bill_address", false, "#{prefix}_defaultasbilladdress"
localStorageService.bind $scope, "Checkout.default_ship_address", false, "#{prefix}_defaultasshipaddress"
$scope.order = Checkout.order # Ordering is important
$scope.secrets = Checkout.secrets
$scope.enabled = !!CurrentUser.id?
$scope.purchase = (event, form) ->
event.preventDefault()
$scope.formdata = form
$scope.submitted = true
if CurrentUser.id
$scope.validateForm(form)
else
$scope.ensureUserIsGuest()
$scope.validateForm = ->
if $scope.formdata.$valid
$scope.Checkout.purchase()
else
$scope.$broadcast 'purchaseFormInvalid', $scope.formdata
$scope.ensureUserIsGuest = (callback = null) ->
$http.post("/user/registered_email", {email: $scope.order.email})
.then (response)->
window.CableReady.perform(response.data)
.catch ->
$scope.validateForm() if $scope.submitted
callback() if callback

View File

@@ -1,8 +0,0 @@
angular.module('Darkswarm').controller "CountryCtrl", ($scope, availableCountries) ->
$scope.countries = availableCountries
$scope.countriesById = $scope.countries.reduce (obj, country) ->
obj[country.id] = country
obj
, {}

View File

@@ -1,24 +0,0 @@
angular.module('Darkswarm').controller "DetailsCtrl", ($scope, $timeout, $http, CurrentUser, SpreeUser, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.name = "details"
$scope.nextPanel = "billing"
$scope.login_or_next = (event) ->
event.preventDefault()
unless CurrentUser.id
$scope.ensureUserIsGuest($scope.next)
return
$scope.next()
$scope.summary = ->
[$scope.fullName(),
$scope.order.email,
$scope.order.bill_address.phone]
$scope.fullName = ->
[$scope.order.bill_address.firstname ? null,
$scope.order.bill_address.lastname ? null].join(" ").trim()
$timeout $scope.onTimeout

View File

@@ -1,19 +0,0 @@
angular.module('Darkswarm').controller "PaymentCtrl", ($scope, $timeout, savedCreditCards, Dates, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.savedCreditCards = savedCreditCards
$scope.name = "payment"
$scope.months = Dates.months
$scope.years = Dates.years
$scope.secrets.card_month = "1"
$scope.secrets.card_year = moment().year()
for card in savedCreditCards when card.is_default
$scope.secrets.selected_card = card.id
break
$scope.summary = ->
[$scope.Checkout.paymentMethod()?.name]
$timeout $scope.onTimeout

View File

@@ -1,11 +0,0 @@
angular.module('Darkswarm').controller "ShippingCtrl", ($scope, $timeout, ShippingMethods, $controller) ->
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
$scope.ShippingMethods = ShippingMethods
$scope.name = "shipping"
$scope.nextPanel = "payment"
$scope.summary = ->
[$scope.Checkout.shippingMethod()?.name]
$timeout $scope.onTimeout

View File

@@ -1,187 +0,0 @@
# frozen_string_literal: true
require 'open_food_network/address_finder'
class CheckoutController < ::BaseController
include OrderStockCheck
include OrderCompletion
include WhiteLabel
layout 'darkswarm'
helper 'terms_and_conditions'
helper 'checkout'
# We need pessimistic locking to avoid race conditions.
# Otherwise we fail on duplicate indexes or end up with negative stock.
prepend_around_action CurrentOrderLocker, only: [:edit, :update]
prepend_before_action :check_hub_ready_for_checkout
prepend_before_action :check_order_cycle_expiry
prepend_before_action :require_order_cycle
prepend_before_action :require_distributor_chosen
before_action :load_order
before_action :handle_insufficient_stock
before_action :associate_user
before_action :check_authorization
before_action :hide_ofn_navigation, only: :edit
helper 'spree/orders'
def edit; end
def update
params_adapter = Checkout::FormDataAdapter.new(permitted_params, @order, spree_current_user)
return action_failed unless @order.update(params_adapter.params[:order] || {})
checkout_workflow(params_adapter.shipping_method_id)
rescue Spree::Core::GatewayError => e
gateway_error(e)
action_failed(e)
rescue StandardError => e
flash[:error] = I18n.t("checkout.failed")
action_failed(e)
ensure
@order.update_order!
end
private
def check_authorization
authorize!(:edit, current_order, session[:access_token])
end
def load_order
load_checkout_order
return handle_invalid_stock unless valid_order_line_items?
before_address
setup_for_current_state
end
def handle_invalid_stock
reset_order_to_cart
respond_to do |format|
format.html do
redirect_to main_app.cart_path
end
format.json do
render json: { path: main_app.cart_path }, status: :bad_request
end
end
end
def setup_for_current_state
method_name = :"before_#{@order.state}"
__send__(method_name) if respond_to?(method_name, true)
end
def before_address
associate_user
finder = OpenFoodNetwork::AddressFinder.new(@order.email, @order.customer, spree_current_user)
@order.bill_address = finder.bill_address
@order.ship_address = finder.ship_address
end
def before_payment
current_order.payments.destroy_all if request.put?
end
def checkout_workflow(shipping_method_id)
while @order.state != "complete"
if @order.state == "payment"
update_payment_total
return if redirect_to_payment_gateway
return action_failed if @order.errors.any?
return action_failed unless @order.process_payments!
end
next if OrderWorkflow.new(@order).next({ "shipping_method_id" => shipping_method_id })
return action_failed
end
update_response
end
def update_payment_total
@order.update_totals
@order.updater.update_pending_payment
end
def redirect_to_payment_gateway
return unless selected_payment_method.external_gateway?
return unless (redirect_url = selected_payment_method.external_payment_url(order: @order))
render json: { path: redirect_url }, status: :ok
true
end
def selected_payment_method
@selected_payment_method ||= Spree::PaymentMethod.find(
params.dig(:order, :payments_attributes, 0, :payment_method_id)
)
end
def update_response
if order_complete?
processing_succeeded
update_succeeded_response
else
action_failed(RuntimeError.new("Order not complete after the checkout workflow"))
end
end
def order_complete?
@order.state == "complete" || @order.completed?
end
def update_succeeded_response
respond_to do |format|
format.html do
respond_with(@order, location: order_completion_route)
end
format.json do
render json: { path: order_completion_route }, status: :ok
end
end
end
def action_failed(error = RuntimeError.new(order_processing_error))
processing_failed(error)
action_failed_response
end
def action_failed_response
respond_to do |format|
format.html do
render :edit
end
format.json do
discard_flash_errors
render json: { errors: @order.errors, flash: flash.to_hash }.to_json, status: :bad_request
end
end
end
def permitted_params
PermittedAttributes::Checkout.new(params).call
end
def discard_flash_errors
# Marks flash errors for deletion after the current action has completed.
# This ensures flash errors generated during XHR requests are not persisted in the
# session for longer than expected.
flash.discard(:error)
end
end

View File

@@ -13,8 +13,6 @@ module OrderStockCheck
def handle_insufficient_stock
return if sufficient_stock?
reset_order_to_cart
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
redirect_to main_app.cart_path
end
@@ -43,10 +41,4 @@ module OrderStockCheck
def sufficient_stock?
@sufficient_stock ||= @order.insufficient_stock_lines.blank?
end
def reset_order_to_cart
return if OpenFoodNetwork::FeatureToggle.enabled? :split_checkout, spree_current_user
OrderCheckoutRestart.new(@order).call
end
end

View File

@@ -29,11 +29,11 @@ module PaymentGateways
flash[:error] =
Spree.t('flash.generic_error', scope: 'paypal',
reasons: pp_response.errors.map(&:long_message).join(" "))
redirect_to main_app.checkout_state_path(:payment)
redirect_to main_app.checkout_step_path(:payment)
end
rescue SocketError
flash[:error] = Spree.t('flash.connection_failed', scope: 'paypal')
redirect_to main_app.checkout_state_path(:payment)
redirect_to main_app.checkout_step_path(:payment)
end
end

View File

@@ -78,7 +78,7 @@ module Spree
format.html do
if params.key?(:checkout)
@order.next_transition.run_callbacks if @order.cart?
redirect_to main_app.checkout_state_path(@order.checkout_steps.first)
redirect_to main_app.checkout_step_path(@order.checkout_steps.first)
elsif @order.complete?
redirect_to main_app.order_path(@order)
else

View File

@@ -89,16 +89,6 @@ module InjectionHelper
render partial: "json/injection_ams", locals: { name: "orderCycleData", json: json }
end
def inject_available_shipping_methods
inject_json_array "shippingMethods", available_shipping_methods,
Api::ShippingMethodSerializer, current_order: current_order
end
def inject_available_payment_methods
inject_json_array "paymentMethods", available_payment_methods,
Api::PaymentMethodSerializer, current_order: current_order
end
def inject_taxons
inject_json_array "taxons", Spree::Taxon.all.to_a, Api::TaxonSerializer
end

View File

@@ -6,16 +6,6 @@ module TermsAndConditionsHelper
rel: "noopener")
end
def render_terms_and_conditions
if platform_terms_required? && distributor_terms_required?
render("checkout/all_terms_and_conditions")
elsif platform_terms_required?
render "checkout/platform_terms_of_service"
elsif distributor_terms_required?
render "checkout/terms_and_conditions"
end
end
def any_terms_required?(distributor)
TermsOfService.required?(distributor)
end

View File

@@ -25,9 +25,7 @@ module Spree
order.update_totals
order.payment_required?
}
go_to_state :confirmation, if: ->(order) {
OpenFoodNetwork::FeatureToggle.enabled? :split_checkout, order.created_by
}
go_to_state :confirmation
go_to_state :complete
end
@@ -319,8 +317,7 @@ module Spree
# Creates new tax charges if there are any applicable rates. If prices already
# include taxes then price adjustments are created instead.
def create_tax_charge!
return if state.in?(["cart", "address", "delivery"]) &&
OpenFoodNetwork::FeatureToggle.enabled?(:split_checkout)
return if state.in?(["cart", "address", "delivery"])
clear_legacy_taxes!

View File

@@ -75,9 +75,9 @@ module Spree
before_transition to: :delivery, do: :create_proposed_shipments
before_transition to: :delivery, do: :ensure_available_shipping_rates
before_transition to: :payment, do: :create_tax_charge!
before_transition to: :confirmation, do: :validate_payment_method!
after_transition to: :payment, do: :create_tax_charge!
after_transition to: :complete, do: :finalize!
after_transition to: :resumed, do: :after_resume
after_transition to: :canceled, do: :after_cancel
@@ -96,14 +96,6 @@ module Spree
end
end
def self.find_transition(options = {})
return nil if options.nil? || !options.include?(:from) || !options.include?(:to)
next_event_transitions.detect do |transition|
transition[options[:from].to_sym] == options[:to].to_sym
end
end
def self.next_event_transitions
@next_event_transitions ||= []
end

View File

@@ -1,85 +0,0 @@
# frozen_string_literal: true
# Adapts checkout form data (params) so that the order can be directly saved to the database
module Checkout
class FormDataAdapter
attr_reader :params, :shipping_method_id
def initialize(params, order, current_user)
@params = params.deep_dup.to_h.with_indifferent_access
@order = order
@current_user = current_user
move_payment_source_to_payment_attributes!
fill_in_card_type
set_amount_in_payments_attributes
construct_saved_card_attributes if @params.dig(:order, :existing_card_id)
@shipping_method_id = @params[:order]&.delete(:shipping_method_id)
end
private
# For payment step, filter order parameters to produce the expected
# nested attributes for a single payment and its source,
# discarding attributes for payment methods other than the one selected
def move_payment_source_to_payment_attributes!
return unless @params[:payment_source].present? &&
payment_source_params = delete_payment_source_params!
@params.dig(:order, :payments_attributes).first[:source_attributes] = payment_source_params
end
# Ensures cc_type is always passed to the model by inferring the type when
# the frontend didn't provide it.
def fill_in_card_type
return unless payment_source_attributes
return if payment_source_attributes.dig(:number).blank?
payment_source_attributes[:cc_type] ||= card_brand(payment_source_attributes[:number])
end
def payment_source_attributes
@payment_source_attributes ||=
@params.dig(:order, :payments_attributes)&.first&.dig(:source_attributes)
end
def card_brand(number)
ActiveMerchant::Billing::CreditCard.brand?(number)
end
def delete_payment_source_params!
@params.delete(:payment_source)[
@params.dig(:order, :payments_attributes).first[:payment_method_id].underscore
]
end
def set_amount_in_payments_attributes
return unless @params.dig(:order, :payments_attributes)
@params.dig(:order, :payments_attributes).first[:amount] = @order.total
end
def construct_saved_card_attributes
existing_card_id = @params[:order].delete(:existing_card_id)
return if existing_card_id.blank?
add_to_payment_attributes(existing_card_id)
@params.dig(:order, :payments_attributes).first.delete :source_attributes
end
def add_to_payment_attributes(existing_card_id)
credit_card = Spree::CreditCard.find(existing_card_id)
if credit_card.try(:user_id).blank? || credit_card.user_id != @current_user.try(:id)
raise Spree::Core::GatewayError, I18n.t(:invalid_credit_card)
end
@params.dig(:order, :payments_attributes).first[:source] = credit_card
end
end
end

View File

@@ -1,25 +0,0 @@
# frozen_string_literal: true
module PermittedAttributes
class Checkout
def initialize(params)
@params = params
end
def call
@params.permit(
order: [
:email, :special_instructions,
:existing_card_id, :shipping_method_id,
{ payments_attributes: [
:payment_method_id,
{ source_attributes: PermittedAttributes::PaymentSource.attributes }
],
ship_address_attributes: PermittedAttributes::Address.attributes,
bill_address_attributes: PermittedAttributes::Address.attributes }
],
payment_source: PermittedAttributes::PaymentSource.attributes
)
end
end
end

View File

@@ -1,17 +0,0 @@
%accordion-heading
.row
.small-8.medium-9.columns
%em
%small
{{ summary() | printArray }}
.small-4.medium-3.columns.text-right
%span.accordion-up
%em
%small
= t :checkout_hide
%i.ofn-i_053-point-up
%span.accordion-down
%em
%small
= t :checkout_expand
%i.ofn-i_052-point-down

View File

@@ -1,4 +0,0 @@
%p
%input{ type: 'checkbox', id: 'accept_terms', ng: { model: "terms_and_conditions_accepted", init: "terms_and_conditions_accepted = #{all_terms_and_conditions_already_accepted?}" } }
%label.small{for: "accept_terms"}
= t('.message_html', terms_and_conditions_link: link_to( t(".terms_and_conditions"), current_order.distributor.terms_and_conditions, target: '_blank'), tos_link: link_to_platform_terms)

View File

@@ -1,2 +0,0 @@
%p.alert-box.info
= t '.message_html', cart: link_to(t('.cart'), "#{main_app.cart_path}#bought-products")

View File

@@ -1,19 +0,0 @@
%section{"ng-show" => "!enabled"}
.row
.small-12.columns.text-center
%h3.pad-top
= t :checkout_headline
.row.pad-top{ "data-controller": "login-modal" }
- if guest_checkout_allowed?
.small-5.columns.text-center
%button.primary.expand{ "data-action": "click->login-modal#call" }
= t :label_login
.small-2.columns.text-center
%p.pad-top= "#{t :action_or}"
.small-5.columns.text-center
%button.neutral-btn.dark.expand{"ng-click" => "enabled = true"}
= t :checkout_as_guest
- else
.small-6.columns.small-centered
%button.primary.expand{ "data-action": "click->login-modal#call" }
= t :label_login

View File

@@ -1,46 +0,0 @@
%fieldset#billing
%ng-form{"ng-controller" => "BillingCtrl", name: "billing"}
%h5{"ng-class" => "{valid: billing.$valid, dirty: billing.$dirty || submitted}"}
%span.right
%label.label.round.alert.right
%i.ofn-i_009-close
%label.label.round.success.right
%i.ofn-i_051-check-big
= t :checkout_billing
%accordion-group{"is-open" => "accordion.billing",
"ng-class" => "{valid: billing.$valid, open: accordion.billing}"}
= render 'checkout/accordion_heading'
- if spree_current_user
.small-12.columns
%label
%input{type: :checkbox, "ng-model" => "Checkout.default_bill_address"}
= t :checkout_default_bill_address
%div{ "ng-controller" => "CountryCtrl" }
= f.fields_for :bill_address, @order.bill_address do |ba|
.row
.small-12.columns
= validated_input t(:address), "order.bill_address.address1", "ofn-focus" => "accordion['billing']"
.row
.small-12.columns
= validated_input t(:address2), "order.bill_address.address2", required: false
.row
.small-6.columns
= validated_input t(:city), "order.bill_address.city"
.small-6.columns
= validated_select t(:state), "order.bill_address.state_id", {}, {"ng-options" => "s.id as s.name for s in countriesById[order.bill_address.country_id].states"}
.row
.small-6.columns
= validated_input t(:postcode), "order.bill_address.zipcode"
.small-6.columns.right
= validated_select t(:country), "order.bill_address.country_id", {}, {"ng-init" => "order.bill_address.country_id = order.bill_address.country_id || #{DefaultCountry.id}", "ng-options" => "c.id as c.name for c in countries"}
.row
.small-12.columns.text-right
%button.primary{"ng-disabled" => "billing.$invalid", "ng-click" => "next($event)"}
= t :next

View File

@@ -1,33 +0,0 @@
%fieldset#details
%ng-form{"ng-controller" => "DetailsCtrl", name: "details"}
%h5{"ng-class" => "{valid: details.$valid, dirty: details.$dirty || submitted}"}
%span.right
%label.label.round.alert.right
%i.ofn-i_009-close
%label.label.round.success.right
%i.ofn-i_051-check-big
= t :checkout_details
%accordion-group{"is-open" => "accordion.details",
"ng-class" => "{valid: details.$valid, open: accordion.details}"}
= render 'checkout/accordion_heading'
.row
.small-6.columns
= validated_input t(:first_name), "order.bill_address.firstname"
.small-6.columns
= validated_input t(:last_name), "order.bill_address.lastname"
.row
.small-6.columns
= validated_input t(:email), 'order.email', type: "email", inputmode: "email", "ofn-focus" => "accordion['details']"
.small-6.columns
= validated_input t(:phone), 'order.bill_address.phone', inputmode: "tel"
.row
.small-12.columns.text-right
%button.primary{"ng-disabled" => "details.$invalid", "ng-click" => "login_or_next($event)"}
= t :next

View File

@@ -1,22 +0,0 @@
- content_for :injection_data do
= inject_available_shipping_methods
= inject_available_payment_methods
= inject_saved_credit_cards
= form_for current_order,
url: order_path(current_order),
html: {name: "checkout",
id: "checkout_form",
novalidate: true,
"ng-submit" => "purchase($event, checkout)"} do |f|
= render "checkout/details", f: f
= render "checkout/billing", f: f
= render "checkout/shipping", f: f
= render "checkout/payment", f: f
= render "checkout/already_ordered", f: f if show_bought_items?
= render_terms_and_conditions
%p
%button.button.primary{ type: :submit, ng: { disabled: "terms_and_conditions_accepted == false || platform_tos_accepted == false" } }
= t :checkout_send
/ {{ checkout.$valid }}

View File

@@ -1,37 +0,0 @@
%fieldset#payment
%ng-form{"ng-controller" => "PaymentCtrl", name: "payment"}
%h5{"ng-class" => "{valid: payment.$valid, dirty: payment.$dirty || submitted}"}
%span.right
%label.label.round.alert.right
%i.ofn-i_009-close
%label.label.round.success.right
%i.ofn-i_051-check-big
= t :checkout_payment
%accordion-group{"is-open" => "accordion.payment",
"ng-class" => "{valid: payment.$valid, open: accordion.payment}"}
= render 'checkout/accordion_heading'
.row
.small-12.medium-12.large-6.columns
- available_payment_methods.each do |method|
.row
.small-12.columns
%label
= radio_button_tag "order[payments_attributes][][payment_method_id]", method.id, false,
required: true,
name: "order.payment_method_id",
"ng-model" => "order.payment_method_id"
= method.name
= "(#{payment_method_price(method, @order)})"
%small.error.medium.input-text{"ng-show" => "!fieldValid('order.payment_method_id')"}
= "{{ fieldErrors('order.payment_method_id') }}"
.row{"ng-if" => "order.payment_method_id == #{method.id}"}
.small-12.columns
= render partial: "spree/checkout/payment/#{method.method_type}", :locals => { :payment_method => method }
.small-12.medium-12.large-6.columns
#distributor_address.panel{"ng-show" => "Checkout.paymentMethod().description"}
%span.pre-wrap {{ Checkout.paymentMethod().description }}

View File

@@ -1,10 +0,0 @@
%p
%input{ type: "checkbox",
id: "accept_terms",
ng: {
model: "platform_tos_accepted",
init: "platform_tos_accepted = #{platform_tos_already_accepted?}"
}
}
%label.small{for: "accept_terms"}
= t(".message_html", tos_link: link_to_platform_terms)

View File

@@ -1,61 +0,0 @@
%fieldset#shipping
%ng-form{"ng-controller" => "ShippingCtrl", name: "shipping"}
%h5{"ng-class" => "{valid: shipping.$valid, dirty: shipping.$dirty || submitted}"}
%span.right
%label.label.round.alert.right
%i.ofn-i_009-close
%label.label.round.success.right
%i.ofn-i_051-check-big
= t :checkout_shipping
%accordion-group{"is-open" => "accordion.shipping",
"ng-class" => "{valid: shipping.$valid, open: accordion.shipping}"}
= render 'checkout/accordion_heading'
.small-12.columns.medium-12.columns.large-6.columns
%label{"ng-repeat" => "method in ShippingMethods.shipping_methods | orderBy: 'name'"}
%input{type: :radio,
required: true,
name: "order.shipping_method_id",
"ng-value" => "method.id",
"ng-model" => "order.shipping_method_id"}
{{ method.name }}
%em.light{"ng-show" => "!method.price || method.price == 0"}
= "(#{t(:checkout_method_free)})"
%em.light{"ng-hide" => "!method.price || method.price == 0"}
({{ method.price | localizeCurrency }})
%small.error.medium.input-text{"ng-show" => "!fieldValid('order.shipping_method_id')"}
= "{{ fieldErrors('order.shipping_method_id') }}"
%label{"ng-if" => "Checkout.requireShipAddress()"}
%input{type: :checkbox, "ng-model" => "Checkout.ship_address_same_as_billing"}
= t :checkout_address_same
- if spree_current_user
%label{"ng-if" => "Checkout.requireShipAddress()"}
%input{type: :checkbox, "ng-model" => "Checkout.default_ship_address"}
= t :checkout_default_ship_address
.small-12.columns.medium-12.columns.large-6.columns
#distributor_address.panel{"ng-show" => "Checkout.shippingMethod().description"}
%span{ style: "white-space: pre-wrap;" }{{ Checkout.shippingMethod().description }}
%br/
%br/
- if @order.order_cycle.pickup_time_for(@order.distributor)
= t :checkout_ready_for
= @order.order_cycle.pickup_time_for(@order.distributor)
= f.fields_for :ship_address, @order.ship_address do |sa|
= render 'checkout/shipping_ship_address'
.row
.small-12.columns
%label{ for: 'order_special_instructions'}= t(:checkout_instructions)
= f.text_area :special_instructions, size: "60x4", "ng-model" => "order.special_instructions"
.row
.small-12.columns.text-right
%button.primary{"ng-disabled" => "shipping.$invalid", "ng-click" => "next($event)"}
= t :next

View File

@@ -1,28 +0,0 @@
.small-12.columns
#ship_address{"ng-if" => "Checkout.requireShipAddress()"}
%div.visible{"ng-if" => "!Checkout.ship_address_same_as_billing"}
%div{ "ng-controller" => "CountryCtrl" }
.row
.small-6.columns
= validated_input t(:first_name), "order.ship_address.firstname", "ofn-focus" => "accordion['shipping']"
.small-6.columns
= validated_input t(:last_name), "order.ship_address.lastname"
.row
.small-12.columns
= validated_input t(:address), "order.ship_address.address1"
.row
.small-12.columns
= validated_input t(:address2), "order.ship_address.address2", required: false
.row
.small-6.columns
= validated_input t(:city), "order.ship_address.city"
.small-6.columns
= validated_select t(:state), "order.ship_address.state_id", {}, {"ng-options" => "s.id as s.name for s in countriesById[order.ship_address.country_id].states"}
.row
.small-6.columns
= validated_input t(:postcode), "order.ship_address.zipcode"
.small-6.columns.right
= validated_select t(:country), "order.ship_address.country_id", {}, {"ng-init" => "order.ship_address.country_id = order.ship_address.country_id || #{DefaultCountry.id}", "ng-options" => "c.id as c.name for c in countries"}
.row
.small-6.columns
= validated_input t(:phone), "order.ship_address.phone"

View File

@@ -1,34 +0,0 @@
%orderdetails
= form_for current_order, url: "#", html: {"ng-submit" => "purchase($event, checkout)"} do |f|
%fieldset
%legend
= t :checkout_your_order
%table
%tr.subtotal
%th
= t :checkout_cart_total
%td.cart-total.text-right= display_checkout_subtotal(@order)
- checkout_adjustments_for(current_order, exclude: [:shipping, :payment, :line_item]).each do |adjustment|
%tr.adjustment
%th= adjustment.label
%td.text-right= adjustment.display_amount.to_html
%tr.shipping
%th
= t :checkout_shipping_price
%td.text-right {{ Checkout.shippingPrice() | localizeCurrency }}
%tr.transaction-fee
%th
= t :payment_method_fee
%td.text-right {{ Checkout.paymentPrice() | localizeCurrency }}
%tr.total
%th
= t :checkout_total_price
%td.text-right {{ Checkout.cartTotal() | localizeCurrency }}
//= f.submit "Purchase", class: "button", "ofn-focus" => "accordion['payment']"
%a.button.secondary{href: main_app.cart_url}
= t :checkout_back_to_cart

View File

@@ -1,3 +0,0 @@
%p
%input{ type: 'checkbox', id: 'accept_terms', ng: { model: "terms_and_conditions_accepted", init: "terms_and_conditions_accepted=#{terms_and_conditions_already_accepted?}" } }
%label.small{for: "accept_terms"}= t('.message_html', terms_and_conditions_link: link_to( t( '.link_text' ), current_order.distributor.terms_and_conditions, target: '_blank'))

View File

@@ -1,36 +0,0 @@
- content_for(:title) do
= t :checkout_title
- content_for :injection_data do
= inject_enterprise_and_relatives
= inject_available_countries
.darkswarm.footer-pad
- content_for :order_cycle_form do
%closing
= t :checkout_now
%p
= t :checkout_order_ready
%strong
= pickup_time current_order_cycle
- content_for :ordercycle_sidebar do
.show-for-large-up.large-4.columns
= render partial: "shopping_shared/order_cycles"
= render partial: "shopping_shared/header"
.sub-header.show-for-medium-down
= render partial: "shopping_shared/order_cycles"
%accordion{"close-others" => "false"}
%checkout.row{"ng-controller" => "CheckoutCtrl"}
.small-12.medium-8.columns
- unless spree_current_user
= render partial: "checkout/authentication"
%div{"ng-show" => "enabled", "ng-controller" => "AccordionCtrl"}
= render partial: "checkout/form"
.small-12.medium-4.columns
= render partial: "checkout/summary"
= render partial: "shared/footer"

View File

@@ -1,27 +0,0 @@
.row
.small-6.columns
%label
= t :first_name
-# Changing name not permitted by default (in checkout) - can be enabled by setting an allow_name_change variable in $scope
%input{type: :text, "ng-disabled" => "!allow_name_change", "ng-value" => "order.bill_address.firstname"}
.small-6.columns
%label
= t :last_name
%input{type: :text, "ng-disabled" => "!allow_name_change", "ng-value" => "order.bill_address.lastname"}
.small-6.columns
= validated_input t(:card_number), "secrets.card_number", maxlength: 19, autocomplete: "off"
.small-6.columns
= validated_input t(:card_securitycode), "secrets.card_verification_value"
.row
.small-12.columns
%label{for: "secrets.card_month"}
= t :card_expiry_date
.row
.small-6.columns
%select{"ng-model" => "secrets.card_month", "ng-options" => "currMonth.value as currMonth.key for currMonth in months", name: "secrets.card_month"}
.small-6.columns
%select{"ng-model" => "secrets.card_year", "ng-options" => "year for year in years", name: "secrets.card_year"}

View File

@@ -1 +0,0 @@
-# This file intentionally overrides the view in the spree_paypal_express gem

View File

@@ -1,22 +0,0 @@
- content_for :injection_data do
- if Stripe.publishable_key
:javascript
angular.module('Darkswarm').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
.row{ "ng-show" => "savedCreditCards.length > 0" }
.small-12.columns
%h6= t('.used_saved_card')
%select{ name: "selected_card", required: false, ng: { model: "secrets.selected_card", options: "card.id as card.formatted for card in savedCreditCards" } }
%option{ value: "" }= "{{ secrets.selected_card ? '#{t('.enter_new_card')}' : '#{t('.choose_one')}' }}"
%h6{ ng: { if: '!secrets.selected_card' } }
= t('.or_enter_new_card')
%div{ ng: { if: '!secrets.selected_card' } }
%stripe-elements
- if spree_current_user
.row
.small-12.columns.text-right
= check_box_tag 'secrets.save_requested_by_customer', '1', false, 'ng-model' => 'secrets.save_requested_by_customer'
= label_tag 'secrets.save_requested_by_customer', t('.remember_this_card')

View File

@@ -1,22 +0,0 @@
- content_for :injection_data do
- if Stripe.publishable_key
:javascript
angular.module('Darkswarm').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
.row{ "ng-show" => "savedCreditCards.length > 0" }
.small-12.columns
%h6= t('.used_saved_card')
%select{ name: "selected_card", required: false, ng: { model: "secrets.selected_card", options: "card.id as card.formatted for card in savedCreditCards" } }
%option{ value: "" }= "{{ secrets.selected_card ? '#{t('.enter_new_card')}' : '#{t('.choose_one')}' }}"
%h6{ ng: { if: '!secrets.selected_card' } }
= t('.or_enter_new_card')
%div{ ng: { if: '!secrets.selected_card' } }
%stripe-elements
- if spree_current_user
.row
.small-12.columns.text-right
= check_box_tag 'secrets.save_requested_by_customer', '1', false, 'ng-model' => 'secrets.save_requested_by_customer'
= label_tag 'secrets.save_requested_by_customer', t('.remember_this_card')

View File

@@ -84,24 +84,15 @@ Openfoodnetwork::Application.routes.draw do
get "/stripe/authorize/:order_number", to: "stripe#authorize", as: :authorize_stripe
end
constraints FeatureToggleConstraint.new(:split_checkout) do
get '/checkout', to: 'split_checkout#edit'
get '/checkout', to: 'split_checkout#edit'
constraints step: /(details|payment|summary)/ do
get '/checkout/:step', to: 'split_checkout#edit', as: :checkout_step
put '/checkout/:step', to: 'split_checkout#update', as: :checkout_update
end
# Redirects to the new checkout for any other 'step' (ie. /checkout/cart from the legacy checkout)
get '/checkout/:other', to: redirect('/checkout')
constraints step: /(details|payment|summary)/ do
get '/checkout/:step', to: 'split_checkout#edit', as: :checkout_step
put '/checkout/:step', to: 'split_checkout#update', as: :checkout_update
end
# When the split_checkout feature is disabled for the current user, use the legacy checkout
constraints FeatureToggleConstraint.new(:split_checkout, negate: true) do
get '/checkout', to: 'checkout#edit'
put '/checkout', to: 'checkout#update', as: :update_checkout
get '/checkout/:state', to: 'checkout#edit', as: :checkout_state
end
# Redirects to the new checkout for any other 'step' (ie. /checkout/cart from the legacy checkout)
get '/checkout/:other', to: redirect('/checkout')
get 'embedded_shopfront/shopfront_session', to: 'application#shopfront_session'
post 'embedded_shopfront/enable', to: 'application#enable_embedded_styles'

View File

@@ -379,6 +379,7 @@ describe Admin::BulkLineItemsController, type: :controller do
order.shipments.map(&:refresh_rates)
order.select_shipping_method(shipping_method.id)
OrderWorkflow.new(order).advance_to_payment
order.finalize!
order.recreate_all_fees!
order.create_tax_charge!

View File

@@ -1,346 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe CheckoutController, type: :controller do
include StripeStubs
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
let(:order_cycle) { create(:simple_order_cycle) }
let(:order) { create(:order) }
before do
allow(order).to receive(:checkout_allowed?).and_return true
allow(controller).to receive(:check_authorization).and_return true
end
it "redirects home when no distributor is selected" do
get :edit
expect(response).to redirect_to root_path
end
it "redirects to the shop when no order cycle is selected" do
allow(controller).to receive(:current_distributor).and_return(distributor)
get :edit
expect(response).to redirect_to shop_path
end
it "redirects to shopfront with message if order cycle is expired" do
allow(controller).to receive(:current_distributor).and_return(distributor)
expect(controller).to receive(:current_order_cycle).and_return(order_cycle).at_least(:once)
expect(controller).to receive(:current_order).and_return(order).at_least(:once)
expect(order_cycle).to receive(:closed?).and_return(true)
expect(order).to receive(:empty!)
expect(order).to receive(:set_order_cycle!).with(nil)
get :edit
expect(response).to redirect_to shop_url
expect(flash[:info]).to eq 'The order cycle you\'ve selected has just closed. Please try again!'
end
it "redirects home with message if hub is not ready for checkout" do
allow(distributor).to receive(:ready_for_checkout?) { false }
allow(order).to receive_messages(distributor: distributor, order_cycle: order_cycle)
allow(controller).to receive(:current_order).and_return(order)
expect(order).to receive(:empty!)
expect(order).to receive(:set_distribution!).with(nil, nil)
get :edit
expect(response).to redirect_to root_url
expect(flash[:info]).to eq('The hub you have selected is temporarily closed for orders. Please try again later.')
end
describe "#update" do
let(:user) { order.user }
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
let(:order_cycle) { create(:order_cycle, distributors: [distributor]) }
let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) }
let(:payment_method) { distributor.payment_methods.first }
let(:shipping_method) { distributor.shipping_methods.first }
before do
order.contents.add(order_cycle.variants_distributed_by(distributor).first)
allow(controller).to receive(:current_distributor).and_return(distributor)
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
allow(controller).to receive(:current_order).and_return(order)
allow(controller).to receive(:spree_current_user).and_return(user)
user.bill_address = create(:address)
user.ship_address = create(:address)
user.save!
end
it "completes the order and redirects to the order confirmation page" do
params = {
"order" => {
"bill_address_attributes" => order.bill_address.attributes,
"default_bill_address" => false,
"default_ship_address" => false,
"email" => user.email,
"payments_attributes" => [{ "payment_method_id" => payment_method.id }],
"ship_address_attributes" => order.bill_address.attributes,
"shipping_method_id" => shipping_method.id
}
}
expect { post :update, params: params }.
to change { Customer.count }.by(1)
expect(order.completed?).to be true
expect(response).to redirect_to order_path(order, order_token: order.token)
end
end
describe "running out of stock" do
let(:order_cycle_distributed_variants) { double(:order_cycle_distributed_variants) }
before do
allow(controller).to receive(:current_order).and_return(order)
allow(order).to receive(:distributor).and_return(distributor)
order.update(order_cycle: order_cycle)
allow(OrderCycleDistributedVariants).to receive(:new).and_return(order_cycle_distributed_variants)
end
context "handling stock issues" do
it "redirects when some items are out of stock" do
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return false
get :edit
expect(response).to redirect_to cart_path
end
it "redirects when some items are not available" do
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return true
expect(order_cycle_distributed_variants).to receive(:distributes_order_variants?).with(order).and_return(false)
get :edit
expect(response).to redirect_to cart_path
end
end
end
describe "building the order" do
before do
allow(controller).to receive(:current_distributor).and_return(distributor)
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
allow(controller).to receive(:current_order).and_return(order)
end
it "set shipping_address_from_distributor when re-rendering edit" do
expect(order.updater).to receive(:shipping_address_from_distributor)
allow(order).to receive(:update).and_return false
spree_post :update, format: :json, order: {}
end
it "set shipping_address_from_distributor when the order state cannot be advanced" do
expect(order.updater).to receive(:shipping_address_from_distributor)
allow(order).to receive(:update).and_return true
allow(order).to receive(:next).and_return false
spree_post :update, format: :json, order: {}
end
context "#update with shipping_method_id" do
let(:test_shipping_method_id) { "111" }
before do
allow(controller).to receive(:order_completion_reset)
allow(order).to receive(:update).and_return true
allow(controller).to receive(:current_order).and_return order
# make order workflow pass through delivery
allow(order).to receive(:next).twice do
if order.state == 'cart'
order.update_column :state, 'delivery'
else
order.update_column :state, 'complete'
end
end
end
it "does not fail to update" do
expect(controller).to_not receive(:clear_ship_address)
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
end
it "does not send shipping_method_id to the order model as an attribute" do
expect(order).to receive(:update).with({})
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
end
it "selects the shipping_method in the order" do
expect(order).to receive(:select_shipping_method).with(test_shipping_method_id)
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
end
end
context 'when completing the order' do
before do
order.state = 'complete'
order.save!
allow(order).to receive(:update).and_return(true)
allow(order).to receive(:next).and_return(true)
allow(order).to receive(:set_distributor!).and_return(true)
end
it "sets the new order's token to the same as the old order" do
order = controller.current_order(true)
spree_post :update, order: {}
expect(controller.current_order.token).to eq order.token
end
it 'expires the current order' do
allow(controller).to receive(:expire_current_order)
put :update, params: { order: {} }
expect(controller).to have_received(:expire_current_order)
end
it 'sets the access_token of the session' do
put :update, params: { order: {} }
expect(session[:access_token]).to eq(controller.current_order.token)
end
end
end
describe '#expire_current_order' do
it 'empties the order_id of the session' do
expect(session).to receive(:[]=).with(:order_id, nil)
controller.send(:expire_current_order)
end
it 'resets the @current_order ivar' do
controller.send(:expire_current_order)
expect(controller.instance_variable_get(:@current_order)).to be_nil
end
end
context "via xhr" do
before do
allow(controller).to receive(:current_distributor).and_return(distributor)
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
allow(controller).to receive(:current_order).and_return(order)
end
it "returns errors and flash if order.update fails" do
spree_post :update, format: :json, order: {}
expect(response.status).to eq(400)
expect(response.body).to eq({ errors: assigns[:order].errors,
flash: { error: order.errors.full_messages.to_sentence } }.to_json)
end
it "returns errors and flash if order.next fails" do
allow(order).to receive(:update).and_return true
allow(order).to receive(:next).and_return false
spree_post :update, format: :json, order: {}
expect(response.body).to eq({ errors: assigns[:order].errors,
flash: { error: "Payment could not be processed, please check the details you entered" } }.to_json)
end
it "returns order confirmation url on success" do
expect(controller).to receive(:expire_current_order)
expect(controller).to receive(:build_new_order).with(order.distributor, order.token)
allow(order).to receive(:update).and_return true
allow(order).to receive(:state).and_return "complete"
spree_post :update, format: :json, order: {}
expect(response.status).to eq(200)
expect(response.body).to eq({ path: order_path(order, order_token: order.token) }.to_json)
end
it "returns an error on unexpected failure" do
allow(order).to receive(:update).and_raise
spree_post :update, format: :json, order: {}
expect(response.status).to eq(400)
expect(response.body).to eq({ errors: {},
flash: { error: 'The checkout failed. Please let us know so that we can process your order.' } }.to_json)
end
it "returns a specific error on Spree::Core::GatewayError" do
allow(order).to receive(:update).and_raise(Spree::Core::GatewayError.new("Gateway blow up"))
spree_post :update, format: :json, order: {}
expect(response.status).to eq(400)
flash_message = "There was a problem with your payment information: %s" % 'Gateway blow up'
expect(json_response["flash"]["error"]).to eq flash_message
end
describe "stale object handling" do
it "retries when a stale object error is encountered" do
expect(controller).to receive(:expire_current_order)
expect(controller).to receive(:build_new_order).with(order.distributor, order.token)
allow(order).to receive(:update).and_return true
allow(controller).to receive(:state_callback)
# The first time, raise a StaleObjectError. The second time, succeed.
allow(order).to receive(:next).once.
and_raise(ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update'))
allow(order).to receive(:next).once do
order.update_column :state, 'complete'
true
end
spree_post :update, format: :json, order: {}
expect(response.status).to eq(200)
end
it "tries a maximum of 3 times before giving up and returning an error" do
allow(order).to receive(:update).and_return true
allow(order).to receive(:next) {
raise ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update')
}
spree_post :update, format: :json, order: {}
expect(response.status).to eq(400)
end
end
end
describe "Payment redirects" do
before do
allow(controller).to receive(:current_distributor) { distributor }
allow(controller).to receive(:current_order_cycle) { order_cycle }
allow(controller).to receive(:current_order) { order }
allow(order).to receive(:update) { true }
allow(order).to receive(:state) { "payment" }
end
describe "redirecting to an external payment gateway" do
let(:payment_method) { create(:payment_method) }
it "should call Stripe redirect and redirect if a path is provided" do
expect(Spree::PaymentMethod).to receive(:find).and_return(payment_method)
expect(payment_method).to receive(:external_gateway?).and_return(true)
expect(payment_method).to receive(:external_payment_url).and_return("test_path")
spree_post :update,
order: { payments_attributes: [{ payment_method_id: payment_method.id }] }
expect(response.body).to eq({ path: "test_path" }.to_json)
end
end
end
describe "#action_failed" do
let(:restart_checkout) { instance_double(OrderCheckoutRestart, call: true) }
before do
controller.instance_variable_set(:@order, order)
allow(OrderCheckoutRestart).to receive(:new) { restart_checkout }
allow(controller).to receive(:current_order) { order }
end
it "set shipping_address_from_distributor and restarts the checkout" do
expect(order.updater).to receive(:shipping_address_from_distributor)
expect(restart_checkout).to receive(:call)
expect(controller).to receive(:respond_to)
controller.send(:action_failed)
end
end
end

View File

@@ -88,8 +88,8 @@ module PaymentGateways
context "when processing fails" do
let(:response) { false }
it "redirects to checkout_state_path with a flash error" do
expect(post(:express)).to redirect_to checkout_state_path(:payment)
it "redirects to checkout_step_path with a flash error" do
expect(post(:express)).to redirect_to checkout_step_path(:payment)
expect(flash[:error]).to eq "PayPal failed. "
end
end
@@ -99,8 +99,8 @@ module PaymentGateways
allow(response_mock).to receive(:success?).and_raise(SocketError)
end
it "redirects to checkout_state_path with a flash error" do
expect(post(:express)).to redirect_to checkout_state_path(:payment)
it "redirects to checkout_step_path with a flash error" do
expect(post(:express)).to redirect_to checkout_step_path(:payment)
expect(flash[:error]).to eq "Could not connect to PayPal."
end
end

View File

@@ -74,8 +74,6 @@ Please try again!"
context "using split checkout" do
before do
Flipper.enable(:split_checkout)
order.update_attribute :state, "confirmation"
end

View File

@@ -16,8 +16,6 @@ describe SplitCheckoutController, type: :controller do
let(:shipping_method) { distributor.shipping_methods.first }
before do
Flipper.enable(:split_checkout)
exchange.variants << order.line_items.first.variant
allow(controller).to receive(:current_order) { order }
allow(controller).to receive(:spree_current_user) { user }

View File

@@ -37,8 +37,6 @@ describe InjectionHelper, type: :helper do
order = create(:order, distributor: current_distributor)
allow(helper).to receive(:current_order) { order }
allow(helper).to receive(:spree_current_user) { nil }
expect(helper.inject_available_shipping_methods).to match sm.id.to_s
expect(helper.inject_available_shipping_methods).to match sm.compute_amount(order).to_s
end
it "injects payment methods" do
@@ -47,8 +45,6 @@ describe InjectionHelper, type: :helper do
order = create(:order, distributor: current_distributor)
allow(helper).to receive(:current_order) { order }
allow(helper).to receive(:spree_current_user) { nil }
expect(helper.inject_available_payment_methods).to match pm.id.to_s
expect(helper.inject_available_payment_methods).to match pm.name
end
it "injects current order" do

View File

@@ -1,24 +0,0 @@
describe "AccordionCtrl", ->
ctrl = null
scope = null
CurrentHubMock =
hub:
id: 1
beforeEach ->
module "Darkswarm"
module ($provide)->
$provide.value "CurrentHub", CurrentHubMock
null
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
scope.order =
id: 129
ctrl = $controller 'AccordionCtrl', {$scope: scope}
it "defaults the details accordion to visible", ->
expect(scope.accordion.details).toEqual true
it "changes accordion", ->
scope.show "shipping"
expect(scope.accordion["shipping"]).toEqual true

View File

@@ -1,100 +0,0 @@
describe "CheckoutCtrl", ->
ctrl = null
scope = null
Checkout = null
CurrentUser = null
CurrentHubMock =
hub:
id: 1
localStorageService = null
beforeEach ->
module("Darkswarm")
angular.module('Darkswarm').value('user', {})
angular.module('Darkswarm').value('currentHub', {id: 1})
module ($provide)->
$provide.value "CurrentHub", CurrentHubMock
null
Checkout =
purchase: ->
submit: ->
navigate: ->
bindFieldsToLocalStorage: ->
order:
id: 1
email: "public"
user_id: 1
bill_address: 'bill_address'
ship_address: 'ship address'
secrets:
card_number: "this is a secret"
describe "with user", ->
beforeEach ->
inject ($controller, $rootScope, _localStorageService_) ->
localStorageService = _localStorageService_
spyOn(localStorageService, "bind").and.callThrough()
scope = $rootScope.$new()
CurrentUser = { id: 1 }
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Checkout: Checkout, CurrentUser: CurrentUser }
describe "submitting", ->
event =
preventDefault: ->
beforeEach ->
spyOn(Checkout, "purchase")
scope.submitted = false
it "delegates to the service when valid", ->
scope.purchase(event, {$valid: true})
expect(Checkout.purchase).toHaveBeenCalled()
expect(scope.submitted).toBe(true)
it "does nothing when invalid", ->
scope.purchase(event, {$valid: false})
expect(Checkout.purchase).not.toHaveBeenCalled()
expect(scope.submitted).toBe(true)
it "is enabled", ->
expect(scope.enabled).toEqual true
describe "Local storage", ->
it "binds to localStorage when given a scope", inject ($timeout) ->
prefix = "order_#{scope.order.id}#{CurrentUser.id or ""}#{CurrentHubMock.hub.id}"
field = scope.fieldsToBind[0]
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.order.#{field}", Checkout.order[field], "#{prefix}_#{field}")
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.ship_address_same_as_billing", true, "#{prefix}_sameasbilling")
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.default_bill_address", false, "#{prefix}_defaultasbilladdress")
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.default_ship_address", false, "#{prefix}_defaultasshipaddress")
it "it can retrieve data from localstorage", ->
prefix = "order_#{scope.order.id}#{CurrentUser.id or ""}#{CurrentHubMock.hub.id}"
scope.$digest()
expect(localStorage.getItem("ls.#{prefix}_email")).toMatch "public"
it "does not store secrets in local storage", ->
Checkout.secrets =
card_number: "superfuckingsecret"
scope.$digest()
keys = (localStorage.key(i) for i in [0..localStorage.length])
for key in keys
expect(localStorage.getItem(key)).not.toMatch Checkout.secrets.card_number
describe "without user", ->
beforeEach ->
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Checkout: Checkout, CurrentUser: {}}
it "is disabled", ->
expect(scope.enabled).toEqual false
it "does not store secrets in local storage", ->
Checkout.secrets =
card_number: "superfuckingsecret"
scope.$digest()
keys = (localStorage.key(i) for i in [0..localStorage.length])
for key in keys
expect(localStorage.getItem(key)).not.toMatch Checkout.secrets.card_number

View File

@@ -1,37 +0,0 @@
describe "DetailsCtrl", ->
ctrl = null
scope = null
order = null
CurrentUser = null
beforeEach ->
module("Darkswarm")
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
CurrentUser = { id: 1 }
ctrl = $controller 'DetailsCtrl', { $scope: scope, CurrentUser: CurrentUser }
it "finds a field by path", ->
scope.details =
path: "test"
expect(scope.field('path')).toEqual "test"
it "tests validity", ->
scope.details =
path:
$dirty: true
$invalid: true
expect(scope.fieldValid('path')).toEqual false
it "returns errors by path", ->
scope.Order =
errors: ->
scope.details =
path:
$error:
email: true
required: true
expect(scope.fieldErrors('path')).toEqual ["must be email address", "can't be blank"].join ", "

View File

@@ -1,17 +0,0 @@
describe "PaymentCtrl", ->
ctrl = null
scope = null
card1 = { id: 1, is_default: false }
card2 = { id: 3, is_default: true }
cards = [card1, card2]
beforeEach ->
module("Darkswarm")
angular.module('Darkswarm').value('savedCreditCards', cards)
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
scope.secrets = {}
ctrl = $controller 'PaymentCtrl', {$scope: scope}
it "sets the default card id as the selected_card", ->
expect(scope.secrets.selected_card).toEqual card2.id

View File

@@ -1,164 +0,0 @@
describe "ShopVariantCtrl", ->
ctrl = null
scope = null
CartMock = null
beforeEach ->
module 'Darkswarm'
inject ($rootScope, $controller, $modal)->
scope = $rootScope.$new()
scope.$watchGroup = ->
scope.variant = {
on_demand: true
product: {group_buy: true}
line_item: {
quantity: undefined
max_quantity: undefined
}
}
CartMock =
adjust: ->
true
ctrl = $controller 'ShopVariantCtrl', {$scope: scope, $modal: $modal, Cart: CartMock, Shopfront: {}}
it "initializes the quantity for shop display", ->
expect(scope.variant.line_item.quantity).toEqual 0
it "adds an item to the cart", ->
scope.add 1
expect(scope.variant.line_item.quantity).toEqual 1
it "adds to the existing quantity", ->
scope.add 1
scope.add 5
expect(scope.variant.line_item.quantity).toEqual 6
it "adds to an invalid quantity", ->
scope.$apply ->
scope.variant.line_item.quantity = -5
scope.add 1
expect(scope.variant.line_item.quantity).toEqual 1
it "adds to an undefined quantity", ->
scope.$apply ->
scope.variant.line_item.quantity = undefined
scope.add 1
expect(scope.variant.line_item.quantity).toEqual 1
it "adds to the max quantity", ->
scope.addMax 5
expect(scope.variant.line_item.quantity).toEqual 0
expect(scope.variant.line_item.max_quantity).toEqual 5
it "adds to an undefined max quantity", ->
scope.variant.line_item.quantity = 3
scope.variant.line_item.max_quantity = undefined
scope.addMax 1
expect(scope.variant.line_item.max_quantity).toEqual 4
it "adds to the max quantity to be at least min quantity", ->
scope.$apply ->
scope.variant.line_item.max_quantity = 2
scope.$apply ->
scope.add 3
expect(scope.variant.line_item.quantity).toEqual 3
expect(scope.variant.line_item.max_quantity).toEqual 3
it "decreases the min quantity to not exceed max quantity", ->
scope.$apply ->
scope.variant.line_item.quantity = 3
scope.variant.line_item.max_quantity = 5
scope.$apply ->
scope.addMax -3
expect(scope.variant.line_item.quantity).toEqual 2
expect(scope.variant.line_item.max_quantity).toEqual 2
it "caps at the available quantity", ->
scope.$apply ->
scope.variant.on_demand = false
scope.variant.on_hand = 3
scope.variant.line_item.quantity = 5
scope.variant.line_item.max_quantity = 7
expect(scope.variant.line_item.quantity).toEqual 3
expect(scope.variant.line_item.max_quantity).toEqual 3
it "allows adding when variant is on demand", ->
expect(scope.canAdd(5000)).toEqual true
it "denies adding if variant is out of stock", ->
scope.variant.on_demand = false
scope.variant.on_hand = 0
expect(scope.canAdd(1)).toEqual false
it "denies adding if stock is limitted", ->
scope.variant.on_demand = false
scope.variant.on_hand = 5
expect(scope.canAdd(4)).toEqual true
expect(scope.canAdd(5)).toEqual true
expect(scope.canAdd(6)).toEqual false
scope.add 3
expect(scope.canAdd(2)).toEqual true
expect(scope.canAdd(3)).toEqual false
it "denies adding if quantity is too high", ->
scope.variant.on_demand = false
scope.variant.on_hand = 5
scope.variant.line_item.quantity = 7
scope.variant.line_item.max_quantity = 7
expect(scope.canAdd(1)).toEqual false
expect(scope.canAddMax(1)).toEqual false
it "allows decrease when quantity is too high", ->
scope.variant.on_demand = false
scope.variant.on_hand = 5
scope.variant.line_item.quantity = 7
scope.variant.line_item.max_quantity = 7
expect(scope.canAdd(-1)).toEqual true
expect(scope.canAddMax(-1)).toEqual true
it "allows increase when quantity is negative", ->
scope.variant.on_demand = false
scope.variant.on_hand = 5
scope.variant.line_item.quantity = -3
scope.variant.line_item.max_quantity = -3
expect(scope.canAdd(1)).toEqual true
expect(scope.canAddMax(1)).toEqual false
scope.variant.line_item.quantity = 1
expect(scope.canAddMax(1)).toEqual true
it "denies decrease when quantity is negative", ->
scope.variant.on_demand = false
scope.variant.on_hand = 5
scope.variant.line_item.quantity = -3
scope.variant.line_item.max_quantity = -3
expect(scope.canAdd(-1)).toEqual false
expect(scope.canAddMax(-1)).toEqual false
it "denies declaring max quantity before item is in cart", ->
expect(scope.canAddMax(1)).toEqual false
it "allows declaring max quantity when item is in cart", ->
scope.add 1
expect(scope.canAddMax(1)).toEqual true
it "denies adding if stock is limitted", ->
scope.variant.on_demand = false
scope.variant.on_hand = 5
scope.variant.line_item.quantity = 1
scope.variant.line_item.max_quantity = 1
expect(scope.canAddMax(3)).toEqual true
expect(scope.canAddMax(4)).toEqual true
expect(scope.canAddMax(5)).toEqual false
expect(scope.canAddMax(6)).toEqual false

View File

@@ -224,7 +224,7 @@ module Spree
}
let(:tax_category) { create(:tax_category, name: "Shipping", tax_rates: [tax_rate] ) }
let(:hub) { create(:distributor_enterprise, charges_sales_tax: true) }
let(:order) { create(:order, distributor: hub) }
let(:order) { create(:order, distributor: hub, state: 'payment') }
let(:line_item) { create(:line_item, order: order) }
let(:shipping_method) {
@@ -348,7 +348,7 @@ module Spree
let(:line_item) { create(:line_item, variant: variant) }
let(:order) {
create(:order, line_items: [line_item], order_cycle: order_cycle,
distributor: coordinator)
distributor: coordinator, state: 'payment')
}
let(:fee) { order.all_adjustments.reload.enterprise_fee.first }
let(:fee_tax) { fee.adjustments.tax.first }
@@ -508,7 +508,7 @@ module Spree
let!(:zone) { create(:zone_with_member) }
let!(:tax_category) { create(:tax_category, name: "Tax Test") }
let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) }
let(:order) { create(:order, distributor: distributor) }
let(:order) { create(:order, distributor: distributor, state: "payment") }
let(:included_in_price) { true }
let(:tax_rate) {
create(:tax_rate, included_in_price: included_in_price, zone: zone,

View File

@@ -6,44 +6,18 @@ describe Spree::Order::Checkout do
let(:order) { Spree::Order.new }
context "with default state machine" do
let(:transitions) do
[
{ address: :delivery },
{ delivery: :payment },
{ payment: :complete },
{ delivery: :complete }
]
end
it "has the following transitions" do
transitions.each do |transition|
transition = Spree::Order.find_transition(from: transition.keys.first,
to: transition.values.first)
expect(transition).to_not be_nil
end
end
it "does not have a transition from delivery to confirm" do
transition = Spree::Order.find_transition(from: :delivery, to: :confirm)
expect(transition).to be_nil
end
it '.find_transition when contract was broken' do
expect(Spree::Order.find_transition({ foo: :bar, baz: :dog })).to be_falsy
end
context "#checkout_steps" do
context "when payment not required" do
before { allow(order).to receive_messages payment_required?: false }
specify do
expect(order.checkout_steps).to eq %w(address delivery complete)
expect(order.checkout_steps).to eq %w(address delivery confirmation complete)
end
end
context "when payment required" do
before { allow(order).to receive_messages payment_required?: true }
specify do
expect(order.checkout_steps).to eq %w(address delivery payment complete)
expect(order.checkout_steps).to eq %w(address delivery payment confirmation complete)
end
end
end
@@ -112,9 +86,9 @@ describe Spree::Order::Checkout do
allow(order).to receive_messages payment_required?: false
end
it "transitions to complete" do
it "transitions to confirmation" do
order.next!
expect(order.state).to eq "complete"
expect(order.state).to eq 'confirmation'
end
end
end

View File

@@ -24,6 +24,8 @@ describe Spree::Order do
before { allow(order).to receive_messages process_payments!: true }
it "should finalize order when transitioning to complete state" do
order.next
expect(order.state).to eq "confirmation"
expect(order).to receive(:finalize!)
order.next!
end
@@ -32,6 +34,8 @@ describe Spree::Order do
before { allow(order).to receive_messages process_payments!: false }
it "should still complete the order" do
order.next
expect(order.state).to eq "confirmation"
order.next
expect(order.state).to eq "complete"
end
@@ -42,6 +46,8 @@ describe Spree::Order do
before { allow(order).to receive_messages process_payments!: false }
it "can transition to complete" do
order.next
expect(order.state).to eq "confirmation"
order.next
expect(order.state).to eq "complete"
end

View File

@@ -152,6 +152,10 @@ module Spree
label: "legacy", originator_type: "Spree::TaxRate")
}
before do
order.update(state: "payment")
end
it "removes any legacy tax adjustments on order" do
order.create_tax_charge!

View File

@@ -690,6 +690,7 @@ describe Spree::Order do
before do
allow(order).to receive(:tax_zone) { shipping_tax_rate.zone }
order.update_attribute(:state, 'payment')
order.reload
order.create_tax_charge!
end
@@ -1269,7 +1270,8 @@ describe Spree::Order do
order.next!
order.payments << create(:payment, order: order)
expect { order.next! }.to change { order.state }.from("payment").to("complete")
expect { order.next! }.to change { order.state }.from("payment").to("confirmation")
expect { order.next! }.to change { order.state }.from("confirmation").to("complete")
end
end
@@ -1303,7 +1305,8 @@ describe Spree::Order do
it "skips the payment state" do
advance_to_delivery_state(order)
expect { order.next! }.to change { order.state }.from("delivery").to("complete")
expect { order.next! }.to change { order.state }.from("delivery").to("confirmation")
expect { order.next! }.to change { order.state }.from("confirmation").to("complete")
end
end
end

View File

@@ -282,61 +282,64 @@ module Spree
end
end
context "#adjust" do
describe "#adjust" do
let!(:country) { create(:country, name: "Default Country") }
let!(:state) { create(:state, name: "Default State", country: country) }
let!(:zone) { create(:zone_with_member, default_tax: true, member: country ) }
let!(:category) { create(:tax_category, name: "Taxable Foo") }
let!(:category2) { create(:tax_category, name: "Non Taxable") }
let!(:rate1) {
create(:tax_rate, amount: 0.10, zone: zone, tax_category: category)
}
let!(:rate2) {
create(:tax_rate, amount: 0.05, zone: zone, tax_category: category)
}
let(:hub) { create(:distributor_enterprise, charges_sales_tax: true) }
let(:address) { create(:address, state: country.states.first, country: country) }
let!(:order) {
create(:order_with_line_items, line_items_count: 2, distributor: hub,
ship_address: address)
}
let!(:taxable) { order.line_items.first.variant.product }
let!(:nontaxable) { order.line_items.last.variant.product }
before do
@country = create(:country)
@zone = create(:zone, name: "Country Zone", default_tax: true, zone_members: [])
@zone.zone_members.create(zoneable: @country)
@category = Spree::TaxCategory.create(name: "Taxable Foo")
@category2 = Spree::TaxCategory.create(name: "Non Taxable")
@rate1 = Spree::TaxRate.create(
amount: 0.10,
calculator: ::Calculator::DefaultTax.new,
tax_category: @category,
zone: @zone
)
@rate2 = Spree::TaxRate.create(
amount: 0.05,
calculator: ::Calculator::DefaultTax.new,
tax_category: @category,
zone: @zone
)
@order = Spree::Order.create!
@taxable = create(:product, tax_category: @category)
@nontaxable = create(:product, tax_category: @category2)
taxable.update(tax_category: category)
nontaxable.update(tax_category: category2)
order.line_items.delete_all
end
context "not taxable line item " do
let!(:line_item) { @order.contents.add(@nontaxable.variants.first, 1) }
let!(:line_item) { order.contents.add(nontaxable.variants.first, 1) }
it "should not create a tax adjustment" do
Spree::TaxRate.adjust(@order, @order.line_items)
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.tax.charge.count).to eq 0
end
it "should not create a refund" do
Spree::TaxRate.adjust(@order, @order.line_items)
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.credit.count).to eq 0
end
end
context "taxable line item" do
let!(:line_item) { @order.contents.add(@taxable.variants.first, 1) }
let!(:line_item) { order.contents.add(taxable.variants.first, 1) }
before do
@rate1.update_column(:included_in_price, true)
@rate2.update_column(:included_in_price, true)
rate1.update_column(:included_in_price, true)
rate2.update_column(:included_in_price, true)
end
context "when price includes tax" do
context "when zone is contained by default tax zone" do
it "should create two adjustments, one for each tax rate" do
Spree::TaxRate.adjust(@order, @order.line_items)
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.count).to eq 2
end
it "should not create a tax refund" do
Spree::TaxRate.adjust(@order, @order.line_items)
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.credit.count).to eq 0
end
end
@@ -344,57 +347,59 @@ module Spree
context "when order's zone is neither the default zone, or included in the default zone, but matches the rate's zone" do
before do
# With no zone members, this zone will not contain anything
@zone.zone_members.delete_all
zone.zone_members.delete_all
end
it "should create an adjustment" do
Spree::TaxRate.adjust(@order, @order.line_items)
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.charge.count).to eq 2
end
it "should not create a tax refund for each tax rate" do
Spree::TaxRate.adjust(@order, @order.line_items)
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.credit.count).to eq 0
end
end
end
context "when order's zone does not match default zone, is not included in the default zone, AND does not match the rate's zone" do
before do
@new_zone = create(:zone, name: "New Zone", default_tax: false)
@new_country = create(:country, name: "New Country")
@new_zone.zone_members.create(zoneable: @new_country)
@new_state = create(:state, country: @new_country)
@order.ship_address = create(:address, country: @new_country, state: @new_state)
@order.save
end
context "when order's zone does not match default zone, is not included in the default zone, AND does not match the rate's zone" do
let!(:other_country) { create(:country, name: "Other Country") }
let!(:other_state) { create(:state, name: "Other State", country: other_country) }
let!(:other_address) { create(:address, state: other_state, country: other_country) }
let!(:other_zone) {
create(:zone_with_member, name: "Other Zone", default_tax: false,
member: other_country)
}
it "should not create positive adjustments" do
Spree::TaxRate.adjust(@order, @order.line_items)
expect(line_item.adjustments.charge.count).to eq 0
end
before do
order.update(ship_address: other_address)
order.all_adjustments.delete_all
end
it "should create a tax refund for each tax rate" do
Spree::TaxRate.adjust(@order, @order.line_items)
expect(line_item.adjustments.credit.count).to eq 2
it "should not create positive adjustments" do
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.charge.count).to eq 0
end
it "should create a tax refund for each tax rate" do
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.credit.count).to eq 2
end
end
end
context "when price does not include tax" do
before do
allow(@order).to receive(:tax_zone) { @zone }
[@rate1, @rate2].each do |rate|
rate.included_in_price = false
rate.zone = @zone
rate.save
allow(order).to receive(:tax_zone) { zone }
[rate1, rate2].each do |rate|
rate.update(included_in_price: false, zone: zone)
end
Spree::TaxRate.adjust(order, order.line_items)
end
it "should not delete adjustments for complete order when taxrate is deleted" do
@order.update_column :completed_at, Time.now
@rate1.destroy!
@rate2.destroy!
rate1.destroy!
rate2.destroy!
expect(line_item.adjustments.count).to eq 2
end
@@ -407,10 +412,9 @@ module Spree
end
it "should remove adjustments when tax_zone is removed" do
Spree::TaxRate.adjust(@order, @order.line_items)
expect(line_item.adjustments.count).to eq 2
allow(@order).to receive(:tax_zone) { nil }
Spree::TaxRate.adjust(@order, @order.line_items)
allow(order).to receive(:tax_zone) { nil }
Spree::TaxRate.adjust(order, order.line_items)
expect(line_item.adjustments.count).to eq 0
end
end

View File

@@ -1,99 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
# This is the first example of testing concurrency in the Open Food Network.
# If we want to do this more often, we should look at:
#
# https://github.com/forkbreak/fork_break
#
# The concurrency flag enables multiple threads to see the same database
# without isolated transactions.
describe "Concurrent checkouts", concurrency: true, type: :request do
include AuthenticationHelper
include ShopWorkflow
let(:order_cycle) { create(:order_cycle) }
let(:distributor) { order_cycle.distributors.first }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) }
let(:address) { create(:address) }
let(:payment_method) { create(:payment_method, distributors: [distributor]) }
let(:breakpoint) { Mutex.new }
let(:address_params) { address.attributes.except("id") }
let(:order_params) {
{
"payments_attributes" => [
{
"payment_method_id" => payment_method.id,
"amount" => order.total
}
],
"bill_address_attributes" => address_params,
"ship_address_attributes" => address_params,
}
}
let(:params) { { format: :json, order: order_params } }
before do
# Create a valid order ready for checkout:
create(:shipping_method, distributors: [distributor])
variant = order_cycle.variants_distributed_by(distributor).first
order.line_items << create(:line_item, variant: variant)
set_order(order)
login_as(order.user)
end
it "handles two concurrent orders successfully" do
breakpoint.lock
breakpoint_reached_counter = 0
# Set a breakpoint after loading the order and before advancing the order's
# state and making payments. If two requests reach this breakpoint at the
# same time, they are in a race condition and bad things can happen.
# Examples are processing payments twice or selling more than we have.
allow_any_instance_of(CheckoutController).
to receive(:checkout_workflow).
and_wrap_original do |method, *args|
breakpoint_reached_counter += 1
breakpoint.synchronize {}
method.call(*args)
end
# Starting two checkout threads. The controller code will determine if
# these two threads are synchronised correctly or run into a race condition.
#
# 1. If the controller synchronises correctly:
# The first thread locks required resources and then waits at the
# breakpoint. The second thread waits for the first one.
# 2. If the controller fails to prevent the race condition:
# Both threads load required resources and wait at the breakpoint to do
# the same checkout action.
threads = [
Thread.new { put update_checkout_path, params: params },
Thread.new { put update_checkout_path, params: params },
]
# Wait for the first thread to reach the breakpoint:
Timeout.timeout(1) do
sleep 0.1 while breakpoint_reached_counter < 1
end
# Give the second thread a chance to reach the breakpoint, too.
# But we hope that it waits for the first thread earlier and doesn't
# reach the breakpoint yet.
sleep 1
expect(breakpoint_reached_counter).to eq 1
# Let the requests continue and finish.
breakpoint.unlock
threads.each(&:join)
# Verify that the checkout happened once.
order.reload
expect(order.completed?).to be true
expect(order.payments.count).to eq 1
end
end

View File

@@ -44,7 +44,7 @@ describe "checking out an order that initially fails", type: :request do
set_order order
end
context "when shipping and payment fees apply" do
pending "when shipping and payment fees apply" do
let(:calculator) { Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }
before do

View File

@@ -46,6 +46,7 @@ describe "checking out an order with a paypal express payment method", type: :re
payment_method.calculator = calculator
payment_method.save!
order.payments.create!(payment_method_id: payment_method.id, amount: order.total)
order.next
end
it "destroys the old payment and processes the order" do

View File

@@ -37,28 +37,13 @@ describe 'checkout endpoints', type: :request do
context "when getting the cart `/checkout/cart`" do
let(:path) { "/checkout/cart" }
context "using the legacy checkout" do
it "do not redirect" do
get path
puts response.redirect_url
expect(response.status).to eq(200)
end
end
it "redirect to the split checkout" do
get path
expect(response.status).to redirect_to("/checkout")
context "using the split checkout" do
before do
# feature toggle is enabled
Flipper.enable(:split_checkout)
end
it "redirect to the split checkout" do
get path
expect(response.status).to redirect_to("/checkout")
# follow the redirect
get response.redirect_url
expect(response.status).to redirect_to("/checkout/details")
end
# follow the redirect
get response.redirect_url
expect(response.status).to redirect_to("/checkout/details")
end
end
end

View File

@@ -114,7 +114,7 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
stub_add_metadata_request(payment_method: "pm_456", response: {})
end
context "when the user submits a new card and doesn't request that the card is saved for later" do
pending "when the user submits a new card and doesn't request that the card is saved for later" do
let(:hubs_payment_method_response_mock) do
{ status: 200, body: JSON.generate(id: hubs_stripe_payment_method) }
end
@@ -161,7 +161,7 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
end
end
context "when saving a card or using a stored card is involved" do
pending "when saving a card or using a stored card is involved" do
let(:hubs_payment_method_response_mock) do
{
status: 200,

View File

@@ -10,8 +10,6 @@ describe VoucherAdjustmentsController, type: :request do
let!(:adjustment) { voucher.create_adjustment(voucher.code, order) }
before do
Flipper.enable(:split_checkout)
# Make sure the order is created by the order user, the factory doesn't set ip properly
order.created_by = user
order.save!

View File

@@ -6,12 +6,12 @@ describe "routing for Stripe return URLS", type: :routing do
context "checkout return URLs" do
it "routes /checkout to checkout#edit" do
expect(get: "checkout").
to route_to("checkout#edit")
to route_to("split_checkout#edit")
end
it "routes /checkout?test=123 to checkout#edit" do
expect(get: "/checkout?test=123").
to route_to(controller: "checkout", action: "edit", test: "123")
to route_to(controller: "split_checkout", action: "edit", test: "123")
end
it "routes /checkout?payment_intent=pm_123 to payment_gateways/stripe#confirm" do

View File

@@ -1,85 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe Checkout::FormDataAdapter do
describe '#params' do
let(:params) { { "order" => { "order_id" => "123" } } }
let(:order) { create(:order) }
let(:user) { create(:user) }
let(:adapter) { Checkout::FormDataAdapter.new(params, order, user) }
it "returns the :order item in the params provided" do
expect(adapter.params[:order]).to eq params["order"]
end
describe "when payment_attributes are provided" do
before { params["order"]["payments_attributes"] = [{ "payment_method_id" => "123" }] }
describe "and source attributes are provided" do
let(:source_attributes) { { "payment_method_name" => "Pay at the farm" } }
before { params["payment_source"] = { "123" => source_attributes } }
it "moves payment source attributes to the order payment attributes" do
expect(adapter.params[:order][:payments_attributes].
first[:source_attributes]).to eq source_attributes
end
end
describe "and order total is not zero" do
before { order.total = "50.0" }
it "sets the payment attributes amount to the order total" do
expect(adapter.params[:order][:payments_attributes].first[:amount]).to eq order.total
end
end
describe "and a credit card is provided" do
before do
params["order"]["payments_attributes"].first["source_attributes"] =
{ "number" => "4444333322221111" }
end
it "fills in missing credit card brand" do
expect(adapter.params[:order][:payments_attributes].first[:source_attributes][:cc_type]).to eq "visa"
end
it "leaves an existing credit card brand" do
params["order"]["payments_attributes"].first["source_attributes"]["cc_type"] = "test"
expect(adapter.params[:order][:payments_attributes].first[:source_attributes][:cc_type]).to eq "test"
end
it "doesn't touch the credit card brand without a number" do
params["order"]["payments_attributes"].first["source_attributes"]["number"] = ""
expect(adapter.params[:order][:payments_attributes].first[:source_attributes].key?(:cc_type)).to eq false
end
end
describe "and existing credit card is provided" do
before { params["order"]["existing_card_id"] = credit_card.id }
describe "and credit card is owned by current user" do
let(:credit_card) { create(:credit_card, user_id: user.id) }
before { params["order"]["existing_card_id"] = credit_card.id }
it "adds card details to payment attributes" do
expect(adapter.params[:order][:payments_attributes].first[:source][:id]).to eq credit_card.id
expect(adapter.params[:order][:payments_attributes].
first[:source][:last_digits]).to eq credit_card.last_digits
end
end
describe "and credit card is not owned by current user" do
let(:credit_card) { create(:credit_card) }
it "raises exception if credit card provided doesnt belong to the current user" do
expect { adapter.params[:order] }.to raise_error Spree::Core::GatewayError
end
end
end
end
end
end

View File

@@ -72,7 +72,8 @@ describe OrderTaxAdjustmentsFetcher do
line_items: [line_item1, line_item2],
bill_address: create(:address),
order_cycle: order_cycle,
distributor: coordinator
distributor: coordinator,
state: 'payment'
)
end
let(:shipping_method) do

View File

@@ -1,19 +1,6 @@
# frozen_string_literal: true
module StripeHelper
def checkout_with_stripe(guest_checkout: true, remember_card: false)
visit checkout_path
checkout_as_guest if guest_checkout
fill_out_form(
free_shipping.name,
stripe_sca_payment_method.name,
save_default_addresses: false
)
fill_out_card_details
check "Remember this card?" if remember_card
place_order
end
def fill_out_card_details
fill_in "stripe-cardnumber", with: '4242424242424242'
fill_in "exp-date", with: "01/#{DateTime.now.year + 1}"

View File

@@ -578,7 +578,7 @@ describe '
}
let(:order1) {
create(:order, order_cycle: order_cycle, distributor: user1.enterprises.first,
shipments: [shipment], bill_address: bill_address)
shipments: [shipment], bill_address: bill_address, state: 'payment')
}
let(:product1) {
create(:taxed_product, zone: zone, price: 12.54, tax_rate_amount: 0, sku: 'sku1')

View File

@@ -61,13 +61,6 @@ describe 'Multilingual' do
expect_menu_and_cookie_in_es
expect(page).to have_content 'Precio'
end
it "in the checkout page" do
visit checkout_path(locale: 'es')
expect_menu_and_cookie_in_es
expect(page).to have_content 'Total del carrito'
end
end
end

View File

@@ -174,18 +174,6 @@ describe "full-page cart" do
end
end
describe "tax" do
before do
add_enterprise_fee enterprise_fee
add_product_to_cart order, product_with_tax
visit main_app.cart_path
end
it "shows the total tax for the order, including product tax and tax on fees" do
expect(page).to have_selector '.tax-total', text: '11.00' # 10 + 1
end
end
describe "updating quantities" do
let(:li) { order.line_items.reload.last }
let(:variant) { product_with_tax.variants.first }

View File

@@ -29,66 +29,42 @@ describe "As a consumer I want to check out my cart" do
add_product_to_cart order, product
end
shared_examples "with different checkout types" do |checkout_type|
context "on #{checkout_type}" do
it "does not render the login form when logged in" do
login_as user
visit checkout_path
within "section[role='main']" do
expect(page).to have_no_content "Login"
expect(page).to have_checkout_details
end
end
it "renders the login buttons when logged out" do
visit checkout_path
within "section[role='main']" do
expect(page).to have_content "Login"
click_button "Login"
end
expect(page).to have_login_modal
end
describe "logging in" do
before do
visit checkout_path
within("section[role='main']") { click_button "Login" }
expect(page).to have_login_modal
fill_in "Email", with: user.email
fill_in "Password", with: user.password
within(".login-modal") { click_button 'Login' }
end
context "and populating user details on (#{checkout_type})",
if: checkout_type.eql?("legacy_checkout") do
it "toggles the Details section" do
expect(page).to have_content "Your details"
page.find(:css, "i.ofn-i_052-point-down").click
end
end
context "and populating user details on (#{checkout_type})",
if: checkout_type.eql?("split_checkout") do
it "should allow proceeding to the next step" do
expect(page).to have_content("Logged in successfully")
click_button "Next - Payment method"
expect(page).to have_button("Next - Order summary")
end
end
context "on split_checkout" do
it "does not render the login form when logged in" do
login_as user
visit checkout_path
within "section[role='main']" do
expect(page).to have_no_content "Login"
expect(page).to have_checkout_details
end
end
end
describe "shared examples" do
context "legacy checkout" do
it_behaves_like "with different checkout types", "legacy_checkout"
it "renders the login buttons when logged out" do
visit checkout_path
within "section[role='main']" do
expect(page).to have_content "Login"
click_button "Login"
end
expect(page).to have_login_modal
end
context "split checkout" do
describe "logging in" do
before do
Flipper.enable(:split_checkout)
visit checkout_path
within("section[role='main']") { click_button "Login" }
expect(page).to have_login_modal
fill_in "Email", with: user.email
fill_in "Password", with: user.password
within(".login-modal") { click_button 'Login' }
end
context "and populating user details on (split_checkout)" do
it "should allow proceeding to the next step" do
expect(page).to have_content("Logged in successfully")
click_button "Next - Payment method"
expect(page).to have_button("Next - Order summary")
end
end
include_examples "with different checkout types", "split_checkout"
end
end
@@ -98,24 +74,6 @@ describe "As a consumer I want to check out my cart" do
checkout_as_guest
expect(page).to have_checkout_details
end
it "asks the user to log in if they are using a registered email" do
visit checkout_path
checkout_as_guest
fill_in 'First Name', with: 'Not'
fill_in 'Last Name', with: 'Guest'
fill_in 'Email', with: user.email
fill_in 'Phone', with: '098712736'
within '#details' do
click_button 'Next'
end
expect(page).to have_selector 'div.login-modal'
expect(page).to have_content 'This email address is already registered. Please log in '\
'to continue, or go back and use another email address.'
end
end
end
end

View File

@@ -47,7 +47,7 @@ describe "Check out with Paypal" do
end
shared_examples "checking out with paypal" do |user_type|
context user_type.to_s do
pending user_type.to_s do
before do
fill_out_details
fill_out_form(free_shipping.name, paypal.name, save_default_addresses: false)
@@ -82,7 +82,7 @@ describe "Check out with Paypal" do
end
describe "shared_examples" do
context "as a guest user" do
pending "as a guest user" do
before do
visit checkout_path
checkout_as_guest
@@ -90,7 +90,7 @@ describe "Check out with Paypal" do
it_behaves_like "checking out with paypal", "as guest"
end
context "as a logged in user" do
pending "as a logged in user" do
before do
login_as user
visit checkout_path

View File

@@ -71,7 +71,7 @@ describe "As a consumer I want to check out my cart" do
distributor.shipping_methods << tagged_shipping
end
describe "when I have an out of stock product in my cart" do
pending "when I have an out of stock product in my cart" do
before do
variant.on_demand = false
variant.on_hand = 0
@@ -87,7 +87,7 @@ describe "As a consumer I want to check out my cart" do
end
end
context 'login in as user' do
pending 'login in as user' do
let(:user) { create(:user) }
let(:pdf_upload) {
Rack::Test::UploadedFile.new(
@@ -335,7 +335,7 @@ describe "As a consumer I want to check out my cart" do
end
end
context "guest checkout" do
pending "guest checkout" do
before do
visit checkout_path
checkout_as_guest

View File

@@ -41,7 +41,7 @@ describe "Check out with Stripe" do
distributor.shipping_methods << [shipping_with_fee, free_shipping]
end
describe "using Stripe SCA" do
pending "using Stripe SCA" do
let!(:stripe_account) { create(:stripe_account, enterprise: distributor) }
let!(:stripe_sca_payment_method) {
create(:stripe_sca_payment_method, distributors: [distributor])

View File

@@ -150,17 +150,19 @@ describe "shopping with variant overrides defined" do
expect(page).to have_selector "#edit-cart .grand-total", text: with_currency(122.22)
end
it "shows the correct prices in the checkout" do
click_add_to_cart product1_variant1, 2
click_checkout
pending "prices in the checkout" do
it "shows the correct prices" do
click_add_to_cart product1_variant1, 2
click_checkout
expect(page).to have_selector 'form.edit_order .cart-total', text: with_currency(122.22)
expect(page).to have_selector 'form.edit_order .shipping', text: with_currency(0.00)
expect(page).to have_selector 'form.edit_order .total', text: with_currency(122.22)
expect(page).to have_selector 'form.edit_order .cart-total', text: with_currency(122.22)
expect(page).to have_selector 'form.edit_order .shipping', text: with_currency(0.00)
expect(page).to have_selector 'form.edit_order .total', text: with_currency(122.22)
end
end
end
describe "creating orders" do
pending "creating orders" do
it "creates the order with the correct prices" do
click_add_to_cart product1_variant1, 2
click_checkout

View File

@@ -70,8 +70,6 @@ describe "As a consumer, I want to checkout my order" do
}
before do
Flipper.enable(:split_checkout)
add_enterprise_fee enterprise_fee
set_order order

View File

@@ -82,158 +82,95 @@ describe "As a consumer, I want to see adjustment breakdown" do
end
describe "for a customer with shipping address within the tax zone" do
context "on legacy checkout" do
before do
set_order order_within_zone
login_as(user_within_zone)
end
it "will be charged tax on the order" do
visit checkout_path
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
choose free_shipping.name.to_s
within "#payment" do
choose free_payment.name.to_s
end
click_on "Place order now"
# UI checks
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
# DB checks
assert_db_tax_incl
end
before do
set_order order_within_zone
login_as(user_within_zone)
end
context "on split-checkout" do
before do
Flipper.enable(:split_checkout)
it "will be charged tax on the order" do
visit checkout_step_path(:details)
set_order order_within_zone
login_as(user_within_zone)
choose "Delivery"
click_button "Next - Payment method"
click_on "Next - Order summary"
click_on "Complete order"
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
# DB checks
assert_db_tax_incl
end
context "when using a voucher" do
let!(:voucher) do
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
end
it "will be charged tax on the order" do
it "will include a tax included amount on the voucher adjustment" do
visit checkout_step_path(:details)
choose "Delivery"
click_button "Next - Payment method"
# add Voucher
fill_in "Enter voucher code", with: voucher.code
click_button("Apply")
# Choose payment
click_on "Next - Order summary"
click_on "Complete order"
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).to have_selector('#order_total', text: with_currency(0.00))
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
# DB checks
assert_db_tax_incl
end
context "when using a voucher" do
let!(:voucher) do
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
# Voucher
within "#line-items" do
expect(page).to have_content(voucher.code)
expect(page).to have_content(with_currency(-10.00))
end
it "will include a tax included amount on the voucher adjustment" do
visit checkout_step_path(:details)
# DB check
order_within_zone.reload
voucher_adjustment = order_within_zone.voucher_adjustments.first
choose "Delivery"
click_button "Next - Payment method"
# add Voucher
fill_in "Enter voucher code", with: voucher.code
click_button("Apply")
# Choose payment
click_on "Next - Order summary"
click_on "Complete order"
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(0.00))
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
# Voucher
within "#line-items" do
expect(page).to have_content(voucher.code)
expect(page).to have_content(with_currency(-10.00))
end
# DB check
order_within_zone.reload
voucher_adjustment = order_within_zone.voucher_adjustments.first
expect(voucher_adjustment.amount.to_f).to eq(-10)
expect(voucher_adjustment.included_tax.to_f).to eq(-1.15)
end
expect(voucher_adjustment.amount.to_f).to eq(-10)
expect(voucher_adjustment.included_tax.to_f).to eq(-1.15)
end
end
end
describe "for a customer with shipping address outside the tax zone" do
context "on legacy checkout" do
before do
set_order order_outside_zone
login_as(user_outside_zone)
end
it "will not be charged tax on the order" do
pending("#7540")
visit checkout_path
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
choose free_shipping.name.to_s
within "#payment" do
choose free_payment.name.to_s
end
click_on "Place order now"
# UI checks
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).not_to have_content("includes tax")
# DB checks
assert_db_no_tax_incl
end
before do
set_order order_outside_zone
login_as(user_outside_zone)
end
context "on split-checkout" do
before do
Flipper.enable(:split_checkout)
it "will not be charged tax on the order" do
pending("#7540")
visit checkout_step_path(:details)
set_order order_outside_zone
login_as(user_outside_zone)
end
choose "Delivery"
check "order_save_bill_address"
check "ship_address_same_as_billing"
it "will not be charged tax on the order" do
pending("#7540")
visit checkout_step_path(:details)
click_button "Next - Payment method"
click_on "Next - Order summary"
click_on "Complete order"
choose "Delivery"
check "order_save_bill_address"
check "ship_address_same_as_billing"
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).not_to have_content("includes tax")
click_button "Next - Payment method"
click_on "Next - Order summary"
click_on "Complete order"
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).not_to have_content("includes tax")
# DB checks
assert_db_no_tax_incl
end
# DB checks
assert_db_no_tax_incl
end
end
end

View File

@@ -62,7 +62,7 @@ describe "As a consumer, I want to see adjustment breakdown" do
Spree::Config.set(tax_using_ship_address: true)
end
describe "a not-included tax" do
pending "a not-included tax" do
before do
zone.update!(default_tax: false)
tax_rate.update!(included_in_price: false)
@@ -88,207 +88,141 @@ describe "As a consumer, I want to see adjustment breakdown" do
end
describe "for a customer with shipping address within the tax zone" do
context "on legacy checkout" do
before do
set_order order_within_zone
login_as(user_within_zone)
end
it "will be charged tax on the order" do
visit checkout_path
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
choose free_shipping.name.to_s
within "#payment" do
choose free_payment.name.to_s
end
click_on "Place order now"
# DB checks
order_within_zone.reload
expect(order_within_zone.additional_tax_total).to eq(1.3)
# UI checks
expect(page).to have_selector('#order_total', text: with_currency(11.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
end
before do
set_order order_within_zone
login_as(user_within_zone)
end
context "on split-checkout" do
before do
Flipper.enable(:split_checkout)
it "will be charged tax on the order" do
visit checkout_step_path(:details)
set_order order_within_zone
login_as(user_within_zone)
choose "Delivery"
click_button "Next - Payment method"
click_on "Next - Order summary"
click_on "Complete order"
# DB checks
order_within_zone.reload
expect(order_within_zone.additional_tax_total).to eq(1.3)
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(11.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
end
context "when using a voucher" do
let!(:voucher) do
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
end
it "will be charged tax on the order" do
it "will include a tax included amount on the voucher adjustment" do
visit checkout_step_path(:details)
choose "Delivery"
click_button "Next - Payment method"
# add Voucher
fill_in "Enter voucher code", with: voucher.code
click_button("Apply")
click_on "Next - Order summary"
click_on "Complete order"
# DB checks
order_within_zone.reload
expect(order_within_zone.additional_tax_total).to eq(1.3)
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(11.30))
expect(page).to have_selector('#order_total', text: with_currency(1.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
end
context "when using a voucher" do
let!(:voucher) do
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
# Voucher
within "#line-items" do
expect(page).to have_content(voucher.code)
expect(page).to have_content(with_currency(-8.85))
expect(page).to have_content("Tax #{voucher.code}")
expect(page).to have_content(with_currency(-1.15))
end
it "will include a tax included amount on the voucher adjustment" do
visit checkout_step_path(:details)
# DB check
order_within_zone.reload
voucher_adjustment = order_within_zone.voucher_adjustments.first
voucher_tax_adjustment = order_within_zone.voucher_adjustments.second
choose "Delivery"
click_button "Next - Payment method"
# add Voucher
fill_in "Enter voucher code", with: voucher.code
click_button("Apply")
click_on "Next - Order summary"
click_on "Complete order"
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(1.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
# Voucher
within "#line-items" do
expect(page).to have_content(voucher.code)
expect(page).to have_content(with_currency(-8.85))
expect(page).to have_content("Tax #{voucher.code}")
expect(page).to have_content(with_currency(-1.15))
end
# DB check
order_within_zone.reload
voucher_adjustment = order_within_zone.voucher_adjustments.first
voucher_tax_adjustment = order_within_zone.voucher_adjustments.second
expect(voucher_adjustment.amount.to_f).to eq(-8.85)
expect(voucher_tax_adjustment.amount.to_f).to eq(-1.15)
end
expect(voucher_adjustment.amount.to_f).to eq(-8.85)
expect(voucher_tax_adjustment.amount.to_f).to eq(-1.15)
end
end
end
describe "for a customer with shipping address outside the tax zone" do
context "on legacy checkout" do
before do
set_order order_outside_zone
login_as(user_outside_zone)
end
it "will not be charged tax on the order" do
visit checkout_path
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
choose free_shipping.name.to_s
within "#payment" do
choose free_payment.name.to_s
end
click_on "Place order now"
# DB checks
order_outside_zone.reload
expect(order_outside_zone.included_tax_total).to eq(0.0)
expect(order_outside_zone.additional_tax_total).to eq(0.0)
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).not_to have_content("includes tax")
end
before do
set_order order_outside_zone
login_as(user_outside_zone)
end
context "on split-checkout" do
it "will not be charged tax on the order" do
visit checkout_step_path(:details)
choose "Delivery"
click_button "Next - Payment method"
click_on "Next - Order summary"
click_on "Complete order"
# DB checks
order_outside_zone.reload
expect(order_outside_zone.included_tax_total).to eq(0.0)
expect(order_outside_zone.additional_tax_total).to eq(0.0)
# UI checks
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).not_to have_content("includes tax")
end
# reproducing bug #9153
context "changing the address on the /details step" do
before do
Flipper.enable(:split_checkout)
set_order order_outside_zone
login_as(user_outside_zone)
end
it "will not be charged tax on the order" do
visit checkout_step_path(:details)
choose "Delivery"
click_button "Next - Payment method"
click_on "Next - Order summary"
expect(page).to have_selector('#order_total', text: with_currency(10.00))
# customer goes back from Summary to Details step, to change Delivery
click_on "Your details"
end
it "should re-calculate the tax accordingly" do
select "Victoria", from: "order_bill_address_attributes_state_id"
# it should not be necessary to save as new default bill address
check "order_save_bill_address"
check "ship_address_same_as_billing"
choose "Delivery"
click_button "Next - Payment method"
click_on "Next - Order summary"
# Summary step should reflect changes
expect(page).to have_selector('#order_total', text: with_currency(11.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
click_on "Complete order"
# DB checks
order_outside_zone.reload
expect(order_outside_zone.included_tax_total).to eq(0.0)
expect(order_outside_zone.additional_tax_total).to eq(0.0)
expect(order_outside_zone.additional_tax_total).to eq(1.3)
# UI checks
# UI checks - Order confirmation page should reflect changes
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(10.00))
expect(page).not_to have_content("includes tax")
end
# reproducing bug #9153
context "changing the address on the /details step" do
before do
visit checkout_step_path(:details)
choose "Delivery"
click_button "Next - Payment method"
click_on "Next - Order summary"
expect(page).to have_selector('#order_total', text: with_currency(10.00))
# customer goes back from Summary to Details step, to change Delivery
click_on "Your details"
end
it "should re-calculate the tax accordingly" do
select "Victoria", from: "order_bill_address_attributes_state_id"
# it should not be necessary to save as new default bill address
check "order_save_bill_address"
check "ship_address_same_as_billing"
choose "Delivery"
click_button "Next - Payment method"
click_on "Next - Order summary"
# Summary step should reflect changes
expect(page).to have_selector('#order_total', text: with_currency(11.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
click_on "Complete order"
# DB checks
order_outside_zone.reload
expect(order_outside_zone.included_tax_total).to eq(0.0)
expect(order_outside_zone.additional_tax_total).to eq(1.3)
# UI checks - Order confirmation page should reflect changes
expect(page).to have_content("Confirmed")
expect(page).to have_selector('#order_total', text: with_currency(11.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
end
expect(page).to have_selector('#order_total', text: with_currency(11.30))
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
end
end
end

View File

@@ -145,7 +145,7 @@ describe 'White label setting' do
set_order(order)
end
shared_examples "hides the OFN navigation when needed only for the checkout" do
context "when the split checkout is enabled" do
it_behaves_like "hides the OFN navigation when needed only"
context "for cart path" do
@@ -174,18 +174,6 @@ describe 'White label setting' do
it_behaves_like "hides the OFN navigation for mobile view as well"
end
end
context "when the split checkout is disabled" do
it_behaves_like "hides the OFN navigation when needed only for the checkout"
end
context "when the split checkout is enabled" do
before do
Flipper.enable(:split_checkout)
end
it_behaves_like "hides the OFN navigation when needed only for the checkout"
end
end
context "when the user has a complete order" do