poltergeist 似乎没有等待 phantomjs 在水豚中加载

poltergeist doesn't seem to wait for phantomjs to load in capybara

我正在尝试使用 Capybara、Selenium、Capybara/webkit 和 Poltergeist 进行一些 rspec 测试 运行。在某些情况下,我需要它 运行 无头,并且宁愿不使用 xvfb 来让 webkit 工作。我可以使用 selenium 或 poltergeist 作为 phantomjs 的 driver。我遇到的问题是我的测试 运行 使用 selenium 和 firefox 或 chrome 很好,但是当我尝试 phantomjs 时,元素总是显示为未找到。在研究了一段时间并在水豚中使用 page.save_screenshot 后,我发现当 driver 告诉它查找元素时 phantomjs 浏览器没有加载,所以它没有返回任何东西。通过编辑 /capybara/poltergeist/driver.rb 中的 poltergeist 源代码,我能够修复此问题

def visit(url)
  if @started
    sleep_time = 0
  else
    sleep_time = 2
  end
  @started = true
  browser.visit(url)
  sleep sleep_time
end

这显然不是问题的理想解决方案,它不适用于 selenium 作为 phantomjs 的 driver。无论如何我可以告诉 driver 等待幻影准备好吗?

更新:

通过更改包含 Capybara::DSL 的位置,我能够将其设置为 运行。我将它添加到 RSpec.configure 块中,如下所示。

RSpec.configure do |config|
  config.include Capybara::DSL

然后我将页面 object 传递给我创建的所有 classes 以与网页 ui 交互。

示例 class 现在看起来像这样

module LoginUI
  require_relative 'webpage'

  class LoginPage < WebPages::Pages
    def initialize(page, values = {})
      super(page)
    end

    def visit
      browser.visit(login_url)
    end

    def login(username, password)
      set_username(username)
      set_password(password)
      sign_in_button
    end

    def set_username(username)
      edit = browser.find_element(@selectors[:login_edit])
      edit.send_keys(username)
    end

    def set_password(password)
      edit = browser.find_element(@selectors[:password_edit])
      edit.send_keys(password)
    end

    def sign_in_button
      browser.find_element(@selectors[:sign_in_button]).click
    end
  end
end

网页模块是这样的

module WebPages
  require_relative 'browser'
  class Pages
    def initialize(page)
      @page = page
      @browser = Browser::Browser.new
    end

    def browser
      @browser
    end

    def sign_out
      browser.visit(sign_out_url)
    end
  end
end

浏览器模块如下所示

module Browser
  class Browser
    include Capybara::DSL
    def refresh_page
      page.evaluate_script("window.location.reload()")
    end

    def submit(locator)
      find_element(locator).click
    end

    def find_element(hash)
      page.find(hash.keys.first, hash.values.first)
    end

    def find_elements(hash)
      page.find(hash.keys.first, hash.values.first, match: :first)
      page.all(hash.keys.first, hash.values.first)
    end

    def current_url
      return page.current_url
    end
  end
end

虽然这可行,但我不想在 RSpec 中包含 Capybara::DSL 或在 class 中包含页面 object。这些 classes 已经为示例删除了一些内容,但显示了一般结构。理想情况下,我希望浏览器模块包含 Capybara::DSL 并能够处理与 Capybara 的所有交互。

Capybara 不依赖于访问是否已完成,相反,查找器和匹配器将在指定的时间段内重试,直到成功。您可以通过增加 Capybara.default_max_wait_time 的值来增加此时间量。默认情况下不等待的唯一方法是 firstall,但可以通过指定任何计数选项

使其变为 wait/retry
first('.some_class', minimum: 1)  # will wait up to Capybara.default_max_wait_time seconds for the element to exist on the page.

尽管您应该尽可能选择 find 而不是 first/all

如果增加最长等待时间不能解决您的问题,请为您的问题添加一个失败的测试示例。

你的更新完全改变了问题,所以我添加了第二个答案。如果您不从浏览器外部调用任何 Capybara 方法 class,则无需在 RSpec 配置中包含 Capybara::DSL,就像不需要传递 [=27] =] 如果您将所有 Capybara 交互限制在您的浏览器中 class,则所有页面 classes。需要注意的一件事是,Capybara::DSL 提供的 page 方法只是 Capybara.current_session 的别名,因此从技术上讲,您总是可以调用它。

您没有在代码中显示您如何处理页面内容上的任何 assertions/expectations - 因此根据您的处理方式,您可能需要在您的代码中包含 Capybara::RSpecMatchers RSpec 配置 and/or 你的 WebPages::Pages class。

您的示例代码有几个问题会立即弹出,首先您的 Browser#find_elements(假设我正在阅读您的意图是首先正确地使用 find)可能只是

def find_elements(hash)
  page.all(hash.keys.first, hash.values.first, minimum: 1)
end

其次,您的 LoginPage#login 方法在视觉变化上应该有一个 assertion/expectation 作为它的最后一行,表明登录成功(验证一些消息是 displayed/logged 在菜单存在/等等) ,以确保浏览器在测试继续之前已收到身份验证 cookie 等。该行的外观取决于您如何构建您的期望。

如果这不能回答您的问题,请提供一个具体示例说明什么对您不起作用,因为您显示的代码的 none 表明需要 Capybara::DSL被包含在你说你不想要它的任何一个地方。