diff --git a/app/controllers/all_casa_admins/casa_orgs_controller.rb b/app/controllers/all_casa_admins/casa_orgs_controller.rb index 7cd7510afe..8bd740c3d6 100644 --- a/app/controllers/all_casa_admins/casa_orgs_controller.rb +++ b/app/controllers/all_casa_admins/casa_orgs_controller.rb @@ -12,7 +12,7 @@ def create @casa_org = CasaOrg.new(casa_org_params) if @casa_org.save - @casa_org.generate_contact_types_and_hearing_types + @casa_org.generate_defaults respond_to do |format| format.html do redirect_to all_casa_admins_casa_org_path(@casa_org), diff --git a/app/controllers/casa_org_controller.rb b/app/controllers/casa_org_controller.rb index 8c6199c6c5..ae9ea870df 100644 --- a/app/controllers/casa_org_controller.rb +++ b/app/controllers/casa_org_controller.rb @@ -55,7 +55,8 @@ def casa_org_update_params :twilio_api_key_sid, :twilio_api_key_secret, :twilio_enabled, - :learning_topic_active + :learning_topic_active, + contact_topics: ContactTopics::PERMITTED_ATTRIBUTES ) end diff --git a/app/controllers/case_contacts/form_controller.rb b/app/controllers/case_contacts/form_controller.rb index 3afead4f64..5d0a46bcde 100644 --- a/app/controllers/case_contacts/form_controller.rb +++ b/app/controllers/case_contacts/form_controller.rb @@ -27,21 +27,21 @@ def update remove_nil_draft_ids if @case_contact.update(case_contact_params) respond_to do |format| - format.html { + format.html do if step == steps.last finish_editing else render_wizard @case_contact, {}, {case_contact_id: @case_contact.id} end - } + end format.json { head :ok } end else respond_to do |format| - format.html { + format.html do get_cases_and_contact_types render step - } + end format.json { head :internal_server_error } end end @@ -80,9 +80,9 @@ def finish_editing end def send_reimbursement_email(case_contact) - if case_contact.should_send_reimbursement_email? - SupervisorMailer.reimbursement_request_email(case_contact.creator, case_contact.supervisor).deliver_later - end + return unless case_contact.should_send_reimbursement_email? + + SupervisorMailer.reimbursement_request_email(case_contact.creator, case_contact.supervisor).deliver_later end def update_volunteer_address(case_contact) @@ -124,15 +124,15 @@ def case_contact_params # Deletes the current associations (from the join table) only if the submitted form body has the parameters for # the contact_type ids. def remove_unwanted_contact_types - if params.dig(:case_contact, :case_contact_contact_type_attributes) - @case_contact.case_contact_contact_type.destroy_all - end + return unless params.dig(:case_contact, :case_contact_contact_type_attributes) + + @case_contact.case_contact_contact_type.destroy_all end def remove_nil_draft_ids - if params.dig(:case_contact, :draft_case_ids) - params[:case_contact][:draft_case_ids] -= [""] - end + return unless params.dig(:case_contact, :draft_case_ids) + + params[:case_contact][:draft_case_ids] -= [""] end def set_progress diff --git a/app/controllers/case_contacts_controller.rb b/app/controllers/case_contacts_controller.rb index b741ab5a72..6348b21521 100644 --- a/app/controllers/case_contacts_controller.rb +++ b/app/controllers/case_contacts_controller.rb @@ -48,7 +48,8 @@ def new [] end - @case_contact = CaseContact.create(creator: current_user, draft_case_ids: draft_case_ids) + @case_contact = CaseContact.create(creator: current_user, draft_case_ids:) + @case_contact.contact_topics_from(current_organization) redirect_to case_contact_form_path(CaseContact::FORM_STEPS.first, case_contact_id: @case_contact.id) end @@ -84,9 +85,10 @@ def leave def update_or_create_additional_expense(all_ae_params, cc) all_ae_params.each do |ae_params| id = ae_params[:id] - current = AdditionalExpense.find_by(id: id) + current = AdditionalExpense.find_by(id:) if current - current.assign_attributes(other_expense_amount: ae_params[:other_expense_amount], other_expenses_describe: ae_params[:other_expenses_describe]) + current.assign_attributes(other_expense_amount: ae_params[:other_expense_amount], + other_expenses_describe: ae_params[:other_expenses_describe]) save_or_add_error(current, cc) else create_new_exp = cc.additional_expenses.build(ae_params) diff --git a/app/models/casa_org.rb b/app/models/casa_org.rb index c1d22664fb..27a33aabce 100644 --- a/app/models/casa_org.rb +++ b/app/models/casa_org.rb @@ -1,5 +1,7 @@ class CasaOrg < ApplicationRecord - CASA_DEFAULT_COURT_REPORT = File.new(Rails.root.join("app", "documents", "templates", "default_report_template.docx"), "r") + CASA_DEFAULT_COURT_REPORT = File.new( + Rails.root.join("app", "documents", "templates", "default_report_template.docx"), "r" + ) CASA_DEFAULT_LOGO = Rails.root.join("public", "logo.jpeg") scope :with_logo, -> { joins(:logo_attachment) } @@ -10,7 +12,10 @@ class CasaOrg < ApplicationRecord validates :name, presence: true, uniqueness: true validates_with CasaOrgValidator - validate :validate_twilio_credentials, if: -> { twilio_enabled || twilio_account_sid.present? || twilio_api_key_sid.present? || twilio_api_key_secret.present? }, on: :update + validate :validate_twilio_credentials, if: lambda { + twilio_enabled || twilio_account_sid.present? || twilio_api_key_sid.present? || twilio_api_key_secret.present? + }, on: :update + validate :validate_contact_topics has_many :users, dependent: :destroy has_many :casa_cases, dependent: :destroy @@ -85,11 +90,17 @@ def set_slug self.slug = name.parameterize end - def generate_contact_types_and_hearing_types + def generate_defaults ActiveRecord::Base.transaction do ContactTypeGroup.generate_for_org!(self) HearingType.generate_for_org!(self) + ContactTopics.generate_for_org!(self) end + # FIXME: is this needed? do we care about the case where the defaults + # are invalid? + rescue ActiveRecord::RecordInvalid => e + errors.add(:base, + "Failed to generate default contact type groups, hearing types, or contact topics: #{e.message}") end def contact_types_by_group @@ -114,11 +125,11 @@ def has_alternate_active_banner?(current_banner_id) private def sanitize_svg - if attachment_changes["logo"] - file = attachment_changes["logo"].attachable - sanitized_file = SvgSanitizerService.sanitize(file) - logo.unfurl(sanitized_file) - end + return unless attachment_changes["logo"] + + file = attachment_changes["logo"].attachable + sanitized_file = SvgSanitizerService.sanitize(file) + logo.unfurl(sanitized_file) end # def to_param @@ -135,10 +146,14 @@ def validate_twilio_credentials end end + def validate_contact_topics + ContactTopics.validate(self) + end + def normalize_phone_number - if twilio_phone_number&.length == 10 - self.twilio_phone_number = "+1#{twilio_phone_number}" - end + return unless twilio_phone_number&.length == 10 + + self.twilio_phone_number = "+1#{twilio_phone_number}" end end @@ -149,6 +164,7 @@ def normalize_phone_number # id :bigint not null, primary key # additional_expenses_enabled :boolean default(FALSE) # address :string +# contact_topics :jsonb # display_name :string # footer_links :string default([]), is an Array # learning_topic_active :boolean default(FALSE) diff --git a/app/models/case_contact.rb b/app/models/case_contact.rb index da624e1777..cbca61fecb 100644 --- a/app/models/case_contact.rb +++ b/app/models/case_contact.rb @@ -5,7 +5,7 @@ class CaseContact < ApplicationRecord attr_accessor :duration_hours validate :contact_made_chosen - validates :miles_driven, numericality: {greater_than_or_equal_to: 0, less_than: 10000} + validates :miles_driven, numericality: {greater_than_or_equal_to: 0, less_than: 10_000} validates :medium_type, presence: true, if: :active_or_details? validates :occurred_at, presence: true, if: :active_or_details? validates :duration_minutes, presence: true, if: :active_or_details? @@ -13,9 +13,10 @@ class CaseContact < ApplicationRecord validate :reimbursement_only_when_miles_driven, if: :active_or_expenses? validate :volunteer_address_when_reimbursement_wanted, if: :active_or_expenses? validate :volunteer_address_is_valid, if: :active_or_expenses? + validate :validate_contact_topics belongs_to :creator, class_name: "User" - has_one :supervisor_volunteer, -> { + has_one :supervisor_volunteer, lambda { where(is_active: true) }, primary_key: :creator_id, foreign_key: :volunteer_id has_one :supervisor, through: :creator @@ -50,53 +51,53 @@ def active_or_expenses? accepts_nested_attributes_for :case_contact_contact_type accepts_nested_attributes_for :casa_case - scope :supervisors, ->(supervisor_ids = nil) { - joins(:supervisor_volunteer).where(supervisor_volunteers: {supervisor_id: supervisor_ids}) if supervisor_ids.present? + scope :supervisors, lambda { |supervisor_ids = nil| + if supervisor_ids.present? + joins(:supervisor_volunteer).where(supervisor_volunteers: {supervisor_id: supervisor_ids}) + end } - scope :creators, ->(creator_ids = nil) { + scope :creators, lambda { |creator_ids = nil| where(creator_id: creator_ids) if creator_ids.present? } - scope :casa_org, ->(casa_org_id = nil) { - joins(:casa_case).where(casa_cases: {casa_org_id: casa_org_id}) if casa_org_id.present? + scope :casa_org, lambda { |casa_org_id = nil| + joins(:casa_case).where(casa_cases: {casa_org_id:}) if casa_org_id.present? } - scope :occurred_between, ->(start_date = nil, end_date = nil) { + scope :occurred_between, lambda { |start_date = nil, end_date = nil| where("occurred_at BETWEEN ? AND ?", start_date, end_date) if start_date.present? && end_date.present? } - scope :occurred_starting_at, ->(start_date = nil) { + scope :occurred_starting_at, lambda { |start_date = nil| where("occurred_at >= ?", start_date) if start_date.present? } - scope :occurred_ending_at, ->(end_date = nil) { + scope :occurred_ending_at, lambda { |end_date = nil| where("occurred_at <= ?", end_date) if end_date.present? } - scope :created_max_ago, ->(time_range = nil) { + scope :created_max_ago, lambda { |time_range = nil| where("case_contacts.created_at > ?", time_range) if time_range.present? } - scope :contact_made, ->(contact_made = nil) { - where(contact_made: contact_made) if /true|false/.match?(contact_made.to_s) + scope :contact_made, lambda { |contact_made = nil| + where(contact_made:) if /true|false/.match?(contact_made.to_s) } - scope :has_transitioned, ->(has_transitioned = nil) { + scope :has_transitioned, lambda { |has_transitioned = nil| if /true|false/.match?(has_transitioned.to_s) operator = has_transitioned ? "<=" : ">" joins(:casa_case).where("casa_cases.birth_month_year_youth #{operator} ?", CasaCase::TRANSITION_AGE.years.ago) end } - scope :want_driving_reimbursement, ->(want_driving_reimbursement = nil) { - if /true|false/.match?(want_driving_reimbursement.to_s) - where(want_driving_reimbursement: want_driving_reimbursement) - end + scope :want_driving_reimbursement, lambda { |want_driving_reimbursement = nil| + where(want_driving_reimbursement:) if /true|false/.match?(want_driving_reimbursement.to_s) } - scope :contact_type, ->(contact_type_ids = nil) { + scope :contact_type, lambda { |contact_type_ids = nil| includes(:contact_types).where("contact_types.id": [contact_type_ids]) if contact_type_ids.present? } - scope :contact_types, ->(contact_type_id_list = nil) { + scope :contact_types, lambda { |contact_type_id_list = nil| contact_type_id_list.reject! { |id| id.blank? } return if contact_type_id_list.blank? includes(:contact_types).where("contact_types.id": contact_type_id_list) } - scope :contact_type_groups, ->(contact_type_group_ids = nil) { + scope :contact_type_groups, lambda { |contact_type_group_ids = nil| # to handle case when passing ids == [''] && ids == nil if contact_type_group_ids&.join&.length&.positive? joins(contact_types: :contact_type_group) @@ -104,17 +105,17 @@ def active_or_expenses? .group(:id) end } - scope :grab_all, ->(current_user) { - with_deleted if current_user.is_a?(CasaAdmin) # TODO since this cases on user type it should be in a Policy file + scope :grab_all, lambda { |current_user| + with_deleted if current_user.is_a?(CasaAdmin) # TODO: since this cases on user type it should be in a Policy file } - scope :contact_medium, ->(medium_type) { - where(medium_type: medium_type) if medium_type.present? + scope :contact_medium, lambda { |medium_type| + where(medium_type:) if medium_type.present? } scope :filter_by_reimbursement_status, ->(boolean) { where reimbursement_complete: boolean } - scope :sorted_by, ->(sort_option) { + scope :sorted_by, lambda { |sort_option| direction = /desc$/.match?(sort_option) ? "desc" : "asc" case sort_option.to_s @@ -133,7 +134,7 @@ def active_or_expenses? end } - scope :with_casa_case, ->(case_ids) { + scope :with_casa_case, lambda { |case_ids| where(casa_case_id: case_ids) if case_ids.present? } @@ -141,15 +142,15 @@ def active_or_expenses? filterrific( default_filter_params: {sorted_by: "occurred_at_desc"}, - available_filters: [ - :sorted_by, - :occurred_starting_at, - :occurred_ending_at, - :contact_type, - :contact_made, - :contact_medium, - :want_driving_reimbursement, - :no_drafts + available_filters: %i[ + sorted_by + occurred_starting_at + occurred_ending_at + contact_type + contact_made + contact_medium + want_driving_reimbursement + no_drafts ] ) @@ -195,17 +196,16 @@ def reimbursement_only_when_miles_driven end def volunteer_address_when_reimbursement_wanted - if want_driving_reimbursement && volunteer_address&.empty? - errors.add(:base, "Must enter a valid mailing address for the reimbursement.") - end + return unless want_driving_reimbursement && volunteer_address&.empty? + + errors.add(:base, "Must enter a valid mailing address for the reimbursement.") end def volunteer_address_is_valid - if volunteer_address&.present? - if Address.new(user_id: creator.id, content: volunteer_address).invalid? - errors.add(:base, "The volunteer's address is not valid.") - end - end + return unless volunteer_address&.present? + return unless Address.new(user_id: creator.id, content: volunteer_address).invalid? + + errors.add(:base, "The volunteer's address is not valid.") end def contact_made_chosen @@ -257,6 +257,15 @@ def volunteer end end + def validate_contact_topics + ContactTopics.validate(self) + end + + def contact_topics_from(source) + self.contact_topics = source.contact_topics + save! + end + def self.options_for_sorted_by sorted_by_params.each.map { |option_pair| option_pair.reverse } end @@ -264,7 +273,9 @@ def self.options_for_sorted_by def self.case_hash_from_cases(cases) casa_case_ids = cases.map(&:draft_case_ids).flatten.uniq.sort casa_case_ids.each_with_object({}) do |casa_case_id, hash| - hash[casa_case_id] = cases.select { |c| c.casa_case_id == casa_case_id || c.draft_case_ids.include?(casa_case_id) } + hash[casa_case_id] = cases.select do |c| + c.casa_case_id == casa_case_id || c.draft_case_ids.include?(casa_case_id) + end end end @@ -290,6 +301,7 @@ def self.case_hash_from_cases(cases) # # id :bigint not null, primary key # contact_made :boolean default(FALSE) +# contact_topics :jsonb # deleted_at :datetime # draft_case_ids :integer default([]), is an Array # duration_minutes :integer diff --git a/app/models/contact_topics.rb b/app/models/contact_topics.rb new file mode 100644 index 0000000000..3bf4bd5394 --- /dev/null +++ b/app/models/contact_topics.rb @@ -0,0 +1,33 @@ +class ContactTopics + CASA_DEFAULT_COURT_TOPICS = Rails.root.join("data", "default_contact_topics.yml") + PERMITTED_ATTRIBUTES = %w[title details active].sort.freeze + + class << self + def generate_for_org!(casa_org) + casa_org.contact_topics = default_contact_topics + casa_org.save! + end + + def validate(source) + contact_topics = Array.wrap(source.contact_topics) + + contact_topics.each do |topic| + unless topic.is_a?(Hash) + source.errors.add(:contact_topics, "must be an array of hashes") + next + end + + next if (topic.keys.sort - PERMITTED_ATTRIBUTES).empty? + + source.errors.add(:contact_topics, + "expected keys: #{PERMITTED_ATTRIBUTES.join(", ")} got keys: #{topic.keys.sort}") + end + end + + private + + def default_contact_topics + YAML.load_file(CASA_DEFAULT_COURT_TOPICS) + end + end +end diff --git a/app/values/case_contact_parameters.rb b/app/values/case_contact_parameters.rb index ed3cdc5e6a..9157cee25b 100644 --- a/app/values/case_contact_parameters.rb +++ b/app/values/case_contact_parameters.rb @@ -14,14 +14,11 @@ def initialize(params) :volunteer_address, draft_case_ids: [], case_contact_contact_type_attributes: [:contact_type_id], - additional_expenses_attributes: [:id, :other_expense_amount, :other_expenses_describe] + additional_expenses_attributes: %i[id other_expense_amount other_expenses_describe], + contact_topics: ContactTopics::PERMITTED_ATTRIBUTES ) - if params.dig(:case_contact, :duration_minutes) - new_params[:duration_minutes] = convert_duration_minutes(params) - end - if params.dig(:case_contact, :miles_driven) - new_params[:miles_driven] = convert_miles_driven(params) - end + new_params[:duration_minutes] = convert_duration_minutes(params) if params.dig(:case_contact, :duration_minutes) + new_params[:miles_driven] = convert_miles_driven(params) if params.dig(:case_contact, :miles_driven) super(new_params) end @@ -40,8 +37,6 @@ def convert_miles_driven(params) miles_driven.to_i end - private - def params __getobj__ end diff --git a/data/default_contact_topics.yml b/data/default_contact_topics.yml new file mode 100644 index 0000000000..b106c46e0d --- /dev/null +++ b/data/default_contact_topics.yml @@ -0,0 +1,32 @@ +- title: "Background information" + details: + - "When did the family first come into contact with the Department of Social Services or Department of Juvenile Justice – how many times?" + - "Tell the history of their involvement with the department and any facts about their life that could help determine the need for placement and/or services." + - "Discuss the child’s history – behavior problems, educational history, medical history, psychological history (any hospitalizations, previous counseling, etc.)" + - "If child has been placed previously give a history of the child’s placements (placed with different parents, relatives, DSS, etc)." +- title: "Current situation" + details: + - "Where is the child placed?" + - "How is the child adjusting to the placement?" + - "Are there any issues or concerns about the placement? If so, describe these concerns and specify the actions being taken to address them." +- title: "Education, vocation, or daycare" + details: + - "Where is the child placed for education (daycare, public school, non-public school, GED, Job Corps, etc)?" + - "How is the child adjusting to the educational placement? Are there any education-related concerns at this point? If yes, detail them and mention the steps taken to address them." + - "Does the child have an IEP? If not, is there a need for one?" + - "Is the child employed? If not, are they looking for a job?" + - "Does the child have vocational/life skills? Are they attending life skill classes?" + - "Are there any other life skill needs? (Driver’s education, state ID, transportation assistance, etc.)" + - "What is the feedback from professionals providing these services about the child's progress? Include strengths and not just needs." +- title: "Health and mental health" + details: + - "Is the child up to date with medical exams?" + - "Are there any other medical concerns?" + - "Is the child receiving therapy, medication monitoring, mentoring, or other services? If so, specify with whom these services are being received." +- title: "Family and community connections" + details: + - "Is this child seeing parents, siblings, other relatives? If so, who is the child visiting, and how often? Does the child desire a different arrangement?" + - "Detail the steps parents have taken to address court orders. Address any barriers and highlight positive steps." +- title: "Child’s strengths" + details: + - "Describe the child’s strengths, interests, and hobbies to provide a well-rounded perspective." diff --git a/db/migrate/20240131210129_add_contact_topics_json_to_casa_org_and_case_contact.rb b/db/migrate/20240131210129_add_contact_topics_json_to_casa_org_and_case_contact.rb new file mode 100644 index 0000000000..eb3cb71874 --- /dev/null +++ b/db/migrate/20240131210129_add_contact_topics_json_to_casa_org_and_case_contact.rb @@ -0,0 +1,6 @@ +class AddContactTopicsJsonToCasaOrgAndCaseContact < ActiveRecord::Migration[7.0] + def change + add_column :casa_orgs, :contact_topics, :jsonb, default: [] + add_column :case_contacts, :contact_topics, :jsonb, default: [] + end +end diff --git a/db/schema.rb b/db/schema.rb index 53af7a751b..45be1108f2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_11_25_150721) do +ActiveRecord::Schema[7.1].define(version: 2024_01_31_210129) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -160,6 +160,7 @@ t.boolean "twilio_enabled", default: false t.boolean "additional_expenses_enabled", default: false t.boolean "learning_topic_active", default: false + t.jsonb "contact_topics", default: [] t.index ["slug"], name: "index_casa_orgs_on_slug", unique: true end @@ -184,6 +185,18 @@ t.index ["contact_type_id"], name: "index_case_contact_contact_types_on_contact_type_id" end + create_table "case_contact_topics", force: :cascade do |t| + t.string "title" + t.string "details" + t.string "answer" + t.boolean "active", default: false + t.string "source_type", null: false + t.bigint "source_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["source_type", "source_id"], name: "index_case_contact_topics_on_source" + end + create_table "case_contacts", force: :cascade do |t| t.bigint "creator_id", null: false t.bigint "casa_case_id" @@ -201,6 +214,7 @@ t.string "status", default: "started" t.integer "draft_case_ids", default: [], array: true t.string "volunteer_address" + t.jsonb "contact_topics", default: [] t.index ["casa_case_id"], name: "index_case_contacts_on_casa_case_id" t.index ["creator_id"], name: "index_case_contacts_on_creator_id" t.index ["deleted_at"], name: "index_case_contacts_on_deleted_at" diff --git a/db/seeds.rb b/db/seeds.rb index 1751534412..f42b48ad84 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -12,6 +12,7 @@ require_relative "../lib/tasks/data_post_processors/case_contact_populator" require_relative "../lib/tasks/data_post_processors/contact_type_populator" require_relative "../lib/tasks/data_post_processors/sms_notification_event_populator" +require_relative "../lib/tasks/data_post_processors/contact_topic_populator" class SeederMain attr_reader :db_populator, :rng @@ -76,6 +77,7 @@ def active_record_classes def post_process_data ContactTypePopulator.populate CaseContactPopulator.populate + ContactTopicPopulator.populate end def get_seed_specification @@ -97,7 +99,7 @@ def get_seed_specification def report_object_counts log "\nRecords written to the DB:\n\nCount Class Name\n----- ----------\n\n" active_record_classes.each do |klass| - log "%5d %s" % [klass.count, klass.name] + log format("%5d %s", klass.count, klass.name) end log "\n\nVolunteers, Supervisors and CasaAdmins are types of Users" end diff --git a/lib/tasks/data_post_processors/contact_topic_populator.rb b/lib/tasks/data_post_processors/contact_topic_populator.rb new file mode 100644 index 0000000000..e0071c75a0 --- /dev/null +++ b/lib/tasks/data_post_processors/contact_topic_populator.rb @@ -0,0 +1,7 @@ +module ContactTopicPopulator + def self.populate + CasaOrg.all.each do |casa_org| + ContactTopics.generate_for_org!(casa_org) + end + end +end diff --git a/spec/factories/casa_orgs.rb b/spec/factories/casa_orgs.rb index bfdcb99331..1a5be81552 100644 --- a/spec/factories/casa_orgs.rb +++ b/spec/factories/casa_orgs.rb @@ -12,5 +12,13 @@ trait :with_logo do logo { Rack::Test::UploadedFile.new(Rails.root.join("spec", "fixtures", "org_logo.jpeg")) } end + + trait :no_twilio do + twilio_account_sid { nil } + twilio_api_key_secret { nil } + twilio_api_key_sid { nil } + twilio_phone_number { nil } + twilio_enabled { false } + end end end diff --git a/spec/models/casa_org_spec.rb b/spec/models/casa_org_spec.rb index fce9d87da2..a7cc7c8863 100644 --- a/spec/models/casa_org_spec.rb +++ b/spec/models/casa_org_spec.rb @@ -80,19 +80,26 @@ before do 5.times do casa_case = create(:casa_case, casa_org: org) - 3.times { create(:case_contact, casa_case: casa_case) } + 3.times { create(:case_contact, casa_case:) } end end it { is_expected.to eq 15 } end - describe "generate_contact_types_and_hearing_types" do - let(:org) { create(:casa_org) } + describe "generate_defaults" do + let(:org) { create(:casa_org, :no_twilio) } + let(:contact_topics) { [{"test" => "test"}] } - before { org.generate_contact_types_and_hearing_types } + before do + allow(ContactTopics).to receive(:default_contact_topics).and_return(contact_topics) + stub_const("ContactTopics::PERMITTED_ATTRIBUTES", ["test"]) + org.generate_defaults + end describe "generates default contact type groups" do - let(:groups) { ContactTypeGroup.where(casa_org: org).joins(:contact_types).pluck(:name, "contact_types.name").sort } + let(:groups) do + ContactTypeGroup.where(casa_org: org).joins(:contact_types).pluck(:name, "contact_types.name").sort + end it "matches default contact type groups" do expect(groups).to eq([["CASA", "Supervisor"], @@ -128,52 +135,68 @@ end end - describe "mileage rate for a given date" do - let(:casa_org) { build(:casa_org) } + describe "generates default contact topics" do + it "matches default contact topics" do + expect(org.contact_topics).to eq(contact_topics) + end + + # FIXME: is this needed? do we care about the case where the defaults + # are invalid? + context "when contact topics are invalid" do + let(:contact_topics) { "invalid" } - describe "with a casa org with no rates" do - it "is nil" do - expect(casa_org.mileage_rate_for_given_date(Date.today)).to be_nil + it "does not change contact topics if invalid" do + expect(org.errors[:base]).to_not be_empty end end + end + end - describe "with a casa org with inactive dates" do - let!(:mileage_rates) do - [ - create(:mileage_rate, casa_org: casa_org, effective_date: 10.days.ago, is_active: false), - create(:mileage_rate, casa_org: casa_org, effective_date: 3.days.ago, is_active: false) - ] - end + describe "mileage rate for a given date" do + let(:casa_org) { build(:casa_org) } - it "is nil" do - expect(casa_org.mileage_rates.count).to eq 2 - expect(casa_org.mileage_rate_for_given_date(Date.today)).to be_nil - end + describe "with a casa org with no rates" do + it "is nil" do + expect(casa_org.mileage_rate_for_given_date(Date.today)).to be_nil end + end - describe "with active dates in the future" do - let!(:mileage_rate) { create(:mileage_rate, casa_org: casa_org, effective_date: 3.days.from_now) } + describe "with a casa org with inactive dates" do + let!(:mileage_rates) do + [ + create(:mileage_rate, casa_org:, effective_date: 10.days.ago, is_active: false), + create(:mileage_rate, casa_org:, effective_date: 3.days.ago, is_active: false) + ] + end - it "is nil" do - expect(casa_org.mileage_rates.count).to eq 1 - expect(casa_org.mileage_rate_for_given_date(Date.today)).to be_nil - end + it "is nil" do + expect(casa_org.mileage_rates.count).to eq 2 + expect(casa_org.mileage_rate_for_given_date(Date.today)).to be_nil end + end - describe "with active dates in the past" do - let!(:mileage_rates) do - [ - create(:mileage_rate, casa_org: casa_org, amount: 4.50, effective_date: 20.days.ago), - create(:mileage_rate, casa_org: casa_org, amount: 5.50, effective_date: 10.days.ago), - create(:mileage_rate, casa_org: casa_org, amount: 6.50, effective_date: 3.days.ago) - ] - end + describe "with active dates in the future" do + let!(:mileage_rate) { create(:mileage_rate, casa_org:, effective_date: 3.days.from_now) } - it "uses the most recent date" do - expect(casa_org.mileage_rate_for_given_date(12.days.ago.to_date)).to eq 4.50 - expect(casa_org.mileage_rate_for_given_date(5.days.ago.to_date)).to eq 5.50 - expect(casa_org.mileage_rate_for_given_date(Date.today)).to eq 6.50 - end + it "is nil" do + expect(casa_org.mileage_rates.count).to eq 1 + expect(casa_org.mileage_rate_for_given_date(Date.today)).to be_nil + end + end + + describe "with active dates in the past" do + let!(:mileage_rates) do + [ + create(:mileage_rate, casa_org:, amount: 4.50, effective_date: 20.days.ago), + create(:mileage_rate, casa_org:, amount: 5.50, effective_date: 10.days.ago), + create(:mileage_rate, casa_org:, amount: 6.50, effective_date: 3.days.ago) + ] + end + + it "uses the most recent date" do + expect(casa_org.mileage_rate_for_given_date(12.days.ago.to_date)).to eq 4.50 + expect(casa_org.mileage_rate_for_given_date(5.days.ago.to_date)).to eq 5.50 + expect(casa_org.mileage_rate_for_given_date(Date.today)).to eq 6.50 end end end diff --git a/spec/models/case_contact_spec.rb b/spec/models/case_contact_spec.rb index 8e1508ce8b..7d7064356c 100644 --- a/spec/models/case_contact_spec.rb +++ b/spec/models/case_contact_spec.rb @@ -71,6 +71,19 @@ it "can be updated for 30 days after end of quarter" do expect(build_stubbed(:case_contact, occurred_at: Time.zone.now - 4.months + 1.day)).to be_valid end + + it "wont save unpermitted topics" do + case_contact = build_stubbed(:case_contact, contact_topics: "") + expect(case_contact).to_not be_valid + expect(case_contact.errors[:contact_topics]).to include("must be an array of hashes") + end + + it "wont save unpermitted keys" do + stub_const("ContactTopics::PERMITTED_ATTRIBUTES", ["test"]) + + case_contact = build_stubbed(:case_contact, contact_topics: [{"test" => 1, "test2" => 2}]) + expect(case_contact).to_not be_valid + end end context "status is started" do @@ -329,7 +342,7 @@ context "with invalid sort option" do let(:sort_option) { "1254645" } - it { expect { sorted_by }.to raise_error(ArgumentError, "Invalid sort option: \"1254645\"") } + it { expect { sorted_by }.to raise_error(ArgumentError, 'Invalid sort option: "1254645"') } end context "with valid sort option" do @@ -463,7 +476,7 @@ describe ".with_casa_case" do let!(:casa_case) { create(:casa_case) } - let!(:case_contacts) { create_list(:case_contact, 3, casa_case: casa_case) } + let!(:case_contacts) { create_list(:case_contact, 3, casa_case:) } before { create_list(:case_contact, 3) } @@ -494,7 +507,7 @@ groups_with_types = case_contact.contact_groups_with_types - expect(groups_with_types.keys).to match_array(["Family", "Health"]) + expect(groups_with_types.keys).to match_array(%w[Family Health]) expect(groups_with_types["Family"]).to match_array(["Parent"]) expect(groups_with_types["Health"]).to match_array(["Medical Professional", "Other Therapist"]) end @@ -511,7 +524,7 @@ context "a followup exists in requested status" do it "returns nil" do case_contact = build_stubbed(:case_contact) - followup = create(:followup, case_contact: case_contact) + followup = create(:followup, case_contact:) expect(case_contact.requested_followup).to eq(followup) end @@ -529,7 +542,9 @@ end describe "when casa org has value for mileage_rate_for_given_date" do - let!(:mileage_rate) { create(:mileage_rate, casa_org: case_contact.casa_case.casa_org, effective_date: 3.days.ago, amount: 5.50) } + let!(:mileage_rate) do + create(:mileage_rate, casa_org: case_contact.casa_case.casa_org, effective_date: 3.days.ago, amount: 5.50) + end it "is multiple of miles driven and mileage rate" do expect(case_contact.reimbursement_amount).to eq 2508 @@ -539,9 +554,9 @@ describe "#should_send_reimbursement_email?" do let(:supervisor) { create(:supervisor, receive_reimbursement_email: true) } - let(:volunteer) { create(:volunteer, supervisor: supervisor) } + let(:volunteer) { create(:volunteer, supervisor:) } let(:casa_case) { create(:casa_case) } - let(:case_contact) { build(:case_contact, :wants_reimbursement, casa_case: casa_case, creator: volunteer) } + let(:case_contact) { build(:case_contact, :wants_reimbursement, casa_case:, creator: volunteer) } it "returns true if wants reimbursement, reimbursement changed, and has active supervisor" do expect(case_contact.want_driving_reimbursement_changed?).to be true @@ -567,11 +582,11 @@ describe "volunteer assignment" do let(:casa_org) { create(:casa_org) } - let(:admin) { create(:casa_admin, casa_org: casa_org) } - let(:supervisor) { create(:supervisor, casa_org: casa_org) } - let(:volunteer) { create(:volunteer, supervisor: supervisor, casa_org: casa_org) } - let(:casa_case) { create(:casa_case, casa_org: casa_org) } - let(:case_contact) { build(:case_contact, casa_case: casa_case, creator: creator) } + let(:admin) { create(:casa_admin, casa_org:) } + let(:supervisor) { create(:supervisor, casa_org:) } + let(:volunteer) { create(:volunteer, supervisor:, casa_org:) } + let(:casa_case) { create(:casa_case, casa_org:) } + let(:case_contact) { build(:case_contact, casa_case:, creator:) } context "when creator is volunteer" do let(:creator) { volunteer } @@ -589,7 +604,7 @@ let(:creator) { admin } context "when casa case has one volunteer assigned" do - let!(:contact_assignment) { create(:case_assignment, volunteer: volunteer, casa_case: casa_case) } + let!(:contact_assignment) { create(:case_assignment, volunteer:, casa_case:) } it "volunteer is the assigned volunteer" do expect(case_contact.volunteer).to eq volunteer @@ -611,13 +626,13 @@ end context "when casa case has more than 1 volunteer assigned" do - let(:other_volunteer) { create(:volunteer, casa_org: casa_org) } - let!(:contact_assignments) { + let(:other_volunteer) { create(:volunteer, casa_org:) } + let!(:contact_assignments) do [ - create(:case_assignment, volunteer: volunteer, casa_case: casa_case), - create(:case_assignment, volunteer: other_volunteer, casa_case: casa_case) + create(:case_assignment, volunteer:, casa_case:), + create(:case_assignment, volunteer: other_volunteer, casa_case:) ] - } + end it "volunteer is nil" do expect(case_contact.volunteer).to be nil @@ -629,4 +644,27 @@ end end end + + describe "contact_topics_from" do + let(:contact_topics) { [{"test" => "test"}] } + let(:casa_org) { build(:casa_org, contact_topics:) } + let(:case_contact) { create(:case_contact) } + let(:second_case_contact) { build(:case_contact, contact_topics:) } + + before do + stub_const("ContactTopics::PERMITTED_ATTRIBUTES", ["test"]) + end + + it "sets the contact topics from the organization" do + expect(case_contact.contact_topics).to eq([]) + case_contact.contact_topics_from(casa_org) + expect(case_contact.reload.contact_topics).to eq(contact_topics) + end + + it "sets contact topics from another case contact" do + expect(case_contact.contact_topics).to eq([]) + case_contact.contact_topics_from(second_case_contact) + expect(case_contact.reload.contact_topics).to eq(contact_topics) + end + end end diff --git a/spec/models/contact_topics_spec.rb b/spec/models/contact_topics_spec.rb new file mode 100644 index 0000000000..4bf77af08a --- /dev/null +++ b/spec/models/contact_topics_spec.rb @@ -0,0 +1,70 @@ +require "rails_helper" + +RSpec.describe ContactTopics, type: :model do + let(:casa_org) { build(:casa_org) } + let(:contact_topics) { [{"test" => "test"}] } + let(:permitted_attributes) { %w[test] } + + before do + allow(YAML).to receive(:load_file).and_return(contact_topics) + + stub_const("ContactTopics::PERMITTED_ATTRIBUTES", permitted_attributes) + end + + describe "sets the default topics for organization" do + # TODO: is this okay? + let(:casa_org) { create(:casa_org, :no_twilio) } + + it "sets the default topics for organization" do + expect(casa_org.contact_topics).to eq([]) + ContactTopics.generate_for_org!(casa_org) + expect(casa_org.reload.contact_topics).to eq(contact_topics) + end + end + + describe "config validations" do + let(:casa_org) { build(:casa_org, contact_topics:) } + + describe "topics is a valid single hash" do + let(:contact_topics) { {"test" => "test"} } + + it "accepts hash" do + expect(casa_org.valid?).to be true + expect(casa_org.errors[:contact_topics]).to eq([]) + end + end + describe "topics is not an array of hashes" do + let(:contact_topics) { ["tests"] } + + it "adds hash error to org" do + expect(casa_org.valid?).to be false + expect(casa_org.errors[:contact_topics]).to include("must be an array of hashes") + end + end + + describe "has unpermitted keys" do + let(:contact_topics) { [{"failing key" => "key"}] } + + it "adds error to org" do + expect(casa_org.valid?).to be false + expect(casa_org.errors[:contact_topics].count).to eq(1) + end + end + + describe "has permitted keys" do + let(:contact_topics) { [{"test" => "test"}] } + let(:permitted_attributes) { %w[test other] } + + it "is valid if not all permitted keys present" do + expect(casa_org.valid?).to be true + expect(casa_org.errors[:contact_topics]).to be_empty + end + + it "is valid if not keys given" do + casa_org = build(:casa_org, contact_topics: []) + expect(casa_org.valid?).to be true + expect(casa_org.errors[:contact_topics]).to be_empty + end + end + end +end diff --git a/spec/requests/casa_org_spec.rb b/spec/requests/casa_org_spec.rb index b0d0b64a67..c9fa793091 100644 --- a/spec/requests/casa_org_spec.rb +++ b/spec/requests/casa_org_spec.rb @@ -2,12 +2,20 @@ RSpec.describe "CasaOrg", type: :request do let(:casa_org) { build(:casa_org) } - let(:casa_case) { build_stubbed(:casa_case, casa_org: casa_org) } + let(:casa_case) { build_stubbed(:casa_case, casa_org:) } + let(:permitted_topics) { %w[title active] } + let(:contact_topics) do + [ + {"title" => "Abuse", "active" => "true"}, + {"title" => "Neglect", "active" => "true"} + ] + end - before { + before do stub_twillio - sign_in create(:casa_admin, casa_org: casa_org) - } + sign_in create(:casa_admin, casa_org:) + stub_const("ContactTopics::PERMITTED_ATTRIBUTES", permitted_topics) + end describe "GET /edit" do subject(:request) do @@ -26,7 +34,8 @@ name: "name", display_name: "display_name", address: "address", twilio_account_sid: "articuno34", twilio_api_key_sid: "Aladdin", twilio_api_key_secret: "open sesame", twilio_phone_number: "+12223334444", - show_driving_reimbursement: "1", additional_expenses_enabled: "1" + show_driving_reimbursement: "1", additional_expenses_enabled: "1", + contact_topics: } end @@ -44,19 +53,20 @@ expect(casa_org.twilio_phone_number).to eq "+12223334444" expect(casa_org.show_driving_reimbursement).to be true expect(casa_org.additional_expenses_enabled).to be true + expect(casa_org.contact_topics).to eq(contact_topics) end describe "on logo update" do let(:logo) { upload_file("#{Rails.root}/spec/fixtures/company_logo.png") } subject(:request) do - patch casa_org_url(casa_org), params: params + patch(casa_org_url(casa_org), params:) response end context "with a new logo" do - let(:params) { {casa_org: {logo: logo}} } + let(:params) { {casa_org: {logo:}} } it "uploads the company logo" do expect { request }.to change(ActiveStorage::Attachment, :count).by(1) @@ -67,7 +77,7 @@ let(:params) { {casa_org: {name: "name"}} } it "does not revert logo to default" do - casa_org.update(logo: logo) + casa_org.update(logo:) expect { request }.not_to change(ActiveStorage::Attachment, :count) end @@ -103,13 +113,13 @@ let(:params) { {casa_org: {name: nil}} } subject(:request) do - patch casa_org_url(casa_org), params: params + patch(casa_org_url(casa_org), params:) response end it "does not update the requested casa_org" do - expect { request }.not_to change { casa_org.reload.name } + expect { request }.not_to(change { casa_org.reload.name }) end context "and html format" do @@ -122,7 +132,7 @@ context "and json format" do subject(:request) do - patch casa_org_url(casa_org, format: :json), params: params + patch(casa_org_url(casa_org, format: :json), params:) response end diff --git a/spec/requests/case_contacts/form_spec.rb b/spec/requests/case_contacts/form_spec.rb index c3d0d99065..c334e36b46 100644 --- a/spec/requests/case_contacts/form_spec.rb +++ b/spec/requests/case_contacts/form_spec.rb @@ -4,14 +4,26 @@ let(:organization) { build(:casa_org) } let(:admin) { create(:casa_admin, casa_org: organization) } let(:supervisor) { create(:supervisor, casa_org: organization) } - let!(:volunteer) { create(:volunteer, casa_org: organization, supervisor: supervisor) } + let!(:volunteer) { create(:volunteer, casa_org: organization, supervisor:) } let(:creator) { admin } let!(:casa_case) { create(:casa_case, casa_org: organization) } - before { sign_in admin } + let(:permitted_topics) { %w[title active] } + + let(:contact_topics) do + [ + {"title" => "Abuse", "active" => "true"}, + {"title" => "Neglect", "active" => "true"} + ] + end + + before do + stub_const("ContactTopics::PERMITTED_ATTRIBUTES", permitted_topics) + sign_in admin + end describe "GET /show" do - let!(:case_contact) { create(:case_contact, :details_status, casa_case: casa_case) } + let!(:case_contact) { create(:case_contact, :details_status, casa_case:) } let!(:contact_type_group_b) { create(:contact_type_group, casa_org: organization, name: "B") } let!(:contact_types_b) do [ @@ -37,7 +49,7 @@ it "shows all contact types alphabetically by group" do page = request.parsed_body - expected_contact_types = ["Parent", "Sibling", "Counselor", "Teacher"] + expected_contact_types = %w[Parent Sibling Counselor Teacher] expect(page).to match(/#{expected_contact_types.join(".*")}/m) end @@ -62,17 +74,18 @@ describe "PATCH /update" do let!(:casa_case) { create(:casa_case, casa_org: organization) } + let!(:case_contact) { create(:case_contact, :details_status, casa_case:) } let(:advance_form) { true } let(:params) { {case_contact: attributes} } subject(:request) do - patch "/case_contacts/#{case_contact.id}/form/#{step}", params: params + patch("/case_contacts/#{case_contact.id}/form/#{step}", params:) response end context "submitting details step" do - let!(:case_contact) { create(:case_contact, :started_status, creator: creator) } + let!(:case_contact) { create(:case_contact, :started_status, creator:) } let(:step) { :details } let!(:contact_type_group_b) { create(:contact_type_group, casa_org: organization, name: "B") } let!(:contact_types_b) do @@ -127,7 +140,10 @@ end context "when updating contact types" do - let(:old_contact_type) { create(:case_contact_contact_type, case_contact: case_contact, contact_type: contact_type_group_b.contact_types.first.id) } + let(:old_contact_type) do + create(:case_contact_contact_type, case_contact:, + contact_type: contact_type_group_b.contact_types.first.id) + end it "removes unselected ones" do expect(case_contact.contact_types.count).to eq 1 @@ -163,8 +179,110 @@ end end + context "submitting notes step: contact topics" do + let(:step) { :notes } + + context "with valid contact topic answers" do + let(:attributes) do + {"contact_topics" => contact_topics} + end + + context "when submitting via button" do + it "updates the requested case_contact" do + request + case_contact.reload + expect(case_contact.contact_topics).to eq(contact_topics) + end + end + + context "when autosaving" do + subject(:request) do + patch "/case_contacts/#{case_contact.id}/form/#{step}", params:, as: :json + + response + end + + it "updates the requested case_contact" do + request + case_contact.reload + expect(case_contact.contact_topics).to eq(attributes["contact_topics"]) + end + + it { is_expected.to have_http_status(:success) } + end + end + + context "with invalid contact topic answers" do + let(:attributes) do + {"contact_topics" => + [ + {"title" => "Abuse", "active" => true}, + {"title" => "Neglect", "active" => true} + ]} + end + + context "when submitting via button" do + context "when the contact topics are not permitted" do + let(:permitted_topics) { [] } + + it "does not change topics" do + request + case_contact.reload + expect(case_contact.contact_topics).to eq([]) + end + + it { is_expected.to have_http_status(:redirect) } + end + + context "when the contact topics are partially permitted" do + let(:permitted_topics) { [:title] } + + it "does not change topics" do + request + case_contact.reload + expect(case_contact.contact_topics).to eq([]) + end + end + + it { is_expected.to have_http_status(:redirect) } + end + + context "when autosaving" do + subject(:request) do + patch "/case_contacts/#{case_contact.id}/form/#{step}", params:, as: :json + + response + end + + context "when the contact topics are not permitted" do + let(:permitted_topics) { [] } + + it "does not change topics" do + request + case_contact.reload + expect(case_contact.contact_topics).to eq([]) + end + + it { is_expected.to have_http_status(:success) } + end + + context "when the contact topics are partially permitted" do + let(:permitted_topics) { [:title] } + + it "does not change topics" do + request + case_contact.reload + expect(case_contact.contact_topics).to eq([]) + end + end + + it { is_expected.to have_http_status(:success) } + end + end + end + context "submitting notes step" do - let!(:case_contact) { create(:case_contact, :details_status, creator: creator) } + let!(:case_contact) { create(:case_contact, :details_status, creator:) } let(:step) { :notes } context "with valid attributes" do @@ -192,7 +310,7 @@ context "when autosaving" do subject(:request) do - patch "/case_contacts/#{case_contact.id}/form/#{step}", params: params, as: :json + patch "/case_contacts/#{case_contact.id}/form/#{step}", params:, as: :json response end @@ -215,7 +333,7 @@ end context "submitting expenses step" do - let!(:case_contact) { create(:case_contact, :notes_status, draft_case_ids: [casa_case.id], creator: creator) } + let!(:case_contact) { create(:case_contact, :notes_status, draft_case_ids: [casa_case.id], creator:) } let(:additional_expenses) do { "0" => {other_expense_amount: 50, other_expenses_describe: "meal"}, @@ -230,7 +348,8 @@ want_driving_reimbursement: true, miles_driven: 60, volunteer_address: "123 str", - additional_expenses_attributes: additional_expenses + additional_expenses_attributes: additional_expenses, + contact_topics: } end @@ -245,6 +364,7 @@ expect(case_contact.additional_expenses.first.other_expenses_describe).to eq "meal" expect(case_contact.additional_expenses.last.other_expense_amount).to eq 100 expect(case_contact.additional_expenses.last.other_expenses_describe).to eq "hotel" + expect(case_contact.contact_topics).to eq(contact_topics) end it "sets the case_contact's status to active" do @@ -261,7 +381,7 @@ end context "with only one volunteer for the first case" do - let!(:case_assignment) { create(:case_assignment, casa_case: casa_case, volunteer: volunteer) } + let!(:case_assignment) { create(:case_assignment, casa_case:, volunteer:) } it "updates the volunteer's address" do request @@ -278,9 +398,12 @@ end it "sends reimbursement email" do - expect { + expect do request - }.to change { have_enqueued_job(ActionMailer::MailDeliveryJob).with("SupervisorMailer", "reimbursement_request_email", volunteer, supervisor) } + end.to(change do + have_enqueued_job(ActionMailer::MailDeliveryJob).with("SupervisorMailer", "reimbursement_request_email", volunteer, + supervisor) + end) end end @@ -288,17 +411,25 @@ context "with multiple cases selected" do let!(:other_casa_case) { create(:casa_case, casa_org: organization) } - let!(:case_contact) { create(:case_contact, :notes_status, draft_case_ids: [casa_case.id, other_casa_case.id], creator: admin) } + let!(:case_contact) do + create(:case_contact, :notes_status, draft_case_ids: [casa_case.id, other_casa_case.id], creator: admin) + end it "creates a copy of the draft for each case" do - expect { + expect do request - }.to change(CaseContact, :count).by(1) + end.to change(CaseContact, :count).by(1) expect(CaseContact.last.casa_case_id).to eq other_casa_case.id expect(CaseContact.last.draft_case_ids).to eq [other_casa_case.id] expect(CaseContact.last.status).to eq "active" end + it "sets contact_topics for all cases" do + request + expect(CaseContact.first.contact_topics).to eq(contact_topics) + expect(CaseContact.last.contact_topics).to eq(contact_topics) + end + it "sets the draft_case_ids of the draft to only the first case" do expect(case_contact.draft_case_ids.count).to eq 2 request diff --git a/spec/requests/case_contacts_spec.rb b/spec/requests/case_contacts_spec.rb index e72425a6e0..40bd000d3e 100644 --- a/spec/requests/case_contacts_spec.rb +++ b/spec/requests/case_contacts_spec.rb @@ -9,12 +9,12 @@ describe "GET /index" do let!(:casa_case) { create(:casa_case, casa_org: organization) } - let!(:past_contact) { create(:case_contact, casa_case: casa_case, occurred_at: 3.weeks.ago) } - let!(:recent_contact) { create(:case_contact, casa_case: casa_case, occurred_at: 3.days.ago) } + let!(:past_contact) { create(:case_contact, casa_case:, occurred_at: 3.weeks.ago) } + let!(:recent_contact) { create(:case_contact, casa_case:, occurred_at: 3.days.ago) } let(:filterrific) { {} } subject(:request) do - get case_contacts_path(filterrific: filterrific) + get case_contacts_path(filterrific:) response end @@ -47,9 +47,19 @@ it { is_expected.to have_http_status(:redirect) } it "creates a new case contact" do - expect { + expect do request - }.to change(CaseContact, :count).by(1) + end.to change(CaseContact, :count).by(1) + end + + context "when current org has contact topics" do + let(:contact_topics) { [{"title" => "Abuse", "active" => true}, {"title" => "Neglect", "active" => true}] } + let(:organization) { create(:casa_org, contact_topics:) } + + it "should set contact topics to org topics" do + request + expect(CaseContact.last.contact_topics).to eq(contact_topics) + end end end @@ -65,7 +75,7 @@ it { is_expected.to have_http_status(:redirect) } describe "unread notification" do - let(:followup) { create(:followup, case_contact: case_contact, creator: admin) } + let(:followup) { create(:followup, case_contact:, creator: admin) } subject(:request) do get edit_case_contact_url(case_contact, notification_id: admin.notifications.first.id) @@ -73,7 +83,7 @@ response end - before { FollowupResolvedNotification.with(followup: followup, created_by: admin).deliver(followup.creator) } + before { FollowupResolvedNotification.with(followup:, created_by: admin).deliver(followup.creator) } it "is marked as read" do request diff --git a/spec/values/case_contact_parameters_spec.rb b/spec/values/case_contact_parameters_spec.rb index 9a25c59520..35ea4fec68 100644 --- a/spec/values/case_contact_parameters_spec.rb +++ b/spec/values/case_contact_parameters_spec.rb @@ -2,7 +2,10 @@ RSpec.describe CaseContactParameters do subject { described_class.new(params) } - let(:params) { + let(:contact_topics) do + [{"invalid_value" => "something"}, {"title" => "something"}] + end + let(:params) do ActionController::Parameters.new( case_contact: ActionController::Parameters.new( duration_hours: "1", @@ -13,10 +16,11 @@ miles_driven: "123", want_driving_reimbursement: "want_driving_reimbursement", notes: "notes", - case_contact_contact_type_attributes: [:contact_type_id] + case_contact_contact_type_attributes: [:contact_type_id], + contact_topics: ) ) - } + end it "returns data" do expect(subject["duration_minutes"]).to eq(62) @@ -27,5 +31,9 @@ expect(subject["want_driving_reimbursement"]).to eq("want_driving_reimbursement") expect(subject["notes"]).to eq("notes") expect(subject["case_contact_contact_type_attributes"]).to eq([]) + + contact_topics_results = subject["contact_topics"].map(&:to_h) + valid_contact_topics = [{}, {"title" => "something"}] + expect(contact_topics_results).to eq(valid_contact_topics) end end