mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-25 20:46:48 +00:00
491 lines
17 KiB
Ruby
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
|