mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-21 05:09:15 +00:00
Merge branch 'master' into tb-rescue-not-found-error-for-enterprise
This commit is contained in:
@@ -150,7 +150,8 @@ RSpec.configure do |config|
|
||||
|
||||
# Reset locale for all specs.
|
||||
config.around(:each) do |example|
|
||||
I18n.with_locale(:en_AU) { example.run }
|
||||
locale = ENV.fetch('LOCALE', 'en_TST')
|
||||
I18n.with_locale(locale) { example.run }
|
||||
end
|
||||
|
||||
# Reset all feature toggles to prevent leaking.
|
||||
@@ -264,6 +265,7 @@ RSpec.configure do |config|
|
||||
config.include OpenFoodNetwork::PerformanceHelper
|
||||
config.include ActiveJob::TestHelper
|
||||
config.include ReportsHelper
|
||||
config.include MailersHelper, type: :mailer
|
||||
config.include TomSelectHelper, type: :system
|
||||
|
||||
config.include ViewComponent::TestHelpers, type: :component
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe "DistributorTitle tests", type: :component do
|
||||
RSpec.describe DistributorTitleComponent, type: :component do
|
||||
it "displays distributor title with its name" do
|
||||
render_inline(DistributorTitleComponent.new(name: "Freddy's Farm Shop")) {}
|
||||
render_inline(described_class.new(name: "Freddy's Farm Shop"))
|
||||
expect(page).to have_selector "h3", text: "Freddy's Farm Shop"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe "ExampleComponent tests", type: :component do
|
||||
RSpec.describe ExampleComponent, type: :component do
|
||||
it "displays the h1 with the given parameter" do
|
||||
render_inline(ExampleComponent.new(title: "Hello")) {}
|
||||
render_inline(described_class.new(title: "Hello"))
|
||||
expect(page).to have_selector "h1", text: "Hello"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,7 +55,9 @@ RSpec.describe Admin::SubscriptionLineItemsController, type: :controller do
|
||||
before { params.merge!(shop_id: shop.id) }
|
||||
|
||||
context "but the shop doesn't have permission to sell product in question" do
|
||||
let!(:outgoing_exchange) {}
|
||||
let!(:outgoing_exchange) {
|
||||
# missing exchange should trigger an error
|
||||
}
|
||||
|
||||
it "returns an error" do
|
||||
spree_post :build, params
|
||||
|
||||
@@ -189,7 +189,7 @@ RSpec.describe Api::V0::ProductsController, type: :controller do
|
||||
# stock info - clone is set to zero
|
||||
it '(does not) clone the stock info of the product' do
|
||||
spree_post :clone, product_id: product.id, format: :json
|
||||
expect(json_response['on_hand']).to eq(0)
|
||||
expect(json_response.dig("variants", 0, "on_hand")).to eq(0)
|
||||
end
|
||||
|
||||
# variants: only the master variant of the product is cloned
|
||||
|
||||
@@ -369,7 +369,7 @@ RSpec.describe Api::V0::ShipmentsController, type: :controller do
|
||||
|
||||
before do
|
||||
allow(Spree::Order).to receive(:find_by!) { fee_order }
|
||||
allow(controller).to receive(:refuse_changing_cancelled_orders) {}
|
||||
allow(controller).to receive(:refuse_changing_cancelled_orders)
|
||||
allow(fee_order).to receive(:contents) { contents }
|
||||
allow(contents).to receive_messages(add: {}, remove: {})
|
||||
allow(fee_order).to receive_message_chain(:shipments, :find_by!) { fee_order_shipment }
|
||||
|
||||
@@ -328,6 +328,52 @@ RSpec.describe CheckoutController, type: :controller do
|
||||
expect_cable_ready_redirect(response)
|
||||
end
|
||||
end
|
||||
|
||||
context "with existing invalid payments" do
|
||||
let(:invalid_payments) {
|
||||
[
|
||||
create(:payment, state: :failed),
|
||||
create(:payment, state: :void),
|
||||
]
|
||||
}
|
||||
|
||||
before do
|
||||
order.payments = invalid_payments
|
||||
end
|
||||
|
||||
it "deletes invalid payments" do
|
||||
expect{
|
||||
put(:update, params:)
|
||||
}.to change { order.payments.to_a }.from(invalid_payments)
|
||||
end
|
||||
end
|
||||
|
||||
context "with different payment method previously chosen" do
|
||||
let(:other_payment_method) { build(:payment_method, distributors: [distributor]) }
|
||||
let(:other_payment) {
|
||||
build(:payment, amount: order.total, payment_method: other_payment_method)
|
||||
}
|
||||
|
||||
before do
|
||||
order.payments = [other_payment]
|
||||
end
|
||||
|
||||
context "and order is in an earlier state" do
|
||||
# This revealed obscure bug #12693. If you progress to order summary, go back to payment
|
||||
# method, then open delivery details in a new tab (or hover over the link with Turbo
|
||||
# enabled), then submit new payment details, this happens.
|
||||
|
||||
before do
|
||||
order.back_to_address
|
||||
end
|
||||
|
||||
it "deletes invalid (old) payments" do
|
||||
put(:update, params:)
|
||||
order.payments.reload
|
||||
expect(order.payments).not_to include other_payment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with no payment source" do
|
||||
|
||||
@@ -7,7 +7,7 @@ RSpec.describe ExtraFields do
|
||||
|
||||
describe "#invalid_query_param" do
|
||||
it "renders error" do
|
||||
allow(dummy_controller).to receive(:render) {}
|
||||
allow(dummy_controller).to receive(:render)
|
||||
dummy_controller.invalid_query_param("param", :unprocessable_entity, "error message")
|
||||
expect(dummy_controller).to have_received(:render).with(
|
||||
json:
|
||||
@@ -44,7 +44,7 @@ RSpec.describe ExtraFields do
|
||||
|
||||
context "when fields not in available fields" do
|
||||
it "calls invalid_query_param" do
|
||||
allow(dummy_controller).to receive(:invalid_query_param) {}
|
||||
allow(dummy_controller).to receive(:invalid_query_param)
|
||||
allow(dummy_controller).to receive(:params).
|
||||
and_return({ extra_fields: { customer: "unknown" } })
|
||||
dummy_controller.extra_fields(:customer, [:balance])
|
||||
|
||||
@@ -73,7 +73,7 @@ RSpec.describe Spree::CreditCardsController, type: :controller do
|
||||
{
|
||||
format: :json,
|
||||
exp_month: 12,
|
||||
exp_year: Time.now.year.next,
|
||||
exp_year: Time.zone.now.year.next,
|
||||
last4: 4242,
|
||||
token:,
|
||||
cc_type: "visa"
|
||||
|
||||
@@ -61,9 +61,11 @@ FactoryBot.define do
|
||||
end
|
||||
|
||||
factory :enterprise_relationship do
|
||||
nil
|
||||
end
|
||||
|
||||
factory :enterprise_role do
|
||||
nil
|
||||
end
|
||||
|
||||
factory :enterprise_group, class: EnterpriseGroup do
|
||||
|
||||
@@ -4,8 +4,8 @@ FactoryBot.define do
|
||||
factory :enterprise, class: Enterprise do
|
||||
transient do
|
||||
users { [] }
|
||||
logo {}
|
||||
promo_image {}
|
||||
logo { nil }
|
||||
promo_image { nil }
|
||||
end
|
||||
|
||||
owner factory: :user
|
||||
|
||||
@@ -6,6 +6,7 @@ FactoryBot.define do
|
||||
uid { user&.email || generate(:random_email) }
|
||||
|
||||
# This is a live test account authenticated via Les Communes.
|
||||
# See .env.test for tips on connecting the account for recording VCR cassettes.
|
||||
factory :testdfc_account do
|
||||
uid { "testdfc@protonmail.com" }
|
||||
refresh_token { ENV.fetch("OPENID_REFRESH_TOKEN") }
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -8,6 +8,10 @@ RSpec.describe BackorderJob do
|
||||
let(:chia_seed) { chia_item.variant }
|
||||
let(:chia_item) { order.line_items.second }
|
||||
let(:user) { order.distributor.owner }
|
||||
let(:catalog_json) { file_fixture("fdc-catalog.json").read }
|
||||
let(:catalog_url) {
|
||||
"https://env-0105831.jcloud-ver-jpe.ik-server.com/api/dfc/Enterprises/test-hodmedod/SuppliedProducts"
|
||||
}
|
||||
let(:product_link) {
|
||||
"https://env-0105831.jcloud-ver-jpe.ik-server.com/api/dfc/Enterprises/test-hodmedod/SuppliedProducts/44519466467635"
|
||||
}
|
||||
@@ -105,6 +109,27 @@ RSpec.describe BackorderJob do
|
||||
perform_enqueued_jobs(only: CompleteBackorderJob)
|
||||
end
|
||||
|
||||
it "skips unavailable items" do
|
||||
order.order_cycle = create(
|
||||
:simple_order_cycle,
|
||||
distributors: [order.distributor],
|
||||
variants: [beans],
|
||||
)
|
||||
beans.on_demand = true
|
||||
beans.on_hand = -3
|
||||
beans.semantic_links << SemanticLink.new(
|
||||
semantic_id: "#{product_link}-removed"
|
||||
)
|
||||
|
||||
stub_request(:get, catalog_url).to_return(body: catalog_json)
|
||||
|
||||
expect {
|
||||
subject.place_backorder(order)
|
||||
}.not_to change { beans.on_hand }
|
||||
|
||||
# And no error was raised, no order was placed.
|
||||
end
|
||||
|
||||
it "succeeds when no work to be done" do
|
||||
# The database can change before the job is run. So maybe there's nothing
|
||||
# to do.
|
||||
|
||||
110
spec/jobs/open_order_cycle_job_spec.rb
Normal file
110
spec/jobs/open_order_cycle_job_spec.rb
Normal file
@@ -0,0 +1,110 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe OpenOrderCycleJob do
|
||||
let(:now){ Time.zone.now }
|
||||
let(:order_cycle) { create(:simple_order_cycle, orders_open_at: now) }
|
||||
subject { OpenOrderCycleJob.perform_now(order_cycle.id) }
|
||||
|
||||
around do |example|
|
||||
Timecop.freeze(now) { example.run }
|
||||
end
|
||||
|
||||
it "marks as open" do
|
||||
expect {
|
||||
subject
|
||||
order_cycle.reload
|
||||
}
|
||||
.to change { order_cycle.opened_at }
|
||||
|
||||
expect(order_cycle.opened_at).to be_within(1).of(now)
|
||||
end
|
||||
|
||||
it "enqueues webhook job" do
|
||||
expect(OrderCycles::WebhookService)
|
||||
.to receive(:create_webhook_job).with(order_cycle, 'order_cycle.opened', now).once
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
describe "syncing remote products" do
|
||||
let!(:user) { create(:testdfc_user, owned_enterprises: [enterprise]) }
|
||||
|
||||
let(:enterprise) { create(:supplier_enterprise) }
|
||||
let!(:variant) { create(:variant, name: "Sauce", supplier_id: enterprise.id) }
|
||||
let!(:order_cycle) {
|
||||
create(:simple_order_cycle, orders_open_at: now,
|
||||
suppliers: [enterprise], variants: [variant])
|
||||
}
|
||||
|
||||
it "synchronises products from a FDC catalog", vcr: true do
|
||||
user.update!(oidc_account: build(:testdfc_account))
|
||||
# One product is existing in OFN
|
||||
product_id =
|
||||
"https://env-0105831.jcloud-ver-jpe.ik-server.com/api/dfc/Enterprises/test-hodmedod/SuppliedProducts/44519466467635"
|
||||
variant.semantic_links << SemanticLink.new(semantic_id: product_id)
|
||||
|
||||
expect {
|
||||
subject
|
||||
variant.reload
|
||||
order_cycle.reload
|
||||
}.to change { order_cycle.opened_at }
|
||||
.and change { enterprise.supplied_products.count }.by(0) # It shouldn't add, only update
|
||||
.and change { variant.display_name }
|
||||
.and change { variant.unit_value }
|
||||
# 18.85 wholesale variant price divided by 12 cans in the slab.
|
||||
.and change { variant.price }.to(1.57)
|
||||
.and change { variant.on_demand }.to(true)
|
||||
.and change { variant.on_hand }.by(0)
|
||||
.and query_database 46
|
||||
end
|
||||
end
|
||||
|
||||
describe "concurrency", concurrency: true do
|
||||
let(:breakpoint) { Mutex.new }
|
||||
|
||||
it "doesn't open order cycle twice" do
|
||||
# Pause in the middle of the job to test if the second job is trying
|
||||
# to do the same thing at the same time.
|
||||
breakpoint.lock
|
||||
expect_any_instance_of(OpenOrderCycleJob).to(
|
||||
receive(:sync_remote_variants).and_wrap_original do |method, *args|
|
||||
breakpoint.synchronize { nil }
|
||||
method.call(*args)
|
||||
end
|
||||
)
|
||||
|
||||
expect(OrderCycles::WebhookService)
|
||||
.to receive(:create_webhook_job).with(order_cycle, 'order_cycle.opened', now).once
|
||||
|
||||
# Start two jobs in parallel:
|
||||
threads = [1, 2].map do
|
||||
Thread.new do
|
||||
# Disable printing expected error
|
||||
Thread.current.report_on_exception = false
|
||||
|
||||
OpenOrderCycleJob.perform_now(order_cycle.id)
|
||||
end
|
||||
end
|
||||
|
||||
# Wait for both to jobs to pause.
|
||||
# This can reveal a race condition.
|
||||
sleep 0.1
|
||||
|
||||
# Resume and complete both jobs:
|
||||
breakpoint.unlock
|
||||
|
||||
# Join the threads until an error is raised.
|
||||
# We expect one of them to raise an error but we don't know which one.
|
||||
expect {
|
||||
threads.pop.join
|
||||
threads.pop.join
|
||||
}.to raise_error ActiveRecord::RecordNotFound
|
||||
|
||||
# If the first `join` raised an error, we still need to wait for the
|
||||
# second thread to finish:
|
||||
threads.pop.join if threads.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,62 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe OrderCycleOpenedJob do
|
||||
let(:oc_opened_before) {
|
||||
create(:order_cycle, orders_open_at: 1.hour.ago)
|
||||
}
|
||||
let(:oc_opened_now) {
|
||||
create(:order_cycle, orders_open_at: Time.zone.now)
|
||||
}
|
||||
let(:oc_opening_soon) {
|
||||
create(:order_cycle, orders_open_at: 1.minute.from_now)
|
||||
}
|
||||
|
||||
it "enqueues jobs for recently opened order cycles only" do
|
||||
expect(OrderCycles::WebhookService)
|
||||
.to receive(:create_webhook_job).with(oc_opened_now, 'order_cycle.opened')
|
||||
|
||||
expect(OrderCycles::WebhookService)
|
||||
.not_to receive(:create_webhook_job).with(oc_opened_before, 'order_cycle.opened')
|
||||
|
||||
expect(OrderCycles::WebhookService)
|
||||
.not_to receive(:create_webhook_job).with(oc_opening_soon, 'order_cycle.opened')
|
||||
|
||||
OrderCycleOpenedJob.perform_now
|
||||
end
|
||||
|
||||
describe "concurrency", concurrency: true do
|
||||
let(:breakpoint) { Mutex.new }
|
||||
|
||||
it "doesn't place duplicate job when run concurrently" do
|
||||
oc_opened_now
|
||||
|
||||
# Pause jobs when placing new job:
|
||||
breakpoint.lock
|
||||
allow(OrderCycleOpenedJob).to(
|
||||
receive(:new).and_wrap_original do |method, *args|
|
||||
breakpoint.synchronize {}
|
||||
method.call(*args)
|
||||
end
|
||||
)
|
||||
|
||||
expect(OrderCycles::WebhookService)
|
||||
.to receive(:create_webhook_job).with(oc_opened_now, 'order_cycle.opened').once
|
||||
|
||||
# Start two jobs in parallel:
|
||||
threads = [
|
||||
Thread.new { OrderCycleOpenedJob.perform_now },
|
||||
Thread.new { OrderCycleOpenedJob.perform_now },
|
||||
]
|
||||
|
||||
# Wait for both to jobs to pause.
|
||||
# This can reveal a race condition.
|
||||
sleep 0.1
|
||||
|
||||
# Resume and complete both jobs:
|
||||
breakpoint.unlock
|
||||
threads.each(&:join)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -202,7 +202,7 @@ RSpec.describe SubscriptionPlacementJob do
|
||||
breakpoint.lock
|
||||
allow(PlaceProxyOrder).to(
|
||||
receive(:new).and_wrap_original do |method, *args|
|
||||
breakpoint.synchronize {}
|
||||
breakpoint.synchronize { nil }
|
||||
method.call(*args)
|
||||
end
|
||||
)
|
||||
|
||||
22
spec/jobs/trigger_order_cycles_to_open_job_spec.rb
Normal file
22
spec/jobs/trigger_order_cycles_to_open_job_spec.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe TriggerOrderCyclesToOpenJob do
|
||||
let(:oc_opened_before) {
|
||||
create(:simple_order_cycle, orders_open_at: 1.hour.ago)
|
||||
}
|
||||
let(:oc_opened_now) {
|
||||
create(:simple_order_cycle, orders_open_at: Time.zone.now)
|
||||
}
|
||||
let(:oc_opening_soon) {
|
||||
create(:simple_order_cycle, orders_open_at: 1.minute.from_now)
|
||||
}
|
||||
|
||||
it "enqueues jobs for recently opened order cycles only" do
|
||||
expect{ TriggerOrderCyclesToOpenJob.perform_now }
|
||||
.to enqueue_job(OpenOrderCycleJob).with(oc_opened_now.id)
|
||||
.and enqueue_job(OpenOrderCycleJob).with(oc_opened_before.id).exactly(0).times
|
||||
.and enqueue_job(OpenOrderCycleJob).with(oc_opening_soon.id).exactly(0).times
|
||||
end
|
||||
end
|
||||
@@ -3,9 +3,10 @@
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WebhookDeliveryJob do
|
||||
subject { WebhookDeliveryJob.new(url, event, data) }
|
||||
subject { WebhookDeliveryJob.new(url, event, data, at:) }
|
||||
let(:url) { 'https://test/endpoint' }
|
||||
let(:event) { 'order_cycle.opened' }
|
||||
let(:at) { 1.second.ago }
|
||||
let(:data) {
|
||||
{
|
||||
order_cycle_id: 123, name: "Order cycle 1", open_at: 1.minute.ago.to_s, tags: ["tag1", "tag2"]
|
||||
@@ -25,7 +26,7 @@ RSpec.describe WebhookDeliveryJob do
|
||||
Timecop.freeze do
|
||||
expected_body = {
|
||||
id: /.+/,
|
||||
at: Time.zone.now.to_s,
|
||||
at: at.to_s,
|
||||
event:,
|
||||
data:,
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ require 'spec_helper'
|
||||
module Reporting
|
||||
module Reports
|
||||
module Bananas
|
||||
class Base; end
|
||||
class Green; end
|
||||
class Yellow; end
|
||||
const_set "Base", Class.new
|
||||
const_set "Green", Class.new
|
||||
const_set "Yellow", Class.new
|
||||
end
|
||||
|
||||
module Orange
|
||||
class OrangeReport; end
|
||||
const_set "OrangeReport", Class.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,25 +3,31 @@
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe EnterpriseMailer do
|
||||
let!(:enterprise) { create(:enterprise) }
|
||||
let!(:user) { create(:user) }
|
||||
let(:enterprise) { build(:enterprise, name: "Fred's Farm") }
|
||||
|
||||
describe "#welcome" do
|
||||
it "sends a welcome email when given an enterprise" do
|
||||
EnterpriseMailer.welcome(enterprise).deliver_now
|
||||
subject(:mail) { EnterpriseMailer.welcome(enterprise) }
|
||||
|
||||
mail = ActionMailer::Base.deliveries.first
|
||||
it "sends a welcome email when given an enterprise" do
|
||||
expect(mail.subject)
|
||||
.to eq "#{enterprise.name} is now on #{Spree::Config[:site_name]}"
|
||||
.to eq "Fred's Farm is now on OFN Demo Site"
|
||||
end
|
||||
|
||||
it "does not set a reply-to email" do
|
||||
expect(mail.reply_to).to eq nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#manager_invitation" do
|
||||
subject(:mail) { EnterpriseMailer.manager_invitation(enterprise, user) }
|
||||
let(:user) { build(:user) }
|
||||
|
||||
it "should send a manager invitation email when given an enterprise and user" do
|
||||
EnterpriseMailer.manager_invitation(enterprise, user).deliver_now
|
||||
expect(ActionMailer::Base.deliveries.count).to eq 1
|
||||
mail = ActionMailer::Base.deliveries.first
|
||||
expect(mail.subject).to eq "#{enterprise.name} has invited you to be a manager"
|
||||
expect(mail.subject).to eq "Fred's Farm has invited you to be a manager"
|
||||
end
|
||||
|
||||
it "sets a reply-to of the enterprise email" do
|
||||
expect(mail.reply_to).to eq([enterprise.contact.email])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -69,6 +69,10 @@ RSpec.describe Spree::OrderMailer do
|
||||
expect(email.body).to include('Payment summary')
|
||||
end
|
||||
|
||||
it "sets a reply-to of the customer email" do
|
||||
expect(email.reply_to).to eq([order.email])
|
||||
end
|
||||
|
||||
context 'when the order has outstanding balance' do
|
||||
before { allow(order).to receive(:new_outstanding_balance) { 123 } }
|
||||
|
||||
@@ -150,6 +154,28 @@ RSpec.describe Spree::OrderMailer do
|
||||
it "includes a link to the cancelled order in admin" do
|
||||
expect(mail.body).to match /#{admin_order_link_href}/
|
||||
end
|
||||
|
||||
it "sets a reply-to of the customer email" do
|
||||
expect(mail.reply_to).to eq([order.email])
|
||||
end
|
||||
end
|
||||
|
||||
describe "#cancel_email (for_customer)" do
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:order) { create(:order, distributor:, state: "canceled") }
|
||||
let(:mail) { Spree::OrderMailer.cancel_email(order) }
|
||||
|
||||
it "sends an email to the customer" do
|
||||
expect(mail.to).to eq([order.email])
|
||||
end
|
||||
|
||||
it "displays the order number" do
|
||||
expect(mail.body).to include(order.number.to_s)
|
||||
end
|
||||
|
||||
it "sets a reply-to of the customer email" do
|
||||
expect(mail.reply_to).to eq([order.distributor.contact.email])
|
||||
end
|
||||
end
|
||||
|
||||
describe "order confimation" do
|
||||
@@ -245,6 +271,7 @@ RSpec.describe Spree::OrderMailer do
|
||||
expect(deliveries.count).to eq(1)
|
||||
expect(deliveries.first.attachments.count).to eq(1)
|
||||
expect(deliveries.first.attachments.first.filename).to eq(attachment_filename)
|
||||
expect(email.reply_to).to eq([order.distributor.contact.email])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
46
spec/mailers/payment_mailer_spec.rb
Normal file
46
spec/mailers/payment_mailer_spec.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe PaymentMailer do
|
||||
describe '#payment_mailer' do
|
||||
let(:enterprise) { create(:enterprise) }
|
||||
let(:payment_method) {
|
||||
create(:payment_method, distributors: [order.distributor])
|
||||
}
|
||||
let(:payment) {
|
||||
create(:payment, order:, payment_method:)
|
||||
}
|
||||
let(:order) { create(:completed_order_with_totals) }
|
||||
|
||||
context "authorize payment email" do
|
||||
subject(:email) { described_class.authorize_payment(payment) }
|
||||
|
||||
it "includes the distributor's name in the subject" do
|
||||
expect(email.subject).to include("authorize your payment to #{order.distributor.name}")
|
||||
end
|
||||
|
||||
it "sets a reply-to of the customer email" do
|
||||
expect(email.reply_to).to eq([order.distributor.contact.email])
|
||||
end
|
||||
|
||||
it "includes a link to authorize the payment" do
|
||||
link = "http://test.host/payments/#{payment.id}/authorize"
|
||||
expect(email.text_part.body).to match link
|
||||
expect(html_body(email)).to have_link link, href: link
|
||||
end
|
||||
end
|
||||
|
||||
context "authorization required email" do
|
||||
subject(:email) { described_class.authorization_required(payment) }
|
||||
|
||||
it "includes the distributor's name in the subject" do
|
||||
expect(email.subject).to include("A payment requires authorization from the customer")
|
||||
end
|
||||
|
||||
it "sets a reply-to of the customer email" do
|
||||
expect(email.reply_to).to eq([order.email])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -83,13 +83,13 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
expect(line).to include 'QTY: 3'
|
||||
expect(line).to include '@ $10.00 = $30.00'
|
||||
end
|
||||
expect(body_as_html(mail).find("table.order-summary tr", text: p1.name))
|
||||
expect(html_body(mail).find("table.order-summary tr", text: p1.name))
|
||||
.to have_selector("td", text: "$30.00")
|
||||
end
|
||||
|
||||
it "displays tax totals for each product" do
|
||||
# Tax for p1 line items
|
||||
expect(body_as_html(mail).find("table.order-summary tr", text: p1.name))
|
||||
expect(html_body(mail).find("table.order-summary tr", text: p1.name))
|
||||
.to have_selector("td.tax", text: "$2.73")
|
||||
expect(
|
||||
product_line_from_order_summary_text(mail, p1.name)
|
||||
@@ -122,7 +122,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
|
||||
it "includes the total" do
|
||||
expect(mail.body.encoded).to include 'Total: $50.00'
|
||||
expect(body_as_html(mail).find("tr.total-row"))
|
||||
expect(html_body(mail).find("tr.total-row"))
|
||||
.to have_selector("td", text: "$50.00")
|
||||
end
|
||||
|
||||
@@ -151,15 +151,15 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
end
|
||||
|
||||
it "adds customer names table" do
|
||||
expect(body_as_html(mail).find(".order-summary.customer-order")).not_to be_nil
|
||||
expect(html_body(mail).find(".order-summary.customer-order")).not_to be_nil
|
||||
expect(customer_details_summary_text(mail)).to be_present
|
||||
end
|
||||
|
||||
it "displays last name for each order" do
|
||||
product_name = order.line_items.first.product.name
|
||||
last_name = order.billing_address.lastname
|
||||
expect(body_as_html(mail).find("table.order-summary.customer-order tr",
|
||||
text: product_name)).to have_selector("td", text: last_name)
|
||||
expect(html_body(mail).find("table.order-summary.customer-order tr",
|
||||
text: product_name)).to have_selector("td", text: last_name)
|
||||
expect(
|
||||
product_line_from_details_summary_text(mail, product_name)
|
||||
).to include(last_name)
|
||||
@@ -168,8 +168,8 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
it "displays first name for each order" do
|
||||
product_name = order.line_items.first.product.name
|
||||
first_name = order.billing_address.firstname
|
||||
expect(body_as_html(mail).find("table.order-summary.customer-order tr",
|
||||
text: product_name)).to have_selector("td", text: first_name)
|
||||
expect(html_body(mail).find("table.order-summary.customer-order tr",
|
||||
text: product_name)).to have_selector("td", text: first_name)
|
||||
expect(
|
||||
product_line_from_details_summary_text(mail, product_name)
|
||||
).to include(first_name)
|
||||
@@ -186,7 +186,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
|
||||
context "validate business name" do
|
||||
let(:table_header) do
|
||||
body_as_html(mail).find("table.order-summary.customer-order thead")
|
||||
html_body(mail).find("table.order-summary.customer-order thead")
|
||||
end
|
||||
|
||||
context "when no customer has customer code" do
|
||||
@@ -202,7 +202,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
it 'displays business name for the customer' do
|
||||
expect(table_header).to have_selector("th", text: 'Business Name')
|
||||
expect(
|
||||
body_as_html(mail).find("table.order-summary.customer-order tbody tr")
|
||||
html_body(mail).find("table.order-summary.customer-order tbody tr")
|
||||
).to have_selector("td", text: 'Test Business Name')
|
||||
expect(customer_details_summary_text(mail)).to include('Test Business Name')
|
||||
end
|
||||
@@ -217,7 +217,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
|
||||
it "does not add customer names table" do
|
||||
expect {
|
||||
body_as_html(mail).find(".order-summary.customer-order")
|
||||
html_body(mail).find(".order-summary.customer-order")
|
||||
}.to raise_error(Capybara::ElementNotFound)
|
||||
expect(customer_details_summary_text(mail)).to be_nil
|
||||
end
|
||||
@@ -236,7 +236,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
end
|
||||
|
||||
it "displays a supplier column" do
|
||||
expect(body_as_html(mail).find(".order-summary"))
|
||||
expect(html_body(mail).find(".order-summary"))
|
||||
.to have_selector("th", text: "Supplier")
|
||||
end
|
||||
|
||||
@@ -244,7 +244,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
before { order_cycle.coordinator.update!(show_customer_names_to_suppliers: true) }
|
||||
|
||||
it "displays a supplier column in the summary of orders grouped by customer" do
|
||||
expect(body_as_html(mail).find(".customer-order"))
|
||||
expect(html_body(mail).find(".customer-order"))
|
||||
.to have_selector("th", text: "Supplier")
|
||||
end
|
||||
end
|
||||
@@ -252,7 +252,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
|
||||
context "products from only one supplier" do
|
||||
it "doesn't display a supplier column" do
|
||||
expect(body_as_html(mail).find(".order-summary"))
|
||||
expect(html_body(mail).find(".order-summary"))
|
||||
.not_to have_selector("th", text: "Supplier")
|
||||
end
|
||||
|
||||
@@ -260,7 +260,7 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
before { order_cycle.coordinator.update!(show_customer_names_to_suppliers: true) }
|
||||
|
||||
it "doesn't display a supplier column in the summary of orders grouped by customer" do
|
||||
expect(body_as_html(mail).find(".customer-order"))
|
||||
expect(html_body(mail).find(".customer-order"))
|
||||
.not_to have_selector("th", text: "Supplier")
|
||||
end
|
||||
end
|
||||
@@ -272,10 +272,6 @@ RSpec.describe ProducerMailer, type: :mailer do
|
||||
mail.body.to_s.lines.select { |line| line.include? str }
|
||||
end
|
||||
|
||||
def body_as_html(mail)
|
||||
Capybara.string(mail.html_part.body.encoded)
|
||||
end
|
||||
|
||||
def body_as_text(mail)
|
||||
mail.text_part.body.decoded
|
||||
end
|
||||
|
||||
@@ -55,4 +55,14 @@ RSpec.describe Spree::ShipmentMailer do
|
||||
shipment_email = Spree::ShipmentMailer.shipped_email(shipment, delivery: false)
|
||||
expect(shipment_email.subject).to include("#{distributor.name} Pick up Notification")
|
||||
end
|
||||
|
||||
it "picked_up email has as the reply to email as the distributor" do
|
||||
shipment_email = Spree::ShipmentMailer.shipped_email(shipment, delivery: false)
|
||||
expect(shipment_email.reply_to).to eq([distributor.contact.email])
|
||||
end
|
||||
|
||||
it "shipment_email email has as the reply to email as the distributor" do
|
||||
shipment_email = Spree::ShipmentMailer.shipped_email(shipment, delivery: true)
|
||||
expect(shipment_email.reply_to).to eq([distributor.contact.email])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe ProductStock do
|
||||
let(:product) { create(:simple_product) }
|
||||
|
||||
context "when product has one variant" do
|
||||
describe "product.on_demand" do
|
||||
it "is the products first variant on_demand" do
|
||||
expect(product.on_demand).to eq(product.variants.first.on_demand)
|
||||
end
|
||||
end
|
||||
|
||||
describe "product.on_hand" do
|
||||
it "is the products first variant on_hand" do
|
||||
expect(product.on_hand).to eq(product.variants.first.on_hand)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when product has more than one variant' do
|
||||
before do
|
||||
product.variants << create(:variant, product:)
|
||||
end
|
||||
|
||||
describe "product.on_demand" do
|
||||
it "raises error" do
|
||||
expect { product.on_demand }
|
||||
.to raise_error(StandardError, /Cannot determine product on_demand value/)
|
||||
end
|
||||
end
|
||||
|
||||
describe "product.on_hand" do
|
||||
it "is the sum of the products variants on_hand values" do
|
||||
expect(product.on_hand)
|
||||
.to eq(product.variants.first.on_hand + product.variants.second.on_hand)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -102,7 +102,7 @@ RSpec.describe "Database" do
|
||||
orphaned_records_query = generate_orphaned_records_query(model_class, foreign_key_table_name,
|
||||
foreign_key_column)
|
||||
|
||||
migration = <<~MIGRATION
|
||||
<<~MIGRATION
|
||||
# Orphaned records can be found before running this migration with the following SQL:
|
||||
|
||||
#{orphaned_records_query}
|
||||
@@ -113,8 +113,6 @@ RSpec.describe "Database" do
|
||||
end
|
||||
end
|
||||
MIGRATION
|
||||
|
||||
migration
|
||||
end
|
||||
|
||||
def generate_orphaned_records_query(model_class, foreign_key_table_name, foreign_key_column)
|
||||
|
||||
@@ -110,9 +110,9 @@ RSpec.describe ProductImport::EntryValidator do
|
||||
describe "inventory validation" do
|
||||
before do
|
||||
allow(entry_validator).to receive(:import_into_inventory?) { true }
|
||||
allow(entry_validator).to receive(:enterprise_validation) {}
|
||||
allow(entry_validator).to receive(:producer_validation) {}
|
||||
allow(entry_validator).to receive(:variant_of_product_validation) {}
|
||||
allow(entry_validator).to receive(:enterprise_validation)
|
||||
allow(entry_validator).to receive(:producer_validation)
|
||||
allow(entry_validator).to receive(:variant_of_product_validation)
|
||||
end
|
||||
|
||||
context "products exist" do
|
||||
|
||||
@@ -157,9 +157,7 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(importer.updated_ids).to be_a(Array)
|
||||
expect(importer.updated_ids.count).to eq 5
|
||||
|
||||
carrots = Spree::Product.find_by(name: 'Carrots')
|
||||
carrots_variant = carrots.variants.first
|
||||
expect(carrots.on_hand).to eq 5
|
||||
carrots_variant = find_variant("Carrots")
|
||||
|
||||
expect(carrots_variant.supplier).to eq enterprise
|
||||
expect(carrots_variant.price).to eq 3.20
|
||||
@@ -167,11 +165,10 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(carrots_variant.variant_unit).to eq 'weight'
|
||||
expect(carrots_variant.variant_unit_scale).to eq 1
|
||||
expect(carrots_variant.on_demand).not_to eq true
|
||||
expect(carrots_variant.on_hand).to eq 5
|
||||
expect(carrots_variant.import_date).to be_within(1.minute).of Time.zone.now
|
||||
|
||||
potatoes = Spree::Product.find_by(name: 'Potatoes')
|
||||
potatoes_variant = potatoes.variants.first
|
||||
expect(potatoes.on_hand).to eq 6
|
||||
potatoes_variant = find_variant("Potatoes")
|
||||
|
||||
expect(potatoes_variant.supplier).to eq enterprise
|
||||
expect(potatoes_variant.price).to eq 6.50
|
||||
@@ -179,11 +176,10 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(potatoes_variant.variant_unit).to eq 'weight'
|
||||
expect(potatoes_variant.variant_unit_scale).to eq 1000
|
||||
expect(potatoes_variant.on_demand).not_to eq true
|
||||
expect(potatoes_variant.on_hand).to eq 6
|
||||
expect(potatoes_variant.import_date).to be_within(1.minute).of Time.zone.now
|
||||
|
||||
pea_soup = Spree::Product.find_by(name: 'Pea Soup')
|
||||
pea_soup_variant = pea_soup.variants.first
|
||||
expect(pea_soup.on_hand).to eq 8
|
||||
pea_soup_variant = find_variant("Pea Soup")
|
||||
|
||||
expect(pea_soup_variant.supplier).to eq enterprise
|
||||
expect(pea_soup_variant.price).to eq 5.50
|
||||
@@ -191,11 +187,10 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(pea_soup_variant.variant_unit).to eq 'volume'
|
||||
expect(pea_soup_variant.variant_unit_scale).to eq 0.001
|
||||
expect(pea_soup_variant.on_demand).not_to eq true
|
||||
expect(pea_soup_variant.on_hand).to eq 8
|
||||
expect(pea_soup_variant.import_date).to be_within(1.minute).of Time.zone.now
|
||||
|
||||
salad = Spree::Product.find_by(name: 'Salad')
|
||||
salad_variant = salad.variants.first
|
||||
expect(salad.on_hand).to eq 7
|
||||
salad_variant = find_variant("Salad")
|
||||
|
||||
expect(salad_variant.supplier).to eq enterprise
|
||||
expect(salad_variant.price).to eq 4.50
|
||||
@@ -203,11 +198,10 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(salad_variant.variant_unit).to eq 'items'
|
||||
expect(salad_variant.variant_unit_scale).to eq nil
|
||||
expect(salad_variant.on_demand).not_to eq true
|
||||
expect(salad_variant.on_hand).to eq 7
|
||||
expect(salad_variant.import_date).to be_within(1.minute).of Time.zone.now
|
||||
|
||||
buns = Spree::Product.find_by(name: 'Hot Cross Buns')
|
||||
buns_variant = buns.variants.first
|
||||
expect(buns.on_hand).to eq 7
|
||||
buns_variant = find_variant("Hot Cross Buns")
|
||||
|
||||
expect(buns_variant.supplier).to eq enterprise
|
||||
expect(buns_variant.price).to eq 3.50
|
||||
@@ -215,6 +209,7 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(buns_variant.variant_unit).to eq 'items'
|
||||
expect(buns_variant.variant_unit_scale).to eq nil
|
||||
expect(buns_variant.on_demand).to eq true
|
||||
expect(buns_variant.on_hand).to eq 7
|
||||
expect(buns_variant.import_date).to be_within(1.minute).of Time.zone.now
|
||||
end
|
||||
end
|
||||
@@ -248,9 +243,8 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(importer.updated_ids).to be_a(Array)
|
||||
expect(importer.updated_ids.count).to eq 1
|
||||
|
||||
carrots = Spree::Product.find_by(name: 'Good Carrots')
|
||||
carrots_variant = carrots.variants.first
|
||||
expect(carrots.on_hand).to eq 5
|
||||
carrots_variant = find_variant("Good Carrots")
|
||||
expect(carrots_variant.on_hand).to eq 5
|
||||
expect(carrots_variant.supplier).to eq enterprise
|
||||
expect(carrots_variant.price).to eq 3.20
|
||||
expect(carrots_variant.import_date).to be_within(1.minute).of Time.zone.now
|
||||
@@ -295,11 +289,9 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
|
||||
expect(importer.products_created_count).to eq 1
|
||||
|
||||
carrots = Spree::Product.find_by(name: 'Good Carrots')
|
||||
carrots_variant = carrots.variants.first
|
||||
|
||||
expect(carrots.on_hand).to eq 5
|
||||
carrots_variant = find_variant("Good Carrots")
|
||||
|
||||
expect(carrots_variant.on_hand).to eq 5
|
||||
expect(carrots_variant.primary_taxon.name).to eq "Vegetables"
|
||||
expect(carrots_variant.supplier).to eq enterprise
|
||||
expect(carrots_variant.price).to eq 3.20
|
||||
@@ -565,11 +557,11 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
expect(importer.updated_ids).to be_a(Array)
|
||||
expect(importer.updated_ids.count).to eq 2
|
||||
|
||||
beetroot = Spree::Product.find_by(name: 'Beetroot').variants.first
|
||||
beetroot = find_variant("Beetroot")
|
||||
expect(beetroot.price).to eq 3.50
|
||||
expect(beetroot.on_demand).not_to eq true
|
||||
|
||||
tomato = Spree::Product.find_by(name: 'Tomato').variants.first
|
||||
tomato = find_variant("Tomato")
|
||||
expect(tomato.price).to eq 5.50
|
||||
expect(tomato.on_demand).to eq true
|
||||
end
|
||||
@@ -960,12 +952,11 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
|
||||
expect(importer.products_reset_count).to eq 7
|
||||
|
||||
expect(Spree::Product.find_by(name: 'Carrots').on_hand).to eq 5 # Present in file, added
|
||||
expect(Spree::Product.find_by(name: 'Beans').on_hand).to eq 6 # Present in file, updated
|
||||
expect(Spree::Product.find_by(name: 'Sprouts').on_hand).to eq 0 # In enterprise, not file
|
||||
expect(Spree::Product.find_by(name: 'Cabbage').on_hand).to eq 0 # In enterprise, not file
|
||||
expect(Spree::Product.find_by(name: 'Lettuce').on_hand)
|
||||
.to eq 100 # In different enterprise; unchanged
|
||||
expect(find_variant("Carrots").on_hand).to eq 5 # Present in file, added
|
||||
expect(find_variant("Beans").on_hand).to eq 6 # Present in file, updated
|
||||
expect(find_variant("Sprouts").on_hand).to eq 0 # In enterprise, not file
|
||||
expect(find_variant("Cabbage").on_hand).to eq 0 # In enterprise, not file
|
||||
expect(find_variant("Lettuce").on_hand).to eq 100 # In different enterprise; unchanged
|
||||
end
|
||||
|
||||
it "can reset all inventory items for an enterprise that are not present " \
|
||||
@@ -1036,6 +1027,10 @@ RSpec.describe ProductImport::ProductImporter do
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def find_variant(name)
|
||||
Spree::Product.find_by(name:).variants.first
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -7,7 +7,7 @@ RSpec.describe Spree::Gateway do
|
||||
Class.new(Spree::Gateway) do
|
||||
def provider_class
|
||||
Class.new do
|
||||
def initialize(options = {}); end
|
||||
def initialize(*); end
|
||||
|
||||
def imaginary_method; end
|
||||
end
|
||||
|
||||
@@ -607,19 +607,6 @@ RSpec.describe Spree::Payment do
|
||||
end
|
||||
end
|
||||
|
||||
context "#can_credit?" do
|
||||
it "is true if credit_allowed > 0" do
|
||||
payment = build_stubbed(:payment)
|
||||
allow(payment).to receive(:credit_allowed) { 100 }
|
||||
expect(payment.can_credit?).to be true
|
||||
end
|
||||
it "is false if credit_allowed is 0" do
|
||||
payment = build_stubbed(:payment)
|
||||
allow(payment).to receive(:credit_allowed) { 0 }
|
||||
expect(payment.can_credit?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "#save" do
|
||||
context "completed payments" do
|
||||
it "updates order payment total" do
|
||||
@@ -868,7 +855,7 @@ RSpec.describe Spree::Payment do
|
||||
let(:payment) { build_stubbed(:payment) }
|
||||
|
||||
it "returns the parameter amount when given" do
|
||||
expect(payment.__send__(:calculate_refund_amount, 123)).to be === 123.0
|
||||
expect(payment.__send__(:calculate_refund_amount, 123)).to eq(123)
|
||||
end
|
||||
|
||||
it "refunds up to the value of the payment when the outstanding balance is larger" do
|
||||
|
||||
@@ -7,12 +7,15 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
create(
|
||||
:simple_order_cycle,
|
||||
name: "Order cycle 1",
|
||||
orders_open_at: "2022-09-19 09:00:00".to_time,
|
||||
orders_close_at: "2022-09-19 17:00:00".to_time,
|
||||
orders_open_at: Time.zone.parse("2022-09-19 09:00:00"),
|
||||
opened_at: Time.zone.parse("2022-09-19 09:00:01"),
|
||||
orders_close_at: Time.zone.parse("2022-09-19 17:00:00"),
|
||||
coordinator:,
|
||||
)
|
||||
}
|
||||
let(:coordinator) { create :distributor_enterprise, name: "Starship Enterprise" }
|
||||
let(:at) { Time.zone.parse("2022-09-19 09:00:02") }
|
||||
subject { OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened", at) }
|
||||
|
||||
describe "creating payloads" do
|
||||
it "doesn't create webhook payload for enterprise users" do
|
||||
@@ -21,7 +24,7 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
coordinator_user = create(:user, enterprises: [coordinator])
|
||||
coordinator_user.webhook_endpoints.create!(url: "http://coordinator_user_url")
|
||||
|
||||
expect{ OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened") }
|
||||
expect{ subject }
|
||||
.not_to enqueue_job(WebhookDeliveryJob).with("http://coordinator_user_url", any_args)
|
||||
end
|
||||
|
||||
@@ -31,7 +34,7 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
end
|
||||
|
||||
it "creates webhook payload for order cycle coordinator" do
|
||||
expect{ OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened") }
|
||||
expect{ subject }
|
||||
.to enqueue_job(WebhookDeliveryJob).with("http://coordinator_owner_url", any_args)
|
||||
end
|
||||
|
||||
@@ -42,21 +45,22 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
data = {
|
||||
id: order_cycle.id,
|
||||
name: "Order cycle 1",
|
||||
orders_open_at: "2022-09-19 09:00:00".to_time,
|
||||
orders_close_at: "2022-09-19 17:00:00".to_time,
|
||||
orders_open_at: Time.zone.parse("2022-09-19 09:00:00"),
|
||||
opened_at: Time.zone.parse("2022-09-19 09:00:01"),
|
||||
orders_close_at: Time.zone.parse("2022-09-19 17:00:00"),
|
||||
coordinator_id: coordinator.id,
|
||||
coordinator_name: "Starship Enterprise",
|
||||
}
|
||||
|
||||
expect{ OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened") }
|
||||
expect{ subject }
|
||||
.to enqueue_job(WebhookDeliveryJob).exactly(1).times
|
||||
.with("http://coordinator_owner_url", "order_cycle.opened", hash_including(data))
|
||||
.with("http://coordinator_owner_url", "order_cycle.opened", hash_including(data), at:)
|
||||
end
|
||||
end
|
||||
|
||||
context "coordinator owner doesn't have endpoint configured" do
|
||||
it "doesn't create webhook payload" do
|
||||
expect{ OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened") }
|
||||
expect{ subject }
|
||||
.not_to enqueue_job(WebhookDeliveryJob)
|
||||
end
|
||||
end
|
||||
@@ -84,13 +88,13 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
coordinator_name: "Starship Enterprise",
|
||||
}
|
||||
|
||||
expect{
|
||||
OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened")
|
||||
}
|
||||
expect{ subject }
|
||||
.to enqueue_job(WebhookDeliveryJob).with("http://distributor1_owner_url",
|
||||
"order_cycle.opened", hash_including(data))
|
||||
"order_cycle.opened", hash_including(data),
|
||||
at:)
|
||||
.and enqueue_job(WebhookDeliveryJob).with("http://distributor2_owner_url",
|
||||
"order_cycle.opened", hash_including(data))
|
||||
"order_cycle.opened", hash_including(data),
|
||||
at:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -107,9 +111,7 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
it "creates only one webhook payload for the user's endpoint" do
|
||||
user.webhook_endpoints.create! url: "http://coordinator_owner_url"
|
||||
|
||||
expect{
|
||||
OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened")
|
||||
}
|
||||
expect{ subject }
|
||||
.to enqueue_job(WebhookDeliveryJob).with("http://coordinator_owner_url", any_args)
|
||||
end
|
||||
end
|
||||
@@ -131,9 +133,7 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
}
|
||||
|
||||
it "doesn't create a webhook payload for supplier owner" do
|
||||
expect{
|
||||
OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened")
|
||||
}
|
||||
expect{ subject }
|
||||
.not_to enqueue_job(WebhookDeliveryJob).with("http://supplier_owner_url", any_args)
|
||||
end
|
||||
end
|
||||
@@ -142,7 +142,7 @@ RSpec.describe OrderCycles::WebhookService do
|
||||
|
||||
context "without webhook subscribed to enterprise" do
|
||||
it "doesn't create webhook payload" do
|
||||
expect{ OrderCycles::WebhookService.create_webhook_job(order_cycle, "order_cycle.opened") }
|
||||
expect{ subject }
|
||||
.not_to enqueue_job(WebhookDeliveryJob)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ RSpec.describe Orders::CustomerCancellationService do
|
||||
|
||||
context "when an order is cancelled successfully" do
|
||||
it "notifies the distributor by email" do
|
||||
order = create(:order, completed_at: Time.now, state: 'complete')
|
||||
order = create(:order, completed_at: Time.zone.now, state: 'complete')
|
||||
|
||||
Orders::CustomerCancellationService.new(order).call
|
||||
|
||||
|
||||
7
spec/support/mailers_helper.rb
Normal file
7
spec/support/mailers_helper.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MailersHelper
|
||||
def html_body(mail)
|
||||
Capybara.string(mail.html_part.body.to_s)
|
||||
end
|
||||
end
|
||||
@@ -20,10 +20,17 @@ RSpec.describe "DFC Product Import" do
|
||||
it "imports from given catalog" do
|
||||
visit admin_product_import_path
|
||||
|
||||
fill_in "catalog_url", with: "invalid url"
|
||||
select enterprise.name, from: "Create products for enterprise"
|
||||
click_button "Preview"
|
||||
|
||||
expect(page).to have_content "This catalog URL is not valid"
|
||||
|
||||
# We are testing against our own catalog for now but we want to replace
|
||||
# this with the URL of another app when available.
|
||||
# We also add a common mistake: copying the URL with an extra space.
|
||||
host = Rails.application.default_url_options[:host]
|
||||
url = "http://#{host}/api/dfc/enterprises/#{enterprise.id}/catalog_items"
|
||||
url = " http://#{host}/api/dfc/enterprises/#{enterprise.id}/catalog_items"
|
||||
fill_in "catalog_url", with: url
|
||||
select enterprise.name, from: "Create products for enterprise"
|
||||
click_button "Preview"
|
||||
|
||||
@@ -569,12 +569,12 @@ RSpec.describe '
|
||||
let(:product) { order_cycle1.products.first }
|
||||
|
||||
before(:each) do
|
||||
@enterprise_user = create(:user)
|
||||
@enterprise_user.enterprise_roles.build(enterprise: supplier1).save
|
||||
@enterprise_user.enterprise_roles.build(enterprise: coordinator1).save
|
||||
@enterprise_user.enterprise_roles.build(enterprise: distributor1).save
|
||||
enterprise_user = create(:user)
|
||||
enterprise_user.enterprise_roles.build(enterprise: supplier1).save
|
||||
enterprise_user.enterprise_roles.build(enterprise: coordinator1).save
|
||||
enterprise_user.enterprise_roles.build(enterprise: distributor1).save
|
||||
|
||||
login_as @enterprise_user
|
||||
login_as enterprise_user
|
||||
end
|
||||
|
||||
describe "viewing the edit page" do
|
||||
@@ -583,14 +583,50 @@ RSpec.describe '
|
||||
distributors: [distributor1])
|
||||
end
|
||||
let!(:order) do
|
||||
create(:order_with_taxes, distributor: distributor1, ship_address: create(:address),
|
||||
product_price: 110, tax_rate_amount: 0.1, included_in_price: true,
|
||||
tax_rate_name: "Tax 1").tap do |order|
|
||||
order.create_tax_charge!
|
||||
order.update_shipping_fees!
|
||||
end
|
||||
create(
|
||||
:order_with_taxes,
|
||||
distributor: distributor1,
|
||||
order_cycle: order_cycle1,
|
||||
ship_address: create(:address),
|
||||
product_price: 110,
|
||||
tax_rate_amount: 0.1,
|
||||
included_in_price: true,
|
||||
tax_rate_name: "Tax 1"
|
||||
).tap do |order|
|
||||
# Add a values to the fees
|
||||
first_calculator = supplier_enterprise_fee1.calculator
|
||||
first_calculator.preferred_amount = 2.5
|
||||
first_calculator.save!
|
||||
|
||||
last_calculator = supplier_enterprise_fee2.calculator
|
||||
last_calculator.preferred_amount = 7.5
|
||||
last_calculator.save!
|
||||
|
||||
# Add all variant to the order cycle for a more realistic scenario
|
||||
order.variants.each do |v|
|
||||
first_exchange.variants << v
|
||||
order_cycle1.cached_outgoing_exchanges.first.variants << v
|
||||
end
|
||||
|
||||
variant1 = first_exchange.variants.first
|
||||
variant2 = last_exchange.variants.first
|
||||
|
||||
order.contents.add(variant1)
|
||||
order.contents.add(variant2)
|
||||
# make sure all the fees are applied to the order
|
||||
order.recreate_all_fees!
|
||||
|
||||
order.update_order!
|
||||
end
|
||||
end
|
||||
|
||||
let(:first_exchange) { order_cycle1.cached_incoming_exchanges.first }
|
||||
let(:last_exchange) { order_cycle1.cached_incoming_exchanges.last }
|
||||
let(:coordinator_fee) { order_cycle1.coordinator_fees.first }
|
||||
let(:distributor_fee) { order_cycle1.cached_outgoing_exchanges.first.enterprise_fees.first }
|
||||
let(:supplier_enterprise_fee1) { first_exchange.enterprise_fees.first }
|
||||
let(:supplier_enterprise_fee2) { last_exchange.enterprise_fees.first }
|
||||
|
||||
before do
|
||||
distributor1.update_attribute(:abn, '12345678')
|
||||
|
||||
@@ -608,15 +644,42 @@ RSpec.describe '
|
||||
end
|
||||
end
|
||||
|
||||
# shows the order items total
|
||||
within('fieldset#order-total') do
|
||||
expect(page).to have_selector "span.order-total", text: order.display_item_total
|
||||
end
|
||||
|
||||
# shows the order non-tax adjustments
|
||||
order.adjustments.eligible.each do |adjustment|
|
||||
expect(page).to have_selector "td", match: :first, text: adjustment.label
|
||||
expect(page).to have_selector "td.total", text: adjustment.display_amount
|
||||
within "#order_adjustments" do
|
||||
# supplier fees only apply to specific product
|
||||
first_exchange.variants.each do |variant|
|
||||
expect(page).to have_content(
|
||||
"#{variant.name} - #{supplier_enterprise_fee1.name} fee \
|
||||
by supplier #{supplier1.name}: $2.50".squish
|
||||
)
|
||||
expect(page).not_to have_content(
|
||||
"#{variant.name} - #{supplier_enterprise_fee2.name} fee \
|
||||
by supplier #{supplier2.name}: $7.50".squish
|
||||
)
|
||||
end
|
||||
|
||||
last_exchange.variants.each do |variant|
|
||||
expect(page).to have_content(
|
||||
"#{variant.name} - #{supplier_enterprise_fee2.name} fee \
|
||||
by supplier #{supplier2.name}: $7.50".squish
|
||||
)
|
||||
expect(page).not_to have_content(
|
||||
"#{variant.name} - #{supplier_enterprise_fee1.name} fee \
|
||||
by supplier #{supplier1.name}: $2.50".squish
|
||||
)
|
||||
end
|
||||
|
||||
## Coordinator fee and Distributor fee apply to all product
|
||||
order.variants.each do |variant|
|
||||
expect(page).to have_content(
|
||||
"#{variant.name} - #{coordinator_fee.name} fee \
|
||||
by coordinator #{coordinator1.name}: $0.00".squish
|
||||
)
|
||||
expect(page).to have_content(
|
||||
"#{variant.name} - #{distributor_fee.name} fee \
|
||||
by distributor #{distributor1.name}: $0.00".squish
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# shows the order total
|
||||
@@ -787,12 +850,11 @@ RSpec.describe '
|
||||
|
||||
find('.edit-method').click
|
||||
|
||||
# TODO assertion not working due to overlapping elements on new BUU design
|
||||
# expect(page).to have_select2('selected_shipping_rate_id',
|
||||
# with_options: [
|
||||
# shipping_method_for_distributor1.name,
|
||||
# different_shipping_method_for_distributor1.name
|
||||
# ], without_options: [shipping_method_for_distributor2.name])
|
||||
expect(page).to have_select2('selected_shipping_rate_id',
|
||||
with_options: [
|
||||
shipping_method_for_distributor1.name,
|
||||
different_shipping_method_for_distributor1.name
|
||||
], without_options: [shipping_method_for_distributor2.name])
|
||||
|
||||
select2_select(different_shipping_method_for_distributor1.name,
|
||||
from: 'selected_shipping_rate_id')
|
||||
@@ -803,7 +865,7 @@ RSpec.describe '
|
||||
)
|
||||
|
||||
within "#order-total" do
|
||||
expect(page).to have_content "$175.00"
|
||||
expect(page).to have_content "$239.98"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -818,6 +880,7 @@ RSpec.describe '
|
||||
order.shipment.adjustments.first.close
|
||||
distributor1.shipping_methods = [shipping_method_for_distributor1]
|
||||
end
|
||||
|
||||
context "shipment is shipped" do
|
||||
before do
|
||||
order.shipments.first.update_attribute(:state, 'shipped')
|
||||
@@ -830,7 +893,7 @@ RSpec.describe '
|
||||
)
|
||||
|
||||
within "#order-total" do
|
||||
expect(page).to have_content "$160.00"
|
||||
expect(page).to have_content "$224.98"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -846,7 +909,7 @@ RSpec.describe '
|
||||
)
|
||||
|
||||
within "#order-total" do
|
||||
expect(page).to have_content "$160.00"
|
||||
expect(page).to have_content "$224.98"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -864,7 +927,7 @@ RSpec.describe '
|
||||
)
|
||||
|
||||
within "#order-total" do
|
||||
expect(page).to have_content "$160.00"
|
||||
expect(page).to have_content "$224.98"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -881,7 +944,7 @@ RSpec.describe '
|
||||
)
|
||||
|
||||
within "#order-total" do
|
||||
expect(page).to have_content "$160.00"
|
||||
expect(page).to have_content "$224.98"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -93,7 +93,7 @@ RSpec.describe "Product Import" do
|
||||
carrots = Spree::Product.find_by(name: 'Carrots')
|
||||
potatoes = Spree::Product.find_by(name: 'Potatoes')
|
||||
expect(potatoes.variants.first.supplier).to eq enterprise
|
||||
expect(potatoes.on_hand).to eq 6
|
||||
expect(potatoes.variants.first.on_hand).to eq 6
|
||||
expect(potatoes.variants.first.price).to eq 6.50
|
||||
expect(potatoes.variants.first.import_date).to be_within(1.minute).of Time.zone.now
|
||||
|
||||
@@ -261,9 +261,9 @@ RSpec.describe "Product Import" do
|
||||
expect(page).to have_selector '.created-count', text: '1'
|
||||
expect(page).to have_selector '.reset-count', text: '3'
|
||||
|
||||
expect(Spree::Product.find_by(name: 'Carrots').on_hand).to eq 500
|
||||
expect(Spree::Product.find_by(name: 'Cabbage').on_hand).to eq 0
|
||||
expect(Spree::Product.find_by(name: 'Beans').on_hand).to eq 0
|
||||
expect(Spree::Product.find_by(name: 'Carrots').variants.first.on_hand).to eq 500
|
||||
expect(Spree::Product.find_by(name: 'Cabbage').variants.first.on_hand).to eq 0
|
||||
expect(Spree::Product.find_by(name: 'Beans').variants.first.on_hand).to eq 0
|
||||
end
|
||||
|
||||
it "can save a new product and variant of that product at the same time, " \
|
||||
|
||||
@@ -83,8 +83,11 @@ RSpec.describe "Shops caching", caching: true do
|
||||
it "caches rendered response for taxons and properties, with the provided options" do
|
||||
visit enterprise_shop_path(distributor)
|
||||
|
||||
expect(page).to have_content "Cached Taxon"
|
||||
expect(page).to have_content "Cached Property"
|
||||
# Ensure we test for the right text after AJAX loads filters:
|
||||
within(".sticky-shop-filters-container", text: "Filter by") do
|
||||
expect(page).to have_content "Cached Taxon"
|
||||
expect(page).to have_content "Cached Property"
|
||||
end
|
||||
|
||||
expect_cached taxons_key, options
|
||||
expect_cached properties_key, options
|
||||
@@ -95,23 +98,33 @@ RSpec.describe "Shops caching", caching: true do
|
||||
Timecop.travel(10.minutes.ago) do
|
||||
visit enterprise_shop_path(distributor)
|
||||
|
||||
expect(page).to have_content taxon.name
|
||||
expect(page).to have_content property.presentation
|
||||
# The page HTML contains the cached text but we need to test for the
|
||||
# visible filters which are loaded asynchronously.
|
||||
# Otherwise we may update the database before the AJAX requests
|
||||
# and cache the new data.
|
||||
within(".sticky-shop-filters-container", text: "Filter by") do
|
||||
expect(page).to have_content taxon.name
|
||||
expect(page).to have_content property.presentation
|
||||
end
|
||||
|
||||
variant.update_attribute(:primary_taxon, taxon2)
|
||||
product.update_attribute(:properties, [property2])
|
||||
|
||||
visit enterprise_shop_path(distributor)
|
||||
|
||||
expect(page).to have_content taxon.name # Taxon list is unchanged
|
||||
expect(page).to have_content property.presentation # Property list is unchanged
|
||||
within(".sticky-shop-filters-container", text: "Filter by") do
|
||||
expect(page).to have_content taxon.name # Taxon list is unchanged
|
||||
expect(page).to have_content property.presentation # Property list is unchanged
|
||||
end
|
||||
end
|
||||
|
||||
# A while later...
|
||||
visit enterprise_shop_path(distributor)
|
||||
|
||||
expect(page).to have_content taxon2.name
|
||||
expect(page).to have_content property2.presentation
|
||||
within(".sticky-shop-filters-container", text: "Filter by") do
|
||||
expect(page).to have_content taxon2.name
|
||||
expect(page).to have_content property2.presentation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user