Selenium 不执行 javascript

Selenium does not execute javascript

我正在使用以 Selenium 作为驱动程序的水豚。我试图点击一个元素,当点击它时它会显示一个 div,但点击永远不会调用 javascript 来做到这一点。

下面是我的代码

scenario 'currently used transport mode cannot be re-selected' do

expect(page).to have_css("h2.summary")
expect(find('h2.summary').text).to eq("Single event")
expect(page).to have_content("Change journey")
page.click_link("Change journey")
expect(find('#travel-times-preview').visible?).to be_truthy # FAILS here because of previous step not working

end

错误信息

Capybara::ElementNotFound: Unable to find css "#travel-times-preview"

html

<a class="change-journey gray-text" href="#">Change journey</a>

javascript 要执行的代码

$(".change-journey").on("click", function(e){
      var target = $(this).data("preview-target");
      $('[data-preview-toggle="'+ target +'"]').toggleClass("hidden");
      if($(this).text().indexOf('Change journey') > -1){
        $(this).text("Close Preview");
      }else{
        $(this).text("Change journey");
      }
    e.preventDefault();
  });

数据库清理器设置

config.before(:suite) do
    if config.use_transactional_fixtures?

      raise(<<-MSG)
        Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
        (or set it to false) to prevent uncommitted transactions being used in
        JavaScript-dependent specs.
        During testing, the Ruby app server that the JavaScript browser driver
        connects to uses a different database connection to the database connection
        used by the spec.

        This Ruby app server database connection would not be able to see data that
        has been setup by the spec's database connection inside an uncommitted
        transaction.
        Disabling the use_transactional_fixtures setting helps avoid uncommitted
        transactions in JavaScript-dependent specs, meaning that the Ruby app server
        database connection can see any data set up by the specs.
      MSG

    end
  end

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, type: :feature) do
    # :rack_test driver's Rack app under test shares database connection
    # with the specs, so we can use transaction strategy for speed.
    driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test

    if driver_shares_db_connection_with_specs
      DatabaseCleaner.strategy = :transaction
    else
      # Non-:rack_test driver is probably a driver for a JavaScript browser
      # with a Rack app under test that does *not* share a database
      # connection with the specs, so we must use truncation strategy.
      DatabaseCleaner.strategy = :truncation
    end
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

虽然我可以看到 link 被点击,但底层的 javascript 没有被执行。

假设您已经查看了打开的 firefox window 中的开发控制台并且没有 JS 错误,您所看到的行为有几个潜在的原因,none其中与 e.preventDefault() 有关(当你听到马蹄声时会想到马,而不是斑马)

  1. 您显示的 link 没有 data-preview-target 属性,因此点击处理程序没有任何内容可读取并附加到 data-preview-toggle=,因此没有任何内容切换。数据可能是由您应用中的其他 JS 添加的,因此被设置为 属性 而不是 HTML 中的属性(或者您只是选择将该细节从您显示的元素中删除) 移动到 #2

  2. 您的 .on JS 代码在元素存在之前 运行ning,因此没有附加。当它将所有 JS 连接到一个文件中时,这可能会在测试模式下显示出来,因为 JS 的加载速度比在开发模式下发出多个请求时更快。这将显示为 link 文本未更改,因为点击处理程序根本不是 运行。如果是这种情况,请延迟附加侦听器,直到加载 DOM。

  3. 这是你写的第一个JS支持测试,hidden/shown的元素是从数据库记录生成的,你还没有禁用事务测试and/or配置database_cleaner 正确。这会导致您在测试中创建的对象实际上在您的应用程序中不可见,因此您期望出现在页面上的元素实际上并不存在。您可以通过暂停测试并查看 HTML 来验证它是否确实在页面上。

  4. 根据您提供的错误,这不是问题的原因,只是为了答案的完整性添加:click_link 点击 link 和 returns 立即地。这意味着测试继续 运行ning 而点击触发的动作继续。这可能会导致您的代码中出现竞争条件(如果您设置了 Capybara.ignore_hidden_elements = false - 坏主意),您的代码会在其中找到元素并在更改之前检查其可见性。因此,您的最后一步应该写成如下所示,因为它将 wait/retry 使元素变得可见

    expect(page).to have_css('#travel-times-preview', visible: true)
    

顺便说一句,使用 Capybara 提供的功能可以改进和加速您的测试代码

scenario 'currently used transport mode cannot be re-selected' do

  expect(page).to have_css("h2.summary", text: "Single event")  # do it in one query
  page.click_link("Change journey") # click_link will find the content anyway so no need to check for it before
  expect(page).to have_css('#travel-times-preview') # By default Capybara only finds visible elements so checking visibility is pointeless, if you've changed that default see #4

end