How to make Capybara check for visibility after so

2020-02-16 07:03发布

After loading a page I have code that runs and hides and shows various items based on data returned by an xhr.

My integration test looks something like this:

it "should not show the blah" do
    page.find('#blah').visible?.should be_true
end 

When I manually go to the page in the context this test runs, #blah is not visible as I expect. I suspect that Capybara is looking at the initial state of the page (invisible in this case), evaluating the state of the DOM and failing the test before the JS runs.

Yes, I set the :js => true on the containing describe block :)

Any ideas would be greatly appreciated! I'm hoping I don't have to put an intentional delay in here, that feels flaky and will slow things down.

8条回答
男人必须洒脱
2楼-- · 2020-02-16 07:59

Using:

 Ruby:     ruby 1.9.3dev (2011-09-23 revision 33323) [i686-linux]
 Rails:    3.2.9
 Capybara: 2.0.3

I have a Rails application in which there is a link which when clicked should submit an AJAX post request and return a JS response.

Link code:

 link_to("Send Notification", notification_path(user_id: user_id), remote: true, method: :post)

The JS response (.js.haml file) should toggle the following hidden div on the page the link exists:

 #notification_status(style='display:none')

js.haml file contents:

:plain
  var notificationStatusContainer = $('#notification_status');
  notificationStatusContainer.val("#{@notification_status_msg}");
  notificationStatusContainer.show();

I was testing my scenario of sending notification and displaying the notification status message to the user using Cucumber (cucumber-rails gem with built in Capybara support)

I was trying to test that the element having id: notification_status was visible on successful response in my step definition.For this I tried following statements:

page.find('#notification_status').should be_visible
page.should have_selector('#notification_status', visible: true)
page.should have_css('#notification_status', visible: true)
page.find('#notification_status', visible: true)
page.find(:css, 'div#notification_status', visible: true)

Neither of above worked for me and failed my step.Out of the above listed 5 snippets the last 4 failed with following error:

'expected to find css "#notification_status" but there were no matches. Also found "", which matched the selector but not all filters. (Capybara::ExpectationNotMet)'

which was strange because following statement was passing correctly:

page.has_selector?('#notification_status')

And in fact I inspected the page source using

  print page.html

which showed up

<div style='' id='notification_status'></div>

which was expected.

Finally I found this link capybara assert attributes of an element which showed up how to inspect an element's attribute in raw manner.

Also I found in Capybara documentation for visible? method (http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element#visible%3F-instance_method) following information:

 Not all drivers support CSS, so the result may be inaccurate.

Thus I came to the conclusion that when testing visibility of an element do not rely on results of Capybara's visible? method when using a CSS selector and using the solution suggested in link capybara assert attributes of an element

I came up with following:

 module CustomMatchers
   def should_be_visible(css_selector)
    find(css_selector)['style'].should_not include('display:none', 'display: none')
   end
 end

 World(CustomMatchers)

Usage:

should_be_visible('#notification_status')
查看更多
虎瘦雄心在
3楼-- · 2020-02-16 08:05

This is another way to do it that works perfectly fine for me:

find(:css, "#some_element").should be_visible

Especially for more complex finds, such as

find(:css, "#comment_stream_list li[data-id='#{@id3}']").should_not be_visible

which would assert that an element has been hidden.

查看更多
登录 后发表回答