Authentication

Great! You now can create blog, view blog, and show blog.

But the blog has no author. And anyone can create blog. It's not what we want. Let's modify the spec:

As a guest user, instead of the 'New Blog' link, i should see the 'Sign up' and 'Sign in' link,so that i could sign up and sign in. Once signing in, i would see the 'New Blog' link.

Based on what we've done till now, let's refactor the original spec.

I don't want to do a full reload page after clicking 'Sign in' or 'Sign up' button. One way is to make rails return ajax/html response, which contains the javascript code of React components. OK, let's write the spec:

require 'rails_helper'

feature 'Guest can sign in' do 
    scenario 'Guest can sign in', :js => true do 
        CreateUser.perform(fullname: 'Truong Dung', email: '[email protected]', password: 'password')
        visit '/'
        click_link 'Sign in'
        fill_in 'email', :with => '[email protected]'
        fill_in 'password', :with => 'password'
        expect(page).to have_content('Welcome Truong Dung')
    end
end

Let's make this spec pass.

The error is: Uninitialized constant User. So let's create model User by running the generator:

rails g model User fullname email password_encrypted
bundle exec rake db:migrate

Run rspec again:

bundle exec rspec

The error is : Uninitialized constant CreateUser. So let's create it in app/services/create_user.rb

class CreateUser
    def self.perform(options)

    end
end

Don't forget to add the services folder to autoload_path in config/application.rb:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)

module Testreact
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de
    config.assets.initialize_on_precompile = true
    config.autoload_paths += %W( #{config.root}/app/services )
  end
end

Run rspec again to see next error:

Failure/Error: click_link 'Sign in'
Capybara::ElementNotFound:
 Unable to find link "Sign in"

This error says that there is no HTML element to input. Let's create it.

Create file authentication.jsx in app/assets/javascripts:

/**
 * @jsx React.DOM
 */

 var Authentication = React.createClass({
     getInitialState: function(){
         return {};
     },
     componentWillMount: function(){

     },
     render: function(){

             return (
                 <p>
                     <a href="#/sign_in">Sign in</a> | <a href="#/sign_up">Sign up</a>
                 </p>
             );
         }

 });

Add it to app/assets/javascripts/index.js.jsx file:

/**
 * @jsx React.DOM
 */
//= require authentication  
...
var HomeView = React.createClass({
...
render : function() {
      var x = this.state.blogs.map(function(d){
          return <li key={"blogs_" + d.id}><a href={"#/blogs/" + d.id}>{d.name}</a></li>
      });
    return (
        <div>
      <Authentication />
        <p>{this.props.message}</p>
        <a href="#/blogs/new">New Blog</a>
        <br />
        <h3>All Blogs:</h3>
        <ul>{x}</ul>
        </div>
    );
  }
...
});

Run rspec again, you will see the error says that There is no input element email and password. Let's create it.

Add the SignInView class to authentication.js.jsx:

var SignInView = React.createClass({
    render: function(){
        return (
            <div>
                <form>
                    <input type="text" id="email" /><br/>
                    <input type="password" id="password"/>
                </form>
            </div>
        );
    }
}) 

Add the new route for sign_in:

//index.js.jsx
...
var Router = Backbone.Router.extend({
...
sign_in: function(){
    React.renderComponent(
      <SignInView />,
      document.getElementById('authentication')
    )
  }
});

Run rspec again. You will see the error:

expected to find text "Welcome Truong Dung" in "Sign in |     Sign up New Blog All Blogs:"