capybara not wait ajax request - ruby-on-rails

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.

Related

Capybara ingore `within` scope at specify assertion

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

Testing if a method returns a `non-null` value

Im testing if my method populate() returns a non-null value (which it does, it returns an integer > 0) but having troubles to correctly write it. I have:
describe House::Room do
describe '.populate' do
let(:info) {
$info = {"people"=>
{"name"=>"Jordan",
"last_name"=>"McClalister"}}
}
it 'should return an integer > 0' do
expect(House::Room.populate(info)).not_to eq(nil)
end
end
end
You'll need to change the let assignment to:
describe House::Room do
describe '.populate' do
let(:info) {"people"=>
{"name"=>"Jordan",
"last_name"=>"McClalister"}
}
it 'should return an integer > 0' do
expect(House::Room.populate(info)).not_to be(nil)
end
end
end
That should make your code work as you expect.
However, you could also use another matcher, like 'be_within' if you wanted to be more specific, or write several expect statements in the same test, like 'expect to be an integer', 'expect to be greater than 0', etc... There is no limit to the number of expect statements you can have in an 'it' block, the test will only pass if all of the expectations are fulfilled. (That said, I believe best practice would be to split it up into individual tests.)

RSpec eq matcher returns failure when two things are equal

In my controller test, I am testing the correct value is assigned to an instance variable.
When I do
expect(assigns(:conversations)).to eq #user_inbox
RSpec tells me:
Failure/Error: expect(assigns(:conversations)).to eq #user_inbox
expected: #<ActiveRecord::Relation [#<Mailboxer::Conversation id: 4, subject: "Dude, what up?", created_at: "2014-10-21 08:43:50", updated_at: "2014-10-21 08:43:50">]>
got: #<ActiveRecord::Relation [#<Mailboxer::Conversation id: 4, subject: "Dude, what up?", created_at: "2014-10-21 08:43:50", updated_at: "2014-10-21 08:43:50">]>
(compared using ==)
Diff:
I see that there is no difference between the expected and the actual. I would like to know what is causing this test to fail.
ActiveRecord::Relation compares based on the actual relation, not the result set. For example,
User.where(:id => 123) == User.where(:email => "fred#example.com")
will return false, even the query results are both the same, since the actual queries are different.
I suspect that you care much more about the query results rather than how it was composed, in which case you can use to_a to convert the relation to an array of active record objects. Note that Active Record defines equality based only on the value of the id attribute (with a special case for unsaved objects).
Yes, because this is two ActiveRecord::Relation object. Your instance variable is the first one and you create another one called conversations
You should test the number of rows or other property with something like this:
expect(assigns(:conversations).count).to eq #user_inbox.count
Maybe you should change the test strategy.
When your test is hard to write your code is wrong or your test strategy is wrong. I recommend you no test query result in the controller's test.
you should mock your query result
describe 'GET user conversations' do
before do
your_user.stub(:conversations).and_return "foo bar"
end
it 'assigns the conversations of the user' do
get :user_conversation
expect(assigns(:conversations)).to eq your_user.conversations
end
end
or you should test that some_collaborator.should_receive(:some_methods)
describe 'GET user conversations' do
before do
some_collaborator.stub(:conversations)
end
it 'assigns the conversations of the user' do
some_collaborator.should_receive(:conversations)
get :user_conversation
end
end

Mutate string for testing with rspec and factory_girl

I'd like to test the validation of a model's attribute with rspec and factory_girl. The 'special' thing is, that one attribute (the name) isn't allowed to start with Numbers or special signs like %,&,$, ...
For testing this it would be great to write only one example like
it "is invalid with name starting by special character" do
["1","$","%"].each do |i|
name = i + "test"
FactoryGirl.build(:tutu, name: name).should_not be_valid
end
end
This work's for the first case but it won't return the result for the whole list. Is it possible to tell rspec not to stop on the error?
Do this instead:
["1","$","%"].each do |i|
it "is invalid with name starting by '#{i}'" do
FactoryGirl.build(:tutu, name: "#{i}test").should_not be_valid
end
end

ArgumentError: assertion message must be String or Proc using assert_select

I'm writing a controller test for a rails 3.1 app using testunit 2.4.0.
I want to assert that a certain heading does not appear on the page.
I'm using assert_select like this:
assert_select 'h1', {:text => /Key Dates/, :count => 0}
and getting the following error:
ArgumentError: assertion message must be String or Proc: <</Key Dates/>
expected but was <"Planner Maternity leave">.>(<Test::Unit::Assertions::AssertionMessage>)
I've tracked this down to the fact that assert_select calls build_message which creates an instance of AssertionMessage and passes it through to test-unit's assert. However in version 2.2 of testunit (Feb 2011) checks were added which check the type of the message passed in. These checks trigger the ArgumentError seen above.
I'm not sure whether the mistake lies with test-unit being over-strict or assert_select passing the wrong object type.
Can you advise how best to follow this up? Any work-arounds?
So, the assert_select documentation shows the following example, passing a block in:
assert_select "ol" do |elements|
elements.each do |element|
assert_select element, "li", 4
end
end
So what if you did something like...
assert_select 'h1' do |elements|
elements.length == 0 ? fail
elements.each do |element|
element.text ~= /Key Dates/ ? fail
end
end
Which basically fails if it finds the pattern OR if the number of h1 elements is zero. Obviously you would change the conditions to match what it is you're trying to test for, but does that get you any closer to what you need?
If you cannot upgrade to a bug-free version, you can just pass a third argument (the message), so you do not force the message be built:
assert_select 'h1', {:text => /Key Dates/, :count => 0}, "Unexpected Key Dates found."

Resources