如何让 RSpec & Capybara 等待足够长的时间让 ActionCable 完成?
How to make RSpec & Capybara wait long enough for ActionCable to complete?
Rails 6 与 ActionCable
Rspec 3.10
水豚 3.36
用户访问 edit_inventory_path @inventory
并看到 Count
的集合。
点击计数会触发 AJAX 调用以使用计数 _form
填充模式
填写表格并点击提交触发器:
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."
}
)
这将替换页面上的每个计数 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 您希望它在页面上找到什么以及等待它出现的最长时间。在您的问题中,您说您希望按钮从“计数”更改为“编辑”,但随后您的代码正在检查“松散计数”,因此我不完全清楚您期望的可见变化到底是什么,但假设是后者,你可以做类似
的事情
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
Rails 6 与 ActionCable Rspec 3.10 水豚 3.36
用户访问
edit_inventory_path @inventory
并看到Count
的集合。点击计数会触发 AJAX 调用以使用计数
填充模式_form
填写表格并点击提交触发器:
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."
}
)
这将替换页面上的每个计数 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 您希望它在页面上找到什么以及等待它出现的最长时间。在您的问题中,您说您希望按钮从“计数”更改为“编辑”,但随后您的代码正在检查“松散计数”,因此我不完全清楚您期望的可见变化到底是什么,但假设是后者,你可以做类似
的事情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