From 20e8d51653723aa7ed9dfc97076b21ad60fda12b Mon Sep 17 00:00:00 2001 From: Marthyn Olthof Date: Wed, 14 Jan 2015 12:15:39 +0100 Subject: [PATCH] Add handleFindOne method --- README.md | 215 ++++++++++-------- dist/amd/factory-guy.js | 32 +++ dist/ember-data-factory-guy.js | 32 +++ dist/ember-data-factory-guy.min.js | 2 +- src/factory_guy_test_mixin.js | 32 +++ tests/factory_guy_test_mixin_test.js | 44 ++++ .../javascripts/ember_data_factory_guy.js | 39 +++- 7 files changed, 290 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index 222530e6..db0f8537 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Ember Data Factory Guy [![Build Status](https://secure.travis-ci.org/danielspaniel/ember-data-factory-guy.png?branch=master)](http://travis-ci.org/danielspaniel/ember-data-factory-guy) *NOTE* - + ember-data is changing the way they are doing relationships in 1.0.0-beta.10 and above so, if you are using ember-data-1.0.0-beta.8 and earlier, then be sure to use version 0.6.4 of ember-data-factory-guy. @@ -21,8 +21,8 @@ FactoryGuy.setStore(store) somewhere in your code before you start making fixtur ## Using with Ember Cli - https://github.com/igorrKurr/ember-cli-factory-guy-example - An example of how to manually set up ember-data-factory-guy with ember cli - + An example of how to manually set up ember-data-factory-guy with ember cli + - https://github.com/cristinawithout/ember-cli-data-factory-guy A wrapper around ember-data-factory-guy for ember-cli for even easier setup @@ -60,7 +60,7 @@ require the 'ember_data_factory_guy' javascript file in your test helper Add as one of your dependencies in bower.json file: -```json +```json "dependencies": { "foo-dependency": "latest", "other-foo-dependency": "latest", @@ -85,29 +85,29 @@ then: ### How this works -- Using DS.RestAdapter / DS.ActiveModelAdapter +- Using DS.RestAdapter / DS.ActiveModelAdapter - Add record instances to the store - - Faster, since models can be accessed synchronously -- Using DS.FixtureAdapter + - Faster, since models can be accessed synchronously +- Using DS.FixtureAdapter - Add fixtures to the store - Slower, since models are accessed asynchronously - + ##### DS.RestAdapter / DS.ActiveModelAdapter -The preferred way to use this project is to use the default adapter for your project, +The preferred way to use this project is to use the default adapter for your project, which is usually going to be the RESTAdapter/ActiveModelAdapter. -*In other words, it is NOT recommended to use the DS.FixtureAdapter.* +*In other words, it is NOT recommended to use the DS.FixtureAdapter.* -When you call: store.makeFixture('user'), you create model in the store and this method -returns this model instance +When you call: store.makeFixture('user'), you create model in the store and this method +returns this model instance *Since you are synchronously getting model instances, you can immediately start asking for data from the model, and its associations, which is why it is faster to use the REST/ActiveModel adapter than the FixtureAdapter* ##### Using DS.FixtureAdapter - + The benefit of using FactoryGuy is that you can run your tests with the default adapter that your application's store normally uses. In other words: You do not have to use the DS.FixtureAdapter. But if you do choose to use the Fixture adapter, @@ -119,10 +119,10 @@ but some of the associated records were not loaded. Either make sure they are al If you do get these types of errors try requiring the factory_guy_has_many.js file ( located in dist dir and vendor dir ) AFTER you require ember-data, -but BEFORE you require your models. +but BEFORE you require your models. -### Setup +### Setup In the following examples, assume the models look like this: @@ -139,22 +139,22 @@ In the following examples, assume the models look like this: title: DS.attr('string'), user: DS.belongsTo('user') }); - - // polymorphic models + + // polymorphic models Hat = DS.Model.extend({ type: DS.attr('string'), user: DS.belongsTo('user') }); - + BigHat = Hat.extend(); SmallHat = Hat.extend(); ``` ### Defining Factories - - A factory has a name and a set of attributes. - - The name should match the model type name. So, for 'User' model, the name would be 'user' - + - A factory has a name and a set of attributes. + - The name should match the model type name. So, for 'User' model, the name would be 'user' + ##### Standard models @@ -187,7 +187,7 @@ It is better to define each polymorphic model in it's own typed definition: type: 'SmallHat' } }) - + FactoryGuy.define('big_hat', { default: { type: 'BigHat' @@ -199,7 +199,7 @@ It is better to define each polymorphic model in it's own typed definition: rather than doing this: ```javascript - + FactoryGuy.define('hat', { default: {}, small_hat: { @@ -209,10 +209,10 @@ rather than doing this: type: 'BigHat' } }) - + ``` -Since there are times that the latter can cause problems when +Since there are times that the latter can cause problems when the store is looking up the correct model type name @@ -220,12 +220,12 @@ the store is looking up the correct model type name - FactorGuy.setStore - pass in the store instance to FactoryGuy before making fixtures. - FactoryGuy.build - - Builds json + - Builds json - FactoryGuy.make - - Loads model instance into the store + - Loads model instance into the store - Can override default attributes by passing in a hash - - Can add attributes with traits ( see traits section ) - + - Can add attributes with traits ( see traits section ) + ```javascript // First set the store on FactoryGuy. You don't have to do this step manually // if you use FactoryGuyTestHelperMixin since this is done for you in the setup @@ -234,41 +234,41 @@ the store is looking up the correct model type name var store = App.__container__.lookup('store:main'); FactoryGuy.setStore(store); - // returns json - var json = FactoryGuy.build('user'); + // returns json + var json = FactoryGuy.build('user'); json // => {id: 1, name: 'Dude', style: 'normal'} - // returns a User instance that is loaded into your application's store + // returns a User instance that is loaded into your application's store var user = FactoryGuy.make('user'); user.toJSON({includeId: true}) // => {id: 2, name: 'Dude', style: 'normal'} - var json = FactoryGuy.build('admin'); + var json = FactoryGuy.build('admin'); json // => {id: 3, name: 'Admin', style: 'super'} var user = FactoryGuy.make('admin'); user.toJSON({includeId: true}) // => {id: 4, name: 'Admin', style: 'super'} - + ``` -You can override the default attributes by passing in a hash +You can override the default attributes by passing in a hash ```javascript - - var json = FactoryGuy.build('user', {name: 'Fred'}); + + var json = FactoryGuy.build('user', {name: 'Fred'}); // json.name => 'Fred' - + ``` ### Sequences -- For generating unique attribute values. -- Can be defined: - - In the model definition's sequences hash - - Inline on the attribute +- For generating unique attribute values. +- Can be defined: + - In the model definition's sequences hash + - Inline on the attribute - Values are generated by calling FactoryGuy.generate -##### Declaring sequences in sequences hash +##### Declaring sequences in sequences hash ```javascript @@ -285,7 +285,7 @@ You can override the default attributes by passing in a hash } }); - var json = FactoryGuy.build('user'); + var json = FactoryGuy.build('user'); json.name // => 'User1' var user = FactoryGuy.make('user'); @@ -293,7 +293,7 @@ You can override the default attributes by passing in a hash ``` -##### Declaring an inline sequence on attribute +##### Declaring an inline sequence on attribute ```javascript @@ -303,7 +303,7 @@ You can override the default attributes by passing in a hash }, }); - var json = FactoryGuy.build('special_project'); + var json = FactoryGuy.build('special_project'); json.title // => 'Project #1' var project = FactoryGuy.make('special_project'); @@ -313,15 +313,15 @@ You can override the default attributes by passing in a hash ### Inline Functions -- Declare a function for an attribute +- Declare a function for an attribute - Can reference other attributes -```javascript - +```javascript + FactoryGuy.define('user', { // Assume that this definition includes the same sequences and default section - // from the user definition in: "Declaring sequences in sequences hash" section. - + // from the user definition in: "Declaring sequences in sequences hash" section. + funny_user: { style: function(f) { return 'funny ' + f.name } } @@ -330,7 +330,7 @@ You can override the default attributes by passing in a hash var json = FactoryGuy.build('funny_user'); json.name // => 'User1' json.style // => 'funny User1' - + var user = FactoryGuy.make('funny_user'); user.get('name') // => 'User2' user.get('style') // => 'funny User2' @@ -343,10 +343,10 @@ You can override the default attributes by passing in a hash ### Traits -- For grouping attributes together +- For grouping attributes together - Can use one or more traits in a row - The last trait included overrides any values in traits before it - + ```javascript FactoryGuy.define('user', { @@ -355,8 +355,8 @@ You can override the default attributes by passing in a hash friendly: { style: 'Friendly' } } }); - - var json = FactoryGuy.build('user', 'big', 'friendly'); + + var json = FactoryGuy.build('user', 'big', 'friendly'); json.name // => 'Big Guy' json.style // => 'Friendly' @@ -366,9 +366,9 @@ You can override the default attributes by passing in a hash ``` -You can still pass in a hash of options when using traits. This hash of -attributes will override any trait attributes or default attributes - +You can still pass in a hash of options when using traits. This hash of +attributes will override any trait attributes or default attributes + ```javascript var user = FactoryGuy.make('user', 'big', 'friendly', {name: 'Dave'}); @@ -387,7 +387,7 @@ attributes will override any trait attributes or default attributes - The inverse association is being set up for you ##### Setup belongsTo associations in Factory Definition - + ```javascript // Recall ( from above setup ) that there is a user belongsTo on the Project model // Also, assume 'user' factory is same as from 'user' factory definition above in @@ -407,8 +407,8 @@ attributes will override any trait attributes or default attributes user: FactoryGuy.belongsTo('admin') } }); - - var json = FactoryGuy.build('project_with_user'); + + var json = FactoryGuy.build('project_with_user'); json.user // => {id:1, name: 'Dude', style: 'normal'} var json = FactoryGuy.build('project_with_bob'); @@ -423,17 +423,17 @@ attributes will override any trait attributes or default attributes *You could also accomplish the above with traits:* ```javascript - + FactoryGuy.define('project', { traits: { with_user: { user: {} }, with_admin: { user: FactoryGuy.belongsTo('admin') } } }); - + var user = FactoryGuy.make('project', 'with_user'); project.get('user').toJSON({includeId: true}) // => {id:1, name: 'Dude', style: 'normal'} - + ``` @@ -442,12 +442,12 @@ attributes will override any trait attributes or default attributes ```javascript var user = FactoryGuy.make('user'); var project = FactoryGuy.make('project', {user: user}); - + project.get('user').toJSON({includeId: true}) // => {id:1, name: 'Dude', style: 'normal'} ``` *Note that though you are setting the 'user' belongsTo association on a project, -the reverse user hasMany 'projects' association is being setup for you on the user +the reverse user hasMany 'projects' association is being setup for you on the user ( for both manual and factory defined belongsTo associations ) as well* ```javascript @@ -462,16 +462,16 @@ the reverse user hasMany 'projects' association is being setup for you on the us FactoryGuy.define('user', { user_with_projects: { FactoryGuy.hasMany('project', 2) } }); - + var user = FactoryGuy.make('user_with_projects'); user.get('projects.length') // => 2 - + ``` *You could also accomplish the above with traits:* ```javascript - + FactoryGuy.define('project', { traits: { with_projects: { @@ -479,10 +479,10 @@ the reverse user hasMany 'projects' association is being setup for you on the us } } }); - + var user = FactoryGuy.make('user', 'with_projects'); user.get('projects.length') // => 2 - + ``` ##### Setup hasMany associations manually @@ -492,18 +492,18 @@ the reverse user hasMany 'projects' association is being setup for you on the us var project2 = FactoryGuy.make('project'); var user = FactoryGuy.make('user', {projects: [project1,project2]}); user.get('projects.length') // => 2 - - // or + + // or var projects = FactoryGuy.makeList('project', 2); var user = FactoryGuy.make('user', {projects: projects}); user.get('projects.length') // => 2 - + ``` *Note that though you are setting the 'projects' hasMany association on a user, -the reverse 'user' belongsTo association is being setup for you on the project +the reverse 'user' belongsTo association is being setup for you on the project ( for both manual and factory defined hasMany associations ) as well* - + ```javascript projects.get('firstObject.user') // => user ``` @@ -517,11 +517,11 @@ the reverse 'user' belongsTo association is being setup for you on the project - Loads one or more instances into store -##### Building json array +##### Building json array ```javascript - var json = FactoryGuy.buildList('user', 2) - json.length // => 2 + var json = FactoryGuy.buildList('user', 2) + json.length // => 2 json[0] // => {id: 1, name: 'User1', style: 'normal'} json[1] // => {id: 2, name: 'User2', style: 'normal'} @@ -531,14 +531,14 @@ the reverse 'user' belongsTo association is being setup for you on the project ```javascript var users = FactoryGuy.makeList('user', 2) - users.get('length') // => 2 + users.get('length') // => 2 users[0].toJSON({includeId: true}) // => {id: 3, name: 'User3', style: 'normal'} users[1].toJSON({includeId: true}) // => {id: 4, name: 'User4', style: 'normal'} ``` -### Testing models, controllers, views +### Testing models, controllers, views - Testing the models, controllers and views in isolation - Use FactoryGuyTestMixin to help with testing @@ -549,7 +549,7 @@ the reverse 'user' belongsTo association is being setup for you on the project - Using FactoryGuyTestMixin helper methods: - make - teardown - + ```javascript // Create a helper class using FactoryGuyTestMixin. @@ -594,11 +594,12 @@ test("make a user using your applications default adapter", function() { - Uses mockjax - Has helper methods - - handleFindMany + - handleFindMany + - handleFindOne - handleFindQuery - handleCreate - - handleUpdate - - handleDelete + - handleUpdate + - handleDelete Since it is recommended to use your normal adapter ( which is usually a subclass of RESTAdapter, ) FactoryGuyTestMixin assumes you will want to use that adapter to do your integration tests. @@ -624,12 +625,24 @@ tests run as shown in the previous section (Using FactoryGuyTestMixin)** ```javascript // can use traits and extra fixture options here as you would with FactoryGuy#makeList testHelper.handleFindMany('profile', 2); - + store.find('profile').then(function (profiles) { profiles.get('length') //=> 2 }); ``` +##### handleFindOne + - for dealing with finding one record with an id + +```javascript + // can use traits and extra fixture options here as you would with FactoryGuy#make + testHelper.handleFindOne('profile', {id: 1}); + + store.find('profile', 1).then(function (profile) { + profile.get('id') //=> 1 + }); +``` + ##### handleFindQuery - for dealing with finding all records for a type of model with query parameters. @@ -669,19 +682,19 @@ tests run as shown in the previous section (Using FactoryGuyTestMixin)** - succeed - flag to indicate if the request should succeed ( default is true ) **Note** - + *Any attributes in match will be added to the response json automatically, so you don't need to include them in the returns hash as well.* - + *If you match on a belongsTo association, you don't have to include that in the returns hash.* - - + + Realistically, you will have code in a view action or controller action that will - create the record, and setup any associations. - + create the record, and setup any associations. + ```javascript - + // most actions that create a record look something like this: action: { addProject: function (user) { @@ -696,10 +709,10 @@ Realistically, you will have code in a view action or controller action that wil In this case, you are are creating a 'project' record with a specific name, and belonging to a particular user. To mock this createRecord call here are a few ways to do this using match and or returns options. - + ```javascript - // Simplest case - // Don't care about a match just handle createRecord for any project + // Simplest case + // Don't care about a match just handle createRecord for any project testHelper.handleCreate('project') // Exactly matching attributes testHelper.handleCreate('project', {match: {name: "Moo", user: user}}) @@ -713,17 +726,17 @@ match and or returns options. *mocking a failed create* ```javascript - // set the succeed flag to 'false' + // set the succeed flag to 'false' testHelper.handleCreate('project', {succeed: false}); - + // when the createRecord on the 'project' is called, it will fail store.createRecord('project').save() //=> fails - // or fail only if the attribues match exactly + // or fail only if the attribues match exactly testHelper.handleCreate('project', { match: {name: "Moo", user: user}, {succeed: false} }) - + store.createRecord('project', {name: "Moo", user: user}).save() //=> fails ``` diff --git a/dist/amd/factory-guy.js b/dist/amd/factory-guy.js index fb84f3e5..2ca255ca 100644 --- a/dist/amd/factory-guy.js +++ b/dist/amd/factory-guy.js @@ -957,6 +957,38 @@ var FactoryGuyTestMixin = Em.Mixin.create({ var url = this.buildURL(modelName); this.stubEndpointForHttpRequest(url, responseJson); }, + + /** + Handling ajax GET for finding one record for a type of model and an id. + You can mock failed find by passing in success argument as false. + + ```js + // Pass in the parameters you would normally pass into FactoryGuy.make, + // like fixture name, number of fixtures to make, and optional traits, + // or fixture options + testHelper.handleFindOne('user', 'with_hats', {id: 1}); + + store.find('user', 1).then(function(user){ + + }); + ``` + + @param {String} name name of the fixture ( or model ) to find + @param {String} trait optional traits (one or more) + @param {Object} opts optional fixture options (including id) + */ + handleFindOne: function () { + // make the records and load them in the store + var record = FactoryGuy.make.apply(FactoryGuy, arguments); + var name = arguments[0]; + var modelName = FactoryGuy.lookupModelForFixtureName(name); + var responseJson = {}; + var json = record.toJSON({includeId: true}); + responseJson[modelName.pluralize()] = json; + var url = this.buildURL(modelName, record.id); + this.stubEndpointForHttpRequest(url, responseJson); + }, + /** Handling ajax GET for finding all records for a type of model with query parameters. diff --git a/dist/ember-data-factory-guy.js b/dist/ember-data-factory-guy.js index e645db1f..323ef285 100644 --- a/dist/ember-data-factory-guy.js +++ b/dist/ember-data-factory-guy.js @@ -952,6 +952,38 @@ var FactoryGuyTestMixin = Em.Mixin.create({ var url = this.buildURL(modelName); this.stubEndpointForHttpRequest(url, responseJson); }, + + /** + Handling ajax GET for finding one record for a type of model and an id. + You can mock failed find by passing in success argument as false. + + ```js + // Pass in the parameters you would normally pass into FactoryGuy.make, + // like fixture name, number of fixtures to make, and optional traits, + // or fixture options + testHelper.handleFindOne('user', 'with_hats', {id: 1}); + + store.find('user', 1).then(function(user){ + + }); + ``` + + @param {String} name name of the fixture ( or model ) to find + @param {String} trait optional traits (one or more) + @param {Object} opts optional fixture options (including id) + */ + handleFindOne: function () { + // make the records and load them in the store + var record = FactoryGuy.make.apply(FactoryGuy, arguments); + var name = arguments[0]; + var modelName = FactoryGuy.lookupModelForFixtureName(name); + var responseJson = {}; + var json = record.toJSON({includeId: true}); + responseJson[modelName.pluralize()] = json; + var url = this.buildURL(modelName, record.id); + this.stubEndpointForHttpRequest(url, responseJson); + }, + /** Handling ajax GET for finding all records for a type of model with query parameters. diff --git a/dist/ember-data-factory-guy.min.js b/dist/ember-data-factory-guy.min.js index 4db6a620..ee198772 100644 --- a/dist/ember-data-factory-guy.min.js +++ b/dist/ember-data-factory-guy.min.js @@ -1 +1 @@ -var Sequence=function(fn){var index=1;this.next=function(){return fn.call(this,index++)};this.reset=function(){index=1}};var MissingSequenceError=function(message){this.toString=function(){return message}};if(FactoryGuy!==undefined){FactoryGuy.sequence=Sequence;FactoryGuy.missingSequenceError=MissingSequenceError}var ModelDefinition=function(model,config){var sequences={};var traits={};var defaultAttributes={};var namedModels={};var modelId=1;var sequenceName=null;this.model=model;this.matchesName=function(name){return model==name||namedModels[name]};this.nextId=function(){return modelId++};this.generate=function(name,sequenceFn){if(sequenceFn){if(!sequences[name]){sequences[name]=new Sequence(sequenceFn)}}var sequence=sequences[name];if(!sequence){throw new MissingSequenceError("Can not find that sequence named ["+sequenceName+"] in '"+model+"' definition")}return sequence.next()};this.build=function(name,opts,traitArgs){var traitsObj={};traitArgs.forEach(function(trait){$.extend(traitsObj,traits[trait])});var modelAttributes=namedModels[name]||{};var fixture=$.extend({},defaultAttributes,modelAttributes,traitsObj,opts);for(var attribute in fixture){if(Ember.typeOf(fixture[attribute])=="function"){fixture[attribute]=fixture[attribute].call(this,fixture)}else if(Ember.typeOf(fixture[attribute])=="object"){if(FactoryGuy.getStore()){if(FactoryGuy.isAttributeRelationship(this.model,attribute)){fixture[attribute]=FactoryGuy.build(attribute,fixture[attribute])}}else{fixture[attribute]=FactoryGuy.build(attribute,fixture[attribute])}}}if(!fixture.id){fixture.id=this.nextId()}return fixture};this.buildList=function(name,number,traits,opts){var arr=[];for(var i=0;i=2);var number=args.splice(1,1)[0];Ember.assert("Second argument to makeList should be a number (of fixtures to make.)",typeof number=="number");for(var i=0;i-1){modelClass.FIXTURES.splice(index,1)}modelClass.FIXTURES.push(fixture);return fixture},indexOfFixture:function(fixtures,fixture){var index=-1,id=fixture.id+"";Ember.A(fixtures).find(function(r,i){if(""+Ember.get(r,"id")===id){index=i;return true}else{return false}});return index},clear:function(opts){if(!opts){this.modelDefinitions={}}}};(function(){DS.Store.reopen({usingFixtureAdapter:function(){var adapter=this.adapterFor("application");return adapter instanceof DS.FixtureAdapter},makeFixture:function(){Ember.deprecate("DEPRECATION Warning: use FactoryGuy.make instead");FactoryGuy.make.call(FactoryGuy,arguments)},makeList:function(){Ember.deprecate("DEPRECATION Warning: use FactoryGuy.makeList instead");FactoryGuy.makeList.call(FactoryGuy,arguments)},makeModel:function(modelType,fixture){var store=this,modelName=store.modelFor(modelType).typeKey,model;Em.run(function(){store.findEmbeddedAssociationsForRESTAdapter(modelType,fixture);if(fixture.type){modelName=fixture.type.underscore();modelType=store.modelFor(modelName)}model=store.push(modelName,fixture);store.setAssociationsForRESTAdapter(modelType,modelName,model)});return model},setAssociationsForFixtureAdapter:function(modelType,modelName,fixture){var self=this;var adapter=this.adapterFor("application");Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){var hasManyRelation,belongsToRecord;if(relationship.kind=="hasMany"){hasManyRelation=fixture[relationship.key];if(hasManyRelation){$.each(fixture[relationship.key],function(index,object){var id=object;if(Ember.typeOf(object)=="object"){FactoryGuy.pushFixture(relationship.type,object);id=object.id;hasManyRelation[index]=id}var hasManyfixtures=adapter.fixturesForType(relationship.type);var hasManyFixture=adapter.findFixtureById(hasManyfixtures,id);hasManyFixture[modelName]=fixture.id;self.loadModelForFixtureAdapter(relationship.type,hasManyFixture)})}}if(relationship.kind=="belongsTo"){belongsToRecord=fixture[relationship.key];if(belongsToRecord){if(typeof belongsToRecord=="object"){FactoryGuy.pushFixture(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord.id}var hasManyName=self.findHasManyRelationshipNameForFixtureAdapter(relationship.type,relationship.parentType);var belongsToFixtures=adapter.fixturesForType(relationship.type);var belongsTofixture=adapter.findFixtureById(belongsToFixtures,fixture[relationship.key]);if(!belongsTofixture[hasManyName]){belongsTofixture[hasManyName]=[]}belongsTofixture[hasManyName].push(fixture.id);self.loadModelForFixtureAdapter(relationship.type,belongsTofixture)}}})},loadModelForFixtureAdapter:function(modelType,fixture){var storeModel=this.getById(modelType,fixture.id),that=this;if(!Ember.isPresent(storeModel)||storeModel.get("isEmpty")){Ember.run(function(){var dup=Ember.copy(fixture,true);that.push(modelType,fixture);Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){if(fixture[relationship.key]){fixture[relationship.key]=dup[relationship.key]}})})}},findEmbeddedAssociationsForRESTAdapter:function(modelType,fixture){var store=this;Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind=="belongsTo"){var belongsToRecord=fixture[relationship.key];if(Ember.typeOf(belongsToRecord)=="object"){store.findEmbeddedAssociationsForRESTAdapter(relationship.type,belongsToRecord);belongsToRecord=store.push(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord}}if(relationship.kind=="hasMany"){var hasManyRecords=fixture[relationship.key];if(Ember.typeOf(hasManyRecords)=="array"){if(Ember.typeOf(hasManyRecords[0])=="object"){var records=Em.A();hasManyRecords.map(function(object){store.findEmbeddedAssociationsForRESTAdapter(relationship.type,object);var record=store.push(relationship.type,object);records.push(record);return record});fixture[relationship.key]=records}}}})},setAssociationsForRESTAdapter:function(modelType,modelName,model){var self=this;Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind=="hasMany"){var children=model.get(name)||[];children.forEach(function(child){var belongsToName=self.findRelationshipName("belongsTo",child.constructor,model);if(belongsToName){child.set(belongsToName,model)}})}})},findRelationshipName:function(kind,belongToModelType,childModel){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind==kind&&childModel instanceof relationship.type){relationshipName=relationship.key}});return relationshipName},findHasManyRelationshipNameForFixtureAdapter:function(belongToModelType,childModelType){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind=="hasMany"&&childModelType==relationship.type){relationshipName=relationship.key}});return relationshipName},pushPayload:function(type,payload){var typeName,model;if(this.usingFixtureAdapter()){if(Ember.typeOf(type)==="string"&&Ember.isPresent(payload)&&Ember.isPresent(payload.id)){model=this.modelFor(type);FactoryGuy.pushFixture(model,payload);this.push(model,Ember.copy(payload,true))}else if(Ember.typeOf(type)==="object"||Ember.typeOf(payload)==="object"){if(Ember.isBlank(payload)){payload=type}for(var prop in payload){typeName=Ember.String.camelize(Ember.String.singularize(prop));model=this.modelFor(typeName);this.pushMany(model,Ember.makeArray(Ember.copy(payload[prop],true)));Ember.ArrayPolyfills.forEach.call(Ember.makeArray(payload[prop]),function(hash){FactoryGuy.pushFixture(model,hash)},this)}}else{throw new Ember.Error("Assertion Failed: You cannot use `store#pushPayload` with this method signature pushPayload("+type+","+payload+")")}}else{this._super(type,payload)}}})})();var FactoryGuyTestMixin=Em.Mixin.create({setup:function(app){this.set("container",app.__container__);FactoryGuy.setStore(this.getStore());return this},useFixtureAdapter:function(app){app.ApplicationAdapter=DS.FixtureAdapter;this.getStore().adapterFor("application").simulateRemoteResponse=false},usingActiveModelSerializer:function(type){var store=this.getStore();var modelType=store.modelFor(type);var serializer=store.serializerFor(modelType.typeKey);return serializer instanceof DS.ActiveModelSerializer},find:function(type,id){return this.getStore().find(type,id)},make:function(){return FactoryGuy.make.apply(FactoryGuy,arguments)},getStore:function(){return this.get("container").lookup("store:main")},stubEndpointForHttpRequest:function(url,json,options){options=options||{};var request={url:url,dataType:"json",responseText:json,type:options.type||"GET",status:options.status||200};if(options.urlParams){request.urlParams=options.urlParams}if(options.data){request.data=options.data}$.mockjax(request)},buildURL:function(typeName,id){var type=this.getStore().modelFor(typeName);return this.getStore().adapterFor(type).buildURL(type.typeKey,id)},handleFindMany:function(){var records=FactoryGuy.makeList.apply(FactoryGuy,arguments);var name=arguments[0];var modelName=FactoryGuy.lookupModelForFixtureName(name);var responseJson={};var json=records.map(function(record){return record.toJSON({includeId:true})});responseJson[modelName.pluralize()]=json;var url=this.buildURL(modelName);this.stubEndpointForHttpRequest(url,responseJson)},handleFindQuery:function(modelName,searchParams,records){Ember.assert("The second argument of searchParams must be an array",Em.typeOf(searchParams)=="array");if(records){Ember.assert("The third argument ( records ) must be an array - found type:"+Em.typeOf(records),Em.typeOf(records)=="array")}else{records=[]}var json=records.map(function(record){return record.toJSON({includeId:true})});var responseJson={};responseJson[modelName.pluralize()]=json;var url=this.buildURL(modelName);this.stubEndpointForHttpRequest(url,responseJson,{urlParams:searchParams})},handleCreate:function(modelName,options){var opts=options===undefined?{}:options;var succeed=opts.succeed===undefined?true:opts.succeed;var match=opts.match||{};var returnArgs=opts.returns||{};var url=this.buildURL(modelName);var definition=FactoryGuy.modelDefinitions[modelName];var httpOptions={type:"POST"};if(opts.match){var expectedRequest={};var record=this.getStore().createRecord(modelName,match);expectedRequest[modelName]=record.serialize();httpOptions.data=JSON.stringify(expectedRequest)}var modelType=this.getStore().modelFor(modelName);var responseJson={};if(succeed){responseJson[modelName]=$.extend({id:definition.nextId()},match,returnArgs);Ember.get(modelType,"relationshipsByName").forEach(function(relationship){if(relationship.kind=="belongsTo"){delete responseJson[modelName][relationship.key]}})}else{httpOptions.status=500}this.stubEndpointForHttpRequest(url,responseJson,httpOptions)},handleUpdate:function(){var args=Array.prototype.slice.call(arguments);Ember.assert("To handleUpdate pass in a model instance or a type and an id",args.length>0);var succeed=true;if(typeof args[args.length-1]=="boolean"){args.pop();succeed=false}Ember.assert("To handleUpdate pass in a model instance or a type and an id",args.length>0);var type,id;if(args[0]instanceof DS.Model){var model=args[0];type=model.constructor.typeKey;id=model.id}else if(typeof args[0]=="string"&&typeof parseInt(args[1])=="number"){type=args[0];id=args[1]}Ember.assert("To handleUpdate pass in a model instance or a type and an id",type&&id);this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"PUT",status:succeed?200:500})},handleDelete:function(type,id,succeed){succeed=succeed===undefined?true:succeed;this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"DELETE",status:succeed?200:500})},teardown:function(){FactoryGuy.resetModels(this.getStore());$.mockjax.clear()}});if(FactoryGuy!==undefined){FactoryGuy.testMixin=FactoryGuyTestMixin} \ No newline at end of file +var Sequence=function(fn){var index=1;this.next=function(){return fn.call(this,index++)};this.reset=function(){index=1}};var MissingSequenceError=function(message){this.toString=function(){return message}};if(FactoryGuy!==undefined){FactoryGuy.sequence=Sequence;FactoryGuy.missingSequenceError=MissingSequenceError}var ModelDefinition=function(model,config){var sequences={};var traits={};var defaultAttributes={};var namedModels={};var modelId=1;var sequenceName=null;this.model=model;this.matchesName=function(name){return model==name||namedModels[name]};this.nextId=function(){return modelId++};this.generate=function(name,sequenceFn){if(sequenceFn){if(!sequences[name]){sequences[name]=new Sequence(sequenceFn)}}var sequence=sequences[name];if(!sequence){throw new MissingSequenceError("Can not find that sequence named ["+sequenceName+"] in '"+model+"' definition")}return sequence.next()};this.build=function(name,opts,traitArgs){var traitsObj={};traitArgs.forEach(function(trait){$.extend(traitsObj,traits[trait])});var modelAttributes=namedModels[name]||{};var fixture=$.extend({},defaultAttributes,modelAttributes,traitsObj,opts);for(var attribute in fixture){if(Ember.typeOf(fixture[attribute])=="function"){fixture[attribute]=fixture[attribute].call(this,fixture)}else if(Ember.typeOf(fixture[attribute])=="object"){if(FactoryGuy.getStore()){if(FactoryGuy.isAttributeRelationship(this.model,attribute)){fixture[attribute]=FactoryGuy.build(attribute,fixture[attribute])}}else{fixture[attribute]=FactoryGuy.build(attribute,fixture[attribute])}}}if(!fixture.id){fixture.id=this.nextId()}return fixture};this.buildList=function(name,number,traits,opts){var arr=[];for(var i=0;i=2);var number=args.splice(1,1)[0];Ember.assert("Second argument to makeList should be a number (of fixtures to make.)",typeof number=="number");for(var i=0;i-1){modelClass.FIXTURES.splice(index,1)}modelClass.FIXTURES.push(fixture);return fixture},indexOfFixture:function(fixtures,fixture){var index=-1,id=fixture.id+"";Ember.A(fixtures).find(function(r,i){if(""+Ember.get(r,"id")===id){index=i;return true}else{return false}});return index},clear:function(opts){if(!opts){this.modelDefinitions={}}}};(function(){DS.Store.reopen({usingFixtureAdapter:function(){var adapter=this.adapterFor("application");return adapter instanceof DS.FixtureAdapter},makeFixture:function(){Ember.deprecate("DEPRECATION Warning: use FactoryGuy.make instead");FactoryGuy.make.call(FactoryGuy,arguments)},makeList:function(){Ember.deprecate("DEPRECATION Warning: use FactoryGuy.makeList instead");FactoryGuy.makeList.call(FactoryGuy,arguments)},makeModel:function(modelType,fixture){var store=this,modelName=store.modelFor(modelType).typeKey,model;Em.run(function(){store.findEmbeddedAssociationsForRESTAdapter(modelType,fixture);if(fixture.type){modelName=fixture.type.underscore();modelType=store.modelFor(modelName)}model=store.push(modelName,fixture);store.setAssociationsForRESTAdapter(modelType,modelName,model)});return model},setAssociationsForFixtureAdapter:function(modelType,modelName,fixture){var self=this;var adapter=this.adapterFor("application");Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){var hasManyRelation,belongsToRecord;if(relationship.kind=="hasMany"){hasManyRelation=fixture[relationship.key];if(hasManyRelation){$.each(fixture[relationship.key],function(index,object){var id=object;if(Ember.typeOf(object)=="object"){FactoryGuy.pushFixture(relationship.type,object);id=object.id;hasManyRelation[index]=id}var hasManyfixtures=adapter.fixturesForType(relationship.type);var hasManyFixture=adapter.findFixtureById(hasManyfixtures,id);hasManyFixture[modelName]=fixture.id;self.loadModelForFixtureAdapter(relationship.type,hasManyFixture)})}}if(relationship.kind=="belongsTo"){belongsToRecord=fixture[relationship.key];if(belongsToRecord){if(typeof belongsToRecord=="object"){FactoryGuy.pushFixture(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord.id}var hasManyName=self.findHasManyRelationshipNameForFixtureAdapter(relationship.type,relationship.parentType);var belongsToFixtures=adapter.fixturesForType(relationship.type);var belongsTofixture=adapter.findFixtureById(belongsToFixtures,fixture[relationship.key]);if(!belongsTofixture[hasManyName]){belongsTofixture[hasManyName]=[]}belongsTofixture[hasManyName].push(fixture.id);self.loadModelForFixtureAdapter(relationship.type,belongsTofixture)}}})},loadModelForFixtureAdapter:function(modelType,fixture){var storeModel=this.getById(modelType,fixture.id),that=this;if(!Ember.isPresent(storeModel)||storeModel.get("isEmpty")){Ember.run(function(){var dup=Ember.copy(fixture,true);that.push(modelType,fixture);Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){if(fixture[relationship.key]){fixture[relationship.key]=dup[relationship.key]}})})}},findEmbeddedAssociationsForRESTAdapter:function(modelType,fixture){var store=this;Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind=="belongsTo"){var belongsToRecord=fixture[relationship.key];if(Ember.typeOf(belongsToRecord)=="object"){store.findEmbeddedAssociationsForRESTAdapter(relationship.type,belongsToRecord);belongsToRecord=store.push(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord}}if(relationship.kind=="hasMany"){var hasManyRecords=fixture[relationship.key];if(Ember.typeOf(hasManyRecords)=="array"){if(Ember.typeOf(hasManyRecords[0])=="object"){var records=Em.A();hasManyRecords.map(function(object){store.findEmbeddedAssociationsForRESTAdapter(relationship.type,object);var record=store.push(relationship.type,object);records.push(record);return record});fixture[relationship.key]=records}}}})},setAssociationsForRESTAdapter:function(modelType,modelName,model){var self=this;Ember.get(modelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind=="hasMany"){var children=model.get(name)||[];children.forEach(function(child){var belongsToName=self.findRelationshipName("belongsTo",child.constructor,model);if(belongsToName){child.set(belongsToName,model)}})}})},findRelationshipName:function(kind,belongToModelType,childModel){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind==kind&&childModel instanceof relationship.type){relationshipName=relationship.key}});return relationshipName},findHasManyRelationshipNameForFixtureAdapter:function(belongToModelType,childModelType){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(relationship,name){if(relationship.kind=="hasMany"&&childModelType==relationship.type){relationshipName=relationship.key}});return relationshipName},pushPayload:function(type,payload){var typeName,model;if(this.usingFixtureAdapter()){if(Ember.typeOf(type)==="string"&&Ember.isPresent(payload)&&Ember.isPresent(payload.id)){model=this.modelFor(type);FactoryGuy.pushFixture(model,payload);this.push(model,Ember.copy(payload,true))}else if(Ember.typeOf(type)==="object"||Ember.typeOf(payload)==="object"){if(Ember.isBlank(payload)){payload=type}for(var prop in payload){typeName=Ember.String.camelize(Ember.String.singularize(prop));model=this.modelFor(typeName);this.pushMany(model,Ember.makeArray(Ember.copy(payload[prop],true)));Ember.ArrayPolyfills.forEach.call(Ember.makeArray(payload[prop]),function(hash){FactoryGuy.pushFixture(model,hash)},this)}}else{throw new Ember.Error("Assertion Failed: You cannot use `store#pushPayload` with this method signature pushPayload("+type+","+payload+")")}}else{this._super(type,payload)}}})})();var FactoryGuyTestMixin=Em.Mixin.create({setup:function(app){this.set("container",app.__container__);FactoryGuy.setStore(this.getStore());return this},useFixtureAdapter:function(app){app.ApplicationAdapter=DS.FixtureAdapter;this.getStore().adapterFor("application").simulateRemoteResponse=false},usingActiveModelSerializer:function(type){var store=this.getStore();var modelType=store.modelFor(type);var serializer=store.serializerFor(modelType.typeKey);return serializer instanceof DS.ActiveModelSerializer},find:function(type,id){return this.getStore().find(type,id)},make:function(){return FactoryGuy.make.apply(FactoryGuy,arguments)},getStore:function(){return this.get("container").lookup("store:main")},stubEndpointForHttpRequest:function(url,json,options){options=options||{};var request={url:url,dataType:"json",responseText:json,type:options.type||"GET",status:options.status||200};if(options.urlParams){request.urlParams=options.urlParams}if(options.data){request.data=options.data}$.mockjax(request)},buildURL:function(typeName,id){var type=this.getStore().modelFor(typeName);return this.getStore().adapterFor(type).buildURL(type.typeKey,id)},handleFindMany:function(){var records=FactoryGuy.makeList.apply(FactoryGuy,arguments);var name=arguments[0];var modelName=FactoryGuy.lookupModelForFixtureName(name);var responseJson={};var json=records.map(function(record){return record.toJSON({includeId:true})});responseJson[modelName.pluralize()]=json;var url=this.buildURL(modelName);this.stubEndpointForHttpRequest(url,responseJson)},handleFindOne:function(){var record=FactoryGuy.make.apply(FactoryGuy,arguments);var name=arguments[0];var modelName=FactoryGuy.lookupModelForFixtureName(name);var responseJson={};var json=record.toJSON({includeId:true});responseJson[modelName.pluralize()]=json;var url=this.buildURL(modelName,record.id);this.stubEndpointForHttpRequest(url,responseJson)},handleFindQuery:function(modelName,searchParams,records){Ember.assert("The second argument of searchParams must be an array",Em.typeOf(searchParams)=="array");if(records){Ember.assert("The third argument ( records ) must be an array - found type:"+Em.typeOf(records),Em.typeOf(records)=="array")}else{records=[]}var json=records.map(function(record){return record.toJSON({includeId:true})});var responseJson={};responseJson[modelName.pluralize()]=json;var url=this.buildURL(modelName);this.stubEndpointForHttpRequest(url,responseJson,{urlParams:searchParams})},handleCreate:function(modelName,options){var opts=options===undefined?{}:options;var succeed=opts.succeed===undefined?true:opts.succeed;var match=opts.match||{};var returnArgs=opts.returns||{};var url=this.buildURL(modelName);var definition=FactoryGuy.modelDefinitions[modelName];var httpOptions={type:"POST"};if(opts.match){var expectedRequest={};var record=this.getStore().createRecord(modelName,match);expectedRequest[modelName]=record.serialize();httpOptions.data=JSON.stringify(expectedRequest)}var modelType=this.getStore().modelFor(modelName);var responseJson={};if(succeed){responseJson[modelName]=$.extend({id:definition.nextId()},match,returnArgs);Ember.get(modelType,"relationshipsByName").forEach(function(relationship){if(relationship.kind=="belongsTo"){delete responseJson[modelName][relationship.key]}})}else{httpOptions.status=500}this.stubEndpointForHttpRequest(url,responseJson,httpOptions)},handleUpdate:function(){var args=Array.prototype.slice.call(arguments);Ember.assert("To handleUpdate pass in a model instance or a type and an id",args.length>0);var succeed=true;if(typeof args[args.length-1]=="boolean"){args.pop();succeed=false}Ember.assert("To handleUpdate pass in a model instance or a type and an id",args.length>0);var type,id;if(args[0]instanceof DS.Model){var model=args[0];type=model.constructor.typeKey;id=model.id}else if(typeof args[0]=="string"&&typeof parseInt(args[1])=="number"){type=args[0];id=args[1]}Ember.assert("To handleUpdate pass in a model instance or a type and an id",type&&id);this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"PUT",status:succeed?200:500})},handleDelete:function(type,id,succeed){succeed=succeed===undefined?true:succeed;this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"DELETE",status:succeed?200:500})},teardown:function(){FactoryGuy.resetModels(this.getStore());$.mockjax.clear()}});if(FactoryGuy!==undefined){FactoryGuy.testMixin=FactoryGuyTestMixin} \ No newline at end of file diff --git a/src/factory_guy_test_mixin.js b/src/factory_guy_test_mixin.js index 91e43298..bcb0c088 100644 --- a/src/factory_guy_test_mixin.js +++ b/src/factory_guy_test_mixin.js @@ -104,6 +104,38 @@ var FactoryGuyTestMixin = Em.Mixin.create({ var url = this.buildURL(modelName); this.stubEndpointForHttpRequest(url, responseJson); }, + + /** + Handling ajax GET for finding one record for a type of model and an id. + You can mock failed find by passing in success argument as false. + + ```js + // Pass in the parameters you would normally pass into FactoryGuy.make, + // like fixture name, number of fixtures to make, and optional traits, + // or fixture options + testHelper.handleFindOne('user', 'with_hats', {id: 1}); + + store.find('user', 1).then(function(user){ + + }); + ``` + + @param {String} name name of the fixture ( or model ) to find + @param {String} trait optional traits (one or more) + @param {Object} opts optional fixture options (including id) + */ + handleFindOne: function () { + // make the records and load them in the store + var record = FactoryGuy.make.apply(FactoryGuy, arguments); + var name = arguments[0]; + var modelName = FactoryGuy.lookupModelForFixtureName(name); + var responseJson = {}; + var json = record.toJSON({includeId: true}); + responseJson[modelName.pluralize()] = json; + var url = this.buildURL(modelName, record.id); + this.stubEndpointForHttpRequest(url, responseJson); + }, + /** Handling ajax GET for finding all records for a type of model with query parameters. diff --git a/tests/factory_guy_test_mixin_test.js b/tests/factory_guy_test_mixin_test.js index f2146bb2..656ae257 100644 --- a/tests/factory_guy_test_mixin_test.js +++ b/tests/factory_guy_test_mixin_test.js @@ -149,6 +149,50 @@ asyncTest("#handleFindMany with traits and extra options", function () { }); +//////// handleFindOne ///////// + +asyncTest("#handleFindOne the basic", function () { + var id = 1 + testHelper.handleFindOne('profile', {id: id}); + + store.find('profile', 1).then(function (profile) { + ok(profile.get('id') == id); + start(); + }); +}); + +asyncTest("#handleFindOne with traits", function () { + var id = 1 + testHelper.handleFindOne('profile', 'goofy_description', {id: id}); + + store.find('profile', 1).then(function (profile) { + ok(profile.get('description') == 'goofy'); + start(); + }); +}); + +asyncTest("#handleFindOne with arguments", function () { + var id = 1 + var description = 'guy'; + testHelper.handleFindOne('profile', {id: id, description: description }); + + store.find('profile', 1).then(function (profile) { + ok(profile.get('description') == description); + start(); + }); +}); + +asyncTest("#handleFindOne with traits and arguments", function () { + var id = 1 + var description = 'guy'; + testHelper.handleFindOne('profile', 'goofy_description', {id: id, description: description}); + + store.find('profile', 1).then(function (profile) { + ok(profile.get('description') == description); + start(); + }); +}) + module('FactoryGuyTestMixin (using mockjax) with DS.ActiveModelAdapter', { diff --git a/vendor/assets/javascripts/ember_data_factory_guy.js b/vendor/assets/javascripts/ember_data_factory_guy.js index da6345b3..d3cb1c8a 100644 --- a/vendor/assets/javascripts/ember_data_factory_guy.js +++ b/vendor/assets/javascripts/ember_data_factory_guy.js @@ -952,6 +952,38 @@ var FactoryGuyTestMixin = Em.Mixin.create({ var url = this.buildURL(modelName); this.stubEndpointForHttpRequest(url, responseJson); }, + + /** + Handling ajax GET for finding one record for a type of model and an id. + You can mock failed find by passing in success argument as false. + + ```js + // Pass in the parameters you would normally pass into FactoryGuy.make, + // like fixture name, number of fixtures to make, and optional traits, + // or fixture options + testHelper.handleFindOne('user', 'with_hats', {id: 1}); + + store.find('user', 1).then(function(user){ + + }); + ``` + + @param {String} name name of the fixture ( or model ) to find + @param {String} trait optional traits (one or more) + @param {Object} opts optional fixture options (including id) + */ + handleFindOne: function () { + // make the records and load them in the store + var record = FactoryGuy.make.apply(FactoryGuy, arguments); + var name = arguments[0]; + var modelName = FactoryGuy.lookupModelForFixtureName(name); + var responseJson = {}; + var json = record.toJSON({includeId: true}); + responseJson[modelName.pluralize()] = json; + var url = this.buildURL(modelName, record.id); + this.stubEndpointForHttpRequest(url, responseJson); + }, + /** Handling ajax GET for finding all records for a type of model with query parameters. @@ -1239,12 +1271,10 @@ if (FactoryGuy !== undefined) { return null; } } + // Inspect the data submitted in the request (either POST body or GET query string) if ( handler.data ) { -// console.log('request.data', requestSettings.data ) -// console.log('handler.data', handler.data ) -// console.log('data equal', isMockDataEqual(handler.data, requestSettings.data) ) - if ( ! requestSettings.data || !isMockDataEqual(handler.data, requestSettings.data) ) { + if ( ! requestSettings.data || !isMockDataEqual(handler.data, requestSettings.data) ) { // They're not identical, do not mock this request return null; } @@ -1255,6 +1285,7 @@ if (FactoryGuy !== undefined) { // The request type doesn't match (GET vs. POST) return null; } + return handler; }