Writing the user authentication component

It is just a beginning. Assumning now we have the following feature specs defined in an authenticate.feature file.

 1Feature: Authenticate
 2  Scenario: Before login
 3    Given I am not logged in
 4    When I go to homepage
 5    Then I should see "Sign in"
 6    And I should see "Sign up"
 7
 8  Scenario: Login Page
 9    Given I am not logged in
10    And I am on homepage
11    When I click "Sign in" link
12    Then I should be in the sign in page
13
14  Scenario: Login action
15    Given I have an account
16    And I am not logged in
17    And I am on sign in page
18    When I fill in correct account information
19    And press "Sign in" button
20    Then I should see "Signed in successfully"
21
22  Scenario: Logged in
23    Given I have logged in
24    When I go to homepage
25    Then I should see "Sign out"
26
27  Scenario: Logout action
28    Given I have logged in
29    And I am on homepage
30    When I click "Sign out" link
31    Then I should see "Sign in"
32    And I should see "Signed out successfully"

This feature can be written by you, the developer, or by the project planner who doesn’t know programming at all.

Now when we run the rake cucumber, we get a large block of response, with lots of pending.

We can implement them one by one.

First comes with the basic steps about page nevigation and mouse clicking.

 1When(/^I go to homepage$/) do
 2  visit root_path
 3end
 4
 5When(/^I click "(.*?)" link$/) do |link|
 6  click_link link
 7end
 8
 9When(/^press "(.*?)" button$/) do |button|
10  click_button button
11end

In the authentication,

1Given(/^I am not logged in$/) do
2  visit '/users/sign_out'
3end

Note that we use the URL instead of the devise path destroy_user_session_path. It is because during the feature and step writing, we are designing the interface interaction, including the URL. It is not nessesary the authentication must happen with devise gem as long as it works.

And the full authenticate_steps.rb file.

 1email = 'test@example.com'
 2password = 'thisisasecret'
 3
 4Given(/^I am not logged in$/) do
 5  visit '/users/sign_out'
 6end
 7
 8Then(/^I should be in the sign in page$/) do
 9  current_path.should == '/users/sign_in'
10end
11
12Given(/^I have an account$/) do
13  User.new(email:email, password:password).save!
14end
15
16Given(/^I am on sign in page$/) do
17  visit '/users/sign_in'
18end
19
20When(/^I fill in correct account information$/) do
21  fill_in 'Email', with: email
22  fill_in 'Password', with: password
23end
24
25Given(/^I have logged in$/) do
26  User.new(email:email, password:password).save!
27  visit '/users/sign_in'
28  fill_in 'Email', with: email
29  fill_in 'Password', with: password
30  click_button 'Sign in'
31end

Absolutely we will get failed scenarios because we have not written any code on authenticatation. It is time to plug the devise gem in.

In Gemfile

gem 'devise'

And bundle install it.

$ rails generate devise:install

Follow the post-installation instruction to add the notice and alert hook to the application.html.erb file.

1<p class="notice"><%= notice %></p>
2<p class="alert"><%= alert %></p>

Then we create the User model with the devise authentication. Don’t forget to rake db:migrate before proceeding.

1$ rails generate devise User

The setup is ready and we can now re-running the rake cucumber to tackle the failed cases.

The first error we encounter is routing error No route matches [GET] "/users/sign_out". It is because the devise’s sign out route uses DELETE method. We can change it at the devise.rb config file.

1config.sign_out_via = :delete
2config.sign_out_via = [:get, :delete] if Rails.env.test?

Next, the error is expected to find text "Sign in". We can fix that by adding the "Sign in" link to layout file application.html.erb.

1<%= link_to 'Sign in', new_user_session_path %>

And alon the sign up page.

1<%= link_to 'Sign up', new_user_registration_path %>

The result of running rake cucumber again:

Cucumber result 1

Thanks to the devise gem have done so much things, we passed several scenarios now. The next error is expected to find text "Sign out".

We can tackle this error by adjusting the authentication links into:

1<%- if user_signed_in? -%>
2  <%= link_to 'Sign out', destroy_user_session_path, method: :delete %>
3<%- else -%>
4  <%= link_to 'Sign in', new_user_session_path %>
5  <%= link_to 'Sign up', new_user_registration_path %>
6<%- end -%>

Now we get all passes.

16 scenarios (6 passed)
224 steps (24 passed)
30m1.072s

Sometimes the scenarios failed at a confusing points that you don’t expect. How about you can duml the HTML output of that step to deal with failing scenarios? capybara-screenshot gem is the one to use.

Add the capybara-screenshot gem to the :test group

1gem 'capybara-screenshot'

And in the features/support/env.rb file, add the following line:

1require 'capybara-screenshot/cucumber'

Now when fail scenario occurs, a dump of the target HTML will be saved:

1Saved file /Users/makzan/Dropbox/share_to_air_mak/CPTTM/CM436-RoR/repo/lesson7_examples/gallery/tmp/capybara/screenshot_2013-10-17-18-13-24.911.html

The idea here is that you can freely change how the view look and the test cases act as a guard to protect the most basic functionality – user can login and logout.

What’s next? We’re going to take a look at “Writing the photo upload component”.

overlaied image when clicked on thumbnail

Makzan | Ruby on rails 101 | Table of Content