Table of Contents 11.6 Iteration F6: Testing AJAX changes
Expected at least 1 element matching "input[type='submit'][value='Place Order']", found 0. Traceback: /home/rubys/git/awdwr/edition4/checkdepot.rb:220
Create a model to contain orders
rails generate scaffold Order name address:text email pay_type
invoke active_record
create db/migrate/20140315104326_create_orders.rb
create app/models/order.rb
invoke test_unit
create test/unit/order_test.rb
create test/fixtures/orders.yml
invoke resource_route
route resources :orders
invoke scaffold_controller
create app/controllers/orders_controller.rb
invoke erb
create app/views/orders
create app/views/orders/index.html.erb
create app/views/orders/edit.html.erb
create app/views/orders/show.html.erb
create app/views/orders/new.html.erb
create app/views/orders/_form.html.erb
invoke test_unit
create test/functional/orders_controller_test.rb
invoke helper
create app/helpers/orders_helper.rb
invoke test_unit
create test/unit/helpers/orders_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/orders.js.coffee
invoke scss
create app/assets/stylesheets/orders.css.scss
invoke scss
identical app/assets/stylesheets/scaffolds.css.scss
Create a migration to add an order_id column to line_items
rails generate migration add_order_id_to_line_item order_id:integer
invoke active_record
create db/migrate/20140315104327_add_order_id_to_line_item.rb
Apply both migrations
rake db:migrate
mv 20140315104326_create_orders.rb 20140315000007_create_orders.rb
mv 20140315104327_add_order_id_to_line_item.rb 20140315000008_add_order_id_to_line_item.rb
== CreateOrders: migrating ===================================================
-- create_table(:orders)
-> 0.0034s
== CreateOrders: migrated (0.0035s) ==========================================
== AddOrderIdToLineItem: migrating ===========================================
-- add_column(:line_items, :order_id, :integer)
-> 0.0007s
== AddOrderIdToLineItem: migrated (0.0008s) ==================================
Add a Checkout button to the cart
edit app/views/carts/_cart.html.erb
<h2>Your Cart</h2>
<table>
<%= render(cart.line_items) %>
<tr class="total_line">
<td colspan="2">Total</td>
<td class="total_cell"><%= number_to_currency(cart.total_price) %></td>
</tr>
</table>
<%= button_to "Checkout", new_order_path, :method => :get %>
<%= button_to 'Empty cart', cart, :method => :delete,
:data => { :confirm => 'Are you sure?' } %>
Return a notice when checking out an empty cart
edit app/controllers/orders_controller.rb
class OrdersController < ApplicationController
before_filter :set_cart, :only => [:new, :create]
# GET /orders
#...
end
edit app/controllers/orders_controller.rb
def new
if @cart.line_items.empty?
redirect_to store_url, :notice => "Your cart is empty"
return
end
@order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => @order }
end
end
Modify tests to ensure that there is an item in the cart
edit test/functional/orders_controller_test.rb
test "requires item in cart" do
get :new
assert_redirected_to store_path
assert_equal flash[:notice], 'Your cart is empty'
end
test "should get new" do
item = LineItem.new
item.build_cart
item.product = products(:ruby)
item.save!
session[:cart_id] = item.cart.id
get :new
assert_response :success
end
Modify the template for new orders
edit app/views/orders/new.html.erb
<div class="depot_form">
<fieldset>
<legend>Please Enter Your Details</legend>
<%= render 'form' %>
</fieldset>
</div>
Modify the partial used by the template
edit app/views/orders/_form.html.erb
<%= form_for(@order) do |f| %>
<% if @order.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@order.errors.count, "error") %>
prohibited this order from being saved:</h2>
<ul>
<% @order.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, :size => 40 %>
</div>
<div class="field">
<%= f.label :address %><br />
<%= f.text_area :address, :rows => 3, :cols => 40 %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, :size => 40 %>
</div>
<div class="field">
<%= f.label :pay_type %><br />
<%= f.select :pay_type, Order::PAYMENT_TYPES,
:prompt => 'Select a payment method' %>
</div>
<div class="actions">
<%= f.submit 'Place Order' %>
</div>
<% end %>
Add payment types to the order
edit app/models/order.rb
class Order < ActiveRecord::Base
PAYMENT_TYPES = [ "Check", "Credit card", "Purchase order" ]
end
Add some CSS
edit app/assets/stylesheets/application.css.scss
.depot_form {
fieldset {
background: #efe;
legend {
color: #dfd;
background: #141;
font-family: sans-serif;
padding: 0.2em 1em;
}
}
form {
label {
width: 5em;
float: left;
text-align: right;
padding-top: 0.2em;
margin-right: 0.1em;
display: block;
}
select, textarea, input {
margin-left: 0.5em;
}
.submit {
margin-left: 4em;
}
br {
display: none
}
}
}
Validate name and pay_type
edit app/models/order.rb
class Order < ActiveRecord::Base
# ...
validates :name, :address, :email, :presence => true
validates :pay_type, :inclusion => PAYMENT_TYPES
end
Update the test data in the orders fixture
edit test/fixtures/orders.yml
# Read about fixtures at
# http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
one:
name: Dave Thomas
address: MyText
email: dave@example.org
pay_type: Check
two:
name: MyString
address: MyText
email: MyString
pay_type: MyString
Move a line item from a cart to an order
edit test/fixtures/line_items.yml
# Read about fixtures at
# http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
one:
product: ruby
order: one
two:
product: ruby
cart: one
Define a relationship from the line item to the order
edit app/models/line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
belongs_to :cart
# attr_accessible :title, :body
attr_accessible :cart_id, :product_id
def total_price
product.price * quantity
end
end
Define a relationship from the order to the line item
edit app/models/order.rb
class Order < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
# ...
end
Add line item to order, destroy cart, and redisplay catalog page
edit app/controllers/orders_controller.rb
def create
@order = Order.new(params[:order])
@order.add_line_items_from_cart(@cart)
respond_to do |format|
if @order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { redirect_to store_url, :notice =>
'Thank you for your order.' }
format.json { render :json => @order, :status => :created,
:location => @order }
else
format.html { render :action => "new" }
format.json { render :json => @order.errors,
:status => :unprocessable_entity }
end
end
end
Implement add_line_items_from_cart
edit app/models/order.rb
class Order < ActiveRecord::Base
# ...
def add_line_items_from_cart(cart)
cart.line_items.each do |item|
item.cart_id = nil
line_items << item
end
end
end
Modify the test to reflect the new redirect
edit test/functional/orders_controller_test.rb
test "should create order" do
assert_difference('Order.count') do
post :create, :order => { :address => @order.address, :email => @order.email,
:name => @order.name, :pay_type => @order.pay_type }
end
assert_redirected_to store_path
end
take a look at the validation errors
get /orders/new
#<EOFError: end of file reached>
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/protocol.rb:135:in `sysread'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/protocol.rb:135:in `rbuf_fill'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/timeout.rb:67:in `timeout'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/timeout.rb:101:in `timeout'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/protocol.rb:134:in `rbuf_fill'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/protocol.rb:126:in `readline'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/http.rb:2028:in `read_status_line'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/http.rb:2017:in `read_new'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/http.rb:1051:in `request'
/home/rubys/git/gorp/lib/gorp/net.rb:106:in `post'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/http.rb:543:in `start'
/home/rubys/.rvm/rubies/ruby-1.8.7-p374/lib/ruby/1.8/net/http.rb:440:in `start'
/home/rubys/git/gorp/lib/gorp/net.rb:97:in `post'
makedepot.rb:2135
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `call'
/home/rubys/git/gorp/lib/gorp/output.rb:59
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `each'
/home/rubys/git/gorp/lib/gorp/output.rb:49
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:170:in `call'
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:170:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:63:in `tag!'
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:88:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:22
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:170:in `call'
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:170:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:63:in `tag!'
/home/rubys/.rvm/gems/ruby-1.8.7-p374/gems/builder-3.0.4/lib/builder/xmlbase.rb:88:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:11
makedepot.rb:4179