From 4388459f47ae17e0d07b9cb0b840ca4621725f3e Mon Sep 17 00:00:00 2001 From: Hok Shun Poon Date: Thu, 10 Dec 2015 17:49:26 +0000 Subject: [PATCH 1/3] Make the test fail by adding a request object. Pass in the request object into the beforeSave handler. Rename variable. Rename request. Add failing test for beforeDelete. Fix test by switching the flag. Fail test again by not defining a return value. Reason about beforeSave override value. Fix test by performing deletion. Simplify creation of Brand. Remove TODO by adding assertion for empty collection. Add assertions for the state of the mock collection. Fix semicolons. Replace => syntax with function() syntax. Inline variable. Remove whitespace. --- src/parse-mockdb.js | 32 ++++++--- test/test.js | 153 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 9 deletions(-) diff --git a/src/parse-mockdb.js b/src/parse-mockdb.js index fda9059..2a2d210 100644 --- a/src/parse-mockdb.js +++ b/src/parse-mockdb.js @@ -310,15 +310,29 @@ const SPECIAL_CLASS_NAMES = { function handleRequest(method, path, body) { const explodedPath = normalizePath(path).split('/'); - const start = explodedPath.shift(); - const className = start === 'classes' ? explodedPath.shift() : SPECIAL_CLASS_NAMES[start] - - var request = { - method: method, - className, - data: body, - objectId: explodedPath.shift(), - }; + + var request; + switch(explodedPath[0]) { + case 'users': { + request = { + method: method, + className: '_User', + data: body, + objectId: explodedPath[1], + }; + break; + } + case 'classes': + default: { + request = { + method: method, + className: explodedPath[1], + data: body, + objectId: explodedPath[2], + }; + } + } + return HANDLERS[method](request); } diff --git a/test/test.js b/test/test.js index 79e623d..c569a6e 100644 --- a/test/test.js +++ b/test/test.js @@ -25,6 +25,8 @@ class Store extends Parse.Object { } Parse.Object.registerSubclass('Store', Store); +class CustomUserSubclass extends Parse.User {} + function createBrandP(name) { const brand = new Brand(); brand.set("name", name); @@ -47,12 +49,119 @@ function createStoreWithItemP(item) { return store.save(); } +function createUserP(name) { + var user = new CustomUserSubclass() + user.set('name', name); + return user.save(); +} + function itemQueryP(price) { const query = new Parse.Query(Item); query.equalTo("price", price); return query.find(); } +function behavesLikeParseObjectOnBeforeSave(typeName, ParseObjectOrUserSubclass) { + + context('when object has beforeSave hook registered', function() { + + function beforeSavePromise(request) { + var object = request.object; + if (object.get("error")) { + return Parse.Promise.error("whoah"); + } + object.set('cool', true); + return Parse.Promise.as(object); + } + + it('runs the hook before saving the model and persists the object', function() { + ParseMockDB.registerHook(typeName, 'beforeSave', beforeSavePromise); + + var object = new ParseObjectOrUserSubclass(); + assert(!object.has('cool')); + + object.save().then(function (savedObject) { + assert(savedObject.has('cool')); + assert(savedObject.get('cool')); + + new Parse.Query(ParseObjectOrUserSubclass).first().then(function (queriedObject) { + assert(queriedObject.has('cool')); + assert(queriedObject.get('cool')); + }); + }); + }); + + it('rejects the save if there is a problem', function(done) { + ParseMockDB.registerHook(typeName, 'beforeSave', beforeSavePromise); + + var object = new ParseObjectOrUserSubclass({error: true}); + + object.save().then(function (savedObject) { + throw new Error("should not have saved") + }, function(error) { + assert.equal(error, "whoah"); + done(); + }); + }); + }); + +} + +function behavesLikeParseObjectOnBeforeDelete(typeName, ParseObjectOrUserSubclass) { + + context('when object has beforeDelete hook registered', function() { + + var beforeDeleteWasRun; + + beforeEach(function () { + beforeDeleteWasRun = false; + }); + + function beforeDeletePromise(request) { + var object = request.object; + if (object.get("error")) { + return Parse.Promise.error("whoah"); + } + beforeDeleteWasRun = true; + return Parse.Promise.as(); + } + + it('runs the hook before deleting the object', function() { + ParseMockDB.registerHook(typeName, 'beforeDelete', beforeDeletePromise); + + new ParseObjectOrUserSubclass().save().done(function (savedParseObjectOrUserSubclass) { + return Parse.Object.destroyAll([savedParseObjectOrUserSubclass]); + }).done(function () { + assert(beforeDeleteWasRun); + }); + + new Parse.Query(ParseObjectOrUserSubclass).find().done(function (results) { + assert.equal(results.length, 0); + }); + }); + + it('rejects the delete if there is a problem', function(done) { + ParseMockDB.registerHook(typeName, 'beforeDelete', beforeDeletePromise); + + var object = new ParseObjectOrUserSubclass({error: true}); + object.save().done(function (savedParseObjectOrUserSubclass) { + return Parse.Object.destroyAll([savedParseObjectOrUserSubclass]); + }).then(function (deletedParseObjectOrUserSubclass) { + throw new Error("should not have deleted") + }, function(error) { + assert.equal(error, "whoah"); + return new Parse.Query(ParseObjectOrUserSubclass).find(); + }).done(function(results) { + assert.equal(results.length, 1); + done(); + }); + }); + + }); + +} + + describe('ParseMock', function(){ beforeEach(function() { Parse.MockDB.mockDB(); @@ -62,6 +171,29 @@ describe('ParseMock', function(){ Parse.MockDB.cleanUp(); }); + context('supports Parse.User subclasses', function() { + + it("should save user", function() { + return createUserP('Tom').then(function(user) { + assert.equal(user.get("name"), 'Tom'); + }); + }); + + it('should save and find a user', function () { + return createUserP('Tom').then(function(user) { + const query = new Parse.Query(CustomUserSubclass); + query.equalTo("name", 'Tom'); + return query.first().then(function(user) { + assert.equal(user.get('name'), 'Tom'); + }); + }); + }); + + behavesLikeParseObjectOnBeforeSave('_User', CustomUserSubclass); + behavesLikeParseObjectOnBeforeDelete('_User', CustomUserSubclass); + + }); + it("should save correctly", function() { return createItemP(30).then(function(item) { assert.equal(item.get("price"), 30); @@ -862,14 +994,24 @@ describe('ParseMock', function(){ return Parse.Promise.as(brand); } +<<<<<<< 676801f521305aeaa676039830357a73058c9f8f it('runs the hook before saving the model and persists the object', function() { +======= + + it('runs the hook before saving the model and persists the object', function(done) { +>>>>>>> Make the test fail by adding a request object. ParseMockDB.registerHook('Brand', 'beforeSave', beforeSavePromise); const brand = new Brand(); assert(!brand.has('cool')); brand.set('nestedObject', { foo: 3 }); +<<<<<<< 676801f521305aeaa676039830357a73058c9f8f return brand.save().then(function(savedBrand) { +======= + + brand.save().then(function(savedBrand) { +>>>>>>> Make the test fail by adding a request object. assert(savedBrand.has('cool'), 'saved brand doesn\'t have cool'); assert(savedBrand.get('cool')); assert.equal(savedBrand.get('nestedObject').foo, 3); @@ -886,8 +1028,14 @@ describe('ParseMock', function(){ const brand = new Brand({error: true}); +<<<<<<< 676801f521305aeaa676039830357a73058c9f8f return brand.save().then(function(savedBrand) { assert.fail(null, null, "should not have saved"); +======= + + brand.save().then(function(savedBrand) { + throw new Error("should not have saved") +>>>>>>> Make the test fail by adding a request object. }, function(error) { assert.equal(error, "whoah"); }); @@ -944,8 +1092,13 @@ describe('ParseMock', function(){ }); }); +<<<<<<< 676801f521305aeaa676039830357a73058c9f8f it('successfully uses containsAll query', function() { return Parse.Promise.when(createItemP(30), createItemP(20)).then((item1, item2) => { +======= + it('successfully uses containsAll query', function(done) { + Parse.Promise.when([createItemP(30), createItemP(20)]).then((item1, item2) => { +>>>>>>> Make the test fail by adding a request object. const store = new Store({ items: [item1.toPointer(), item2.toPointer()], }); From 789a65f9e3d159141c02f478e5edce70a7910943 Mon Sep 17 00:00:00 2001 From: Arthur Cinader Date: Mon, 20 Jun 2016 12:45:09 -0700 Subject: [PATCH 2/3] clean up parse user class handeling 1. don't use switch 2. get unit tests in working order 3. remove code duplication in unit tests. --- src/parse-mockdb.js | 32 ++---- test/test.js | 239 +++++++++++++------------------------------- 2 files changed, 76 insertions(+), 195 deletions(-) diff --git a/src/parse-mockdb.js b/src/parse-mockdb.js index 2a2d210..17d7072 100644 --- a/src/parse-mockdb.js +++ b/src/parse-mockdb.js @@ -310,29 +310,15 @@ const SPECIAL_CLASS_NAMES = { function handleRequest(method, path, body) { const explodedPath = normalizePath(path).split('/'); - - var request; - switch(explodedPath[0]) { - case 'users': { - request = { - method: method, - className: '_User', - data: body, - objectId: explodedPath[1], - }; - break; - } - case 'classes': - default: { - request = { - method: method, - className: explodedPath[1], - data: body, - objectId: explodedPath[2], - }; - } - } - + const start = explodedPath.shift(); + const className = start === 'classes' ? explodedPath.shift() : SPECIAL_CLASS_NAMES[start]; + + const request = { + method: method, + className, + data: body, + objectId: explodedPath.shift(), + }; return HANDLERS[method](request); } diff --git a/test/test.js b/test/test.js index c569a6e..64dc08b 100644 --- a/test/test.js +++ b/test/test.js @@ -25,7 +25,7 @@ class Store extends Parse.Object { } Parse.Object.registerSubclass('Store', Store); -class CustomUserSubclass extends Parse.User {} +class CustomUserSubclass extends Parse.User { }; function createBrandP(name) { const brand = new Brand(); @@ -50,7 +50,7 @@ function createStoreWithItemP(item) { } function createUserP(name) { - var user = new CustomUserSubclass() + const user = new CustomUserSubclass(); user.set('name', name); return user.save(); } @@ -62,11 +62,10 @@ function itemQueryP(price) { } function behavesLikeParseObjectOnBeforeSave(typeName, ParseObjectOrUserSubclass) { - context('when object has beforeSave hook registered', function() { function beforeSavePromise(request) { - var object = request.object; + const object = request.object; if (object.get("error")) { return Parse.Promise.error("whoah"); } @@ -77,34 +76,32 @@ function behavesLikeParseObjectOnBeforeSave(typeName, ParseObjectOrUserSubclass) it('runs the hook before saving the model and persists the object', function() { ParseMockDB.registerHook(typeName, 'beforeSave', beforeSavePromise); - var object = new ParseObjectOrUserSubclass(); + const object = new ParseObjectOrUserSubclass(); assert(!object.has('cool')); - object.save().then(function (savedObject) { + return object.save().then(function(savedObject) { assert(savedObject.has('cool')); assert(savedObject.get('cool')); - new Parse.Query(ParseObjectOrUserSubclass).first().then(function (queriedObject) { + return new Parse.Query(ParseObjectOrUserSubclass).first().then(function(queriedObject) { assert(queriedObject.has('cool')); assert(queriedObject.get('cool')); }); }); }); - it('rejects the save if there is a problem', function(done) { + it('rejects the save if there is a problem', function() { ParseMockDB.registerHook(typeName, 'beforeSave', beforeSavePromise); - var object = new ParseObjectOrUserSubclass({error: true}); + const object = new ParseObjectOrUserSubclass({error: true}); - object.save().then(function (savedObject) { - throw new Error("should not have saved") + return object.save().then(function(savedObject) { + assert.fail(null, null, "should not have saved"); }, function(error) { assert.equal(error, "whoah"); - done(); }); }); }); - } function behavesLikeParseObjectOnBeforeDelete(typeName, ParseObjectOrUserSubclass) { @@ -113,12 +110,12 @@ function behavesLikeParseObjectOnBeforeDelete(typeName, ParseObjectOrUserSubclas var beforeDeleteWasRun; - beforeEach(function () { + beforeEach(function() { beforeDeleteWasRun = false; }); function beforeDeletePromise(request) { - var object = request.object; + const object = request.object; if (object.get("error")) { return Parse.Promise.error("whoah"); } @@ -129,38 +126,42 @@ function behavesLikeParseObjectOnBeforeDelete(typeName, ParseObjectOrUserSubclas it('runs the hook before deleting the object', function() { ParseMockDB.registerHook(typeName, 'beforeDelete', beforeDeletePromise); - new ParseObjectOrUserSubclass().save().done(function (savedParseObjectOrUserSubclass) { + const promises = []; + + promises.push(new ParseObjectOrUserSubclass() + .save() + .done(function(savedParseObjectOrUserSubclass) { return Parse.Object.destroyAll([savedParseObjectOrUserSubclass]); - }).done(function () { - assert(beforeDeleteWasRun); - }); + }).done(function() { + assert(beforeDeleteWasRun); + })); - new Parse.Query(ParseObjectOrUserSubclass).find().done(function (results) { - assert.equal(results.length, 0); - }); + promises.push(new Parse.Query(ParseObjectOrUserSubclass) + .find() + .done(function(results) { + assert.equal(results.length, 0); + })); + + return Parse.Promise.when(promises); }); - it('rejects the delete if there is a problem', function(done) { + it('rejects the delete if there is a problem', function() { ParseMockDB.registerHook(typeName, 'beforeDelete', beforeDeletePromise); - var object = new ParseObjectOrUserSubclass({error: true}); - object.save().done(function (savedParseObjectOrUserSubclass) { + const object = new ParseObjectOrUserSubclass({error: true}); + return object.save().done(function(savedParseObjectOrUserSubclass) { return Parse.Object.destroyAll([savedParseObjectOrUserSubclass]); - }).then(function (deletedParseObjectOrUserSubclass) { - throw new Error("should not have deleted") - }, function(error) { - assert.equal(error, "whoah"); - return new Parse.Query(ParseObjectOrUserSubclass).find(); - }).done(function(results) { - assert.equal(results.length, 1); - done(); - }); + }).then(function(deletedParseObjectOrUserSubclass) { + assert.fail(null, null, "should not have deleted"); + }, function(error) { + assert.equal(error, "whoah"); + return new Parse.Query(ParseObjectOrUserSubclass).find(); + }).done(function(results) { + assert.equal(results.length, 1); + }); }); - }); - -} - +}; describe('ParseMock', function(){ beforeEach(function() { @@ -179,7 +180,7 @@ describe('ParseMock', function(){ }); }); - it('should save and find a user', function () { + it('should save and find a user', function() { return createUserP('Tom').then(function(user) { const query = new Parse.Query(CustomUserSubclass); query.equalTo("name", 'Tom'); @@ -191,7 +192,6 @@ describe('ParseMock', function(){ behavesLikeParseObjectOnBeforeSave('_User', CustomUserSubclass); behavesLikeParseObjectOnBeforeDelete('_User', CustomUserSubclass); - }); it("should save correctly", function() { @@ -741,8 +741,9 @@ describe('ParseMock', function(){ /** * see: https://github.com/ParsePlatform/Parse-SDK-JS/issues/91 - * - it("should not overwrite included objects after a save", function() { + * NOTE TEST IS DISABLED + */ + xit("should not overwrite included objects after a save", function() { return createBrandP("Acme").then(function(brand) { return createItemP(30, brand).then(function(item) { return createStoreWithItemP(item).then(function(store) { @@ -759,12 +760,12 @@ describe('ParseMock', function(){ }); }); }); -*/ /** * see: https://github.com/ParsePlatform/Parse-SDK-JS/issues/91 - * - it("should update an existing object correctly", function() { + * NOTE TEST IS DISABLED + */ + xit("should update an existing object correctly", function() { return Parse.Promise.when(createItemP(30), createItemP(20)).then(function(item1, item2) { return createStoreWithItemP(item1).then(function(store) { item2.set("price", 10); @@ -776,7 +777,6 @@ describe('ParseMock', function(){ }); }); }); -*/ it("should support a nested query", function() { const brand = new Brand(); @@ -962,143 +962,38 @@ describe('ParseMock', function(){ // See github issue: https://github.com/ParsePlatform/Parse-SDK-JS/issues/89 // and uncomment, delete or rewrite when resolved - // it('should deep save and update nested objects', function() { - // const brand = new Brand(); - // brand.set("name", "Acme"); - // brand.set("country", "US"); - // const item = new Item(); - // item.set("price", 30); - // item.set("country_code", "US"); - // brand.set("items", [item]); - // return brand.save().then(function(savedBrand) { - // assert.equal(savedBrand.get("items")[0].get("price"), item.get("price")); - - // const item2 = new Item(); - // item2.set("price", 20); - // brand.set("items", [item2]); - // return brand.save().then(function(updatedBrand) { - // assert.equal(updatedBrand.get("items")[0].get("price"), 20); - // }); - // }); - // }); - - - context('when object has beforeSave hook registered', function() { - - function beforeSavePromise(request) { - const brand = request.object; - if (brand.get("error")) { - return Parse.Promise.error("whoah"); - } - brand.set('cool', true); - return Parse.Promise.as(brand); - } - -<<<<<<< 676801f521305aeaa676039830357a73058c9f8f - it('runs the hook before saving the model and persists the object', function() { -======= - - it('runs the hook before saving the model and persists the object', function(done) { ->>>>>>> Make the test fail by adding a request object. - ParseMockDB.registerHook('Brand', 'beforeSave', beforeSavePromise); - - const brand = new Brand(); - assert(!brand.has('cool')); - brand.set('nestedObject', { foo: 3 }); - -<<<<<<< 676801f521305aeaa676039830357a73058c9f8f - return brand.save().then(function(savedBrand) { -======= - - brand.save().then(function(savedBrand) { ->>>>>>> Make the test fail by adding a request object. - assert(savedBrand.has('cool'), 'saved brand doesn\'t have cool'); - assert(savedBrand.get('cool')); - assert.equal(savedBrand.get('nestedObject').foo, 3); - return new Parse.Query(Brand).first().then(function(queriedBrand) { - assert(queriedBrand.has('cool')); - assert(queriedBrand.get('cool')); - assert.equal(savedBrand.get('nestedObject').foo, 3); - }); + // NOTE TEST IS DISABLED + xit('should deep save and update nested objects', function() { + const brand = new Brand(); + brand.set("name", "Acme"); + brand.set("country", "US"); + const item = new Item(); + item.set("price", 30); + item.set("country_code", "US"); + brand.set("items", [item]); + return brand.save().then(function(savedBrand) { + assert.equal(savedBrand.get("items")[0].get("price"), item.get("price")); + + const item2 = new Item(); + item2.set("price", 20); + brand.set("items", [item2]); + return brand.save().then(function(updatedBrand) { + assert.equal(updatedBrand.get("items")[0].get("price"), 20); }); }); + }); - it('rejects the save if there is a problem', function() { - ParseMockDB.registerHook('Brand', 'beforeSave', beforeSavePromise); - - const brand = new Brand({error: true}); - -<<<<<<< 676801f521305aeaa676039830357a73058c9f8f - return brand.save().then(function(savedBrand) { - assert.fail(null, null, "should not have saved"); -======= - brand.save().then(function(savedBrand) { - throw new Error("should not have saved") ->>>>>>> Make the test fail by adding a request object. - }, function(error) { - assert.equal(error, "whoah"); - }); - }); + context('when object has beforeSave hook registered', function() { + behavesLikeParseObjectOnBeforeSave('Brand', Brand); }); context('when object has beforeDelete hook registered', function() { - - var beforeDeleteWasRun; - - beforeEach(function() { - beforeDeleteWasRun = false; - }); - - function beforeDeletePromise(request) { - const brand = request.object; - if (brand.get("error")) { - return Parse.Promise.error("whoah"); - } - beforeDeleteWasRun = true; - return Parse.Promise.as(); - } - - it('runs the hook before deleting the object', function() { - ParseMockDB.registerHook('Brand', 'beforeDelete', beforeDeletePromise); - const promises = []; - - promises.push(createBrandP().then(function(savedBrand) { - return Parse.Object.destroyAll([savedBrand]); - })); - - promises.push(new Parse.Query(Brand).find()); - - return Parse.Promise.when(promises).then(function(results) { - assert(results[0]); - assert.equal(results[1].length, 0); - }); - }); - - it('rejects the delete if there is a problem', function() { - ParseMockDB.registerHook('Brand', 'beforeDelete', beforeDeletePromise); - - const brand = new Brand({error: true}); - return brand.save().done(function(savedBrand) { - return Parse.Object.destroyAll([savedBrand]); - }).then(function(deletedBrand) { - assert.fail(null, null, "should not have deleted"); - }, function(error) { - assert.equal(error, "whoah"); - return new Parse.Query(Brand).find(); - }).done(function(results) { - assert.equal(results.length, 1); - }); - }); + behavesLikeParseObjectOnBeforeDelete('Brand', Brand); }); -<<<<<<< 676801f521305aeaa676039830357a73058c9f8f it('successfully uses containsAll query', function() { return Parse.Promise.when(createItemP(30), createItemP(20)).then((item1, item2) => { -======= - it('successfully uses containsAll query', function(done) { - Parse.Promise.when([createItemP(30), createItemP(20)]).then((item1, item2) => { ->>>>>>> Make the test fail by adding a request object. const store = new Store({ items: [item1.toPointer(), item2.toPointer()], }); From 75c99d2152733ac5cd3fccc5e6ab4996cc75b2b4 Mon Sep 17 00:00:00 2001 From: Arthur Cinader Date: Wed, 29 Jun 2016 15:28:22 -0700 Subject: [PATCH 3/3] Add missing semicolons I figured out how to set up lint for this stuff using eslint. --- test/test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test.js b/test/test.js index 64dc08b..304444a 100644 --- a/test/test.js +++ b/test/test.js @@ -291,7 +291,7 @@ describe('ParseMock', function(){ it('should support addUnique', function() { return createItemP(30).then(function(item) { item.add("languages", "JS"); - item.add("languages", "Ruby") + item.add("languages", "Ruby"); return item.save(); }).then(function(item) { assert.deepEqual(item.get("languages"), ["JS", "Ruby"]); @@ -306,7 +306,7 @@ describe('ParseMock', function(){ return createItemP(30).then(function(item) { item.add("languages", "JS"); item.add("languages", "JS"); - item.add("languages", "Ruby") + item.add("languages", "Ruby"); return item.save(); }).then(function(item) { assert.deepEqual(item.get("languages"), ["JS", "JS", "Ruby"]); @@ -519,7 +519,7 @@ describe('ParseMock', function(){ let savedItem; return new Item().save({price: 30}).then(function(item1) { savedItem = item1; - return Item.createWithoutData(item1.id).fetch() + return Item.createWithoutData(item1.id).fetch(); }).then(function(fetched) { assert.equal(fetched.id, savedItem.id); assert.equal(fetched.get('price'), 30); @@ -1064,7 +1064,7 @@ describe('ParseMock', function(){ const relation = store2.relation('items'); relation.add(paperTowels); relation.add(toothPaste); - return store2.save() + return store2.save(); }).then(() => { const query = new Parse.Query(Store); query.equalTo('items', Item.createWithoutData(tpId)); @@ -1078,7 +1078,7 @@ describe('ParseMock', function(){ it('should handle the User class', function() { const user = new Parse.User({name: "Turtle"}); return user.save().then((savedUser) => { - return (new Parse.Query(Parse.User).find()) + return (new Parse.Query(Parse.User).find()); }).then((foundUsers) => { assert.equal(foundUsers.length, 1); assert.equal(foundUsers[0].get('name'), "Turtle"); @@ -1090,7 +1090,7 @@ describe('ParseMock', function(){ roleACL.setPublicReadAccess(true); const role = new Parse.Role("Turtle", roleACL); return role.save().then((savedRole) => { - return (new Parse.Query(Parse.Role).find()) + return (new Parse.Query(Parse.Role).find()); }).then((foundRoles) => { assert.equal(foundRoles.length, 1); assert.equal(foundRoles[0].get('name'), "Turtle"); @@ -1098,7 +1098,7 @@ describe('ParseMock', function(){ }); it('should handle redirectClassNameForKey', function() { - const user = new Parse.User({name: "T Rutlidge"}) + const user = new Parse.User({name: "T Rutlidge"}); return user.save().then((savedUser) => { const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true);