【问题标题】:How to make RSpec & Capybara wait long enough for ActionCable to complete?如何让 RSpec 和 Capybara 等待足够长的时间让 ActionCable 完成?
【发布时间】:2022-01-06 13:39:59
【问题描述】:

带有 ActionCable 的 Rails 6 规格 3.10 水豚3.36

  1. 用户访问edit_inventory_path @inventory 并查看Counts 的集合。

  2. 单击计数会触发 AJAX 调用以使用计数 _form 填充模式

  3. 填写表单并点击提交触发器:

CountsChannel.broadcast_to(
  @inventory,
  {
    count_id: @count.id,
    html_slug: render_to_string(partial: 'counts/count', collection: @inventory.counts.sort_by { |c| [c.sort_by_status, - c.item.name] }),
    uncounted: "#{view_context.pluralize(@inventory.counts.uncounted.size, 'item')} uncounted."
  }
)

这会替换页面上的每个 count div。新提交的计数排序到页面底部,按钮文字发生变化。

我的测试期望该按钮从“计数”变为“编辑”。

RSpec.describe 'Performing an inventory', type: :system do
  ...
  context 'when submitting a count' do
  ...

    it 'changes the count button text' do
      # making sure page is loaded
      expect(page).to have_content "Edit #{inventory.name}"

      # clicking the button opens a modal.
      # AJAX puts the count edit form in the modal before it open
      find("div#count_#{Count.first.id} a.count-btn").click
      # wait to make sure the modal is open
      find('input#count_unopened_boxes_count')

      fill_in 'count_unopened_boxes_count', with: 5
      click_button('Submit')
      Rails.logger.debug 'HEYA just hit the submit button.'
      # clicking the button closes and clears the modal
      # and AJAXs form data to CountsController#update
      # which triggers the ActionCable broadcast

      # HERE: find('the button', wait: 5) does nothing
      # because the old div is still on the page
      # so Capybara finds it right away
      # wait is a maximum wait time, not an absolute
      count_1_btn_text = find("div#count_#{Count.first.id} a.count-btn").text
      Rails.logger.debug 'HEYA found the button.'

      Rails.logger.debug 'HEYA hoping ActionCable is complete.'
      expect(count_1_btn_text).to eq 'Loose Count'


      # some time later, the button text actually changes
    end
  end
end

当我查看 test.log 时,我知道这是时间问题:

...
Started GET "/inventories/1/edit" for 127.0.0.1 at 2021-11-29 12:51:05 -0500
  ...
Processing by InventoriesController#edit as HTML
  Parameters: {"id"=>"1"}
  ...
  Rendering layout layouts/application.haml
  Rendering inventories/edit.haml within layouts/application
  ...
  Rendered collection of counts/_count.haml [11 times] (Duration: 53.0ms | Allocations: 26025)
  ...
Completed 200 OK in 10635ms (Views: 10574.2ms | ActiveRecord: 15.7ms | Allocations: 645565)
...
Started GET "/assets/channels/consumer-ddc23d99c3f3572746769fa1851baebf4ef5d005651f6ac89f0739c1682a80bc.js" for 127.0.0.1 at 2021-11-29 12:51:15 -0500
Started GET "/assets/channels/counts_channel-6e2c07931c38a8f979370ee55ad4ca4783da4a2def12996ad4efe6f213d4fb78.js" for 127.0.0.1 at 2021-11-29 12:51:16 -0500
Started GET "/cable" for 127.0.0.1 at 2021-11-29 12:51:16 -0500
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2021-11-29 12:51:16 -0500
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
  [1m[36mUser Load (0.6ms)[0m  [1m[34mSELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1[0m
Registered connection (Z2lkOi8vYnVpbGQtcGxhbm5lci9Vc2VyLzE)
...
  [1m[36mInventory Load (44.2ms)[0m  [1m[34mSELECT "inventories".* FROM "inventories" WHERE "inventories"."id" = 1 LIMIT 1[0m
CountsChannel is transmitting the subscription confirmation
CountsChannel is streaming from counts:Z2lkOi8vYnVpbGQtcGxhbm5lci9JbnZlbnRvcnkvMQ
  ...
  ...
  Rendered counts/_edit.haml (Duration: 48.7ms | Allocations: 28122)
  Rendered counts/edit.js.erb (Duration: 50.3ms | Allocations: 28697)
Completed 200 OK in 66ms (Views: 56.2ms | ActiveRecord: 3.4ms | Allocations: 34258)

HEYA just hit the submit button.

  ...
Started PATCH "/inventories/1/counts/1" for 127.0.0.1 at 2021-11-29 12:51:17 -0500
Processing by CountsController#update as JS
  Parameters: {"count"=>{"loose_count"=>"0", "unopened_boxes_count"=>"5"}, "partial_box"=>"Submit Box Count", "inventory_id"=>"1", "id"=>"1"}

HEYA found the button.

HEYA hoping ActionCable is complete.

... 49 lines later ...
[ActionCable] Broadcasting to counts:Z2lkOi8vYnVpbGQtcGxhbm5lci9JbnZlbnRvcnkvMQ: {:count_id=>1, :html_slug=>"...real long string...", :uncounted=>"11 items uncounted."}
  Rendering counts/update.js.erb
CountsChannel transmitting {"count_id"=>1, "html_slug"=>"...really long string... (via streamed from counts:Z2lkOi8vYnVpbGQtcGxhbm5lci9JbnZlbnRvcnkvMQ)
  Rendered counts/update.js.erb (Duration: 41.1ms | Allocations: 674)
Completed 200 OK in 344ms (Views: 56.4ms | ActiveRecord: 123.9ms | Allocations: 30670)
Finished "/cable/" [WebSocket] for 127.0.0.1 at 2021-11-29 12:51:18 -0500
CountsChannel stopped streaming from counts:Z2lkOi8vYnVpbGQtcGxhbm5lci9JbnZlbnRvcnkvMQ

有人告诉我,像sleep() 这样的做法是不好的做法,那么我有什么办法可以迫使 Capybara 等待几秒钟?

【问题讨论】:

  • 当我在click_button 'Submit' 之后添加sleep(3) 时,它可以工作,只是感觉很糟糕。

标签: ruby-on-rails rspec capybara


【解决方案1】:

只需告诉 Capybara 您希望它在页面上找到什么以及等待它出现的最长时间。在您的问题中,您说您希望按钮从“计数”更改为“编辑”,但是您的代码正在检查“松散计数”,所以我不完全清楚您期望的可见变化,但假设后者你可以做类似的事情

expect(page).to have_css("div#count_#{Count.first.id} a.count-btn", text: 'Loose Count', wait: 10) 

此外,在 find 后跟 fill_in 只是为了等待元素出现是没有意义的,因为 fill_in 已经在等待元素出现了,所以

find('input#count_unopened_boxes_count')
fill_in 'count_unopened_boxes_count', with: 5

实际上和刚刚做的一样

fill_in 'count_unopened_boxes_count', with: 5

【讨论】:

  • Re: "Count" vs "Loose Count",很好,只是试图简化语言,因为存在部分计数。
  • 其中一个让我感动的答案是:“是的,我应该完全想到这一点。”纯粹的天才,谢谢。 +1 指出我无用的额外代码。干杯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
  • 2020-11-22
相关资源
最近更新 更多