Turbo+Stimulus即时搜索在Capybara测试中无法触发JS动作
问题描述
我基于Turbo和Stimulus控制器实现了即时输入搜索功能,相关JavaScript代码位于app/javascript/controllers/form_submission_controller.js:
import { Controller } from "@hotwired/stimulus" export default class extends Controller { search() { clearTimeout(this.timeout) this.timeout = setTimeout(() => { this.element.requestSubmit() }, 200) } }
视图代码如下:
<%= form_with(url: users_path, method: :get, data: { controller: "form-submission", turbo_frame: "users", turbo_action: "advance" }) do |form| %> <%= form.label :query, "Search by name:" %> <%= form.text_field :query, data: { action: "input->form-submission#search" } %> <% end %>
该功能在浏览器中正常运行,但在使用RSpec + Capybara(Poltergeist驱动)的测试套件中,调用fill_in输入查询时,无法触发Stimulus控制器更新页面,页面仍显示完整列表,且日志中无搜索HTTP请求。如何在测试套件中触发该控制器函数?
解决方案
1. 手动触发输入事件并等待异步操作
Poltergeist基于PhantomJS,对现代前端事件的模拟存在局限,fill_in可能不会自动触发Stimulus绑定的input事件,或者定时器延迟导致测试未等请求完成就执行断言。可以手动触发事件并配合Capybara的断言等待:
fill_in "Search by name:", with: "test" # 手动触发input事件 page.execute_script("document.querySelector('[data-action=\"input->form-submission#search\"]').dispatchEvent(new Event('input'))") # 用断言等待页面更新,替代固定时长sleep expect(page).to have_selector("#users .user", count: 1) # 替换为实际的结果选择器
2. 替换Poltergeist为现代驱动
Poltergeist已停止维护,对Hotwire(Turbo/Stimulus)的支持不足,建议切换到更适配的驱动:
- Selenium + Chrome/Firefox Headless:完全模拟真实浏览器行为,对前端框架支持更好
- Capybara-webkit:仍在维护,轻量且支持现代JS特性
以Selenium+Chrome为例,配置步骤:
- 在
Gemfile添加依赖:
group :test do gem 'capybara' gem 'selenium-webdriver' gem 'webdrivers' # 自动管理浏览器驱动版本 end
- 在
spec/rails_helper.rb中配置驱动:
Capybara.register_driver :selenium_chrome_headless do |app| options = Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless=new') options.add_argument('--disable-gpu') options.add_argument('--no-sandbox') Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) end Capybara.default_driver = :selenium_chrome_headless Capybara.javascript_driver = :selenium_chrome_headless
切换驱动后,fill_in通常能自动触发input事件和Stimulus逻辑。
3. 直接调用Stimulus控制器方法
如果前两种方法无效,可以通过JS直接调用控制器的search方法:
fill_in "Search by name:", with: "test" # 获取控制器实例并执行搜索方法 page.execute_script(" const formElement = document.querySelector('[data-controller=\"form-submission\"]') const controller = Stimulus.controllers.find(c => c.element === formElement) controller.search() ") # 等待页面更新 expect(page).to have_content("匹配的搜索结果") # 替换为实际的结果标识
4. 优化测试等待逻辑
Capybara默认不会等待异步操作完成,需用显式断言替代sleep,让测试自动等待直到条件满足:
fill_in "Search by name:", with: "test" page.execute_script("document.querySelector('[data-action=\"input->form-submission#search\"]').dispatchEvent(new Event('input'))") # 等待搜索结果出现/总数变化 expect(page).to have_selector(".user", count: 1)
内容的提问来源于stack exchange,提问作者vinnydiehl
相关产品推荐
相关产品推荐

