mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-01 02:03:22 +00:00
Refactoring tag rule logic, placing in TagRuleApplicator lib class
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
require 'open_food_network/tag_rule_applicator'
|
||||
|
||||
class BaseController < ApplicationController
|
||||
include Spree::Core::ControllerHelpers
|
||||
include Spree::Core::ControllerHelpers::RespondWith
|
||||
@@ -19,12 +21,9 @@ class BaseController < ApplicationController
|
||||
@order_cycles = OrderCycle.with_distributor(@distributor).active
|
||||
.order(@distributor.preferred_shopfront_order_cycle_order)
|
||||
|
||||
@distributor.apply_tag_rules(
|
||||
type: "FilterOrderCycles",
|
||||
subject: @order_cycles,
|
||||
customer_tags: current_order.andand.customer.andand.tag_list || [],
|
||||
shop: @distributor
|
||||
)
|
||||
customer_tags = current_order.andand.customer.andand.tag_list
|
||||
applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor, "FilterOrderCycles", customer_tags)
|
||||
applicator.filter!(@order_cycles)
|
||||
|
||||
# And default to the only order cycle if there's only the one
|
||||
if @order_cycles.count == 1
|
||||
|
||||
@@ -15,7 +15,7 @@ class ShopController < BaseController
|
||||
|
||||
# If we add any more filtering logic, we should probably
|
||||
# move it all to a lib class like 'CachedProductsFilterer'
|
||||
products_json = filtered_json(renderer.products_json)
|
||||
products_json = filter(renderer.products_json)
|
||||
|
||||
render json: products_json
|
||||
|
||||
@@ -40,22 +40,22 @@ class ShopController < BaseController
|
||||
private
|
||||
|
||||
def filtered_json(products_json)
|
||||
tag_rules = relevant_tag_rules
|
||||
return apply_tag_rules(tag_rules, products_json) if tag_rules.any?
|
||||
products_json
|
||||
if applicator.send(:rules).any?
|
||||
filter(products_json)
|
||||
else
|
||||
products_json
|
||||
end
|
||||
end
|
||||
|
||||
def apply_tag_rules(tag_rules, products_json)
|
||||
def filter(products_json)
|
||||
products_hash = JSON.parse(products_json)
|
||||
current_distributor.apply_tag_rules(
|
||||
rules: tag_rules,
|
||||
subject: products_hash,
|
||||
customer_tags: current_order.andand.customer.andand.tag_list || []
|
||||
)
|
||||
applicator.filter!(products_hash)
|
||||
JSON.unparse(products_hash)
|
||||
end
|
||||
|
||||
def relevant_tag_rules
|
||||
TagRule.for(current_distributor).of_type("FilterProducts")
|
||||
def applicator
|
||||
return @applicator unless @applicator.nil?
|
||||
customer_tags = current_order.andand.customer.andand.tag_list
|
||||
@applicator = OpenFoodNetwork::TagRuleApplicator.new(current_distributor, "FilterProducts", customer_tags)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,13 +5,11 @@ module EnterprisesHelper
|
||||
|
||||
def available_shipping_methods
|
||||
shipping_methods = current_distributor.shipping_methods
|
||||
if current_distributor.present?
|
||||
current_distributor.apply_tag_rules(
|
||||
type: "FilterShippingMethods",
|
||||
subject: shipping_methods,
|
||||
customer_tags: current_order.andand.customer.andand.tag_list
|
||||
)
|
||||
end
|
||||
|
||||
customer_tags = current_order.andand.customer.andand.tag_list
|
||||
applicator = OpenFoodNetwork::TagRuleApplicator.new(current_distributor, "FilterShippingMethods", customer_tags)
|
||||
applicator.filter!(shipping_methods)
|
||||
|
||||
shipping_methods.uniq
|
||||
end
|
||||
|
||||
|
||||
@@ -344,13 +344,6 @@ class Enterprise < ActiveRecord::Base
|
||||
abn.present?
|
||||
end
|
||||
|
||||
def apply_tag_rules(context)
|
||||
tag_rules_for(context).each do |rule|
|
||||
rule.context = context
|
||||
rule.apply
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def devise_mailer
|
||||
@@ -459,10 +452,4 @@ class Enterprise < ActiveRecord::Base
|
||||
def initialize_permalink
|
||||
self.permalink = Enterprise.find_available_permalink(name)
|
||||
end
|
||||
|
||||
def tag_rules_for(context)
|
||||
rules = context[:rules] || tag_rules
|
||||
return rules unless context[:type]
|
||||
rules.merge(TagRule.of_type(context[:type]))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'open_food_network/enterprise_fee_calculator'
|
||||
require 'open_food_network/distribution_change_validator'
|
||||
require 'open_food_network/feature_toggle'
|
||||
require 'open_food_network/tag_rule_applicator'
|
||||
|
||||
ActiveSupport::Notifications.subscribe('spree.order.contents_changed') do |name, start, finish, id, payload|
|
||||
payload[:order].reload.update_distribution_charge!
|
||||
@@ -178,10 +179,6 @@ Spree::Order.class_eval do
|
||||
if order_cycle
|
||||
OpenFoodNetwork::EnterpriseFeeCalculator.new.create_order_adjustments_for self
|
||||
end
|
||||
|
||||
if distributor.present? && customer.present?
|
||||
distributor.apply_tag_rules(type: "DiscountOrder", subject: self, customer_tags: customer.andand.tag_list)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -214,7 +211,11 @@ Spree::Order.class_eval do
|
||||
def available_payment_methods
|
||||
return [] unless distributor.present?
|
||||
payment_methods = distributor.payment_methods.available(:front_end).all
|
||||
distributor.apply_tag_rules( type: "FilterPaymentMethods", subject: payment_methods, customer_tags: customer.andand.tag_list)
|
||||
|
||||
customer_tags = customer.andand.tag_list
|
||||
applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor, "FilterPaymentMethods", customer_tags)
|
||||
applicator.filter!(payment_methods)
|
||||
|
||||
payment_methods
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
class TagRule < ActiveRecord::Base
|
||||
attr_accessor :subject, :context
|
||||
|
||||
belongs_to :enterprise
|
||||
|
||||
preference :customer_tags, :string, default: ""
|
||||
@@ -10,25 +8,6 @@ class TagRule < ActiveRecord::Base
|
||||
attr_accessible :enterprise, :enterprise_id, :is_default, :preferred_customer_tags
|
||||
|
||||
scope :for, ->(enterprise) { where(enterprise_id: enterprise) }
|
||||
scope :of_type, ->(type) { where(type: "TagRule::#{type}") }
|
||||
|
||||
def context=(context)
|
||||
raise "Context for tag rule cannot be nil" if context.nil?
|
||||
raise "Subject for tag rule cannot be nil" if context[:subject].nil?
|
||||
|
||||
@context = context
|
||||
@subject = context[:subject]
|
||||
end
|
||||
|
||||
def apply
|
||||
if relevant?
|
||||
if customer_tags_match?
|
||||
apply!
|
||||
else
|
||||
apply_default! if respond_to?(:apply_default!,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.mapping_for(enterprises)
|
||||
self.for(enterprises).inject({}) do |mapping, rule|
|
||||
@@ -42,20 +21,4 @@ class TagRule < ActiveRecord::Base
|
||||
mapping
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relevant?
|
||||
return false unless subject_class_matches?
|
||||
if respond_to?(:additional_requirements_met?, true)
|
||||
return false unless additional_requirements_met?
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def customer_tags_match?
|
||||
context_customer_tags = context[:customer_tags] || []
|
||||
preferred_tags = preferred_customer_tags.split(",")
|
||||
(context_customer_tags & preferred_tags).any?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,33 +4,19 @@ class TagRule::FilterOrderCycles < TagRule
|
||||
|
||||
attr_accessible :preferred_matched_order_cycles_visibility, :preferred_exchange_tags
|
||||
|
||||
private
|
||||
|
||||
# Warning: this should only EVER be called via TagRule#apply
|
||||
def apply!
|
||||
unless preferred_matched_order_cycles_visibility == "visible"
|
||||
subject.reject!{ |oc| tags_match?(oc) }
|
||||
end
|
||||
end
|
||||
|
||||
def apply_default!
|
||||
if preferred_matched_order_cycles_visibility == "visible"
|
||||
subject.reject!{ |oc| tags_match?(oc) }
|
||||
end
|
||||
end
|
||||
|
||||
def tags_match?(order_cycle)
|
||||
exchange_tags = exchange_for(order_cycle).andand.tag_list || []
|
||||
preferred_tags = preferred_exchange_tags.split(",")
|
||||
( exchange_tags & preferred_tags ).any?
|
||||
end
|
||||
|
||||
def exchange_for(order_cycle)
|
||||
order_cycle.exchanges.outgoing.to_enterprise(context[:shop]).first
|
||||
def reject_matched?
|
||||
preferred_matched_order_cycles_visibility != "visible"
|
||||
end
|
||||
|
||||
def subject_class_matches?
|
||||
subject.class == ActiveRecord::Relation &&
|
||||
subject.klass == OrderCycle
|
||||
private
|
||||
|
||||
def exchange_for(order_cycle)
|
||||
order_cycle.exchanges.outgoing.to_enterprise(enterprise).first
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,29 +4,13 @@ class TagRule::FilterPaymentMethods < TagRule
|
||||
|
||||
attr_accessible :preferred_matched_payment_methods_visibility, :preferred_payment_method_tags
|
||||
|
||||
private
|
||||
|
||||
# Warning: this should only EVER be called via TagRule#apply
|
||||
def apply!
|
||||
unless preferred_matched_payment_methods_visibility == "visible"
|
||||
subject.reject!{ |pm| tags_match?(pm) }
|
||||
end
|
||||
end
|
||||
|
||||
def apply_default!
|
||||
if preferred_matched_payment_methods_visibility == "visible"
|
||||
subject.reject!{ |pm| tags_match?(pm) }
|
||||
end
|
||||
end
|
||||
|
||||
def tags_match?(payment_method)
|
||||
payment_method_tags = payment_method.andand.tag_list || []
|
||||
preferred_tags = preferred_payment_method_tags.split(",")
|
||||
( payment_method_tags & preferred_tags ).any?
|
||||
end
|
||||
|
||||
def subject_class_matches?
|
||||
subject.class == Array &&
|
||||
subject.all? { |i| i.class < Spree::PaymentMethod }
|
||||
def reject_matched?
|
||||
preferred_matched_payment_methods_visibility != "visible"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,25 +5,8 @@ class TagRule
|
||||
|
||||
attr_accessible :preferred_matched_variants_visibility, :preferred_variant_tags
|
||||
|
||||
private
|
||||
|
||||
# Warning: this should only EVER be called via TagRule#apply
|
||||
def apply!
|
||||
unless preferred_matched_variants_visibility == "visible"
|
||||
subject.reject! do |product|
|
||||
product["variants"].reject! { |v| tags_match?(v) }
|
||||
product["variants"].empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def apply_default!
|
||||
if preferred_matched_variants_visibility == "visible"
|
||||
subject.reject! do |product|
|
||||
product["variants"].reject! { |v| tags_match?(v) }
|
||||
product["variants"].empty?
|
||||
end
|
||||
end
|
||||
def self.tagged_children_for(product)
|
||||
product["variants"]
|
||||
end
|
||||
|
||||
def tags_match?(variant)
|
||||
@@ -32,8 +15,8 @@ class TagRule
|
||||
(variant_tags & preferred_tags).any?
|
||||
end
|
||||
|
||||
def subject_class_matches?
|
||||
subject.class == Array
|
||||
def reject_matched?
|
||||
preferred_matched_variants_visibility != "visible"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,19 +4,8 @@ class TagRule::FilterShippingMethods < TagRule
|
||||
|
||||
attr_accessible :preferred_matched_shipping_methods_visibility, :preferred_shipping_method_tags
|
||||
|
||||
private
|
||||
|
||||
# Warning: this should only EVER be called via TagRule#apply
|
||||
def apply!
|
||||
unless preferred_matched_shipping_methods_visibility == "visible"
|
||||
subject.reject!{ |sm| tags_match?(sm) }
|
||||
end
|
||||
end
|
||||
|
||||
def apply_default!
|
||||
if preferred_matched_shipping_methods_visibility == "visible"
|
||||
subject.reject!{ |sm| tags_match?(sm) }
|
||||
end
|
||||
def reject_matched?
|
||||
preferred_matched_shipping_methods_visibility != "visible"
|
||||
end
|
||||
|
||||
def tags_match?(shipping_method)
|
||||
@@ -24,9 +13,4 @@ class TagRule::FilterShippingMethods < TagRule
|
||||
preferred_tags = preferred_shipping_method_tags.split(",")
|
||||
( shipping_method_tags & preferred_tags ).any?
|
||||
end
|
||||
|
||||
def subject_class_matches?
|
||||
subject.class == Array &&
|
||||
subject.all? { |i| i.class == Spree::ShippingMethod }
|
||||
end
|
||||
end
|
||||
|
||||
61
lib/open_food_network/tag_rule_applicator.rb
Normal file
61
lib/open_food_network/tag_rule_applicator.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
module OpenFoodNetwork
|
||||
class TagRuleApplicator
|
||||
attr_reader :enterprise, :rule_class, :customer_tags
|
||||
|
||||
def initialize(enterprise, rule_type, customer_tags=[])
|
||||
raise "Enterprise cannot be nil" if enterprise.nil?
|
||||
raise "Rule Type cannot be nil" if rule_type.nil?
|
||||
|
||||
@enterprise = enterprise
|
||||
@rule_class = "TagRule::#{rule_type}".constantize
|
||||
@customer_tags = customer_tags || []
|
||||
end
|
||||
|
||||
def filter!(subject)
|
||||
return unless subject.respond_to?(:any?) && subject.any?
|
||||
subject.reject! do |element|
|
||||
if rule_class.respond_to?(:tagged_children_for)
|
||||
children = rule_class.tagged_children_for(element)
|
||||
children.reject! { |child| reject?(child) }
|
||||
children.empty?
|
||||
else
|
||||
reject?(element)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reject?(element)
|
||||
customer_rules.each do |rule|
|
||||
return rule.reject_matched? if rule.tags_match?(element)
|
||||
end
|
||||
|
||||
default_rules.each do |rule|
|
||||
return rule.reject_matched? if rule.tags_match?(element)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def rules
|
||||
return @rules unless @rules.nil?
|
||||
@rules = rule_class.for(enterprise)
|
||||
end
|
||||
|
||||
def customer_rules
|
||||
return @customer_matched_rules unless @customer_matched_rules.nil?
|
||||
@customer_matched_rules = rules.select{ |rule| customer_tags_match?(rule) }
|
||||
end
|
||||
|
||||
def default_rules
|
||||
return @default_rules unless @default_rules.nil?
|
||||
@default_rules = rules.select(&:is_default?)
|
||||
end
|
||||
|
||||
def customer_tags_match?(rule)
|
||||
preferred_tags = rule.preferred_customer_tags.split(",")
|
||||
(customer_tags & preferred_tags).any?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -40,12 +40,17 @@ describe EnterprisesController do
|
||||
order.update_attribute(:customer_id, customer.id)
|
||||
end
|
||||
|
||||
it "shows order cycles allowed by the rule" do
|
||||
it "shows order cycles allowed by the rules" do
|
||||
create(:filter_order_cycles_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_customer_tags: "wholesale",
|
||||
preferred_exchange_tags: "wholesale",
|
||||
preferred_matched_order_cycles_visibility: 'visible')
|
||||
create(:filter_order_cycles_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_exchange_tags: "wholesale",
|
||||
preferred_matched_order_cycles_visibility: 'hidden')
|
||||
|
||||
spree_get :shop, {id: distributor}
|
||||
expect(assigns(:order_cycles)).to include order_cycle1, order_cycle2, order_cycle3
|
||||
|
||||
@@ -96,21 +96,20 @@ describe ShopController do
|
||||
|
||||
describe "determining rule relevance" do
|
||||
let(:products_json) { double(:products_json) }
|
||||
let(:applicator) { double(:applicator) }
|
||||
|
||||
before do
|
||||
# allow(controller).to receive(:products_json) { products_json }
|
||||
allow(controller).to receive(:relevant_tag_rules) { relevant_tag_rules }
|
||||
allow(controller).to receive(:apply_tag_rules) { "some filtered json" }
|
||||
allow(applicator).to receive(:rules) { tag_rules }
|
||||
allow(controller).to receive(:applicator) { applicator }
|
||||
allow(controller).to receive(:filter) { "some filtered json" }
|
||||
end
|
||||
|
||||
context "when no relevant rules exist" do
|
||||
let(:relevant_tag_rules) { [] }
|
||||
|
||||
before { allow(controller).to receive(:relevant_rules) { relevant_rules } }
|
||||
let(:tag_rules) { [] }
|
||||
|
||||
it "does not attempt to apply any rules" do
|
||||
controller.send(:filtered_json, products_json)
|
||||
expect(expect(controller).to_not have_received(:apply_tag_rules))
|
||||
expect(expect(controller).to_not have_received(:filter))
|
||||
end
|
||||
|
||||
it "returns products as JSON" do
|
||||
@@ -120,11 +119,11 @@ describe ShopController do
|
||||
|
||||
context "when relevant rules exist" do
|
||||
let(:tag_rule) { create(:filter_products_tag_rule, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) }
|
||||
let(:relevant_tag_rules) { [tag_rule] }
|
||||
let(:tag_rules) { [tag_rule] }
|
||||
|
||||
it "attempts to apply any rules" do
|
||||
controller.send(:filtered_json, products_json)
|
||||
expect(controller).to have_received(:apply_tag_rules).with(relevant_tag_rules, products_json)
|
||||
expect(controller).to have_received(:filter).with(products_json)
|
||||
end
|
||||
|
||||
it "returns filtered JSON" do
|
||||
@@ -133,49 +132,86 @@ describe ShopController do
|
||||
end
|
||||
end
|
||||
|
||||
describe "applying tag rules" do
|
||||
let(:product1) { { id: 1, name: 'product 1', "variants" => [{ id: 4, "tag_list" => ["tag1"] }] } }
|
||||
let(:product2) { { id: 2, name: 'product 2', "variants" => [{ id: 5, "tag_list" => ["tag1"] }, {id: 9, "tag_list" => ["tag2"]}] } }
|
||||
let(:product3) { { id: 3, name: 'product 3', "variants" => [{ id: 6, "tag_list" => ["tag3"] }] } }
|
||||
context "when FilterProducts tag rules are in effect" do
|
||||
let!(:tagged_customer) { create(:customer, enterprise: distributor, tag_list: "member") }
|
||||
let!(:untagged_customer) { create(:customer, enterprise: distributor, tag_list: "") }
|
||||
let!(:order) { create(:order, distributor: distributor) }
|
||||
let!(:tag_rule) { create(:filter_products_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_customer_tags: "member",
|
||||
preferred_variant_tags: "members-only") }
|
||||
let!(:default_tag_rule) { create(:filter_products_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_variant_tags: "members-only") }
|
||||
let(:product1) { { "id" => 1, "name" => 'product 1', "variants" => [{ "id" => 4, "tag_list" => ["members-only"] }] } }
|
||||
let(:product2) { { "id" => 2, "name" => 'product 2', "variants" => [{ "id" => 5, "tag_list" => ["members-only"] }, {"id" => 9, "tag_list" => ["something"]}] } }
|
||||
let(:product3) { { "id" => 3, "name" => 'product 3', "variants" => [{ "id" => 6, "tag_list" => ["something-else"] }] } }
|
||||
let(:product2_without_v5) { { "id" => 2, "name" => 'product 2', "variants" => [{"id" => 9, "tag_list" => ["something"]}] } }
|
||||
let!(:products_array) { [product1, product2, product3] }
|
||||
let!(:products_json) { JSON.unparse( products_array ) }
|
||||
let(:tag_rule) { create(:filter_products_tag_rule, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) }
|
||||
let(:relevant_tag_rules) { [tag_rule] }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:current_order) { order }
|
||||
allow(tag_rule).to receive(:context=)
|
||||
allow(tag_rule).to receive(:apply)
|
||||
allow(distributor).to receive(:apply_tag_rules).and_call_original
|
||||
end
|
||||
|
||||
context "when a current order with a customer does not exist" do
|
||||
let(:order) { double(:order, customer: nil) }
|
||||
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, 'visible') }
|
||||
before { default_tag_rule.update_attribute(:preferred_matched_variants_visibility, 'hidden') }
|
||||
|
||||
it "sets the context customer_tags as an empty array" do
|
||||
controller.send(:apply_tag_rules, relevant_tag_rules, products_json)
|
||||
expect(distributor).to have_received(:apply_tag_rules).with(rules: relevant_tag_rules, subject: JSON.parse(products_json), :customer_tags=>[])
|
||||
end
|
||||
end
|
||||
let(:filtered_products) { JSON.parse(controller.send(:filter, products_json)) }
|
||||
|
||||
context "when a customer does exist" do
|
||||
let(:order) { double(:order, customer: double(:customer, tag_list: ["tag1", "tag2"])) }
|
||||
|
||||
it "sets the context customer_tags" do
|
||||
controller.send(:apply_tag_rules, relevant_tag_rules, products_json)
|
||||
expect(distributor).to have_received(:apply_tag_rules).with(rules: relevant_tag_rules, subject: JSON.parse(products_json), :customer_tags=>["tag1", "tag2"])
|
||||
end
|
||||
|
||||
context "applies the rule" do
|
||||
before do
|
||||
allow(tag_rule).to receive(:context=).and_call_original
|
||||
allow(tag_rule).to receive(:apply).and_call_original
|
||||
context "when the customer is nil" do
|
||||
it "applies default action (hide)" do
|
||||
expect(filtered_products).to include product2_without_v5, product3
|
||||
expect(filtered_products).to_not include product1, product2
|
||||
end
|
||||
end
|
||||
|
||||
it "applies the rule" do
|
||||
result = controller.send(:apply_tag_rules, relevant_tag_rules, products_json)
|
||||
expect(tag_rule).to have_received(:apply)
|
||||
expect(result).to eq JSON.unparse([{ id: 2, name: 'product 2', variants: [{id: 9, tag_list: ["tag2"]}] }, product3])
|
||||
context "when the customer's tags match" do
|
||||
before { order.update_attribute(:customer_id, tagged_customer.id) }
|
||||
|
||||
it "applies the action (show)" do
|
||||
expect(filtered_products).to include product1, product2, product3
|
||||
end
|
||||
end
|
||||
|
||||
context "when the customer's tags don't match" do
|
||||
before { order.update_attribute(:customer_id, untagged_customer.id) }
|
||||
|
||||
it "applies the default action (hide)" do
|
||||
expect(filtered_products).to include product2_without_v5, product3
|
||||
expect(filtered_products).to_not include product1, product2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, 'hidden') }
|
||||
before { default_tag_rule.update_attribute(:preferred_matched_variants_visibility, 'visible') }
|
||||
|
||||
let(:filtered_products) { JSON.parse(controller.send(:filter, products_json)) }
|
||||
|
||||
context "when the customer is nil" do
|
||||
it "applies default action (show)" do
|
||||
expect(filtered_products).to include product1, product2, product3
|
||||
end
|
||||
end
|
||||
|
||||
context "when the customer's tags match" do
|
||||
before { order.update_attribute(:customer_id, tagged_customer.id) }
|
||||
|
||||
it "applies the action (hide)" do
|
||||
expect(filtered_products).to include product2_without_v5, product3
|
||||
expect(filtered_products).to_not include product1, product2
|
||||
end
|
||||
end
|
||||
|
||||
context "when the customer's tags don't match" do
|
||||
before { order.update_attribute(:customer_id, untagged_customer.id) }
|
||||
|
||||
it "applies the default action (show)" do
|
||||
expect(filtered_products).to include product1, product2, product3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -118,10 +118,15 @@ feature "As a consumer I want to check out my cart", js: true do
|
||||
preferred_customer_tags: "local",
|
||||
preferred_shipping_method_tags: "local",
|
||||
preferred_matched_shipping_methods_visibility: 'visible')
|
||||
create(:filter_shipping_methods_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_shipping_method_tags: "local",
|
||||
preferred_matched_shipping_methods_visibility: 'hidden')
|
||||
visit checkout_path
|
||||
checkout_as_guest
|
||||
|
||||
# Rule in effect, disallows access to 'Local'
|
||||
# Default rule in effect, disallows access to 'Local'
|
||||
page.should have_content "Frogs"
|
||||
page.should have_content "Donkeys"
|
||||
page.should_not have_content "Local"
|
||||
|
||||
@@ -3,15 +3,19 @@ require 'spec_helper'
|
||||
describe EnterprisesHelper do
|
||||
describe "loading available shipping methods" do
|
||||
|
||||
context "when a FilterShippingMethods tag rule is in effect, with preferred visibility of 'visible'" do
|
||||
context "when FilterShippingMethods tag rules are in effect" do
|
||||
let!(:distributor) { create(:distributor_enterprise) }
|
||||
let!(:allowed_customer) { create(:customer, enterprise: distributor, tag_list: "local") }
|
||||
let!(:disallowed_customer) { create(:customer, enterprise: distributor, tag_list: "") }
|
||||
let!(:tagged_customer) { create(:customer, enterprise: distributor, tag_list: "local") }
|
||||
let!(:untagged_customer) { create(:customer, enterprise: distributor, tag_list: "") }
|
||||
let!(:order) { create(:order, distributor: distributor) }
|
||||
let!(:tag_rule) { create(:filter_shipping_methods_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_customer_tags: "local",
|
||||
preferred_shipping_method_tags: "local-delivery") }
|
||||
let!(:default_tag_rule) { create(:filter_shipping_methods_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_shipping_method_tags: "local-delivery") }
|
||||
let!(:tagged_sm) { create(:shipping_method, require_ship_address: false, name: "Untagged", tag_list: "local-delivery") }
|
||||
let!(:untagged_sm) { create(:shipping_method, require_ship_address: false, name: "Tagged", tag_list: "") }
|
||||
|
||||
@@ -20,8 +24,9 @@ describe EnterprisesHelper do
|
||||
allow(helper).to receive(:current_order) { order }
|
||||
end
|
||||
|
||||
context "with a preferred visiblity of 'visible" do
|
||||
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'visible') }
|
||||
before { default_tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'hidden') }
|
||||
|
||||
context "when the customer is nil" do
|
||||
it "applies default action (hide)" do
|
||||
@@ -31,7 +36,7 @@ describe EnterprisesHelper do
|
||||
end
|
||||
|
||||
context "when the customer's tags match" do
|
||||
before { order.update_attribute(:customer_id, allowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, tagged_customer.id) }
|
||||
|
||||
it "applies the action (show)" do
|
||||
expect(helper.available_shipping_methods).to include tagged_sm, untagged_sm
|
||||
@@ -39,7 +44,7 @@ describe EnterprisesHelper do
|
||||
end
|
||||
|
||||
context "when the customer's tags don't match" do
|
||||
before { order.update_attribute(:customer_id, disallowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, untagged_customer.id) }
|
||||
|
||||
it "applies the default action (hide)" do
|
||||
expect(helper.available_shipping_methods).to include untagged_sm
|
||||
@@ -48,8 +53,9 @@ describe EnterprisesHelper do
|
||||
end
|
||||
end
|
||||
|
||||
context "with a preferred visiblity of 'hidden" do
|
||||
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'hidden') }
|
||||
before { default_tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'visible') }
|
||||
|
||||
context "when the customer is nil" do
|
||||
it "applies default action (show)" do
|
||||
@@ -58,7 +64,7 @@ describe EnterprisesHelper do
|
||||
end
|
||||
|
||||
context "when the customer's tags match" do
|
||||
before { order.update_attribute(:customer_id, allowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, tagged_customer.id) }
|
||||
|
||||
it "applies the action (hide)" do
|
||||
expect(helper.available_shipping_methods).to include untagged_sm
|
||||
@@ -67,7 +73,7 @@ describe EnterprisesHelper do
|
||||
end
|
||||
|
||||
context "when the customer's tags don't match" do
|
||||
before { order.update_attribute(:customer_id, disallowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, untagged_customer.id) }
|
||||
|
||||
it "applies the default action (show)" do
|
||||
expect(helper.available_shipping_methods).to include tagged_sm, untagged_sm
|
||||
|
||||
@@ -30,7 +30,6 @@ describe InjectionHelper do
|
||||
shipping_methods = double(:shipping_methods, uniq: [sm])
|
||||
current_distributor = double(:distributor, shipping_methods: shipping_methods)
|
||||
allow(helper).to receive(:current_distributor) { current_distributor }
|
||||
allow(current_distributor).to receive(:apply_tag_rules).with({type: "FilterShippingMethods", subject: shipping_methods, customer_tags: nil})
|
||||
helper.inject_available_shipping_methods.should match sm.id.to_s
|
||||
helper.inject_available_shipping_methods.should match sm.compute_amount(order).to_s
|
||||
end
|
||||
|
||||
250
spec/lib/open_food_network/tag_rule_applicator_spec.rb
Normal file
250
spec/lib/open_food_network/tag_rule_applicator_spec.rb
Normal file
@@ -0,0 +1,250 @@
|
||||
require 'open_food_network/tag_rule_applicator'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe TagRuleApplicator do
|
||||
let!(:enterprise) { create(:distributor_enterprise) }
|
||||
let!(:oc_tag_rule) { create(:filter_order_cycles_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_exchange_tags: "tag1", preferred_matched_order_cycles_visibility: "visible" )}
|
||||
let!(:product_tag_rule1) { create(:filter_products_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "visible" ) }
|
||||
let!(:product_tag_rule2) { create(:filter_products_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_variant_tags: "tag3", preferred_matched_variants_visibility: "hidden" ) }
|
||||
let!(:product_tag_rule3) { create(:filter_products_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag2", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "visible" ) }
|
||||
let!(:default_product_tag_rule) { create(:filter_products_tag_rule, enterprise: enterprise, is_default: true, preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) }
|
||||
let!(:sm_tag_rule) { create(:filter_shipping_methods_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_shipping_method_tags: "tag1", preferred_matched_shipping_methods_visibility: "visible" )}
|
||||
|
||||
describe "initialisation" do
|
||||
context "when enterprise is nil" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(nil, "FilterProducts", ["tag1"]) }
|
||||
it { expect{applicator}.to raise_error "Enterprise cannot be nil" }
|
||||
end
|
||||
|
||||
context "when rule_type is nil" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, nil, ["tag1"]) }
|
||||
it { expect{applicator}.to raise_error "Rule Type cannot be nil" }
|
||||
end
|
||||
|
||||
context "when rule_type does not match an existing rule type" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterSomething", ["tag1"]) }
|
||||
it { expect{applicator}.to raise_error NameError }
|
||||
end
|
||||
|
||||
context "when enterprise and rule_type are present" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", customer_tags) }
|
||||
|
||||
context "when the customer tags are nil" do
|
||||
let!(:customer_tags) { nil }
|
||||
|
||||
it "sets customer tags to an empty array" do
|
||||
expect(applicator.customer_tags).to eq []
|
||||
end
|
||||
|
||||
it "does not match rules without customer tags" do
|
||||
rule = double(:rule, preferred_customer_tags: "")
|
||||
expect(applicator.send(:customer_tags_match?, rule)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when customer tags are empty" do
|
||||
let!(:customer_tags) { [] }
|
||||
|
||||
it "sets customer tags to an empty array" do
|
||||
expect(applicator.customer_tags).to eq []
|
||||
end
|
||||
|
||||
it "does not match rules without customer tags" do
|
||||
rule = double(:rule, preferred_customer_tags: "")
|
||||
expect(applicator.send(:customer_tags_match?, rule)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when customer_tags are present" do
|
||||
let!(:customer_tags) { ["tag1"] }
|
||||
|
||||
let(:rules) { applicator.send(:rules)}
|
||||
let(:customer_rules) { applicator.send(:customer_rules)}
|
||||
let(:default_rules) { applicator.send(:default_rules)}
|
||||
|
||||
it "stores enterprise, rule_class and customer_tags as instance variables" do
|
||||
expect(applicator.enterprise).to eq enterprise
|
||||
expect(applicator.rule_class).to eq TagRule::FilterProducts
|
||||
expect(applicator.customer_tags).to eq ["tag1"]
|
||||
end
|
||||
|
||||
it "selects only rules of the specified type" do
|
||||
expect(rules).to include product_tag_rule1, product_tag_rule2, product_tag_rule3, default_product_tag_rule
|
||||
expect(rules).not_to include oc_tag_rule, sm_tag_rule
|
||||
end
|
||||
|
||||
it "splits rules into those which match customer tags and those which don't" do
|
||||
expect(customer_rules).to include product_tag_rule1, product_tag_rule2
|
||||
expect(customer_rules).not_to include default_product_tag_rule, product_tag_rule3, oc_tag_rule, sm_tag_rule
|
||||
end
|
||||
|
||||
it "splits out default rules" do
|
||||
expect(default_rules).to include default_product_tag_rule
|
||||
expect(default_rules).not_to include product_tag_rule1, product_tag_rule2, product_tag_rule3, oc_tag_rule, sm_tag_rule
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "filter!" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", []) }
|
||||
|
||||
context "when the subject is nil" do
|
||||
let(:subject) { double(:subject, reject!: false) }
|
||||
|
||||
it "returns immediately" do
|
||||
applicator.filter!(subject)
|
||||
expect(subject).to_not have_received(:reject!)
|
||||
end
|
||||
end
|
||||
|
||||
context "when subject is empty" do
|
||||
let(:subject) { double(:subject, reject!: false) }
|
||||
|
||||
it "returns immediately" do
|
||||
applicator.filter!(subject)
|
||||
expect(subject).to_not have_received(:reject!)
|
||||
end
|
||||
end
|
||||
|
||||
context "when subject is an array" do
|
||||
let(:element) { double(:element, ) }
|
||||
let(:subject) { [element] }
|
||||
|
||||
context "when rule_class reponds to tagged_children_for" do
|
||||
let(:child1) { double(:child) }
|
||||
let(:child2) { double(:child) }
|
||||
let(:children) { [child1, child2] }
|
||||
let(:rule_class) { double(:rule_class, tagged_children_for: children) }
|
||||
|
||||
before{ allow(applicator).to receive(:rule_class) { rule_class } }
|
||||
|
||||
context "when reject? returns true only for some children" do
|
||||
before do
|
||||
allow(applicator).to receive(:reject?).with(child1) { true }
|
||||
allow(applicator).to receive(:reject?).with(child2) { false }
|
||||
applicator.filter!(subject)
|
||||
end
|
||||
|
||||
it "rejects the specified children from the array" do
|
||||
expect(children).to eq [child2]
|
||||
end
|
||||
|
||||
it "does not remove the element from the original subject" do
|
||||
expect(subject).to eq [element]
|
||||
end
|
||||
end
|
||||
|
||||
context "when reject? returns true for all children" do
|
||||
before do
|
||||
allow(applicator).to receive(:reject?).with(child1) { true }
|
||||
allow(applicator).to receive(:reject?).with(child2) { true }
|
||||
applicator.filter!(subject)
|
||||
end
|
||||
|
||||
it "removes all children from the array" do
|
||||
expect(children).to eq []
|
||||
end
|
||||
|
||||
it "removes the element from the original subject" do
|
||||
expect(subject).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when rule_class doesn't respond to tagged_children_for" do
|
||||
let(:rule_class) { double(:rule_class) }
|
||||
|
||||
before{ allow(applicator).to receive(:rule_class) { rule_class } }
|
||||
|
||||
context "when reject? returns false for the element" do
|
||||
before do
|
||||
allow(applicator).to receive(:reject?).with(element) { false }
|
||||
applicator.filter!(subject)
|
||||
end
|
||||
|
||||
it "does not remove the element from the original subject" do
|
||||
expect(subject).to eq [element]
|
||||
end
|
||||
end
|
||||
|
||||
context "when reject? returns true for the element" do
|
||||
before do
|
||||
allow(applicator).to receive(:reject?).with(element) { true }
|
||||
applicator.filter!(subject)
|
||||
end
|
||||
|
||||
it "removes the element from the original subject" do
|
||||
expect(subject).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "reject?" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", ["tag1"]) }
|
||||
let(:customer_rule) { double(:customer_rule, reject_matched?: "customer_rule.reject_matched?" )}
|
||||
let(:default_rule) { double(:customer_rule, reject_matched?: "default_rule.reject_matched?" )}
|
||||
let(:dummy) { double(:dummy) }
|
||||
|
||||
before{ allow(applicator).to receive(:customer_rules) { [customer_rule] } }
|
||||
before{ allow(applicator).to receive(:default_rules) { [default_rule] } }
|
||||
|
||||
context "when a customer rule matches the tags of the element" do
|
||||
before{ allow(customer_rule).to receive(:tags_match?).with(dummy) { true } }
|
||||
|
||||
it "returns the value of customer_rule.reject_matched?" do
|
||||
expect(applicator.send(:reject?, dummy)).to eq "customer_rule.reject_matched?"
|
||||
end
|
||||
end
|
||||
|
||||
context "when no customer rules match the tags of the element" do
|
||||
before{ allow(customer_rule).to receive(:tags_match?) { false } }
|
||||
|
||||
context "when a default rule matches the tags of the element" do
|
||||
before{ allow(default_rule).to receive(:tags_match?) { true } }
|
||||
|
||||
it "returns the value of the default_rule.reject_matched?" do
|
||||
expect(applicator.send(:reject?, dummy)).to eq "default_rule.reject_matched?"
|
||||
end
|
||||
end
|
||||
|
||||
context "when a default rule matches the tags of the element" do
|
||||
before{ allow(default_rule).to receive(:tags_match?) { false } }
|
||||
|
||||
it "returns false" do
|
||||
expect(applicator.send(:reject?, dummy)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "smoke test for products" do
|
||||
let(:product1) { { id: 1, name: 'product 1', "variants" => [{ id: 4, "tag_list" => ["tag1"] }] } }
|
||||
let(:product2) { { id: 2, name: 'product 2', "variants" => [{ id: 5, "tag_list" => ["tag1"] }, {id: 9, "tag_list" => ["tag2"]}] } }
|
||||
let(:product3) { { id: 3, name: 'product 3', "variants" => [{ id: 6, "tag_list" => ["tag3"] }] } }
|
||||
let!(:products_array) { [product1, product2, product3] }
|
||||
|
||||
context "when customer tags don't match any rules" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", ["lalalala"]) }
|
||||
|
||||
it "applies the default rule" do
|
||||
applicator.filter!(products_array)
|
||||
expect(products_array).to eq [{ id: 2, name: 'product 2', "variants" => [{id: 9, "tag_list" => ["tag2"]}] }, product3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when customer tags match one or more rules" do
|
||||
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", ["tag1"]) }
|
||||
|
||||
it "applies those rules" do
|
||||
# product_tag_rule1 and product_tag_rule2 are being applied
|
||||
applicator.filter!(products_array)
|
||||
expect(products_array).to eq [product1, product2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -859,54 +859,4 @@ describe Enterprise do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "finding tag rules" do
|
||||
let(:enterprise) { create(:enterprise) }
|
||||
let(:tag_rule1) { create(:filter_products_tag_rule, enterprise: enterprise) }
|
||||
let(:tag_rule2) { create(:filter_shipping_methods_tag_rule, enterprise: enterprise) }
|
||||
let(:tag_rule3) { create(:tag_rule, enterprise: enterprise) }
|
||||
let(:context) { { subject: "something" } }
|
||||
|
||||
context "when a set of rules are passed in with the context" do
|
||||
let(:tag_rules) { TagRule.where(id: [tag_rule1.id, tag_rule3.id]) }
|
||||
before { context[:rules] = tag_rules }
|
||||
|
||||
context "when a rule type has been passed in with the context" do
|
||||
before { context[:type] = "FilterProducts" }
|
||||
|
||||
it "returns rules of the specified type from within the list of passed-in rules" do
|
||||
rules = enterprise.send(:tag_rules_for, context)
|
||||
expect(rules).to include tag_rule1
|
||||
expect(rules).to_not include tag_rule2, tag_rule3
|
||||
end
|
||||
end
|
||||
|
||||
context "when a rule type has not been passed in with the context" do
|
||||
it "returns the list of passed-in rules" do
|
||||
rules = enterprise.send(:tag_rules_for, context)
|
||||
expect(rules).to include tag_rule1, tag_rule3
|
||||
expect(rules).to_not include tag_rule2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when a set of rules are not passed in with the context" do
|
||||
context "when a rule type has been passed in with the context" do
|
||||
before { context[:type] = "FilterShippingMethods" }
|
||||
|
||||
it "returns rules of the specified type belonging to the enterprise" do
|
||||
rules = enterprise.send(:tag_rules_for, context)
|
||||
expect(rules).to include tag_rule2
|
||||
expect(rules).to_not include tag_rule1, tag_rule3
|
||||
end
|
||||
end
|
||||
|
||||
context "when a rule type has not been passed in with the context" do
|
||||
it "returns all rules belonging to the enterprise" do
|
||||
rules = enterprise.send(:tag_rules_for, context)
|
||||
expect(rules).to include tag_rule1, tag_rule2, tag_rule3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,13 +43,17 @@ describe Spree::Order do
|
||||
end
|
||||
end
|
||||
|
||||
context "when a FilterPaymentMethods tag rule is in effect, with preferred visibility of 'visible'" do
|
||||
let!(:allowed_customer) { create(:customer, enterprise: distributor, tag_list: "trusted") }
|
||||
let!(:disallowed_customer) { create(:customer, enterprise: distributor, tag_list: "") }
|
||||
context "when FilterPaymentMethods tag rules are in effect" do
|
||||
let!(:tagged_customer) { create(:customer, enterprise: distributor, tag_list: "trusted") }
|
||||
let!(:untagged_customer) { create(:customer, enterprise: distributor, tag_list: "") }
|
||||
let!(:tag_rule) { create(:filter_payment_methods_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_customer_tags: "trusted",
|
||||
preferred_payment_method_tags: "trusted") }
|
||||
let!(:default_tag_rule) { create(:filter_payment_methods_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_payment_method_tags: "trusted") }
|
||||
let(:tagged_pm) { pm1 }
|
||||
let(:untagged_pm) { pm2 }
|
||||
|
||||
@@ -58,8 +62,9 @@ describe Spree::Order do
|
||||
distributor.payment_methods = [tagged_pm, untagged_pm]
|
||||
end
|
||||
|
||||
context "with a preferred visiblity of 'visible" do
|
||||
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'visible') }
|
||||
before { default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden') }
|
||||
|
||||
context "when the customer is nil" do
|
||||
it "applies default action (hide)" do
|
||||
@@ -69,7 +74,7 @@ describe Spree::Order do
|
||||
end
|
||||
|
||||
context "when the customer's tags match" do
|
||||
before { order.update_attribute(:customer_id, allowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, tagged_customer.id) }
|
||||
|
||||
it "applies the action (show)" do
|
||||
expect(order.available_payment_methods).to include tagged_pm, untagged_pm
|
||||
@@ -77,7 +82,7 @@ describe Spree::Order do
|
||||
end
|
||||
|
||||
context "when the customer's tags don't match" do
|
||||
before { order.update_attribute(:customer_id, disallowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, untagged_customer.id) }
|
||||
|
||||
it "applies the default action (hide)" do
|
||||
expect(order.available_payment_methods).to include untagged_pm
|
||||
@@ -86,8 +91,9 @@ describe Spree::Order do
|
||||
end
|
||||
end
|
||||
|
||||
context "with a preferred visiblity of 'hidden" do
|
||||
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden') }
|
||||
before { default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'visible') }
|
||||
|
||||
context "when the customer is nil" do
|
||||
it "applies default action (show)" do
|
||||
@@ -96,7 +102,7 @@ describe Spree::Order do
|
||||
end
|
||||
|
||||
context "when the customer's tags match" do
|
||||
before { order.update_attribute(:customer_id, allowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, tagged_customer.id) }
|
||||
|
||||
it "applies the action (hide)" do
|
||||
expect(order.available_payment_methods).to include untagged_pm
|
||||
@@ -105,7 +111,7 @@ describe Spree::Order do
|
||||
end
|
||||
|
||||
context "when the customer's tags don't match" do
|
||||
before { order.update_attribute(:customer_id, disallowed_customer.id) }
|
||||
before { order.update_attribute(:customer_id, untagged_customer.id) }
|
||||
|
||||
it "applies the default action (show)" do
|
||||
expect(order.available_payment_methods).to include tagged_pm, untagged_pm
|
||||
@@ -186,38 +192,6 @@ describe Spree::Order do
|
||||
|
||||
subject.update_distribution_charge!
|
||||
end
|
||||
|
||||
context "appying tag rules" do
|
||||
let(:enterprise) { create(:distributor_enterprise) }
|
||||
let(:customer) { create(:customer, enterprise: enterprise, tag_list: "tagtagtag") }
|
||||
let(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "tagtagtag") }
|
||||
let(:order) { create(:order_with_totals_and_distribution, distributor: enterprise, customer: customer) }
|
||||
|
||||
before do
|
||||
tag_rule.calculator.update_attribute(:preferred_flat_percent, -10)
|
||||
end
|
||||
|
||||
context "when the rule applies" do
|
||||
it "applies the rule" do
|
||||
order.update_distribution_charge!
|
||||
order.reload
|
||||
discount = order.adjustments.find_by_label("Discount")
|
||||
expect(discount).to be_a Spree::Adjustment
|
||||
expect(discount.amount).to eq (order.item_total / -10).round(2)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the rule does not apply" do
|
||||
before { tag_rule.update_attribute(:preferred_customer_tags, "tagtag") }
|
||||
|
||||
it "does not apply the rule" do
|
||||
order.update_distribution_charge!
|
||||
order.reload
|
||||
discount = order.adjustments.find_by_label("Discount")
|
||||
expect(discount).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "looking up whether a line item can be provided by an order cycle" do
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'spec_helper'
|
||||
describe TagRule::DiscountOrder, type: :model do
|
||||
let!(:tag_rule) { create(:tag_rule) }
|
||||
|
||||
describe "determining relevance based on additional requirements" do
|
||||
pending "determining relevance based on additional requirements" do
|
||||
let(:subject) { double(:subject) }
|
||||
|
||||
before do
|
||||
@@ -29,7 +29,7 @@ describe TagRule::DiscountOrder, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe "determining whether a the rule has already been applied to an order" do
|
||||
pending "determining whether a the rule has already been applied to an order" do
|
||||
let!(:order) { create(:order) }
|
||||
let!(:adjustment) { order.adjustments.create({:amount => 12.34, :source => order, :originator => tag_rule, :label => 'discount' }, :without_protection => true) }
|
||||
|
||||
@@ -47,7 +47,7 @@ describe TagRule::DiscountOrder, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe "applying the rule" do
|
||||
pending "applying the rule" do
|
||||
# Assume that all validation is done by the TagRule base class
|
||||
|
||||
let!(:line_item) { create(:line_item, price: 100.00) }
|
||||
|
||||
@@ -37,55 +37,4 @@ describe TagRule::FilterOrderCycles, type: :model do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "applying the rule" do
|
||||
# Assume that all validation is done by the TagRule base class
|
||||
|
||||
let(:enterprise) { create(:distributor_enterprise) }
|
||||
let(:order_cycle1) { create(:simple_order_cycle, name: "order_cycle1", exchanges: [ create(:exchange, incoming: false, receiver: enterprise, tag_list: ["tag1", "something", "somethingelse"])]) }
|
||||
let(:order_cycle2) { create(:simple_order_cycle, name: "order_cycle2", exchanges: [ create(:exchange, incoming: false, receiver: enterprise, tag_list: ["tag2"])]) }
|
||||
let(:order_cycle3) { create(:simple_order_cycle, name: "order_cycle3", exchanges: [ create(:exchange, incoming: false, receiver: enterprise, tag_list: ["tag3"])]) }
|
||||
let!(:order_cycle_hash) { [order_cycle1, order_cycle2, order_cycle3] }
|
||||
|
||||
before do
|
||||
tag_rule.update_attribute(:preferred_exchange_tags, "tag2")
|
||||
tag_rule.context = {subject: order_cycle_hash, shop: enterprise}
|
||||
end
|
||||
|
||||
context "apply!" do
|
||||
context "when showing matching exchanges" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "visible") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(order_cycle_hash).to eq [order_cycle1, order_cycle2, order_cycle3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching exchanges" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "hidden") }
|
||||
it "removes matching exchanges from the list" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(order_cycle_hash).to eq [order_cycle1, order_cycle3]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "apply_default!" do
|
||||
context "when showing matching exchanges" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "visible") }
|
||||
it "remove matching exchanges from the list" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(order_cycle_hash).to eq [order_cycle1, order_cycle3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching exchanges" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "hidden") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(order_cycle_hash).to eq [order_cycle1, order_cycle2, order_cycle3]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,54 +30,4 @@ describe TagRule::FilterPaymentMethods, type: :model do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "applying the rule" do
|
||||
# Assume that all validation is done by the TagRule base class
|
||||
|
||||
let(:sm1) { create(:payment_method, tag_list: ["tag1", "something", "somethingelse"]) }
|
||||
let(:sm2) { create(:payment_method, tag_list: ["tag2"]) }
|
||||
let(:sm3) { create(:payment_method, tag_list: ["tag3"]) }
|
||||
let!(:payment_methods) { [sm1, sm2, sm3] }
|
||||
|
||||
before do
|
||||
tag_rule.update_attribute(:preferred_payment_method_tags, "tag2")
|
||||
tag_rule.context = {subject: payment_methods}
|
||||
end
|
||||
|
||||
context "apply!" do
|
||||
context "when showing matching payment methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "visible") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(payment_methods).to eq [sm1, sm2, sm3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching payment methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "hidden") }
|
||||
it "removes matching payment methods from the list" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(payment_methods).to eq [sm1, sm3]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "apply_default!" do
|
||||
context "when showing matching payment methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "visible") }
|
||||
it "remove matching payment methods from the list" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(payment_methods).to eq [sm1, sm3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching payment methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "hidden") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(payment_methods).to eq [sm1, sm2, sm3]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,54 +30,4 @@ describe TagRule::FilterProducts, type: :model do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "applying the rule" do
|
||||
# Assume that all validation is done by the TagRule base class
|
||||
|
||||
let(:product1) { { name: "product1", "variants" => [{ name: "v1", "tag_list" => ["tag1", "something", "somethingelse"]}] } }
|
||||
let(:product2) { { name: "product2", "variants" => [{ name: "v2", "tag_list" => ["tag2"]}] } }
|
||||
let(:product3) { { name: "product3", "variants" => [{ name: "v3", "tag_list" => ["tag3"]}] } }
|
||||
let!(:product_hash) { [product1, product2, product3] }
|
||||
|
||||
before do
|
||||
tag_rule.update_attribute(:preferred_variant_tags, "tag2")
|
||||
tag_rule.context = {subject: product_hash}
|
||||
end
|
||||
|
||||
context "apply!" do
|
||||
context "when showing matching variants" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "visible") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(product_hash).to eq [product1, product2, product3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching variants" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "hidden") }
|
||||
it "removes matching variants from the list" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(product_hash).to eq [product1, product3]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "apply_default!" do
|
||||
context "when showing matching variants" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "visible") }
|
||||
it "remove matching variants from the list" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(product_hash).to eq [product1, product3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching variants" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "hidden") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(product_hash).to eq [product1, product2, product3]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,54 +30,4 @@ describe TagRule::FilterShippingMethods, type: :model do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "applying the rule" do
|
||||
# Assume that all validation is done by the TagRule base class
|
||||
|
||||
let(:sm1) { create(:shipping_method, tag_list: ["tag1", "something", "somethingelse"]) }
|
||||
let(:sm2) { create(:shipping_method, tag_list: ["tag2"]) }
|
||||
let(:sm3) { create(:shipping_method, tag_list: ["tag3"]) }
|
||||
let!(:shipping_methods) { [sm1, sm2, sm3] }
|
||||
|
||||
before do
|
||||
tag_rule.update_attribute(:preferred_shipping_method_tags, "tag2")
|
||||
tag_rule.context = {subject: shipping_methods}
|
||||
end
|
||||
|
||||
context "apply!" do
|
||||
context "when showing matching shipping methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "visible") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(shipping_methods).to eq [sm1, sm2, sm3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching shipping methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "hidden") }
|
||||
it "removes matching shipping methods from the list" do
|
||||
tag_rule.send(:apply!)
|
||||
expect(shipping_methods).to eq [sm1, sm3]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "apply_default!" do
|
||||
context "when showing matching shipping methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "visible") }
|
||||
it "remove matching shipping methods from the list" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(shipping_methods).to eq [sm1, sm3]
|
||||
end
|
||||
end
|
||||
|
||||
context "when hiding matching shipping methods" do
|
||||
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "hidden") }
|
||||
it "does nothing" do
|
||||
tag_rule.send(:apply_default!)
|
||||
expect(shipping_methods).to eq [sm1, sm2, sm3]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,196 +8,4 @@ describe TagRule, type: :model do
|
||||
expect(tag_rule).to validate_presence_of :enterprise
|
||||
end
|
||||
end
|
||||
|
||||
describe 'setting the context' do
|
||||
let(:subject) { double(:subject) }
|
||||
let(:context) { { subject: subject, some_other_property: "yay"} }
|
||||
|
||||
it "raises an error when context is nil" do
|
||||
expect{ tag_rule.context = nil }.to raise_error "Context for tag rule cannot be nil"
|
||||
end
|
||||
|
||||
it "raises an error when subject is nil" do
|
||||
expect{ tag_rule.context = {} }.to raise_error "Subject for tag rule cannot be nil"
|
||||
end
|
||||
|
||||
it "stores the subject and context provided as instance variables on the model" do
|
||||
tag_rule.context = context
|
||||
expect(tag_rule.subject).to eq subject
|
||||
expect(tag_rule.context).to eq context
|
||||
expect(tag_rule.instance_variable_get(:@subject)).to eq subject
|
||||
expect(tag_rule.instance_variable_get(:@context)).to eq context
|
||||
end
|
||||
end
|
||||
|
||||
describe "determining relevance based on subject and context" do
|
||||
context "when the subject is nil" do
|
||||
it "returns false" do
|
||||
expect(tag_rule.send(:relevant?)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when the subject is not nil" do
|
||||
let(:subject) { double(:subject) }
|
||||
|
||||
before do
|
||||
tag_rule.context = {subject: subject}
|
||||
allow(tag_rule).to receive(:customer_tags_match?) { :customer_tags_match_result }
|
||||
allow(tag_rule).to receive(:subject_class) { Spree::Order}
|
||||
end
|
||||
|
||||
|
||||
context "when the subject class matches tag_rule#subject_class" do
|
||||
before do
|
||||
allow(subject).to receive(:class) { Spree::Order }
|
||||
end
|
||||
|
||||
context "when the rule does not repond to #additional_requirements_met?" do
|
||||
before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { false } }
|
||||
|
||||
it "returns true" do
|
||||
expect(tag_rule.send(:relevant?)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "when the rule reponds to #additional_requirements_met?" do
|
||||
before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { true } }
|
||||
|
||||
context "and #additional_requirements_met? returns a truthy value" do
|
||||
before { allow(tag_rule).to receive(:additional_requirements_met?) { "smeg" } }
|
||||
|
||||
it "returns true immediately" do
|
||||
expect(tag_rule.send(:relevant?)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "and #additional_requirements_met? returns true" do
|
||||
before { allow(tag_rule).to receive(:additional_requirements_met?) { true } }
|
||||
|
||||
it "returns true immediately" do
|
||||
expect(tag_rule.send(:relevant?)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "and #additional_requirements_met? returns false" do
|
||||
before { allow(tag_rule).to receive(:additional_requirements_met?) { false } }
|
||||
|
||||
it "returns false immediately" do
|
||||
expect(tag_rule.send(:relevant?)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the subject class does not match tag_rule#subject_class" do
|
||||
before do
|
||||
allow(subject).to receive(:class) { Spree::LineItem }
|
||||
end
|
||||
|
||||
it "returns false immediately" do
|
||||
expect(tag_rule.send(:relevant?)).to be false
|
||||
expect(tag_rule).to_not have_received :customer_tags_match?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "determining whether specified customer tags match the given context" do
|
||||
context "when the context has no customer tags specified" do
|
||||
let(:context) { { subject: double(:something), not_tags: double(:not_tags) } }
|
||||
|
||||
before { tag_rule.context = context }
|
||||
|
||||
it "returns false" do
|
||||
expect(tag_rule.send(:customer_tags_match?)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when the context has customer tags specified" do
|
||||
let(:context) { { subject: double(:something), customer_tags: ["member","local","volunteer"] } }
|
||||
|
||||
before { tag_rule.context = context }
|
||||
|
||||
context "when the rule has no preferred customer tags specified" do
|
||||
before do
|
||||
allow(tag_rule).to receive(:preferred_customer_tags) { "" }
|
||||
end
|
||||
|
||||
it "returns false" do
|
||||
expect(tag_rule.send(:customer_tags_match?)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when the rule has preferred customer tags specified that match ANY of the customer tags" do
|
||||
before do
|
||||
allow(tag_rule).to receive(:preferred_customer_tags) { "wholesale,some_tag,member" }
|
||||
end
|
||||
|
||||
it "returns false" do
|
||||
expect(tag_rule.send(:customer_tags_match?)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "when the rule has preferred customer tags specified that match NONE of the customer tags" do
|
||||
before do
|
||||
allow(tag_rule).to receive(:preferred_customer_tags) { "wholesale,some_tag,some_other_tag" }
|
||||
end
|
||||
|
||||
it "returns false" do
|
||||
expect(tag_rule.send(:customer_tags_match?)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "applying a tag rule to a subject" do
|
||||
before { allow(tag_rule).to receive(:apply!) }
|
||||
|
||||
context "when the rule is deemed to be relevant" do
|
||||
before { allow(tag_rule).to receive(:relevant?) { true } }
|
||||
|
||||
context "and customer_tags_match? returns true" do
|
||||
before { expect(tag_rule).to receive(:customer_tags_match?) { true } }
|
||||
|
||||
it "applies the rule" do
|
||||
tag_rule.apply
|
||||
expect(tag_rule).to have_received(:apply!)
|
||||
end
|
||||
end
|
||||
|
||||
context "when customer_tags_match? returns false" do
|
||||
before { expect(tag_rule).to receive(:customer_tags_match?) { false } }
|
||||
before { allow(tag_rule).to receive(:apply_default!) }
|
||||
|
||||
context "and the rule responds to #apply_default!" do
|
||||
before { allow(tag_rule).to receive(:respond_to?).with(:apply_default!, true) { true } }
|
||||
|
||||
it "applies the default action" do
|
||||
tag_rule.apply
|
||||
expect(tag_rule).to_not have_received(:apply!)
|
||||
expect(tag_rule).to have_received(:apply_default!)
|
||||
end
|
||||
end
|
||||
|
||||
context "and the rule does not respond to #apply_default!" do
|
||||
before { allow(tag_rule).to receive(:respond_to?).with(:apply_default!, true) { false } }
|
||||
|
||||
it "does not apply the rule or the default action" do
|
||||
tag_rule.apply
|
||||
expect(tag_rule).to_not have_received(:apply!)
|
||||
expect(tag_rule).to_not have_received(:apply_default!)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the rule is deemed not to be relevant" do
|
||||
before { allow(tag_rule).to receive(:relevant?) { false } }
|
||||
|
||||
it "does not apply the rule" do
|
||||
tag_rule.apply
|
||||
expect(tag_rule).to_not have_received(:apply!)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user