diff --git a/dom/fixture/fixture.js b/dom/fixture/fixture.js index d2355033..07b0dcc6 100644 --- a/dom/fixture/fixture.js +++ b/dom/fixture/fixture.js @@ -1,22 +1,22 @@ steal('jquery/dom', 'jquery/lang/object', 'jquery/lang/string',function( $ ) { - + //used to check urls - - + + // the pre-filter needs to re-route the url - + $.ajaxPrefilter( function( settings, originalOptions, jqXHR ) { // if fixtures are on if(! $.fixture.on) { return; } - + // add the fixture option if programmed in var data = overwrite(settings); - + // if we don't have a fixture, do nothing if(!settings.fixture){ if(window.location.protocol === "file:"){ @@ -24,19 +24,23 @@ steal('jquery/dom', } return; } - + //if referencing something else, update the fixture option if ( typeof settings.fixture === "string" && $.fixture[settings.fixture] ) { settings.fixture = $.fixture[settings.fixture]; } - + // if a string, we just point to the right url if ( typeof settings.fixture == "string" ) { var url = settings.fixture; - + if (/^\/\//.test(url) ) { url = steal.root.mapJoin(settings.fixture.substr(2))+''; } + // replace {id} of templated static fixture + if(data){ + url = $.String.sub(url, data); + } //!steal-remove-start steal.dev.log("looking for fixture in " + url); //!steal-remove-end @@ -53,40 +57,41 @@ steal('jquery/dom', //!steal-remove-start steal.dev.log("using a dynamic fixture for " +settings.type+" "+ settings.url); //!steal-remove-end - + //it's a function ... add the fixture datatype so our fixture transport handles it // TODO: make everything go here for timing and other fun stuff settings.dataTypes.splice(0,0,"fixture"); - + if(data){ - $.extend(originalOptions.data, data) + originalOptions.data = originalOptions.data || {}; + $.extend(originalOptions.data, data); } // add to settings data from fixture ... - + } - + }); - - + + $.ajaxTransport( "fixture", function( s, original ) { // remove the fixture from the datatype s.dataTypes.shift(); - + //we'll return the result of the next data type var next = s.dataTypes[0], timeout; - + return { - + send: function( headers , callback ) { - + // callback after a timeout timeout = setTimeout(function() { - + // get the callback data from the fixture function var response = s.fixture(original, s, headers); - + // normalize the fixture data into a response if(!$.isArray(response)){ var tmp = [{}]; @@ -96,24 +101,24 @@ steal('jquery/dom', if(typeof response[0] != 'number'){ response.unshift(200,"success") } - + // make sure we provide a response type that matches the first datatype (typically json) if(!response[2] || !response[2][next]){ var tmp = {} tmp[next] = response[2]; response[2] = tmp; } - + // pass the fixture data back to $.ajax callback.apply(null, response ); }, $.fixture.delay); }, - + abort: function() { clearTimeout(timeout) } }; - + }); @@ -144,7 +149,9 @@ steal('jquery/dom', * @param {Object} settings */ getId = function(settings){ - var id = settings.data.id; + // In GET, PUT and DELETE calls data is always passed + // eg PUT /api/v1/orders/1/approve - To approve order 1 + var id = settings.data && settings.data.id; if(id === undefined && typeof settings.data === "number") { id = settings.data; @@ -163,7 +170,7 @@ steal('jquery/dom', id = num; }); } - + if(id === undefined){ id = settings.url.replace(/\/(\w+)(\/|$|\.)/g, function(all, num){ if(num != 'update'){ @@ -171,7 +178,7 @@ steal('jquery/dom', } }) } - + if(id === undefined){ // if still not set, guess a random number id = Math.round(Math.random()*1000) } @@ -185,104 +192,104 @@ steal('jquery/dom', * @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/dom/fixture/fixture.js * @test jquery/dom/fixture/qunit.html * @parent dom - * + * * $.fixture intercepts a AJAX request and simulates - * the response with a file or function. They are a great technique - * when you want to develop JavaScript - * independently of the backend. - * + * the response with a file or function. They are a great technique + * when you want to develop JavaScript + * independently of the backend. + * * ## Types of Fixtures - * - * There are two common ways of using fixtures. The first is to - * map Ajax requests to another file. The following - * intercepts requests to /tasks.json and directs them + * + * There are two common ways of using fixtures. The first is to + * map Ajax requests to another file. The following + * intercepts requests to /tasks.json and directs them * to fixtures/tasks.json: - * + * * $.fixture("/tasks.json","fixtures/tasks.json"); - * + * * The other common option is to generate the Ajax response with * a function. The following intercepts updating tasks at * /tasks/ID.json and responds with updated data: - * + * * $.fixture("PUT /tasks/{id}.json", function(original, settings, headers){ * return { updatedAt : new Date().getTime() } * }) - * + * * We categorize fixtures into the following types: - * + * * - __Static__ - the response is in a file. * - __Dynamic__ - the response is generated by a function. - * + * * There are different ways to lookup static and dynamic fixtures. - * + * * ## Static Fixtures - * + * * Static fixtures use an alternate url as the response of the Ajax request. - * + * * // looks in fixtures/tasks1.json relative to page * $.fixture("tasks/1", "fixtures/task1.json"); - * + * * $.fixture("tasks/1", "//fixtures/task1.json"); - * + * * ## Dynamic Fixtures - * - * Dynamic Fixtures are functions that get the details of + * + * Dynamic Fixtures are functions that get the details of * the Ajax request and return the result of the mocked service - * request from your server. - * - * For example, the following returns a successful response + * request from your server. + * + * For example, the following returns a successful response * with JSON data from the server: - * + * * $.fixture("/foobar.json", function(orig, settings, headers){ * return [200, "success", {json: {foo: "bar" } }, {} ] * }) - * + * * The fixture function has the following signature: - * + * * function( originalOptions, options, headers ) { * return [ status, statusText, responses, responseHeaders ] * } - * + * * where the fixture function is called with: - * + * * - originalOptions - are the options provided to the ajax method, unmodified, * and thus, without defaults from ajaxSettings * - options - are the request options * - headers - a map of key/value request headers - * + * * and the fixture function returns an array as arguments for ajaxTransport's completeCallback with: - * + * * - status - is the HTTP status code of the response. * - statusText - the status text of the response * - responses - a map of dataType/value that contains the responses for each data format supported * - headers - response headers - * - * However, $.fixture handles the - * common case where you want a successful response with JSON data. The + * + * However, $.fixture handles the + * common case where you want a successful response with JSON data. The * previous can be written like: - * + * * $.fixture("/foobar.json", function(orig, settings, headers){ * return {foo: "bar" }; * }) - * + * * If you want to return an array of data, wrap your array in another array: - * + * * $.fixture("/tasks.json", function(orig, settings, headers){ * return [ [ "first","second","third"] ]; * }) - * - * $.fixture works closesly with jQuery's + * + * $.fixture works closesly with jQuery's * ajaxTransport system. Understanding it is the key to creating advanced * fixtures. - * + * * ### Templated Urls - * - * Often, you want a dynamic fixture to handle urls + * + * Often, you want a dynamic fixture to handle urls * for multiple resources (for example a REST url scheme). $.fixture's - * templated urls allow you to match urls with a wildcard. - * - * The following example simulates services that get and update 100 todos. - * + * templated urls allow you to match urls with a wildcard. + * + * The following example simulates services that get and update 100 todos. + * * // create todos * var todos = {}; * for(var i = 0; i < 100; i++) { @@ -299,25 +306,37 @@ steal('jquery/dom', * $.fixture("PUT /todos/{id}", function(orig){ * // update the todo's data * $.extend( todos[orig.data.id], orig.data ); - * + * * // return data * return {}; * }) - * + * * Notice that data found in templated urls (ex: {id}) is added to the original * data object. - * + * + * Templated urls can also be used with templated static fixtures. + * + * $.fixture({ + * type : "GET", + * url : "/todos/{id}", + * }, "//jquery/dom/fixture/fixtures/test.{id}.json"); + * + * // below ajax call looks for jquery/dom/fixture/fixtures/test.911.json fixture + * $.get("/todo/911", function(data) { + * // data is json object constructed from //jquery/dom/fixture/fixtures/test.911.json fixture + * }, "json"); + * * ## Simulating Errors - * - * The following simulates an unauthorized request + * + * The following simulates an unauthorized request * to /foo. - * + * * $.fixture("/foo", function(){ * return [401,"{type: 'unauthorized'}"] * }); - * + * * This could be received by the following Ajax request: - * + * * $.ajax({ * url: '/foo', * error : function(jqXhr, status, statusText){ @@ -325,55 +344,55 @@ steal('jquery/dom', * // statusText === "{type: 'unauthorized'}" * } * }) - * + * * ## Turning off Fixtures - * + * * You can remove a fixture by passing null for the fixture option: - * + * * // add a fixture * $.fixture("GET todos.json","//fixtures/todos.json"); - * + * * // remove the fixture * $.fixture("GET todos.json", null) - * + * * You can also set [jQuery.fixture.on $.fixture.on] to false: - * + * * $.fixture.on = false; - * + * * ## Make - * + * * [jQuery.fixture.make $.fixture.make] makes a CRUD service layer that handles sorting, grouping, * filtering and more. - * + * * ## Testing Performance - * - * Dynamic fixtures are awesome for performance testing. Want to see what + * + * Dynamic fixtures are awesome for performance testing. Want to see what * 10000 files does to your app's performance? Make a fixture that returns 10000 items. - * + * * What to see what the app feels like when a request takes 5 seconds to return? Set * [jQuery.fixture.delay] to 5000. - * + * * @demo jquery/dom/fixture/fixture.html - * - * @param {Object|String} settings Configures the AJAX requests the fixture should + * + * @param {Object|String} settings Configures the AJAX requests the fixture should * intercept. If an __object__ is passed, the object's properties and values - * are matched against the settings passed to $.ajax. - * + * are matched against the settings passed to $.ajax. + * * If a __string__ is passed, it can be used to match the url and type. Urls - * can be templated, using {NAME} as wildcards. - * - * @param {Function|String} fixture The response to use for the AJAX + * can be templated, using {NAME} as wildcards. + * + * @param {Function|String} fixture The response to use for the AJAX * request. If a __string__ url is passed, the ajax request is redirected * to the url. If a __function__ is provided, it looks like: - * + * * fixture( originalSettings, settings, headers ) - * + * * where: - * + * * - originalSettings - the orignal settings passed to $.ajax * - settings - the settings after all filters have run * - headers - request headers - * + * * If __null__ is passed, and there is a fixture at settings, that fixture will be removed, * allowing the AJAX request to behave normally. */ @@ -393,23 +412,23 @@ steal('jquery/dom', type: matches[1] }; } - + } - + //handle removing. An exact match if fixture was provided, otherwise, anything similar var index = find(settings, !!fixture); if(index > -1){ overwrites.splice(index,1) } if(fixture == null){ - return + return } settings.fixture = fixture; overwrites.push(settings) } }; var replacer = $.String._regs.replacer; - + $.extend($.fixture, { // given ajax settings, find an overwrite _similar : function(settings, overwrite, exact){ @@ -435,7 +454,7 @@ steal('jquery/dom', return "([^\/]+)" })+"$").exec(url), data = {}; - + if(!res){ return null; } @@ -456,7 +475,7 @@ steal('jquery/dom', location: settings.url+"/"+getId(settings) }]; }, - + /** * @hide * Provides a rest destroy fixture function @@ -464,7 +483,7 @@ steal('jquery/dom', "-restDestroy": function( settings, cbType ) { return {}; }, - + /** * @hide * Provides a rest create fixture function @@ -474,15 +493,15 @@ steal('jquery/dom', return [200,"succes",{ id: id },{ - location: settings.url+"/"+id + location: settings.url+"/"+id }]; }, - + /** * @function jQuery.fixture.make * @parent jQuery.fixture * Used to make fixtures for findAll / findOne style requests. - * + * * //makes a nested list of messages * $.fixture.make(["messages","message"],1000, function(i, messages){ * return { @@ -495,16 +514,16 @@ steal('jquery/dom', * //uses the message fixture to return messages limited by offset, limit, order, etc. * $.ajax({ * url: "messages", - * data:{ - * offset: 100, - * limit: 50, + * data:{ + * offset: 100, + * limit: 50, * order: ["date ASC"], * parentId: 5}, * }, * fixture: "-messages", * success: function( messages ) { ... } * }); - * + * * @param {Array|String} types An array of the fixture names or the singular fixture name. * If an array, the first item is the plural fixture name (prefixed with -) and the second * item is the singular name. If a string, it's assumed to be the singular fixture name. Make @@ -512,18 +531,18 @@ steal('jquery/dom', * @param {Number} count the number of items to create * @param {Function} make a function that will return json data representing the object. The * make function is called back with the id and the current array of items. - * @param {Function} filter (optional) a function used to further filter results. Used for to simulate - * server params like searchText or startDate. The function should return true if the item passes the filter, + * @param {Function} filter (optional) a function used to further filter results. Used for to simulate + * server params like searchText or startDate. The function should return true if the item passes the filter, * false otherwise. For example: - * - * + * + * * function(item, settings){ * if(settings.data.searchText){ * var regex = new RegExp("^"+settings.data.searchText) * return regex.test(item.name); * } * } - * + * */ make: function( types, count, make, filter ) { if(typeof types === "string"){ @@ -538,7 +557,7 @@ steal('jquery/dom', } } }; - + for ( var i = 0; i < (count); i++ ) { //call back provided make var item = make(i, items); @@ -606,8 +625,8 @@ steal('jquery/dom', } } } - - + + if( filter ) { i = 0; while (i < retArr.length) { @@ -648,26 +667,26 @@ steal('jquery/dom', break; } } - + // TODO: make it work with non-linear ids .. $.extend(findOne(id), settings.data); return $.fixture["-restDestroy"](settings, cbType) }; $.fixture["-" + types[1]+"Create"] = function( settings, cbType ) { var item = make(items.length, items); - + $.extend(item, settings.data); - + if(!item.id){ item.id = items.length; } - + items.push(item); - + return $.fixture["-restCreate"](settings, cbType, undefined, item.id ); }; - - + + return { getId: getId, findOne : findOne, @@ -679,38 +698,38 @@ steal('jquery/dom', /** * @function jQuery.fixture.rand * @parent jQuery.fixture - * - * Creates random integers or random arrays of - * other arrays. - * + * + * Creates random integers or random arrays of + * other arrays. + * * ## Examples - * + * * var rand = $.fixture.rand; - * + * * // get a random integer between 0 and 10 (inclusive) * rand(11); - * + * * // get a random number between -5 and 5 (inclusive) * rand(-5, 6); - * + * * // pick a random item from an array * rand(["j","m","v","c"],1)[0] - * + * * // pick a random number of items from an array * rand(["j","m","v","c"]) - * + * * // pick 2 items from an array * rand(["j","m","v","c"],2) - * + * * // pick between 2 and 3 items at random * rand(["j","m","v","c"],2,3) - * - * + * + * * @param {Array|Number} arr An array of items to select from. * If a number is provided, a random number is returned. * If min and max are not provided, a random number of items are selected - * from this array. - * @param {Number} [min] If only min is provided, min items + * from this array. + * @param {Number} [min] If only min is provided, min items * are selected. * @param {Number} [max] If min and max are provided, a random number of * items between min and max (inclusive) is selected. @@ -722,7 +741,7 @@ steal('jquery/dom', } else { return Math.floor(Math.random() * arr); } - + } var rand = arguments.callee; // get a random set @@ -745,24 +764,24 @@ steal('jquery/dom', }, /** * @hide - * Use $.fixture.xhr to create an object that looks like an xhr object. - * + * Use $.fixture.xhr to create an object that looks like an xhr object. + * * ## Example - * - * The following example shows how the -restCreate fixture uses xhr to return + * + * The following example shows how the -restCreate fixture uses xhr to return * a simulated xhr object: * @codestart * "-restCreate" : function( settings, cbType ) { * switch(cbType){ - * case "success": + * case "success": * return [ - * {id: parseInt(Math.random()*1000)}, - * "success", + * {id: parseInt(Math.random()*1000)}, + * "success", * $.fixture.xhr()]; * case "complete": - * return [ + * return [ * $.fixture.xhr({ - * getResponseHeader: function() { + * getResponseHeader: function() { * return settings.url+"/"+parseInt(Math.random()*1000); * } * }), @@ -796,7 +815,7 @@ steal('jquery/dom', /** * @attribute on * On lets you programatically turn off fixtures. This is mostly used for testing. - * + * * $.fixture.on = false * Task.findAll({}, function(){ * $.fixture.on = true; @@ -836,59 +855,59 @@ steal('jquery/dom', return false; }; - - + + /** * @page jquery.fixture.0organizing Organizing Fixtures * @parent jQuery.fixture - * + * * The __best__ way of organizing fixtures is to have a 'fixtures.js' file that steals * jquery/dom/fixture and defines all your fixtures. For example, - * if you have a 'todo' application, you might + * if you have a 'todo' application, you might * have todo/fixtures/fixtures.js look like: - * + * * steal({ * path: '//jquery/dom/fixture.js', * ignore: true * }) * .then(function(){ - * + * * $.fixture({ - * type: 'get', + * type: 'get', * url: '/services/todos.json' * }, * '//todo/fixtures/todos.json'); - * + * * $.fixture({ - * type: 'post', + * type: 'post', * url: '/services/todos.json' * }, * function(settings){ - * return {id: Math.random(), + * return {id: Math.random(), * name: settings.data.name} * }); - * + * * }) - * - * __Notice__: We used steal's ignore option to prevent + * + * __Notice__: We used steal's ignore option to prevent * loading the fixture plugin in production. - * - * Finally, we steal todo/fixtures/fixtures.js in the + * + * Finally, we steal todo/fixtures/fixtures.js in the * app file (todo/todo.js) like: - * - * + * + * * steal({path: '//todo/fixtures/fixtures.js',ignore: true}); - * + * * //start of your app's steals * steal( ... ) - * + * * We typically keep it a one liner so it's easy to comment out. - * + * * ## Switching Between Sets of Fixtures - * + * * If you are using fixtures for testing, you often want to use different * sets of fixtures. You can add something like the following to your fixtures.js file: - * + * * if( /fixtureSet1/.test( window.location.search) ){ * $.fixture("/foo","//foo/fixtures/foo1.json'); * } else if(/fixtureSet2/.test( window.location.search)){ @@ -896,7 +915,7 @@ steal('jquery/dom', * } else { * // default fixtures (maybe no fixtures) * } - * + * */ //Expose this for fixture debugging $.fixture.overwrites = overwrites; diff --git a/dom/fixture/fixture_test.js b/dom/fixture/fixture_test.js index 7559d1ba..f5276989 100644 --- a/dom/fixture/fixture_test.js +++ b/dom/fixture/fixture_test.js @@ -6,75 +6,104 @@ module("jquery/dom/fixture"); test("static fixtures", function(){ stop(); - + $.fixture("GET something", "//jquery/dom/fixture/fixtures/test.json"); $.fixture("POST something", "//jquery/dom/fixture/fixtures/test.json"); - - + + $.get("something",function(data){ equals(data.sweet,"ness","$.get works"); - + $.post("something",function(data){ equals(data.sweet,"ness","$.post works"); - - + + start(); },'json'); - + },'json'); }) +test("templated static fixtures", function(){ + stop(); + + $.fixture({ + type : "GET", + url : "/templated-url/{id}", + }, "//jquery/dom/fixture/fixtures/test.{id}.json"); + + $.get("/templated-url/1", function(data) { + equals(data.name, "Arjun", "templated static fixtures works"); + start(); + }, "json"); +}); + +test("id is set to original data object", function(){ + stop(); + + $.fixture({ + type : "GET", + url : "/templated-url/{id}", + }, function(original){ + equal(original.data.id, "1"); + }); + + $.get("/templated-url/1", function(data) { + start(); + }, "json"); +}); + test("dynamic fixtures",function(){ stop(); $.fixture.delay = 10; $.fixture("something", function(){ return [{sweet: "ness"}] }) - + $.get("something",function(data){ equals(data.sweet,"ness","$.get works"); start(); - + },'json'); }); test("fixture function", 3, function(){ - + stop(); var url = steal.root.join("jquery/dom/fixture/fixtures/foo.json")+''; $.fixture(url,"//jquery/dom/fixture/fixtures/foobar.json" ); - + $.get(url,function(data){ equals(data.sweet,"ner","url passed works"); - + $.fixture(url,"//jquery/dom/fixture/fixtures/test.json" ); - - $.get(url,function(data){ - + + $.get(url,function(data){ + equals(data.sweet,"ness","replaced"); - + $.fixture(url, null ); - - $.get(url,function(data){ - + + $.get(url,function(data){ + equals(data.a,"b","removed"); - + start(); - + },'json') - - + + },'json') - - - + + + },"json"); }); test("fixtures with converters", function(){ - + stop(); $.ajax( { url : steal.root.join("jquery/dom/fixture/fixtures/foobar.json")+'', @@ -104,7 +133,7 @@ test("$.fixture.make fixtures",function(){ id: i, name: "thing "+i } - }, + }, function(item, settings){ if(settings.data.searchText){ var regex = new RegExp("^"+settings.data.searchText) @@ -131,12 +160,12 @@ test("$.fixture.make fixtures",function(){ test("simulating an error", function(){ var st = '{type: "unauthorized"}'; - + $.fixture("/foo", function(){ return [401,st] }); stop(); - + $.ajax({ url : "/foo", success : function(){ @@ -156,7 +185,7 @@ test("rand", function(){ var num = rand(5); equals(typeof num, "number"); ok(num >= 0 && num < 5, "gets a number" ); - + stop(); var zero, three, between, next = function(){ start() @@ -178,7 +207,7 @@ test("rand", function(){ setTimeout(arguments.callee, 10) } }, 10) - + }); @@ -198,40 +227,40 @@ test("_compare", function(){ var same = $.Object.same( {url : "/thingers/5"}, {url : "/thingers/{id}"}, $.fixture._compare) - + ok(same, "they are similar"); - + same = $.Object.same( {url : "/thingers/5"}, {url : "/thingers"}, $.fixture._compare); - + ok(!same, "they are not the same"); }) test("_similar", function(){ - + var same = $.fixture._similar( {url : "/thingers/5"}, {url : "/thingers/{id}"}); - + ok(same, "similar"); - + same = $.fixture._similar( {url : "/thingers/5", type: "get"}, {url : "/thingers/{id}"}); - + ok(same, "similar with extra pops on settings"); - + var exact = $.fixture._similar( {url : "/thingers/5", type: "get"}, {url : "/thingers/{id}"}, true); - + ok(!exact, "not exact" ) - + var exact = $.fixture._similar( {url : "/thingers/5"}, {url : "/thingers/5"}, true); - + ok(exact, "exact" ) }) @@ -257,27 +286,27 @@ test("replacing and removing a fixture", function(){ stop(); $.get(url,{}, function(json){ equals(json.weird,"ness!","fixture set right") - + $.fixture("GET "+url, function(){ return {weird: "ness?"} }) - + $.get(url,{}, function(json){ equals(json.weird,"ness?","fixture set right"); - + $.fixture("GET "+url, null ) - + $.get(url,{}, function(json){ equals(json.weird,"ness","fixture set right"); start(); },'json'); - - + + },'json') - - - + + + },'json') }); @@ -295,7 +324,7 @@ $.fixture('GET /foo', function(orig, settings, headers, cb){ // fixture that creates a nice store var store = $.fixture.store(1000, function(){ - + }) store.find() diff --git a/dom/fixture/fixtures/test.1.json b/dom/fixture/fixtures/test.1.json new file mode 100644 index 00000000..4be4b493 --- /dev/null +++ b/dom/fixture/fixtures/test.1.json @@ -0,0 +1,3 @@ +{ + "name" :"Arjun" +}