From 3395c749c34506c5b3d9396e8b58ab6ac44bf974 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Wed, 19 Dec 2012 17:09:51 -0800 Subject: [PATCH 01/20] Let's not include a password confirmation field. --- app_prototype/app/controllers/password_resets_controller.rb | 3 --- app_prototype/app/models/user.rb | 1 - app_prototype/app/views/password_resets/edit.html.slim | 1 - app_prototype/app/views/registrations/new.html.slim | 1 - 4 files changed, 6 deletions(-) diff --git a/app_prototype/app/controllers/password_resets_controller.rb b/app_prototype/app/controllers/password_resets_controller.rb index 6cb77bc..22d93b7 100644 --- a/app_prototype/app/controllers/password_resets_controller.rb +++ b/app_prototype/app/controllers/password_resets_controller.rb @@ -24,9 +24,6 @@ def update @user = User.load_from_reset_password_token(@token) not_authenticated if !@user - # Makes the password confirmation validation work. - @user.password_confirmation = params[:user][:password_confirmation] - # Clear the temporary token and update the password. if @user.change_password!(params[:user][:password]) redirect_to sign_in_path, notice: "Password was successfully updated." diff --git a/app_prototype/app/models/user.rb b/app_prototype/app/models/user.rb index 7ff66e9..14352e6 100644 --- a/app_prototype/app/models/user.rb +++ b/app_prototype/app/models/user.rb @@ -11,7 +11,6 @@ class User < ActiveRecord::Base validates :password, presence: true, length: { minimum: 6 }, - confirmation: true, if: :password end diff --git a/app_prototype/app/views/password_resets/edit.html.slim b/app_prototype/app/views/password_resets/edit.html.slim index ecf706b..658de8a 100644 --- a/app_prototype/app/views/password_resets/edit.html.slim +++ b/app_prototype/app/views/password_resets/edit.html.slim @@ -7,7 +7,6 @@ .form-inputs = f.input :email, disabled: true = f.input :password, required: true, autofocus: true - = f.input :password_confirmation, required: true = hidden_field_tag :token, @token .form-actions diff --git a/app_prototype/app/views/registrations/new.html.slim b/app_prototype/app/views/registrations/new.html.slim index e6fa890..777a2e4 100644 --- a/app_prototype/app/views/registrations/new.html.slim +++ b/app_prototype/app/views/registrations/new.html.slim @@ -8,7 +8,6 @@ = f.input :name, autofocus: true = f.input :email = f.input :password - = f.input :password_confirmation .form-actions = f.button :submit, 'Sign up', class: 'btn btn-primary' From 9c389d6afdde2d3799a992ce2c3fe1971d5ae6aa Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Wed, 19 Dec 2012 17:10:09 -0800 Subject: [PATCH 02/20] Registration spec. --- app_prototype/Gemfile | 1 + .../spec/features/registrations_spec.rb | 23 +++++++++++++++++++ app_prototype/spec/spec_helper.rb | 1 + 3 files changed, 25 insertions(+) diff --git a/app_prototype/Gemfile b/app_prototype/Gemfile index 0e735e4..53dc6e0 100644 --- a/app_prototype/Gemfile +++ b/app_prototype/Gemfile @@ -26,6 +26,7 @@ end group :test, :development do gem 'rspec-rails', '~> 2.12.0' gem 'capybara', github: 'jnicklas/capybara' # Switch from github once issue #882 is resolved. + gem 'capybara-email' gem 'factory_girl_rails' gem 'jasminerice' gem 'timecop' diff --git a/app_prototype/spec/features/registrations_spec.rb b/app_prototype/spec/features/registrations_spec.rb index 09d527f..60f70b4 100644 --- a/app_prototype/spec/features/registrations_spec.rb +++ b/app_prototype/spec/features/registrations_spec.rb @@ -4,7 +4,30 @@ feature "Registrations" do + before do + clear_emails + end + scenario "sign up" do + email = 'stan.user@example.com' + visit sign_up_path + + within '#new_user' do + fill_in 'user_email', with: email + fill_in 'user_name', with: 'Stan User' + fill_in 'user_password', with: 'sekr1tpants' + click_button 'Sign up' + end + + user = User.where(email: email).first + user.should_not be_nil + + current_path.should eq sign_in_path + + find('.alert').should have_content "Thanks for signing up. Please check your email for activation instructions." + + open_email(email).should_not be_nil + current_email.should have_content "http://0.0.0.0:3000/sign_up/#{user.activation_token}/activate" end end diff --git a/app_prototype/spec/spec_helper.rb b/app_prototype/spec/spec_helper.rb index 5aee06c..8b5d06c 100644 --- a/app_prototype/spec/spec_helper.rb +++ b/app_prototype/spec/spec_helper.rb @@ -29,6 +29,7 @@ def format(result) require File.expand_path('../../config/environment', __FILE__) require 'rspec/rails' require 'rspec/autorun' +require 'capybara/email/rspec' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. From a3d427828a45c057e423f50e1d58bcf45b2bbb6a Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Thu, 20 Dec 2012 10:28:20 -0800 Subject: [PATCH 03/20] Use the new expect() syntax. In fact, disallow the old syntax. --- .../user_sessions_controller_spec.rb | 8 ++--- .../spec/controllers/users_controller_spec.rb | 32 +++++++++---------- .../spec/features/user_sessions_spec.rb | 6 ++-- .../spec/mailers/user_mailer_spec.rb | 24 +++++++------- app_prototype/spec/models/user_spec.rb | 8 ++--- app_prototype/spec/spec_helper.rb | 4 +++ .../spec/views/users/edit.html.slim_spec.rb | 1 - .../spec/views/users/index.html.slim_spec.rb | 1 - .../spec/views/users/new.html.slim_spec.rb | 1 - .../spec/views/users/show.html.slim_spec.rb | 5 ++- 10 files changed, 45 insertions(+), 45 deletions(-) diff --git a/app_prototype/spec/controllers/user_sessions_controller_spec.rb b/app_prototype/spec/controllers/user_sessions_controller_spec.rb index c1093c0..7d4cc4a 100644 --- a/app_prototype/spec/controllers/user_sessions_controller_spec.rb +++ b/app_prototype/spec/controllers/user_sessions_controller_spec.rb @@ -5,7 +5,7 @@ describe "GET new" do it "assigns a new user as @user" do get :new - assigns(:user_session).should_not be_nil + expect(assigns(:user_session)).to_not be_nil end end @@ -14,7 +14,7 @@ it "redirect to the target page" do subject.stub(:login) { build_stubbed :user } post :create, { user_session: { email: 'valid', password: 'valid' } }, { return_to_url: 'url' } - response.should redirect_to('url') + expect(response).to redirect_to('url') end end @@ -22,7 +22,7 @@ it "re-renders the 'new' template" do subject.stub(:login) { nil } post :create, { user_session: { email: 'invalid', password: 'invalid' } } - response.should render_template('new') + expect(response).to render_template('new') end end end @@ -35,7 +35,7 @@ it "redirects to the sign in page" do delete :destroy - response.should redirect_to(sign_in_url) + expect(response).to redirect_to(sign_in_url) end end diff --git a/app_prototype/spec/controllers/users_controller_spec.rb b/app_prototype/spec/controllers/users_controller_spec.rb index 634627c..7692d08 100644 --- a/app_prototype/spec/controllers/users_controller_spec.rb +++ b/app_prototype/spec/controllers/users_controller_spec.rb @@ -2,14 +2,14 @@ describe UsersController do - # This should return the minimal set of attributes required to create a valid + # expect(This).to return the minimal set of attributes required to create a valid # User. As you add validations to User, be sure to # update the return value of this method accordingly. def valid_attributes attributes_for :user end - # This should return the minimal set of values that should be in the session + # expect(This).to expect(return the minimal set of values that).to be in the session # in order to pass any filters (e.g. authentication) defined in # UsersController. Be sure to keep this updated too. def valid_session @@ -24,7 +24,7 @@ def valid_session it "assigns all users as @users" do user = User.create! valid_attributes get :index, {}, valid_session - assigns(:users).should eq([user]) + expect(assigns(:users)).to eq([user]) end end @@ -32,14 +32,14 @@ def valid_session it "assigns the requested user as @user" do user = User.create! valid_attributes get :show, { id: user.to_param }, valid_session - assigns(:user).should eq(user) + expect(assigns(:user)).to eq(user) end end describe "GET new" do it "assigns a new user as @user" do get :new, {}, valid_session - assigns(:user).should be_a_new(User) + expect(assigns(:user)).to be_a_new(User) end end @@ -47,7 +47,7 @@ def valid_session it "assigns the requested user as @user" do user = User.create! valid_attributes get :edit, { id: user.to_param }, valid_session - assigns(:user).should eq(user) + expect(assigns(:user)).to eq(user) end end @@ -61,13 +61,13 @@ def valid_session it "assigns a newly created user as @user" do post :create, {user: valid_attributes }, valid_session - assigns(:user).should be_a(User) - assigns(:user).should be_persisted + expect(assigns(:user)).to be_a(User) + expect(assigns(:user)).to be_persisted end it "redirects to the created user" do post :create, { user: valid_attributes }, valid_session - response.should redirect_to(User.last) + expect(response).to redirect_to(User.last) end end @@ -76,14 +76,14 @@ def valid_session # Trigger the behavior that occurs when invalid params are submitted User.any_instance.stub(:save).and_return(false) post :create, { user: { "email" => "invalid value" } }, valid_session - assigns(:user).should be_a_new(User) + expect(assigns(:user)).to be_a_new(User) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted User.any_instance.stub(:save).and_return(false) post :create, { user: { "email" => "invalid value" } }, valid_session - response.should render_template("new") + expect(response).to render_template("new") end end end @@ -103,13 +103,13 @@ def valid_session it "assigns the requested user as @user" do user = User.create! valid_attributes put :update, { id: user.to_param, user: valid_attributes }, valid_session - assigns(:user).should eq(user) + expect(assigns(:user)).to eq(user) end it "redirects to the user" do user = User.create! valid_attributes put :update, { id: user.to_param, user: valid_attributes }, valid_session - response.should redirect_to(user) + expect(response).to redirect_to(user) end end @@ -119,7 +119,7 @@ def valid_session # Trigger the behavior that occurs when invalid params are submitted User.any_instance.stub(:save).and_return(false) put :update, { id: user.to_param, user: { "email" => "invalid value" } }, valid_session - assigns(:user).should eq(user) + expect(assigns(:user)).to eq(user) end it "re-renders the 'edit' template" do @@ -127,7 +127,7 @@ def valid_session # Trigger the behavior that occurs when invalid params are submitted User.any_instance.stub(:save).and_return(false) put :update, { id: user.to_param, user: { "email" => "invalid value" } }, valid_session - response.should render_template("edit") + expect(response).to render_template("edit") end end end @@ -143,7 +143,7 @@ def valid_session it "redirects to the users list" do user = User.create! valid_attributes delete :destroy, { id: user.to_param }, valid_session - response.should redirect_to(users_url) + expect(response).to redirect_to(users_url) end end diff --git a/app_prototype/spec/features/user_sessions_spec.rb b/app_prototype/spec/features/user_sessions_spec.rb index f67887f..524c258 100644 --- a/app_prototype/spec/features/user_sessions_spec.rb +++ b/app_prototype/spec/features/user_sessions_spec.rb @@ -14,19 +14,19 @@ scenario "Sign in with valid credentials" do sign_in(@user.email, 'password') - find('.alert').should have_content("Successfully signed in") + expect(find('.alert')).to have_content("Successfully signed in") end scenario "Sign in with an invalid email" do sign_in('this is not valid', 'password') - find('.alert').should have_content("Sign in failed") + expect(find('.alert')).to have_content("Sign in failed") end scenario "Sign in with an invalid password" do sign_in(@user.email, 'this is not valid') - find('.alert').should have_content("Sign in failed") + expect(find('.alert')).to have_content("Sign in failed") end end diff --git a/app_prototype/spec/mailers/user_mailer_spec.rb b/app_prototype/spec/mailers/user_mailer_spec.rb index 9be9af0..7f83351 100644 --- a/app_prototype/spec/mailers/user_mailer_spec.rb +++ b/app_prototype/spec/mailers/user_mailer_spec.rb @@ -7,13 +7,13 @@ let(:mail) { UserMailer.activation_needed_email(user) } it "renders the headers" do - mail.subject.should eq("Welcome to My Awesome Site!") - mail.to.should eq([user.email]) - mail.from.should eq(['notifications@example.com']) + expect(mail.subject).to eq "Welcome to My Awesome Site!" + expect(mail.to).to eq [user.email] + expect(mail.from).to eq ['notifications@example.com'] end it "renders the body" do - mail.body.encoded.should match("Welcome to") + expect(mail.body.encoded).to match "Welcome to" end end @@ -21,13 +21,13 @@ let(:mail) { UserMailer.activation_success_email(user) } it "renders the headers" do - mail.subject.should eq("Your account has been activated!") - mail.to.should eq([user.email]) - mail.from.should eq(['notifications@example.com']) + expect(mail.subject).to eq "Your account has been activated!" + expect(mail.to).to eq [user.email] + expect(mail.from).to eq ['notifications@example.com'] end it "renders the body" do - mail.body.encoded.should match("You have successfully activated") + expect(mail.body.encoded).to match "You have successfully activated" end end @@ -35,13 +35,13 @@ let(:mail) { UserMailer.reset_password_email(user) } it "renders the headers" do - mail.subject.should eq("Password reset requested") - mail.to.should eq([user.email]) - mail.from.should eq(['notifications@example.com']) + expect(mail.subject).to eq "Password reset requested" + expect(mail.to).to eq [user.email] + expect(mail.from).to eq ['notifications@example.com'] end it "renders the body" do - mail.body.encoded.should match("You have requested to reset your password.") + expect(mail.body.encoded).to match "You have requested to reset your password." end end diff --git a/app_prototype/spec/models/user_spec.rb b/app_prototype/spec/models/user_spec.rb index 1a32fe8..8643399 100644 --- a/app_prototype/spec/models/user_spec.rb +++ b/app_prototype/spec/models/user_spec.rb @@ -7,7 +7,7 @@ describe "name" do it "is required" do - subject.should_not accept_values(:email, nil, '') + expect(subject).to_not accept_values(:email, nil, '') end it "should be less than 30 characters" @@ -15,12 +15,12 @@ describe "email" do it "is required" do - subject.should_not accept_values(:email, nil, '', ' ') + expect(subject).to_not accept_values(:email, nil, '', ' ') end it "must be properly formatted" do - subject.should accept_values(:email, 'a@b.com', 'a@b.c.com') - subject.should_not accept_values(:email, 'a@b', 'a.b.com') + expect(subject).to accept_values(:email, 'a@b.com', 'a@b.c.com') + expect(subject).to_not accept_values(:email, 'a@b', 'a.b.com') end it "must be unique" diff --git a/app_prototype/spec/spec_helper.rb b/app_prototype/spec/spec_helper.rb index 8b5d06c..4df6aa5 100644 --- a/app_prototype/spec/spec_helper.rb +++ b/app_prototype/spec/spec_helper.rb @@ -49,6 +49,10 @@ def format(result) # rspec-rails. config.infer_base_class_for_anonymous_controllers = false + config.expect_with :rspec do |c| + c.syntax = :expect + end + # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. diff --git a/app_prototype/spec/views/users/edit.html.slim_spec.rb b/app_prototype/spec/views/users/edit.html.slim_spec.rb index 072acc9..77fa699 100644 --- a/app_prototype/spec/views/users/edit.html.slim_spec.rb +++ b/app_prototype/spec/views/users/edit.html.slim_spec.rb @@ -11,7 +11,6 @@ it "renders the edit user form" do render - # Run the generator again with the --webrat flag if you want to use webrat matchers assert_select "form", action: users_path(@user), method: "post" do assert_select "input#user_email", name: "user[email]" assert_select "input#user_name", name: "user[name]" diff --git a/app_prototype/spec/views/users/index.html.slim_spec.rb b/app_prototype/spec/views/users/index.html.slim_spec.rb index 7fdcd4a..9d3b395 100644 --- a/app_prototype/spec/views/users/index.html.slim_spec.rb +++ b/app_prototype/spec/views/users/index.html.slim_spec.rb @@ -16,7 +16,6 @@ it "renders a list of users" do render - # Run the generator again with the --webrat flag if you want to use webrat matchers assert_select "tr>td", text: "Email".to_s, count: 2 assert_select "tr>td", text: "Name".to_s, count: 2 end diff --git a/app_prototype/spec/views/users/new.html.slim_spec.rb b/app_prototype/spec/views/users/new.html.slim_spec.rb index 4663c3a..4b03092 100644 --- a/app_prototype/spec/views/users/new.html.slim_spec.rb +++ b/app_prototype/spec/views/users/new.html.slim_spec.rb @@ -11,7 +11,6 @@ it "renders new user form" do render - # Run the generator again with the --webrat flag if you want to use webrat matchers assert_select "form", action: users_path, method: "post" do assert_select "input#user_email", name: "user[email]" assert_select "input#user_name", name: "user[name]" diff --git a/app_prototype/spec/views/users/show.html.slim_spec.rb b/app_prototype/spec/views/users/show.html.slim_spec.rb index 34fe041..fcffad5 100644 --- a/app_prototype/spec/views/users/show.html.slim_spec.rb +++ b/app_prototype/spec/views/users/show.html.slim_spec.rb @@ -10,8 +10,7 @@ it "renders attributes in

" do render - # Run the generator again with the --webrat flag if you want to use webrat matchers - rendered.should match(/Email/) - rendered.should match(/Name/) + expect(rendered).to match /Email/ + expect(rendered).to match /Name/ end end From d87d94fc427a182dc38dbd8501a528853d3c37f4 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Thu, 20 Dec 2012 10:28:37 -0800 Subject: [PATCH 04/20] No empty specs needed. --- app_prototype/spec/helpers/users_helper_spec.rb | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 app_prototype/spec/helpers/users_helper_spec.rb diff --git a/app_prototype/spec/helpers/users_helper_spec.rb b/app_prototype/spec/helpers/users_helper_spec.rb deleted file mode 100644 index e65fff9..0000000 --- a/app_prototype/spec/helpers/users_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the UsersHelper. For example: -# -# describe UsersHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe UsersHelper do - pending "add some examples to (or delete) #{__FILE__}" -end From 262aad94aa87e9095bb2db61181d5a30507509bb Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Thu, 20 Dec 2012 10:29:01 -0800 Subject: [PATCH 05/20] Launchy is nice to have when debugging acceptance specs. --- app_prototype/Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/app_prototype/Gemfile b/app_prototype/Gemfile index 53dc6e0..3cb092e 100644 --- a/app_prototype/Gemfile +++ b/app_prototype/Gemfile @@ -35,6 +35,7 @@ end group :development do gem 'foreman' + gem 'launchy' gem 'guard' gem 'guard-rspec' gem 'guard-jasmine' From 18ac4a38542206a76a2f779767a7de204cf2c197 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Thu, 20 Dec 2012 16:29:01 -0800 Subject: [PATCH 06/20] Cover registration happy path and rename feature spec files. --- .../spec/features/registration_spec.rb | 42 +++++++++++++++++++ .../spec/features/registrations_spec.rb | 33 --------------- ...{user_sessions_spec.rb => sign_in_spec.rb} | 4 +- 3 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 app_prototype/spec/features/registration_spec.rb delete mode 100644 app_prototype/spec/features/registrations_spec.rb rename app_prototype/spec/features/{user_sessions_spec.rb => sign_in_spec.rb} (95%) diff --git a/app_prototype/spec/features/registration_spec.rb b/app_prototype/spec/features/registration_spec.rb new file mode 100644 index 0000000..84be538 --- /dev/null +++ b/app_prototype/spec/features/registration_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +# http://www.elabs.se/blog/51-simple-tricks-to-clean-up-your-capybara-tests +# Not psyched about running the background on each spec. + +feature "Registration" do + context "successful registration" do + background do + clear_emails + + @email = 'stan@example.com' + + visit sign_up_path + + within '#new_user' do + fill_in 'Email', with: @email + fill_in 'Name', with: 'Stan' + fill_in 'Password', with: 'p@ssword' + click_button 'Sign up' + end + + @user = User.find_by_email(@email) + end + + after do + @user.destroy + end + + it "creates a new user" do + expect(@user).to_not be_nil + end + + it "displays a message about activation" do + expect(find('.alert')).to have_content "Thanks for signing up. Please check your email for activation instructions." + end + + it "sends the activation email" do + expect(open_email(@email)).to_not be_nil + expect(current_email).to have_content "/sign_up/#{@user.activation_token}/activate" + end + end +end diff --git a/app_prototype/spec/features/registrations_spec.rb b/app_prototype/spec/features/registrations_spec.rb deleted file mode 100644 index 60f70b4..0000000 --- a/app_prototype/spec/features/registrations_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'spec_helper' - -# http://www.elabs.se/blog/51-simple-tricks-to-clean-up-your-capybara-tests - -feature "Registrations" do - - before do - clear_emails - end - - scenario "sign up" do - email = 'stan.user@example.com' - - visit sign_up_path - - within '#new_user' do - fill_in 'user_email', with: email - fill_in 'user_name', with: 'Stan User' - fill_in 'user_password', with: 'sekr1tpants' - click_button 'Sign up' - end - - user = User.where(email: email).first - user.should_not be_nil - - current_path.should eq sign_in_path - - find('.alert').should have_content "Thanks for signing up. Please check your email for activation instructions." - - open_email(email).should_not be_nil - current_email.should have_content "http://0.0.0.0:3000/sign_up/#{user.activation_token}/activate" - end -end diff --git a/app_prototype/spec/features/user_sessions_spec.rb b/app_prototype/spec/features/sign_in_spec.rb similarity index 95% rename from app_prototype/spec/features/user_sessions_spec.rb rename to app_prototype/spec/features/sign_in_spec.rb index 524c258..4039b75 100644 --- a/app_prototype/spec/features/user_sessions_spec.rb +++ b/app_prototype/spec/features/sign_in_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' -feature "User Sessions" do - +feature "Sign In" do background(:all) do @user = create(:user) @user.activate! @@ -28,5 +27,4 @@ expect(find('.alert')).to have_content("Sign in failed") end - end From c14d8c80a9caa9af4083f149c5eb85e4a8ede50b Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Fri, 21 Dec 2012 09:08:05 -0800 Subject: [PATCH 07/20] Sign out helper. --- app_prototype/spec/support/user_sessions_feature_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app_prototype/spec/support/user_sessions_feature_helper.rb b/app_prototype/spec/support/user_sessions_feature_helper.rb index 687b30e..84567ca 100644 --- a/app_prototype/spec/support/user_sessions_feature_helper.rb +++ b/app_prototype/spec/support/user_sessions_feature_helper.rb @@ -10,8 +10,8 @@ def sign_in(email, password) end end - def sign_out(user = @current_user) - # TODO + def sign_out + visit sign_out_path end end From 2d83fd6f471e0d7d50cf4f281844c5cd0a2140c1 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Fri, 21 Dec 2012 11:33:32 -0800 Subject: [PATCH 08/20] User mailcacher in development if it's running. --- app_prototype/Gemfile | 1 + app_prototype/config/initializers/email.rb | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 app_prototype/config/initializers/email.rb diff --git a/app_prototype/Gemfile b/app_prototype/Gemfile index 3cb092e..4ef6848 100644 --- a/app_prototype/Gemfile +++ b/app_prototype/Gemfile @@ -36,6 +36,7 @@ end group :development do gem 'foreman' gem 'launchy' + gem 'mailcatcher' gem 'guard' gem 'guard-rspec' gem 'guard-jasmine' diff --git a/app_prototype/config/initializers/email.rb b/app_prototype/config/initializers/email.rb new file mode 100644 index 0000000..0c847e3 --- /dev/null +++ b/app_prototype/config/initializers/email.rb @@ -0,0 +1,11 @@ +# http://www.mikeperham.com/2012/12/09/12-gems-of-christmas-4-mailcatcher-and-mail_view/ + +begin + sock = TCPSocket.new('localhost', 1025) + sock.close + catcher = true +rescue + catcher = false +end + +ActionMailer::Base.smtp_settings = (Rails.env.development? && catcher) ? { host: 'localhost', port: '1025', } : {} From f95fc2a0716ebada50f6ea87627488008a00ad0b Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Fri, 21 Dec 2012 13:38:53 -0800 Subject: [PATCH 09/20] User emails are now unique --- app_prototype/app/models/user.rb | 6 +++++- app_prototype/spec/models/user_spec.rb | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app_prototype/app/models/user.rb b/app_prototype/app/models/user.rb index 14352e6..4fe0db1 100644 --- a/app_prototype/app/models/user.rb +++ b/app_prototype/app/models/user.rb @@ -4,9 +4,13 @@ class User < ActiveRecord::Base authenticates_with_sorcery! + validates :name, + length: { maximum: 30 } + validates :email, presence: true, - email: true + email: true, + uniqueness: true validates :password, presence: true, diff --git a/app_prototype/spec/models/user_spec.rb b/app_prototype/spec/models/user_spec.rb index 8643399..01be2fe 100644 --- a/app_prototype/spec/models/user_spec.rb +++ b/app_prototype/spec/models/user_spec.rb @@ -10,7 +10,10 @@ expect(subject).to_not accept_values(:email, nil, '') end - it "should be less than 30 characters" + it "should be less than 30 characters" do + expect(subject).to accept_values(:name, 'a' * 30) + expect(subject).to_not accept_values(:name, 'a' * 31) + end end describe "email" do @@ -23,7 +26,11 @@ expect(subject).to_not accept_values(:email, 'a@b', 'a.b.com') end - it "must be unique" + it "must be unique" do + subject.save + stunt_double = subject.dup + expect(stunt_double).to_not accept_values(:email, subject.email) + end end end end From ff2b8496f4d14d6ff897acb565e206d1710c8646 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Fri, 21 Dec 2012 13:39:13 -0800 Subject: [PATCH 10/20] Comment tweaks --- app_prototype/app/controllers/registrations_controller.rb | 2 +- app_prototype/spec/controllers/users_controller_spec.rb | 2 +- app_prototype/spec/features/registration_spec.rb | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app_prototype/app/controllers/registrations_controller.rb b/app_prototype/app/controllers/registrations_controller.rb index 80abf33..624d51f 100644 --- a/app_prototype/app/controllers/registrations_controller.rb +++ b/app_prototype/app/controllers/registrations_controller.rb @@ -7,7 +7,7 @@ def new end def create - @user = User.new(params[:user]) + @user = User.new(params[:user]) # TODO Safe attributes if @user.save redirect_to sign_in_path, notice: "Thanks for signing up. Please check your email for activation instructions." diff --git a/app_prototype/spec/controllers/users_controller_spec.rb b/app_prototype/spec/controllers/users_controller_spec.rb index 7692d08..c57c44c 100644 --- a/app_prototype/spec/controllers/users_controller_spec.rb +++ b/app_prototype/spec/controllers/users_controller_spec.rb @@ -2,7 +2,7 @@ describe UsersController do - # expect(This).to return the minimal set of attributes required to create a valid + # This should return the minimal set of attributes required to create a valid # User. As you add validations to User, be sure to # update the return value of this method accordingly. def valid_attributes diff --git a/app_prototype/spec/features/registration_spec.rb b/app_prototype/spec/features/registration_spec.rb index 84be538..575949d 100644 --- a/app_prototype/spec/features/registration_spec.rb +++ b/app_prototype/spec/features/registration_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' # http://www.elabs.se/blog/51-simple-tricks-to-clean-up-your-capybara-tests -# Not psyched about running the background on each spec. feature "Registration" do context "successful registration" do From bb2b2a5977bdf7ebcfcca150fabf47a6dae3e07b Mon Sep 17 00:00:00 2001 From: Hugo Melo Date: Fri, 21 Dec 2012 14:44:55 -0800 Subject: [PATCH 11/20] Add activation acceptance coverage --- app_prototype/spec/features/activation_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app_prototype/spec/features/activation_spec.rb diff --git a/app_prototype/spec/features/activation_spec.rb b/app_prototype/spec/features/activation_spec.rb new file mode 100644 index 0000000..6877303 --- /dev/null +++ b/app_prototype/spec/features/activation_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +feature "Activation" do + scenario "a successful activation" do + @user = create(:user) + visit "/sign_up/#{@user.activation_token}/activate" # FIXME + + expect(current_path).to eq sign_in_path + expect(page).to have_content "Your account has been activated and you're now signed in." + end + + scenario "an unsuccessful activation" do + visit "/sign_up/bogus_activation_token/activate" # FIXME + + expect(current_path).to eq sign_in_path + expect(page).to have_content "Please sign in first." + end +end From 418c20b6bc1231d90c70de8697b141736e51b05f Mon Sep 17 00:00:00 2001 From: Hugo Melo Date: Fri, 21 Dec 2012 14:45:28 -0800 Subject: [PATCH 12/20] Add unsuccessful registration acceptance coverage --- app_prototype/spec/features/registration_spec.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app_prototype/spec/features/registration_spec.rb b/app_prototype/spec/features/registration_spec.rb index 575949d..c3b3077 100644 --- a/app_prototype/spec/features/registration_spec.rb +++ b/app_prototype/spec/features/registration_spec.rb @@ -35,7 +35,20 @@ it "sends the activation email" do expect(open_email(@email)).to_not be_nil - expect(current_email).to have_content "/sign_up/#{@user.activation_token}/activate" + expect(current_email).to have_content "/sign_up/#{@user.activation_token}/activate" # FIXME + end + end + + context "unsuccessful registration" do + it "redirects to new" do + visit sign_up_path + within '#new_user' do + fill_in 'Email', with: 'INVALID EMAIL' + fill_in 'Name', with: 'Stan' + fill_in 'Password', with: 'p@ssword' + click_button 'Sign up' + end + expect(current_path).to eq sign_up_path end end end From 7737e6163f690ae4348ae67598be9859e9553392 Mon Sep 17 00:00:00 2001 From: Hugo Melo Date: Fri, 21 Dec 2012 14:54:52 -0800 Subject: [PATCH 13/20] Fix authorization for password reset controller. --- .../app/controllers/password_resets_controller.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app_prototype/app/controllers/password_resets_controller.rb b/app_prototype/app/controllers/password_resets_controller.rb index 22d93b7..744e257 100644 --- a/app_prototype/app/controllers/password_resets_controller.rb +++ b/app_prototype/app/controllers/password_resets_controller.rb @@ -1,13 +1,14 @@ class PasswordResetsController < ApplicationController skip_before_filter :require_login + skip_authorization_check def create @user = User.find_by_email(params[:email]) - + # Send an email to the user with instructions on how to reset their password. @user.deliver_reset_password_instructions! if @user - + # Tell the user instructions have been sent whether or not email was found. # This is to not leak information to attackers about which emails exist in the system. redirect_to sign_in_path, notice: "Password reset instructions have been sent to your email." @@ -18,7 +19,7 @@ def edit @token = params[:id] not_authenticated if !@user end - + def update @token = params[:token] # needed to render the form again in case of error @user = User.load_from_reset_password_token(@token) From af1d333b108a4f56e91efa185b3536f2cc134ff4 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Fri, 21 Dec 2012 16:12:15 -0800 Subject: [PATCH 14/20] Add password reset coverage. --- .../spec/features/password_reset_spec.rb | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 app_prototype/spec/features/password_reset_spec.rb diff --git a/app_prototype/spec/features/password_reset_spec.rb b/app_prototype/spec/features/password_reset_spec.rb new file mode 100644 index 0000000..8bd5feb --- /dev/null +++ b/app_prototype/spec/features/password_reset_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +feature "Password Reset" do + background do + clear_emails + @user = create(:user) + @user.activate! + + visit sign_in_path + click_link 'Reset forgotten password' + + fill_in 'Email', with: @user.email + click_button 'Reset my password!' + + @user.reload + end + + after do + @user.destroy + end + + scenario "displays a message about the password reset email" do + expect(page).to have_content "Password reset instructions have been sent to your email." + expect(current_path).to eq sign_in_path + end + + scenario "the password reset email is sent with a reset url" do + expect(open_email(@user.email)).to_not be_nil + expect(current_email).to have_content edit_password_reset_path(@user.reset_password_token) # FIXME + end + + scenario "password can be reset" do + visit edit_password_reset_path(@user.reset_password_token) + + fill_in 'Password', with: 'some_good_password' + click_button 'Reset Password' + + expect(page).to have_content "Password was successfully updated." + expect(current_path).to eq sign_in_path + end +end From b0f959f53b4e9bae80c0a7764dcee955320664ae Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Tue, 25 Dec 2012 14:54:30 -0800 Subject: [PATCH 15/20] Consistent titlecasing on action buttons. --- app_prototype/app/views/layouts/application.html.slim | 2 +- app_prototype/app/views/password_resets/new.html.slim | 2 +- app_prototype/app/views/registrations/new.html.slim | 2 +- app_prototype/app/views/user_sessions/new.html.slim | 2 +- app_prototype/public/index.html | 4 ++-- app_prototype/spec/features/password_reset_spec.rb | 2 +- app_prototype/spec/features/registration_spec.rb | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app_prototype/app/views/layouts/application.html.slim b/app_prototype/app/views/layouts/application.html.slim index 8096567..6fbf9f1 100644 --- a/app_prototype/app/views/layouts/application.html.slim +++ b/app_prototype/app/views/layouts/application.html.slim @@ -22,7 +22,7 @@ html ul.dropdown-menu li= link_to 'Sign Out', sign_out_path - else - li= link_to 'Sign in', sign_in_path + li= link_to 'Sign In', sign_in_path - flash.each do |name, msg| = content_tag :div, raw(msg), class: "alert #{alert_class(name)}" diff --git a/app_prototype/app/views/password_resets/new.html.slim b/app_prototype/app/views/password_resets/new.html.slim index 000bc8b..5ccad4a 100644 --- a/app_prototype/app/views/password_resets/new.html.slim +++ b/app_prototype/app/views/password_resets/new.html.slim @@ -8,4 +8,4 @@ = text_field_tag :email, nil, placeholder: 'joe@example.com' .form-actions - = submit_tag "Reset my password!", class: 'btn btn-primary' + = submit_tag "Reset My Password!", class: 'btn btn-primary' diff --git a/app_prototype/app/views/registrations/new.html.slim b/app_prototype/app/views/registrations/new.html.slim index 777a2e4..ab68786 100644 --- a/app_prototype/app/views/registrations/new.html.slim +++ b/app_prototype/app/views/registrations/new.html.slim @@ -10,4 +10,4 @@ = f.input :password .form-actions - = f.button :submit, 'Sign up', class: 'btn btn-primary' + = f.button :submit, 'Sign Up', class: 'btn btn-primary' diff --git a/app_prototype/app/views/user_sessions/new.html.slim b/app_prototype/app/views/user_sessions/new.html.slim index 57ccf52..41fc982 100644 --- a/app_prototype/app/views/user_sessions/new.html.slim +++ b/app_prototype/app/views/user_sessions/new.html.slim @@ -6,8 +6,8 @@ = f.input :password ul.unstyled - li= link_to 'Sign up', sign_up_path li= link_to 'Reset forgotten password', new_password_reset_path + li= link_to 'Sign Up', sign_up_path .form-actions = f.button :submit, 'Sign In', class: 'btn btn-primary' diff --git a/app_prototype/public/index.html b/app_prototype/public/index.html index f58c39d..3d09074 100644 --- a/app_prototype/public/index.html +++ b/app_prototype/public/index.html @@ -15,7 +15,7 @@

Project_prototype
@@ -29,7 +29,7 @@

Hello, world!

Custom generator templates create views that are bootstrap compatible and specs that are factory-aware and follow best practices.

This application also includes authentication and an example of authorization (sign in as user@example.com / password).

-

Sign in

+

Sign In

diff --git a/app_prototype/spec/features/password_reset_spec.rb b/app_prototype/spec/features/password_reset_spec.rb index 8bd5feb..6cc78a3 100644 --- a/app_prototype/spec/features/password_reset_spec.rb +++ b/app_prototype/spec/features/password_reset_spec.rb @@ -10,7 +10,7 @@ click_link 'Reset forgotten password' fill_in 'Email', with: @user.email - click_button 'Reset my password!' + click_button 'Reset My Password!' @user.reload end diff --git a/app_prototype/spec/features/registration_spec.rb b/app_prototype/spec/features/registration_spec.rb index c3b3077..ffdb651 100644 --- a/app_prototype/spec/features/registration_spec.rb +++ b/app_prototype/spec/features/registration_spec.rb @@ -15,7 +15,7 @@ fill_in 'Email', with: @email fill_in 'Name', with: 'Stan' fill_in 'Password', with: 'p@ssword' - click_button 'Sign up' + click_button 'Sign Up' end @user = User.find_by_email(@email) @@ -46,7 +46,7 @@ fill_in 'Email', with: 'INVALID EMAIL' fill_in 'Name', with: 'Stan' fill_in 'Password', with: 'p@ssword' - click_button 'Sign up' + click_button 'Sign Up' end expect(current_path).to eq sign_up_path end From 294f4ba18d90242fdd2894064da8eb570688b29a Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Tue, 25 Dec 2012 19:44:31 -0800 Subject: [PATCH 16/20] Finishing touches on specs: - Better spec descriptions - Use path/url helpers in feature specs - Rename routes --- CHANGES.md | 5 +- .../controllers/password_resets_controller.rb | 4 +- .../app/views/password_resets/edit.html.slim | 4 +- .../app/views/password_resets/new.html.slim | 4 +- .../activation_needed_email.html.erb | 2 +- .../activation_needed_email.text.erb | 2 +- .../user_mailer/reset_password_email.html.erb | 2 +- .../user_mailer/reset_password_email.text.erb | 2 +- .../app/views/user_sessions/new.html.slim | 2 +- app_prototype/app/views/users/show.html.slim | 2 +- app_prototype/config/routes.rb | 14 +++-- .../user_sessions_controller_spec.rb | 6 +- .../spec/controllers/users_controller_spec.rb | 16 ++--- .../spec/features/activation_spec.rb | 8 +-- .../spec/features/password_reset_spec.rb | 14 ++--- .../spec/features/registration_spec.rb | 59 +++++++------------ app_prototype/spec/features/sign_in_spec.rb | 6 +- .../spec/mailers/user_mailer_spec.rb | 12 ++-- 18 files changed, 77 insertions(+), 87 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5d141e9..b6c25f9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,5 +5,6 @@ * Basic usage information. * Added guard-livereload to Guardfile. * Gemfile, rvmrc, rbenv files configured with the ruby version used to generate the app. -* Better specs for auth flows (register, password reset, sign in). -* Hardcode 1.9.3-p327 so that app_prototype is executable without futzing. \ No newline at end of file +* Better specs for auth flows (register, password reset, sign in) (~98% coverage). +* Hardcode 1.9.3-p327 so that app_prototype is executable without futzing. +* Consistent hostnames across environments. diff --git a/app_prototype/app/controllers/password_resets_controller.rb b/app_prototype/app/controllers/password_resets_controller.rb index 744e257..1f9c448 100644 --- a/app_prototype/app/controllers/password_resets_controller.rb +++ b/app_prototype/app/controllers/password_resets_controller.rb @@ -15,8 +15,8 @@ def create end def edit - @user = User.load_from_reset_password_token(params[:id]) - @token = params[:id] + @token = params[:token] + @user = User.load_from_reset_password_token(@token) not_authenticated if !@user end diff --git a/app_prototype/app/views/password_resets/edit.html.slim b/app_prototype/app/views/password_resets/edit.html.slim index 658de8a..391f90b 100644 --- a/app_prototype/app/views/password_resets/edit.html.slim +++ b/app_prototype/app/views/password_resets/edit.html.slim @@ -1,12 +1,12 @@ .page-header h1 Reset Your Password -= simple_form_for(@user, url: password_reset_path(@user)) do |f| += simple_form_for(@user, url: reset_password_path(@user)) do |f| = f.error_notification .form-inputs = f.input :email, disabled: true - = f.input :password, required: true, autofocus: true + = f.input :password, label: 'New Password', required: true, autofocus: true = hidden_field_tag :token, @token .form-actions diff --git a/app_prototype/app/views/password_resets/new.html.slim b/app_prototype/app/views/password_resets/new.html.slim index 5ccad4a..58a3508 100644 --- a/app_prototype/app/views/password_resets/new.html.slim +++ b/app_prototype/app/views/password_resets/new.html.slim @@ -1,11 +1,11 @@ .page-header h1 Password Reset -= form_tag(password_resets_path, method: :post) do += form_tag(forgotten_password_path, method: :post) do .control-group = label_tag :email, nil, class: 'control-label' .controls = text_field_tag :email, nil, placeholder: 'joe@example.com' .form-actions - = submit_tag "Reset My Password!", class: 'btn btn-primary' + = submit_tag 'Reset My Password', class: 'btn btn-primary' diff --git a/app_prototype/app/views/user_mailer/activation_needed_email.html.erb b/app_prototype/app/views/user_mailer/activation_needed_email.html.erb index a00cdee..eafa49f 100644 --- a/app_prototype/app/views/user_mailer/activation_needed_email.html.erb +++ b/app_prototype/app/views/user_mailer/activation_needed_email.html.erb @@ -10,7 +10,7 @@ your username is: <%= @user.email %>.

- To login to the site, just follow this link: <%= link_to activate_registration_url(@user.activation_token), activate_registration_url(@user.activation_token) %>. + To login to the site, just follow this link: <%= link_to activation_url(@user.activation_token), activation_url(@user.activation_token) %>.

Thanks for joining and have a great day!

diff --git a/app_prototype/app/views/user_mailer/activation_needed_email.text.erb b/app_prototype/app/views/user_mailer/activation_needed_email.text.erb index 9fe9c30..dc7c0ff 100644 --- a/app_prototype/app/views/user_mailer/activation_needed_email.text.erb +++ b/app_prototype/app/views/user_mailer/activation_needed_email.text.erb @@ -4,6 +4,6 @@ Welcome to example.com, <%= @user.email %> You have successfully signed up to example.com, your username is: <%= @user.email %>. -To login to the site, just follow this link: <%= activate_registration_url(@user.activation_token) %> +To login to the site, just follow this link: <%= activation_url(@user.activation_token) %> Thanks for joining and have a great day! diff --git a/app_prototype/app/views/user_mailer/reset_password_email.html.erb b/app_prototype/app/views/user_mailer/reset_password_email.html.erb index 5d7e572..e1231ea 100644 --- a/app_prototype/app/views/user_mailer/reset_password_email.html.erb +++ b/app_prototype/app/views/user_mailer/reset_password_email.html.erb @@ -9,7 +9,7 @@ You have requested to reset your password.

- To choose a new password, just follow this link: <%= link_to edit_password_reset_url(@user.reset_password_token), edit_password_reset_url(@user.reset_password_token) %>. + To choose a new password, just follow this link: <%= link_to reset_password_url(@user.reset_password_token), reset_password_url(@user.reset_password_token) %>.

Have a great day!

diff --git a/app_prototype/app/views/user_mailer/reset_password_email.text.erb b/app_prototype/app/views/user_mailer/reset_password_email.text.erb index 8a607e0..828c37e 100644 --- a/app_prototype/app/views/user_mailer/reset_password_email.text.erb +++ b/app_prototype/app/views/user_mailer/reset_password_email.text.erb @@ -3,6 +3,6 @@ Hello, <%= @user.email %> You have requested to reset your password. -To choose a new password, just follow this link: edit_password_reset_url(@user.reset_password_token) +To choose a new password, just follow this link: <%= reset_password_url(@user.reset_password_token) %> Have a great day! diff --git a/app_prototype/app/views/user_sessions/new.html.slim b/app_prototype/app/views/user_sessions/new.html.slim index 41fc982..977cff8 100644 --- a/app_prototype/app/views/user_sessions/new.html.slim +++ b/app_prototype/app/views/user_sessions/new.html.slim @@ -6,8 +6,8 @@ = f.input :password ul.unstyled - li= link_to 'Reset forgotten password', new_password_reset_path li= link_to 'Sign Up', sign_up_path + li= link_to 'Reset Password', forgotten_password_path .form-actions = f.button :submit, 'Sign In', class: 'btn btn-primary' diff --git a/app_prototype/app/views/users/show.html.slim b/app_prototype/app/views/users/show.html.slim index 2077381..fe7a62d 100644 --- a/app_prototype/app/views/users/show.html.slim +++ b/app_prototype/app/views/users/show.html.slim @@ -14,4 +14,4 @@ dl - if can?(:destroy, @user) ' - = link_to'Destroy', user_path(@user), method: :delete, data: { confirm: "Are you sure?" }, class: 'btn btn-danger' + = link_to 'Destroy', user_path(@user), method: :delete, data: { confirm: "Are you sure?" }, class: 'btn btn-danger' diff --git a/app_prototype/config/routes.rb b/app_prototype/config/routes.rb index 74454b5..0de4c02 100644 --- a/app_prototype/config/routes.rb +++ b/app_prototype/config/routes.rb @@ -5,12 +5,16 @@ resources :user_sessions, only: [:new, :create, :destroy] - resources :registrations, only: [:new, :create, :activate] - match 'sign_up' => 'registrations#new', via: :get - match 'sign_up' => 'registrations#create', via: :post - match 'sign_up/:token/activate' => 'registrations#activate', via: :get, as: :activate_registration + #resources :registrations, only: [:new, :create, :activate] + match 'sign_up' => 'registrations#new', via: :get, as: :sign_up + match 'sign_up' => 'registrations#create', via: :post, as: :sign_up + match 'activate/:token' => 'registrations#activate', via: :get, as: :activation - resources :password_resets, only: [:new, :create, :edit, :update] + #resources :password_resets, only: [:new, :create, :edit, :update] + match 'forgotten_password' => 'password_resets#new', via: :get, as: :forgotten_password + match 'forgotten_password' => 'password_resets#create', via: :post, as: :forgotten_password + match 'reset_password/:token' => 'password_resets#edit', via: :get, as: :reset_password + match 'reset_password/:id' => 'password_resets#update', via: :put resources :users diff --git a/app_prototype/spec/controllers/user_sessions_controller_spec.rb b/app_prototype/spec/controllers/user_sessions_controller_spec.rb index 7d4cc4a..5a076a4 100644 --- a/app_prototype/spec/controllers/user_sessions_controller_spec.rb +++ b/app_prototype/spec/controllers/user_sessions_controller_spec.rb @@ -2,14 +2,14 @@ describe UserSessionsController do - describe "GET new" do + describe "#new" do it "assigns a new user as @user" do get :new expect(assigns(:user_session)).to_not be_nil end end - describe "POST create" do + describe "#create" do describe "with valid params" do it "redirect to the target page" do subject.stub(:login) { build_stubbed :user } @@ -27,7 +27,7 @@ end end - describe "DELETE destroy" do + describe "#destroy" do it "destroys the requested user session" do subject.should_receive(:logout) delete :destroy diff --git a/app_prototype/spec/controllers/users_controller_spec.rb b/app_prototype/spec/controllers/users_controller_spec.rb index c57c44c..101af4d 100644 --- a/app_prototype/spec/controllers/users_controller_spec.rb +++ b/app_prototype/spec/controllers/users_controller_spec.rb @@ -9,7 +9,7 @@ def valid_attributes attributes_for :user end - # expect(This).to expect(return the minimal set of values that).to be in the session + # This returns the minimal set of values that should be in the session # in order to pass any filters (e.g. authentication) defined in # UsersController. Be sure to keep this updated too. def valid_session @@ -20,7 +20,7 @@ def valid_session login_user build :admin end - describe "GET index" do + describe "#index" do it "assigns all users as @users" do user = User.create! valid_attributes get :index, {}, valid_session @@ -28,7 +28,7 @@ def valid_session end end - describe "GET show" do + describe "#show" do it "assigns the requested user as @user" do user = User.create! valid_attributes get :show, { id: user.to_param }, valid_session @@ -36,14 +36,14 @@ def valid_session end end - describe "GET new" do + describe "#new" do it "assigns a new user as @user" do get :new, {}, valid_session expect(assigns(:user)).to be_a_new(User) end end - describe "GET edit" do + describe "#edit" do it "assigns the requested user as @user" do user = User.create! valid_attributes get :edit, { id: user.to_param }, valid_session @@ -51,7 +51,7 @@ def valid_session end end - describe "POST create" do + describe "#create" do describe "with valid params" do it "creates a new User" do expect { @@ -88,7 +88,7 @@ def valid_session end end - describe "PUT update" do + describe "#update" do describe "with valid params" do it "updates the requested user" do user = User.create! valid_attributes @@ -132,7 +132,7 @@ def valid_session end end - describe "DELETE destroy" do + describe "#destroy" do it "destroys the requested user" do user = User.create! valid_attributes expect { diff --git a/app_prototype/spec/features/activation_spec.rb b/app_prototype/spec/features/activation_spec.rb index 6877303..e49e60d 100644 --- a/app_prototype/spec/features/activation_spec.rb +++ b/app_prototype/spec/features/activation_spec.rb @@ -1,16 +1,16 @@ require 'spec_helper' feature "Activation" do - scenario "a successful activation" do + scenario "with a valid token should activate the user and sign them in" do @user = create(:user) - visit "/sign_up/#{@user.activation_token}/activate" # FIXME + visit activation_path(@user.activation_token) expect(current_path).to eq sign_in_path expect(page).to have_content "Your account has been activated and you're now signed in." end - scenario "an unsuccessful activation" do - visit "/sign_up/bogus_activation_token/activate" # FIXME + scenario "with an invalid token should send the user to sign in" do + visit activation_path('BOGUS') expect(current_path).to eq sign_in_path expect(page).to have_content "Please sign in first." diff --git a/app_prototype/spec/features/password_reset_spec.rb b/app_prototype/spec/features/password_reset_spec.rb index 6cc78a3..fc71321 100644 --- a/app_prototype/spec/features/password_reset_spec.rb +++ b/app_prototype/spec/features/password_reset_spec.rb @@ -7,10 +7,10 @@ @user.activate! visit sign_in_path - click_link 'Reset forgotten password' + click_link 'Reset Password' fill_in 'Email', with: @user.email - click_button 'Reset My Password!' + click_button 'Reset My Password' @user.reload end @@ -24,15 +24,15 @@ expect(current_path).to eq sign_in_path end - scenario "the password reset email is sent with a reset url" do + scenario "sends a password reset email with url" do expect(open_email(@user.email)).to_not be_nil - expect(current_email).to have_content edit_password_reset_path(@user.reset_password_token) # FIXME + expect(current_email).to have_content reset_password_path(@user.reset_password_token) end - scenario "password can be reset" do - visit edit_password_reset_path(@user.reset_password_token) + scenario "resets the password" do + visit reset_password_path(@user.reset_password_token) - fill_in 'Password', with: 'some_good_password' + fill_in 'New Password', with: 'som3_g00d_p@ssword' click_button 'Reset Password' expect(page).to have_content "Password was successfully updated." diff --git a/app_prototype/spec/features/registration_spec.rb b/app_prototype/spec/features/registration_spec.rb index ffdb651..323b71d 100644 --- a/app_prototype/spec/features/registration_spec.rb +++ b/app_prototype/spec/features/registration_spec.rb @@ -3,52 +3,37 @@ # http://www.elabs.se/blog/51-simple-tricks-to-clean-up-your-capybara-tests feature "Registration" do - context "successful registration" do - background do - clear_emails + background do + clear_emails - @email = 'stan@example.com' + @email = 'stan@example.com' - visit sign_up_path + visit sign_up_path - within '#new_user' do - fill_in 'Email', with: @email - fill_in 'Name', with: 'Stan' - fill_in 'Password', with: 'p@ssword' - click_button 'Sign Up' - end - - @user = User.find_by_email(@email) + within '#new_user' do + fill_in 'Email', with: @email + fill_in 'Name', with: 'Stan' + fill_in 'Password', with: 'p@ssword' + click_button 'Sign Up' end - after do - @user.destroy - end + @user = User.find_by_email(@email) + end - it "creates a new user" do - expect(@user).to_not be_nil - end + after do + @user.destroy + end - it "displays a message about activation" do - expect(find('.alert')).to have_content "Thanks for signing up. Please check your email for activation instructions." - end + it "creates a new user" do + expect(@user).to_not be_nil + end - it "sends the activation email" do - expect(open_email(@email)).to_not be_nil - expect(current_email).to have_content "/sign_up/#{@user.activation_token}/activate" # FIXME - end + it "displays a message about activation" do + expect(find('.alert')).to have_content "Thanks for signing up. Please check your email for activation instructions." end - context "unsuccessful registration" do - it "redirects to new" do - visit sign_up_path - within '#new_user' do - fill_in 'Email', with: 'INVALID EMAIL' - fill_in 'Name', with: 'Stan' - fill_in 'Password', with: 'p@ssword' - click_button 'Sign Up' - end - expect(current_path).to eq sign_up_path - end + it "sends the activation email with url" do + expect(open_email(@email)).to_not be_nil + expect(current_email).to have_content activation_path(@user.activation_token) end end diff --git a/app_prototype/spec/features/sign_in_spec.rb b/app_prototype/spec/features/sign_in_spec.rb index 4039b75..7c6111e 100644 --- a/app_prototype/spec/features/sign_in_spec.rb +++ b/app_prototype/spec/features/sign_in_spec.rb @@ -10,19 +10,19 @@ @user.destroy end - scenario "Sign in with valid credentials" do + scenario "authenticates with valid credentials" do sign_in(@user.email, 'password') expect(find('.alert')).to have_content("Successfully signed in") end - scenario "Sign in with an invalid email" do + scenario "displays a generic error message with an invalid email" do sign_in('this is not valid', 'password') expect(find('.alert')).to have_content("Sign in failed") end - scenario "Sign in with an invalid password" do + scenario "displays a generic error message with an invalid password" do sign_in(@user.email, 'this is not valid') expect(find('.alert')).to have_content("Sign in failed") diff --git a/app_prototype/spec/mailers/user_mailer_spec.rb b/app_prototype/spec/mailers/user_mailer_spec.rb index 7f83351..f53632e 100644 --- a/app_prototype/spec/mailers/user_mailer_spec.rb +++ b/app_prototype/spec/mailers/user_mailer_spec.rb @@ -3,13 +3,13 @@ describe UserMailer do let(:user) { build_stubbed(:user, activation_state: 'pending', activation_token: 'ABC', reset_password_token: 'XYZ') } - describe "activation_needed_email" do + describe "#activation_needed_email" do let(:mail) { UserMailer.activation_needed_email(user) } it "renders the headers" do expect(mail.subject).to eq "Welcome to My Awesome Site!" expect(mail.to).to eq [user.email] - expect(mail.from).to eq ['notifications@example.com'] + expect(mail.from).to eq %w(notifications@example.com) end it "renders the body" do @@ -17,13 +17,13 @@ end end - describe "activation_success_email" do + describe "#activation_success_email" do let(:mail) { UserMailer.activation_success_email(user) } it "renders the headers" do expect(mail.subject).to eq "Your account has been activated!" expect(mail.to).to eq [user.email] - expect(mail.from).to eq ['notifications@example.com'] + expect(mail.from).to eq %w(notifications@example.com) end it "renders the body" do @@ -31,13 +31,13 @@ end end - describe "reset_password_email" do + describe "#reset_password_email" do let(:mail) { UserMailer.reset_password_email(user) } it "renders the headers" do expect(mail.subject).to eq "Password reset requested" expect(mail.to).to eq [user.email] - expect(mail.from).to eq ['notifications@example.com'] + expect(mail.from).to eq %w(notifications@example.com) end it "renders the body" do From 80a3ee820014b6f62dceb4103b34cbeadcae5da7 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Wed, 26 Dec 2012 10:16:38 -0800 Subject: [PATCH 17/20] Generate controller specs using the new expect().to syntax. --- .../rspec/scaffold/controller_spec.rb | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/app_prototype/lib/templates/rspec/scaffold/controller_spec.rb b/app_prototype/lib/templates/rspec/scaffold/controller_spec.rb index eaa27db..0bad134 100644 --- a/app_prototype/lib/templates/rspec/scaffold/controller_spec.rb +++ b/app_prototype/lib/templates/rspec/scaffold/controller_spec.rb @@ -22,39 +22,39 @@ def valid_session end <% unless options[:singleton] -%> - describe "GET index" do + describe "#index" do it "assigns all <%= table_name.pluralize %> as @<%= table_name.pluralize %>" do <%= file_name %> = <%= class_name %>.create! valid_attributes get :index, {}, valid_session - assigns(:<%= table_name %>).should eq([<%= file_name %>]) + expect(assigns(:<%= table_name %>)).to eq([<%= file_name %>]) end end <% end -%> - describe "GET show" do + describe "#show" do it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do <%= file_name %> = <%= class_name %>.create! valid_attributes get :show, { :id => <%= file_name %>.to_param }, valid_session - assigns(:<%= ns_file_name %>).should eq(<%= file_name %>) + expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>) end end - describe "GET new" do + describe "#new" do it "assigns a new <%= ns_file_name %> as @<%= ns_file_name %>" do get :new, {}, valid_session - assigns(:<%= ns_file_name %>).should be_a_new(<%= class_name %>) + expect(assigns(:<%= ns_file_name %>)).to be_a_new(<%= class_name %>) end end - describe "GET edit" do + describe "#edit" do it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do <%= file_name %> = <%= class_name %>.create! valid_attributes get :edit, { :id => <%= file_name %>.to_param }, valid_session - assigns(:<%= ns_file_name %>).should eq(<%= file_name %>) + expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>) end end - describe "POST create" do + describe "#create" do describe "with valid params" do it "creates a new <%= class_name %>" do expect { @@ -64,13 +64,13 @@ def valid_session it "assigns a newly created <%= ns_file_name %> as @<%= ns_file_name %>" do post :create, {:<%= ns_file_name %> => valid_attributes }, valid_session - assigns(:<%= ns_file_name %>).should be_a(<%= class_name %>) - assigns(:<%= ns_file_name %>).should be_persisted + expect(assigns(:<%= ns_file_name %>)).to be_a(<%= class_name %>) + expect(assigns(:<%= ns_file_name %>)).to be_persisted end it "redirects to the created <%= ns_file_name %>" do post :create, { :<%= ns_file_name %> => valid_attributes }, valid_session - response.should redirect_to(<%= class_name %>.last) + expect(response).to redirect_to(<%= class_name %>.last) end end @@ -79,19 +79,19 @@ def valid_session # Trigger the behavior that occurs when invalid params are submitted <%= class_name %>.any_instance.stub(:save).and_return(false) post :create, { :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session - assigns(:<%= ns_file_name %>).should be_a_new(<%= class_name %>) + expect(assigns(:<%= ns_file_name %>)).to be_a_new(<%= class_name %>) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted <%= class_name %>.any_instance.stub(:save).and_return(false) post :create, { :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session - response.should render_template("new") + expect(response).to render_template("new") end end end - describe "PUT update" do + describe "#update" do describe "with valid params" do it "updates the requested <%= ns_file_name %>" do <%= file_name %> = <%= class_name %>.create! valid_attributes @@ -106,13 +106,13 @@ def valid_session it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do <%= file_name %> = <%= class_name %>.create! valid_attributes put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => valid_attributes }, valid_session - assigns(:<%= ns_file_name %>).should eq(<%= file_name %>) + expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>) end it "redirects to the <%= ns_file_name %>" do <%= file_name %> = <%= class_name %>.create! valid_attributes put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => valid_attributes }, valid_session - response.should redirect_to(<%= file_name %>) + expect(response).to redirect_to(<%= file_name %>) end end @@ -122,7 +122,7 @@ def valid_session # Trigger the behavior that occurs when invalid params are submitted <%= class_name %>.any_instance.stub(:save).and_return(false) put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session - assigns(:<%= ns_file_name %>).should eq(<%= file_name %>) + expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>) end it "re-renders the 'edit' template" do @@ -130,12 +130,12 @@ def valid_session # Trigger the behavior that occurs when invalid params are submitted <%= class_name %>.any_instance.stub(:save).and_return(false) put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session - response.should render_template("edit") + expect(response).to render_template("edit") end end end - describe "DELETE destroy" do + describe "#destroy" do it "destroys the requested <%= ns_file_name %>" do <%= file_name %> = <%= class_name %>.create! valid_attributes expect { @@ -146,7 +146,7 @@ def valid_session it "redirects to the <%= table_name %> list" do <%= file_name %> = <%= class_name %>.create! valid_attributes delete :destroy, { :id => <%= file_name %>.to_param }, valid_session - response.should redirect_to(<%= index_helper %>_url) + expect(response).to redirect_to(<%= index_helper %>_url) end end From 42e2640d538b7df176b9a01ad6e0ade5ee626fbb Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Wed, 26 Dec 2012 10:16:55 -0800 Subject: [PATCH 18/20] Don't generate request specs by default. --- app_prototype/config/application.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app_prototype/config/application.rb b/app_prototype/config/application.rb index a3b8955..5fc00b7 100644 --- a/app_prototype/config/application.rb +++ b/app_prototype/config/application.rb @@ -22,6 +22,7 @@ class Application < Rails::Application #generate.helper false generate.routing_specs false #generate.view_specs false + generate.request_specs false end # Settings in config/environments/* take precedence over those specified here. From da13bde933b086c0d29b4551eb4f3b7bcc69d943 Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Wed, 26 Dec 2012 17:43:32 -0800 Subject: [PATCH 19/20] Keep the default factory_girl info comment. --- app_prototype/spec/factories/users.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app_prototype/spec/factories/users.rb b/app_prototype/spec/factories/users.rb index da80d76..cfa376e 100644 --- a/app_prototype/spec/factories/users.rb +++ b/app_prototype/spec/factories/users.rb @@ -1,3 +1,5 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + FactoryGirl.define do factory :user do sequence(:email) { |n| "person#{n}@example.com" } From f04ff3deb066153bfda78ca85582c98facd750ce Mon Sep 17 00:00:00 2001 From: Christian Nelson Date: Wed, 26 Dec 2012 20:09:56 -0800 Subject: [PATCH 20/20] Update the change log. --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index b6c25f9..a5ecf83 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,5 +6,7 @@ * Added guard-livereload to Guardfile. * Gemfile, rvmrc, rbenv files configured with the ruby version used to generate the app. * Better specs for auth flows (register, password reset, sign in) (~98% coverage). +* Use the new rspec expect(...).to syntax ([more info](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax)). * Hardcode 1.9.3-p327 so that app_prototype is executable without futzing. * Consistent hostnames across environments. +* Use mailcatcher when it's running locally ([more info](http://www.mikeperham.com/2012/12/09/12-gems-of-christmas-4-mailcatcher-and-mail_view/)).