Estoy tratando de probar una determinada biblioteca interna que tiene algún comportamiento JS activado en el evento ajax:success .
La biblioteca crea un enlace que se ve así:
<%= link_to 'click here', '/some_path', class: 'special-link', remote: true %>Y en la parte JS de la biblioteca hay un código de enlace de eventos, que es la parte que quiero probar en la caja negra a través de su efecto en el DOM :
$(document).on 'ajax:success', '.special-link', (e, data, status, xhr) -> # Code that has some effect on the DOM as a function of the server response La biblioteca funciona como se esperaba en el navegador. Sin embargo, cuando trato de probar la biblioteca en Jasmine llamando a $('.special-link').click() , no se puede observar el efecto deseable en el DOM.
El problema, al parecer, es que el evento ajax:success no se activa:
describe 'my library', -> beforeEach -> MagicLamp.load('fixture') # Fixture library that injects the link above to the DOM jasmine.Ajax.install() jasmine.Ajax.stubRequest('/some_path').andReturn({ responseText: 'response that is supposed to trigger some effect on the DOM'}) afterEach -> jasmine.Ajax.uninstall() # Works. The fixtures are loading properly it '[sanity] loads fixtures correctly', -> expect($('.special-link').length).toEqual(1) # Works. The jquery-ujs correctly triggers an ajax request on click it '[sanity] triggers the ajax call', -> $('.special-link').click() expect(jasmine.Ajax.requests.mostRecent().url).toContain('/some_path') # Works. Code that tests a click event-triggering seems to be supported by Jasmine it '[sanity] knows how to handle click events', -> spy = jasmine.createSpy('my spy') $('.special-link').on 'click', spy $('.special-link').click() expect(spy).toHaveBeenCalled() # Does not work. Same code from above on the desired `ajax:success` event does not work it 'knows how to handle ajax:success events', -> spy = jasmine.createSpy('my spy') $('.special-link').on 'ajax:success', spy $('.special-link').click() expect(spy).toHaveBeenCalled() ¿Cuál es la forma correcta de probar el efecto en el DOM del código que se ejecuta en ajax:success ?
¿Has intentado simplemente espiar la función ajax ? Para eso, necesita usar spyOn y obligarlo a llamar al controlador de eventos success . Eso le permitirá probar lo que espera que suceda cuando se llame.
it 'knows how to handle ajax:success events', -> spyOn($, "ajax").and.callFake( (e) -> e.success({}); ) $('.special-link').click() # expect some method to be called or something to be changed in the DOMAsí es como manejaríamos este tipo de cosas en mi equipo.
it 'knows how to handle ajax:success events', -> spyOn($.fn, 'on'); $('.special-link').click() expect($.fn.on).toHaveBeenCalledWith('ajax:success', '.special-link' some_func);Este patrón también se extiende bien para probar otros eventos 'encendidos'. Digamos que tenemos algo de jQuery como este:
$document.on('myCustomEvent', '.some_selector', somecode.custom_func); $document.on('ajax:error', '.some_selector', somecode.failure_func);Entonces podemos probarlo usando este patrón:
beforeEach -> spyOn($.fn, 'on'); somecode.init();Probando una falla de Ajax
it('indicates failure after ajax error', -> expect($.fn.on).toHaveBeenCalledWith('ajax:error', '.some_selector', somecode.failure_func);La prueba de Ajax fue llamada desde un evento personalizado
it('indicates ajax call from custom event', -> expect($.fn.on).toHaveBeenCalledWith('myCustomEvent', '.some_selector', somecode.custom_func);Después de mucha depuración, encontré una solución.
Cuando publiqué mi pregunta, cometí 3 errores críticos.
jasmine.Ajax.stubRequest no es relativa La llamada Ajax no se interrumpió correctamente, ya que la ruta no debería ser la relativa /some_path sino la absoluta http://localhost:3000/some_path cuando se prueba desde el navegador.
En otras palabras, en lugar de:
jasmine.Ajax.stubRequest('/some_path')Debería haber usado la versión regexp:
jasmine.Ajax.stubRequest(/.*\/some_path/)jasmine.Ajax.andReturn debe incluir cotentTypeEn vez de:
jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({ responseText: 'response that is supposed to trigger some effect on the DOM'})Yo debí haber hecho:
jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({ contentType: 'text/html;charset=UTF-8', responseText: 'response that is supposed to trigger some effect on the DOM'}) Sin él, se activa ajax:error en lugar de ajax:success , con un parseerror .
ajax:success se llama asíncronoEstas líneas de código:
spy = jasmine.createSpy('my spy') $('.special-link').on 'ajax:success', spy $('.special-link').click() expect(spy).toHaveBeenCalled() no funcionan, ya que el controlador ajax:success que llama a spy() se llama de forma asíncrona después de alcanzar expect(spy).toHaveBeenCalled() . Puede leer más sobre esto en la documentación de Jasmine .
Este es el código que funciona, centrándose solo en la última declaración it que era la intención principal detrás de la pregunta original:
describe 'my library', -> beforeEach -> MagicLamp.load('fixture') # Fixture library that injects the link above to the DOM jasmine.Ajax.install() jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({ contentType: 'text/html;charset=UTF-8', responseText: 'response that is supposed to trigger some effect on the DOM'}) afterEach -> jasmine.Ajax.uninstall() # Restructuring the original `it` statement to allow async handling describe 'ajax:success event handling', -> spy = jasmine.createSpy('spy') # Ensures no `it` statement runs before `done()` is called beforeEach (done) -> $('.special-link').on 'ajax:success', -> spy() done() $('.special-link').click() it 'knows how to handle ajax:success events', -> expect(spy).toHaveBeenCalled()Espero que esto ayude a otros.