简体   繁体   中英

How to make RSpec & Capybara wait long enough for ActionCable to complete?

Rails 6 with ActionCable Rspec 3.10 Capybara 3.36

  1. Users visit edit_inventory_path @inventory and see a collection of Count s.

  2. Clicking on a count triggers an AJAX call to populate a modal with the count _form

  3. Filling out the form and clicking submit triggers:

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."
  }
)

This replaces every count div on the page. The newly submitted count is sorted to the bottom of the page and the button text changes.

My test is expecting that button to change from "Count" to "Edit".

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

I know it's a timing issue when I check out 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

I've been taught that doing things like sleep() are bad practices, so what are my options to force Capybara to wait a few seconds?

Just tell Capybara what you expect it to find on the page and maximum time to wait for that to appear. In your question you say that you're expecting the button to change from "Count" to "Edit" but then your code is checking for "Loose Count" so I'm not fully clear on exactly what visible change you're expecting, but assuming the latter you could just do something like

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

Additionally there's no point in doing a find followed by fill_in just to wait for the element to appear because fill_in will already wait for the element to appear, so

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

is effectively the same as just doing

fill_in 'count_unopened_boxes_count', with: 5

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM