如何让 Capybara 在 JS 运行后检查可见性?

发布于 2024-12-26 02:19:01 字数 393 浏览 10 评论 0原文

加载页面后,我有一些代码可以根据 xhr 返回的数据运行、隐藏和显示各种项目。

我的集成测试看起来像这样:

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

当我手动转到此测试运行的上下文中的页面时,#blah 如我预期可见。我怀疑 Capybara 正在查看页面的初始状态(在本例中不可见),评估 DOM 的状态并在 JS 运行之前测试失败。

是的,我设置了 :js =>; true 在包含描述块上:)

任何想法将不胜感激!我希望我不必在这里故意拖延,这感觉不稳定并且会减慢速度。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

つ低調成傷 2025-01-02 02:19:01

我认为这里的 find 语句是带有隐式等待的语句,因此 Capybara 会等待元素出现在页面上,但不会等待它变得可见。

在这里,您希望水豚等待可见元素出现,这应该可以通过指定 visible 选项来实现:

expect(page).to have_selector('#blah', visible: true)

我还没有尝试过,但是 ignore_hidden_​​elements 配置如果您希望 find 始终等待可见元素,选项在这里也可能很有用。

I think that the find statement here is the one with the implicit wait, so Capybara will wait until the element is on the page, but won't wait for it to become visible.

Here, you would want Capybara to wait for the visible element to appear, which should be achievable by specifying the visible option:

expect(page).to have_selector('#blah', visible: true)

I haven't tried it, but the ignore_hidden_elements configuration option might be useful here as well, if you wanted find to always wait for visible elements.

一抹淡然 2025-01-02 02:19:01

这是另一种对我来说非常有效的方法:

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

特别是对于更复杂的查找,例如

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

断言元素已被隐藏。

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.

没企图 2025-01-02 02:19:01

如果您想检查某个元素是否在页面上但不可见,则 visible: false 将无法按您的预期工作。让我有点难住了。

操作方法如下:

# assert element is present, regardless of visibility
page.should have_css('#some_element', :visible => false)
# assert visible element is not present
page.should have_no_css('#some_element', :visible => true)

If you want to check that an element is on the page but is not visible, visible: false won't work as you might expect. Had me stumped for a bit.

Here's how to do it:

# assert element is present, regardless of visibility
page.should have_css('#some_element', :visible => false)
# assert visible element is not present
page.should have_no_css('#some_element', :visible => true)
等风来 2025-01-02 02:19:01

可见意味着不明显

失败可能来自于对可见或不可见内容的误解,因为它不明显,驱动程序不便携,并且记录不足。一些测试:

HTML:

<div id="visible-empty"                                                                   ></div>
<div id="visible-empty-background"      style="width:10px; height:10px; background:black;"></div>
<div id="visible-empty-background-same" style="width:10px; height:10px; background:white;"></div>
<div id="visible-visibility-hidden"     style="visibility:hidden;"                        >a</div>
<div id="visible-display-none"          style="display:none;"                             >a</div>

Rack 测试唯一认为不可见的是内联 display: none (不是内部 CSS,因为它不执行选择器):

!all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
!all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Poltergeist 有类似的行为,但它可以处理内部 CSS和 Js style.display 操作:

Capybara.current_driver = :poltergeist
!all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
!all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Selenium 的行为完全不同:如果认为空元素不可见且 visibility-hidden 以及 display: none

Capybara.current_driver = :selenium
 all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
 all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

另一个常见问题是默认值visible的:

  • 以前为false(看到both可见和不可见元素),
  • 目前为true
  • 控制通过Capybara.ignore_hidden_​​elements选项。

参考

完整的可运行测试在我的 GitHub 上

What visible means is not obvious

The failure may come from a misunderstanding of what is considered visible or not as it is non-obvious, not driver portable, and under-documented. Some tests:

HTML:

<div id="visible-empty"                                                                   ></div>
<div id="visible-empty-background"      style="width:10px; height:10px; background:black;"></div>
<div id="visible-empty-background-same" style="width:10px; height:10px; background:white;"></div>
<div id="visible-visibility-hidden"     style="visibility:hidden;"                        >a</div>
<div id="visible-display-none"          style="display:none;"                             >a</div>

The only thing Rack test considers as invisible is inline display: none (not internal CSS since it does not do selectors):

!all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
!all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Poltergeist has a similar behavior, but it can deal with internal CSS and Js style.display manipulation:

Capybara.current_driver = :poltergeist
!all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
!all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Selenium behaves quite differently: if considers an empty element invisible and visibility-hidden as well as display: none:

Capybara.current_driver = :selenium
 all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
 all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Another common catch is the default value of visible:

  • it used to be false (sees both visible and invisible elements),
  • currently is true
  • is controlled by the Capybara.ignore_hidden_elements option.

Reference.

Full runnable test on my GitHub.

我很坚强 2025-01-02 02:19:01

使用:

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

我有一个 Rails 应用程序,其中有一个链接,单击该链接应提交 AJAX post 请求并返回 JS 响应。

链接代码:

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

JS 响应(.js.haml 文件)应在链接存在的页面上切换以下隐藏的 div:

 #notification_status(style='display:none')

js.haml 文件内容:

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

我正在测试发送通知并显示的场景
使用 Cucumber 向用户发送通知状态消息(cucumber-rails gem 内置
Capybara 支持

我试图测试具有 id: notification_status 的元素是否可见
在我的步骤定义中成功响应。为此,我尝试了以下语句:

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)

以上都不适合我,并且我的步骤失败了。上面列出的 5 个片段中
最后 4 个失败并出现以下错误:

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

这很奇怪,因为以下语句正确传递:

page.has_selector?('#notification_status')

事实上,我使用

  print page.html

显示的内容

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

检查了页面源,这是预期的。

最后我找到了这个链接capybara断言元素的属性,它显示了如何以原始方式检查元素的属性。

我还在水豚文档中找到了可见的?方法(http://rubydoc.info/ github/jnicklas/capybara/master/Capybara/Node/Element#visible%3F-instance_method)
以下信息:

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

因此我得出的结论是,在测试元素的可见性时
不靠水豚的可见结果吗?使用 CSS 选择器时的方法
并使用链接中建议的解决方案 capybara 断言元素的属性

我想出了具有以下内容:

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

 World(CustomMatchers)

用法:

should_be_visible('#notification_status')

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')
梦里梦着梦中梦 2025-01-02 02:19:01

您可能需要查看这篇文章,其中提供了等待所有 ajax 请求完成的示例方法:

def wait_for_ajax(timeout = Capybara.default_wait_time)
  page.wait_until(timeout) do
    page.evaluate_script 'jQuery.active == 0'
  end
end

You might want to look at this post, which gives a sample method for waiting until all ajax requests are complete:

def wait_for_ajax(timeout = Capybara.default_wait_time)
  page.wait_until(timeout) do
    page.evaluate_script 'jQuery.active == 0'
  end
end
空‖城人不在 2025-01-02 02:19:01

接受的答案现在有点过时了,因为“应该”是不推荐使用的语法。这些天你最好按照 expect(page).not_to have_css('#blah',visible: :hidden) 的方式做一些事情

The accepted answer is a bit outdated now as 'should' is deprecated syntax. These days you'd be better off doing something along the lines of expect(page).not_to have_css('#blah', visible: :hidden)

那小子欠揍 2025-01-02 02:19:01

这里的其他答案是“等待”元素的最佳方法。但是我发现这对于我正在开发的网站不起作用。基本上,需要单击的元素在其背后的功能完全加载之前是可见的。这只是不到一秒的时间,但我发现我的测试有时运行得很快,以至于单击了按钮却什么也没发生。我设法通过执行这个临时布尔表达式来解决这个问题:

if page.has_selector?('<css-that-appears-after-click>')
  puts ('<Some-message-you-want-printed-in-the-output>')
else
  find('<css-for-the-button-to-click-again>', :match == :first).trigger('click')
end

基本上,它使用水豚默认等待时间来查找应该出现的内容,如果它不存在,它将重试您的单击。

我再说一遍,应该首先尝试 should have_selector 方法,但如果它不起作用,请尝试这个

The other answers on here are the best way to "wait" for the element. However I have found this didn't work for the site I am working on. Basically the element that needed clicking was visible before the function behind that was fully loaded. This is in fractions of a second but I found my test ran so quick on occasion that it clicked the button and nothing happened. I managed to work around it by doing this make-shift boolean expression:

if page.has_selector?('<css-that-appears-after-click>')
  puts ('<Some-message-you-want-printed-in-the-output>')
else
  find('<css-for-the-button-to-click-again>', :match == :first).trigger('click')
end

Basically it uses the capybara default wait time to look for something that should appear, if it isnt there it will retry your click.

Again I will say that the should have_selector method should be tried first but if it just wont work try this

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文