From 2a33fc0f792a145d583940c449d456a6f8c74293 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Sun, 1 Oct 2017 16:13:24 -0500 Subject: [PATCH 1/7] Support for Aggregate Queries --- integration/test/ParseQueryAggregateTest.js | 78 ++++++++++++++++ src/CoreManager.js | 1 + src/ParseQuery.js | 98 +++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 integration/test/ParseQueryAggregateTest.js diff --git a/integration/test/ParseQueryAggregateTest.js b/integration/test/ParseQueryAggregateTest.js new file mode 100644 index 000000000..d31d1d712 --- /dev/null +++ b/integration/test/ParseQueryAggregateTest.js @@ -0,0 +1,78 @@ +'use strict'; + +const assert = require('assert'); +const clear = require('./clear'); +const mocha = require('mocha'); +const Parse = require('../../node'); + +const TestObject = Parse.Object.extend('TestObject'); + +describe('Parse Aggregate Query', () => { + before((done) => { + Parse.initialize('integration', null, 'notsosecret'); + Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse'); + Parse.Storage._clear(); + clear().then(() => { + const obj1 = new TestObject({score: 10, name: 'foo'}); + const obj2 = new TestObject({score: 10, name: 'foo'}); + const obj3 = new TestObject({score: 10, name: 'bar'}); + const obj4 = new TestObject({score: 20, name: 'dpl'}); + return Parse.Object.saveAll([obj1, obj2, obj3, obj4]); + }).then(() => { + return Parse.User.logOut(); + }) + .then(() => { done() }, () => { done() }); + }); + + it('aggregate pipeline object query', (done) => { + const pipeline = { + group: { objectId: '$name' } + }; + const query = new Parse.Query(TestObject); + query.aggregate(pipeline).then((results) => { + assert.equal(results.length, 3); + done(); + }); + }); + + it('aggregate pipeline array query', (done) => { + const pipeline = [ + { group: { objectId: '$name' } } + ]; + const query = new Parse.Query(TestObject); + query.aggregate(pipeline).then((results) => { + assert.equal(results.length, 3); + done(); + }); + }); + + it('aggregate pipeline invalid query', (done) => { + const pipeline = 1234; + const query = new Parse.Query(TestObject); + try { + query.aggregate(pipeline).then(() => {}); + } catch (e) { + done(); + } + }); + + it('distinct query', (done) => { + const query = new Parse.Query(TestObject); + query.distinct('score').then((results) => { + assert.equal(results.length, 2); + assert.equal(results[0], 10); + assert.equal(results[1], 20); + done(); + }); + }); + + it('distinct equalTo query', (done) => { + const query = new Parse.Query(TestObject); + query.equalTo('name', 'foo') + query.distinct('score').then((results) => { + assert.equal(results.length, 1); + assert.equal(results[0], 10); + done(); + }); + }); +}); diff --git a/src/CoreManager.js b/src/CoreManager.js index 8dac29644..c3f11289a 100644 --- a/src/CoreManager.js +++ b/src/CoreManager.js @@ -71,6 +71,7 @@ type PushController = { }; type QueryController = { find: (className: string, params: QueryJSON, options: RequestOptions) => ParsePromise; + aggregate: (className: string, params: any, options: RequestOptions) => ParsePromise; }; type RESTController = { request: (method: string, path: string, data: mixed) => ParsePromise; diff --git a/src/ParseQuery.js b/src/ParseQuery.js index 77fc9b146..86dd6cca0 100644 --- a/src/ParseQuery.js +++ b/src/ParseQuery.js @@ -479,6 +479,93 @@ export default class ParseQuery { })._thenRunCallbacks(options); } + /** + * Executes a distinct query and returns unique values + * + * @method distinct + * @param {String} key A field to find distinct values + * @param {Object} options A Backbone-style options object. Valid options + * are: + * + * @return {Parse.Promise} A promise that is resolved with the query completes. + */ + distinct(key: string, options?: FullOptions): ParsePromise { + options = options || {}; + + const distinctOptions = { + useMasterKey: true + }; + if (options.hasOwnProperty('sessionToken')) { + distinctOptions.sessionToken = options.sessionToken; + } + const controller = CoreManager.getQueryController(); + const params = { + distinct: key, + where: this._where + }; + + return controller.aggregate( + this.className, + params, + distinctOptions + ).then((results) => { + return results.results; + })._thenRunCallbacks(options); + } + + /** + * Executes an aggregate query and returns aggregate results + * + * @method aggregate + * @param {Mixed} pipeline Array or Object of stages to process query + * @param {Object} options A Backbone-style options object. Valid options + * are: + * + * @return {Parse.Promise} A promise that is resolved with the query completes. + */ + aggregate(pipeline: mixed, options?: FullOptions): ParsePromise { + options = options || {}; + + const aggregateOptions = { + useMasterKey: true + }; + if (options.hasOwnProperty('sessionToken')) { + aggregateOptions.sessionToken = options.sessionToken; + } + const controller = CoreManager.getQueryController(); + let stages = {}; + + if (Array.isArray(pipeline)) { + pipeline.forEach((stage) => { + for (let op in stage) { + stages[op] = stage[op]; + } + }); + } else if (pipeline && typeof pipeline === 'object') { + stages = pipeline; + } else { + throw new Error('Invalid pipeline must be Array or Object'); + } + + return controller.aggregate( + this.className, + stages, + aggregateOptions + ).then((results) => { + return results.results; + })._thenRunCallbacks(options); + } + /** * Retrieves at most one Parse.Object that satisfies this query. * @@ -1229,6 +1316,17 @@ var DefaultController = { params, options ); + }, + + aggregate(className: string, params: any, options: RequestOptions): ParsePromise { + const RESTController = CoreManager.getRESTController(); + + return RESTController.request( + 'GET', + 'aggregate/' + className, + params, + options + ); } }; From 4f4b389d5e5a01242ebdd557004ab9b7ffb381ca Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Mon, 27 Nov 2017 15:41:02 -0600 Subject: [PATCH 2/7] bump to parse-server to 2.7.0 --- integration/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/package.json b/integration/package.json index b5a35b928..5927ccbc9 100644 --- a/integration/package.json +++ b/integration/package.json @@ -3,7 +3,7 @@ "dependencies": { "express": "^4.13.4", "mocha": "^2.4.5", - "parse-server": "^2.6.0" + "parse-server": "^2.7.0" }, "scripts": { "test": "mocha --reporter dot -t 5000" From fa7d1b68c8cb656e92ae6918508e2623d9d28147 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Tue, 28 Nov 2017 11:05:40 -0600 Subject: [PATCH 3/7] travis.yml 6.11.4 and docs --- .travis.yml | 2 +- src/ParseQuery.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 28561741c..fc6cec052 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ --- language: node_js node_js: -- '5' +- '6.11.4' branches: only: diff --git a/src/ParseQuery.js b/src/ParseQuery.js index 86dd6cca0..8e6b2ef31 100644 --- a/src/ParseQuery.js +++ b/src/ParseQuery.js @@ -482,7 +482,6 @@ export default class ParseQuery { /** * Executes a distinct query and returns unique values * - * @method distinct * @param {String} key A field to find distinct values * @param {Object} options A Backbone-style options object. Valid options * are:
    @@ -521,7 +520,6 @@ export default class ParseQuery { /** * Executes an aggregate query and returns aggregate results * - * @method aggregate * @param {Mixed} pipeline Array or Object of stages to process query * @param {Object} options A Backbone-style options object. Valid options * are:
      From dcb95b2812473634772bb8e434403c3530eeff14 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 29 Nov 2017 19:53:18 -0600 Subject: [PATCH 4/7] jest tests --- src/__tests__/ParseQuery-test.js | 236 ++++++++++++++++++++++++++----- 1 file changed, 198 insertions(+), 38 deletions(-) diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index 61d3b6cc1..3b3774776 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -1085,6 +1085,7 @@ describe('ParseQuery', () => { } }); + var q = new ParseQuery('Item'); q.equalTo('size', 'small').count({ useMasterKey: true, @@ -1338,7 +1339,7 @@ describe('ParseQuery', () => { }); - + it('overrides cached object with query results', (done) => { jest.dontMock("../ParseObject"); jest.resetModules(); @@ -1347,12 +1348,12 @@ describe('ParseQuery', () => { ParseQuery = require('../ParseQuery').default; ParseObject.enableSingleInstance(); - - var objectToReturn = { - objectId: 'T01', - name: 'Name', - other: 'other', - className:"Thing", + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + other: 'other', + className:"Thing", createdAt: '2017-01-10T10:00:00Z' }; @@ -1368,10 +1369,10 @@ describe('ParseQuery', () => { var testObject; q.find().then((results) => { testObject = results[0]; - + expect(testObject.get("name")).toBe("Name"); expect(testObject.get("other")).toBe("other"); - + objectToReturn = { objectId: 'T01', name: 'Name2'}; var q2 = new ParseQuery("Thing"); return q2.find(); @@ -1393,13 +1394,13 @@ describe('ParseQuery', () => { ParseQuery = require('../ParseQuery').default; ParseObject.enableSingleInstance(); - - var objectToReturn = { - objectId: 'T01', - name: 'Name', - other: 'other', - tbd: 'exists', - className:"Thing", + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + other: 'other', + tbd: 'exists', + className:"Thing", createdAt: '2017-01-10T10:00:00Z', subObject: {key1:"value", key2:"value2", key3:"thisWillGoAway"} }; @@ -1416,14 +1417,14 @@ describe('ParseQuery', () => { var testObject; return q.find().then((results) => { testObject = results[0]; - + expect(testObject.get("name")).toBe("Name"); expect(testObject.get("other")).toBe("other"); expect(testObject.has("tbd")).toBe(true); expect(testObject.get("subObject").key1).toBe("value"); expect(testObject.get("subObject").key2).toBe("value2"); expect(testObject.get("subObject").key3).toBe("thisWillGoAway"); - + var q2 = new ParseQuery("Thing"); q2.select("other", "tbd", "subObject.key1", "subObject.key3"); objectToReturn = { objectId: 'T01', other: 'other2', subObject:{key1:"updatedValue"}}; @@ -1442,7 +1443,7 @@ describe('ParseQuery', () => { expect(testObject.has("tbd")).toBe(false); expect(testObject.get("subObject").key1).toBe("updatedValue"); expect(testObject.get("subObject").key2).toBe("value2"); - expect(testObject.get("subObject").key3).toBeUndefined(); + expect(testObject.get("subObject").key3).toBeUndefined(); done(); }, (error) => { done.fail(error); @@ -1457,12 +1458,12 @@ describe('ParseQuery', () => { ParseQuery = require('../ParseQuery').default; ParseObject.enableSingleInstance(); - - var objectToReturn = { - objectId: 'T01', - name: 'Name', - other: 'other', - className:"Thing", + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + other: 'other', + className:"Thing", createdAt: '2017-01-10T10:00:00Z' }; @@ -1478,10 +1479,10 @@ describe('ParseQuery', () => { var testObject; q.first().then((result) => { testObject = result; - + expect(testObject.get("name")).toBe("Name"); expect(testObject.get("other")).toBe("other"); - + objectToReturn = { objectId: 'T01', name: 'Name2'}; var q2 = new ParseQuery("Thing"); return q2.first(); @@ -1503,13 +1504,13 @@ describe('ParseQuery', () => { ParseQuery = require('../ParseQuery').default; ParseObject.enableSingleInstance(); - - var objectToReturn = { - objectId: 'T01', - name: 'Name', - other: 'other', - tbd: 'exists', - className:"Thing", + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + other: 'other', + tbd: 'exists', + className:"Thing", subObject: {key1:"value", key2:"value2", key3:"thisWillGoAway"}, createdAt: '2017-01-10T10:00:00Z', }; @@ -1526,11 +1527,11 @@ describe('ParseQuery', () => { var testObject; return q.first().then((result) => { testObject = result; - + expect(testObject.get("name")).toBe("Name"); expect(testObject.get("other")).toBe("other"); expect(testObject.has("tbd")).toBe(true); - + var q2 = new ParseQuery("Thing"); q2.select("other", "tbd", "subObject.key1", "subObject.key3"); objectToReturn = { objectId: 'T01', other: 'other2', subObject:{key1:"updatedValue"}}; @@ -1546,10 +1547,10 @@ describe('ParseQuery', () => { }).then(() => { expect(testObject.get("name")).toBe("Name"); expect(testObject.get("other")).toBe("other2"); - expect(testObject.has("tbd")).toBe(false); + expect(testObject.has("tbd")).toBe(false); expect(testObject.get("subObject").key1).toBe("updatedValue"); expect(testObject.get("subObject").key2).toBe("value2"); - expect(testObject.get("subObject").key3).toBeUndefined(); + expect(testObject.get("subObject").key3).toBeUndefined(); done(); }, (error) => { done.fail(error); @@ -1582,6 +1583,165 @@ describe('ParseQuery', () => { size: 'medium' } }); + }); + it('can issue a distinct query', (done) => { + CoreManager.setQueryController({ + find() {}, + aggregate(className, params, options) { + expect(className).toBe('Item'); + expect(params).toEqual({ + distinct: 'size', + where: { + size: 'small' + } + }); + expect(options).toEqual({ useMasterKey: true }); + return ParsePromise.as({ + results: ['L'], + }); + } + }); + + var q = new ParseQuery('Item'); + q.equalTo('size', 'small').distinct('size').then((results) => { + expect(results[0]).toBe('L'); + done(); + }); + }); + + it('can pass options to a distinct query', (done) => { + CoreManager.setQueryController({ + find() {}, + aggregate(className, params, options) { + expect(className).toBe('Item'); + expect(params).toEqual({ + distinct: 'size', + where: { + size: 'small' + } + }); + expect(options).toEqual({ + useMasterKey: true, + sessionToken: '1234' + }); + return ParsePromise.as({ + results: ['L'] + }); + } + }); + + + var q = new ParseQuery('Item'); + q.equalTo('size', 'small').distinct('size', { + sessionToken: '1234' + }).then((results) => { + expect(results[0]).toBe('L'); + done(); + }); + }); + + it('can issue an aggregate query with array pipeline', (done) => { + const pipeline = [ + { group: { objectId: '$name' } } + ]; + CoreManager.setQueryController({ + find() {}, + aggregate(className, params, options) { + expect(className).toBe('Item'); + expect(params).toEqual({ + group: { objectId: '$name' } + }); + expect(options).toEqual({ useMasterKey: true }); + return ParsePromise.as({ + results: [], + }); + } + }); + + var q = new ParseQuery('Item'); + q.aggregate(pipeline).then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('can issue an aggregate query with object pipeline', (done) => { + const pipeline = { + group: { objectId: '$name' } + }; + CoreManager.setQueryController({ + find() {}, + aggregate(className, params, options) { + expect(className).toBe('Item'); + expect(params).toEqual({ + group: { objectId: '$name' } + }); + expect(options).toEqual({ useMasterKey: true }); + return ParsePromise.as({ + results: [], + }); + } + }); + + var q = new ParseQuery('Item'); + q.aggregate(pipeline).then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('cannot issue an aggregate query with invalid pipeline', (done) => { + const pipeline = 1234; + CoreManager.setQueryController({ + find() {}, + aggregate(className, params, options) { + expect(className).toBe('Item'); + expect(params).toEqual({ + group: { objectId: '$name' } + }); + expect(options).toEqual({ useMasterKey: true }); + return ParsePromise.as({ + results: [], + }); + } + }); + + try { + var q = new ParseQuery('Item'); + q.aggregate(pipeline).then(() => {}); + } catch (e) { + done(); + } + }); + + it('can pass options to an aggregate query', (done) => { + const pipeline = [ + { group: { objectId: '$name' } } + ]; + CoreManager.setQueryController({ + find() {}, + aggregate(className, params, options) { + expect(className).toBe('Item'); + expect(params).toEqual({ + group: { objectId: '$name' } + }); + expect(options).toEqual({ + useMasterKey: true, + sessionToken: '1234' + }); + return ParsePromise.as({ + results: [] + }); + } + }); + + var q = new ParseQuery('Item'); + q.aggregate(pipeline, { + sessionToken: '1234' + }).then((results) => { + expect(results).toEqual([]); + done(); + }); }); }); From 9b06a7ca0a71093663d9e24cf19361d4cd0ea940 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 29 Nov 2017 20:15:57 -0600 Subject: [PATCH 5/7] improve jest test --- src/CoreManager.js | 2 +- src/__tests__/ParseQuery-test.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/CoreManager.js b/src/CoreManager.js index c3f11289a..f7327179c 100644 --- a/src/CoreManager.js +++ b/src/CoreManager.js @@ -269,7 +269,7 @@ module.exports = { }, setQueryController(controller: QueryController) { - requireMethods('QueryController', ['find'], controller); + requireMethods('QueryController', ['find', 'aggregate'], controller); config['QueryController'] = controller; }, diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index 3b3774776..475dd272d 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -885,6 +885,7 @@ describe('ParseQuery', () => { it('can get the first object of a query', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -917,6 +918,7 @@ describe('ParseQuery', () => { it('can pass options to a first() query', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -947,6 +949,7 @@ describe('ParseQuery', () => { it('can get a single object by id', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -979,6 +982,7 @@ describe('ParseQuery', () => { it('will error when getting a nonexistent object', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1008,6 +1012,7 @@ describe('ParseQuery', () => { it('can pass options to a get() query', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1039,6 +1044,7 @@ describe('ParseQuery', () => { it('can issue a count query', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1065,6 +1071,7 @@ describe('ParseQuery', () => { it('can pass options to a count query', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1098,6 +1105,7 @@ describe('ParseQuery', () => { it('can issue a query to the controller', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1146,6 +1154,7 @@ describe('ParseQuery', () => { it('can pass options to find()', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1179,6 +1188,7 @@ describe('ParseQuery', () => { it('can iterate over results with each()', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1235,6 +1245,7 @@ describe('ParseQuery', () => { it('can pass options to each()', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { expect(className).toBe('Item'); expect(params).toEqual({ @@ -1303,6 +1314,7 @@ describe('ParseQuery', () => { it('does not override the className if it comes from the server', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [ @@ -1321,6 +1333,7 @@ describe('ParseQuery', () => { it('can override the className with a name from the server', (done) => { CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [ @@ -1358,6 +1371,7 @@ describe('ParseQuery', () => { }; CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [objectToReturn] @@ -1406,6 +1420,7 @@ describe('ParseQuery', () => { }; CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [objectToReturn] @@ -1468,6 +1483,7 @@ describe('ParseQuery', () => { }; CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [objectToReturn] @@ -1516,6 +1532,7 @@ describe('ParseQuery', () => { }; CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [objectToReturn] From 3419dd6ef5550cc6b36964f6dc665146bc5aa3ba Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 29 Nov 2017 20:31:27 -0600 Subject: [PATCH 6/7] fix CoreManager tests --- src/__tests__/CoreManager-test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/__tests__/CoreManager-test.js b/src/__tests__/CoreManager-test.js index 5429907e6..1a307637d 100644 --- a/src/__tests__/CoreManager-test.js +++ b/src/__tests__/CoreManager-test.js @@ -220,13 +220,15 @@ describe('CoreManager', () => { ); expect(CoreManager.setQueryController.bind(null, { - find: function() {} + find: function() {}, + aggregate: function() {} })).not.toThrow(); }); it('can set and get QueryController', () => { var controller = { - find: function() {} + find: function() {}, + aggregate: function() {} }; CoreManager.setQueryController(controller); From 7e506c91c8f140413ec8426f9ce971b59111b694 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 29 Nov 2017 20:58:24 -0600 Subject: [PATCH 7/7] rebase master --- src/__tests__/ParseQuery-test.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index ebf94fe21..78c01e5ab 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -1770,16 +1770,17 @@ describe('ParseQuery', () => { ParseQuery = require('../ParseQuery').default; ParseObject.enableSingleInstance(); - - var objectToReturn = { - objectId: 'T01', - name: 'Name', - tbd: 'exists', - className:"Thing", + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + tbd: 'exists', + className:"Thing", createdAt: '2017-01-10T10:00:00Z' }; CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [objectToReturn] @@ -1792,7 +1793,7 @@ describe('ParseQuery', () => { var testObject; return q.find().then((results) => { testObject = results[0]; - + expect(testObject.get("name")).toBe("Name"); expect(testObject.has("other")).toBe(false); expect(testObject.has("subObject")).toBe(false); @@ -1812,12 +1813,12 @@ describe('ParseQuery', () => { ParseQuery = require('../ParseQuery').default; ParseObject.enableSingleInstance(); - - var objectToReturn = { - objectId: 'T01', - name: 'Name', - tbd: 'exists', - className:"Thing", + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + tbd: 'exists', + className:"Thing", subObject1: {foo:"bar"}, subObject2: {foo:"bar"}, subObject3: {foo:"bar"}, @@ -1826,6 +1827,7 @@ describe('ParseQuery', () => { }; CoreManager.setQueryController({ + aggregate() {}, find(className, params, options) { return ParsePromise.as({ results: [objectToReturn] @@ -1837,7 +1839,7 @@ describe('ParseQuery', () => { var testObject; return q.find().then((results) => { testObject = results[0]; - + expect(testObject.has("subObject1")).toBe(true); expect(testObject.has("subObject2")).toBe(true); expect(testObject.has("subObject3")).toBe(true); @@ -1852,7 +1854,7 @@ describe('ParseQuery', () => { expect(testObject.has("subObject2")).toBe(false); //selected and not returned expect(testObject.has("subObject3")).toBe(true); //not selected, so should still be there expect(testObject.has("subObject4")).toBe(true); //selected and just added - expect(testObject.has("subObject5")).toBe(true); + expect(testObject.has("subObject5")).toBe(true); expect(testObject.get("subObject5").subSubObject).toBeDefined(); expect(testObject.get("subObject5").subSubObject.bar).toBeDefined(); //not selected but a sibiling was, so should still be there }).then(() => {