Files
openfoodnetwork/spec/system/consumer/checkout/summary_spec.rb
2025-04-24 09:29:40 +02:00

491 lines
17 KiB
Ruby

# frozen_string_literal: true
require "system_helper"
RSpec.describe "As a consumer, I want to checkout my order" do
include ShopWorkflow
include CheckoutHelper
include FileHelper
include StripeHelper
include StripeStubs
include PaypalHelper
include AuthenticationHelper
include UIComponentHelper
let!(:zone) { create(:zone_with_member) }
let(:supplier) { create(:supplier_enterprise) }
let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) }
let(:product) {
create(:taxed_product, supplier_id: supplier.id, price: 10, zone:, tax_rate_amount: 0.1)
}
let(:variant) { product.variants.first }
let!(:order_cycle) {
create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor],
coordinator: create(:distributor_enterprise), variants: [variant])
}
let(:order) {
create(:order, order_cycle:, distributor:, bill_address_id: nil,
ship_address_id: nil, state: "cart",
line_items: [create(:line_item, variant:)])
}
let(:fee_tax_rate) { create(:tax_rate, amount: 0.10, zone:, included_in_price: true) }
let(:fee_tax_category) { create(:tax_category, tax_rates: [fee_tax_rate]) }
let(:enterprise_fee) { create(:enterprise_fee, amount: 1.23, tax_category: fee_tax_category) }
let(:free_shipping_with_required_address) {
create(:shipping_method, require_ship_address: true,
name: "A Free Shipping with required address")
}
before do
add_enterprise_fee enterprise_fee
pick_order order
distributor.shipping_methods.push(free_shipping_with_required_address)
end
context "as a logged in user" do
let(:user) { create(:user) }
before do
login_as(user)
end
context "summary step" do
let(:order) {
create(:order_ready_for_confirmation, distributor:)
}
describe "display the delivery address and not the ship address" do
let(:ship_address) { create(:address, :randomized) }
let(:bill_address) { create(:address, :randomized) }
before do
order.update_attribute(:ship_address, ship_address)
order.update_attribute(:bill_address, bill_address)
visit checkout_step_path(:summary)
end
it "displays title and ship address" do
expect(page).to have_title "Checkout Summary - Open Food Network"
expect(page).to have_content "Delivery address"
expect(page).to have_content order.ship_address.address1
expect(page).to have_content order.ship_address.city
expect(page).to have_content order.ship_address.zipcode
expect(page).to have_content order.ship_address.phone
# but not the billing address
expect(page).not_to have_content order.bill_address.address1
expect(page).not_to have_content order.bill_address.city
expect(page).not_to have_content order.bill_address.zipcode
expect(page).not_to have_content order.bill_address.phone
end
end
describe "with an order with special instructions" do
before do
order.update_attribute(:special_instructions, "Please deliver on Tuesday")
visit checkout_step_path(:summary)
end
it "displays the special instructions" do
expect(page).to have_content "Please deliver on Tuesday"
end
end
describe "completing the checkout" do
it "keeps the distributor selected for the current user after completion" do
visit checkout_step_path(:summary)
expect(page).to have_content "Shopping @ #{distributor.name}"
place_order
expect(page).to have_content "Back To Store"
expect(order.reload.state).to eq "complete"
expect(page).to have_content "Shopping @ #{distributor.name}"
end
end
describe "navigating away from checkout summary page" do
it "navigates to new page when popup is confirmed" do
visit checkout_step_path(:summary)
expect(page).to have_content "Order summary"
within '.nav-main-menu' do
accept_alert do
click_link(href: '/groups')
end
end
expect(page).not_to have_content "Order summary"
expect(page).to have_content "Groups / regions"
end
it "doesn't navigate to new page when popup is canceled" do
visit checkout_step_path(:summary)
expect(page).to have_content "Order summary"
within '.nav-main-menu' do
dismiss_confirm do
click_link(href: '/groups')
end
end
expect(page).to have_content "Order summary"
expect(page).not_to have_content "Groups / regions"
end
it "opens correct order step when edit link is clicked" do
visit checkout_step_path(:summary)
expect(page).to have_content "Order summary"
click_link(href: '/checkout/details')
expect(page).to have_content "Contact information"
expect(page).not_to have_content "Groups / regions"
end
end
describe "navigation available" do
it "redirect to Payment method step by clicking on 'Payment method' link" do
visit checkout_step_path(:summary)
click_link "Payment method"
expect(page).to have_content("You can review and confirm your order in the next step " \
"which includes the final costs.")
end
end
describe "terms and conditions" do
let(:customer) { create(:customer, enterprise: order.distributor, user:) }
let(:tos_url) { "https://example.org/tos" }
let(:system_terms) { terms_pdf_file }
let(:shop_terms) { terms_pdf_file }
context "when none are required" do
it "doesn't show checkbox or links" do
visit checkout_step_path(:summary)
within "#checkout" do
expect(page).not_to have_field "order_accept_terms"
expect(page).not_to have_link "Terms and Conditions"
expect(page).not_to have_link "Terms of service"
end
end
end
context "when distributor has T&Cs" do
before do
order.distributor.update!(terms_and_conditions: shop_terms)
end
describe "when customer has not accepted T&Cs before" do
it "shows a link to the T&Cs and disables checkout button until terms are accepted" do
visit checkout_step_path(:summary)
expect(page).to have_link "Terms and Conditions", href: /Terms-of-service\.pdf/
expect(page).to have_field "order_accept_terms", checked: false
end
end
describe "when customer has already accepted T&Cs before" do
before do
customer.update terms_and_conditions_accepted_at: Time.zone.now
end
it "enables checkout button (because T&Cs are accepted by default)" do
visit checkout_step_path(:summary)
expect(page).to have_field "order_accept_terms", checked: true
end
describe "but afterwards the enterprise has uploaded a new T&Cs file" do
before { order.distributor.update!(terms_and_conditions: shop_terms) }
it "disables checkout button until terms are accepted" do
visit checkout_step_path(:summary)
expect(page).to have_field "order_accept_terms", checked: false
end
end
end
end
context "when the platform's terms of service have to be accepted" do
before do
allow(Spree::Config).to receive(:shoppers_require_tos).and_return(true)
end
let!(:tos) do
TermsOfServiceFile.create!(attachment: system_terms)
end
it "shows the terms which need to be accepted" do
visit checkout_step_path(:summary)
expect(page).to have_link("Terms of service", href: /Terms-of-service.pdf/, count: 2)
expect(find_link("Terms of service")[:target]).to eq "_blank"
expect(page).to have_field "order_accept_terms", checked: false
end
context "when the terms have been accepted in the past" do
context "with a dedicated ToS file" do
before do
tos.update!(
updated_at: 1.day.ago
)
customer.update(terms_and_conditions_accepted_at: Time.zone.now)
end
it "remembers the selection" do
visit checkout_step_path(:summary)
expect(page).to have_link("Terms of service", href: /Terms-of-service.pdf/)
expect(page).to have_field "order_accept_terms", checked: true
end
end
context "with the default ToS file" do
before do
customer.update(terms_and_conditions_accepted_at: Time.zone.now)
end
it "remembers the selection" do
pending "#10675"
visit checkout_step_path(:summary)
expect(page).to have_link("Terms of service", href: tos_url)
expect(page).to have_field "order_accept_terms", checked: true
end
end
end
end
context "when the seller's terms and the platform's terms have to be accepted" do
before do
order.distributor.update!(terms_and_conditions: shop_terms)
allow(Spree::Config).to receive(:shoppers_require_tos).and_return(true)
end
let!(:tos) do
TermsOfServiceFile.create!(attachment: system_terms)
end
it "shows links to both terms and all need accepting" do
visit checkout_step_path(:summary)
expect(page).to have_link "Terms and Conditions", href: /Terms-of-service\.pdf/
expect(page).to have_link("Terms of service", href: /Terms-of-service\.pdf/, count: 2)
expect(page).to have_field "order_accept_terms", checked: false
end
end
end
context "handle the navigation when the order is ready for confirmation" do
it "redirect to summary step" do
visit "/checkout"
expect(page).to have_current_path checkout_step_path(:summary)
end
it "handle the navigation between each step by clicking tabs/buttons to submit the form" do
visit checkout_step_path(:summary)
click_on "Your details"
expect(page).to have_current_path checkout_step_path(:details)
click_on "Next - Payment method"
expect(page).to have_current_path checkout_step_path(:payment)
end
end
describe "order state" do
before do
visit checkout_step_path(:summary)
end
it "emptying the cart changes the order state back to address" do
visit main_app.cart_path
expect {
find('#clear_cart_link').click
expect(page).to have_current_path enterprise_shop_path(distributor)
}.to change { order.reload.state }.from("confirmation").to("address")
end
end
describe "vouchers" do
let(:voucher) do
create(:voucher_flat_rate, code: 'some_code', enterprise: distributor, amount: 6)
end
before do
add_voucher_to_order(voucher, order)
end
it "shows the applied voucher" do
visit checkout_step_path(:summary)
within ".summary-right" do
expect(page).to have_content "some_code"
expect(page).to have_content "-6"
end
end
context "with voucher deactivated after being added to an order" do
it "completes the order" do
visit checkout_step_path(:summary)
# Deactivate voucher
voucher.destroy
place_order
expect(order.reload.state).to eq "complete"
end
end
end
context "with a VINE voucher", feature: :connected_apps do
let!(:vine_connected_app) {
ConnectedApps::Vine.create(
enterprise: distributor, data: { api_key: "1234568", secret: "my_secret" }
)
}
let(:vine_voucher) {
create(:vine_voucher, code: 'some_vine_code', enterprise: distributor, amount: 0.01)
}
before do
allow(ENV).to receive(:fetch).and_call_original
allow(ENV).to receive(:fetch).with("VINE_API_URL")
.and_return("https://vine-staging.openfoodnetwork.org.au/api/v1")
add_voucher_to_order(vine_voucher, order)
end
it "shows the applied voucher" do
visit checkout_step_path(:summary)
within ".summary-right" do
expect(page).to have_content "some_vine_code"
expect(page).to have_content "-0.01"
end
end
context "when placing the order" do
it "completes the order", :vcr do
visit checkout_step_path(:summary)
place_order
within "#line-items" do
expect(page).to have_content "Voucher: some_vine_code"
expect(page).to have_content "$-0.01"
end
expect(order.reload.state).to eq "complete"
end
end
end
end
context "with previous open orders" do
let(:order) {
create(:order_ready_for_confirmation, distributor:,
order_cycle:, user_id: user.id)
}
let!(:prev_order) {
create(:completed_order_with_totals,
order_cycle:, distributor:, user_id: order.user_id)
}
context "when distributor allows order changes" do
before do
order.distributor.allow_order_changes = true
order.distributor.save
visit checkout_step_path(:summary)
end
it "informs about previous orders" do
expect(page).to have_content("You have an order for this order cycle already.")
end
it "show a link to /cart#bought-products page" do
expect(page).to have_link("cart", href: "/cart#bought-products")
click_on "cart"
expect(page).to have_text(
"#{prev_order.line_items.length} " \
"additional items already confirmed for this order cycle"
)
end
end
it "don't display any message if distributor don't allow order changes" do
order.distributor.allow_order_changes = false
order.distributor.save
visit checkout_step_path(:summary)
expect(page).not_to have_content("You have an order for this order cycle already.")
end
end
describe "order confirmation page" do
let(:completed_order) {
create(:order_ready_to_ship, distributor:,
order_cycle:, user_id: user.id)
}
let(:payment) { completed_order.payments.first }
context "an order with balance due" do
before { set_up_order(-10, "balance_due") }
it "displays balance due and paid state" do
expect(page).to have_selector('h5', text: "Balance Due")
expect(page).to have_selector('h5', text: with_currency(10))
confirmation_page_expect_paid(paid_state: "NOT PAID", paid_amount: 40)
end
end
context "an order with credit owed" do
before { set_up_order(10, "credit_owed") }
it "displays Credit owned and paid state" do
expect(page).to have_selector('h5', text: "Credit Owed")
expect(page).to have_selector('h5', text: with_currency(-10))
confirmation_page_expect_paid(paid_state: "PAID", paid_amount: 60)
end
end
context "an order with no outstanding balance" do
before { set_up_order(0, "paid") }
it "displays paid state" do
expect(page).not_to have_selector('h5', text: "Credit Owed")
expect(page).not_to have_selector('h5', text: "Balance Due")
confirmation_page_expect_paid(paid_state: "PAID", paid_amount: 50)
end
end
end
end
def confirmation_page_expect_paid(paid_state:, paid_amount:)
expect(page).to have_content paid_state.to_s
expect(page).to have_selector('strong', text: "Amount Paid")
expect(page).to have_selector('strong', text: with_currency(paid_amount))
end
def add_voucher_to_order(voucher, order)
voucher.create_adjustment(voucher.code, order)
OrderManagement::Order::Updater.new(order).update_voucher
end
def set_up_order(balance, state)
payment.update!(amount: payment.amount + balance)
completed_order.reload
expect(completed_order.payment_state).to eq state.to_s
visit "/orders/#{completed_order.number}"
end
end