Skip to content

Instantly share code, notes, and snippets.

@davidjbeveridge
Created January 23, 2014 19:34
Show Gist options
  • Save davidjbeveridge/8585283 to your computer and use it in GitHub Desktop.
Save davidjbeveridge/8585283 to your computer and use it in GitHub Desktop.
utility for spying on jQuery.ajax request by url, type, data.
var ajaxSpy = (function($){
var oldAjax = $.ajax;
var requests = [];
var watchers = [];
function newAjax(opts){
// execute real $.ajax and save the promise
var promise = oldAjax.apply(this, arguments);
requests.push({
opts: opts,
promise: promise
});
promise
.then(responseSuccess(promise))
.fail(responseFailure(promise));
return promise;
}
function addWatcher(match, success, failure) {
watchers.push([match, success, failure]);
}
function responseSuccess(promise) {
var request = _.find(requests, function(r) {
return r.promise === promise;
});
return function(data, status, xhr){
fireSuccessWatcher(request, data, status, xhr);
};
}
function fireSuccessWatcher(request, data, status, xhr) {
var applicableWatchers = _.filter(watchers, watcherMatches(request));
_.each(applicableWatchers, function(watcher) {
var success = watcher[1];
if(typeof success === 'function') {
success(data, status, xhr);
}
});
}
function responseFailure(promise) {
var request = _.find(requests, function(r) {
return r.promise === promise;
});
return function(xhr, status, err){
fireFailureWatcher(request, xhr, status, err);
};
}
function fireFailureWatcher(request, xhr, status, err) {
var applicableWatchers = _.filter(watchers, watcherMatches(request));
_.each(applicableWatchers, function(watcher) {
var fail = watcher[2];
if(typeof fail === 'function') {
fail(xhr, status, err);
}
});
}
function watcherMatches(request) {
return function(watcher){
return _.isEqual(watcher, request.opts);
};
}
// public interface
return {
start: function() {
$.ajax = newAjax;
},
end: function() {
$.ajax = oldAjax;
},
watch: addWatcher
};
})(jQuery);
@rschifflin
Copy link

The matching wasn't quite right. Here's a slightly tweaked version:

var ajaxSpy = (function($){
  var oldAjax = $.ajax;
  var requests = [];
  var watchers = [];

  function newAjax(opts){
    // execute real $.ajax and save the promise
    var promise = oldAjax.apply(this, arguments);
    requests.push({
      opts: opts,
      promise: promise
    });
    promise
      .then(responseSuccess(promise))
      .fail(responseFailure(promise));
    return promise;
  }

  function addWatcher(match, success, failure) {
    watchers.push([match, success, failure]);
  }

  function responseSuccess(promise) {
    var request = _.find(requests, function(r) {
      return r.promise === promise;
    });
    return function(data, status, xhr){
      fireSuccessWatcher(request, data, status, xhr);
    };
  }

  function fireSuccessWatcher(request, data, status, xhr) {
    var applicableWatchers = _.filter(watchers, watcherMatches(request));
    _.each(applicableWatchers, function(watcher) {
      var success = watcher[1];
      if(typeof success === 'function') {
        success(data, status, xhr);
      }
    });
  }

  function responseFailure(promise) {
    var request = _.find(requests, function(r) {
      return r.promise === promise;
    });
    return function(xhr, status, err){
      fireFailureWatcher(request, xhr, status, err);
    };
  }

  function fireFailureWatcher(request, xhr, status, err) {
    var applicableWatchers = _.filter(watchers, watcherMatches(request));
    _.each(applicableWatchers, function(watcher) {
      var fail = watcher[2];
      if(typeof fail === 'function') {
        fail(xhr, status, err);
      }
    });
  }

  function watcherMatches(request) {
    return function(watcher){
      console.log(watcher[0]);
      console.log(request.opts);
      return (
        _.isEqual(watcher[0].url, request.opts.url)   && 
        _.isEqual(watcher[0].type, request.opts.type) &&
        _.isEqual(watcher[0].data, request.opts.data)
      );
    };
  }


  // public interface
  return {
    start: function() {
      $.ajax = newAjax;
    },
    end: function() {
      $.ajax = oldAjax;
    },
    watch: addWatcher
  };
})(jQuery);

Below are the ruby helpers I added for simple capybara testing- it doesn't actually care about success or failure, it just uses a flag to get notified when it's safe to refresh the page.

def listen_to_ajax(req)
  page.execute_script("ajaxSpy.start();")
  add_ajax_watcher(req)
end

def stop_listening_to_ajax
  page.execute_script("ajaxSpy.end();")
end

def add_ajax_watcher(req)
  page.execute_script("""
    ajax_returned = null;
    ajaxSpy.watch(
      #{req},
      function(){ ajax_returned = true },
      function(){ ajax_returned = true }
    );
    """)
end

def wait_for_ajax(opts = {})
  opts = { timeout: Capybara.default_wait_time }.merge(opts)
  opts[:timeout].times do 
    break if page.evaluate_script('ajax_returned !== null') 
    sleep 1
  end
  stop_listening_to_ajax
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment