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.