【问题标题】:Capybara synchronize with has_no_css?Capybara 与 has_no_css 同步?
【发布时间】:2019-10-17 08:33:10
【问题描述】:

自从升级到 Capybara 2.4 后,我一直遇到这个问题。以前,这个块工作正常:

page.document.synchronize do
  page.should have_no_css('#ajax_indicator', :visible => true)
end

意思是强制等到ajax指示符消失后再继续下一步。

由于上面返回RSpec::Expectations::ExpectationNotMetError,同步不会重新运行块,而是抛出错误。不知道为什么这在我之前使用的版本中有效(我相信是 2.1)。

synchronize 块仅重新运行返回以下内容的块:

Capybara::ElementNotFound
Capybara::ExpectationNotMet

以及某个驱动程序添加到该列表中的任何内容。

查看 Justin 的回复以获得更全面的解释和不使用 synchronize 的示例,或者查看我的回复以获得直接解决方案。


跟进:我已经重新审视了这个问题,所以这里有一些提示:

1) document.synchronize 通常没有什么用处,正如其他人所提到的,大多数查找器都有内置的等待。您可以在有意义的情况下使用wait: 0 手动强制它不要等待。

请注意,因此,以下内容不等效:

!assert_css('#ajax_indicator')
assert_no_css('#ajax_indicator')

前者会等到元素存在,而后者会等到元素不存在,即使它们在逻辑上是等价的。

2) 导致我们最初插入同步的问题是由于各种鸡和蛋的问题。

#ajax_indicator 在这种情况下会在有活动加载时出现,然后消失。我们无法区分指标还没有出现,出现了然后消失了。

虽然我还没有 100% 解决这个问题,但提高我们测试可靠性的方法是寻找指标来确保页面加载达到某个点,例如

do_thing_that_triggers_ajax
find('#thing-that-should-exist')
assert_no_selector('#ajax_indicator', visible: true)

这与断言 #ajax_indicator 存在然后断言它不存在不同,因为在这种情况下,如果 ajax 发生得太快,capybara 可能无法捕捉到它的作用。

根据您的脚本,您可能会找到更可靠的指标。

【问题讨论】:

  • 其实Capybara的用户不应该直接使用synchronize
  • 我不明白为什么不这样做,它是公开记录在案的。不过,我完全有可能使用不正确(如elabs.se/blog/53-why-wait_until-was-removed-from-capybara 所述)。
  • 如您所见,它适用于必须使用native 的情况。此类案件数量非常少见。是的,你用错了。

标签: ruby capybara


【解决方案1】:

have_no_css 匹配器已经在等待元素消失。问题似乎是在 synchronize 块中使用它。 synchronize 方法只针对某些异常重新运行,不包括RSpec::Expectations::ExpectationNotMetError

删除synchronize 似乎可以满足您的要求 - 即强制等待元素消失。换句话说,只需这样做:

page.should have_no_css('#ajax_indicator', :visible => true)

工作示例

这是一个页面,说“wait.htm”,我认为它重现了您的问题。它有一个链接,点击后会等待 6 秒,然后隐藏指示器元素。

<html>
  <head>
    <title>wait test</title>
    <script type="text/javascript" charset="utf-8">
      function setTimeoutDisplay(id, display, timeout) {
          setTimeout(function() {
              document.getElementById(id).style.display = display;
          }, timeout);
      }
    </script>
  </head>

  <body>
    <div id="ajax_indicator" style="display:block;">indicator</div>
    <a id="hide_foo" href="#" onclick="setTimeoutDisplay('ajax_indicator', 'none', 6000);">hide indicator</a>
  </body>
</html>

以下规范表明,通过使用page.should have_no_css 而不手动调用synchronize,Capybara 已经强制等待。当仅等待 2 秒时,规范失败,因为元素没有消失。等待 10 秒后,规范通过,因为元素有时间消失。

require 'capybara/rspec'

Capybara.run_server = false
Capybara.current_driver = :selenium
Capybara.app_host = 'file:///C:/test/wait.htm'

RSpec.configure do |config|
  config.expect_with :rspec do |c|
    c.syntax = [:should, :expect]
  end
end

RSpec.describe "#have_no_css", :js => true, :type => :feature do
  it 'raise exception when element does not disappear in time' do
    Capybara.default_wait_time = 2

    visit('')
    click_link('hide indicator')
    page.should have_no_css('#ajax_indicator', :visible => true)
  end

  it 'passes when element disappears in time' do
    Capybara.default_wait_time = 10

    visit('')
    click_link('hide indicator')
    page.should have_no_css('#ajax_indicator', :visible => true)
  end
end

【讨论】:

    【解决方案2】:

    从 Capybara 2.0 版本开始,您可以自定义内联等待时间参数以将其传递给 #have_no_css 方法:

    page.should have_no_css('#ajax_indicator', visible: true, wait: 3)
    

    【讨论】:

      【解决方案3】:

      我确定的解决方案如下:

      page.document.synchronize do
        page.assert_no_selector('#ajax_indicator', :visible => true)
      end
      

      assert_no_selector 方法正确地引发了一个Capybara::ExpectationNotMet 错误,并且似乎与has_no_css 的工作方式相同,所以我对这个解决方案很满意。

      我仍然不知道为什么某些方法会引发 RSpec 错误,而其他方法则不会。


      编辑:虽然这可行,但实际上这样做并不是一个好主意,请参阅其他回复。

      【讨论】:

        猜你喜欢
        • 2013-01-13
        • 1970-01-01
        • 2015-02-23
        • 2012-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多