From 2e6a0c4849925579ecab54097876014f25a9d658 Mon Sep 17 00:00:00 2001 From: ksilex Date: Tue, 2 Mar 2021 22:45:32 +0300 Subject: [PATCH 1/4] questions controller, tests --- Gemfile | 3 + Gemfile.lock | 26 ++++ app/assets/stylesheets/questions.scss | 3 + app/controllers/questions_controller.rb | 49 ++++++++ app/helpers/questions_helper.rb | 2 + app/views/questions/edit.html.haml | 0 app/views/questions/index.html.haml | 0 app/views/questions/new.html.haml | 0 app/views/questions/show.html.haml | 0 config/application.rb | 9 ++ config/routes.rb | 4 +- spec/controllers/questions_controller_spec.rb | 114 ++++++++++++++++++ spec/factories/questions.rb | 4 + spec/rails_helper.rb | 1 + 14 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/questions.scss create mode 100644 app/controllers/questions_controller.rb create mode 100644 app/helpers/questions_helper.rb create mode 100644 app/views/questions/edit.html.haml create mode 100644 app/views/questions/index.html.haml create mode 100644 app/views/questions/new.html.haml create mode 100644 app/views/questions/show.html.haml create mode 100644 spec/controllers/questions_controller_spec.rb diff --git a/Gemfile b/Gemfile index 017ff14..db05a5b 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,8 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '2.7.1' +gem 'haml' +gem "haml-rails", "~> 2.0" # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main' gem 'rails', '~> 6.1.3' # Use postgresql as the database for Active Record @@ -33,6 +35,7 @@ group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails', '~> 4.0.2' gem 'factory_bot_rails' + gem 'rails-controller-testing' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 8c0665f..64db31b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -80,6 +80,7 @@ GEM crass (1.0.6) diff-lcs (1.4.4) erubi (1.10.0) + erubis (2.7.0) factory_bot (6.1.0) activesupport (>= 5.0.0) factory_bot_rails (6.1.0) @@ -88,6 +89,20 @@ GEM ffi (1.14.2) globalid (0.4.2) activesupport (>= 4.2.0) + haml (5.2.1) + temple (>= 0.8.0) + tilt + haml-rails (2.0.1) + actionpack (>= 5.1) + activesupport (>= 5.1) + haml (>= 4.0.6, < 6.0) + html2haml (>= 1.0.1) + railties (>= 5.1) + html2haml (2.2.0) + erubis (~> 2.7.0) + haml (>= 4.0, < 6) + nokogiri (>= 1.6.0) + ruby_parser (~> 3.5) i18n (1.8.9) concurrent-ruby (~> 1.0) jbuilder (2.11.2) @@ -139,6 +154,10 @@ GEM bundler (>= 1.15.0) railties (= 6.1.3) sprockets-rails (>= 2.0.0) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) @@ -172,6 +191,8 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.10.2) + ruby_parser (3.15.1) + sexp_processor (~> 4.9) rubyzip (2.3.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) @@ -187,6 +208,7 @@ GEM childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) semantic_range (2.3.1) + sexp_processor (4.15.2) shoulda-matchers (4.5.1) activesupport (>= 4.2.0) spring (2.1.1) @@ -197,6 +219,7 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + temple (0.8.2) thor (1.1.0) tilt (2.0.10) turbolinks (5.2.1) @@ -233,12 +256,15 @@ DEPENDENCIES byebug capybara (>= 3.26) factory_bot_rails + haml + haml-rails (~> 2.0) jbuilder (~> 2.7) listen (~> 3.3) pg (~> 1.1) puma (~> 5.0) rack-mini-profiler (~> 2.0) rails (~> 6.1.3) + rails-controller-testing rspec-rails (~> 4.0.2) sass-rails (>= 6) selenium-webdriver diff --git a/app/assets/stylesheets/questions.scss b/app/assets/stylesheets/questions.scss new file mode 100644 index 0000000..b42accd --- /dev/null +++ b/app/assets/stylesheets/questions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the questions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb new file mode 100644 index 0000000..be6ed41 --- /dev/null +++ b/app/controllers/questions_controller.rb @@ -0,0 +1,49 @@ +class QuestionsController < ApplicationController + def index + @questions = Question.all + end + + def show + end + + def new + end + + def edit + end + + def create + @question = Question.new(question_params) + + if @question.save + redirect_to @question + else + render :new + end + end + + def update + if question.update(question_params) + redirect_to @question + else + render :edit + end + end + + def destroy + question.destroy + redirect_to questions_path + end + + private + + def question + @question ||= params[:id] ? Question.find(params[:id]) : Question.new + end + + helper_method :question + + def question_params + params.require(:question).permit(:title, :body) + end +end diff --git a/app/helpers/questions_helper.rb b/app/helpers/questions_helper.rb new file mode 100644 index 0000000..2eaab4a --- /dev/null +++ b/app/helpers/questions_helper.rb @@ -0,0 +1,2 @@ +module QuestionsHelper +end diff --git a/app/views/questions/edit.html.haml b/app/views/questions/edit.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/app/views/questions/index.html.haml b/app/views/questions/index.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/app/views/questions/new.html.haml b/app/views/questions/new.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/app/views/questions/show.html.haml b/app/views/questions/show.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/config/application.rb b/config/application.rb index 782c96f..2f02588 100644 --- a/config/application.rb +++ b/config/application.rb @@ -18,5 +18,14 @@ class Application < Rails::Application # # config.time_zone = "Central Time (US & Canada)" # config.eager_load_paths << Rails.root.join("extras") + config.generators do |g| + g.test_framework :rspec, + controller_specs: true, + view_specs: false, + helper_specs: false, + routing_specs: false, + request_specs: false + + end end end diff --git a/config/routes.rb b/config/routes.rb index c06383a..325f1ca 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,5 @@ Rails.application.routes.draw do - # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html + resources :questions do + resources :answers, shallow: true + end end diff --git a/spec/controllers/questions_controller_spec.rb b/spec/controllers/questions_controller_spec.rb new file mode 100644 index 0000000..d61794b --- /dev/null +++ b/spec/controllers/questions_controller_spec.rb @@ -0,0 +1,114 @@ +require 'rails_helper' + +RSpec.describe QuestionsController, type: :controller do + let(:question) { create(:question) } + + describe 'GET #index' do + let(:questions) { create_list(:question, 3) } + before { get :index } + + it 'populates an array of all questions' do + expect(assigns(:questions)).to match_array(questions) + end + + it 'renders index view' do + expect(response).to render_template :index + end + end + + describe 'GET #show' do + before { get :show, params: { id: question } } + + it 'renders show view' do + expect(response).to render_template :show + end + end + + describe 'GET #new' do + before { get :new } + + it 'renders new view' do + expect(response).to render_template :new + end + end + + describe 'GET #edit' do + before { get :edit, params: { id: question } } + + it 'renders edit view' do + expect(response).to render_template :edit + end + end + + describe 'POST #create' do + context 'with valid attributes' do + it 'saves a new question in the database' do + expect { post :create, params: { question: attributes_for(:question) } }.to change(Question, :count).by(1) + end + + it 'redirects to show view' do + post :create, params: { question: attributes_for(:question) } + expect(response).to redirect_to assigns(:question) + end + end + + context 'with invalid attributes' do + it 'does not save the question' do + expect { post :create, params: { question: attributes_for(:question, :invalid) } }.to_not change(Question, :count) + end + + it 're-renders new view' do + post :create, params: { question: attributes_for(:question, :invalid) } + expect(response).to render_template :new + end + end + end + + describe 'PATCH #update' do + context 'with valid attributes' do + it 'assigns the requested question to @question' do + patch :update, params: { id: question, question: attributes_for(:question) } + expect(assigns(:question)).to eq question + end + + it 'changes question attributes' do + patch :update, params: { id: question, question: { title: 'new title', body: 'new body' } } + question.reload + expect(question.title).to eq 'new title' + expect(question.body).to eq 'new body' + end + + it 'redirects to updated question' do + patch :update, params: { id: question, question: attributes_for(:question) } + expect(response).to redirect_to question + end + end + + context 'with invalid attributes' do + before { patch :update, params: { id: question, question: attributes_for(:question, :invalid) } } + + it 'does not change question' do + question.reload + expect(question.title).to eq 'MyString' + expect(question.body).to eq 'MyText' + end + + it 're-renders edit view' do + expect(response).to render_template :edit + end + end + end + + describe 'DELETE #destroy' do + let!(:question) { create(:question) } + + it 'deletes the question' do + expect { delete :destroy, params: { id: question } }.to change(Question, :count).by(-1) + end + + it 'redirects to index' do + delete :destroy, params: { id: question } + expect(response).to redirect_to questions_path + end + end +end diff --git a/spec/factories/questions.rb b/spec/factories/questions.rb index 0b51d2a..5907d70 100644 --- a/spec/factories/questions.rb +++ b/spec/factories/questions.rb @@ -3,4 +3,8 @@ title { "MyString" } body { "MyText" } end + + trait :invalid do + title { nil } + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 5c61d26..b72aa0e 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -31,6 +31,7 @@ exit 1 end RSpec.configure do |config| + config.include FactoryBot::Syntax::Methods # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" From 5dbdb7f6b5301c9382394949f18742eeaa72d2e5 Mon Sep 17 00:00:00 2001 From: ksilex Date: Wed, 3 Mar 2021 14:57:16 +0300 Subject: [PATCH 2/4] answers controller create action --- app/assets/stylesheets/answers.scss | 3 ++ app/controllers/answers_controller.rb | 29 ++++++++++++++ app/helpers/answers_helper.rb | 2 + app/views/answers/new.html.haml | 0 config/routes.rb | 2 +- spec/controllers/answers_controller_spec.rb | 40 +++++++++++++++++++ spec/controllers/questions_controller_spec.rb | 7 ++-- spec/factories/answers.rb | 6 ++- spec/factories/questions.rb | 4 +- 9 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 app/assets/stylesheets/answers.scss create mode 100644 app/controllers/answers_controller.rb create mode 100644 app/helpers/answers_helper.rb create mode 100644 app/views/answers/new.html.haml create mode 100644 spec/controllers/answers_controller_spec.rb diff --git a/app/assets/stylesheets/answers.scss b/app/assets/stylesheets/answers.scss new file mode 100644 index 0000000..ce16dd0 --- /dev/null +++ b/app/assets/stylesheets/answers.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the answers controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb new file mode 100644 index 0000000..602f88a --- /dev/null +++ b/app/controllers/answers_controller.rb @@ -0,0 +1,29 @@ +class AnswersController < ApplicationController + def new + end + + def create + @answer = question.answers.new(answer_params) + if @answer.save + redirect_to @answer.question + else + render :new + end + end + + private + + def answer + @answer ||= params[:id] ? Answer.find(params[:id]) : question.answers.new + end + + helper_method :answer + + def question + @question ||= Question.find(params[:question_id]) + end + + def answer_params + params.require(:answer).permit(:body) + end +end diff --git a/app/helpers/answers_helper.rb b/app/helpers/answers_helper.rb new file mode 100644 index 0000000..b7cdb29 --- /dev/null +++ b/app/helpers/answers_helper.rb @@ -0,0 +1,2 @@ +module AnswersHelper +end diff --git a/app/views/answers/new.html.haml b/app/views/answers/new.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/config/routes.rb b/config/routes.rb index 325f1ca..352f024 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ Rails.application.routes.draw do resources :questions do - resources :answers, shallow: true + resources :answers, shallow: true, except: %i[index show] end end diff --git a/spec/controllers/answers_controller_spec.rb b/spec/controllers/answers_controller_spec.rb new file mode 100644 index 0000000..26bb94f --- /dev/null +++ b/spec/controllers/answers_controller_spec.rb @@ -0,0 +1,40 @@ +require 'rails_helper' + +RSpec.describe AnswersController, type: :controller do + let!(:answer) { create(:answer) } + describe 'GET #new' do + before { get :new, params: { question_id: answer.question } } + + it 'renders new view' do + expect(response).to render_template :new + end + end + + describe 'POST #create' do + context 'with valid attributes' do + it 'saves a new answer in the database' do + expect { post :create, params: { answer: attributes_for(:answer), + question_id: answer.question } + }.to change(Answer, :count).by(1) + end + + it 'redirects to associated question' do + post :create, params: { answer: attributes_for(:answer), question_id: answer.question } + expect(response).to redirect_to answer.question + end + end + + context 'with invalid attributes' do + it 'does not save the answer' do + expect { post :create, params: { answer: attributes_for(:answer, :invalid_answer), + question_id: answer.question } + }.to_not change(Answer, :count) + end + + it 're-renders new view' do + post :create, params: { answer: attributes_for(:answer, :invalid_answer), question_id: answer.question } + expect(response).to render_template :new + end + end + end +end diff --git a/spec/controllers/questions_controller_spec.rb b/spec/controllers/questions_controller_spec.rb index d61794b..5358b32 100644 --- a/spec/controllers/questions_controller_spec.rb +++ b/spec/controllers/questions_controller_spec.rb @@ -54,11 +54,12 @@ context 'with invalid attributes' do it 'does not save the question' do - expect { post :create, params: { question: attributes_for(:question, :invalid) } }.to_not change(Question, :count) + expect { post :create, params: { question: attributes_for(:question, :invalid_question) } } + .to_not change(Question, :count) end it 're-renders new view' do - post :create, params: { question: attributes_for(:question, :invalid) } + post :create, params: { question: attributes_for(:question, :invalid_question) } expect(response).to render_template :new end end @@ -85,7 +86,7 @@ end context 'with invalid attributes' do - before { patch :update, params: { id: question, question: attributes_for(:question, :invalid) } } + before { patch :update, params: { id: question, question: attributes_for(:question, :invalid_question) } } it 'does not change question' do question.reload diff --git a/spec/factories/answers.rb b/spec/factories/answers.rb index 72955ae..8a04d82 100644 --- a/spec/factories/answers.rb +++ b/spec/factories/answers.rb @@ -1,6 +1,10 @@ FactoryBot.define do factory :answer do body { "MyString" } - question { nil } + association :question + end + + trait :invalid_answer do + body { nil } end end diff --git a/spec/factories/questions.rb b/spec/factories/questions.rb index 5907d70..f11d400 100644 --- a/spec/factories/questions.rb +++ b/spec/factories/questions.rb @@ -3,8 +3,8 @@ title { "MyString" } body { "MyText" } end - - trait :invalid do + + trait :invalid_question do title { nil } end end From a22ddc272aeb3236f747588e55df04c202bfdad1 Mon Sep 17 00:00:00 2001 From: ksilex Date: Wed, 3 Mar 2021 16:08:42 +0300 Subject: [PATCH 3/4] update/edit added --- app/controllers/answers_controller.rb | 11 +++++ app/views/answers/edit.html.haml | 0 db/migrate/20210301193229_create_answers.rb | 2 +- db/schema.rb | 2 +- spec/controllers/answers_controller_spec.rb | 49 +++++++++++++++++++ spec/controllers/questions_controller_spec.rb | 5 -- spec/factories/answers.rb | 2 +- 7 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 app/views/answers/edit.html.haml diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index 602f88a..966be6e 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -11,6 +11,17 @@ def create end end + def edit + end + + def update + if answer.update(answer_params) + redirect_to answer.question + else + render :edit + end + end + private def answer diff --git a/app/views/answers/edit.html.haml b/app/views/answers/edit.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/db/migrate/20210301193229_create_answers.rb b/db/migrate/20210301193229_create_answers.rb index 94c74ad..4a17add 100644 --- a/db/migrate/20210301193229_create_answers.rb +++ b/db/migrate/20210301193229_create_answers.rb @@ -1,7 +1,7 @@ class CreateAnswers < ActiveRecord::Migration[6.1] def change create_table :answers do |t| - t.string :body, null: false + t.text :body, null: false t.references :question, null: false, foreign_key: true t.timestamps diff --git a/db/schema.rb b/db/schema.rb index ee2b9c0..4440d68 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -16,7 +16,7 @@ enable_extension "plpgsql" create_table "answers", force: :cascade do |t| - t.string "body", null: false + t.text "body", null: false t.bigint "question_id", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false diff --git a/spec/controllers/answers_controller_spec.rb b/spec/controllers/answers_controller_spec.rb index 26bb94f..dfd76b9 100644 --- a/spec/controllers/answers_controller_spec.rb +++ b/spec/controllers/answers_controller_spec.rb @@ -37,4 +37,53 @@ end end end + + describe 'GET #edit' do + before { get :edit, params: { id: answer } } + + it 'renders edit view' do + expect(response).to render_template :edit + end + end + + describe 'PATCH #update' do + context 'with valid attributes' do + it 'changes answer attributes' do + patch :update, params: { id: answer, answer: { body: 'new body' } } + answer.reload + expect(answer.body).to eq 'new body' + end + + it 'redirects to associated question' do + patch :update, params: { id: answer, answer: attributes_for(:answer) } + expect(response).to redirect_to answer.question + end + end + + context 'with invalid attributes' do + before { patch :update, params: { id: answer, answer: attributes_for(:answer, :invalid_answer) } } + + it 'does not change answer' do + answer.reload + expect(answer.body).to eq "MyText" + end + + it 're-renders edit view' do + expect(response).to render_template :edit + end + end + end + + # describe 'DELETE #destroy' do + # let!(:question) { create(:question) } + + # it 'deletes the question' do + # expect { delete :destroy, params: { id: question } }.to change(Question, :count).by(-1) + # end + + # it 'redirects to index' do + # delete :destroy, params: { id: question } + # expect(response).to redirect_to questions_path + # end + # end end diff --git a/spec/controllers/questions_controller_spec.rb b/spec/controllers/questions_controller_spec.rb index 5358b32..a12d733 100644 --- a/spec/controllers/questions_controller_spec.rb +++ b/spec/controllers/questions_controller_spec.rb @@ -67,11 +67,6 @@ describe 'PATCH #update' do context 'with valid attributes' do - it 'assigns the requested question to @question' do - patch :update, params: { id: question, question: attributes_for(:question) } - expect(assigns(:question)).to eq question - end - it 'changes question attributes' do patch :update, params: { id: question, question: { title: 'new title', body: 'new body' } } question.reload diff --git a/spec/factories/answers.rb b/spec/factories/answers.rb index 8a04d82..1349d25 100644 --- a/spec/factories/answers.rb +++ b/spec/factories/answers.rb @@ -1,6 +1,6 @@ FactoryBot.define do factory :answer do - body { "MyString" } + body { "MyText" } association :question end From 5c3d5521d1df94c4568478a539c8db6f22be2931 Mon Sep 17 00:00:00 2001 From: ksilex Date: Wed, 3 Mar 2021 16:29:55 +0300 Subject: [PATCH 4/4] delete action --- app/controllers/answers_controller.rb | 5 +++++ spec/controllers/answers_controller_spec.rb | 21 ++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index 966be6e..0c4a66c 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -22,6 +22,11 @@ def update end end + def destroy + answer.destroy + redirect_to answer.question + end + private def answer diff --git a/spec/controllers/answers_controller_spec.rb b/spec/controllers/answers_controller_spec.rb index dfd76b9..5be3f26 100644 --- a/spec/controllers/answers_controller_spec.rb +++ b/spec/controllers/answers_controller_spec.rb @@ -2,6 +2,7 @@ RSpec.describe AnswersController, type: :controller do let!(:answer) { create(:answer) } + describe 'GET #new' do before { get :new, params: { question_id: answer.question } } @@ -74,16 +75,14 @@ end end - # describe 'DELETE #destroy' do - # let!(:question) { create(:question) } - - # it 'deletes the question' do - # expect { delete :destroy, params: { id: question } }.to change(Question, :count).by(-1) - # end + describe 'DELETE #destroy' do + it 'deletes the answer' do + expect { delete :destroy, params: { id: answer } }.to change(Answer, :count).by(-1) + end - # it 'redirects to index' do - # delete :destroy, params: { id: question } - # expect(response).to redirect_to questions_path - # end - # end + it 'redirects to associated question' do + delete :destroy, params: { id: answer } + expect(response).to redirect_to answer.question + end + end end