I have form that has an delivery_address
input field and checkbox that will trigger an AJAX
call to retrieve the default address and populate the delivery_address field.
This feature is working fine on the browser! But when I am trying to write a test for it on rspec
, it doesn't seems to work
HTML snippet:
<div class="form-group col-lg-12">
<label for="order_delivery_address">Delivery address</label>
<input type="text" name="order[delivery_address]" id="order_delivery_address" class="form-control" required="required" placeholder="Enter a location" autocomplete="off">
<label class="default-address-label">
<input type="checkbox" name="default_address" id="default_address" value="1">
Use my default address
</label>
</div>
JS snippet:
var $order_details_form = $('.order-details-form');
var $delivery_address_input = $order_details_form.find('#order_delivery_address');
$order_details_form.find('#default_address').change(function() {
if($(this).is(':checked')){ //checkbox is tick
$.ajax({
type: 'GET',
url: '/order-details/get-user-default-address',
data: {
'userId': $order_details_form.find('#order_user_id').val()
},
dataType: 'json',
success: function(data) {
//populate the delivery address input field with default address
var default_address = data.default_address;
$delivery_address_input.val(default_address);
},
error: function (textStatus, errorThrown) {
//display error message and uncheck the default address checkbox
toastr.error('Error retrieving your default address at the moment. Please try again.');
$order_details_form.find('#default_address').prop('checked', false);
}
});
}
});
Controller snippet:
def get_user_default_address
default_address = User.find(params[:userId]).address.full_address
respond_to do |format|
format.json { render :json => { :default_address => default_address } }
end
end
Rspec snippet:
context 'default address' do
let!(:user){ create :user }
let!(:address1){ create :address, user: user}
it 'should proceed with default address selected', js: true do
expect(page.current_path).to eq(order_details_path)
expect(find('#order_user_id', visible: false).value).to eq(user.id.to_s)
expect(page.find('#default_address')).to_not be_checked
check 'default_address'
expect(page.find('#default_address')).to be_checked
expect(page.find('#order_delivery_address').value).to eq address1.full_address
end
end
The test result fails at this line expect(page.find('#order_delivery_address').value).to eq address1.full_address
, where I got empty string instead of the address which indicates that the delivery_address field wasn't populated.
I think it doesn't seems to be even making the AJAX request because I've tried inserting binding.pry
into the controller get_user_default_address
and the binding.pry
is not even triggered.
Any idea what am I missing out here? Any help would be appreciated!
*Note: i am using capybara webkit and it has been configured properly as my other test cases that uses js works successfully.
This is usually caused by one of three things.
Not actually using a JS capable driver for the test. You claim it is running with capybara-webkit, which is a JS capable driver, and that other tests work fine with JS so this is probably not the issue.
Error in JS. By default, in dev mode each JS asset is loaded separately but in test and production modes they are all concatenated into one file to reduce the number of requests. This means that an error in any one of your JS assets that is concatenated before the JS snippet which handles your delivery address checkbox can cause the handler to never get attached. That combined with the fact that capybara-webkit only really supports up to ES5 (without polyfill/transpiling) means you can end up with "errors" in your JS that aren't actually errors in the real browser you're testing with (use of let, const, ES5.1+ methods, etc). You can see if this is the issue by enabling debug mode in capybara-webkit to see if that shows errors and/or running with selenium and a real browser to see if the issue goes away.
Use of the wrong RSpec matchers. The RSpec provided eq
matcher doesn't provide any sort of waiting/retrying behavior. When combined with the fact there is no guarantee all side effects triggered by an action initiated in the browser have completed when Capybaras check
/ click
/etc return means you will potentially have all sorts of flaky tests/failures. Instead you should be using the Capybara provided RSpec matchers which provide waiting/retrying behavior. As a general rule you shouldn't be using the eq
matcher for anything to do with elements on your page. In your case that would mean your test becoming
it 'should proceed with default address selected', js: true do expect(page).to have_current_path(order_details_path) # NEVER use `eq` matcher with current_path expect(find('#order_user_id', visible: false).value).to eq(user.id.to_s) expect(page).to have_unchecked_field('default_address') # same as have_field('default_address', checked: false) check 'default_address' expect(page).to have_checked_field('default_address') expect(page).to have_field('order_delivery_address', with: address1.full_address) end
The one use of eq
that remains in that test is because the Capybara :field selector doesn't find inputs of type 'hidden' (which I assume #order_user_id is) and once you're on the order_details_path the input should already exist. Therefore this is one of the few cases where using the eq
matcher would be acceptable, if you REALLY need to verify this input. Generally you shouldn't be worried about verifying anything the user can't see in a feature test since it's an implementation detail, and if the value was missing/wrong it would show in a test that verifies the functionality of submitting the form.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.