Capybara ingore `within` scope at specify assertion - capybara

Suppose I'm testing a form with Capybara and Minitest, the form has a text input, which using bootstrap-datepicker, I wanna unscope(like ActiveRecord) the within('form') scope only at assert_selector '.datepicker-dropdown', count: 1, as .datepicker-dropdown is appended to body not the form
within "form" do
# other tests...
find("input.date-picker").click
assert_selector '.datepicker-dropdown', count: 1
# failed because .datepicker-dropdown is appended to body
end
Though bootstrap-datepicker have an option container to specify where to append the datepicker-dropdown widget, but not suitable for this case.

There are two ways of escaping the scope of within. The first would be to use XPath and taking advantage of the Xpath-trap (https://github.com/teamcapybara/capybara#beware-the-xpath--trap) by intentionally breaking the scope
assert_xpath '//*', class: 'datepicker-dropdown', count: 1
The second (and probably clearer) method would be to use the page.document method to escape the current scope
assert_selector page.document, '.datepicker-dropdown', count: 1

Related

Don't change string value on insert

I have a Model user with the following method:
def number_with_hyphen
number&.insert(8, "-")
end
When I run it several times in my tests I get the following output:
users(:default).number_with_hyphen
"340909-1234"
(byebug) users(:default).number_with_hyphen
"340909--1234"
(byebug) users(:default).number_with_hyphen
"340909---1234"
(byebug) users(:default).number_with_hyphen
"340909----1234"
It changes the number ?Here are the docs https://apidock.com/ruby/v1_9_3_392/String/insert
When I restructure my method to:
def number_with_hyphen
"#{number}".insert(8, "-") if number
end
If works like expected. The output stays the same!
How would you structure the code, how would you perform the insert?
which method should I use instead. Thanks
If you're using the insert method, which in the documentation explicitly states "modifies str", then you will need to avoid doing this twice, rendering it idempotent, or use another method that doesn't mangle data.
One way is a simple regular expression to extract the components you're interested in, ignoring any dash already present:
def number_with_hyphen
if (m = number.match(/\A(\d{8})\-?(\d+)\z/))
[ m[1], m[2] ].join('-')
else
number
end
end
That ends up being really safe. If modified to accept an argument, you can test this:
number = '123456781234'
number_with_hyphen(number)
# => "12345678-1234"
number
# => "123456781234"
number_with_hyphen(number_with_hyphen(number))
# => "12345678-1234"
number_with_hyphen('1234')
# => "1234"
Calling it twice doesn't mangle anything, and any non-conforming data is sent through as-is.
Do a clone of the string:
"#{number}".clone.insert(8, '-')

capybara not wait ajax request

I get the following error in rspec + capybara + poltergeist:
given!(:user_owner) { create(:user) }
given!(:second_user) { create(:user) }
given!(:question) { create(:question, user: user_owner) }
describe 'as not question owner' do
before do
login_as(second_user, scope: :user, run_callbacks: false)
visit question_path(question)
end
scenario 'can upvote just one time', js: true do
first('a#question-upvote').click
expect(first('div#question-score').text).to eq '1'
first('a#question-upvote').click
expect(first('div#question-score').text).to eq '1'
end
Failure/Error: expect(page.first('div#question-score').text).to eq '-1'
expected: "-1"
got: "0"
When I insert sleep 1:
scenario 'can upvote just one time', js: true do
first('a#question-upvote').click
sleep 1
expect(first('div#question-score').text).to eq '1'
first('a#question-upvote').click
sleep 1
expect(first('div#question-score').text).to eq '1'
end
Test pass.
I understood that page not waited asynchronous request.
How can I rewrite test to start it to work well without sleeping?
P.S. Sorry for English.
You're killing any waiting behavior by using the eq matcher. This is because once you call .text on a found element you have a String and there is no way to reload/re-query that string when used with the eq matcher. If you want waiting/retrying behavior you need to use the matchers provided by Capybara with Capybara elements.
So instead of expect(first('div#question-score').text).to eq '1' you should be doing
expect(first('div#question-score')).to have_text('1', exact: true) # you could also use a Regexp instead of specifying exact: true
Another thing to note is that all/first disable reloading of elements, so if the entire page is changing (or the element you are waiting for text on is being completely replaced) and the initial page had an element that would match the selector but you actually want the element from the second page (or replaced element) to be checked you shouldn't be using first/all - In that case you would want to use find with a query using the css :first-child/:first-of-type, etc type things (or equivalent XPath) to uniquely identify your element instead of returning multiples and picking one of them. If it's just the value of the element being replaced asynchronously on the page then you did not to worry about it.

How to make a stub return a hash value

I have a model and a method inside that defined as follows :
class Farm < ActiveRecord::Base
def flags
#has other code which returns a flag_details hash eg:
# flag_details['flag1'] = true;
# flag_details['flag2'] = false;
end
end
Now I need to write a test to verify that a particular div is displayed based on the flags that are set/unset. And I want to write a stub to be able to return these flags and then test if the page is displaying the correct div. How do I correct the following code, to get what I intend:
scenario "display div named flower when flag1 or flag2 is false" do
farm.stub(:flags).and_return("flag1" => false, "flag2" => false)
if !farm.flags['flag1'] || !farm.flags['flag2']
expect(page).to have_selector('div.flower', text: "4" )
end
end
I am a beginner in ruby, rails and rspec, so any help will be great. I also tried using the following method but it did not work:
farm.stub(:flags[]).with('flag1').and_return(false)
farm.stub(:flags[]).with('flag2').and_return(false)
I also checked this documentation (https://www.relishapp.com/rspec/rspec-mocks/v/2-4/docs/method-stubs) but did not get my answer. Any other links that could be helpful in this, are really appreciated!
Thanks!
I think you should just be able to wrap that return value in curly braces ({}) and it should solve your problem:
farm.stub(:flags).and_return({"flag1" => false, "flag2" => false})
First, get rid of the if conditional in your test. There is no reason to add control flow to a test. How stubbing works: Your test needs to call the flags method on that farm object and it will return the hash that you have specified. Have your scenario visit the page in question and then check to make sure the expectation is behaving as you have intended.

Create a dictionary as an attribute in FactoryGirl

Factory Girl allows to do something like:
FactoryGirl define
factory :post do
content "some content"
styles "styles here"
team 1
end
end
However, if I try something inside the factory block like:
factory :post do
content "some content"
styles "styles here"
team 1
my_dictionary {'a' => 1, 'b' => 2}
end
The my_dictionary does not get interpreted as a dictionary type. I don't know how to make a dictionary as an attribute inside FactoryGirl. Can anyone help me ?
The issue you observe comes from a syntax ambiguity in Ruby. The language uses curly braces both for defining hashes (which you call dictionaries) as well as for blocks (e.g. when using each loops). As you now use the hash as the only parameter to the my_dictionary method, it is unclear to the parser whether that opening curly brace is to be interpreted as the start of a block or a hash. In this case, Ruby defaults to the block assumption.
To enforce the interpretation as a method parameter, you can use parenthesis like so:
my_dictionary({'a' => 1, 'b' => 2})
Then, the statement can be parsed without any ambiguity. What you have here is just one of the rare cases where you can't easily omit the parenthesis for method calls.

How do you test for an empty input field?

I'm trying to test to see if an input field matches one of my factories where the field is empty.
address => {:first_name => '', :last_name => ''}
When checking for what is in the input field I've been using this:
assert_select '#first_name[value=?]', address.first_name
Except this does not work if the first name is blank. I'll get this error and the test fails.
Expected at least 1 element matching "#first_name[value='']", found 0.
<false> is not true.
This makes sense because the code generated will not have the value attribute. Is there a better way to verify the value of an input field?
As of now to test for this I can check if the address field is blank then check if there is an input field without a value attribute. But this is messy and verbose.
Example of a universal check that works but is lengthy:
if address.first_name.blank?
assert_select '#first_name[value]', 0
assert_select '#first_name[type=text]', 1
else
assert_select '#first_name[value=?]', address.first_name
end
Related Information I'm using:
Hpricot 0.8.1
Nokogiri 1.1.1
Rails 2.2.2
Thoughtbot-Shoulda 2.0.5
Webrat 0.4.1
Maybe you can use:
assert_select "#first_name" do
assert_select "[value=?]", address.first_name unless address.first_name.blank?
end
I don't think I can get it any shorter. If it is a recurring pattern in your test case, you could extract it to a custom assertion:
def assert_has_value_unless_blank(selector, value)
assert_select selector do
assert_select "[value=?]", value unless value.blank?
end
end
assert_has_value_unless_blank "#first_name", address.first_name

Resources