diff --git a/README.md b/README.md index 9d32575ba..7498e43a1 100644 --- a/README.md +++ b/README.md @@ -280,24 +280,24 @@ You can write a custom password strategy that has two instance methods: config.password_strategy = CustomPasswordStrategy end -Optional Cucumber features +Optional Integration tests -------------------------- -Clearance's Cucumber features are dependent on: +Clearance's integration tests are dependent on: -* Cucumber * Capybara * RSpec * Factory Girl -As your app evolves, you want to know that authentication still works. If you've -installed [Cucumber](http://cukes.info) into your app: +As your app evolves, you want to know that authentication still works. We have added support for RSpec integration tests. - rails generate cucumber:install +If you've installed [RSpec](https://github.com/rspec/rspec) gem in your app: -Then, you can use the Clearance features generator: + rails generate rspec:install - rails generate clearance:features +Then, you can use the Clearance specs generator: + + rails generate clearance:specs Edit your Gemfile to include: diff --git a/features/add_migrations_to_project.feature b/features/add_migrations_to_project.feature new file mode 100644 index 000000000..db3712fdc --- /dev/null +++ b/features/add_migrations_to_project.feature @@ -0,0 +1,65 @@ +Feature: add migrations to the project + + Background: + Given I have a project with clearance + And I run `bundle install --local` + + Scenario: Users table does not exist + And I successfully run `bundle exec rails generate clearance:install` + And I successfully run `ls db/migrate` + Then the output should contain: + """ + create_users.rb + """ + + Scenario: Users table without clearance fields exists in the database + When I write to "db/migrate/001_create_users.rb" with: + """ + class CreateUsers < ActiveRecord::Migration + def self.up + create_table(:users) do |t| + t.string :email + t.string :name + end + end + def self.down + end + end + """ + And I successfully run `bundle exec rake db:migrate --trace` + And I successfully run `bundle exec rails generate clearance:install` + And I successfully run `ls db/migrate` + Then the output should contain: + """ + add_clearance_to_users.rb + """ + + Scenario: Users table with clearance fields exists in the database + When I write to "db/migrate/001_create_users.rb" with: + """ + class CreateUsers < ActiveRecord::Migration + def self.up + create_table :users do |t| + t.timestamps :null => false + t.string :email, :null => false + t.string :encrypted_password, :limit => 128, :null => false + t.string :confirmation_token, :limit => 128 + t.string :remember_token, :limit => 128, :null => false + end + + add_index :users, :email + add_index :users, :remember_token + end + + def self.down + drop_table :users + end + end + """ + And I successfully run `bundle exec rake db:migrate --trace` + And I successfully run `bundle exec rails generate clearance:install` + And I successfully run `ls db/migrate` + Then the output should not contain: + """ + add_clearance_to_users.rb + """ diff --git a/features/integration.feature b/features/integration.feature deleted file mode 100644 index 9845a1c1f..000000000 --- a/features/integration.feature +++ /dev/null @@ -1,50 +0,0 @@ -Feature: integrate with application - - Background: - When I successfully run `bundle exec rails new testapp` - And I cd to "testapp" - And I remove the file "public/index.html" - And I remove the file "app/views/layouts/application.html.erb" - And I configure ActionMailer to use "localhost" as a host - And I configure a root route - And I add the "cucumber-rails" gem - And I add the "capybara" gem - And I add the "rspec-rails" gem - And I add the "factory_girl_rails" gem - And I add the "database_cleaner" gem - And I add the "clearance" gem from this project - And I run `bundle install --local` - And I successfully run `bundle exec rails generate cucumber:install` - And I successfully run `bundle exec rails generate clearance:features` - - Scenario: generate a Rails app, run the generators, and run the tests - When I successfully run `bundle exec rails generate clearance:install` - Then the output should contain "Next steps" - When I successfully run `bundle exec rake db:migrate --trace` - And I successfully run `bundle exec rake --trace` - Then the output should contain "passed" - And the output should not contain "failed" - And the output should not contain "Could not find generator" - - Scenario: Developer already has a users table in their database - When I write to "db/migrate/001_create_users.rb" with: - """ - class CreateUsers < ActiveRecord::Migration - def self.up - create_table(:users) do |t| - t.string :email - t.string :name - end - end - def self.down - end - end - """ - And I successfully run `bundle exec rake db:migrate --trace` - And I successfully run `bundle exec rails generate clearance:install` - And I successfully run `bundle exec rake db:migrate --trace` - And I successfully run `bundle exec rake --trace` - Then the output should contain "passed" - And the output should not contain "failed" - And the output should not contain "Could not find generator" - diff --git a/features/integration_with_cucumber.feature b/features/integration_with_cucumber.feature new file mode 100644 index 000000000..fffef1c64 --- /dev/null +++ b/features/integration_with_cucumber.feature @@ -0,0 +1,22 @@ +Feature: install and run cucumber features + + Background: + Given I have a project with clearance and the following gems: + | gem | + | cucumber-rails | + | capybara | + | rspec-rails | + | factory_girl_rails | + | database_cleaner | + And I run `bundle install --local` + And I successfully run `bundle exec rails generate cucumber:install` + And I successfully run `bundle exec rails generate clearance:features` + And I successfully run `bundle exec rails generate clearance:install` + + Scenario: generate a Rails app, run the generators, and run the tests + Then the output should contain "Next steps" + When I successfully run `bundle exec rake db:migrate --trace` + And I successfully run `bundle exec rake --trace` + Then the output should contain "passed" + And the output should not contain "failed" + And the output should not contain "Could not find generator" \ No newline at end of file diff --git a/features/integration_with_rspec.feature b/features/integration_with_rspec.feature new file mode 100644 index 000000000..248e0b542 --- /dev/null +++ b/features/integration_with_rspec.feature @@ -0,0 +1,21 @@ +Feature: generate rspec integration tests with application + + Background: + Given I have a project with clearance and the following gems: + | gem | + | capybara | + | rspec-rails | + | factory_girl_rails | + | database_cleaner | + And I run `bundle install --local` + And I successfully run `bundle exec rails generate rspec:install` + And I successfully run `bundle exec rails generate clearance:specs` + + Scenario: generate a Rails app, run the generators, and run the tests + And I successfully run `bundle exec rails generate clearance:install` + Then the output should contain "Next steps" + When I successfully run `bundle exec rake db:migrate --trace` + And I successfully run `bundle exec rspec` + Then the output should contain "Finished" + And the output should not contain "Failed examples" + And the output should not contain "Could not find generator" diff --git a/features/step_definitions/configuration_steps.rb b/features/step_definitions/configuration_steps.rb index f828448c8..613931434 100644 --- a/features/step_definitions/configuration_steps.rb +++ b/features/step_definitions/configuration_steps.rb @@ -1,3 +1,24 @@ +When "I have a project with clearance and the following gems:" do |table| + step "I have a project with clearance" + + table.map_column!('gem') do |gem| + step %Q{ And I add the "#{gem}" gem } + end +end + +When "I have a project with clearance" do + step "I successfully run `bundle exec rails new testapp`" + + steps %Q{ + And I cd to "testapp" + And I remove the file "public/index.html" + And I remove the file "app/views/layouts/application.html.erb" + And I configure ActionMailer to use "localhost" as a host + And I configure a root route + And I add the "clearance" gem from this project + } +end + When /^I configure ActionMailer to use "([^"]+)" as a host$/ do |host| mailer_config = "config.action_mailer.default_url_options = { :host => '#{host}' }" path = 'config/application.rb' diff --git a/lib/generators/clearance/features/templates/spec/factories/clearance.rb b/lib/generators/clearance/features/templates/spec/factories/clearance.rb index d519458bb..333e25044 100644 --- a/lib/generators/clearance/features/templates/spec/factories/clearance.rb +++ b/lib/generators/clearance/features/templates/spec/factories/clearance.rb @@ -5,6 +5,6 @@ factory :user do email - password "password" + password 'password' end end diff --git a/lib/generators/clearance/specs/USAGE b/lib/generators/clearance/specs/USAGE new file mode 100644 index 000000000..160d6c875 --- /dev/null +++ b/lib/generators/clearance/specs/USAGE @@ -0,0 +1,5 @@ +Description: + Generate RSpec integration tests + +Examples: + rails generate clearance:specs \ No newline at end of file diff --git a/lib/generators/clearance/specs/specs_generator.rb b/lib/generators/clearance/specs/specs_generator.rb new file mode 100644 index 000000000..44097b9ee --- /dev/null +++ b/lib/generators/clearance/specs/specs_generator.rb @@ -0,0 +1,13 @@ +require 'rails/generators/base' + +module Clearance + module Generators + class SpecsGenerator < Rails::Generators::Base + source_root File.expand_path('../templates', __FILE__) + + def create_specs + directory '.', 'spec' + end + end + end +end diff --git a/lib/generators/clearance/specs/templates/factories/clearance.rb b/lib/generators/clearance/specs/templates/factories/clearance.rb new file mode 100644 index 000000000..333e25044 --- /dev/null +++ b/lib/generators/clearance/specs/templates/factories/clearance.rb @@ -0,0 +1,10 @@ +FactoryGirl.define do + sequence :email do |n| + "user#{n}@example.com" + end + + factory :user do + email + password 'password' + end +end diff --git a/lib/generators/clearance/specs/templates/integration/clearance/user_signs_out_spec.rb b/lib/generators/clearance/specs/templates/integration/clearance/user_signs_out_spec.rb new file mode 100644 index 000000000..10b110409 --- /dev/null +++ b/lib/generators/clearance/specs/templates/integration/clearance/user_signs_out_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' + +feature 'User signs out' do + scenario 'signs out' do + user = signed_in_user + sign_out + + user_should_be_signed_out + end +end diff --git a/lib/generators/clearance/specs/templates/integration/clearance/visitor_resets_password_spec.rb b/lib/generators/clearance/specs/templates/integration/clearance/visitor_resets_password_spec.rb new file mode 100644 index 000000000..60122f40d --- /dev/null +++ b/lib/generators/clearance/specs/templates/integration/clearance/visitor_resets_password_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +feature 'Visitor resets password' do + scenario 'with valid email' do + user = user_with_reset_password + + page_should_display_change_password_message + reset_notification_should_be_sent_to user + end + + scenario 'with non-user account' do + reset_password_for 'unknown.email@example.com' + + page_should_display_change_password_message + mailer_should_have_no_deliveries + end + + private + + def reset_notification_should_be_sent_to(user) + user.confirmation_token.should_not be_blank + mailer_should_have_delivery user.email, 'password', user.confirmation_token + end + + def page_should_display_change_password_message + page.should have_content('instructions for changing your password') + end +end diff --git a/lib/generators/clearance/specs/templates/integration/clearance/visitor_signs_in_spec.rb b/lib/generators/clearance/specs/templates/integration/clearance/visitor_signs_in_spec.rb new file mode 100644 index 000000000..c0fb1edc0 --- /dev/null +++ b/lib/generators/clearance/specs/templates/integration/clearance/visitor_signs_in_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +feature 'Visitor signs in' do + scenario 'with valid email and password' do + create_user 'user@example.com', 'password' + sign_in_with 'user@example.com', 'password' + + user_should_be_signed_in + end + + scenario 'with valid mixed-case email and password ' do + create_user 'user.name@example.com', 'password' + sign_in_with 'User.Name@example.com', 'password' + + user_should_be_signed_in + end + + scenario 'tries with invalid password' do + create_user 'user@example.com', 'password' + sign_in_with 'user@example.com', 'wrong_password' + + page_should_display_sign_in_error + user_should_be_signed_out + end + + scenario 'tries with invalid email' do + sign_in_with 'unknown.email@example.com', 'password' + + page_should_display_sign_in_error + user_should_be_signed_out + end + + private + + def create_user(email, password) + create(:user, :email => email, :password => password) + end + + def page_should_display_sign_in_error + page.should have_content('Bad email or password') + end + end diff --git a/lib/generators/clearance/specs/templates/integration/clearance/visitor_signs_up_spec.rb b/lib/generators/clearance/specs/templates/integration/clearance/visitor_signs_up_spec.rb new file mode 100644 index 000000000..5c56a23fb --- /dev/null +++ b/lib/generators/clearance/specs/templates/integration/clearance/visitor_signs_up_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +feature 'Visitor signs up' do + scenario 'with valid email and password' do + sign_up_with 'valid@example.com', 'password' + + user_should_be_signed_in + end + + scenario 'tries with invalid email' do + sign_up_with 'invalid_email', 'password' + + user_should_be_signed_out + end + + scenario 'tries with blank password' do + sign_up_with 'valid@example.com', '' + + user_should_be_signed_out + end +end diff --git a/lib/generators/clearance/specs/templates/integration/clearance/visitor_updates_password_spec.rb b/lib/generators/clearance/specs/templates/integration/clearance/visitor_updates_password_spec.rb new file mode 100644 index 000000000..3d7eb0a85 --- /dev/null +++ b/lib/generators/clearance/specs/templates/integration/clearance/visitor_updates_password_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +feature 'Visitor updates password' do + scenario 'with valid password' do + user = user_with_reset_password + update_password user, 'newpassword' + + user_should_be_signed_in + end + + scenario 'signs in with new password' do + user = user_with_reset_password + update_password user, 'newpassword' + sign_out + sign_in_with user.email, 'newpassword' + + user_should_be_signed_in + end + + scenario 'tries with a blank password' do + user = user_with_reset_password + visit_password_reset_page_for user + change_password_to '' + + page.should have_content("Password can't be blank") + user_should_be_signed_out + end + + private + + def update_password(user, password) + visit_password_reset_page_for user + change_password_to password + end + + def visit_password_reset_page_for(user) + visit edit_user_password_path( + :user_id => user, + :token => user.confirmation_token + ) + end + + def change_password_to(password) + fill_in 'Choose password', :with => password + click_button 'Save this password' + end +end diff --git a/lib/generators/clearance/specs/templates/support/action_mailer.rb b/lib/generators/clearance/specs/templates/support/action_mailer.rb new file mode 100644 index 000000000..107702fb0 --- /dev/null +++ b/lib/generators/clearance/specs/templates/support/action_mailer.rb @@ -0,0 +1,3 @@ +RSpec.configure do |config| + config.before(:each) { ActionMailer::Base.deliveries.clear } +end \ No newline at end of file diff --git a/lib/generators/clearance/specs/templates/support/clearance.rb b/lib/generators/clearance/specs/templates/support/clearance.rb new file mode 100644 index 000000000..2eda1eabb --- /dev/null +++ b/lib/generators/clearance/specs/templates/support/clearance.rb @@ -0,0 +1 @@ +require 'clearance/testing' \ No newline at end of file diff --git a/lib/generators/clearance/specs/templates/support/factory_girl.rb b/lib/generators/clearance/specs/templates/support/factory_girl.rb new file mode 100644 index 000000000..73bf81026 --- /dev/null +++ b/lib/generators/clearance/specs/templates/support/factory_girl.rb @@ -0,0 +1,5 @@ +require 'factory_girl_rails' + +RSpec.configure do |config| + config.include FactoryGirl::Syntax::Methods +end \ No newline at end of file diff --git a/lib/generators/clearance/specs/templates/support/integration.rb b/lib/generators/clearance/specs/templates/support/integration.rb new file mode 100644 index 000000000..c1cba278f --- /dev/null +++ b/lib/generators/clearance/specs/templates/support/integration.rb @@ -0,0 +1,4 @@ +RSpec.configure do |config| + config.include Integration::ClearanceHelpers + config.include Integration::ActionMailerHelpers +end diff --git a/lib/generators/clearance/specs/templates/support/integration/action_mailer_helpers.rb b/lib/generators/clearance/specs/templates/support/integration/action_mailer_helpers.rb new file mode 100644 index 000000000..1dab6a3a1 --- /dev/null +++ b/lib/generators/clearance/specs/templates/support/integration/action_mailer_helpers.rb @@ -0,0 +1,19 @@ +module Integration + module ActionMailerHelpers + def mailer_should_have_delivery(recipient, subject, body) + ActionMailer::Base.deliveries.should_not be_empty + + message = ActionMailer::Base.deliveries.any? do |email| + email.to == [recipient] && + email.subject =~ /#{subject}/i && + email.body =~ /#{body}/ + end + + message.should be + end + + def mailer_should_have_no_deliveries + ActionMailer::Base.deliveries.should be_empty + end + end +end diff --git a/lib/generators/clearance/specs/templates/support/integration/clearance_helpers.rb b/lib/generators/clearance/specs/templates/support/integration/clearance_helpers.rb new file mode 100644 index 000000000..4f1b35fe4 --- /dev/null +++ b/lib/generators/clearance/specs/templates/support/integration/clearance_helpers.rb @@ -0,0 +1,49 @@ +module Integration + module ClearanceHelpers + def sign_up_with(email, password) + visit sign_up_path + fill_in 'Email', :with => email + fill_in 'Password', :with => password + click_button 'Sign up' + end + + def sign_in_with(email, password) + visit sign_in_path + fill_in 'Email', :with => email + fill_in 'Password', :with => password + click_button 'Sign in' + end + + def signed_in_user + password = 'password' + user = create(:user, :password => password) + sign_in_with user.email, password + user + end + + def user_should_be_signed_in + visit root_path + page.should have_content('Sign out') + end + + def sign_out + click_link 'Sign out' + end + + def user_should_be_signed_out + page.should have_content('Sign in') + end + + def user_with_reset_password + user = create(:user) + reset_password_for user.email + user.reload + end + + def reset_password_for(email) + visit new_password_path + fill_in 'Email address', :with => email + click_button 'Reset password' + end + end +end