I've got an asynchronous javascript function I need to test with capybara. I understand that I have to use the js: true
option on the tests and set config.use_transactional_fixtures = false
, then throw in some database_cleaner
, but I still can't get it working.
The cart_spec
runs through the whole process of adding an item to the cart and purchasing it (so it is nested pretty deep).
checkout.html.erb
uses the stripe
library to handle credit cards. That has the javascript functionality I need to test.
When I run the tests with the js: true
option, I get a test failure that 'Add to Cart' doesn't exist on the sports_url
, indicating the sports aren't being saved to the database.
When I run the tests without the js: true
option, all the tests pass except for it "should warn user that cc number is invalid" do
because that is where the javascript is.
I'm running spork
and have tried restarting the server.
Edit
The problem is that my test is running at the domain www.example.com, so my sports_url
is being directed to www.example.com/sports. I've asked a new question here. If I access it with sports_path
, it works great.
Here are the files:
cart_spec:
require "spec_helper"
describe "Cart" do
before do
@user = FactoryGirl.create(:user)
@cart = @user.carts.create!
end
describe "using stripe" do
before do
@sport = FactoryGirl.create(:sport)
end
describe "user adds sport to cart", js: true do
before do
visit sports_url
click_link "Add to Cart"
end
it "should be checkout page" do
page.should have_content("Total")
end
describe "user clicks checkout" do
before do
click_button "Checkout"
end
it "should redirect user to sign in form" do
page.should have_selector('h2', text: "Sign in")
end
describe "user logs on" do
before do
fill_in "Email", with: @user.email
fill_in "Password", with: @user.password
click_button "Sign in"
end
it "should be on checkout page" do
page.should have_selector('h2', text: "Checkout")
end
describe "user fills in form" do
context "with invalid cc number" do
before do
fill_in "card-number", with: 42
click_button "Submit Payment"
end
it "should warn user that cc number is invalid" do
page.should have_content("Your card number is invalid")
end
end
end
end
end
end
end
describe "GET /carts/checkout" do
subject { @cart }
it { should respond_to(:paypal_url) }
it { should respond_to(:apply_discount) }
it "paypal_url contains notification" do
@cart.paypal_url(root_url, payment_notifications_url).should include("¬ify_url=http%3A%2F%2Fwww.example.com%2Fpayment_notifications")
end
it "paypal_url contains invoice id" do
@cart.paypal_url(root_url, payment_notifications_url).should match /&invoice=\d+&/
end
it "paypal_url contains return url" do
@cart.paypal_url(root_url, payment_notifications_url).should include("&return=http%3A%2F%2Fwww.example.com")
end
end
describe "GET /carts/discount" do
it "should apply discount to all line items" do
@cart.line_items.build(:unit_price => 48)
@cart.apply_discount
@cart.line_items.each do |lineItem|
lineItem.unit_price.should == 9.99
end
end
end
end
checkout.html.erb:
<h2>Checkout</h2>
<span class="payment-errors"></span>
<form action="" method="POST" id="payment-form">
<div class="form-row">
<label>Card Number</label>
<input type="text" size="20" autocomplete="off" id ="card-number" class="card-number"/>
</div>
<div class="form-row">
<label>CVC</label>
<input type="text" size="4" autocomplete="off" class="card-cvc"/>
</div>
<div class="form-row">
<label>Expiration (MM/YYYY)</label>
<input type="text" size="2" class="card-expiry-month"/>
<span> / </span>
<input type="text" size="4" class="card-expiry-year"/>
</div>
<button type="submit" class="submit-button">Submit Payment</button>
</form>
<script type="text/javascript" src="https://js.stripe.com/v1/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('pk_xIm00GVAKVLMWmfeR2J8GlmeHcyhL');
$(document).ready(function() {
$("#payment-form").submit(function(event) {
// disable the submit button to prevent repeated clicks
$('.submit-button').attr("disabled", "disabled");
Stripe.createToken({
number: $('.card-number').val(),
cvc: $('.card-cvc').val(),
exp_month: $('.card-expiry-month').val(),
exp_year: $('.card-expiry-year').val()
}, stripeResponseHandler);
// prevent the form from submitting with the default action
return false;
});
});
function stripeResponseHandler(status, response) {
if (response.error) {
$('.submit-button').removeAttr("disabled");
//show the errors on the form
$(".payment-errors").html(response.error.message);
} else {
var form$ = $("#payment-form");
// token contains id, last4, and card type
var token = response['id'];
// insert the token into the form so it gets submitted to the server
form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
// and submit
form$.get(0).submit();
}
}
</script>
spec_helper:
require 'rubygems'
require 'spork'
Spork.prefork do
# Loading more in this block will cause your tests to run faster. However,
# if you change any configuration or code from libraries loaded here, you'll
# need to restart spork for it take effect.
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
end
Spork.each_run do
# This code will be run each time you run your specs.
end
The problem was I was using visit <route>_url
. The default domain for rails tests is exmaple.com, so my browser was trying to access www.example.com/sports
, which is kindly reserved by the international internet naming committee.
I changed those to visit <route>_path
and things work great.