如何让RSpec与Capybara等待足够时长直到ActionCable操作完成?
解决方案
你遇到的问题核心是先获取元素文本再断言的写法无法触发Capybara的自动等待逻辑:旧DOM元素还存在时,find会立即返回结果,不会等待ActionCable更新DOM。可以用以下几种方案解决:
方案1:直接使用带文本匹配的选择器(最推荐)
把文本校验规则直接放到选择器的匹配条件里,Capybara会自动等待直到符合条件的元素出现,或达到超时时间:
# 把原来的find拿文本再断言的两行替换成这一行即可 expect(page).to have_selector( "div#count_#{Count.first.id} a.count-btn", text: "Loose Count", wait: 5 )
如果要兼容元素文本存在前后空格的情况,可以加exact_text: false参数放宽匹配规则。
方案2:先等待旧DOM被替换
因为你的业务逻辑是ActionCable广播后会替换整页的count div,你可以先等待旧的count节点被移除,再查询新节点的文本:
# 先给提交前的旧元素做个标记,方便后续判断是否被移除 old_count_btn = find("div#count_#{Count.first.id} a.count-btn") fill_in 'count_unopened_boxes_count', with: 5 click_button('Submit') # 等待旧节点被移除 expect(page).not_to have_css("div#count_#{Count.first.id} a.count-btn", text: old_count_btn.text, wait: 5) # 此时再查询新节点的文本做断言 expect(find("div#count_#{Count.first.id} a.count-btn").text).to eq 'Loose Count'
方案3:等待ActionCable消费完成(适用于复杂场景)
如果需要更精准的等待,可以通过执行JS等待前端的CountsChannel订阅回调执行完成:
click_button('Submit') # 等待ActionCable广播消费完成 page.execute_script <<~JS return new Promise((resolve) => { const subscription = window.cable.subscriptions.subscriptions.find( sub => sub.identifier.includes('CountsChannel') ) // 监听一次received事件触发后再结束等待 if (subscription) { const originalReceived = subscription.received subscription.received = function(data) { originalReceived.call(this, data) subscription.received = originalReceived resolve() } } else { resolve() } }) JS # 再执行后续断言 expect(page).to have_selector("div#count_#{Count.first.id} a.count-btn", text: "Loose Count")
内容的提问来源于stack exchange,提问作者Chiperific
相关产品推荐
相关产品推荐

