// This is a port of the test suite: // hungry/js/test/parse_query_test.js // // Some new tests are added. 'use strict'; const Parse = require('parse/node'); const rp = require('request-promise'); const masterKeyHeaders = { 'X-Parse-Application-Id': 'test', 'X-Parse-Rest-API-Key': 'test', 'X-Parse-Master-Key': 'test' } const masterKeyOptions = { headers: masterKeyHeaders, json: true } describe('Parse.Query testing', () => { it("basic query", function(done) { const baz = new TestObject({ foo: 'baz' }); const qux = new TestObject({ foo: 'qux' }); Parse.Object.saveAll([baz, qux], function() { const query = new Parse.Query(TestObject); query.equalTo('foo', 'baz'); query.find({ success: function(results) { equal(results.length, 1); equal(results[0].get('foo'), 'baz'); done(); } }); }); }); it("searching for null", function(done) { const baz = new TestObject({ foo: null }); const qux = new TestObject({ foo: 'qux' }); const qux2 = new TestObject({ }); Parse.Object.saveAll([baz, qux, qux2], function() { const query = new Parse.Query(TestObject); query.equalTo('foo', null); query.find({ success: function(results) { equal(results.length, 2); qux.set('foo', null); qux.save({ success: function () { query.find({ success: function (results) { equal(results.length, 3); done(); } }); } }); } }); }); }); it("searching for not null", function(done) { const baz = new TestObject({ foo: null }); const qux = new TestObject({ foo: 'qux' }); const qux2 = new TestObject({ }); Parse.Object.saveAll([baz, qux, qux2], function() { const query = new Parse.Query(TestObject); query.notEqualTo('foo', null); query.find({ success: function(results) { equal(results.length, 1); qux.set('foo', null); qux.save({ success: function () { query.find({ success: function (results) { equal(results.length, 0); done(); } }); }, error: function (error) { console.log(error); } }); }, error: function (error) { console.log(error); } }); }); }); it("notEqualTo with Relation is working", function(done) { const user = new Parse.User(); user.setPassword("asdf"); user.setUsername("zxcv"); const user1 = new Parse.User(); user1.setPassword("asdf"); user1.setUsername("qwerty"); const user2 = new Parse.User(); user2.setPassword("asdf"); user2.setUsername("asdf"); const Cake = Parse.Object.extend("Cake"); const cake1 = new Cake(); const cake2 = new Cake(); const cake3 = new Cake(); user.signUp().then(function(){ return user1.signUp(); }).then(function(){ return user2.signUp(); }).then(function(){ const relLike1 = cake1.relation("liker"); relLike1.add([user, user1]); const relDislike1 = cake1.relation("hater"); relDislike1.add(user2); return cake1.save(); }).then(function(){ const rellike2 = cake2.relation("liker"); rellike2.add([user, user1]); const relDislike2 = cake2.relation("hater"); relDislike2.add(user2); const relSomething = cake2.relation("something"); relSomething.add(user); return cake2.save(); }).then(function(){ const rellike3 = cake3.relation("liker"); rellike3.add(user); const relDislike3 = cake3.relation("hater"); relDislike3.add([user1, user2]); return cake3.save(); }).then(function(){ const query = new Parse.Query(Cake); // User2 likes nothing so we should receive 0 query.equalTo("liker", user2); return query.find().then(function(results){ equal(results.length, 0); }); }).then(function(){ const query = new Parse.Query(Cake); // User1 likes two of three cakes query.equalTo("liker", user1); return query.find().then(function(results){ // It should return 2 -> cake 1 and cake 2 equal(results.length, 2); }); }).then(function(){ const query = new Parse.Query(Cake); // We want to know which cake the user1 is not appreciating -> cake3 query.notEqualTo("liker", user1); return query.find().then(function(results){ // Should return 1 -> the cake 3 equal(results.length, 1); }); }).then(function(){ const query = new Parse.Query(Cake); // User2 is a hater of everything so we should receive 0 query.notEqualTo("hater", user2); return query.find().then(function(results){ equal(results.length, 0); }); }).then(function(){ const query = new Parse.Query(Cake); // Only cake3 is liked by user query.notContainedIn("liker", [user1]); return query.find().then(function(results){ equal(results.length, 1); }); }).then(function(){ const query = new Parse.Query(Cake); // All the users query.containedIn("liker", [user, user1, user2]); // Exclude user 1 query.notEqualTo("liker", user1); // Only cake3 is liked only by user1 return query.find().then(function(results){ equal(results.length, 1); const cake = results[0]; expect(cake.id).toBe(cake3.id); }); }).then(function(){ const query = new Parse.Query(Cake); // Exclude user1 query.notEqualTo("liker", user1); // Only cake1 query.equalTo("objectId", cake1.id) // user1 likes cake1 so this should return no results return query.find().then(function(results){ equal(results.length, 0); }); }).then(function(){ const query = new Parse.Query(Cake); query.notEqualTo("hater", user2); query.notEqualTo("liker", user2); // user2 doesn't like any cake so this should be 0 return query.find().then(function(results){ equal(results.length, 0); }); }).then(function(){ const query = new Parse.Query(Cake); query.equalTo("hater", user); query.equalTo("liker", user); // user doesn't hate any cake so this should be 0 return query.find().then(function(results){ equal(results.length, 0); }); }).then(function(){ const query = new Parse.Query(Cake); query.equalTo("hater", null); query.equalTo("liker", null); // user doesn't hate any cake so this should be 0 return query.find().then(function(results){ equal(results.length, 0); }); }).then(function(){ const query = new Parse.Query(Cake); query.equalTo("something", null); // user doesn't hate any cake so this should be 0 return query.find().then(function(results){ equal(results.length, 0); }); }).then(function(){ done(); }).catch((err) => { jfail(err); done(); }) }); it("query with limit", function(done) { const baz = new TestObject({ foo: 'baz' }); const qux = new TestObject({ foo: 'qux' }); Parse.Object.saveAll([baz, qux], function() { const query = new Parse.Query(TestObject); query.limit(1); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("query with limit equal to maxlimit", function(done) { const baz = new TestObject({ foo: 'baz' }); const qux = new TestObject({ foo: 'qux' }); reconfigureServer({ maxLimit: 1 }) Parse.Object.saveAll([baz, qux], function() { const query = new Parse.Query(TestObject); query.limit(1); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("query with limit exceeding maxlimit", function(done) { const baz = new TestObject({ foo: 'baz' }); const qux = new TestObject({ foo: 'qux' }); reconfigureServer({ maxLimit: 1 }) Parse.Object.saveAll([baz, qux], function() { const query = new Parse.Query(TestObject); query.limit(2); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("containedIn object array queries", function(done) { const messageList = []; for (let i = 0; i < 4; ++i) { const message = new TestObject({}); if (i > 0) { message.set('prior', messageList[i - 1]); } messageList.push(message); } Parse.Object.saveAll(messageList, function() { equal(messageList.length, 4); const inList = []; inList.push(messageList[0]); inList.push(messageList[2]); const query = new Parse.Query(TestObject); query.containedIn('prior', inList); query.find({ success: function(results) { equal(results.length, 2); done(); }, error: function(e) { jfail(e); done(); } }); }, (e) => { jfail(e); done(); }); }); it('containedIn null array', (done) => { const emails = ['contact@xyz.com', 'contact@zyx.com', null]; const user = new Parse.User(); user.setUsername(emails[0]); user.setPassword('asdf'); user.signUp().then(() => { const query = new Parse.Query(Parse.User); query.containedIn('username', emails); return query.find({ useMasterKey: true }); }).then((results) => { equal(results.length, 1); done(); }, done.fail); }); it('nested containedIn string', (done) => { const sender1 = { group: ['A', 'B'] }; const sender2 = { group: ['A', 'C'] }; const sender3 = { group: ['B', 'C'] }; const obj1 = new TestObject({ sender: sender1 }); const obj2 = new TestObject({ sender: sender2 }); const obj3 = new TestObject({ sender: sender3 }); Parse.Object.saveAll([obj1, obj2, obj3]).then(() => { const query = new Parse.Query(TestObject); query.containedIn('sender.group', ['A']); return query.find(); }).then((results) => { equal(results.length, 2); done(); }, done.fail); }); it('nested containedIn number', (done) => { const sender1 = { group: [1, 2] }; const sender2 = { group: [1, 3] }; const sender3 = { group: [2, 3] }; const obj1 = new TestObject({ sender: sender1 }); const obj2 = new TestObject({ sender: sender2 }); const obj3 = new TestObject({ sender: sender3 }); Parse.Object.saveAll([obj1, obj2, obj3]).then(() => { const query = new Parse.Query(TestObject); query.containedIn('sender.group', [1]); return query.find(); }).then((results) => { equal(results.length, 2); done(); }, done.fail); }); it("containsAll number array queries", function(done) { const NumberSet = Parse.Object.extend({ className: "NumberSet" }); const objectsList = []; objectsList.push(new NumberSet({ "numbers" : [1, 2, 3, 4, 5] })); objectsList.push(new NumberSet({ "numbers" : [1, 3, 4, 5] })); Parse.Object.saveAll(objectsList, function() { const query = new Parse.Query(NumberSet); query.containsAll("numbers", [1, 2, 3]); query.find({ success: function(results) { equal(results.length, 1); done(); }, error: function(err) { jfail(err); done(); }, }); }).catch((err) => { jfail(err); done(); }); }); it("containsAll string array queries", function(done) { const StringSet = Parse.Object.extend({ className: "StringSet" }); const objectsList = []; objectsList.push(new StringSet({ "strings" : ["a", "b", "c", "d", "e"] })); objectsList.push(new StringSet({ "strings" : ["a", "c", "d", "e"] })); Parse.Object.saveAll(objectsList, function() { const query = new Parse.Query(StringSet); query.containsAll("strings", ["a", "b", "c"]); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }).catch((err) => { jfail(err); done(); }); }); it("containsAll date array queries", function(done) { const DateSet = Parse.Object.extend({ className: "DateSet" }); function parseDate(iso8601) { const regexp = new RegExp( '^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})' + 'T' + '([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})' + '(.([0-9]+))?' + 'Z$'); const match = regexp.exec(iso8601); if (!match) { return null; } const year = match[1] || 0; const month = (match[2] || 1) - 1; const day = match[3] || 0; const hour = match[4] || 0; const minute = match[5] || 0; const second = match[6] || 0; const milli = match[8] || 0; return new Date(Date.UTC(year, month, day, hour, minute, second, milli)); } const makeDates = function(stringArray) { return stringArray.map(function(dateStr) { return parseDate(dateStr + "T00:00:00Z"); }); }; const objectsList = []; objectsList.push(new DateSet({ "dates" : makeDates(["2013-02-01", "2013-02-02", "2013-02-03", "2013-02-04"]) })); objectsList.push(new DateSet({ "dates" : makeDates(["2013-02-01", "2013-02-03", "2013-02-04"]) })); Parse.Object.saveAll(objectsList, function() { const query = new Parse.Query(DateSet); query.containsAll("dates", makeDates( ["2013-02-01", "2013-02-02", "2013-02-03"])); query.find({ success: function(results) { equal(results.length, 1); done(); }, error: function(e) { jfail(e); done(); }, }); }); }); it("containsAll object array queries", function(done) { const MessageSet = Parse.Object.extend({ className: "MessageSet" }); const messageList = []; for (let i = 0; i < 4; ++i) { messageList.push(new TestObject({ 'i' : i })); } Parse.Object.saveAll(messageList, function() { equal(messageList.length, 4); const messageSetList = []; messageSetList.push(new MessageSet({ 'messages' : messageList })); const someList = []; someList.push(messageList[0]); someList.push(messageList[1]); someList.push(messageList[3]); messageSetList.push(new MessageSet({ 'messages' : someList })); Parse.Object.saveAll(messageSetList, function() { const inList = []; inList.push(messageList[0]); inList.push(messageList[2]); const query = new Parse.Query(MessageSet); query.containsAll('messages', inList); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); }); it('containsAllStartingWith should match all strings that starts with string', (done) => { const object = new Parse.Object('Object'); object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); const object2 = new Parse.Object('Object'); object2.set('strings', ['the', 'brown', 'fox', 'jumps']); const object3 = new Parse.Object('Object'); object3.set('strings', ['over', 'the', 'lazy', 'dog']); const objectList = [object, object2, object3]; Parse.Object.saveAll(objectList).then((results) => { equal(objectList.length, results.length); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [ {$regex: '\^\\Qthe\\E'}, {$regex: '\^\\Qfox\\E'}, {$regex: '\^\\Qlazy\\E'} ] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }) .then(function (results) { equal(results.results.length, 1); arrayContains(results.results, object); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [ {$regex: '\^\\Qthe\\E'}, {$regex: '\^\\Qlazy\\E'} ] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }) .then(function (results) { equal(results.results.length, 2); arrayContains(results.results, object); arrayContains(results.results, object3); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [ {$regex: '\^\\Qhe\\E'}, {$regex: '\^\\Qlazy\\E'} ] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }) .then(function (results) { equal(results.results.length, 0); done(); }); }); }); it('containsAllStartingWith values must be all of type starting with regex', (done) => { const object = new Parse.Object('Object'); object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object.save().then(() => { equal(object.isNew(), false); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [ {$regex: '\^\\Qthe\\E'}, {$regex: '\^\\Qlazy\\E'}, {$regex: '\^\\Qfox\\E'}, {$unknown: /unknown/} ] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }) .then(function () { }, function () { done(); }); }); it('containsAllStartingWith empty array values should return empty results', (done) => { const object = new Parse.Object('Object'); object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object.save().then(() => { equal(object.isNew(), false); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }) .then(function (results) { equal(results.results.length, 0); done(); }, function () { }); }); it('containsAllStartingWith single empty value returns empty results', (done) => { const object = new Parse.Object('Object'); object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object.save().then(() => { equal(object.isNew(), false); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [ {} ] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }) .then(function (results) { equal(results.results.length, 0); done(); }, function () { }); }); it('containsAllStartingWith single regex value should return corresponding matching results', (done) => { const object = new Parse.Object('Object'); object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); const object2 = new Parse.Object('Object'); object2.set('strings', ['the', 'brown', 'fox', 'jumps']); const object3 = new Parse.Object('Object'); object3.set('strings', ['over', 'the', 'lazy', 'dog']); const objectList = [object, object2, object3]; Parse.Object.saveAll(objectList).then((results) => { equal(objectList.length, results.length); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [ {$regex: '\^\\Qlazy\\E'} ] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }) .then(function (results) { equal(results.results.length, 2); done(); }, function () { }); }); it('containsAllStartingWith single invalid regex returns empty results', (done) => { const object = new Parse.Object('Object'); object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object.save().then(() => { equal(object.isNew(), false); return require('request-promise').get({ url: Parse.serverURL + "/classes/Object", json: { where: { strings: { $all: [ {$unknown: '\^\\Qlazy\\E'} ] } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }) .then(function (results) { equal(results.results.length, 0); done(); }, function () { }); }); it('containedBy pointer array', (done) => { const objects = Array.from(Array(10).keys()).map((idx) => { const obj = new Parse.Object('Object'); obj.set('key', idx); return obj; }); const parent = new Parse.Object('Parent'); const parent2 = new Parse.Object('Parent'); const parent3 = new Parse.Object('Parent'); Parse.Object.saveAll(objects).then(() => { // [0, 1, 2] parent.set('objects', objects.slice(0, 3)); const shift = objects.shift(); // [2, 0] parent2.set('objects', [objects[1], shift]); // [1, 2, 3, 4] parent3.set('objects', objects.slice(1, 4)); return Parse.Object.saveAll([parent, parent2, parent3]); }).then(() => { // [1, 2, 3, 4, 5, 6, 7, 8, 9] const pointers = objects.map(object => object.toPointer()); // Return all Parent where all parent.objects are contained in objects return rp.get({ url: Parse.serverURL + "/classes/Parent", json: { where: { objects: { $containedBy: pointers } } }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }).then((results) => { expect(results.results[0].objectId).not.toBeUndefined(); expect(results.results[0].objectId).toBe(parent3.id); expect(results.results.length).toBe(1); done(); }); }); it('containedBy number array', (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { numbers: { $containedBy: [1, 2, 3, 4, 5, 6, 7, 8, 9] } }, } }); const obj1 = new TestObject({ numbers: [0, 1, 2] }); const obj2 = new TestObject({ numbers: [2, 0] }); const obj3 = new TestObject({ numbers: [1, 2, 3, 4] }); Parse.Object.saveAll([obj1, obj2, obj3]).then(() => { return rp.get(Parse.serverURL + "/classes/TestObject", options); }).then((results) => { expect(results.results[0].objectId).not.toBeUndefined(); expect(results.results[0].objectId).toBe(obj3.id); expect(results.results.length).toBe(1); done(); }); }); it('containedBy empty array', (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { numbers: { $containedBy: [] } }, } }); const obj1 = new TestObject({ numbers: [0, 1, 2] }); const obj2 = new TestObject({ numbers: [2, 0] }); const obj3 = new TestObject({ numbers: [1, 2, 3, 4] }); Parse.Object.saveAll([obj1, obj2, obj3]).then(() => { return rp.get(Parse.serverURL + "/classes/TestObject", options); }).then((results) => { expect(results.results.length).toBe(0); done(); }); }); it('containedBy invalid query', (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { objects: { $containedBy: 1234 } }, } }); const obj = new TestObject(); obj.save().then(() => { return rp.get(Parse.serverURL + "/classes/TestObject", options); }).then(done.fail).catch((error) => { equal(error.error.code, Parse.Error.INVALID_JSON); equal(error.error.error, 'bad $containedBy: should be an array'); done(); }); }); const BoxedNumber = Parse.Object.extend({ className: "BoxedNumber" }); it("equalTo queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.equalTo('number', 3); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("equalTo undefined", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.equalTo('number', undefined); query.find(expectSuccess({ success: function(results) { equal(results.length, 0); done(); } })); }); }); it("lessThan queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.lessThan('number', 7); query.find({ success: function(results) { equal(results.length, 7); done(); } }); }); }); it("lessThanOrEqualTo queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.lessThanOrEqualTo('number', 7); query.find({ success: function(results) { equal(results.length, 8); done(); } }); }); }); it("lessThan zero queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.lessThan('number', 0); return query.find(); }).then((results) => { equal(results.length, 3); done(); }); }); it("lessThanOrEqualTo zero queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.lessThanOrEqualTo('number', 0); return query.find(); }).then((results) => { equal(results.length, 4); done(); }); }); it("greaterThan queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.greaterThan('number', 7); query.find({ success: function(results) { equal(results.length, 2); done(); } }); }); }); it("greaterThanOrEqualTo queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.greaterThanOrEqualTo('number', 7); query.find({ success: function(results) { equal(results.length, 3); done(); } }); }); }); it("greaterThan zero queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.greaterThan('number', 0); return query.find(); }).then((results) => { equal(results.length, 1); done(); }); }); it("greaterThanOrEqualTo zero queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.greaterThanOrEqualTo('number', 0); return query.find(); }).then((results) => { equal(results.length, 2); done(); }); }); it("lessThanOrEqualTo greaterThanOrEqualTo queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.lessThanOrEqualTo('number', 7); query.greaterThanOrEqualTo('number', 7); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("lessThan greaterThan queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.lessThan('number', 9); query.greaterThan('number', 3); query.find({ success: function(results) { equal(results.length, 5); done(); } }); }); }); it("notEqualTo queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.notEqualTo('number', 5); query.find({ success: function(results) { equal(results.length, 9); done(); } }); }); }); it("notEqualTo zero queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.notEqualTo('number', 0); return query.find(); }).then((results) => { equal(results.length, 4); done(); }); }); it("equalTo zero queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.equalTo('number', 0); return query.find(); }).then((results) => { equal(results.length, 1); done(); }); }); it("number equalTo boolean queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.equalTo('number', false); return query.find(); }).then((results) => { equal(results.length, 0); done(); }); }); it("equalTo false queries", (done) => { const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: true }); Parse.Object.saveAll([obj1, obj2]).then(() => { const query = new Parse.Query(TestObject); query.equalTo('field', false); return query.find(); }).then((results) => { equal(results.length, 1); done(); }); }); it("where $eq false queries (rest)", (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { field: { $eq: false } }, } }); const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: true }); Parse.Object.saveAll([obj1, obj2]).then(() => { rp.get(Parse.serverURL + '/classes/TestObject', options) .then((resp) => { equal(resp.results.length, 1); done(); }); }) }); it("where $eq null queries (rest)", (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { field: { $eq: null } }, } }); const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: null }); Parse.Object.saveAll([obj1, obj2]).then(() => { rp.get(Parse.serverURL + '/classes/TestObject', options) .then((resp) => { equal(resp.results.length, 1); done(); }); }) }); it("containedIn queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.containedIn('number', [3,5,7,9,11]); query.find({ success: function(results) { equal(results.length, 4); done(); } }); }); }); it("containedIn false queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.containedIn('number', false); return query.find(); }).then(done.fail).catch((error) => { equal(error.code, Parse.Error.INVALID_JSON); equal(error.message, 'bad $in value'); done(); }); }); it("notContainedIn false queries", (done) => { const makeBoxedNumber = (i) => { return new BoxedNumber({ number: i }); }; const numbers = [-3, -2, -1, 0, 1]; const boxedNumbers = numbers.map(makeBoxedNumber); Parse.Object.saveAll(boxedNumbers).then(() => { const query = new Parse.Query(BoxedNumber); query.notContainedIn('number', false); return query.find(); }).then(done.fail).catch((error) => { equal(error.code, Parse.Error.INVALID_JSON); equal(error.message, 'bad $nin value'); done(); }); }); it("notContainedIn queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.notContainedIn('number', [3,5,7,9,11]); query.find({ success: function(results) { equal(results.length, 6); done(); } }); }); }); it("objectId containedIn queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function(list) { const query = new Parse.Query(BoxedNumber); query.containedIn('objectId', [list[2].id, list[3].id, list[0].id, "NONSENSE"]); query.ascending('number'); query.find({ success: function(results) { if (results.length != 3) { fail('expected 3 results'); } else { equal(results[0].get('number'), 0); equal(results[1].get('number'), 2); equal(results[2].get('number'), 3); } done(); } }); }); }); it("objectId equalTo queries", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function(list) { const query = new Parse.Query(BoxedNumber); query.equalTo('objectId', list[4].id); query.find({ success: function(results) { if (results.length != 1) { fail('expected 1 result') done(); } else { equal(results[0].get('number'), 4); } done(); } }); }); }); it("find no elements", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.equalTo('number', 17); query.find(expectSuccess({ success: function(results) { equal(results.length, 0); done(); } })); }); }); it("find with error", function(done) { const query = new Parse.Query(BoxedNumber); query.equalTo('$foo', 'bar'); query.find(expectError(Parse.Error.INVALID_KEY_NAME, done)); }); it("get", function(done) { Parse.Object.saveAll([new TestObject({foo: 'bar'})], function(items) { ok(items[0]); const objectId = items[0].id; const query = new Parse.Query(TestObject); query.get(objectId, { success: function(result) { ok(result); equal(result.id, objectId); equal(result.get('foo'), 'bar'); ok(result.createdAt instanceof Date); ok(result.updatedAt instanceof Date); done(); } }); }); }); it("get undefined", function(done) { Parse.Object.saveAll([new TestObject({foo: 'bar'})], function(items) { ok(items[0]); const query = new Parse.Query(TestObject); query.get(undefined, { success: fail, error: done, }); }); }); it("get error", function(done) { Parse.Object.saveAll([new TestObject({foo: 'bar'})], function(items) { ok(items[0]); const query = new Parse.Query(TestObject); query.get("InvalidObjectID", { success: function() { ok(false, "The get should have failed."); done(); }, error: function(object, error) { equal(error.code, Parse.Error.OBJECT_NOT_FOUND); done(); } }); }); }); it("first", function(done) { Parse.Object.saveAll([new TestObject({foo: 'bar'})], function() { const query = new Parse.Query(TestObject); query.equalTo('foo', 'bar'); query.first({ success: function(result) { equal(result.get('foo'), 'bar'); done(); } }); }); }); it("first no result", function(done) { Parse.Object.saveAll([new TestObject({foo: 'bar'})], function() { const query = new Parse.Query(TestObject); query.equalTo('foo', 'baz'); query.first({ success: function(result) { equal(result, undefined); done(); } }); }); }); it("first with two results", function(done) { Parse.Object.saveAll([new TestObject({foo: 'bar'}), new TestObject({foo: 'bar'})], function() { const query = new Parse.Query(TestObject); query.equalTo('foo', 'bar'); query.first({ success: function(result) { equal(result.get('foo'), 'bar'); done(); } }); }); }); it("first with error", function(done) { const query = new Parse.Query(BoxedNumber); query.equalTo('$foo', 'bar'); query.first(expectError(Parse.Error.INVALID_KEY_NAME, done)); }); const Container = Parse.Object.extend({ className: "Container" }); it("notEqualTo object", function(done) { const item1 = new TestObject(); const item2 = new TestObject(); const container1 = new Container({item: item1}); const container2 = new Container({item: item2}); Parse.Object.saveAll([item1, item2, container1, container2], function() { const query = new Parse.Query(Container); query.notEqualTo('item', item1); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("skip", function(done) { Parse.Object.saveAll([new TestObject(), new TestObject()], function() { const query = new Parse.Query(TestObject); query.skip(1); query.find({ success: function(results) { equal(results.length, 1); query.skip(3); query.find({ success: function(results) { equal(results.length, 0); done(); } }); } }); }); }); it("skip doesn't affect count", function(done) { Parse.Object.saveAll([new TestObject(), new TestObject()], function() { const query = new Parse.Query(TestObject); query.count({ success: function(count) { equal(count, 2); query.skip(1); query.count({ success: function(count) { equal(count, 2); query.skip(3); query.count({ success: function(count) { equal(count, 2); done(); } }); } }); } }); }); }); it("count", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.greaterThan("number", 1); query.count({ success: function(count) { equal(count, 8); done(); } }); }); }); it("order by ascending number", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.ascending("number"); query.find(expectSuccess({ success: function(results) { equal(results.length, 3); equal(results[0].get("number"), 1); equal(results[1].get("number"), 2); equal(results[2].get("number"), 3); done(); } })); }); }); it("order by descending number", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber)).then(function() { const query = new Parse.Query(BoxedNumber); query.descending("number"); query.find(expectSuccess({ success: function(results) { equal(results.length, 3); equal(results[0].get("number"), 3); equal(results[1].get("number"), 2); equal(results[2].get("number"), 1); done(); } })); }); }); it('can order on an object string field', function (done) { const testSet = [ { sortField: { value: "Z" } }, { sortField: { value: "A" } }, { sortField: { value: "M" } }, ]; const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) .then((result) => { expect(result.get('sortField').value).toBe("Z"); return new Parse.Query('Test').addAscending('sortField.value').first() }) .then((result) => { expect(result.get('sortField').value).toBe("A"); done(); }) .catch(done.fail); }); it('can order on an object string field (level 2)', function (done) { const testSet = [ { sortField: { value: { field: "Z" } } }, { sortField: { value: { field: "A" } } }, { sortField: { value: { field: "M" } } }, ]; const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) .then((result) => { expect(result.get('sortField').value.field).toBe("Z"); return new Parse.Query('Test').addAscending('sortField.value.field').first() }) .then((result) => { expect(result.get('sortField').value.field).toBe("A"); done(); }) .catch(done.fail); }); it('can order on an object number field', function (done) { const testSet = [ { sortField: { value: 10 } }, { sortField: { value: 1 } }, { sortField: { value: 5 } }, ]; const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) .then((result) => { expect(result.get('sortField').value).toBe(10); return new Parse.Query('Test').addAscending('sortField.value').first() }) .then((result) => { expect(result.get('sortField').value).toBe(1); done(); }) .catch(done.fail); }); it('can order on an object number field (level 2)', function (done) { const testSet = [ { sortField: { value: { field: 10 } } }, { sortField: { value: { field: 1 } } }, { sortField: { value: { field: 5 } } }, ]; const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) .then((result) => { expect(result.get('sortField').value.field).toBe(10); return new Parse.Query('Test').addAscending('sortField.value.field').first() }) .then((result) => { expect(result.get('sortField').value.field).toBe(1); done(); }) .catch(done.fail); }); it("order by ascending number then descending string", function(done) { const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function(num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll( [3, 1, 3, 2].map(makeBoxedNumber)).then( function() { const query = new Parse.Query(BoxedNumber); query.ascending("number").addDescending("string"); query.find(expectSuccess({ success: function(results) { equal(results.length, 4); equal(results[0].get("number"), 1); equal(results[0].get("string"), "b"); equal(results[1].get("number"), 2); equal(results[1].get("string"), "d"); equal(results[2].get("number"), 3); equal(results[2].get("string"), "c"); equal(results[3].get("number"), 3); equal(results[3].get("string"), "a"); done(); } })); }); }); it("order by descending number then ascending string", function(done) { const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function(num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; const objects = [3, 1, 3, 2].map(makeBoxedNumber); Parse.Object.saveAll(objects) .then(() => { const query = new Parse.Query(BoxedNumber); query.descending("number").addAscending("string"); return query.find(); }).then((results) => { equal(results.length, 4); equal(results[0].get("number"), 3); equal(results[0].get("string"), "a"); equal(results[1].get("number"), 3); equal(results[1].get("string"), "c"); equal(results[2].get("number"), 2); equal(results[2].get("string"), "d"); equal(results[3].get("number"), 1); equal(results[3].get("string"), "b"); done(); }, (err) => { jfail(err); done(); }); }); it("order by descending number and string", function(done) { const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function(num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then( function() { const query = new Parse.Query(BoxedNumber); query.descending("number,string"); query.find(expectSuccess({ success: function(results) { equal(results.length, 4); equal(results[0].get("number"), 3); equal(results[0].get("string"), "c"); equal(results[1].get("number"), 3); equal(results[1].get("string"), "a"); equal(results[2].get("number"), 2); equal(results[2].get("string"), "d"); equal(results[3].get("number"), 1); equal(results[3].get("string"), "b"); done(); } })); }); }); it("order by descending number and string, with space", function(done) { const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({number: num, string: strings[i]}); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then( function () { const query = new Parse.Query(BoxedNumber); query.descending("number, string"); query.find(expectSuccess({ success: function (results) { equal(results.length, 4); equal(results[0].get("number"), 3); equal(results[0].get("string"), "c"); equal(results[1].get("number"), 3); equal(results[1].get("string"), "a"); equal(results[2].get("number"), 2); equal(results[2].get("string"), "d"); equal(results[3].get("number"), 1); equal(results[3].get("string"), "b"); done(); } })); }, (err) => { jfail(err); done(); }); }); it("order by descending number and string, with array arg", function(done) { const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function(num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then( function() { const query = new Parse.Query(BoxedNumber); query.descending(["number", "string"]); query.find(expectSuccess({ success: function(results) { equal(results.length, 4); equal(results[0].get("number"), 3); equal(results[0].get("string"), "c"); equal(results[1].get("number"), 3); equal(results[1].get("string"), "a"); equal(results[2].get("number"), 2); equal(results[2].get("string"), "d"); equal(results[3].get("number"), 1); equal(results[3].get("string"), "b"); done(); } })); }); }); it("order by descending number and string, with multiple args", function(done) { const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function(num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then( function() { const query = new Parse.Query(BoxedNumber); query.descending("number", "string"); query.find(expectSuccess({ success: function(results) { equal(results.length, 4); equal(results[0].get("number"), 3); equal(results[0].get("string"), "c"); equal(results[1].get("number"), 3); equal(results[1].get("string"), "a"); equal(results[2].get("number"), 2); equal(results[2].get("string"), "d"); equal(results[3].get("number"), 1); equal(results[3].get("string"), "b"); done(); } })); }); }); it("can't order by password", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber), function() { const query = new Parse.Query(BoxedNumber); query.ascending("_password"); query.find(expectError(Parse.Error.INVALID_KEY_NAME, done)); }); }); it("order by _created_at", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; const numbers = [3, 1, 2].map(makeBoxedNumber); numbers[0].save().then(() => { return numbers[1].save(); }).then(() => { return numbers[2].save(); }).then(function() { const query = new Parse.Query(BoxedNumber); query.ascending("_created_at"); query.find({ success: function(results) { equal(results.length, 3); equal(results[0].get("number"), 3); equal(results[1].get("number"), 1); equal(results[2].get("number"), 2); done(); }, error: function(e) { jfail(e); done(); }, }); }); }); it("order by createdAt", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; const numbers = [3, 1, 2].map(makeBoxedNumber); numbers[0].save().then(() => { return numbers[1].save(); }).then(() => { return numbers[2].save(); }).then(function() { const query = new Parse.Query(BoxedNumber); query.descending("createdAt"); query.find({ success: function(results) { equal(results.length, 3); equal(results[0].get("number"), 2); equal(results[1].get("number"), 1); equal(results[2].get("number"), 3); done(); } }); }); }); it("order by _updated_at", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; const numbers = [3, 1, 2].map(makeBoxedNumber); numbers[0].save().then(() => { return numbers[1].save(); }).then(() => { return numbers[2].save(); }).then(function() { numbers[1].set("number", 4); numbers[1].save(null, { success: function() { const query = new Parse.Query(BoxedNumber); query.ascending("_updated_at"); query.find({ success: function(results) { equal(results.length, 3); equal(results[0].get("number"), 3); equal(results[1].get("number"), 2); equal(results[2].get("number"), 4); done(); } }); } }); }); }); it("order by updatedAt", function(done) { const makeBoxedNumber = function(i) { return new BoxedNumber({ number: i }); }; const numbers = [3, 1, 2].map(makeBoxedNumber); numbers[0].save().then(() => { return numbers[1].save(); }).then(() => { return numbers[2].save(); }).then(function() { numbers[1].set("number", 4); numbers[1].save(null, { success: function() { const query = new Parse.Query(BoxedNumber); query.descending("_updated_at"); query.find({ success: function(results) { equal(results.length, 3); equal(results[0].get("number"), 4); equal(results[1].get("number"), 2); equal(results[2].get("number"), 3); done(); } }); } }); }); }); // Returns a promise function makeTimeObject(start, i) { const time = new Date(); time.setSeconds(start.getSeconds() + i); const item = new TestObject({name: "item" + i, time: time}); return item.save(); } // Returns a promise for all the time objects function makeThreeTimeObjects() { const start = new Date(); let one, two, three; return makeTimeObject(start, 1).then((o1) => { one = o1; return makeTimeObject(start, 2); }).then((o2) => { two = o2; return makeTimeObject(start, 3); }).then((o3) => { three = o3; return [one, two, three]; }); } it("time equality", function(done) { makeThreeTimeObjects().then(function(list) { const query = new Parse.Query(TestObject); query.equalTo("time", list[1].get("time")); query.find({ success: function(results) { equal(results.length, 1); equal(results[0].get("name"), "item2"); done(); } }); }); }); it("time lessThan", function(done) { makeThreeTimeObjects().then(function(list) { const query = new Parse.Query(TestObject); query.lessThan("time", list[2].get("time")); query.find({ success: function(results) { equal(results.length, 2); done(); } }); }); }); // This test requires Date objects to be consistently stored as a Date. it("time createdAt", function(done) { makeThreeTimeObjects().then(function(list) { const query = new Parse.Query(TestObject); query.greaterThanOrEqualTo("createdAt", list[0].createdAt); query.find({ success: function(results) { equal(results.length, 3); done(); } }); }); }); it("matches string", function(done) { const thing1 = new TestObject(); thing1.set("myString", "football"); const thing2 = new TestObject(); thing2.set("myString", "soccer"); Parse.Object.saveAll([thing1, thing2], function() { const query = new Parse.Query(TestObject); query.matches("myString", "^fo*\\wb[^o]l+$"); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("matches regex", function(done) { const thing1 = new TestObject(); thing1.set("myString", "football"); const thing2 = new TestObject(); thing2.set("myString", "soccer"); Parse.Object.saveAll([thing1, thing2], function() { const query = new Parse.Query(TestObject); query.matches("myString", /^fo*\wb[^o]l+$/); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); it("case insensitive regex success", function(done) { const thing = new TestObject(); thing.set("myString", "football"); Parse.Object.saveAll([thing], function() { const query = new Parse.Query(TestObject); query.matches("myString", "FootBall", "i"); query.find({ success: function() { done(); } }); }); }); it("regexes with invalid options fail", function(done) { const query = new Parse.Query(TestObject); query.matches("myString", "FootBall", "some invalid option"); query.find(expectError(Parse.Error.INVALID_QUERY, done)); }); it("Use a regex that requires all modifiers", function(done) { const thing = new TestObject(); thing.set("myString", "PArSe\nCom"); Parse.Object.saveAll([thing], function() { const query = new Parse.Query(TestObject); query.matches( "myString", "parse # First fragment. We'll write this in one case but match " + "insensitively\n.com # Second fragment. This can be separated by any " + "character, including newline", "mixs"); query.find({ success: function(results) { equal(results.length, 1); done(); }, error: function(err) { jfail(err); done(); } }); }); }); it("Regular expression constructor includes modifiers inline", function(done) { const thing = new TestObject(); thing.set("myString", "\n\nbuffer\n\nparse.COM"); Parse.Object.saveAll([thing], function() { const query = new Parse.Query(TestObject); query.matches("myString", /parse\.com/mi); query.find({ success: function(results) { equal(results.length, 1); done(); } }); }); }); const someAscii = "\\E' !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU" + "VWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'"; it("contains", function(done) { Parse.Object.saveAll([new TestObject({myString: "zax" + someAscii + "qub"}), new TestObject({myString: "start" + someAscii}), new TestObject({myString: someAscii + "end"}), new TestObject({myString: someAscii})], function() { const query = new Parse.Query(TestObject); query.contains("myString", someAscii); query.find({ success: function(results) { equal(results.length, 4); done(); } }); }); }); it('nested contains', (done) => { const sender1 = { group: ['A', 'B'] }; const sender2 = { group: ['A', 'C'] }; const sender3 = { group: ['B', 'C'] }; const obj1 = new TestObject({ sender: sender1 }); const obj2 = new TestObject({ sender: sender2 }); const obj3 = new TestObject({ sender: sender3 }); Parse.Object.saveAll([obj1, obj2, obj3]).then(() => { const query = new Parse.Query(TestObject); query.contains('sender.group', 'A'); return query.find(); }).then((results) => { equal(results.length, 2); done(); }, done.fail); }); it("startsWith", function(done) { Parse.Object.saveAll([new TestObject({myString: "zax" + someAscii + "qub"}), new TestObject({myString: "start" + someAscii}), new TestObject({myString: someAscii + "end"}), new TestObject({myString: someAscii})], function() { const query = new Parse.Query(TestObject); query.startsWith("myString", someAscii); query.find({ success: function(results) { equal(results.length, 2); done(); } }); }); }); it("endsWith", function(done) { Parse.Object.saveAll([new TestObject({myString: "zax" + someAscii + "qub"}), new TestObject({myString: "start" + someAscii}), new TestObject({myString: someAscii + "end"}), new TestObject({myString: someAscii})], function() { const query = new Parse.Query(TestObject); query.endsWith("myString", someAscii); query.find({ success: function(results) { equal(results.length, 2); done(); } }); }); }); it("exists", function(done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const item = new TestObject(); if (i % 2 === 0) { item.set('x', i + 1); } else { item.set('y', i + 1); } objects.push(item); } Parse.Object.saveAll(objects, function() { const query = new Parse.Query(TestObject); query.exists("x"); query.find({ success: function(results) { equal(results.length, 5); for (const result of results) { ok(result.get("x")); } done(); } }); }); }); it("doesNotExist", function(done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const item = new TestObject(); if (i % 2 === 0) { item.set('x', i + 1); } else { item.set('y', i + 1); } objects.push(item); } Parse.Object.saveAll(objects, function() { const query = new Parse.Query(TestObject); query.doesNotExist("x"); query.find({ success: function(results) { equal(results.length, 4); for (const result of results) { ok(result.get("y")); } done(); } }); }); }); it("exists relation", function(done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const container = new Container(); if (i % 2 === 0) { const item = new TestObject(); item.set('x', i); container.set('x', item); objects.push(item); } else { container.set('y', i); } objects.push(container); } Parse.Object.saveAll(objects).then(function() { const query = new Parse.Query(Container); query.exists("x"); query.find({ success: function(results) { equal(results.length, 5); for (const result of results) { ok(result.get("x")); } done(); } }); }); }); it("doesNotExist relation", function(done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7]) { const container = new Container(); if (i % 2 === 0) { const item = new TestObject(); item.set('x', i); container.set('x', item); objects.push(item); } else { container.set('y', i); } objects.push(container); } Parse.Object.saveAll(objects, function() { const query = new Parse.Query(Container); query.doesNotExist("x"); query.find({ success: function(results) { equal(results.length, 4); for (const result of results) { ok(result.get("y")); } done(); } }); }); }); it("don't include by default", function(done) { const child = new TestObject(); const parent = new Container(); child.set("foo", "bar"); parent.set("child", child); Parse.Object.saveAll([child, parent], function() { child._clearServerData(); const query = new Parse.Query(Container); query.find({ success: function(results) { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; const childAgain = parentAgain.get("child"); ok(childAgain); equal(childAgain.get("foo"), undefined); Parse.serverURL = goodURL; done(); } }); }); }); it("include relation", function(done) { const child = new TestObject(); const parent = new Container(); child.set("foo", "bar"); parent.set("child", child); Parse.Object.saveAll([child, parent], function() { const query = new Parse.Query(Container); query.include("child"); query.find({ success: function(results) { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; const childAgain = parentAgain.get("child"); ok(childAgain); equal(childAgain.get("foo"), "bar"); Parse.serverURL = goodURL; done(); } }); }); }); it("include relation array", function(done) { const child = new TestObject(); const parent = new Container(); child.set("foo", "bar"); parent.set("child", child); Parse.Object.saveAll([child, parent], function() { const query = new Parse.Query(Container); query.include(["child"]); query.find({ success: function(results) { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; const childAgain = parentAgain.get("child"); ok(childAgain); equal(childAgain.get("foo"), "bar"); Parse.serverURL = goodURL; done(); } }); }); }); it("nested include", function(done) { const Child = Parse.Object.extend("Child"); const Parent = Parse.Object.extend("Parent"); const Grandparent = Parse.Object.extend("Grandparent"); const objects = []; for (let i = 0; i < 5; ++i) { const grandparent = new Grandparent({ z:i, parent: new Parent({ y:i, child: new Child({ x:i }) }) }); objects.push(grandparent); } Parse.Object.saveAll(objects, function() { const query = new Parse.Query(Grandparent); query.include(["parent.child"]); query.find({ success: function(results) { equal(results.length, 5); for (const object of results) { equal(object.get("z"), object.get("parent").get("y")); equal(object.get("z"), object.get("parent").get("child").get("x")); } done(); } }); }); }); it("include doesn't make dirty wrong", function(done) { const Parent = Parse.Object.extend("ParentObject"); const Child = Parse.Object.extend("ChildObject"); const parent = new Parent(); const child = new Child(); child.set("foo", "bar"); parent.set("child", child); Parse.Object.saveAll([child, parent], function() { const query = new Parse.Query(Parent); query.include("child"); query.find({ success: function(results) { equal(results.length, 1); const parentAgain = results[0]; const childAgain = parentAgain.get("child"); equal(childAgain.id, child.id); equal(parentAgain.id, parent.id); equal(childAgain.get("foo"), "bar"); equal(false, parentAgain.dirty()); equal(false, childAgain.dirty()); done(); } }); }); }); it('properly includes array', (done) => { const objects = []; let total = 0; while(objects.length != 5) { const object = new Parse.Object('AnObject'); object.set('key', objects.length); total += objects.length; objects.push(object); } Parse.Object.saveAll(objects).then(() => { const object = new Parse.Object("AContainer"); object.set('objects', objects); return object.save(); }).then(() => { const query = new Parse.Query('AContainer'); query.include('objects'); return query.find() }).then((results) => { expect(results.length).toBe(1); const res = results[0]; const objects = res.get('objects'); expect(objects.length).toBe(5); objects.forEach((object) => { total -= object.get('key'); }); expect(total).toBe(0); done() }, () => { fail('should not fail'); done(); }) }); it('properly includes array of mixed objects', (done) => { const objects = []; let total = 0; while(objects.length != 5) { const object = new Parse.Object('AnObject'); object.set('key', objects.length); total += objects.length; objects.push(object); } while(objects.length != 10) { const object = new Parse.Object('AnotherObject'); object.set('key', objects.length); total += objects.length; objects.push(object); } Parse.Object.saveAll(objects).then(() => { const object = new Parse.Object("AContainer"); object.set('objects', objects); return object.save(); }).then(() => { const query = new Parse.Query('AContainer'); query.include('objects'); return query.find() }).then((results) => { expect(results.length).toBe(1); const res = results[0]; const objects = res.get('objects'); expect(objects.length).toBe(10); objects.forEach((object) => { total -= object.get('key'); }); expect(total).toBe(0); done() }, (e) => { fail('should not fail'); fail(JSON.stringify(e)); done(); }) }); it('properly nested array of mixed objects with bad ids', (done) => { const objects = []; let total = 0; while(objects.length != 5) { const object = new Parse.Object('AnObject'); object.set('key', objects.length); objects.push(object); } while(objects.length != 10) { const object = new Parse.Object('AnotherObject'); object.set('key', objects.length); objects.push(object); } Parse.Object.saveAll(objects).then(() => { const object = new Parse.Object("AContainer"); for (let i = 0; i < objects.length; i++) { if (i % 2 == 0) { objects[i].id = 'randomThing' } else { total += objects[i].get('key'); } } object.set('objects', objects); return object.save(); }).then(() => { const query = new Parse.Query('AContainer'); query.include('objects'); return query.find() }).then((results) => { expect(results.length).toBe(1); const res = results[0]; const objects = res.get('objects'); expect(objects.length).toBe(5); objects.forEach((object) => { total -= object.get('key'); }); expect(total).toBe(0); done() }, (err) => { jfail(err); fail('should not fail'); done(); }) }); it('properly fetches nested pointers', (done) => { const color = new Parse.Object('Color'); color.set('hex','#133733'); const circle = new Parse.Object('Circle'); circle.set('radius', 1337); Parse.Object.saveAll([color, circle]).then(() => { circle.set('color', color); const badCircle = new Parse.Object('Circle'); badCircle.id = 'badId'; const complexFigure = new Parse.Object('ComplexFigure'); complexFigure.set('consistsOf', [circle, badCircle]); return complexFigure.save(); }).then(() => { const q = new Parse.Query('ComplexFigure'); q.include('consistsOf.color'); return q.find() }).then((results) => { expect(results.length).toBe(1); const figure = results[0]; expect(figure.get('consistsOf').length).toBe(1); expect(figure.get('consistsOf')[0].get('color').get('hex')).toBe('#133733'); done(); }, () => { fail('should not fail'); done(); }) }); it("result object creation uses current extension", function(done) { const ParentObject = Parse.Object.extend({ className: "ParentObject" }); // Add a foo() method to ChildObject. let ChildObject = Parse.Object.extend("ChildObject", { foo: function() { return "foo"; } }); const parent = new ParentObject(); const child = new ChildObject(); parent.set("child", child); Parse.Object.saveAll([child, parent], function() { // Add a bar() method to ChildObject. ChildObject = Parse.Object.extend("ChildObject", { bar: function() { return "bar"; } }); const query = new Parse.Query(ParentObject); query.include("child"); query.find({ success: function(results) { equal(results.length, 1); const parentAgain = results[0]; const childAgain = parentAgain.get("child"); equal(childAgain.foo(), "foo"); equal(childAgain.bar(), "bar"); done(); } }); }); }); it("matches query", function(done) { const ParentObject = Parse.Object.extend("ParentObject"); const ChildObject = Parse.Object.extend("ChildObject"); const objects = []; for (let i = 0; i < 10; ++i) { objects.push( new ParentObject({ child: new ChildObject({x: i}), x: 10 + i })); } Parse.Object.saveAll(objects, function() { const subQuery = new Parse.Query(ChildObject); subQuery.greaterThan("x", 5); const query = new Parse.Query(ParentObject); query.matchesQuery("child", subQuery); query.find({ success: function(results) { equal(results.length, 4); for (const object of results) { ok(object.get("x") > 15); } const query = new Parse.Query(ParentObject); query.doesNotMatchQuery("child", subQuery); query.find({ success: function (results) { equal(results.length, 6); for (const object of results) { ok(object.get("x") >= 10); ok(object.get("x") <= 15); done(); } } }); } }); }); }); it("select query", function(done) { const RestaurantObject = Parse.Object.extend("Restaurant"); const PersonObject = Parse.Object.extend("Person"); const objects = [ new RestaurantObject({ ratings: 5, location: "Djibouti" }), new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), new PersonObject({ name: "Bob", hometown: "Djibouti" }), new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), new PersonObject({ name: "Billy", hometown: "Detroit" }) ]; Parse.Object.saveAll(objects, function() { const query = new Parse.Query(RestaurantObject); query.greaterThan("ratings", 4); const mainQuery = new Parse.Query(PersonObject); mainQuery.matchesKeyInQuery("hometown", "location", query); mainQuery.find(expectSuccess({ success: function(results) { equal(results.length, 1); equal(results[0].get('name'), 'Bob'); done(); } })); }); }); it('$select inside $or', (done) => { const Restaurant = Parse.Object.extend('Restaurant'); const Person = Parse.Object.extend('Person'); const objects = [ new Restaurant({ ratings: 5, location: "Djibouti" }), new Restaurant({ ratings: 3, location: "Ouagadougou" }), new Person({ name: "Bob", hometown: "Djibouti" }), new Person({ name: "Tom", hometown: "Ouagadougou" }), new Person({ name: "Billy", hometown: "Detroit" }) ]; Parse.Object.saveAll(objects).then(() => { const subquery = new Parse.Query(Restaurant); subquery.greaterThan('ratings', 4); const query1 = new Parse.Query(Person); query1.matchesKeyInQuery('hometown', 'location', subquery); const query2 = new Parse.Query(Person); query2.equalTo('name', 'Tom'); const query = Parse.Query.or(query1, query2); return query.find(); }).then((results) => { expect(results.length).toEqual(2); done(); }, (error) => { jfail(error); done(); }); }); it('$nor valid query', (done) => { const objects = Array.from(Array(10).keys()).map((rating) => { return new TestObject({ 'rating': rating }); }); const highValue = 5; const lowValue = 3; const options = Object.assign({}, masterKeyOptions, { body: { where: { $nor: [ { rating : { $gt : highValue } }, { rating : { $lte : lowValue } }, ] }, } }); Parse.Object.saveAll(objects).then(() => { return rp.get(Parse.serverURL + "/classes/TestObject", options); }).then((results) => { expect(results.results.length).toBe(highValue - lowValue); expect(results.results.every(res => res.rating > lowValue && res.rating <= highValue)).toBe(true); done(); }); }); it('$nor invalid query - empty array', (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { $nor: [] }, } }); const obj = new TestObject(); obj.save().then(() => { return rp.get(Parse.serverURL + "/classes/TestObject", options); }).then(done.fail).catch((error) => { equal(error.error.code, Parse.Error.INVALID_QUERY); done(); }); }); it('$nor invalid query - wrong type', (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { $nor: 1337 }, } }); const obj = new TestObject(); obj.save().then(() => { return rp.get(Parse.serverURL + "/classes/TestObject", options); }).then(done.fail).catch((error) => { equal(error.error.code, Parse.Error.INVALID_QUERY); done(); }); }); it("dontSelect query", function(done) { const RestaurantObject = Parse.Object.extend("Restaurant"); const PersonObject = Parse.Object.extend("Person"); const objects = [ new RestaurantObject({ ratings: 5, location: "Djibouti" }), new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), new PersonObject({ name: "Bob", hometown: "Djibouti" }), new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), new PersonObject({ name: "Billy", hometown: "Djibouti" }) ]; Parse.Object.saveAll(objects, function() { const query = new Parse.Query(RestaurantObject); query.greaterThan("ratings", 4); const mainQuery = new Parse.Query(PersonObject); mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); mainQuery.find(expectSuccess({ success: function(results) { equal(results.length, 1); equal(results[0].get('name'), 'Tom'); done(); } })); }); }); it("dontSelect query without conditions", function(done) { const RestaurantObject = Parse.Object.extend("Restaurant"); const PersonObject = Parse.Object.extend("Person"); const objects = [ new RestaurantObject({ location: "Djibouti" }), new RestaurantObject({ location: "Ouagadougou" }), new PersonObject({ name: "Bob", hometown: "Djibouti" }), new PersonObject({ name: "Tom", hometown: "Yoloblahblahblah" }), new PersonObject({ name: "Billy", hometown: "Ouagadougou" }) ]; Parse.Object.saveAll(objects, function() { const query = new Parse.Query(RestaurantObject); const mainQuery = new Parse.Query(PersonObject); mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); mainQuery.find().then(results => { equal(results.length, 1); equal(results[0].get('name'), 'Tom'); done(); }); }); }); it("equalTo on same column as $dontSelect should not break $dontSelect functionality (#3678)", function(done) { const AuthorObject = Parse.Object.extend("Author"); const BlockedObject = Parse.Object.extend("Blocked"); const PostObject = Parse.Object.extend("Post"); let postAuthor = null; let requestUser = null; return new AuthorObject({ name: "Julius"}).save().then((user) => { postAuthor = user; return new AuthorObject({ name: "Bob"}).save(); }).then((user) => { requestUser = user; const objects = [ new PostObject({ author: postAuthor, title: "Lorem ipsum" }), new PostObject({ author: requestUser, title: "Kafka" }), new PostObject({ author: requestUser, title: "Brown fox" }), new BlockedObject({ blockedBy: postAuthor, blockedUser: requestUser}) ]; return Parse.Object.saveAll(objects); }).then(() => { const banListQuery = new Parse.Query(BlockedObject); banListQuery.equalTo("blockedUser", requestUser); return new Parse.Query(PostObject) .equalTo("author", postAuthor) .doesNotMatchKeyInQuery("author", "blockedBy", banListQuery) .find() .then((r) => { expect(r.length).toEqual(0); done(); }, done.fail); }) }); it("multiple dontSelect query", function(done) { const RestaurantObject = Parse.Object.extend("Restaurant"); const PersonObject = Parse.Object.extend("Person"); const objects = [ new RestaurantObject({ ratings: 7, location: "Djibouti2" }), new RestaurantObject({ ratings: 5, location: "Djibouti" }), new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), new PersonObject({ name: "Bob2", hometown: "Djibouti2" }), new PersonObject({ name: "Bob", hometown: "Djibouti" }), new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), ]; Parse.Object.saveAll(objects, function() { const query = new Parse.Query(RestaurantObject); query.greaterThan("ratings", 6); const query2 = new Parse.Query(RestaurantObject); query2.lessThan("ratings", 4); const subQuery = new Parse.Query(PersonObject); subQuery.matchesKeyInQuery("hometown", "location", query); const subQuery2 = new Parse.Query(PersonObject); subQuery2.matchesKeyInQuery("hometown", "location", query2); const mainQuery = new Parse.Query(PersonObject); mainQuery.doesNotMatchKeyInQuery("objectId", "objectId", Parse.Query.or(subQuery, subQuery2)); mainQuery.find(expectSuccess({ success: function(results) { equal(results.length, 1); equal(results[0].get('name'), 'Bob'); done(); } })); }); }); it("object with length", function(done) { const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); obj.set("length", 5); equal(obj.get("length"), 5); obj.save(null, { success: function() { const query = new Parse.Query(TestObject); query.find({ success: function(results) { equal(results.length, 1); equal(results[0].get("length"), 5); done(); }, error: function(error) { ok(false, error.message); done(); } }); }, error: function(error) { ok(false, error.message); done(); } }); }); it("include user", function(done) { Parse.User.signUp("bob", "password", { age: 21 }, { success: function(user) { const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); obj.save({ owner: user }, { success: function(obj) { const query = new Parse.Query(TestObject); query.include("owner"); query.get(obj.id, { success: function(objAgain) { equal(objAgain.id, obj.id); ok(objAgain.get("owner") instanceof Parse.User); equal(objAgain.get("owner").get("age"), 21); done(); }, error: function(objAgain, error) { ok(false, error.message); done(); } }); }, error: function(obj, error) { ok(false, error.message); done(); } }); }, error: function(user, error) { ok(false, error.message); done(); } }); }); it("or queries", function(done) { const objects = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function(x) { const object = new Parse.Object('BoxedNumber'); object.set('x', x); return object; }); Parse.Object.saveAll(objects, expectSuccess({ success: function() { const query1 = new Parse.Query('BoxedNumber'); query1.lessThan('x', 2); const query2 = new Parse.Query('BoxedNumber'); query2.greaterThan('x', 5); const orQuery = Parse.Query.or(query1, query2); orQuery.find(expectSuccess({ success: function(results) { equal(results.length, 6); for (const number of results) { ok(number.get('x') < 2 || number.get('x') > 5); } done(); } })); } })); }); // This relies on matchesQuery aka the $inQuery operator it("or complex queries", function(done) { const objects = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function(x) { const child = new Parse.Object('Child'); child.set('x', x); const parent = new Parse.Object('Parent'); parent.set('child', child); parent.set('y', x); return parent; }); Parse.Object.saveAll(objects, expectSuccess({ success: function() { const subQuery = new Parse.Query('Child'); subQuery.equalTo('x', 4); const query1 = new Parse.Query('Parent'); query1.matchesQuery('child', subQuery); const query2 = new Parse.Query('Parent'); query2.lessThan('y', 2); const orQuery = Parse.Query.or(query1, query2); orQuery.find(expectSuccess({ success: function(results) { equal(results.length, 3); done(); } })); } })); }); it("async methods", function(done) { const saves = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function(x) { const obj = new Parse.Object("TestObject"); obj.set("x", x + 1); return obj.save(); }); Parse.Promise.when(saves).then(function() { const query = new Parse.Query("TestObject"); query.ascending("x"); return query.first(); }).then(function(obj) { equal(obj.get("x"), 1); const query = new Parse.Query("TestObject"); query.descending("x"); return query.find(); }).then(function(results) { equal(results.length, 10); const query = new Parse.Query("TestObject"); return query.get(results[0].id); }).then(function(obj1) { equal(obj1.get("x"), 10); const query = new Parse.Query("TestObject"); return query.count(); }).then(function(count) { equal(count, 10); }).then(function() { done(); }); }); it("query.each", function(done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function(x) { const obj = new TestObject(); obj.set("x", x); return obj; }); Parse.Object.saveAll(items).then(function() { const query = new Parse.Query(TestObject); query.lessThan("x", COUNT); const seen = []; query.each(function(obj) { seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }, { batchSize: 10, success: function() { equal(seen.length, COUNT); for (let i = 0; i < COUNT; i++) { equal(seen[i], 1, "Should have seen object number " + i); } done(); }, error: function(error) { ok(false, error); done(); } }); }); }); it("query.each async", function(done) { const TOTAL = 50; const COUNT = 25; expect(COUNT + 1); const items = range(TOTAL).map(function(x) { const obj = new TestObject(); obj.set("x", x); return obj; }); const seen = []; Parse.Object.saveAll(items).then(function() { const query = new Parse.Query(TestObject); query.lessThan("x", COUNT); return query.each(function(obj) { const promise = new Parse.Promise(); process.nextTick(function() { seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; promise.resolve(); }); return promise; }, { batchSize: 10 }); }).then(function() { equal(seen.length, COUNT); for (let i = 0; i < COUNT; i++) { equal(seen[i], 1, "Should have seen object number " + i); } done(); }); }); it("query.each fails with order", function(done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function(x) { const obj = new TestObject(); obj.set("x", x); return obj; }); const seen = []; Parse.Object.saveAll(items).then(function() { const query = new Parse.Query(TestObject); query.lessThan("x", COUNT); query.ascending("x"); return query.each(function(obj) { seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }); }).then(function() { ok(false, "This should have failed."); done(); }, function() { done(); }); }); it("query.each fails with skip", function(done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function(x) { const obj = new TestObject(); obj.set("x", x); return obj; }); const seen = []; Parse.Object.saveAll(items).then(function() { const query = new Parse.Query(TestObject); query.lessThan("x", COUNT); query.skip(5); return query.each(function(obj) { seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }); }).then(function() { ok(false, "This should have failed."); done(); }, function() { done(); }); }); it("query.each fails with limit", function(done) { const TOTAL = 50; const COUNT = 25; expect(0); const items = range(TOTAL).map(function(x) { const obj = new TestObject(); obj.set("x", x); return obj; }); const seen = []; Parse.Object.saveAll(items).then(function() { const query = new Parse.Query(TestObject); query.lessThan("x", COUNT); query.limit(5); return query.each(function(obj) { seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }); }).then(function() { ok(false, "This should have failed."); done(); }, function() { done(); }); }); it("select keys query", function(done) { const obj = new TestObject({ foo: 'baz', bar: 1 }); obj.save().then(function () { obj._clearServerData(); const query = new Parse.Query(TestObject); query.select('foo'); return query.first(); }).then(function(result) { ok(result.id, "expected object id to be set"); ok(result.createdAt, "expected object createdAt to be set"); ok(result.updatedAt, "expected object updatedAt to be set"); ok(!result.dirty(), "expected result not to be dirty"); strictEqual(result.get('foo'), 'baz'); strictEqual(result.get('bar'), undefined, "expected 'bar' field to be unset"); return result.fetch(); }).then(function(result) { strictEqual(result.get('foo'), 'baz'); strictEqual(result.get('bar'), 1); }).then(function() { obj._clearServerData(); const query = new Parse.Query(TestObject); query.select([]); return query.first(); }).then(function(result) { ok(result.id, "expected object id to be set"); ok(!result.dirty(), "expected result not to be dirty"); strictEqual(result.get('foo'), undefined, "expected 'foo' field to be unset"); strictEqual(result.get('bar'), undefined, "expected 'bar' field to be unset"); }).then(function() { obj._clearServerData(); const query = new Parse.Query(TestObject); query.select(['foo','bar']); return query.first(); }).then(function(result) { ok(result.id, "expected object id to be set"); ok(!result.dirty(), "expected result not to be dirty"); strictEqual(result.get('foo'), 'baz'); strictEqual(result.get('bar'), 1); }).then(function() { obj._clearServerData(); const query = new Parse.Query(TestObject); query.select('foo', 'bar'); return query.first(); }).then(function(result) { ok(result.id, "expected object id to be set"); ok(!result.dirty(), "expected result not to be dirty"); strictEqual(result.get('foo'), 'baz'); strictEqual(result.get('bar'), 1); }).then(function() { done(); }, function (err) { ok(false, "other error: " + JSON.stringify(err)); done(); }); }); it('select keys with each query', function(done) { const obj = new TestObject({ foo: 'baz', bar: 1 }); obj.save().then(function() { obj._clearServerData(); const query = new Parse.Query(TestObject); query.select('foo'); query.each(function(result) { ok(result.id, 'expected object id to be set'); ok(result.createdAt, 'expected object createdAt to be set'); ok(result.updatedAt, 'expected object updatedAt to be set'); ok(!result.dirty(), 'expected result not to be dirty'); strictEqual(result.get('foo'), 'baz'); strictEqual(result.get('bar'), undefined, 'expected "bar" field to be unset'); }).then(function() { done(); }, function(err) { jfail(err); done(); }); }); }); it('notEqual with array of pointers', (done) => { const children = []; const parents = []; const promises = []; for (let i = 0; i < 2; i++) { const proc = (iter) => { const child = new Parse.Object('Child'); children.push(child); const parent = new Parse.Object('Parent'); parents.push(parent); promises.push( child.save().then(() => { parents[iter].set('child', [children[iter]]); return parents[iter].save(); }) ); }; proc(i); } Promise.all(promises).then(() => { const query = new Parse.Query('Parent'); query.notEqualTo('child', children[0]); return query.find(); }).then((results) => { expect(results.length).toEqual(1); expect(results[0].id).toEqual(parents[1].id); done(); }).catch((error) => { console.log(error); }); }); // PG don't support creating a null column it_exclude_dbs(['postgres'])('querying for null value', (done) => { const obj = new Parse.Object('TestObject'); obj.set('aNull', null); obj.save().then(() => { const query = new Parse.Query('TestObject'); query.equalTo('aNull', null); return query.find(); }).then((results) => { expect(results.length).toEqual(1); expect(results[0].get('aNull')).toEqual(null); done(); }) }); it('query within dictionary', (done) => { const promises = []; for (let i = 0; i < 2; i++) { const proc = (iter) => { const obj = new Parse.Object('TestObject'); obj.set('aDict', { x: iter + 1, y: iter + 2 }); promises.push(obj.save()); }; proc(i); } Promise.all(promises).then(() => { const query = new Parse.Query('TestObject'); query.equalTo('aDict.x', 1); return query.find(); }).then((results) => { expect(results.length).toEqual(1); done(); }, (error) => { console.log(error); }); }); it('supports include on the wrong key type (#2262)', function(done) { const childObject = new Parse.Object('TestChildObject'); childObject.set('hello', 'world'); childObject.save().then(() => { const obj = new Parse.Object('TestObject'); obj.set('foo', 'bar'); obj.set('child', childObject); return obj.save(); }).then(() => { const q = new Parse.Query('TestObject'); q.include('child'); q.include('child.parent'); q.include('createdAt'); q.include('createdAt.createdAt'); return q.find(); }).then((objs) => { expect(objs.length).toBe(1); expect(objs[0].get('child').get('hello')).toEqual('world'); expect(objs[0].createdAt instanceof Date).toBe(true); done(); }, () => { fail('should not fail'); done(); }); }); it('query match on array with single object', (done) => { const target = {__type: 'Pointer', className: 'TestObject', objectId: 'abc123'}; const obj = new Parse.Object('TestObject'); obj.set('someObjs', [target]); obj.save().then(() => { const query = new Parse.Query('TestObject'); query.equalTo('someObjs', target); return query.find(); }).then((results) => { expect(results.length).toEqual(1); done(); }, (error) => { console.log(error); }); }); it('query match on array with multiple objects', (done) => { const target1 = {__type: 'Pointer', className: 'TestObject', objectId: 'abc'}; const target2 = {__type: 'Pointer', className: 'TestObject', objectId: '123'}; const obj = new Parse.Object('TestObject'); obj.set('someObjs', [target1, target2]); obj.save().then(() => { const query = new Parse.Query('TestObject'); query.equalTo('someObjs', target1); return query.find(); }).then((results) => { expect(results.length).toEqual(1); done(); }, (error) => { console.log(error); }); }); it('query should not match on array when searching for null', (done) => { const target = {__type: 'Pointer', className: 'TestObject', objectId: '123'}; const obj = new Parse.Object('TestObject'); obj.set('someKey', 'someValue'); obj.set('someObjs', [target]); obj.save().then(() => { const query = new Parse.Query('TestObject'); query.equalTo('someKey', 'someValue'); query.equalTo('someObjs', null); return query.find(); }).then((results) => { expect(results.length).toEqual(0); done(); }, (error) => { console.log(error); }); }); // #371 it('should properly interpret a query v1', (done) => { const query = new Parse.Query("C1"); const auxQuery = new Parse.Query("C1"); query.matchesKeyInQuery("A1", "A2", auxQuery); query.include("A3"); query.include("A2"); query.find().then(() => { done(); }, (err) => { jfail(err); fail("should not failt"); done(); }) }); it('should properly interpret a query v2', (done) => { const user = new Parse.User(); user.set("username", "foo"); user.set("password", "bar"); return user.save().then((user) => { const objIdQuery = new Parse.Query("_User").equalTo("objectId", user.id); const blockedUserQuery = user.relation("blockedUsers").query(); const aResponseQuery = new Parse.Query("MatchRelationshipActivityResponse"); aResponseQuery.equalTo("userA", user); aResponseQuery.equalTo("userAResponse", 1); const bResponseQuery = new Parse.Query("MatchRelationshipActivityResponse"); bResponseQuery.equalTo("userB", user); bResponseQuery.equalTo("userBResponse", 1); const matchOr = Parse.Query.or(aResponseQuery, bResponseQuery); const matchRelationshipA = new Parse.Query("_User"); matchRelationshipA.matchesKeyInQuery("objectId", "userAObjectId", matchOr); const matchRelationshipB = new Parse.Query("_User"); matchRelationshipB.matchesKeyInQuery("objectId", "userBObjectId", matchOr); const orQuery = Parse.Query.or(objIdQuery, blockedUserQuery, matchRelationshipA, matchRelationshipB); const query = new Parse.Query("_User"); query.doesNotMatchQuery("objectId", orQuery); return query.find(); }).then(() => { done(); }, (err) => { jfail(err); fail("should not fail"); done(); }); }); it("should match a key in an array (#3195)", function(done) { const AuthorObject = Parse.Object.extend("Author"); const GroupObject = Parse.Object.extend("Group"); const PostObject = Parse.Object.extend("Post"); return new AuthorObject().save().then((user) => { const post = new PostObject({ author: user }); const group = new GroupObject({ members: [user], }); return Parse.Promise.when(post.save(), group.save()); }).then((p) => { return new Parse.Query(PostObject) .matchesKeyInQuery("author", "members", new Parse.Query(GroupObject)) .find() .then((r) => { expect(r.length).toEqual(1); if (r.length > 0) { expect(r[0].id).toEqual(p.id); } done(); }, done.fail); }); }); it('should find objects with array of pointers', (done) => { const objects = []; while(objects.length != 5) { const object = new Parse.Object('ContainedObject'); object.set('index', objects.length); objects.push(object); } Parse.Object.saveAll(objects).then((objects) => { const container = new Parse.Object('Container'); const pointers = objects.map((obj) => { return { __type: 'Pointer', className: 'ContainedObject', objectId: obj.id } }) container.set('objects', pointers); const container2 = new Parse.Object('Container'); container2.set('objects', pointers.slice(2, 3)); return Parse.Object.saveAll([container, container2]); }).then(() => { const inQuery = new Parse.Query('ContainedObject'); inQuery.greaterThanOrEqualTo('index', 1); const query = new Parse.Query('Container'); query.matchesQuery('objects', inQuery); return query.find(); }).then((results) => { if (results) { expect(results.length).toBe(2); } done(); }).fail((err) => { jfail(err); fail('should not fail'); done(); }) }) it('query with two OR subqueries (regression test #1259)', done => { const relatedObject = new Parse.Object('Class2'); relatedObject.save().then(relatedObject => { const anObject = new Parse.Object('Class1'); const relation = anObject.relation('relation'); relation.add(relatedObject); return anObject.save(); }).then(anObject => { const q1 = anObject.relation('relation').query(); q1.doesNotExist('nonExistantKey1'); const q2 = anObject.relation('relation').query(); q2.doesNotExist('nonExistantKey2'); Parse.Query.or(q1, q2).find().then(results => { expect(results.length).toEqual(1); if (results.length == 1) { expect(results[0].objectId).toEqual(q1.objectId); } done(); }); }); }); it('objectId containedIn with multiple large array', done => { const obj = new Parse.Object('MyClass'); obj.save().then(obj => { const longListOfStrings = []; for (let i = 0; i < 130; i++) { longListOfStrings.push(i.toString()); } longListOfStrings.push(obj.id); const q = new Parse.Query('MyClass'); q.containedIn('objectId', longListOfStrings); q.containedIn('objectId', longListOfStrings); return q.find(); }).then(results => { expect(results.length).toEqual(1); done(); }); }); it('containedIn with pointers should work with string array', done => { const obj = new Parse.Object('MyClass'); const child = new Parse.Object('Child'); child.save().then(() => { obj.set('child', child); return obj.save(); }).then(() => { const objs = []; for(let i = 0; i < 10; i++) { objs.push(new Parse.Object('MyClass')); } return Parse.Object.saveAll(objs); }).then(() => { const query = new Parse.Query('MyClass'); query.containedIn('child', [child.id]); return query.find(); }).then((results) => { expect(results.length).toBe(1); }).then(done).catch(done.fail); }); it('containedIn with pointers should work with string array, with many objects', done => { const objs = []; const children = []; for(let i = 0; i < 10; i++) { const obj = new Parse.Object('MyClass'); const child = new Parse.Object('Child'); objs.push(obj); children.push(child); } Parse.Object.saveAll(children).then(() => { return Parse.Object.saveAll(objs.map((obj, i) => { obj.set('child', children[i]); return obj; })); }).then(() => { const query = new Parse.Query('MyClass'); const subset = children.slice(0, 5).map((child) => { return child.id; }); query.containedIn('child', subset); return query.find(); }).then((results) => { expect(results.length).toBe(5); }).then(done).catch(done.fail); }); it('include for specific object', function(done){ const child = new Parse.Object('Child'); const parent = new Parse.Object('Parent'); child.set('foo', 'bar'); parent.set('child', child); Parse.Object.saveAll([child, parent], function(response){ const savedParent = response[1]; const parentQuery = new Parse.Query('Parent'); parentQuery.include('child'); parentQuery.get(savedParent.id, { success: function(parentObj) { const childPointer = parentObj.get('child'); ok(childPointer); equal(childPointer.get('foo'), 'bar'); done(); } }); }); }); it('select keys for specific object', function(done) { const Foobar = new Parse.Object('Foobar'); Foobar.set('foo', 'bar'); Foobar.set('fizz', 'buzz'); Foobar.save({ success: function(savedFoobar){ const foobarQuery = new Parse.Query('Foobar'); foobarQuery.select('fizz'); foobarQuery.get(savedFoobar.id,{ success: function(foobarObj){ equal(foobarObj.get('fizz'), 'buzz'); equal(foobarObj.get('foo'), undefined); done(); } }); } }) }); it('select nested keys (issue #1567)', function(done) { const Foobar = new Parse.Object('Foobar'); const BarBaz = new Parse.Object('Barbaz'); BarBaz.set('key', 'value'); BarBaz.set('otherKey', 'value'); BarBaz.save().then(() => { Foobar.set('foo', 'bar'); Foobar.set('fizz', 'buzz'); Foobar.set('barBaz', BarBaz); return Foobar.save(); }).then(function(savedFoobar){ const foobarQuery = new Parse.Query('Foobar'); foobarQuery.include('barBaz'); foobarQuery.select(['fizz', 'barBaz.key']); foobarQuery.get(savedFoobar.id,{ success: function(foobarObj){ equal(foobarObj.get('fizz'), 'buzz'); equal(foobarObj.get('foo'), undefined); if (foobarObj.has('barBaz')) { equal(foobarObj.get('barBaz').get('key'), 'value'); equal(foobarObj.get('barBaz').get('otherKey'), undefined); } else { fail('barBaz should be set'); } done(); } }); }); }); it('select nested keys 2 level (issue #1567)', function(done) { const Foobar = new Parse.Object('Foobar'); const BarBaz = new Parse.Object('Barbaz'); const Bazoo = new Parse.Object('Bazoo'); Bazoo.set('some', 'thing'); Bazoo.set('otherSome', 'value'); Bazoo.save().then(() => { BarBaz.set('key', 'value'); BarBaz.set('otherKey', 'value'); BarBaz.set('bazoo', Bazoo); return BarBaz.save(); }).then(() => { Foobar.set('foo', 'bar'); Foobar.set('fizz', 'buzz'); Foobar.set('barBaz', BarBaz); return Foobar.save(); }).then(function(savedFoobar){ const foobarQuery = new Parse.Query('Foobar'); foobarQuery.include('barBaz'); foobarQuery.include('barBaz.bazoo'); foobarQuery.select(['fizz', 'barBaz.key', 'barBaz.bazoo.some']); foobarQuery.get(savedFoobar.id,{ success: function(foobarObj){ equal(foobarObj.get('fizz'), 'buzz'); equal(foobarObj.get('foo'), undefined); if (foobarObj.has('barBaz')) { equal(foobarObj.get('barBaz').get('key'), 'value'); equal(foobarObj.get('barBaz').get('otherKey'), undefined); equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); } else { fail('barBaz should be set'); } done(); } }); }); }); it('includeAll', (done) => { const child1 = new TestObject({ foo: 'bar', name: 'ac' }); const child2 = new TestObject({ foo: 'baz', name: 'flo' }); const child3 = new TestObject({ foo: 'bad', name: 'mo' }); const parent = new Container({ child1, child2, child3 }); Parse.Object.saveAll([parent, child1, child2, child3]).then(() => { const options = Object.assign({}, masterKeyOptions, { body: { where: { objectId: parent.id }, includeAll: true, } }); return rp.get(Parse.serverURL + "/classes/Container", options); }).then((resp) => { const result = resp.results[0]; equal(result.child1.foo, 'bar'); equal(result.child2.foo, 'baz'); equal(result.child3.foo, 'bad'); equal(result.child1.name, 'ac'); equal(result.child2.name, 'flo'); equal(result.child3.name, 'mo'); done(); }); }); it('select nested keys 2 level includeAll', (done) => { const Foobar = new Parse.Object('Foobar'); const BarBaz = new Parse.Object('Barbaz'); const Bazoo = new Parse.Object('Bazoo'); const Tang = new Parse.Object('Tang'); Bazoo.set('some', 'thing'); Bazoo.set('otherSome', 'value'); Bazoo.save().then(() => { BarBaz.set('key', 'value'); BarBaz.set('otherKey', 'value'); BarBaz.set('bazoo', Bazoo); return BarBaz.save(); }).then(() => { Tang.set('clan', 'wu'); return Tang.save(); }).then(() => { Foobar.set('foo', 'bar'); Foobar.set('fizz', 'buzz'); Foobar.set('barBaz', BarBaz); Foobar.set('group', Tang); return Foobar.save(); }).then((savedFoobar) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { objectId: savedFoobar.id }, includeAll: true, keys: 'fizz,barBaz.key,barBaz.bazoo.some', } }); return rp.get(Parse.serverURL + "/classes/Foobar", options); }).then((resp) => { const result = resp.results[0]; equal(result.group.clan, 'wu'); equal(result.foo, undefined); equal(result.fizz, 'buzz'); equal(result.barBaz.key, 'value'); equal(result.barBaz.otherKey, undefined); equal(result.barBaz.bazoo.some, 'thing'); equal(result.barBaz.bazoo.otherSome, undefined); done(); }) }); it('select nested keys 2 level without include (issue #3185)', function(done) { const Foobar = new Parse.Object('Foobar'); const BarBaz = new Parse.Object('Barbaz'); const Bazoo = new Parse.Object('Bazoo'); Bazoo.set('some', 'thing'); Bazoo.set('otherSome', 'value'); Bazoo.save().then(() => { BarBaz.set('key', 'value'); BarBaz.set('otherKey', 'value'); BarBaz.set('bazoo', Bazoo); return BarBaz.save(); }).then(() => { Foobar.set('foo', 'bar'); Foobar.set('fizz', 'buzz'); Foobar.set('barBaz', BarBaz); return Foobar.save(); }).then(function(savedFoobar){ const foobarQuery = new Parse.Query('Foobar'); foobarQuery.select(['fizz', 'barBaz.key', 'barBaz.bazoo.some']); return foobarQuery.get(savedFoobar.id); }).then((foobarObj) => { equal(foobarObj.get('fizz'), 'buzz'); equal(foobarObj.get('foo'), undefined); if (foobarObj.has('barBaz')) { equal(foobarObj.get('barBaz').get('key'), 'value'); equal(foobarObj.get('barBaz').get('otherKey'), undefined); if (foobarObj.get('barBaz').has('bazoo')) { equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); } else { fail('bazoo should be set'); } } else { fail('barBaz should be set'); } done(); }) }); it('properly handles nested ors', function(done) { const objects = []; while(objects.length != 4) { const obj = new Parse.Object('Object'); obj.set('x', objects.length); objects.push(obj) } Parse.Object.saveAll(objects).then(() => { const q0 = new Parse.Query('Object'); q0.equalTo('x', 0); const q1 = new Parse.Query('Object'); q1.equalTo('x', 1); const q2 = new Parse.Query('Object'); q2.equalTo('x', 2); const or01 = Parse.Query.or(q0,q1); return Parse.Query.or(or01, q2).find(); }).then((results) => { expect(results.length).toBe(3); done(); }).catch((error) => { fail('should not fail'); jfail(error); done(); }) }); it('should not depend on parameter order #3169', function(done) { const score1 = new Parse.Object('Score', {scoreId: '1'}); const score2 = new Parse.Object('Score', {scoreId: '2'}); const game1 = new Parse.Object('Game', {gameId: '1'}); const game2 = new Parse.Object('Game', {gameId: '2'}); Parse.Object.saveAll([score1, score2, game1, game2]).then(() => { game1.set('score', [score1]); game2.set('score', [score2]); return Parse.Object.saveAll([game1, game2]); }).then(() => { const where = { score: { objectId: score1.id, className: 'Score', __type: 'Pointer', } } return require('request-promise').post({ url: Parse.serverURL + "/classes/Game", json: { where, "_method": "GET" }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey } }); }).then((response) => { expect(response.results.length).toBe(1); done(); }, done.fail); }); it('should not interfere with has when using select on field with undefined value #3999', (done) => { const obj1 = new Parse.Object('TestObject'); const obj2 = new Parse.Object('OtherObject'); obj2.set('otherField', 1); obj1.set('testPointerField', obj2); obj1.set('shouldBe', true); const obj3 = new Parse.Object('TestObject'); obj3.set('shouldBe', false); Parse.Object.saveAll([obj1, obj3]).then(() => { const query = new Parse.Query('TestObject'); query.include('testPointerField'); query.select(['testPointerField', 'testPointerField.otherField', 'shouldBe']); return query.find(); }).then(results => { results.forEach(result => { equal(result.has('testPointerField'), result.get('shouldBe')); }); done(); }).catch(done.fail); }); it_only_db('mongo')('should handle relative times correctly', function(done) { const now = Date.now(); const obj1 = new Parse.Object('MyCustomObject', { name: 'obj1', ttl: new Date(now + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); const obj2 = new Parse.Object('MyCustomObject', { name: 'obj2', ttl: new Date(now - 2 * 24 * 60 * 60 * 1000), // 2 days ago }); Parse.Object.saveAll([obj1, obj2]) .then(() => { const q = new Parse.Query('MyCustomObject'); q.greaterThan('ttl', { $relativeTime: 'in 1 day' }); return q.find({ useMasterKey: true }); }) .then((results) => { expect(results.length).toBe(1); }) .then(() => { const q = new Parse.Query('MyCustomObject'); q.greaterThan('ttl', { $relativeTime: '1 day ago' }); return q.find({ useMasterKey: true }); }) .then((results) => { expect(results.length).toBe(1); }) .then(() => { const q = new Parse.Query('MyCustomObject'); q.lessThan('ttl', { $relativeTime: '5 days ago' }); return q.find({ useMasterKey: true }); }) .then((results) => { expect(results.length).toBe(0); }) .then(() => { const q = new Parse.Query('MyCustomObject'); q.greaterThan('ttl', { $relativeTime: '3 days ago' }); return q.find({ useMasterKey: true }); }) .then((results) => { expect(results.length).toBe(2); }) .then(() => { const q = new Parse.Query('MyCustomObject'); q.greaterThan('ttl', { $relativeTime: 'now' }); return q.find({ useMasterKey: true }); }) .then((results) => { expect(results.length).toBe(1); }) .then(() => { const q = new Parse.Query('MyCustomObject'); q.greaterThan('ttl', { $relativeTime: 'now' }); q.lessThan('ttl', { $relativeTime: 'in 1 day' }); return q.find({ useMasterKey: true }); }) .then((results) => { expect(results.length).toBe(0); }) .then(() => { const q = new Parse.Query('MyCustomObject'); q.greaterThan('ttl', { $relativeTime: '1 year 3 weeks ago' }); return q.find({ useMasterKey: true }); }) .then((results) => { expect(results.length).toBe(2); }) .then(done, done.fail); }); it_only_db('mongo')('should error on invalid relative time', function(done) { const obj1 = new Parse.Object('MyCustomObject', { name: 'obj1', ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); const q = new Parse.Query('MyCustomObject'); q.greaterThan('ttl', { $relativeTime: '-12 bananas ago' }); obj1.save({ useMasterKey: true }) .then(() => q.find({ useMasterKey: true })) .then(done.fail, done); }); it_only_db('mongo')('should error when using $relativeTime on non-Date field', function(done) { const obj1 = new Parse.Object('MyCustomObject', { name: 'obj1', nonDateField: 'abcd', ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); const q = new Parse.Query('MyCustomObject'); q.greaterThan('nonDateField', { $relativeTime: '1 day ago' }); obj1.save({ useMasterKey: true }) .then(() => q.find({ useMasterKey: true })) .then(done.fail, done); }); it('should match complex structure with dot notation when using matchesKeyInQuery', function(done) { const group1 = new Parse.Object('Group', { name: 'Group #1' }); const group2 = new Parse.Object('Group', { name: 'Group #2' }); Parse.Object.saveAll([group1, group2]) .then(() => { const role1 = new Parse.Object('Role', { name: 'Role #1', type: 'x', belongsTo: group1 }); const role2 = new Parse.Object('Role', { name: 'Role #2', type: 'y', belongsTo: group1 }); return Parse.Object.saveAll([role1, role2]); }) .then(() => { const rolesOfTypeX = new Parse.Query('Role'); rolesOfTypeX.equalTo('type', 'x'); const groupsWithRoleX = new Parse.Query('Group'); groupsWithRoleX.matchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); groupsWithRoleX.find(expectSuccess({ success: function(results) { equal(results.length, 1); equal(results[0].get('name'), group1.get('name')); done(); } })) }) }); it('should match complex structure with dot notation when using doesNotMatchKeyInQuery', function(done) { const group1 = new Parse.Object('Group', { name: 'Group #1' }); const group2 = new Parse.Object('Group', { name: 'Group #2' }); Parse.Object.saveAll([group1, group2]) .then(() => { const role1 = new Parse.Object('Role', { name: 'Role #1', type: 'x', belongsTo: group1 }); const role2 = new Parse.Object('Role', { name: 'Role #2', type: 'y', belongsTo: group1 }); return Parse.Object.saveAll([role1, role2]); }) .then(() => { const rolesOfTypeX = new Parse.Query('Role'); rolesOfTypeX.equalTo('type', 'x'); const groupsWithRoleX = new Parse.Query('Group'); groupsWithRoleX.doesNotMatchKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); groupsWithRoleX.find(expectSuccess({ success: function(results) { equal(results.length, 1); equal(results[0].get('name'), group2.get('name')); done(); } })) }) }); it('withJSON supports geoWithin.centerSphere', (done) => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); const obj1 = new Parse.Object('TestObject', {location: inbound}); const obj2 = new Parse.Object('TestObject', {location: onbound}); const obj3 = new Parse.Object('TestObject', {location: outbound}); const center = new Parse.GeoPoint(0, 0); const distanceInKilometers = 1569 + 1; // 1569km is the approximate distance between {0, 0} and {10, 10}. Parse.Object.saveAll([obj1, obj2, obj3]).then(() => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { '$geoWithin': { '$centerSphere': [ center, distanceInKilometers / 6371.0 ] } }; q.withJSON(jsonQ); return q.find(); }).then(results => { equal(results.length, 2); const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { '$geoWithin': { '$centerSphere': [ [0, 0], distanceInKilometers / 6371.0 ] } }; q.withJSON(jsonQ); return q.find(); }).then(results => { equal(results.length, 2); done(); }).catch(error => { fail(error); done(); }); }); it('withJSON with geoWithin.centerSphere fails without parameters', (done) => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { '$geoWithin': { '$centerSphere': [ ] } }; q.withJSON(jsonQ); q.find(expectError(Parse.Error.INVALID_JSON, done)); }); it('withJSON with geoWithin.centerSphere fails with invalid distance', (done) => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { '$geoWithin': { '$centerSphere': [ [0, 0], 'invalid_distance' ] } }; q.withJSON(jsonQ); q.find(expectError(Parse.Error.INVALID_JSON, done)); }); it('withJSON with geoWithin.centerSphere fails with invalid coordinate', (done) => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { '$geoWithin': { '$centerSphere': [ [-190,-190], 1 ] } }; q.withJSON(jsonQ); q.find(expectError(undefined, done)); }); it('withJSON with geoWithin.centerSphere fails with invalid geo point', (done) => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { '$geoWithin': { '$centerSphere': [ {'longitude': 0, 'dummytude': 0}, 1 ] } }; q.withJSON(jsonQ); q.find(expectError(undefined, done)); }); });