rails3-jquery-autocomplete: 如何使用RSpec和Capybara进行测试?

7
我正在使用Rails 3.2.8,其中包括以下gem:
  • gem 'jquery-rails',版本为'2.1.2'
  • gem 'rails3-jquery-autocomplete',版本为'1.0.9'
  • gem 'rspec-rails',版本为'2.11.4'
  • gem 'capybara',版本为'1.1.3'
  • gem 'capybara-webkit',版本为'0.12.1'

我最近将选择下拉列表转换为自动完成(使用rails3-jquery-autocomplete),并尝试更新我的请求规范以填写自动完成内容。

我的方法是创建一个辅助程序,如this gist所示:

def fill_autocomplete(field, options = {})
  fill_in field, :with => options[:with]

  selector = "ul.ui-autocomplete a:contains('#{options[:select]}')"

  page.should have_selector selector
  page.execute_script "$(\"#{selector}\").mouseenter().click()"
end

如果我使用Webkit测试,测试将失败并显示以下消息:
expected css "ul.ui-autocomplete a:contains('Firstname Lastname')" to return something.

如果我使用Selenium进行测试,在Firefox中观察测试的过程,我并没有看到下拉选择列表出现。添加一个“sleep 3”来等待下拉列表的出现并没有帮助。
我在这里看到了一个相关但有些陈旧的问题,涉及到capybara-webkit,当填写字段时,它会立即模糊不清,这当然会阻止自动完成选项列表的出现。但是其他人似乎已经让它正常工作了。
目前是否可能使用RSpec和Capybara测试包含rails3-jquery-autocomplete字段的表单?如何做?如果不能,是否有一种方法手动填写相应的隐藏id字段,以便仍然可以测试表单创建正确的数据?
5个回答

10

最终至少通过Selenium驱动程序实现了自动完成测试。

解决方案是将焦点放在该字段上并发出 keydown 事件。

我首先通过浏览器的手动测试进行了确认。如果我使用鼠标(而不是 Ctrl-V)将搜索词粘贴到自动完成字段中,则不会发生任何事情--不会显示下拉选择列表。这显然相当于Capybara发送 fill_in 命令时发生的情况。然而,在术语输入到字段中之后,仍然聚焦于该字段,如果我触摸任何键,例如 Shift 键,则选择列表将显示。因此,似乎只有按键时才会出现选择列表。

选项1

一种解决方法是将函数从原始问题扩展如下:

def fill_autocomplete(field, options = {})
  fill_in field, :with => options[:with]

  page.execute_script %Q{ $('##{field}').trigger("focus") }
  page.execute_script %Q{ $('##{field}').trigger("keydown") }
  selector = "ul.ui-autocomplete a:contains('#{options[:select]}')"

  page.should have_selector selector
  page.execute_script "$(\"#{selector}\").mouseenter().click()"
end

然后像这样调用它:

fill_autocomplete "to_contact_name", with: "Jone", select: "Bob Jones"

选项2

类似的方法,改编自主要的rails3-jquery-autocomplete宝石中Steak测试的方法,使用标准的fill_in,然后跟随这个 choose_autocomplete_result:

def choose_autocomplete_result(item_text, input_selector="input[data-autocomplete]")
  page.execute_script %Q{ $('#{input_selector}').trigger("focus") }
  page.execute_script %Q{ $('#{input_selector}').trigger("keydown") }
  # Set up a selector, wait for it to appear on the page, then use it.
  item_selector = "ul.ui-autocomplete li.ui-menu-item a:contains('#{item_text}')"
  page.should have_selector item_selector
  page.execute_script %Q{ $("#{item_selector}").trigger("mouseenter").trigger("click"); }
end

这个被称为这样:

fill_in "to_contact_name", :with => "Jone"
choose_autocomplete_result "Bob Jones", "#to_contact_name"

对于我的测试,我采用了第二种方法。在使用Selenium时,它似乎非常可靠,但在使用WebKit时却不起作用,这很糟糕,因为与此相比,Selenium测试速度很慢。希望能找到在WebKit下工作的解决方案!


1
是的,它在Webkit上无法工作。我在capybara + poltergeist(webkit环境)上进行了测试,在have_selector验证时崩溃了。当我通过录屏调试时,我发现自动完成项已经生成,但页面对象无法识别它。 - raykin
2
你的选项1解决方案无法正常工作,因为:contains不是有效的W3C CSS选择器(只有jQuery支持)。我通过https://dev59.com/H1jUa4cB1Zd3GeqPPkO-中列出的解决方案,在我的名字下进行了修复。 - Trung Lê

4

我自己也遇到了同样的问题。在花费几个小时后,我找到了一个很好的助手,它可以同时与selenium和polstergeist一起使用,而且不需要使用sleep()。以下代码已经通过Capybara 2.1.0测试:

  def fill_autocomplete(field, options = {})
    fill_in field, with: options[:with]

    page.execute_script %Q{ $('##{field}').trigger('focus') }
    page.execute_script %Q{ $('##{field}').trigger('keydown') }
    selector = %Q{ul.ui-autocomplete li.ui-menu-item a:contains("#{options[:select]}")}

    page.should have_selector('ul.ui-autocomplete li.ui-menu-item a')
    page.execute_script %Q{ $('#{selector}').trigger('mouseenter').click() }
  end

基本上,我告诉Capybara填写输入字段,然后使用JS触发keydown事件以激活自动完成。但是,我不使用sleep(),而是利用page.should have_selector('ul.ui-autocomplete li.ui-menu-item a')等待下拉列表出现。然后我使用JS触发mouseenter事件然后单击。我希望有比使用JS eval更好的方法,但这是我能想到的最可靠的解决方案。


1
Keydown事件在Poltergeist中无法触发自动完成代码。我通过在屏幕上添加一个虚拟输入框,并为其附加一个keydown监听器来验证了这一点,该监听器只是使测试对象可见或不可见。然后我在测试中拍摄了截图,比较了按键前后的情况。所以我知道keydown事件正在触发,但由于某种原因它没有触发在浏览器中运行得很好的自动完成代码。你有任何想法吗?我已经花费了近48个工作小时在这个问题上,但我就是无法解决它。 - RubyRedGrapefruit

2

如果有人需要一些代码启发(即使在阅读本帖后也需要一段时间),这个可以使用:

      context "with 5 items results" do
        before do
          5.times { FactoryGirl.create(:item, title: "chinese " + random_string(5) ) }
          page.execute_script("$('input#search_param').val('chinese')")
          page.execute_script("$('input#search_param').trigger('focus')")
          page.execute_script("$('input#search_param').trigger('keydown')")
        end
        specify { page.evaluate_script("$('input#search_param').val()").should == 'chinese' }
        specify { page.should have_css "#autocomplete-menu li", count: 5 }
      end

感谢Mark Berry的启发。

1
另一种选择是使用 capybara 的 send_keys 方法: find('#my_object_id').send_keys 'bob' 我更喜欢这个选项,而不是注入 javascript 来触发 keydown/focus/mouseenter 事件。

0

不使用 page.execute_script, 我在我的输入框中填入空白,以便触发事件。

fill_in("query", :with => "ma")
fill_in("query", :with => "")
# autocomplete suggestions will come up
all('.autocomplete div').first.should have_content("mango")
# other assertions

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接