Create the test first, inside your specs/feature/ folder:
spec/features/your_feature_name_spec.rb
Sample content for that file:
require "rails_helper"
RSpec.feature "Users can create a project" do
scenario "with valid attributes" do
visit "/"
click_link "New Project"
fill_in "Name", with: "Some Project name"
fill_in "Description", with: "Some project description"
click_button "Create"
expect(page).to have_content "Project has been created."
end
end
The test will fail when you run:
bundle exec rspec
If you want to run only one:
bundle exec rspec spec/features/creating_projects_spec.rb
You need to create the db in order for them to pass
bundle exec rake db:create
To make the test pass, you need to build a route from the “/” request
Inside config/routes.rb, add the following line:
root "projects#index"
Create your controller (use plurals! singular names are for models)
rails g controller projects
And, your models (by default, the columns will be string)
rails g model project name description
bundle exec rake db:migrate
Now, inside your src/app/controllers/projects_controller.rb, create your controller methods:
class ProjectsController < ApplicationController
def index
end
def new
@project = Project.new
end
def create
@project = Project.new(project_params)
if @project.save
flash[:notice] = "Project has been created."
redirect_to @project
else
flash.now[:alert] = "Project has not been created."
render "new"
end
end
def show
@project = Project.find(params[:id])
end
def edit
@project = Project.find(params[:id])
end
def update
@project = Project.find(params[:id])
if @project.update(project_params)
flash[:notice] = "Project has been updated."
redirect_to @project
else
flash.now[:alert] = "Project has not been updated."
render "edit"
end
end
def destroy
@project = Project.find(params[:id])
@project.destroy
flash[:notice] = "Project has been deleted."
redirect_to projects_path
end
private
def project_params
params.require(:project).permit(:name)
end
end
Also, build your view templates:
/src/app/views/projects/index.html.erb
/src/app/views/projects/new.html.erb
/src/app/views/projects/show.html.erb
Example of the way you display model information inside any of the ERBs above:
<h1><%= @project.name %></h1>
<p><%= @project.description %></p>
Create the form, usually inside a partial, so you can reuse the code for Create and Edit views (inside app/views/projects/_form.html.erb):
<%= form_for(project) do |f| %>
<% if project.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(project.errors.count, "error") %>
prohibited this project from being saved:</h2>
<ul>
<% project.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_field :description %>
</p>
<%= f.submit %>
<% end %>
Inside your view, since you were looking for a link in this particular test, you can put the following content to make the test pass:
<%= link_to “New Project”, new_project_path %>
But now we need to define a route for that link (inside your config/routes.rb file:
resources :projects
By adding that resource, you are basically accessing seven potential actions on your page:
GET | /projects | index |
POST | /projects | create |
GET | /projects/new | new |
GET | /projects/:id | show |
PATCH/PUT | /projects/:id | update |
DELETE | /projects/:id | destroy |
GET | /projects/:id/edit | edit |
If you want Flash messages to display in the page, you need to include this on your views/layout/application.hml.erb, or in the page where you need them:
<% flash.each do |key, message| %>
<div><%= message %></div>
<% end %>
Basic validation
Add the following to your model:
class Project < ActiveRecord::Base
validates :name, presence: true
end
Index page
The rspec (spec/features/viewing_projects_spec.rb) will look like this:
require "rails_helper"
RSpec.feature "Users can view projects" do
scenario "with the project details" do
project = FactoryGirl.create(:project, name: "Sublime Text 3")
visit "/"
click_link "Sublime Text 3"
expect(page.current_url).to eq project_url(project)
end
end
Before you run you new test, make sure you add the project factory, inside spec/factories/project_factory.rb:
FactoryGirl.define do
factory :project do
name "Example project"
end
end
A typical index.html.erb view page will look like this:
<h1>Projects</h1>
<%= link_to “New Project”, new_project_path %>
<div id=“projects”>
<% @projects.each do |project| %>
<h2><%= link_to project.name, project %></h2>
<p><%= project.description %></p>
<% end %>
</div>
Updating section
Add the following test in spec/features/editing_projects_spec.rb
require "rails_helper"
RSpec.feature "Users can edit existing projects" do
before do
FactoryGirl.create(:project, name: "Sublime Text 3")
visit "/"
click_link "Sublime Text 3"
click_link "Edit Project"
end
scenario "with valid attributes" do
fill_in "Name", with: "Sublime Text 4 beta"
click_button "Update Project"
expect(page).to have_content "Project has been updated."
expect(page).to have_content "Sublime Text 4 beta"
end
scenario "when providing invalid attributes" do
fill_in "Name", with: ""
click_button "Update Project"
expect(page).to have_content "Project has not been updated."
end
end
Then, add a link to the edit page on your show.html.erb page:
<%= link_to “Edit Project”, edit_project_path(@project) %>
And the actual template (app/views/projects/edit.html.erb):
<h1>Edit Project</h1>
<%= render "form", project: @project %>
Delete section
Create your test at: spec/features/deleting_projects_spec.rb
require "rails_helper"
RSpec.feature "Users can delete projects" do
scenario "successfully" do
FactoryGirl.create(:project, name: "Sublime Text 3")
visit "/"
click_link "Sublime Text 3"
click_link "Delete Project"
expect(page).to have_content "Project has been deleted."
expect(page.current_url).to eq projects_url
expect(page).to have_no_content "Sublime Text 3"
end
end
In your show.html.erb template, you need to add the delete action:
<%= link_to "Delete Project",
project_path(@project),
method: :delete,
data: { confirm: "Are you sure you want to delete this project?" }
%>