From a04df00bdcd2a0e03ae0f2090566f89fd533bbe7 Mon Sep 17 00:00:00 2001 From: Stella Rouzi Date: Wed, 16 Sep 2015 12:20:52 +0300 Subject: [PATCH] Adds model and controller tests for Question. --- app/controllers/admin/questions_controller.rb | 28 ++- .../admin/questions_controller_spec.rb | 238 +++++++++++++++++- spec/factories/answer.rb | 4 +- spec/factories/question.rb | 4 +- spec/models/answer_spec.rb | 39 +++ spec/models/question_spec.rb | 22 ++ 6 files changed, 311 insertions(+), 24 deletions(-) create mode 100644 spec/models/answer_spec.rb create mode 100644 spec/models/question_spec.rb diff --git a/app/controllers/admin/questions_controller.rb b/app/controllers/admin/questions_controller.rb index 0211aca8..6aec757c 100644 --- a/app/controllers/admin/questions_controller.rb +++ b/app/controllers/admin/questions_controller.rb @@ -34,10 +34,10 @@ module Admin respond_to do |format| # Do not automatically associate newly created question with the conference. The new question shall be enabled for the conference manually. if @question.save - format.html { redirect_to admin_conference_questions_path, notice: 'Question was successfully created.' } + format.html { redirect_to admin_conference_questions_path(@conference.short_title), notice: 'Question was successfully created.' } else flash[:error] = "Oops, couldn't save Question. #{@question.errors.full_messages.join('. ')}" - format.html { redirect_to admin_conference_questions_path } + format.html { redirect_to admin_conference_questions_path(@conference.short_title) } end end end @@ -59,10 +59,11 @@ module Admin @conference.questions.delete(@question) end - redirect_to(admin_conference_questions_path(conference_id: @conference.short_title), notice: "Question '#{@question.title}' for #{@conference.short_title} updated successfully.") + flash[:notice] = "Question '#{@question.title}' for #{@conference.short_title} updated successfully." + redirect_to admin_conference_questions_path(@conference.short_title) else flash[:error] = "Update of questions for #{@conference.short_title} failed. #{@question.errors.full_messages.join('. ')}" - redirect_to admin_conference_questions_path(conference_id: @conference.short_title) + redirect_to admin_conference_questions_path(@conference.short_title) end end @@ -77,16 +78,19 @@ module Admin ids.delete(@question.id) end - if @conference.update_attributes(question_ids: ids) - flash[:notice] = "Questions for #{@conference.short_title} successfully updated. Note: Only questions with answers can be enabled for a conference." + if @conference.update(question_ids: ids) + flash[:notice] = "Question '#{@question.title}' #{params[:enable]=='true' ? 'enabled' : 'disabled'} for #{@conference.title}." else - flash[:error] = "Update of questions for #{@conference.short_title} failed." + flash[:error] = "Failed to #{params[:enable]=='true' ? 'enable' : 'disable'} question '#{@question.title}' for #{@conference.title}. Note: Only questions with answers can be enabled for a conference." end - if request.xhr? - render js: 'index' - else - redirect_to admin_conference_questions_path(conference_id: @conference.short_title) + respond_to do |format| + format.html do + redirect_to admin_conference_questions_path(@conference.short_title) + end + format.js do + render js: 'index' + end end end @@ -114,7 +118,7 @@ module Admin flash[:error] = 'You cannot delete global questions that are currently being used for a conference.' end else - flash[:error] = 'You must be an admin to delete a question.' + flash[:error] = 'You cannot delete the question. Do you have the necessary permissions?' end @questions = Question.where(global: true).all | Question.where(conference_id: @conference.id) | @conference.questions diff --git a/spec/controllers/admin/questions_controller_spec.rb b/spec/controllers/admin/questions_controller_spec.rb index 9c168721..92be5984 100644 --- a/spec/controllers/admin/questions_controller_spec.rb +++ b/spec/controllers/admin/questions_controller_spec.rb @@ -1,16 +1,21 @@ require 'spec_helper' describe Admin::QuestionsController do - let!(:conference) { create(:conference) } - let!(:question_with_answers) { create(:question_with_answers) } - let!(:question_without_answers) { create(:question) } + let!(:conference) { create(:conference, title: 'my conference') } + let!(:question_with_answers) { create(:question_with_answers, conference_id: conference.id) } + let!(:question_without_answers) { create(:question, conference_id: conference.id) } + let(:question_type_yes_no) { QuestionType.find_by(title: 'Yes/No') } + let(:question_type_single_choice) { create(:single_choice) } + let(:first_answer) { create(:first_answer) } + let(:second_answer) { create(:second_answer) } let(:organizer) { create(:organizer) } + let(:question) { create(:question, title: 'test title', question_type_id: create(:single_choice).id) } - describe 'PATCH #update_conference' do - before(:each) do - sign_in(organizer) - end + before(:each) do + sign_in(organizer) + end + describe 'PATCH #toggle_question' do it 'enables a question for a conference if the question has answers' do patch :toggle_question, conference_id: conference.short_title, id: question_with_answers, enable: 'true' @@ -19,8 +24,225 @@ describe Admin::QuestionsController do expect(conference.question_ids).to eq([question_with_answers.id]) expect(conference.question_ids).to_not include([question_without_answers.id]) - expect(flash[:notice]).to eq("Questions for #{conference.short_title} successfully updated. Note: Only questions with answers can be enabled for a conference.") + expect(flash[:notice]).to eq("Question 'Which do you choose?' enabled for my conference.") + expect(response).to redirect_to admin_conference_questions_path(conference.short_title) + end + + it 'disables a question for a conference if the question has answers' do + patch :toggle_question, conference_id: conference.short_title, id: question_with_answers, enable: 'true' + patch :toggle_question, conference_id: conference.short_title, id: question_with_answers, enable: 'false' + + conference.reload + + expect(conference.question_ids).to eq([]) + expect(conference.question_ids).to_not include([question_with_answers.id]) + + expect(flash[:notice]).to eq("Question 'Which do you choose?' disabled for my conference.") expect(response).to redirect_to admin_conference_questions_path(conference.short_title) end end + + describe 'GET #index' do + it 'renders the index template' do + + get :index, conference_id: conference.short_title + expect(response).to render_template :index + end + + it 'properly assigns questions, when conference does not have enabled questions' do + questions_global = Question.where(global: true) + + get :index, conference_id: conference.short_title + + questions_array = questions_global.to_a + questions_array << question_with_answers + questions_array << question_without_answers + expect(assigns(:questions)).to match_array(questions_array) + end + end + + describe 'GET #show' do + before :each do + @registration = create(:registration, conference: conference) + conference.questions << question_with_answers + @registration.qanswers << question_with_answers.qanswers.first + end + it 'properly assigns registrations' do + get :show, conference_id: conference.short_title, id: question_with_answers.id + expect(assigns(:registrations).to_a).to eq [@registration] + end + end + + describe 'POST #create' do + context 'creates a question' do + before :each do + @expected = Question.count + 1 + post :create, conference_id: conference.short_title, question: attributes_for(:question, title: 'test', + question_type_id: question_type_single_choice.id) + end + + it 'successfully' do + expect(Question.count).to eq @expected + end + + it 'assigns question object with correct attributes' do + expect(assigns(:question).title).to eq 'test' + expect(assigns(:question).conference_id).to eq conference.id + end + + it 'renders flash with success message' do + expect(flash[:notice]).to eq 'Question was successfully created.' + end + + it 'redirects to index' do + expect(response).to redirect_to admin_conference_questions_path(conference.short_title) + end + end + + context 'creates a question with Yes/No question type' do + before :each do + @expected = Question.count + 1 + post :create, conference_id: conference.short_title, question: attributes_for(:question, title: 'test', + question_type_id: QuestionType.find_by(title: 'Yes/No').id) + end + + it 'successfully' do + expect(Question.count).to eq @expected + end + + it 'assigns question object with correct attributes' do + expect(assigns(:question).title).to eq 'test' + expect(assigns(:question).conference_id).to eq conference.id + end + + it 'renders flash with success message' do + expect(flash[:notice]).to eq 'Question was successfully created.' + end + + it 'redirects to index' do + expect(response).to redirect_to admin_conference_questions_path(conference.short_title) + end + + it 'assigns answers automatically' do + expect(assigns(:question).answers.to_a).to eq [ Answer.find_by(title: 'Yes'), Answer.find_by(title: 'No') ] + end + end + + context 'with invalid attributes' do + before :each do + @expected = Question.count + post :create, conference_id: conference.short_title, question: attributes_for(:question, title: '', question_type_id: question_type_single_choice.id) + end + + it 'does not save the new question in database' do + expect(Question.count).to eq @expected + end + + it 'redirects to index page' do + expect(response).to redirect_to admin_conference_questions_path(conference.short_title) + end + + it 'shows flash error message' do + expect(flash[:error]).to eq "Oops, couldn't save Question. Title can't be blank" + end + end + end + + describe 'PATCH #update' do + before :each do + question.answers << first_answer + question.answers << second_answer + question.conferences << conference + end + + context 'with invalid attributes' do + before :each do + patch :update, conference_id: conference.short_title, id: question.id, question: attributes_for(:question, title: '') + end + + it 'does not save the question' do + expect(question.title).to eq 'test title' + end + + it 'renders failure flash error message' do + expect(flash[:error]).to eq "Update of questions for #{conference.short_title} failed. Title can't be blank" + end + + it 'redirects to index' do + expect(response).to redirect_to admin_conference_questions_path(conference.short_title) + end + end + + context 'with valid attibutes' do + before :each do + patch :update, conference_id: conference.short_title, id: question.id, question: attributes_for(:question, title: 'new title') + end + + it 'successfully' do + question.reload + expect(question.title).to eq 'new title' + end + + it 'renders success flash notice message' do + question.reload + expect(flash[:notice]).to eq "Question 'new title' for #{conference.short_title} updated successfully." + end + + it 'redirects to index' do + expect(response).to redirect_to admin_conference_questions_path(conference.short_title) + end + end + + it 'disables question for conference, if question does not have answers' do + question.answers = [] + patch :update, conference_id: conference.short_title, id: question.id, question: attributes_for(:question) + + question.reload + expect(question.conferences).to eq [] + end + end + + describe 'DELETE #destroy' do + context 'global questions' do + it 'deletes question not used in any conference' do + global_question = Question.find_by(global: true) + + expected = Question.count - 1 + delete :destroy, conference_id: conference.short_title, id: global_question.id + + expect(Question.count).to eq expected + end + + it 'does not delete question used in a conference' do + global_question = Question.find_by(global: true) + conference.questions << global_question + + expected = Question.count + delete :destroy, conference_id: conference.short_title, id: global_question.id + + expect(Question.count).to eq expected + end + end + + context 'not global questions' do + it 'deletes a question not used in the conference' do + expected = Question.count - 1 + expect(conference.questions).not_to include question_with_answers + + delete :destroy, conference_id: conference.short_title, id: question_with_answers.id + + expect(Question.count).to eq expected + end + + it 'deletes question used in a conference' do + conference.questions << question_with_answers + expect(question_with_answers.conferences).to include conference + + expected = Question.count - 1 + delete :destroy, conference_id: conference.short_title, id: question_with_answers.id + + expect(Question.count).to eq expected + end + end + end end diff --git a/spec/factories/answer.rb b/spec/factories/answer.rb index b2a6ad97..aadb98f7 100644 --- a/spec/factories/answer.rb +++ b/spec/factories/answer.rb @@ -4,11 +4,11 @@ FactoryGirl.define do factory :answer do title 'I do' - factory :answer1 do + factory :first_answer do title 'First Answer' end - factory :answer2 do + factory :second_answer do title 'Second Answer' end diff --git a/spec/factories/question.rb b/spec/factories/question.rb index fe017949..9fdacc92 100644 --- a/spec/factories/question.rb +++ b/spec/factories/question.rb @@ -9,8 +9,8 @@ FactoryGirl.define do title 'Which do you choose?' after(:build) do |question| - question.answers << build(:answer1) - question.answers << build(:answer2) + question.answers << build(:first_answer) + question.answers << build(:second_answer) end end diff --git a/spec/models/answer_spec.rb b/spec/models/answer_spec.rb new file mode 100644 index 00000000..e7ad5653 --- /dev/null +++ b/spec/models/answer_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Answer do + let(:conference) { create(:conference) } + let(:question) { create(:question) } + let(:second_answer) { create(:second_answer) } + let(:registration) { create(:registration, conference: conference) } + + describe 'validations' do + + it 'has a valid factory' do + expect(build(:answer)).to be_valid + end + + it 'is not valid without a title' do + should validate_presence_of(:title) + expect(build(:answer, title: nil)).not_to be_valid + end + + it 'cannot be modified if the question is being used' do + question.answers << second_answer + second_answer.title = 'new title' + + expect(second_answer.valid?).to be false + end + + end + + describe '#sum_replies' do + before :each do + conference.questions << question + registration.qanswers << create(:qanswer, question: question, answer: second_answer) + end + + it 'returns no of replies for the answer, given a question and a conference' do + expect(second_answer.sum_replies(question, conference)).to eq 1 + end + end +end diff --git a/spec/models/question_spec.rb b/spec/models/question_spec.rb new file mode 100644 index 00000000..e61efe22 --- /dev/null +++ b/spec/models/question_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Question do + + describe 'validations' do + + it 'has a valid factory' do + expect(build(:question)).to be_valid + end + + it 'is not valid without a title' do + should validate_presence_of(:title) + expect(build(:question, title: nil)).not_to be_valid + end + + it 'is not valid without a question type' do + should validate_presence_of(:question_type_id) + expect(build(:question, question_type_id: nil)).not_to be_valid + end + + end +end