¿Cómo hacer que RSpec y Capybara esperen el tiempo suficiente para que ActionCable se complete?
Rails 6 con ActionCable
Rspec 3.10
Capybara 3.36
- Los usuarios visitan
edit_inventory_path @inventory
y ven una colección deCount
s. - Hacer clic en un recuento desencadena una llamada AJAX para poblar un modal con el formulario
_form
de recuento. - Completar el formulario y hacer clic en Enviar desencadena:
ruby
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."
}
)
Esto reemplaza todos los divs de conteo en la página. El recuento recién enviado se ordena en la parte inferior de la página y el texto del botón cambia.
Mi prueba espera que el texto del botón cambie de “Count” a “Edit”.
RSpec.describe 'Realización de un inventario', type: :system do
...
context 'cuando se envía un recuento' do
...
it 'cambia el texto del botón de conteo' do
# asegurarse de que la página esté cargada
expect(page).to have_content "Edit #{inventory.name}"
# hacer clic en el botón abre un modal.
# AJAX coloca el formulario de edición de conteo en el modal antes de que se abra
find("div#count_#{Count.first.id} a.count-btn").click
# esperar para asegurarse de que el modal está abierto
find('input#count_unopened_boxes_count')
fill_in 'count_unopened_boxes_count', with: 5
click_button('Submit')
Rails.logger.debug 'HEYA acabo de presionar el botón de enviar.'
# hacer clic en el botón cierra y borra el modal
# y los datos del formulario de AJAXs a CountsController#update
# que desencadena la transmisión de ActionCable
# AQUÍ: find('el botón', wait: 5) no hace nada
# porque el antiguo div todavía está en la página
# así que Capybara lo encuentra de inmediato
# wait es un tiempo máximo de espera, no un valor absoluto
count_1_btn_text = find("div#count_#{Count.first.id} a.count-btn").text
Rails.logger.debug 'HEYA encontró el botón.'
Rails.logger.debug 'HEYA esperando que ActionCable esté completo.'
expect(count_1_btn_text).to eq 'Loose Count'
# algún tiempo después, el texto del botón realmente cambia
end
end
end
Sé que es un problema de sincronización cuando consulto test.log:
“`ruby
…
Start de GET “/inventories/1/edit” para 127.0.0.1 en 2021-11-29 12:51:05 -0500
…
Procesamiento por InventoriesController#edit como HTML
Parámetros: {“id”=>”1”}
…
Renderizado el diseño layouts/application.haml
Renderizado inventories/edit.haml dentro de layouts/application
…
Renderizado la colección de conteo/conteo.haml [11 veces] (Duración: 53,0ms | Asignaciones: 26025)
…
Completado 200 OK en 10635ms (Vistas: 10574,2ms | ActiveRecord: 15,7ms | Asignaciones: 645565)
…
Inicio de GET “/assets/channels/consumer-ddc23d99c3f3572746769fa1851baebf4ef5d005651f6ac89f0739c1682a80bc.js” para 127.0.0.1 en 2021-11-29 12:51:15 -0500
Inicio de GET “/assets/channels/countschannel-6e2c07931c38a8f979370ee55ad4ca4783da4a2def12996ad4efe6f213d4fb78.js” para 127.0.0.1 en 2021-11-29 12:51:16 -0500
Inicio de GET “/cable” para 127.0.0.1 en 2021-11-29 12:51:16 -0500
Inicio de GET “/cable/” [WebSocket] para 127.0.0.1 en 2021-11-29 12:51:16 -0500
Subida con éxito a WebSocket (MÉTODODE PETICIÓN: GET, HTTPCONEXIÓN: Upgrade, HTTPUPGRADE: websocket)
[1m[36mUser Load (0.6ms)[0m [1m[34mSELECT “users”.* FROM “users” WHERE “users”.”id” = 1 ORDER BY “users”.”id” ASC LIMIT 1[0m
La conexión registrada (Z2lkOi8vYnVpbGQtcGxhbm5lci9Vc2VyLzE)
…
[1m[36mInventory Load (44.2ms)[0m [1m[34mSELECT “inventories”.* FROM “inventories” WHERE “inventories”.”id” = 1 LIMIT 1[0m
CountsChannel está transmitiendo la confirmación de la suscripción
CountsChannel se está transmitiendo desde counts:Z2lkOi8vYnVpbGQtcGxhbm5lci9JbnZlbnRvcnkvMQ
…
…
Renderizado counts/edit.haml (Duración: 48,7ms | Asignaciones: 28122)
Renderizado counts/edit.js.erb (Duración: 50,3ms | Asignaciones: 28697)
Completado 200 OK en 66ms (Vistas: 56,2ms | ActiveRecord: 3,4ms | Asignaciones: 34258)
HEYA acabó de presionar el botón de enviar.
…
Inicio de PATCH “/inventories/1/counts/1” para 127.0.0.1 en 2021-11-29 12:51:17 -0500
Procesamiento por CountsController#update como JS
Parámetros: {“count”=>{“loosecount”=>”0″, “unopenedboxescount”=>”5″}, “partialbox”=>”Submit Box Count”, “inventory_id”=>”1”, “id”=>”1”}
HEYA encontró el botón.
HEYA esperando que ActionCable esté completo.
… 49 líneas después …
[ActionCable] Transmitiendo a counts:Z2lkOi8vYnVpbGQtcGxhbm5lci9JbnZlbnRvcnkvMQ: {:countid=>1, :htmlslug=>”…cadena realmente larga…”, :uncounted=>”11 elementos sin contar.”}
Se está renderizando counts/update.js.erb
CountsChannel transmitiendo {“countid”=>1, “htmlslug”=>”…cadena realmente larga… (vía transmitida desde counts:Z2lkOi8vYnVpbGQtcGxhbm5lci9JbnZlbnRvcnkvMQ)
Se está renderizado counts/update.js.erb (Duración: 41,1ms | Asignaciones: 674)
Completado 200 OK en 344ms (Vistas: 56.4ms | ActiveRecord: 123.9ms | Asignaciones:
davy.ai
Una opción es utilizar la opción
wait
incorporada en Capybara, que esperará un cierto período de tiempo antes de informar un error si no aparece un elemento. Esto se puede usar junto confind
u otros métodos de Capybara. Por ejemplo,find('#my-element', wait: 3)
esperará 3 segundos para que aparezca un elemento con el IDmy-element
antes de informar un error si no lo hace.Otra opción es utilizar el método
eventually
del paqueterspec-retry
. Este método ejecuta repetidamente un bloque de código hasta que tenga éxito o alcance un timeout. Por ejemplo,eventually { expect(page).to have_content('Success!') }
ejecutaráexpect(page).to have_content('Success!')
repetidamente hasta que tenga éxito o hasta que se alcance el timeout especificado.Es importante equilibrar el tiempo de espera con la confiabilidad de la suite de pruebas. Esperar demasiado tiempo puede ralentizar las pruebas y generar resultados falsos positivos, mientras que no esperar lo suficiente puede hacer que las pruebas fallen de manera intermitente.