diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index a354852391..1ad8853bb0 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -1,10 +1,12 @@ require 'spree/core/controller_helpers/order_decorator' +require 'spree/core/controller_helpers/auth_decorator' Spree::OrdersController.class_eval do before_filter :update_distribution, only: :update before_filter :filter_order_params, only: :update before_filter :enable_embedded_shopfront + prepend_before_filter :require_order_authentication, only: :show prepend_before_filter :require_order_cycle, only: :edit prepend_before_filter :require_distributor_chosen, only: :edit before_filter :check_hub_ready_for_checkout, only: :edit @@ -128,6 +130,13 @@ Spree::OrdersController.class_eval do private + def require_order_authentication + return if session[:access_token] || params[:token] || spree_current_user + + flash[:error] = I18n.t("spree.orders.edit.login_to_view_order") + require_login_then_redirect_to request.env['PATH_INFO'] + end + def order_to_update return @order_to_update if defined? @order_to_update return @order_to_update = current_order unless params[:id] diff --git a/config/locales/en.yml b/config/locales/en.yml index f34706ca29..7d3ce3b831 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2699,6 +2699,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using inventory: Inventory zipcode: Postcode orders: + edit: + login_to_view_order: "Please log in to view your order." bought: item: "Already ordered in this order cycle" shipment_states: diff --git a/lib/spree/core/controller_helpers/auth_decorator.rb b/lib/spree/core/controller_helpers/auth_decorator.rb new file mode 100644 index 0000000000..1c5f9de911 --- /dev/null +++ b/lib/spree/core/controller_helpers/auth_decorator.rb @@ -0,0 +1,5 @@ +Spree::Core::ControllerHelpers::Auth.class_eval do + def require_login_then_redirect_to(url) + redirect_to root_path(anchor: "login?after_login=#{url}") + end +end diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index b9012ff890..d2f2cbf8da 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -5,61 +5,133 @@ describe Spree::OrdersController, type: :controller do let(:order) { create(:order) } let(:order_cycle) { create(:simple_order_cycle) } - it "redirects home when no distributor is selected" do - spree_get :edit - expect(response).to redirect_to root_path - end - - it "redirects to shop when order is empty" 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 - allow(order).to receive_message_chain(:line_items, :empty?).and_return true - allow(order).to receive(:insufficient_stock_lines).and_return [] - session[:access_token] = order.token - spree_get :edit - expect(response).to redirect_to shop_path - end - - it "redirects to the shop when no order cycle is selected" do - allow(controller).to receive(:current_distributor).and_return(distributor) - spree_get :edit - expect(response).to redirect_to shop_path - end - - it "redirects home with message if hub is not ready for checkout" do - allow(VariantOverride).to receive(:indexed).and_return({}) - - order = subject.current_order(true) - allow(distributor).to receive(:ready_for_checkout?) { false } - allow(order).to receive_messages(distributor: distributor, order_cycle: order_cycle) - - expect(order).to receive(:empty!) - expect(order).to receive(:set_distribution!).with(nil, nil) - - spree_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 "when an item has insufficient stock" do - let(:order) { subject.current_order(true) } - let(:oc) { create(:simple_order_cycle, distributors: [d], variants: [variant]) } - let(:d) { create(:distributor_enterprise, shipping_methods: [create(:shipping_method)], payment_methods: [create(:payment_method)]) } - let(:variant) { create(:variant, on_demand: false, on_hand: 5) } - let(:line_item) { order.line_items.last } + describe "viewing an order" do + let(:customer) { create(:customer) } + let(:order) { create(:order_with_credit_payment, customer: customer, distributor: customer.enterprise) } before do - order.set_distribution! d, oc - order.add_variant variant, 5 - variant.update_attributes! on_hand: 3 + allow(controller).to receive(:spree_current_user) { current_user } end - it "displays a flash message when we view the cart" do + context "after checking out as an anonymous guest" do + let(:customer) { create(:customer, user: nil) } + let(:current_user) { nil } + + it "loads page" do + spree_get :show, id: order.number, token: order.token + expect(response).to be_success + end + + it "stores order token in session as 'access_token'" do + spree_get :show, id: order.number, token: order.token + expect(session[:access_token]).to eq(order.token) + end + end + + context "when returning to order page after checking out as an anonymous guest" do + let(:customer) { create(:customer, user: nil) } + let(:current_user) { nil } + + before do + session[:access_token] = order.token + end + + it "loads page" do + spree_get :show, id: order.number + expect(response).to be_success + end + end + + context "when logged in as the customer" do + let(:current_user) { order.user } + + it "loads page" do + spree_get :show, id: order.number + expect(response).to be_success + end + end + + context "when logged in as another customer" do + let(:current_user) { create(:user) } + + it "redirects to unauthorized" do + spree_get :show, id: order.number + expect(response.status).to eq(401) + end + end + + context "when neither checked out as an anonymous guest nor logged in" do + let(:current_user) { nil } + + before do + request.env["PATH_INFO"] = spree.order_path(order) + end + + it "redirects to unauthorized" do + spree_get :show, id: order.number + expect(response).to redirect_to(root_path(anchor: "login?after_login=#{spree.order_path(order)}")) + expect(flash[:error]).to eq("Please log in to view your order.") + end + end + end + + describe "viewing cart" do + it "redirects home when no distributor is selected" do spree_get :edit - expect(response.status).to eq 200 - expect(flash[:error]).to eq("An item in your cart has become unavailable.") + expect(response).to redirect_to root_path + end + + it "redirects to shop when order is empty" 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 + allow(order).to receive_message_chain(:line_items, :empty?).and_return true + allow(order).to receive(:insufficient_stock_lines).and_return [] + session[:access_token] = order.token + spree_get :edit + expect(response).to redirect_to shop_path + end + + it "redirects to the shop when no order cycle is selected" do + allow(controller).to receive(:current_distributor).and_return(distributor) + spree_get :edit + expect(response).to redirect_to shop_path + end + + it "redirects home with message if hub is not ready for checkout" do + allow(VariantOverride).to receive(:indexed).and_return({}) + + order = subject.current_order(true) + allow(distributor).to receive(:ready_for_checkout?) { false } + allow(order).to receive_messages(distributor: distributor, order_cycle: order_cycle) + + expect(order).to receive(:empty!) + expect(order).to receive(:set_distribution!).with(nil, nil) + + spree_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 "when an item has insufficient stock" do + let(:order) { subject.current_order(true) } + let(:oc) { create(:simple_order_cycle, distributors: [d], variants: [variant]) } + let(:d) { create(:distributor_enterprise, shipping_methods: [create(:shipping_method)], payment_methods: [create(:payment_method)]) } + let(:variant) { create(:variant, on_demand: false, on_hand: 5) } + let(:line_item) { order.line_items.last } + + before do + order.set_distribution! d, oc + order.add_variant variant, 5 + variant.update_attributes! on_hand: 3 + end + + it "displays a flash message when we view the cart" do + spree_get :edit + expect(response.status).to eq 200 + expect(flash[:error]).to eq("An item in your cart has become unavailable.") + end end end diff --git a/spec/features/consumer/shopping/orders_spec.rb b/spec/features/consumer/shopping/orders_spec.rb index ada9ba8020..4f0adfe772 100644 --- a/spec/features/consumer/shopping/orders_spec.rb +++ b/spec/features/consumer/shopping/orders_spec.rb @@ -3,6 +3,84 @@ require 'spec_helper' feature "Order Management", js: true do include AuthenticationWorkflow + describe "viewing a completed order" do + let!(:distributor) { create(:distributor_enterprise) } + let!(:customer) { create(:customer, user: user, enterprise: distributor) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } + + let!(:bill_address) { create(:address) } + let!(:ship_address) { create(:address) } + let!(:shipping_method) { create(:free_shipping_method, distributors: [distributor]) } + + let!(:order) do + create(:order_with_credit_payment, + customer: customer, + user: user, + distributor: distributor, + order_cycle: order_cycle + ) + end + + before do + # For some reason, both bill_address and ship_address are not set + # automatically. + # + # Also, assigning the shipping_method to a ShippingMethod instance results + # in a SystemStackError. + order.update_attributes!( + bill_address: bill_address, + ship_address: ship_address, + shipping_method_id: shipping_method.id + ) + end + + context "when checking out as an anonymous guest" do + let(:user) { nil } + + it "allows the user to see the details" do + # Cannot load the page without token + visit spree.order_path(order) + expect(page).to_not be_confirmed_order_page + + # Can load the page with token + visit spree.order_path(order, token: order.token) + expect(page).to be_confirmed_order_page + + # Can load the page even without the token, after loading the page with + # token. + visit spree.order_path(order) + expect(page).to be_confirmed_order_page + end + end + + context "when logged in as the customer" do + let(:user) { create(:user) } + + before do + login_as user + end + + it "allows the user to see order details" do + visit spree.order_path(order) + expect(page).to be_confirmed_order_page + end + end + + context "when not logged in" do + let(:user) { create(:user) } + + it "allows the user to see order details after login" do + # Cannot load the page without signing in + visit spree.order_path(order) + expect(page).to_not be_confirmed_order_page + + # Can load the page after signing in + fill_in_and_submit_login_form user + expect(page).to be_confirmed_order_page + end + end + end + describe "editing a completed order" do let(:address) { create(:address) } let(:user) { create(:user, bill_address: address, ship_address: address) } @@ -86,4 +164,8 @@ feature "Order Management", js: true do end end end + + def be_confirmed_order_page + have_content /Order #\w+ Confirmed NOT PAID/ + end end diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index 5d3181ea4f..7645db0012 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -54,9 +54,13 @@ module AuthenticationWorkflow user.spree_roles << user_role visit spree.login_path - fill_in 'email', :with => 'someone@ofn.org' - fill_in 'password', :with => 'passw0rd' - click_button 'Login' + fill_in_and_submit_login_form user + end + + def fill_in_and_submit_login_form(user) + fill_in "email", with: user.email + fill_in "password", with: user.password + click_button "Login" end end