Testing Asych Fill in Values with Capybara and Controller Spec

If you’ve got a field such that onBlur causes other fields to get populated, here’s how to set that up and to test it.

Use case:

  1. fill in the phone field and tab out (blur)
  2. other address fields get populated.

Ajax Setup

   var $phone = $("#phone")
    $phone.blur(function(evt) {
      var url = "/get_address_for_phone";
      var phoneValue = $phone.val().trim();
      $.ajax({
         url: url,
         type: "GET",
         data: { phone: phoneValue },
         dataType : "json"
         }).done(function(json) {
           $("#street").val(json.street);
         }).fail(function( xhr, status, errorThrown ) {
           var msg = "Phone '" + phoneValue + "' does not exist.";
           alert(msg);
           $phone.focus();
      });

Controller Setup

def address_for_phone
    phone = params[:phone].try(:strip)
    address_record = Addresses.where(phone: phone).first
    respond_to do |format|
       if address_record
         format.json do
           address = {
             street: address_record.street,
             city: address_record.city,
             state: address_record.state,
             zip: address_record.zip
           }
           render json: address, status: :ok
         end
       else
         format.json do
           render json: { error:  "Address with phone '#{phone}' does not exist" }, status: 404
         end
       end
     end

Testing with Capybara

How to trigger the loss of focus

  # Trigger focus on another field
  page.execute_script("$('#street').trigger('focus')")

How to check that some field has a value (reference)

  expect(page).to have_field('Street', with: '123 Main Street')

Here’s the test code to fill in the field and see that that a field was updated by the ajax call

  fill_in "Phone", with: phone
  # next line auto-fills the address fields
  page.execute_script("$('#street').trigger('focus')")
  expect(page).to have_field('Street', with: '123 Main Street')

Controller Spec

require 'spec_helper'

describe AddressController do
  let(:phone) { "808-555-3344" }
  # Important to use let! so that record is created!
  let!(:address) { FactoryGirl.create :address, phone: phone }
  let(:user) { FactoryGirl.create :user }
  context 'address for phone' do
    before do
      sign_in user
    end

    it 'should get address for phone that exists' do
      get :address_for_phone, phone: phone, format: :json
      expect(response).to be_success
      expect(JSON.parse(response.body)).to eq({
                                   "street" => address.street,
                                   "city" => address.city,
                                   "state" => address.state,
                                   "zip" => address.zip
                                 })
    end

    it 'should get error for phone that does not exist' do
      get :address_for_phone, phone: phone, format: :json
      expect(response).not_to be_success
      expect(response.response_code).to eq(404)
      expect(JSON.parse(response.body)["error"]).to match /#{phone} does not exist/
    end
  end

  context "not logged in" do
    it "should get a 401" do
      get :address_for_phone, phone: phone, format: :json
      expect(response).not_to be_success
      expect(response.response_code).to eq(401)
    end
  end
end