Skip to content
This repository

Specs for baked out functionality (redux) #51

Merged
merged 20 commits into from over 1 year ago

2 participants

Christian Nelson Hugo Melo
Christian Nelson

No description provided.

Christian Nelson christiannelson merged commit c62f473 into from
Christian Nelson christiannelson closed this
Christian Nelson christiannelson deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 38 changed files with 273 additions and 171 deletions. Show diff stats Hide diff stats

  1. 7  CHANGES.md
  2. 3  app_prototype/Gemfile
  3. 14  app_prototype/app/controllers/password_resets_controller.rb
  4. 2  app_prototype/app/controllers/registrations_controller.rb
  5. 7  app_prototype/app/models/user.rb
  6. 2  app_prototype/app/views/layouts/application.html.slim
  7. 5  app_prototype/app/views/password_resets/edit.html.slim
  8. 4  app_prototype/app/views/password_resets/new.html.slim
  9. 3  app_prototype/app/views/registrations/new.html.slim
  10. 2  app_prototype/app/views/user_mailer/activation_needed_email.html.erb
  11. 2  app_prototype/app/views/user_mailer/activation_needed_email.text.erb
  12. 2  app_prototype/app/views/user_mailer/reset_password_email.html.erb
  13. 2  app_prototype/app/views/user_mailer/reset_password_email.text.erb
  14. 4  app_prototype/app/views/user_sessions/new.html.slim
  15. 2  app_prototype/app/views/users/show.html.slim
  16. 1  app_prototype/config/application.rb
  17. 11  app_prototype/config/initializers/email.rb
  18. 14  app_prototype/config/routes.rb
  19. 42  app_prototype/lib/templates/rspec/scaffold/controller_spec.rb
  20. 4  app_prototype/public/index.html
  21. 14  app_prototype/spec/controllers/user_sessions_controller_spec.rb
  22. 44  app_prototype/spec/controllers/users_controller_spec.rb
  23. 2  app_prototype/spec/factories/users.rb
  24. 18  app_prototype/spec/features/activation_spec.rb
  25. 41  app_prototype/spec/features/password_reset_spec.rb
  26. 39  app_prototype/spec/features/registration_spec.rb
  27. 10  app_prototype/spec/features/registrations_spec.rb
  28. 30  app_prototype/spec/features/sign_in_spec.rb
  29. 32  app_prototype/spec/features/user_sessions_spec.rb
  30. 15  app_prototype/spec/helpers/users_helper_spec.rb
  31. 30  app_prototype/spec/mailers/user_mailer_spec.rb
  32. 19  app_prototype/spec/models/user_spec.rb
  33. 5  app_prototype/spec/spec_helper.rb
  34. 4  app_prototype/spec/support/user_sessions_feature_helper.rb
  35. 1  app_prototype/spec/views/users/edit.html.slim_spec.rb
  36. 1  app_prototype/spec/views/users/index.html.slim_spec.rb
  37. 1  app_prototype/spec/views/users/new.html.slim_spec.rb
  38. 5  app_prototype/spec/views/users/show.html.slim_spec.rb
7  CHANGES.md
Source Rendered
@@ -5,5 +5,8 @@
5 5
 * Basic usage information.
6 6
 * Added guard-livereload to Guardfile.
7 7
 * Gemfile, rvmrc, rbenv files configured with the ruby version used to generate the app.
8  
-* Better specs for auth flows (register, password reset, sign in).
9  
-* Hardcode 1.9.3-p327 so that app_prototype is executable without futzing.
  8
+* Better specs for auth flows (register, password reset, sign in) (~98% coverage).
  9
+* Use the new rspec expect(...).to syntax ([more info](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax)).
  10
+* Hardcode 1.9.3-p327 so that app_prototype is executable without futzing.
  11
+* Consistent hostnames across environments.
  12
+* 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/)).
3  app_prototype/Gemfile
@@ -26,6 +26,7 @@ end
26 26
 group :test, :development do
27 27
   gem 'rspec-rails', '~> 2.12.0'
28 28
   gem 'capybara', github: 'jnicklas/capybara' # Switch from github once issue #882 is resolved.
  29
+  gem 'capybara-email'
29 30
   gem 'factory_girl_rails'
30 31
   gem 'jasminerice'
31 32
   gem 'timecop'
@@ -34,6 +35,8 @@ end
34 35
 
35 36
 group :development do
36 37
   gem 'foreman'
  38
+  gem 'launchy'
  39
+  gem 'mailcatcher'
37 40
   gem 'guard'
38 41
   gem 'guard-rspec'
39 42
   gem 'guard-jasmine'
14  app_prototype/app/controllers/password_resets_controller.rb
... ...
@@ -1,32 +1,30 @@
1 1
 class PasswordResetsController < ApplicationController
2 2
 
3 3
   skip_before_filter :require_login
  4
+  skip_authorization_check
4 5
 
5 6
   def create
6 7
     @user = User.find_by_email(params[:email])
7  
-    
  8
+
8 9
     # Send an email to the user with instructions on how to reset their password.
9 10
     @user.deliver_reset_password_instructions! if @user
10  
-    
  11
+
11 12
     # Tell the user instructions have been sent whether or not email was found.
12 13
     # This is to not leak information to attackers about which emails exist in the system.
13 14
     redirect_to sign_in_path, notice: "Password reset instructions have been sent to your email."
14 15
   end
15 16
 
16 17
   def edit
17  
-    @user = User.load_from_reset_password_token(params[:id])
18  
-    @token = params[:id]
  18
+    @token = params[:token]
  19
+    @user = User.load_from_reset_password_token(@token)
19 20
     not_authenticated if !@user
20 21
   end
21  
-  
  22
+
22 23
   def update
23 24
     @token = params[:token] # needed to render the form again in case of error
24 25
     @user = User.load_from_reset_password_token(@token)
25 26
     not_authenticated if !@user
26 27
 
27  
-    # Makes the password confirmation validation work.
28  
-    @user.password_confirmation = params[:user][:password_confirmation]
29  
-
30 28
     # Clear the temporary token and update the password.
31 29
     if @user.change_password!(params[:user][:password])
32 30
       redirect_to sign_in_path, notice: "Password was successfully updated."
2  app_prototype/app/controllers/registrations_controller.rb
@@ -7,7 +7,7 @@ def new
7 7
   end
8 8
 
9 9
   def create
10  
-    @user = User.new(params[:user])
  10
+    @user = User.new(params[:user]) # TODO Safe attributes
11 11
 
12 12
     if @user.save
13 13
       redirect_to sign_in_path, notice: "Thanks for signing up. Please check your email for activation instructions."
7  app_prototype/app/models/user.rb
@@ -4,14 +4,17 @@ class User < ActiveRecord::Base
4 4
 
5 5
   authenticates_with_sorcery!
6 6
 
  7
+  validates :name,
  8
+            length: { maximum: 30 }
  9
+
7 10
   validates :email,
8 11
             presence: true,
9  
-            email: true
  12
+            email: true,
  13
+            uniqueness: true
10 14
 
11 15
   validates :password,
12 16
             presence: true,
13 17
             length: { minimum: 6 },
14  
-            confirmation: true,
15 18
             if: :password
16 19
 
17 20
 end
2  app_prototype/app/views/layouts/application.html.slim
@@ -22,7 +22,7 @@ html
22 22
                     ul.dropdown-menu
23 23
                       li= link_to 'Sign Out', sign_out_path
24 24
                 - else
25  
-                  li= link_to 'Sign in', sign_in_path
  25
+                  li= link_to 'Sign In', sign_in_path
26 26
 
27 27
         - flash.each do |name, msg|
28 28
           = content_tag :div, raw(msg), class: "alert #{alert_class(name)}"
5  app_prototype/app/views/password_resets/edit.html.slim
... ...
@@ -1,13 +1,12 @@
1 1
 .page-header
2 2
   h1 Reset Your Password
3 3
 
4  
-= simple_form_for(@user, url: password_reset_path(@user)) do |f|
  4
+= simple_form_for(@user, url: reset_password_path(@user)) do |f|
5 5
   = f.error_notification
6 6
 
7 7
   .form-inputs
8 8
     = f.input :email, disabled: true
9  
-    = f.input :password, required: true, autofocus: true
10  
-    = f.input :password_confirmation, required: true
  9
+    = f.input :password, label: 'New Password', required: true, autofocus: true
11 10
     = hidden_field_tag :token, @token
12 11
 
13 12
   .form-actions
4  app_prototype/app/views/password_resets/new.html.slim
... ...
@@ -1,11 +1,11 @@
1 1
 .page-header
2 2
   h1 Password Reset
3 3
 
4  
-= form_tag(password_resets_path, method: :post) do
  4
+= form_tag(forgotten_password_path, method: :post) do
5 5
   .control-group
6 6
     = label_tag :email, nil, class: 'control-label'
7 7
     .controls
8 8
       = text_field_tag :email, nil, placeholder: 'joe@example.com'
9 9
 
10 10
   .form-actions
11  
-    = submit_tag "Reset my password!", class: 'btn btn-primary'
  11
+    = submit_tag 'Reset My Password', class: 'btn btn-primary'
3  app_prototype/app/views/registrations/new.html.slim
@@ -8,7 +8,6 @@
8 8
     = f.input :name, autofocus: true
9 9
     = f.input :email
10 10
     = f.input :password
11  
-    = f.input :password_confirmation
12 11
 
13 12
   .form-actions
14  
-    = f.button :submit, 'Sign up', class: 'btn btn-primary'
  13
+    = f.button :submit, 'Sign Up', class: 'btn btn-primary'
2  app_prototype/app/views/user_mailer/activation_needed_email.html.erb
@@ -10,7 +10,7 @@
10 10
       your username is: <%= @user.email %>.<br/>
11 11
     </p>
12 12
     <p>
13  
-      To login to the site, just follow this link: <%= link_to activate_registration_url(@user.activation_token), activate_registration_url(@user.activation_token) %>.
  13
+      To login to the site, just follow this link: <%= link_to activation_url(@user.activation_token), activation_url(@user.activation_token) %>.
14 14
     </p>
15 15
     <p>Thanks for joining and have a great day!</p>
16 16
   </body>
2  app_prototype/app/views/user_mailer/activation_needed_email.text.erb
@@ -4,6 +4,6 @@ Welcome to example.com, <%= @user.email %>
4 4
 You have successfully signed up to example.com,
5 5
 your username is: <%= @user.email %>.
6 6
  
7  
-To login to the site, just follow this link: <%= activate_registration_url(@user.activation_token) %>
  7
+To login to the site, just follow this link: <%= activation_url(@user.activation_token) %>
8 8
  
9 9
 Thanks for joining and have a great day!
2  app_prototype/app/views/user_mailer/reset_password_email.html.erb
@@ -9,7 +9,7 @@
9 9
       You have requested to reset your password.
10 10
     </p>
11 11
     <p>
12  
-      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) %>.
  12
+      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) %>.
13 13
     </p>
14 14
     <p>Have a great day!</p>
15 15
   </body>
2  app_prototype/app/views/user_mailer/reset_password_email.text.erb
@@ -3,6 +3,6 @@ Hello, <%= @user.email %>
3 3
  
4 4
 You have requested to reset your password.
5 5
  
6  
-To choose a new password, just follow this link: edit_password_reset_url(@user.reset_password_token)
  6
+To choose a new password, just follow this link: <%= reset_password_url(@user.reset_password_token) %>
7 7
  
8 8
 Have a great day!
4  app_prototype/app/views/user_sessions/new.html.slim
@@ -6,8 +6,8 @@
6 6
     = f.input :password
7 7
 
8 8
   ul.unstyled
9  
-    li= link_to 'Sign up', sign_up_path
10  
-    li= link_to 'Reset forgotten password', new_password_reset_path
  9
+    li= link_to 'Sign Up', sign_up_path
  10
+    li= link_to 'Reset Password', forgotten_password_path
11 11
 
12 12
   .form-actions
13 13
     = f.button :submit, 'Sign In', class: 'btn btn-primary'
2  app_prototype/app/views/users/show.html.slim
@@ -14,4 +14,4 @@ dl
14 14
 
15 15
 - if can?(:destroy, @user)
16 16
   '
17  
-  = link_to'Destroy', user_path(@user), method: :delete, data: { confirm: "Are you sure?" }, class: 'btn btn-danger'
  17
+  = link_to 'Destroy', user_path(@user), method: :delete, data: { confirm: "Are you sure?" }, class: 'btn btn-danger'
1  app_prototype/config/application.rb
@@ -22,6 +22,7 @@ class Application < Rails::Application
22 22
       #generate.helper        false
23 23
       generate.routing_specs false
24 24
       #generate.view_specs    false
  25
+      generate.request_specs false
25 26
     end
26 27
 
27 28
     # Settings in config/environments/* take precedence over those specified here.
11  app_prototype/config/initializers/email.rb
... ...
@@ -0,0 +1,11 @@
  1
+# http://www.mikeperham.com/2012/12/09/12-gems-of-christmas-4-mailcatcher-and-mail_view/
  2
+
  3
+begin
  4
+  sock = TCPSocket.new('localhost', 1025)
  5
+  sock.close
  6
+  catcher = true
  7
+rescue
  8
+  catcher = false
  9
+end
  10
+
  11
+ActionMailer::Base.smtp_settings = (Rails.env.development? && catcher) ? { host: 'localhost', port: '1025', } : {}
14  app_prototype/config/routes.rb
@@ -5,12 +5,16 @@
5 5
 
6 6
   resources :user_sessions, only: [:new, :create, :destroy]
7 7
 
8  
-  resources :registrations, only: [:new, :create, :activate]
9  
-  match 'sign_up' => 'registrations#new',                      via: :get
10  
-  match 'sign_up' => 'registrations#create',                   via: :post
11  
-  match 'sign_up/:token/activate' => 'registrations#activate', via: :get, as: :activate_registration
  8
+  #resources :registrations, only: [:new, :create, :activate]
  9
+  match 'sign_up' => 'registrations#new',              via: :get,  as: :sign_up
  10
+  match 'sign_up' => 'registrations#create',           via: :post, as: :sign_up
  11
+  match 'activate/:token' => 'registrations#activate', via: :get,  as: :activation
12 12
 
13  
-  resources :password_resets, only: [:new, :create, :edit, :update]
  13
+  #resources :password_resets, only: [:new, :create, :edit, :update]
  14
+  match 'forgotten_password' => 'password_resets#new',     via: :get,  as: :forgotten_password
  15
+  match 'forgotten_password' => 'password_resets#create',  via: :post, as: :forgotten_password
  16
+  match 'reset_password/:token' => 'password_resets#edit', via: :get,  as: :reset_password
  17
+  match 'reset_password/:id' => 'password_resets#update',  via: :put
14 18
 
15 19
   resources :users
16 20
 
42  app_prototype/lib/templates/rspec/scaffold/controller_spec.rb
@@ -22,39 +22,39 @@ def valid_session
22 22
   end
23 23
 
24 24
 <% unless options[:singleton] -%>
25  
-  describe "GET index" do
  25
+  describe "#index" do
26 26
     it "assigns all <%= table_name.pluralize %> as @<%= table_name.pluralize %>" do
27 27
       <%= file_name %> = <%= class_name %>.create! valid_attributes
28 28
       get :index, {}, valid_session
29  
-      assigns(:<%= table_name %>).should eq([<%= file_name %>])
  29
+      expect(assigns(:<%= table_name %>)).to eq([<%= file_name %>])
30 30
     end
31 31
   end
32 32
 
33 33
 <% end -%>
34  
-  describe "GET show" do
  34
+  describe "#show" do
35 35
     it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
36 36
       <%= file_name %> = <%= class_name %>.create! valid_attributes
37 37
       get :show, { :id => <%= file_name %>.to_param }, valid_session
38  
-      assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
  38
+      expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
39 39
     end
40 40
   end
41 41
 
42  
-  describe "GET new" do
  42
+  describe "#new" do
43 43
     it "assigns a new <%= ns_file_name %> as @<%= ns_file_name %>" do
44 44
       get :new, {}, valid_session
45  
-      assigns(:<%= ns_file_name %>).should be_a_new(<%= class_name %>)
  45
+      expect(assigns(:<%= ns_file_name %>)).to be_a_new(<%= class_name %>)
46 46
     end
47 47
   end
48 48
 
49  
-  describe "GET edit" do
  49
+  describe "#edit" do
50 50
     it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
51 51
       <%= file_name %> = <%= class_name %>.create! valid_attributes
52 52
       get :edit, { :id => <%= file_name %>.to_param }, valid_session
53  
-      assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
  53
+      expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
54 54
     end
55 55
   end
56 56
 
57  
-  describe "POST create" do
  57
+  describe "#create" do
58 58
     describe "with valid params" do
59 59
       it "creates a new <%= class_name %>" do
60 60
         expect {
@@ -64,13 +64,13 @@ def valid_session
64 64
 
65 65
       it "assigns a newly created <%= ns_file_name %> as @<%= ns_file_name %>" do
66 66
         post :create, {:<%= ns_file_name %> => valid_attributes }, valid_session
67  
-        assigns(:<%= ns_file_name %>).should be_a(<%= class_name %>)
68  
-        assigns(:<%= ns_file_name %>).should be_persisted
  67
+        expect(assigns(:<%= ns_file_name %>)).to be_a(<%= class_name %>)
  68
+        expect(assigns(:<%= ns_file_name %>)).to be_persisted
69 69
       end
70 70
 
71 71
       it "redirects to the created <%= ns_file_name %>" do
72 72
         post :create, { :<%= ns_file_name %> => valid_attributes }, valid_session
73  
-        response.should redirect_to(<%= class_name %>.last)
  73
+        expect(response).to redirect_to(<%= class_name %>.last)
74 74
       end
75 75
     end
76 76
 
@@ -79,19 +79,19 @@ def valid_session
79 79
         # Trigger the behavior that occurs when invalid params are submitted
80 80
         <%= class_name %>.any_instance.stub(:save).and_return(false)
81 81
         post :create, { :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session
82  
-        assigns(:<%= ns_file_name %>).should be_a_new(<%= class_name %>)
  82
+        expect(assigns(:<%= ns_file_name %>)).to be_a_new(<%= class_name %>)
83 83
       end
84 84
 
85 85
       it "re-renders the 'new' template" do
86 86
         # Trigger the behavior that occurs when invalid params are submitted
87 87
         <%= class_name %>.any_instance.stub(:save).and_return(false)
88 88
         post :create, { :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session
89  
-        response.should render_template("new")
  89
+        expect(response).to render_template("new")
90 90
       end
91 91
     end
92 92
   end
93 93
 
94  
-  describe "PUT update" do
  94
+  describe "#update" do
95 95
     describe "with valid params" do
96 96
       it "updates the requested <%= ns_file_name %>" do
97 97
         <%= file_name %> = <%= class_name %>.create! valid_attributes
@@ -106,13 +106,13 @@ def valid_session
106 106
       it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
107 107
         <%= file_name %> = <%= class_name %>.create! valid_attributes
108 108
         put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => valid_attributes }, valid_session
109  
-        assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
  109
+        expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
110 110
       end
111 111
 
112 112
       it "redirects to the <%= ns_file_name %>" do
113 113
         <%= file_name %> = <%= class_name %>.create! valid_attributes
114 114
         put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => valid_attributes }, valid_session
115  
-        response.should redirect_to(<%= file_name %>)
  115
+        expect(response).to redirect_to(<%= file_name %>)
116 116
       end
117 117
     end
118 118
 
@@ -122,7 +122,7 @@ def valid_session
122 122
         # Trigger the behavior that occurs when invalid params are submitted
123 123
         <%= class_name %>.any_instance.stub(:save).and_return(false)
124 124
         put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session
125  
-        assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
  125
+        expect(assigns(:<%= ns_file_name %>)).to eq(<%= file_name %>)
126 126
       end
127 127
 
128 128
       it "re-renders the 'edit' template" do
@@ -130,12 +130,12 @@ def valid_session
130 130
         # Trigger the behavior that occurs when invalid params are submitted
131 131
         <%= class_name %>.any_instance.stub(:save).and_return(false)
132 132
         put :update, { :id => <%= file_name %>.to_param, :<%= ns_file_name %> => <%= formatted_hash(example_invalid_attributes) %> }, valid_session
133  
-        response.should render_template("edit")
  133
+        expect(response).to render_template("edit")
134 134
       end
135 135
     end
136 136
   end
137 137
 
138  
-  describe "DELETE destroy" do
  138
+  describe "#destroy" do
139 139
     it "destroys the requested <%= ns_file_name %>" do
140 140
       <%= file_name %> = <%= class_name %>.create! valid_attributes
141 141
       expect {
@@ -146,7 +146,7 @@ def valid_session
146 146
     it "redirects to the <%= table_name %> list" do
147 147
       <%= file_name %> = <%= class_name %>.create! valid_attributes
148 148
       delete :destroy, { :id => <%= file_name %>.to_param }, valid_session
149  
-      response.should redirect_to(<%= index_helper %>_url)
  149
+      expect(response).to redirect_to(<%= index_helper %>_url)
150 150
     end
151 151
   end
152 152
 
4  app_prototype/public/index.html
@@ -15,7 +15,7 @@
15 15
           <div class="container">
16 16
             <div class="brand">Project_prototype</div>
17 17
             <ul class="nav pull-right">
18  
-              <li><a href="/sign_in">Sign in</a></li>
  18
+              <li><a href="/sign_in">Sign In</a></li>
19 19
             </ul>
20 20
           </div>
21 21
         </nav>
@@ -29,7 +29,7 @@
29 29
         <p>Custom generator templates create views that are bootstrap compatible and specs that are factory-aware and follow best practices.</p>
30 30
         <p>This application also includes authentication and an example of authorization (sign in as user@example.com / password).</p>
31 31
 
32  
-        <p><a class="btn btn-primary btn-large" href="sign_in">Sign in</a></p>
  32
+        <p><a class="btn btn-primary btn-large" href="sign_in">Sign In</a></p>
33 33
       </div>
34 34
     </div>
35 35
     <div class="push"></div>
14  app_prototype/spec/controllers/user_sessions_controller_spec.rb
@@ -2,19 +2,19 @@
2 2
 
3 3
 describe UserSessionsController do
4 4
 
5  
-  describe "GET new" do
  5
+  describe "#new" do
6 6
     it "assigns a new user as @user" do
7 7
       get :new
8  
-      assigns(:user_session).should_not be_nil
  8
+      expect(assigns(:user_session)).to_not be_nil
9 9
     end
10 10
   end
11 11
 
12  
-  describe "POST create" do
  12
+  describe "#create" do
13 13
     describe "with valid params" do
14 14
       it "redirect to the target page" do
15 15
         subject.stub(:login) { build_stubbed :user }
16 16
         post :create, { user_session: { email: 'valid', password: 'valid' } }, { return_to_url: 'url' }
17  
-        response.should redirect_to('url')
  17
+        expect(response).to redirect_to('url')
18 18
       end
19 19
     end
20 20
 
@@ -22,12 +22,12 @@
22 22
       it "re-renders the 'new' template" do
23 23
         subject.stub(:login) { nil }
24 24
         post :create, { user_session: { email: 'invalid', password: 'invalid' } }
25  
-        response.should render_template('new')
  25
+        expect(response).to render_template('new')
26 26
       end
27 27
     end
28 28
   end
29 29
 
30  
-  describe "DELETE destroy" do
  30
+  describe "#destroy" do
31 31
     it "destroys the requested user session" do
32 32
       subject.should_receive(:logout)
33 33
       delete :destroy
@@ -35,7 +35,7 @@
35 35
 
36 36
     it "redirects to the sign in page" do
37 37
       delete :destroy
38  
-      response.should redirect_to(sign_in_url)
  38
+      expect(response).to redirect_to(sign_in_url)
39 39
     end
40 40
   end
41 41
 
44  app_prototype/spec/controllers/users_controller_spec.rb
@@ -9,7 +9,7 @@ def valid_attributes
9 9
     attributes_for :user
10 10
   end
11 11
 
12  
-  # This should return the minimal set of values that should be in the session
  12
+  # This returns the minimal set of values that should be in the session
13 13
   # in order to pass any filters (e.g. authentication) defined in
14 14
   # UsersController. Be sure to keep this updated too.
15 15
   def valid_session
@@ -20,38 +20,38 @@ def valid_session
20 20
     login_user build :admin
21 21
   end
22 22
 
23  
-  describe "GET index" do
  23
+  describe "#index" do
24 24
     it "assigns all users as @users" do
25 25
       user = User.create! valid_attributes
26 26
       get :index, {}, valid_session
27  
-      assigns(:users).should eq([user])
  27
+      expect(assigns(:users)).to eq([user])
28 28
     end
29 29
   end
30 30
 
31  
-  describe "GET show" do
  31
+  describe "#show" do
32 32
     it "assigns the requested user as @user" do
33 33
       user = User.create! valid_attributes
34 34
       get :show, { id: user.to_param }, valid_session
35  
-      assigns(:user).should eq(user)
  35
+      expect(assigns(:user)).to eq(user)
36 36
     end
37 37
   end
38 38
 
39  
-  describe "GET new" do
  39
+  describe "#new" do
40 40
     it "assigns a new user as @user" do
41 41
       get :new, {}, valid_session
42  
-      assigns(:user).should be_a_new(User)
  42
+      expect(assigns(:user)).to be_a_new(User)
43 43
     end
44 44
   end
45 45
 
46  
-  describe "GET edit" do
  46
+  describe "#edit" do
47 47
     it "assigns the requested user as @user" do
48 48
       user = User.create! valid_attributes
49 49
       get :edit, { id: user.to_param }, valid_session
50  
-      assigns(:user).should eq(user)
  50
+      expect(assigns(:user)).to eq(user)
51 51
     end
52 52
   end
53 53
 
54  
-  describe "POST create" do
  54
+  describe "#create" do
55 55
     describe "with valid params" do
56 56
       it "creates a new User" do
57 57
         expect {
@@ -61,13 +61,13 @@ def valid_session
61 61
 
62 62
       it "assigns a newly created user as @user" do
63 63
         post :create, {user: valid_attributes }, valid_session
64  
-        assigns(:user).should be_a(User)
65  
-        assigns(:user).should be_persisted
  64
+        expect(assigns(:user)).to be_a(User)
  65
+        expect(assigns(:user)).to be_persisted
66 66
       end
67 67
 
68 68
       it "redirects to the created user" do
69 69
         post :create, { user: valid_attributes }, valid_session
70  
-        response.should redirect_to(User.last)
  70
+        expect(response).to redirect_to(User.last)
71 71
       end
72 72
     end
73 73
 
@@ -76,19 +76,19 @@ def valid_session
76 76
         # Trigger the behavior that occurs when invalid params are submitted
77 77
         User.any_instance.stub(:save).and_return(false)
78 78
         post :create, { user: { "email" => "invalid value" } }, valid_session
79  
-        assigns(:user).should be_a_new(User)
  79
+        expect(assigns(:user)).to be_a_new(User)
80 80
       end
81 81
 
82 82
       it "re-renders the 'new' template" do
83 83
         # Trigger the behavior that occurs when invalid params are submitted
84 84
         User.any_instance.stub(:save).and_return(false)
85 85
         post :create, { user: { "email" => "invalid value" } }, valid_session
86  
-        response.should render_template("new")
  86
+        expect(response).to render_template("new")
87 87
       end
88 88
     end
89 89
   end
90 90
 
91  
-  describe "PUT update" do
  91
+  describe "#update" do
92 92
     describe "with valid params" do
93 93
       it "updates the requested user" do
94 94
         user = User.create! valid_attributes
@@ -103,13 +103,13 @@ def valid_session
103 103
       it "assigns the requested user as @user" do
104 104
         user = User.create! valid_attributes
105 105
         put :update, { id: user.to_param, user: valid_attributes }, valid_session
106  
-        assigns(:user).should eq(user)
  106
+        expect(assigns(:user)).to eq(user)
107 107
       end
108 108
 
109 109
       it "redirects to the user" do
110 110
         user = User.create! valid_attributes
111 111
         put :update, { id: user.to_param, user: valid_attributes }, valid_session
112  
-        response.should redirect_to(user)
  112
+        expect(response).to redirect_to(user)
113 113
       end
114 114
     end
115 115
 
@@ -119,7 +119,7 @@ def valid_session
119 119
         # Trigger the behavior that occurs when invalid params are submitted
120 120
         User.any_instance.stub(:save).and_return(false)
121 121
         put :update, { id: user.to_param, user: { "email" => "invalid value" } }, valid_session
122  
-        assigns(:user).should eq(user)
  122
+        expect(assigns(:user)).to eq(user)
123 123
       end
124 124
 
125 125
       it "re-renders the 'edit' template" do
@@ -127,12 +127,12 @@ def valid_session
127 127
         # Trigger the behavior that occurs when invalid params are submitted
128 128
         User.any_instance.stub(:save).and_return(false)
129 129
         put :update, { id: user.to_param, user: { "email" => "invalid value" } }, valid_session
130  
-        response.should render_template("edit")
  130
+        expect(response).to render_template("edit")
131 131
       end
132 132
     end
133 133
   end
134 134
 
135  
-  describe "DELETE destroy" do
  135
+  describe "#destroy" do
136 136
     it "destroys the requested user" do
137 137
       user = User.create! valid_attributes
138 138
       expect {
@@ -143,7 +143,7 @@ def valid_session
143 143
     it "redirects to the users list" do
144 144
       user = User.create! valid_attributes
145 145
       delete :destroy, { id: user.to_param }, valid_session
146  
-      response.should redirect_to(users_url)
  146
+      expect(response).to redirect_to(users_url)
147 147
     end
148 148
   end
149 149
 
2  app_prototype/spec/factories/users.rb
... ...
@@ -1,3 +1,5 @@
  1
+# Read about factories at https://github.com/thoughtbot/factory_girl
  2
+
1 3
 FactoryGirl.define do
2 4
   factory :user do
3 5
     sequence(:email) { |n| "person#{n}@example.com" }
18  app_prototype/spec/features/activation_spec.rb
... ...
@@ -0,0 +1,18 @@
  1
+require 'spec_helper'
  2
+
  3
+feature "Activation" do
  4
+  scenario "with a valid token should activate the user and sign them in" do
  5
+    @user = create(:user)
  6
+    visit activation_path(@user.activation_token)
  7
+
  8
+    expect(current_path).to eq sign_in_path
  9
+    expect(page).to have_content "Your account has been activated and you're now signed in."
  10
+  end
  11
+
  12
+  scenario "with an invalid token should send the user to sign in" do
  13
+    visit activation_path('BOGUS')
  14
+
  15
+    expect(current_path).to eq sign_in_path
  16
+    expect(page).to have_content "Please sign in first."
  17
+  end
  18
+end
41  app_prototype/spec/features/password_reset_spec.rb
... ...
@@ -0,0 +1,41 @@
  1
+require 'spec_helper'
  2
+
  3
+feature "Password Reset" do
  4
+  background do
  5
+    clear_emails
  6
+    @user = create(:user)
  7
+    @user.activate!
  8
+
  9
+    visit sign_in_path
  10
+    click_link 'Reset Password'
  11
+
  12
+    fill_in 'Email', with: @user.email
  13
+    click_button 'Reset My Password'
  14
+
  15
+    @user.reload
  16
+  end
  17
+
  18
+  after do
  19
+    @user.destroy
  20
+  end
  21
+
  22
+  scenario "displays a message about the password reset email" do
  23
+    expect(page).to have_content "Password reset instructions have been sent to your email."
  24
+    expect(current_path).to eq sign_in_path
  25
+  end
  26
+
  27
+  scenario "sends a password reset email with url" do
  28
+    expect(open_email(@user.email)).to_not be_nil
  29
+    expect(current_email).to have_content reset_password_path(@user.reset_password_token)
  30
+  end
  31
+
  32
+  scenario "resets the password" do
  33
+    visit reset_password_path(@user.reset_password_token)
  34
+
  35
+    fill_in 'New Password', with: 'som3_g00d_p@ssword'
  36
+    click_button 'Reset Password'
  37
+
  38
+    expect(page).to have_content "Password was successfully updated."
  39
+    expect(current_path).to eq sign_in_path
  40
+  end
  41
+end
39  app_prototype/spec/features/registration_spec.rb
... ...
@@ -0,0 +1,39 @@
  1
+require 'spec_helper'
  2
+
  3
+# http://www.elabs.se/blog/51-simple-tricks-to-clean-up-your-capybara-tests
  4
+
  5
+feature "Registration" do
  6
+  background do
  7
+    clear_emails
  8
+
  9
+    @email = 'stan@example.com'
  10
+
  11
+    visit sign_up_path
  12
+
  13
+    within '#new_user' do
  14
+      fill_in 'Email',    with: @email
  15
+      fill_in 'Name',     with: 'Stan'
  16
+      fill_in 'Password', with: 'p@ssword'
  17
+      click_button 'Sign Up'
  18
+    end
  19
+
  20
+    @user = User.find_by_email(@email)
  21
+  end
  22
+
  23
+  after do
  24
+    @user.destroy
  25
+  end
  26
+
  27
+  it "creates a new user" do
  28
+    expect(@user).to_not be_nil
  29
+  end
  30
+
  31
+  it "displays a message about activation" do
  32
+    expect(find('.alert')).to have_content "Thanks for signing up. Please check your email for activation instructions."
  33
+  end
  34
+
  35
+  it "sends the activation email with url" do
  36
+    expect(open_email(@email)).to_not be_nil
  37
+    expect(current_email).to have_content activation_path(@user.activation_token)
  38
+  end
  39
+end
10  app_prototype/spec/features/registrations_spec.rb
... ...
@@ -1,10 +0,0 @@
1  
-require 'spec_helper'
2  
-
3  
-# http://www.elabs.se/blog/51-simple-tricks-to-clean-up-your-capybara-tests
4  
-
5  
-feature "Registrations" do
6  
-
7  
-  scenario "sign up" do
8  
-    visit sign_up_path
9  
-  end
10  
-end
30  app_prototype/spec/features/sign_in_spec.rb
... ...
@@ -0,0 +1,30 @@
  1
+require 'spec_helper'
  2
+
  3
+feature "Sign In" do
  4
+  background(:all) do
  5
+    @user = create(:user)
  6
+    @user.activate!
  7
+  end
  8
+
  9
+  after(:all) do
  10
+    @user.destroy
  11
+  end
  12
+
  13
+  scenario "authenticates with valid credentials" do
  14
+    sign_in(@user.email, 'password')
  15
+
  16
+    expect(find('.alert')).to have_content("Successfully signed in")
  17
+  end
  18
+
  19
+  scenario "displays a generic error message with an invalid email" do
  20
+    sign_in('this is not valid', 'password')
  21
+
  22
+    expect(find('.alert')).to have_content("Sign in failed")
  23
+  end
  24
+
  25
+  scenario "displays a generic error message with an invalid password" do
  26
+    sign_in(@user.email, 'this is not valid')
  27
+
  28
+    expect(find('.alert')).to have_content("Sign in failed")
  29
+  end
  30
+end
32  app_prototype/spec/features/user_sessions_spec.rb
... ...
@@ -1,32 +0,0 @@
1  
-require 'spec_helper'
2  
-
3  
-feature "User Sessions" do
4  
-
5  
-  background(:all) do
6  
-    @user = create(:user)
7  
-    @user.activate!
8  
-  end
9  
-
10  
-  after(:all) do
11  
-    @user.destroy
12  
-  end
13  
-
14  
-  scenario "Sign in with valid credentials" do
15  
-    sign_in(@user.email, 'password')
16  
-
17  
-    find('.alert').should have_content("Successfully signed in")
18  
-  end
19  
-
20  
-  scenario "Sign in with an invalid email" do
21  
-    sign_in('this is not valid', 'password')
22  
-
23  
-    find('.alert').should have_content("Sign in failed")
24  
-  end
25  
-
26  
-  scenario "Sign in with an invalid password" do
27  
-    sign_in(@user.email, 'this is not valid')
28  
-
29  
-    find('.alert').should have_content("Sign in failed")
30  
-  end
31  
-
32  
-end
15  app_prototype/spec/helpers/users_helper_spec.rb
... ...
@@ -1,15 +0,0 @@
1  
-require 'spec_helper'
2  
-
3  
-# Specs in this file have access to a helper object that includes
4  
-# the UsersHelper. For example:
5  
-#
6  
-# describe UsersHelper do
7  
-#   describe "string concat" do
8  
-#     it "concats two strings with spaces" do
9  
-#       helper.concat_strings("this","that").should == "this that"
10  
-#     end
11  
-#   end
12  
-# end
13  
-describe UsersHelper do
14  
-  pending "add some examples to (or delete) #{__FILE__}"
15  
-end
30  app_prototype/spec/mailers/user_mailer_spec.rb
@@ -3,45 +3,45 @@
3 3
 describe UserMailer do
4 4
   let(:user) { build_stubbed(:user, activation_state: 'pending', activation_token: 'ABC', reset_password_token: 'XYZ') }
5 5
 
6  
-  describe "activation_needed_email" do
  6
+  describe "#activation_needed_email" do
7 7
     let(:mail) { UserMailer.activation_needed_email(user) }
8 8
 
9 9
     it "renders the headers" do
10  
-      mail.subject.should eq("Welcome to My Awesome Site!")
11  
-      mail.to.should eq([user.email])
12  
-      mail.from.should eq(['notifications@example.com'])
  10
+      expect(mail.subject).to eq "Welcome to My Awesome Site!"
  11
+      expect(mail.to).to      eq [user.email]
  12
+      expect(mail.from).to    eq %w(notifications@example.com)
13 13
     end
14 14
 
15 15
     it "renders the body" do
16  
-      mail.body.encoded.should match("Welcome to")
  16
+      expect(mail.body.encoded).to match "Welcome to"
17 17
     end
18 18
   end
19 19
 
20  
-  describe "activation_success_email" do
  20
+  describe "#activation_success_email" do
21 21
     let(:mail) { UserMailer.activation_success_email(user) }
22 22
 
23 23
     it "renders the headers" do
24  
-      mail.subject.should eq("Your account has been activated!")
25  
-      mail.to.should eq([user.email])
26  
-      mail.from.should eq(['notifications@example.com'])
  24
+      expect(mail.subject).to eq "Your account has been activated!"
  25
+      expect(mail.to).to      eq [user.email]
  26
+      expect(mail.from).to    eq %w(notifications@example.com)
27 27
     end
28 28
 
29 29
     it "renders the body" do
30  
-      mail.body.encoded.should match("You have successfully activated")
  30
+      expect(mail.body.encoded).to match "You have successfully activated"
31 31
     end
32 32
   end
33 33
 
34  
-  describe "reset_password_email" do
  34
+  describe "#reset_password_email" do
35 35
     let(:mail) { UserMailer.reset_password_email(user) }
36 36
 
37 37
     it "renders the headers" do
38  
-      mail.subject.should eq("Password reset requested")
39  
-      mail.to.should eq([user.email])
40  
-      mail.from.should eq(['notifications@example.com'])
  38
+      expect(mail.subject).to eq "Password reset requested"
  39
+      expect(mail.to).to      eq [user.email]
  40
+      expect(mail.from).to    eq %w(notifications@example.com)
41 41
     end
42 42
 
43 43
     it "renders the body" do
44  
-      mail.body.encoded.should match("You have requested to reset your password.")
  44
+      expect(mail.body.encoded).to match "You have requested to reset your password."
45 45
     end
46 46
   end
47 47
 
19  app_prototype/spec/models/user_spec.rb
@@ -7,23 +7,30 @@
7 7
 
8 8
     describe "name" do
9 9
       it "is required" do
10  
-        subject.should_not accept_values(:email, nil, '')
  10
+        expect(subject).to_not accept_values(:email, nil, '')
11 11
       end
12 12
 
13  
-      it "should be less than 30 characters"
  13
+      it "should be less than 30 characters" do
  14
+        expect(subject).to     accept_values(:name, 'a' * 30)
  15
+        expect(subject).to_not accept_values(:name, 'a' * 31)
  16
+      end
14 17
     end
15 18
 
16 19
     describe "email" do
17 20
       it "is required" do
18  
-        subject.should_not accept_values(:email, nil, '', ' ')
  21
+        expect(subject).to_not accept_values(:email, nil, '', ' ')
19 22
       end
20 23
 
21 24
       it "must be properly formatted" do
22  
-        subject.should     accept_values(:email, 'a@b.com', 'a@b.c.com')
23  
-        subject.should_not accept_values(:email, 'a@b', 'a.b.com')
  25
+        expect(subject).to     accept_values(:email, 'a@b.com', 'a@b.c.com')
  26
+        expect(subject).to_not accept_values(:email, 'a@b', 'a.b.com')
24 27
       end
25 28
 
26  
-      it "must be unique"
  29
+      it "must be unique" do
  30
+        subject.save
  31
+        stunt_double = subject.dup
  32
+        expect(stunt_double).to_not accept_values(:email, subject.email)
  33
+      end
27 34
     end
28 35
   end
29 36
 end
5  app_prototype/spec/spec_helper.rb
@@ -29,6 +29,7 @@ def format(result)
29 29
 require File.expand_path('../../config/environment', __FILE__)
30 30
 require 'rspec/rails'
31 31
 require 'rspec/autorun'
  32
+require 'capybara/email/rspec'
32 33
 
33 34
 # Requires supporting ruby files with custom matchers and macros, etc,
34 35
 # in spec/support/ and its subdirectories.
@@ -48,6 +49,10 @@ def format(result)
48 49
   # rspec-rails.
49 50
   config.infer_base_class_for_anonymous_controllers = false
50 51
 
  52
+  config.expect_with :rspec do |c|
  53
+    c.syntax = :expect
  54
+  end
  55
+
51 56
   # Run specs in random order to surface order dependencies. If you find an
52 57
   # order dependency and want to debug it, you can fix the order by providing
53 58
   # the seed, which is printed after each run.
4  app_prototype/spec/support/user_sessions_feature_helper.rb
@@ -10,8 +10,8 @@ def sign_in(email, password)
10 10
     end
11 11
   end
12 12