2011-07-05 10 views
25

Estoy usando el dispositivo 1.4.2 con los rieles 3.0.9, pepino-rails 1.0.2, capybara 1.0.0. Obtuve el error No route matches "https://stackoverflow.com/users/sign_out" cuando hice clic en cerrar sesión. Agregué :method => :delete a la etiqueta link_to después de pasar por esta pregunta (no-route-matches-users-sign-out-devise-rails-3).Rails 3.0.9 + Devise + Cucumber + Capybara el infame "Ninguna ruta coincide/users/sign_out"

Desde que sustituye prototipo con jQuery, también tuvo que cambiar

config.action_view.javascript_expansions[:defaults] = %w(jquery rails) 

a

config.action_view.javascript_expansions[:defaults] = %w(jquery jquery_ujs) 

para obtener alrededor de error no rails.js encontrado.

A pesar de los cambios anteriores estoy en condiciones de firmar con éxito y redirige a la raíz, cuando miro a la respuesta de localhost: 3000/usuarios/solicitud sign_out en FireBug se muestra el mensaje de error misma ruta click here to see the screenshot with notes

Tras superar con éxito implementar la autenticación de los carriles 3 aplicación a través de legado, Cuando la función y las especificaciones añadí usando pepino + Carpincho + RSpec siguiendo este tutorial (github.com/RailsApps/rails3-devise-rspec-cucumber/wiki/Tutorial), me siguiente error

When I sign in as "[email protected]/please"        # features/step_definitions/user_steps.rb:41 
Then I should be signed in           # features/step_definitions/user_steps.rb:49 
And I sign out              # features/step_definitions/user_steps.rb:53 
    No route matches "https://stackoverflow.com/users/sign_out" (ActionController::RoutingError) 
    <internal:prelude>:10:in `synchronize' 
    ./features/step_definitions/user_steps.rb:55:in `/^I sign out$/' 
    features/users/sign_out.feature:10:in `And I sign out' 
And I should see "Signed out"           # features/step_definitions/web_steps.rb:105 
When I return next time            # features/step_definitions/user_steps.rb:60 
Then I should be signed out 

con la siguiente step_definition para 'cierro sesión'

Then /^I sign out$/ do 
    visit('/users/sign_out') 
end 

busqué mucho y encontraron que esto es debido a javascript unobrusive en Rails 3 que se utiliza para la 'técnica de método' atributos, sino que también he leído en alguna parte que Carpincho no comprobar si los atributos método de datos y se comporta en consecuencia. Pero no funcionó para mí, así que siguiendo este post Capybara attack: rack-test, lost sessions and http request methods he cambiado de definición de paso a siguiente:

Then /^I sign out$/ do 
    rack_test_session_wrapper = Capybara.current_session.driver 
    rack_test_session_wrapper.process :delete, '/users/sign_out' 
end 

pero tengo método no definido process para Capybara::RackTest::Driver (NoMethodError).

Siguiendo esta pista que cambió la definición paso por encima de la siguiente manera:

Then /^I sign out$/ do 
    rack_test_session_wrapper = Capybara.current_session.driver 
    rack_test_session_wrapper.delete '/users/sign_out' 
end 

Esto, al menos, aprobó la 'cierro sesión' paso, pero no redirigidos a la página principal después de cerrar la sesión y la siguiente paso error:

And I should see "Signed out"           # features/step_definitions/web_steps.rb:105 
    expected there to be content "Signed out" in "YasPiktochart\n\n \n  Signed in as [email protected] Not you?\n  Logout\n \n\n Signed in successfully.\n\n Home\n User: [email protected]\n\n\n\n" (RSpec::Expectations::ExpectationNotMetError) 
    ./features/step_definitions/web_steps.rb:107:in `/^(?:|I)should see "([^"]*)"$/' 
    features/users/sign_out.feature:11:in `And I should see "Signed out"' 

Después de todo esto he tenido que recurrir a la adición de método 'GET' para cerrar la sesión en el archivo de rutas:

devise_for :users do get 'logout' => 'devise/sessions#destroy' end 

modificado mi vista desde

<%= link_to "Logout", destroy_user_session_path, :method => :delete %> 

a

<%= link_to "Logout", logout_path %> 

y me cambió la definición de paso a siguiente:

Then /^I sign out$/ do 
    visit('/logout') 
end 

Obviamente, esto resuelve todos los problemas, todas las pruebas pasaron y Firebug hicieron no mostrar ningún error en sign_out Pero sé que usar la solicitud 'get' para destruir sesiones no es una buena práctica, porque es un comportamiento que cambia el estado.

¿Esto podría deberse a una versión particular de Rails, Devise, Cucumber-Rails o Capybara que estoy usando? Quiero usar la ruta predeterminada de sign_out de Devise en lugar de anularla con el método get y poder hacer BDD usando Cucumber y RSpec. Soy nuevo en el uso de Cucumber + Capybara, ¿existe otro método para enviar solicitudes POST en lugar de usar "visit ('/ users/sign_out')", que solo utiliza el método GET?

Respuesta

12

Así que he encontrado que

<%= link_to "Logout", destroy_user_session_path, :method => :delete %> 

carriles ayudante genera HTML siguiente

<a rel="nofollow" data-method="delete" href="https://stackoverflow.com/users/sign_out">Sign out</a> 

y jquery_ujs.js ha siguiente método para convertir los vínculos con los datos-method = "eliminar" a un atributo formulario y enviar en tiempo de ejecución:

// Handles "data-method" on links such as: 
// <a href="https://stackoverflow.com/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> 
handleMethod: function(link) { 
var href = link.attr('href'), 
method = link.data('method'), 
csrf_token = $('meta[name=csrf-token]').attr('content'), 
csrf_param = $('meta[name=csrf-param]').attr('content'), 
form = $('<form method="post" action="' + href + '"></form>'), 
metadata_input = '<input name="_method" value="' + method + '" type="hidden" />'; 
if (csrf_param !== undefined && csrf_token !== undefined) { 
metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />'; 
} 
form.hide().append(metadata_input).appendTo('body'); 
form.submit(); 
} 

Y Capybara helper visit ('/ users/sign_out') simplemente hace clic en el enlace y envía una solicitud GET al servidor que no tiene ninguna ruta para esta solicitud.

A diferencia de helper link_to el ayudante button_to añade la forma requerida dentro del HTML cuando la página se hace en lugar de confiar en javascript:

<%= button_to "Logout", destroy_user_session_path, :method => :delete %> 

genera código HTML siguiente

<form class="button_to" action="https://stackoverflow.com/users/sign_out" method="post"> 
    <div> 
     <input type="hidden" name="_method" value="delete"> 
     <input type="submit" value="Logout"> 
     <input type="hidden" name="authenticity_token" value="0Il8D+7hRcWYfl7A1MjNPenDixLYZUkMBL4OOoryeJs="> 
    </div> 
</form> 

con esto puedo utilizar fácilmente Capybara helper click_button ('Logout') en mi definición de paso 'I sesión'.

"link_to with a method anything other than GET is actually a bad idea, as links can be right clicked and opened in a new tab/window, and because this just copies the url (and not the method) it will break for non-get links..."

Como Max Will explained clic derecho y abrir el enlace con link_to no obtener datos método de nuevos resultados de pestaña en un enlace roto.

Algunos discusión más útil sobre helper link_to con ': method =>: borrar' y emisión capibara se puede encontrar on this link

Por ahora me gustaría seguir a la simple ayudante link_to sin: atributo del método, y prefiere usar button_to si quiero cambiar al método "no obtener" para eliminar.

Al mismo tiempo, creo que debe haber un asistente de capibara equivalente a Visit para atender el atributo de método de datos para enviar una solicitud posterior, de modo que se pueda evitar el uso de un controlador basado en JavaScript para realizar pruebas de integración. O puede estar allí ya hay uno del que no estoy enterado. Corrígeme si estoy equivocado.

+1

Gracias por tomarse el tiempo para investigar y documentar este problema. –

+2

¿Tiene alguna sugerencia sobre cómo puedo mejorar el tutorial en http://github.com/RailsApps/rails3-devise-rspec-cucumber/wiki/Tutorial para que otras personas no encuentren este problema? –

+0

Creo que es importante tener en cuenta que el problema fundamental aquí no tiene nada que ver con jQuery versus Prototype, o Cumcumber/Capybara. La aplicación de inicio rails3-devise-rspec-cucumber se rompe en su estado "listo para usar". Está roto porque el enlace para cerrar la sesión se procesa con 'link_to 'Logout' destroy_user_session_path' que simplemente no funciona con las rutas provistas por Rails 3.1rc4 y Devise 1.4.2. Esto * would * ha funcionado con Rails 3.0.6 y Devise 1.3.3 porque el destroy_user_session_path respondió o una solicitud GET. Creo que el lugar para comenzar es corregir este problema. – cailinanne

8

La manera más fácil de corregir este problema (aunque probablemente no sea la más correcta) es modificar el archivo de rutas para que coincida con el resto de la aplicación. P.ej. hacer que funcione la versión GET de destroy_user_session_path. Puede hacerlo modificando el archivo de rutas de la siguiente manera

Quitar:

devise_for :users 

Añadir:

devise_for :users do 
    get "https://stackoverflow.com/users/sign_out" => "devise/sessions#destroy", :as => :destroy_user_session 
end 

Esto es un poco sucio. Estoy seguro de que Devise desaprobó la ruta GET por una buena razón.Sin embargo, arreglarlo de otra forma está más allá de mi conocimiento de Cucumber en este punto, ya que cada prueba en ese conjunto depende finalmente de la visita ('/ users/logout') que simplemente no es posible con el dispositivo listo para usar. rutas

ACTUALIZACIÓN

También puede solucionar este problema comentando lo siguiente en config/initialers/devise.rb

#config.sign_out_via = :delete 
+0

Gran respuesta. lo que estaba buscando – jlstr

8

Diseñar 1.4.1 (27 de junio de 2011) cambió el comportamiento predeterminado de firmar peticiones a cabo:

https://github.com/plataformatec/devise/commit/adb127bb3e3b334cba903db2c21710e8c41c2b40

José Valim explican por qué: "GET req Las misiones no deberían cambiar el estado del servidor. Cuando cerrar sesión es una solicitud GET, CSRF se puede utilizar para cerrar sesión automáticamente y las cosas que precargan enlaces pueden eventualmente cerrar la sesión por error también. "

Pepino quiere probar las solicitudes GET no DELETE las solicitudes de destroy_user_session_path. tiene la intención de utilizar pepino con Idear, cambiar el valor predeterminado Legado de bORRAR para conseguir para el entorno de prueba rieles sólo con este cambio en config/initializers/devise.rb:

config.sign_out_via = Rails.env.test:?? obtener:: eliminar

No intente modificar el archivo routes.rb t o hacer la corrección. No es necesario Si no va a utilizar Pepino, deje el nuevo valor predeterminado de DEVISE (ELIMINAR) en su lugar.

El código fuente de ejemplo aquí:

https://github.com/RailsApps/rails3-devise-rspec-cucumber

incluye ahora el cambio en el inicializador para Diseñar pepino.

La plantilla de aplicación aquí:

https://github.com/RailsApps/rails3-application-templates

ahora detecta la colisión entre Diseñar y pepino y altera el inicializador Diseñar según sea necesario.

Estos cambios se probaron con Rails 3.1.0.rc4 pero el comportamiento debería ser el mismo con Rails 3.0.9. Agregue comentarios aquí si el problema no está resuelto o si tiene más información.

0

En realidad estoy teniendo el mismo problema pero con la aplicación Rails/Sinatra. Tengo a Devise configurado para Rails y el cierre de sesión funciona. Tengo una aplicación GuestApp Sinatra ejecutándose en lib, que funciona muy bien, excepto por el enlace de cierre de sesión. Estoy intentando forzar el método-datos = "eliminar" en el enlace de cierre de sesión de sinatra, pero nada de lo que haga hará que la solicitud sea eliminada.

Creo que esto podría ser un problema de sinatra para mí, pero pensé que las solicitudes entrantes son procesadas por las rutas de los raíles primero hasta que lleguen a mi ruta de Sinatra. Estoy a punto de agregar manualmente la ruta GET para cerrar la sesión, pero prefiero no tener que hacer eso.

Aquí está mi rutas Idear:

devise_for :member, :path => '', :path_names => { 
    :sign_in => "login", 
    :sign_out => "logout", 
    :sign_up => "register" } 

Aquí está mi enlace:

%a{:href => '/logout', :"data-method" => 'delete', :rel => 'nofollow'}Log Out 
<a href="/logout" data-method="delete" rel="nofollow">Log Out</a> 

#- realized it should be method instead, but still not reaching routes.rb as delete 
<a href="/logout" method="delete" rel="nofollow">Log Out</a> 
+0

Asegúrate de incluir los 'rails.js' discretos helpers de Javascript en las páginas que Sinatra está renderizando. Usted _desea_ que el atributo en el HTML renderizado sea 'método-datos' y no' método', pero los ayudantes Rails JS son los que buscan este atributo de datos y lo transforma antes de enviarlo. _EDIT: _ El código que hace que funcione [está aquí] (https://github.com/rails/jquery-ujs/blob/master/src/rails.js#L153-L169) si tiene curiosidad (asumiendo que estás usando jQuery). – ches

4

La forma correcta de resolver este problema se explica en la página wiki del legado: https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara

Básicamente, una vez que ha incluido en su archivo user_step.rb:

include Warden::Test::Helpers 
Warden.test_mode! 

Puede reemplazarse con visit '/users/sign_out'logout(:user)

+1

Esta es la respuesta correcta.Lamentablemente, va a ser difícil superar las soluciones más votadas. – lime

0

cuando necesito usar algo como esto en test.env:

visit destroy_user_session_path 

es un trabajo para mí, pero tal vez esto no es correcto)

config/init/devise.rb

# The default HTTP method used to sign out a resource. Default is :delete. 
    if Rails.env.test? 
    config.sign_out_via = :get 
    else 
    config.sign_out_via = :delete 
    end 
Cuestiones relacionadas