Rails + Jasmine-Ajax: какой правильный способ проверить код, вызванный `ajax: success` (jquery-ujs)

Я пытаюсь протестировать определенную внутреннюю библиотеку, которая имеет некоторый JS-режим, срабатывающий в событии ajax:success.

Библиотека создает ссылку, которая выглядит так:

<%= link_to 'click here', '/some_path', class: 'special-link', remote: true %>

И в JS-части библиотеки есть код привязки событий, , который является частью, которую я хочу проверить с помощью черного ящика через ее эффект на 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

Библиотека работает как ожидается в браузере. Однако, когда я пытаюсь протестировать библиотеку в Жасмине, вызывая $('.special-link').click(), желаемого эффекта на DOM не наблюдается.

Проблема, похоже, в том, что событие ajax:success не запускается:

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()

Каков правильный способ проверить влияние на DOM кода, который выполняется в событиях ajax:success?

Ответ 1

Вы пробовали просто следить за функцией ajax? Для этого вам нужно использовать spyOn и заставить его вызвать обработчик события success. Это позволит вам проверить, что вы ожидаете, когда оно будет вызвано.

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 DOM

Ответ 2

Вот как мы будем обращаться с такими вещами в моей команде.

it 'knows how to handle ajax:success events', ->
  spyOn($.fn, 'on');
  $('.special-link').click()
  expect($.fn.on).toHaveBeenCalledWith('ajax:success', 
                                       '.special-link'
                                       some_func);

Этот шаблон хорошо подходит для тестирования других событий 'on'. Скажем, у нас есть несколько jQuery:

$document.on('myCustomEvent', '.some_selector', somecode.custom_func);
$document.on('ajax:error', '.some_selector', somecode.failure_func);

Затем мы можем протестировать его с помощью этого шаблона:

beforeEach ->
  spyOn($.fn, 'on');
  somecode.init();

Тестирование отказа Ajax

it('indicates failure after ajax error', ->
  expect($.fn.on).toHaveBeenCalledWith('ajax:error',
                                       '.some_selector',
                                       somecode.failure_func);

Тестирование Ajax вызывается из настраиваемого события

it('indicates ajax call from custom event', ->
  expect($.fn.on).toHaveBeenCalledWith('myCustomEvent',
                                       '.some_selector',
                                       somecode.custom_func);

Ответ 3

После много отладки я нашел решение.

Когда я отправил свой вопрос, я сделал 3 критических ошибки.

Ошибка №1: jasmine.Ajax.stubRequest путь не относительный

Вызов Ajax не был правильно закодирован, поскольку при тестировании в браузере путь должен быть не относительным /some_path, а абсолютным http://localhost:3000/some_path.

Другими словами, вместо:

jasmine.Ajax.stubRequest('/some_path')

Я должен был использовать версию regexp:

jasmine.Ajax.stubRequest(/.*\/some_path/)

Ошибка №2: jasmine.Ajax.andReturn должна включать cotentType

Вместо:

jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({
  responseText: 'response that is supposed to trigger some effect on the DOM'})

Я должен был сделать:

jasmine.Ajax.stubRequest(/.*\/some_path/).andReturn({
      contentType: 'text/html;charset=UTF-8',
      responseText: 'response that is supposed to trigger some effect on the DOM'})

Без него ajax:error запускается, а не ajax:success, с parseerror.

Ошибка №3: ​​Обработчик ajax:success называется async-ly

Эти строки кода:

spy = jasmine.createSpy('my spy')
$('.special-link').on 'ajax:success', spy
$('.special-link').click()
expect(spy).toHaveBeenCalled()

не работают, поскольку обработчик ajax:success, вызывающий spy(), вызывается асинхронно после достижения expect(spy).toHaveBeenCalled(). Подробнее об этом можно узнать в документации Jasmine.

Объединяя все вместе

Это код, который работает, фокусируясь только на последнем it заявлении, которое было основным намерением оригинального вопроса:

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()

Надеюсь, что это поможет другим.