diff --git a/app/models/poll/pair_answer.rb b/app/models/poll/pair_answer.rb new file mode 100644 index 000000000..2b314565c --- /dev/null +++ b/app/models/poll/pair_answer.rb @@ -0,0 +1,34 @@ +class Poll::PairAnswer < ApplicationRecord + + belongs_to :question, -> { with_hidden } + belongs_to :author, -> { with_hidden }, class_name: "User", foreign_key: "author_id" + belongs_to :answer_right, class_name: "Poll::Question::Answer", foreign_key: "answer_rigth_id" + belongs_to :answer_left, class_name: "Poll::Question::Answer", foreign_key: "answer_left_id" + + delegate :poll, :poll_id, to: :question + + validates :question, presence: true + validates :author, presence: true + validates :answer_left, presence: true + validates :answer_right, presence: true + + validates :answer_left, inclusion: { in: ->(a) { a.question.question_answers.visibles }}, + unless: ->(a) { a.question.blank? } + + validates :answer_right, inclusion: { in: ->(a) { a.question.question_answers.visibles }}, + unless: ->(a) { a.question.blank? } + + + scope :by_author, ->(author_id) { where(author_id: author_id) } + scope :by_question, ->(question_id) { where(question_id: question_id) } + + def self.generate_pair(question, user) + answers = question.question_answers.visibles.sample(2) + question.pair_answers.by_author(user).map(&:destroy) + question.pair_answers.create(author: user, answer_left: answers[0], answer_right: answers[1]) + end + + def answers + [answer_left, answer_right].compact + end +end diff --git a/app/models/poll/partial_result.rb b/app/models/poll/partial_result.rb index 7c4a6657e..036e80b75 100644 --- a/app/models/poll/partial_result.rb +++ b/app/models/poll/partial_result.rb @@ -11,6 +11,7 @@ class Poll::PartialResult < ApplicationRecord validates :author, presence: true validates :answer, presence: true validates :answer, inclusion: { in: ->(a) { a.question.question_answers + .visibles .joins(:translations) .pluck("poll_question_answer_translations.title") }}, unless: ->(a) { a.question.blank? } diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index bcdfe68a4..41f31c867 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -15,14 +15,22 @@ class Poll::Question < ApplicationRecord has_many :answers, class_name: "Poll::Answer" has_many :question_answers, -> { order "given_order asc" }, class_name: "Poll::Question::Answer", dependent: :destroy has_many :partial_results + has_many :pair_answers, class_name: "Poll::PairAnswer" + has_one :votation_type, as: :questionable belongs_to :proposal + attr_accessor :enum_type, :max_votes, :prioritization_type + validates_translation :title, presence: true, length: { minimum: 4 } validates :author, presence: true validates :poll_id, presence: true, if: Proc.new { |question| question.poll.nil? } + validates_associated :votation_type accepts_nested_attributes_for :question_answers, reject_if: :all_blank, allow_destroy: true + delegate :enum_type, :max_votes, :prioritization_type, :max_groups_answers, + to: :votation_type, allow_nil: true + scope :by_poll_id, ->(poll_id) { where(poll_id: poll_id) } scope :sort_for_list, -> { order("poll_questions.proposal_id IS NULL", :created_at)} @@ -59,10 +67,24 @@ class Poll::Question < ApplicationRecord end def answers_total_votes - question_answers.inject(0) { |total, question_answer| total + question_answer.total_votes } + question_answers.visibles.inject(0) { |total, question_answer| total + question_answer.total_votes } end def most_voted_answer_id question_answers.max_by { |answer| answer.total_votes }.id end + + def answers_with_read_more? + question_answers.visibles.any? do |answer| answer.description.present? || answer.images.any? || + answer.documents.present? || answer.videos.present? + end + end + + def user_can_vote(user) + max_votes.nil? || max_votes > answers.where(author: user).count + end + + def is_positive_negative? + votation_type.present? && enum_type == "positive_negative_open" + end end diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index aab7acb59..c9856575e 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -1,6 +1,7 @@ class Poll::Question::Answer < ApplicationRecord include Galleryable include Documentable + paginates_per 10 translates :title, touch: true translates :description, touch: true @@ -14,6 +15,10 @@ class Poll::Question::Answer < ApplicationRecord validates_translation :title, presence: true validates :given_order, presence: true, uniqueness: { scope: :question_id } + scope :by_author, -> (author) { where(author: author) } + + scope :visibles, -> { where(hidden: false) } + def description self[:description].try :html_safe end @@ -29,11 +34,72 @@ class Poll::Question::Answer < ApplicationRecord end def total_votes - Poll::Answer.where(question_id: question, answer: title).count + - ::Poll::PartialResult.where(question: question).where(answer: title).sum(:amount) + if !question.votation_type.present? + Poll::Answer.where(question_id: question, answer: title).count + + ::Poll::PartialResult.where(question: question).where(answer: title).sum(:amount) + else + case question.votation_type.enum_type + when "positive_negative_open" + total_votes_positive_negative + when "prioritized" + total_votes_prioritized + when "unique" + Poll::Answer.where(question_id: question, answer: title).count + + ::Poll::PartialResult.where(question: question).where(answer: title).sum(:amount) + else + Poll::Answer.where(question_id: question, answer: title).count + end + end + end + + def total_votes_positive_negative + count_positive_negative(self, true) - count_positive_negative(self, false) + end + + def total_votes_prioritized + Poll::Answer.where(question_id: question, answer: title).sum(:value) + end + + def most_voted? + most_voted end def total_votes_percentage question.answers_total_votes.zero? ? 0 : (total_votes * 100.0) / question.answers_total_votes end + + def set_most_voted + if question.enum_type.nil? + for_only_votes + else + case question.enum_type + when "positive_negative_open" + answers = question.question_answers.visibles + .map { |a| count_positive_negative(a, true) - count_positive_negative(a, false) } + is_most_voted = answers.none? {|a| a > total_votes_positive_negative} + update(most_voted: is_most_voted) + when "prioritized" + answers = question.question_answers.visibles + .map { |a| Poll::Answer.where(question_id: a.question, answer: a.title).sum(:value) } + is_most_voted = answers.none? {|a| a > total_votes_prioritized} + update(most_voted: is_most_voted) + else + for_only_votes + end + end + end + + private + + def count_positive_negative(answer, value) + Poll::Answer.where(question_id: answer.question, answer: answer.title, positive: value).count + end + + def for_only_votes + answers = question.question_answers.visibles + .map {|a| Poll::Answer.where(question_id: a.question, answer: a.title).count} + is_most_voted = answers.none? {|a| a > total_votes} + update(most_voted: is_most_voted) + end + end diff --git a/app/models/user.rb b/app/models/user.rb index fd2adecc2..2f787f34d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -35,6 +35,7 @@ class User < ApplicationRecord has_many :follows has_many :budget_rol_assignments has_many :budgets, through: :budget_rol_assignments + has_many :votation_set_answers belongs_to :geozone validates :username, presence: true, if: :username_required? diff --git a/app/models/votation_set_answer.rb b/app/models/votation_set_answer.rb new file mode 100644 index 000000000..bcce7df69 --- /dev/null +++ b/app/models/votation_set_answer.rb @@ -0,0 +1,6 @@ +class VotationSetAnswer < ApplicationRecord + belongs_to :votation_type + belongs_to :author, -> { with_hidden }, class_name: "User", foreign_key: "author_id" + + scope :by_author, -> (author) { where(author: author) } +end diff --git a/app/models/votation_type.rb b/app/models/votation_type.rb new file mode 100644 index 000000000..c84418401 --- /dev/null +++ b/app/models/votation_type.rb @@ -0,0 +1,202 @@ +class VotationType < ApplicationRecord + belongs_to :questionable, polymorphic: true + has_many :votation_set_answers + + QUESTIONABLE_TYPES = %w[Poll::Question].freeze + + ENUM_TYPES_PROPS = { + unique: { enum_type: 0, open_answer: false, prioritized: false }, + multiple: { enum_type: 1, open_answer: false, prioritized: false, + variables: [:max_votes] }, + prioritized: { enum_type: 2, open_answer: false, prioritized: true, + variables: [:max_votes, :prioritization_type] }, + positive_open: { enum_type: 3, open_answer: true, prioritized: false, + variables: [:max_votes] }, + positive_negative_open: { enum_type: 4, open_answer: true, prioritized: false, + variables: [:max_votes] }, + answer_couples_open: { enum_type: 5, open_answer: true, prioritized: false, + variables: [:max_votes, :display_skip_question] }, + answer_couples_closed: { enum_type: 6, open_answer: false, prioritized: false, + variables: [:max_votes, :display_skip_question] }, + answer_set_open: { enum_type: 7, open_answer: true, prioritized: false, + variables: [:max_votes, :max_groups_answers] }, + answer_set_closed: { enum_type: 8, open_answer: false, prioritized: false, + variables: [:max_votes, :max_groups_answers] }, + }.freeze + + enum enum_type: ENUM_TYPES_PROPS.map{ |k,v| [k, v[:enum_type]] }.to_h.freeze + + enum prioritization_type: {borda: 1, dowdall: 2}.freeze + + validates :questionable, presence: true + validates :questionable_type, inclusion: {in: QUESTIONABLE_TYPES} + validates_presence_of :max_votes, allow_blank: false, + if: :max_votes_required? + validates_presence_of :max_groups_answers, allow_blank: false, + if: :max_groups_answers_required? + validates_presence_of :prioritization_type, allow_blank: false, + if: :prioritization_type_required? + + after_create :add_skip_question_answer, if: :display_skip_question? + + attr_accessor :display_skip_question + + def open? + open_answer + end + + def prioritized? + prioritized + end + + def answer (user, answer, options = {}) + result = nil + votes = questionable.answers + + if votable_question?(answer) + case enum_type + when "unique" + result = votes.find_or_initialize_by(author: user) + + when "multiple", "positive_open" + if check_max_votes(user, votes) + result = votes.find_or_initialize_by(author: user, answer: answer) + end + + when "prioritized" + result = votes.find_by(author: user, answer: answer) + if result.nil? + if check_max_votes(user, votes) + if votes.by_author(user.id).empty? + order = 1 + else + order = votes.by_author(user.id).order(:order).last.order + 1 + end + result = votes.find_or_initialize_by(author: user, + answer: answer, + order: order) + end + else + !result.update(order: options[:order]) + end + + when "positive_negative_open" + result = votes.by_author(user.id).find_by(answer: answer) + if result.nil? + if check_max_votes(user, votes) + result = votes.create(author: user, + answer: answer, + positive: options[:positive]) + end + else + !result.update(positive: options[:positive]) + end + + when "answer_couples_closed", "answer_couples_open" + if check_max_votes(user, votes) + result = votes.create( + answer: answer, + author: user, + positive: true, + order: votes&.by_author(user.id).count + 1 + ) + end + Poll::PairAnswer.generate_pair(questionable, user) + + when "answer_set_open", "answer_set_closed" + if check_max_votes(user, votes) && answer_in_set?(answer, user) + result = votes&.find_or_initialize_by(author: user, answer: answer) + end + end + end + + + result + end + + def create_question_answer(answer, hidden=false) + return if questionable.question_answers.where(title: answer).any? + + questionable.question_answers + .create( + title: answer, + given_order: questionable.question_answers.maximum(:given_order).to_i + 1, + hidden: hidden + ) + true + end + + def votable_question?(answer) + questionable.question_answers.where(title: answer).present? + end + + def self.build_by_type(questionable, params) + attributes = {questionable: questionable} + enum_type = self.enum_types.key(params[:enum_type].to_i) + enum_type_props = enum_properties(enum_type) + attributes.merge!(enum_type_props.except(:variables)) + enum_type_props[:variables]&.each do |property| + attributes[property] = params[property] + end + attributes[:prioritization_type] = attributes[:prioritization_type]&.to_i + new(attributes) + end + + def self.create_by_type(questionable, params) + votation_type = build_by_type(questionable, params) + votation_type.save + end + + def update_priorized_values(user) + case prioritization_type + when "borda" + questionable.answers.by_author(user).order(:order).each_with_index do |answer, i| + value = max_votes - i + !answer.update(value: value) + end + when "dowdall" + questionable.answers.by_author(user).order(:order).each_with_index do |answer, i| + value = 60/(i + 1) + !answer.update(value: value) + end + end + end + + private + + def answer_in_set?(answer, user) + votation_set_answers&.by_author(user)&.pluck(:answer).include?(answer) + end + + def check_max_votes(user, votes) + max_votes > votes&.by_author(user.id).count + end + + def self.enum_properties(enum_type) + ENUM_TYPES_PROPS[enum_type&.to_sym] || ENUM_TYPES_PROPS[:unique] + end + + def self.enum_properties_variables(enum_type) + enum_properties(enum_type)&.dig(:variables) + end + + def max_votes_required? + VotationType.enum_properties_variables(self.enum_type)&.include?(:max_votes) + end + + def max_groups_answers_required? + VotationType.enum_properties_variables(self.enum_type)&.include?(:max_groups_answers) + end + + def prioritization_type_required? + VotationType.enum_properties_variables(self.enum_type)&.include?(:prioritization_type) + end + + def display_skip_question? + VotationType.enum_properties_variables(self.enum_type)&.include?(:display_skip_question) + end + + def add_skip_question_answer + create_question_answer("I can't decided", true) + end +end diff --git a/db/migrate/20190513112613_create_votation_types.rb b/db/migrate/20190513112613_create_votation_types.rb new file mode 100644 index 000000000..4ccfa8b55 --- /dev/null +++ b/db/migrate/20190513112613_create_votation_types.rb @@ -0,0 +1,17 @@ +class CreateVotationTypes < ActiveRecord::Migration[5.0] + def change + create_table :votation_types do |t| + t.integer :questionable_id + t.string :questionable_type + t.integer :enum_type + t.boolean :open_answer + t.boolean :prioritized + t.integer :prioritization_type + t.integer :max_votes + t.integer :max_groups_answers + + t.timestamps + end + end +end + diff --git a/db/migrate/20190514153436_add_order_and_positive_in_poll_answers.rb b/db/migrate/20190514153436_add_order_and_positive_in_poll_answers.rb new file mode 100644 index 000000000..10d34e7e9 --- /dev/null +++ b/db/migrate/20190514153436_add_order_and_positive_in_poll_answers.rb @@ -0,0 +1,7 @@ +class AddOrderAndPositiveInPollAnswers < ActiveRecord::Migration[5.0] + def change + add_column :poll_answers, :positive, :boolean + add_column :poll_answers, :order, :integer + end +end + diff --git a/db/migrate/20190522075912_create_poll_pair_answers.rb b/db/migrate/20190522075912_create_poll_pair_answers.rb new file mode 100644 index 000000000..d230c7f5b --- /dev/null +++ b/db/migrate/20190522075912_create_poll_pair_answers.rb @@ -0,0 +1,13 @@ +class CreatePollPairAnswers < ActiveRecord::Migration[5.0] + def change + create_table :poll_pair_answers do |t| + t.references :question, index: true + t.references :author, index: true + t.references :answer_rigth + t.references :answer_left + + t.timestamps + end + end +end + diff --git a/db/migrate/20190523113821_add_hidden_field_to_poll_question_answers.rb b/db/migrate/20190523113821_add_hidden_field_to_poll_question_answers.rb new file mode 100644 index 000000000..9c91fd4bc --- /dev/null +++ b/db/migrate/20190523113821_add_hidden_field_to_poll_question_answers.rb @@ -0,0 +1,6 @@ +class AddHiddenFieldToPollQuestionAnswers < ActiveRecord::Migration[5.0] + def change + add_column :poll_question_answers, :hidden, :boolean, default: false + end +end + diff --git a/db/migrate/20190524121957_add_value_to_poll_answers.rb b/db/migrate/20190524121957_add_value_to_poll_answers.rb new file mode 100644 index 000000000..22a6dcb9b --- /dev/null +++ b/db/migrate/20190524121957_add_value_to_poll_answers.rb @@ -0,0 +1,6 @@ +class AddValueToPollAnswers < ActiveRecord::Migration[5.0] + def change + add_column :poll_answers, :value, :integer + end +end + diff --git a/db/migrate/20190528134530_create_votation_set_answers.rb b/db/migrate/20190528134530_create_votation_set_answers.rb new file mode 100644 index 000000000..92367e63e --- /dev/null +++ b/db/migrate/20190528134530_create_votation_set_answers.rb @@ -0,0 +1,12 @@ +class CreateVotationSetAnswers < ActiveRecord::Migration[5.0] + def change + create_table :votation_set_answers do |t| + t.integer :author_id, index: true + t.references :votation_type, foreign_key: true, index: true + t.string :answer + + t.timestamps + end + end +end + diff --git a/db/schema.rb b/db/schema.rb index ae60009b0..313147f38 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1029,6 +1029,9 @@ ActiveRecord::Schema.define(version: 20190607160900) do t.string "answer" t.datetime "created_at" t.datetime "updated_at" + t.boolean "positive" + t.integer "order" + t.integer "value" t.index ["author_id"], name: "index_poll_answers_on_author_id", using: :btree t.index ["question_id", "answer"], name: "index_poll_answers_on_question_id_and_answer", using: :btree t.index ["question_id"], name: "index_poll_answers_on_question_id", using: :btree @@ -1084,6 +1087,19 @@ ActiveRecord::Schema.define(version: 20190607160900) do t.index ["user_id"], name: "index_poll_officers_on_user_id", using: :btree end + create_table "poll_pair_answers", force: :cascade do |t| + t.integer "question_id" + t.integer "author_id" + t.integer "answer_rigth_id" + t.integer "answer_left_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["answer_left_id"], name: "index_poll_pair_answers_on_answer_left_id", using: :btree + t.index ["answer_rigth_id"], name: "index_poll_pair_answers_on_answer_rigth_id", using: :btree + t.index ["author_id"], name: "index_poll_pair_answers_on_author_id", using: :btree + t.index ["question_id"], name: "index_poll_pair_answers_on_question_id", using: :btree + end + create_table "poll_partial_results", force: :cascade do |t| t.integer "question_id" t.integer "author_id" @@ -1127,6 +1143,7 @@ ActiveRecord::Schema.define(version: 20190607160900) do t.integer "question_id" t.integer "given_order", default: 1 t.boolean "most_voted", default: false + t.boolean "hidden", default: false t.index ["question_id"], name: "index_poll_question_answers_on_question_id", using: :btree end @@ -1613,6 +1630,29 @@ ActiveRecord::Schema.define(version: 20190607160900) do t.index ["user_id"], name: "index_visits_on_user_id", using: :btree end + create_table "votation_set_answers", force: :cascade do |t| + t.integer "author_id" + t.integer "votation_type_id" + t.string "answer" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["author_id"], name: "index_votation_set_answers_on_author_id", using: :btree + t.index ["votation_type_id"], name: "index_votation_set_answers_on_votation_type_id", using: :btree + end + + create_table "votation_types", force: :cascade do |t| + t.integer "questionable_id" + t.string "questionable_type" + t.integer "enum_type" + t.boolean "open_answer" + t.boolean "prioritized" + t.integer "prioritization_type" + t.integer "max_votes" + t.integer "max_groups_answers" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "votes", force: :cascade do |t| t.string "votable_type" t.integer "votable_id" @@ -1721,4 +1761,5 @@ ActiveRecord::Schema.define(version: 20190607160900) do add_foreign_key "trackers", "users" add_foreign_key "users", "geozones" add_foreign_key "valuators", "users" + add_foreign_key "votation_set_answers", "votation_types" end diff --git a/spec/factories/polls.rb b/spec/factories/polls.rb index 7b49d3f5a..e9b525d00 100644 --- a/spec/factories/polls.rb +++ b/spec/factories/polls.rb @@ -38,6 +38,69 @@ FactoryBot.define do create(:poll_question_answer, question: question, title: "No") end end + + factory :poll_question_unique do + after(:create) do |question| + create(:votation_type_unique, questionable: question) + question.reload + end + end + + factory :poll_question_multiple do + after(:create) do |question| + create(:votation_type_multiple, questionable: question) + question.reload + end + end + + factory :poll_question_prioritized do + after(:create) do |question| + create(:votation_type_prioritized, questionable: question) + question.reload + end + end + + factory :poll_question_positive_open do + after(:create) do |question| + create(:votation_type_positive_open, questionable: question) + question.reload + end + end + + factory :poll_question_positive_negative_open do + after(:create) do |question| + create(:votation_type_positive_negative_open, questionable: question) + question.reload + end + end + + factory :poll_question_answer_couples_open do + after(:create) do |question| + create(:votation_type_answer_couples_open, questionable: question) + question.reload + end + end + + factory :poll_question_answer_couples_closed do + after(:create) do |question| + create(:votation_type_answer_couples_closed, questionable: question) + question.reload + end + end + + factory :poll_question_answer_set_open do + after(:create) do |question| + create(:votation_type_answer_set_open, questionable: question) + question.reload + end + end + + factory :poll_question_answer_set_closed do + after(:create) do |question| + create(:votation_type_answer_set_closed, questionable: question) + question.reload + end + end end factory :poll_question_answer, class: "Poll::Question::Answer" do @@ -177,4 +240,11 @@ FactoryBot.define do factory :active_poll do end + + factory :poll_pair_answer, class: "Poll::PairAnswer" do + question { create :poll_question } + author { create :user } + answer_left { create(:poll_question_answer, question: question) } + answer_right { create(:poll_question_answer, question: question) } + end end diff --git a/spec/factories/votation_type.rb b/spec/factories/votation_type.rb new file mode 100644 index 000000000..60a851e29 --- /dev/null +++ b/spec/factories/votation_type.rb @@ -0,0 +1,76 @@ +FactoryBot.define do + factory :votation_type do + factory :votation_type_unique do + enum_type "unique" + open_answer false + prioritized false + end + + factory :votation_type_multiple do + enum_type "multiple" + open_answer false + prioritized false + max_votes 5 + end + + factory :votation_type_prioritized do + enum_type "prioritized" + open_answer false + prioritized true + max_votes 5 + prioritization_type "borda" + end + + factory :votation_type_positive_open do + enum_type "positive_open" + open_answer true + prioritized false + max_votes 5 + end + + factory :votation_type_positive_negative_open do + enum_type "answer_couples_open" + open_answer true + prioritized false + max_votes 5 + end + + factory :votation_type_answer_couples_open do + enum_type "answer_couples_open" + open_answer true + prioritized false + max_votes 5 + end + + factory :votation_type_answer_couples_closed do + enum_type "answer_couples_open" + open_answer false + prioritized false + max_votes 5 + end + + factory :votation_type_answer_set_open do + enum_type "answer_set_open" + open_answer true + prioritized false + max_votes 5 + max_groups_answers 3 + end + + factory :votation_type_answer_set_closed do + enum_type "answer_set_open" + open_answer false + prioritized false + max_votes 5 + max_groups_answers 3 + end + + trait :open do + open_answer true + end + trait :prioritized do + prioritized true + end + + end +end diff --git a/spec/models/poll/pair_answer_spec.rb b/spec/models/poll/pair_answer_spec.rb new file mode 100644 index 000000000..8f7d5efde --- /dev/null +++ b/spec/models/poll/pair_answer_spec.rb @@ -0,0 +1,131 @@ +require "rails_helper" + +describe Poll::PairAnswer do + + describe "validations" do + + let(:pair_answer) { build(:poll_pair_answer) } + + it "is valid" do + expect(pair_answer).to be_valid + end + + it "is not valid wihout a question" do + pair_answer.question = nil + + expect(pair_answer).not_to be_valid + end + + it "is not valid without an author" do + pair_answer.author = nil + + expect(pair_answer).not_to be_valid + end + + it "is valid if answer_left is included in the Poll::Question's question_answers list" do + question = create(:poll_question) + answer1 = create(:poll_question_answer, title: "One", question: question) + answer2 = create(:poll_question_answer, title: "Two", question: question) + answer3 = create(:poll_question_answer, title: "Three") + + expect(build(:poll_pair_answer, question: question, answer_left: answer1)).to be_valid + expect(build(:poll_pair_answer, question: question, answer_left: answer2)).to be_valid + expect(build(:poll_pair_answer, question: question, answer_left: answer3)).not_to be_valid + end + + it "is valid if answer_right is included in the Poll::Question's question_answers list" do + question = create(:poll_question) + answer1 = create(:poll_question_answer, title: "One", question: question) + answer2 = create(:poll_question_answer, title: "Two", question: question) + answer3 = create(:poll_question_answer, title: "Three") + + expect(build(:poll_pair_answer, question: question, answer_right: answer1)).to be_valid + expect(build(:poll_pair_answer, question: question, answer_right: answer2)).to be_valid + expect(build(:poll_pair_answer, question: question, answer_right: answer3)).not_to be_valid + end + end + + context "scopes" do + let(:pair_answer_1) { create(:poll_pair_answer) } + let(:pair_answer_2) { create(:poll_pair_answer) } + + describe "#by_author" do + + it "returns pair_answers associated to an user" do + author = pair_answer_1.author + + expect(described_class.by_author(author)).to include(pair_answer_1) + expect(described_class.by_author(author)).not_to include(pair_answer_2) + end + + end + + describe "#by_question" do + + it "returns pair_answers associated to a question" do + question = pair_answer_1.question + + expect(described_class.by_question(question)).to include(pair_answer_1) + expect(described_class.by_question(question)).not_to include(pair_answer_2) + end + end + + end + + + describe "#generate_pair" do + let(:user) { create(:user) } + let(:question) { create(:poll_question) } + + context "without question_answers" do + + it "assigns nil value to pair_answers" do + pair_answer = described_class.generate_pair(question, user) + + expect(pair_answer).to be_a Poll::PairAnswer + expect(pair_answer.question).to eq(question) + expect(pair_answer.author).to eq(user) + expect(pair_answer.answer_left).to be_nil + expect(pair_answer.answer_right).to be_nil + end + end + + context "With question answers" do + let!(:answer1) { create(:poll_question_answer, question: question) } + + it "assigns only right question if only has one question_answer" do + pair_answer = described_class.generate_pair(question, user) + + expect(pair_answer).to be_a Poll::PairAnswer + expect(pair_answer.question).to eq(question) + expect(pair_answer.author).to eq(user) + expect(pair_answer.answer_left).to eq(answer1) + expect(pair_answer.answer_right).to be_nil + end + + it "assigns random values if question has some question_answer" do + create(:poll_question_answer, question: question) + + pair_answer = described_class.generate_pair(question, user) + + expect(pair_answer).to be_a Poll::PairAnswer + expect(pair_answer.question).to eq(question) + expect(pair_answer.author).to eq(user) + expect(pair_answer.answer_left).to be_a Poll::Question::Answer + expect(pair_answer.answer_right).to be_a Poll::Question::Answer + expect(pair_answer.answer_left).not_to eq(pair_answer.answer_right) + end + end + end + + describe "#answers" do + let(:pair_answer) { create(:poll_pair_answer) } + + it "returns and array of answers" do + expect(pair_answer.answers).to be_a Array + expect(pair_answer.answers[0]).to eq(pair_answer.answer_left) + expect(pair_answer.answers[1]).to eq(pair_answer.answer_right) + end + end + +end diff --git a/spec/models/poll/question_spec.rb b/spec/models/poll/question_spec.rb index 87a3698f1..7adee447d 100644 --- a/spec/models/poll/question_spec.rb +++ b/spec/models/poll/question_spec.rb @@ -44,4 +44,35 @@ RSpec.describe Poll::Question, type: :model do end end + describe "#enum_type" do + + it "returns nil if not has votation_type association" do + expect(poll_question.votation_type).to be_nil + expect(poll_question.enum_type).to be_nil + end + + it "returns enum_type from votation_type association" do + question = create(:poll_question_answer_couples_open) + + expect(question.votation_type).not_to be_nil + expect(question.enum_type).to eq("answer_couples_open") + end + + end + + describe "#max_votes" do + + it "returns nil if not has votation_type association" do + expect(poll_question.votation_type).to be_nil + expect(poll_question.max_votes).to be_nil + end + + it "returns max_votes from votation_type association" do + question = create(:poll_question_answer_couples_open) + + expect(question.votation_type).not_to be_nil + expect(question.max_votes).to eq(5) + end + + end end