ThomasBurleson
11/16/2011 - 11:59 PM

Simple jQuery (1.5+) AJAX Mocking (requires JSON, tested in jQuery 1.7))

Simple jQuery (1.5+) AJAX Mocking (requires JSON, tested in jQuery 1.7))

// Simulate your API.

$.mockAjax("json", {
  "/user": {status: -1},
  "/user/(\\d+)": function(matches) {
    return {status: 1, user: "sample user " + matches[1]};
  }
});

// Unit tests.

test("user tests", function() {
  expect(5);

  stop();
  $.getJSON("/user", function(data) {
    ok(data, "data is returned from the server");
    equal(data.status, "-1", "no user specified, status should be -1");
    start();
  });

  stop();
  $.getJSON("/user/123", function(data) {
    ok(data, "data is returned from the server");
    equal(data.status, "1", "user found, status should be 1");
    equal(data.user, "sample user 123", "user found, id should be 123");
    start();
  });
});
/*!
 * Simple jQuery (1.5+) AJAX Mocking - v0.1.0 - 11/16/2011
 * http://benalman.com/
 *
 * Copyright (c) 2011 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

(function($) {

  // Process all rules for a given AJAX request.
  function processRules(options) {
    // The dataType (eg. "json").
    var dataType = options.dataType;
    // If a rule is matched, override the built-in transport.
    var transport;
    // Iterate over all specified rules for this dataType
    $.each($.mockAjax.rules[dataType], function(_, rule) {
      // Test the AJAX request URL against this rule's regexp.
      var matches = options.url.match(rule.re);
      // If there was a match, override the default transport.
      if (matches) {
        transport = {
          // Override the transport's send to immediately return a result.
          send: function(_, done) {
            // Get the response value.
            var response = rule.response;
            // If the response is a function, invoke it, passing in the matches
            // array and the AJAX request options, and get its result.
            if ($.isFunction(response)) {
              response = response(matches, options);
            }
            // If the dataType is "json" or "jsonp" and not a string, serialize
            // it into a valid JSON string. Note: requires JSON!
            if (/^json/.test(dataType) && typeof response !== "string") {
              response = window.JSON ? JSON.stringify(response) : String(response);
            }
            // Respond successfully!
            done("200", "success", {status: response});
          },
          // Don't do anything on abort. Don't abort. Should this do anything?
          abort: $.noop
        };
        // Don't process any other rules for this AJAX request.
        return false;
      }
    });
    return transport;
  }

  // Mock AJAX requests for a given dataType and map of rules.
  $.mockAjax = function(dataType, userRules) {
    var rules = $.mockAjax.rules[dataType];

    // If no rules exist for this datatype, create a place to store them and
    // register an ajax transport handler for that datatype.
    if (!rules) {
      rules = $.mockAjax.rules[dataType] = {};
      $.ajaxTransport(dataType, processRules);
    }

    // For each user rule specified, add an entry into this dataType's rules
    // object, overwriting any already-existing rule with the same pattern.
    $.each(userRules, function(pattern, response) {
      rules[pattern] = {
        // Compile a matching regexp up-front to save processing later.
        re: new RegExp("^" + pattern + "$"),
        // Store the response value / function.
        response: response
      };
    });
  };

  // Initialize an empty rules object.
  $.mockAjax.rules = {};

}(jQuery));
    // mock ajax
    $.ajaxPrefilter( function( options, originalOptions, jqXHR ) {

        if ( /^\/?mock-ajax/.test( originalOptions.url ) ) {
            // make new deferred and save any success/error handlers
            var success = options.success,
                error   = options.error;
        
                // kill off the old handlers
                options.success = options.error = null;

            var promise = $.Deferred( function(dfd) {
                var echo   = originalOptions.data.echo   || {},
                    delay  = originalOptions.data.delay  || 50,
                    status = originalOptions.data.status || true;
                    
                    // Use the Deferred to simulate success and error
                    dfd.done( function() {
                           success.apply( options, arguments );         
                        })
                        .fail( function(a,b,c) {
                           error.apply( options, arguments );
                        });

                    // resolve the data
                    setTimeout( function() {
                        dfd[ status ? 'resolve' : 'reject' ]( echo, 'success', jqXHR );
                    }, delay );

                    // !! Abort out of this function, so the promise still gets returned via $.ajax
                    setTimeout(function(){
                        jqXHR.abort( 'success' );
                    }, 0);   
            
            }).promise( jqXHR );  // map these promise methods onto the jqXHR
        }
   
    });

    $.ajax({
        url: '/mock-ajax/390?thing=poop&pop[]=1&pop[]=2',
        type: 'GET',
        data: {
            echo: 'gimme',
            delay: 2000
        },
        success: function( data ) {
            console.log( 'SUCCESS!' );
           console.dir( data );
        },
        error: function(){
            console.log( 'failed :/');
        }
    }).always( function(a,b,c) {
        console.log( 'promise done', a,b,c );
    });