Toggling a Bootstrap collapsible is failing in Rails/Capybara feature tests - ruby-on-rails

I'm new to Capybara and feature testing. I've been trying test a minor feature on a Rails app that toggles comments on a post into and out of view. The first test for toggling the comments into view passes, but the second test for toggling them out of view doesn't. (I am using the headless-chrome webdriver).
context 'viewing comments', js: true do
scenario 'toggling comments into view' do
#post.comments.create(body: 'This is a comment.', user_id: #commenter.id)
visit authenticated_root_path
click_button 'Toggle comments'
expect(page).to have_content('This is a comment')
end
scenario 'toggling comments out of view' do
#post.comments.create(body: 'This is a comment.', user_id: #commenter.id)
visit authenticated_root_path
click_button 'Toggle comments'
expect(page).to have_content('This is a comment')
click_button 'Toggle comments'
expect(page).to_not have_content('This is a comment')
end
end
Initially, I had click_button 'Toggle comments' twice, back-to-back. Neither iteration of the test work. I also tried using sleep n in between the two actions, but to no avail.
Failures:
1) Comment management viewing comments toggling comments out of view
Failure/Error: expect(page).to_not have_content('This is a comment')
expected not to find text "This is a comment" in "OdinFB PROFILE REQUESTS 0 LOG OUT The Feed Create post Luna Lovegood said... Body of post 0 Likes 1 Comments Like Comment Share Toggle comments This is a comment. Morfin Gaunt on Sep 18 2017 at 4:22PM"
The button itself works when the app is fired up locally. It appears to become inactive once activated the first time around in testing.
Any insight would be appreciated, and thanks for reading.

What's happening here is the second button click is occurring after the expected text becomes visible on the page but before the animation has completed. The bootstrap collapse code then gets confused and doesn't collapse the comments since it considers them not fully opened yet. A sleep for a second or so immediately before the second click_button will fix this since it delays long enough for the animation to complete. The other option (and better from a test time perspective) is to disable animations in test mode.

Related

doing a rails test:system that always returns no error even if I purposely made it an errof

Example 1:
click_on 'Add Cart'
assert_no_selector "#empty-cart" #note #empty-cart should be dynamically created since we added an item
accept_confirm do
click_on 'Empty Cart'
end
assert_selector #"empty-cart" #note: empty-cart should no longer be present since it was dynamically removed
but somehow I run the test and everything is fine.
Example 2
click_on 'Add Cart'
visit my_url
assert_no_selector "#empty-cart"
accept_confirm do
click_on 'Empty Cart'
end
visit my_url
assert_selector #"empty-cart"
By forcing a reload, the test script somehow is able to detect that the assertion is wrong.
Note: I'm assuming you meant the #s to be inside the quotes in the two examples you give - otherwise they're just creating comments
The concept you're missing is that all actions in the browser occur asynchronously. This means that when accept_confirm returns, the confirm modal has been clicked but any actions that triggers are not guaranteed to have occurred yet. Therefore when assert_selector "#empty-cart" is run it is highly likely that element is still on the page and the assertion will immediately pass.
For your second example you have a similar issue. Clicking on 'Add Cart' and then immediately calling visit is likely to either abort the visit call or have the visit call processed in parallel and not actually see the results of 'Add Cart'. You need to add an assertion to confirm that the 'Add Cart' actions have completed before moving on. Something like
click_on 'Add Cart'
assert_no_selector '#empty-cart' # or maybe assert_text 'Item added to cart!' etc.
visit my_url

How do I confirm a css element attribute with Capybara?

This may seem unusually basic but how do I confirm the presence of a pop up confirmation?
<a data-confirm="delete this video?" rel="nofollow" data-method="delete" href="/videos/21">Delete</a>
<a is the "tag"/"element" and data-confirm is an attribute. I want to test for the existence of the "data-confirm" attribute within the <a> element/tag
I have tried
expect(page).to have_css("a.data-confirm.delete this video?")
from
capybara assert attributes of an element
but no joy.
Edit:
I've tried the expectation from Arup's comment below
expect(page).to have_content "Content"
click_link "Delete"
expect(page).to have_css('a[data-confirm="delete this video?"]')
But it raises the following (same) error
Failures:
1) Visiting the video index page should search and save movies
Failure/Error: expect(page).to have_css('a[data-confirm="delete this video?"]')
expected #has_css?("a[data-confirm=\"delete this video?\"]") to return true, got false
but the page source shows it there and it is clearly working for the user
Any assistance would be very appreciated
You can write this expectation as:
expect(page).to have_css('a[data-confirm="delete this video?"]')
The answer by Arup is correct for the title of the question (and as he stated in the comments it's just valid CSS - https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors), however it's not actually testing the more detailed part of the question "how do I confirm the presence of a pop up confirmation". All it is doing is confirming the correct data attribute is on the link element to trigger the rails provided JS that should show a confirm.
If you wanted to actually test the confirm box is shown you would need to swap to using a JS capable driver - https://github.com/teamcapybara/capybara/tree/2.17_stable#drivers - and then use something like the following in your test
expect(page).to have_content "Content"
accept_confirm "delete this video?" do
click_link "Delete" # The action that will make the system modal confirm box appear
end
See - http://www.rubydoc.info/gems/capybara/Capybara/Session#accept_confirm-instance_method

Link doesn't hide after some time capybara

I use capybara to test this code which located in my Comment model (5 minutes are conventional):
def editable?
self.created_at < (Time.now - 5.minute)
end
The view of link:
- unless comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)
So after 5 minutes link to edit must hide from the page (but we need to refresh the page). Here is my code in RSpec for creating comments and testing the link-hide functionality:
def create_comment(options={})
options[:content] ||= 'I am a comment'
visit category_theme_path(category, theme)
within '.comment-form' do
fill_in 'Content', with: options[:content]
click_button 'Submit'
end
end
context 'Comment has content' do
before(:each) { create_comment }
it 'hides the edit symbol due 5 minutes after comment was created' do
using_wait_time 400 do
visit category_theme_path(category, theme)
save_and_open_page
expect(page).to have_no_css('.comment-edit')
end
end
end
But I got: Failure/Error: expect(page).to have_no_css('.comment-edit')expected #has_no_css?(".comment-edit") to return true, got false
I also try to use page.reload!, expect(page).to have_no_css('.comment-edit', wait: 400) and other related staff, but capybara don't want to wait. Maybe I use using_wait_time for a wrong place, if that - how I can test this?
There are a number of things wrong with your approach to testing this. The reason your attempt is failing is because using_wait_time just sets the amount of time Capybaras matchers will wait for their expectations to become true, it doesn't actually make the program wait. Therefore expect(page).to have_no_css('.comment-edit') will wait up to the time your using_wait_time specifies, rechecking the page every 50 milliseconds or so for the content, but it never reloads the page and doesn't wait before the loading the page. For your approach to work you would need to sleep before visiting the page
it 'hides the edit symbol due 5 minutes after comment was created' do
sleep 5.minutes
visit category_theme_path(category, theme)
save_and_open_page
expect(page).to have_no_css('.comment-edit')
end
however this is a terrible idea since your tests will become ridiculously slow.
Rather than that approach you could be generating your test comment already "old" (as pascal betz suggests), using something like FactoryGirl and specifying a created_at that is more than 5 minutes ago.
FactoryGirl.create(:comment, created_at: 5.minutes.ago)
or if you want to continue creating your comment through the interface then include something like the Timecop gem and you can do
Timecop.travel(5.minutes.from_now) do
visit category_theme_path(category, theme)
save_and_open_page
expect(page).to have_no_css('.comment-edit')
end
which will move the clock 5 minutes forward before visiting the page, and then reset it back to normal once the block if finished.
Additionally, your editable? method is comparing in the wrong direction and should be
def editable?
self.created_at > (Time.now - 5.minute)
end
Then the view should be
- if comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)
Work with seed data that is 5 minutes old and visit that page. Like this you do not need to wait five minutes.
Also your code
- unless comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)
should probably read
- if comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)

Testing ruby with rails, Element not found

I get the error:
Capybara::ElementNotFound:
Unable to find field "user_email"
And this is the test code:
feature 'User' do
given!(:user) { User.new(email: 'testuserid#example.com', encrypted_password: 'test') }
scenario 'opens sign_up page' do
visit new_user_session_path
expect(page).to have_content 'unique text on the page'
end
scenario 'signs in with invalid email' do
visit new_user_session_path
fill_in('user_email',with: 'ssd')
expect(page).to have_content 'unique text on the page'
end
end
My HTML file consists of this code literally:
unique text on the page
<br>
<input type="text" id="user_email">
So this proves that the path is correct because my first scenario runs correctly. It is visiting the right page. But still I get this error for second scenario in fill_in.
I have also tried element = page.find("user_email"), it gives same error.
What am I possibly doing wrong?
I have been scratching my head like hell.
Usually the reason for this is that the input isn't actually visible on the page. You can verify this by doing
fill_in('user_email', with: 'ssd', visible: false)
If that succeeds in finding the element, then you need to change your test to first perform whatever actions make the field visible before attempting to fill it in.
Your code seems right. Maybe you are visiting wrong url or you have used user_email id once more. But you can give a try with alternative syntax like following :
find("input[id$='user_email']").set "ssd"

Odd error from an RSpec/Capybara Test - Calling create from a controller doesn't finish before the test check.

Here the main part of the test:
it "should add an energy level correctly", :js => true do
visit energy_tracking_path
find(".energy-4").click
click_button "Submit"
#user.energy_levels.should_not be_empty
#user.energy_levels.last.level.should be_equal(4)
#user.energy_levels.last.date.should be_present
end
Right now the first test returns an error:
Failure/Error: #user.energy_levels.should_not be_empty
expected empty? to return false, got true
However, if I put a binding.pry line right in between click_button "Submit" and the first test, the entire thing passes.
"Submit" sends the selected energy level to its controller and creates it.
It looks like the create method isn't finishing before the the first test is run, but when binding.pry is put in, it gives the test time to finish the method. Has anyone else run into this problem/have a solution?
I fixed the problem by adding a page.should have_content("Success") line right after the click_button "Submit". I guess this ensures that the page loads first before checking the database.

Resources