diff --git a/integration/test/ParseCloudTest.js b/integration/test/ParseCloudTest.js index 3f39bdf22..8ec1702b1 100644 --- a/integration/test/ParseCloudTest.js +++ b/integration/test/ParseCloudTest.js @@ -102,8 +102,13 @@ describe('Parse Cloud', () => { let jobStatus = await Parse.Cloud.getJobStatus(jobStatusId); assert.equal(jobStatus.get('status'), 'running'); - await sleep(2000); - + const checkJobStatus = async () => { + const result = await Parse.Cloud.getJobStatus(jobStatusId); + return result && result.get('status') === 'succeeded'; + }; + while (!(await checkJobStatus())) { + await sleep(100); + } jobStatus = await Parse.Cloud.getJobStatus(jobStatusId); assert.equal(jobStatus.get('status'), 'succeeded'); }); diff --git a/integration/test/ParseEventuallyQueueTest.js b/integration/test/ParseEventuallyQueueTest.js index ce718b99f..2df0a1bfe 100644 --- a/integration/test/ParseEventuallyQueueTest.js +++ b/integration/test/ParseEventuallyQueueTest.js @@ -163,9 +163,14 @@ describe('Parse EventuallyQueue', () => { Parse.EventuallyQueue.poll(); assert.ok(Parse.EventuallyQueue.isPolling()); - await sleep(4000); + while (Parse.EventuallyQueue.isPolling()) { + await sleep(100); + } const query = new Parse.Query(TestObject); - const result = await query.get(object.id); + let result = await query.get(object.id); + while (result.get('foo') !== 'bar') { + result = await query.get(object.id); + } assert.strictEqual(result.get('foo'), 'bar'); const length = await Parse.EventuallyQueue.length(); @@ -188,7 +193,6 @@ describe('Parse EventuallyQueue', () => { it('can saveEventually', async done => { const parseServer = await reconfigureServer(); const object = new TestObject({ hash: 'saveSecret' }); - await parseServer.handleShutdown(); parseServer.server.close(async () => { await object.saveEventually(); let length = await Parse.EventuallyQueue.length(); @@ -196,15 +200,23 @@ describe('Parse EventuallyQueue', () => { assert.strictEqual(length, 1); await reconfigureServer({}); - await sleep(3000); // Wait for polling - + while (Parse.EventuallyQueue.isPolling()) { + await sleep(100); + } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); + + while (await Parse.EventuallyQueue.length()) { + await sleep(100); + } length = await Parse.EventuallyQueue.length(); assert.strictEqual(length, 0); const query = new Parse.Query(TestObject); query.equalTo('hash', 'saveSecret'); - const results = await query.find(); + let results = await query.find(); + while (results.length === 0) { + results = await query.find(); + } assert.strictEqual(results.length, 1); done(); }); @@ -214,7 +226,6 @@ describe('Parse EventuallyQueue', () => { const parseServer = await reconfigureServer(); const object = new TestObject({ hash: 'deleteSecret' }); await object.save(); - await parseServer.handleShutdown(); parseServer.server.close(async () => { await object.destroyEventually(); let length = await Parse.EventuallyQueue.length(); @@ -222,15 +233,22 @@ describe('Parse EventuallyQueue', () => { assert.strictEqual(length, 1); await reconfigureServer({}); - await sleep(3000); // Wait for polling - + while (Parse.EventuallyQueue.isPolling()) { + await sleep(100); + } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); + while (await Parse.EventuallyQueue.length()) { + await sleep(100); + } length = await Parse.EventuallyQueue.length(); assert.strictEqual(length, 0); const query = new Parse.Query(TestObject); query.equalTo('hash', 'deleteSecret'); - const results = await query.find(); + let results = await query.find(); + while (results.length) { + results = await query.find(); + } assert.strictEqual(results.length, 0); done(); }); diff --git a/integration/test/ParseGeoPointTest.js b/integration/test/ParseGeoPointTest.js index 075c2a8de..9f4414eff 100644 --- a/integration/test/ParseGeoPointTest.js +++ b/integration/test/ParseGeoPointTest.js @@ -311,8 +311,6 @@ describe('Geo Point', () => { }) .then(results => { assert.equal(results.length, 2); - assert.equal(results[0].get('index'), 0); - assert.equal(results[1].get('index'), 1); done(); }); }); @@ -358,8 +356,9 @@ describe('Geo Point', () => { query.withinKilometers('location', sfo, 3700.0, false); query.find().then(results => { assert.equal(results.length, 2); - assert.equal(results[0].get('name'), 'San Francisco'); - assert.equal(results[1].get('name'), 'Sacramento'); + results.forEach(result => { + assert.strictEqual(['San Francisco', 'Sacramento'].includes(result.get('name')), true); + }); done(); }); }); @@ -401,8 +400,9 @@ describe('Geo Point', () => { query.withinMiles('location', sfo, 2200.0, false); query.find().then(results => { assert.equal(results.length, 2); - assert.equal(results[0].get('name'), 'San Francisco'); - assert.equal(results[1].get('name'), 'Sacramento'); + results.forEach(result => { + assert.strictEqual(['San Francisco', 'Sacramento'].includes(result.get('name')), true); + }); done(); }); }); @@ -476,19 +476,13 @@ describe('Geo Point', () => { }); }); - xit( - 'minimum 3 points withinPolygon', - function (done) { - const query = new Parse.Query(TestPoint); - query.withinPolygon('location', []); - query - .find() - .then(done.fail, err => { - assert.equal(err.code, Parse.Error.INVALID_JSON); - done(); - }) - .catch(done.fail); - }, - 'Test passes locally but not on CI' - ); + it('minimum 3 points withinPolygon', async () => { + const query = new Parse.Query(TestPoint); + query.withinPolygon('location', []); + try { + await query.find(); + } catch (error) { + assert.strictEqual(error.code, Parse.Error.INVALID_JSON); + } + }); }); diff --git a/integration/test/ParseLocalDatastoreTest.js b/integration/test/ParseLocalDatastoreTest.js index b2dabe3ca..7fb81005e 100644 --- a/integration/test/ParseLocalDatastoreTest.js +++ b/integration/test/ParseLocalDatastoreTest.js @@ -2856,6 +2856,8 @@ function runTest(controller) { localDatastore = await Parse.LocalDatastore._getAllContents(); expect(localDatastore[LDS_KEY(testClassB)][0].classA.objectId).toEqual(testClassA.id); + Parse.Object.unregisterSubclass('ClassA'); + Parse.Object.unregisterSubclass('ClassB'); }); }); } diff --git a/integration/test/ParseServerTest.js b/integration/test/ParseServerTest.js index 0cecc829e..3b22e9dba 100644 --- a/integration/test/ParseServerTest.js +++ b/integration/test/ParseServerTest.js @@ -4,9 +4,12 @@ const assert = require('assert'); describe('ParseServer', () => { it('can reconfigure server', async done => { - const server = await reconfigureServer({ serverURL: 'www.google.com' }); - assert.strictEqual(server.config.serverURL, 'www.google.com'); - done(); + const parseServer = await reconfigureServer({ serverURL: 'www.google.com' }); + assert.strictEqual(parseServer.config.serverURL, 'www.google.com'); + parseServer.server.close(async () => { + await reconfigureServer(); + done(); + }); }); it('can shutdown', async done => { diff --git a/integration/test/ParseSubclassTest.js b/integration/test/ParseSubclassTest.js index 5de1e2fe8..99af8eb94 100644 --- a/integration/test/ParseSubclassTest.js +++ b/integration/test/ParseSubclassTest.js @@ -195,6 +195,7 @@ describe('Parse Object Subclasses', () => { }); it('registerSubclass with unknown className', async () => { + Parse.Object.unregisterSubclass('TestObject'); let outerClassName = ''; class TestObject extends Parse.Object { constructor(className) { @@ -210,5 +211,6 @@ describe('Parse Object Subclasses', () => { expect(first instanceof TestObject).toBe(true); expect(first.className).toBe('TestObject'); expect(outerClassName).toBe('TestObject'); + Parse.Object.unregisterSubclass('TestObject'); }); }); diff --git a/integration/test/ParseUserTest.js b/integration/test/ParseUserTest.js index 15e4fb125..721104b6b 100644 --- a/integration/test/ParseUserTest.js +++ b/integration/test/ParseUserTest.js @@ -2,6 +2,7 @@ const assert = require('assert'); const Parse = require('../../node'); +const uuidv4 = require('uuid/v4'); class CustomUser extends Parse.User { constructor(attributes) { @@ -42,8 +43,8 @@ global.FB = { }; describe('Parse User', () => { - beforeEach(() => { - Parse.Object.registerSubclass('_User', Parse.User); + afterAll(() => { + Parse.Object.unregisterSubclass('CustomUser'); }); it('can sign up users via static method', done => { @@ -491,6 +492,7 @@ describe('Parse User', () => { }); it('can update users', done => { + Parse.User.enableUnsafeCurrentUser(); const user = new Parse.User(); user .signUp({ @@ -804,8 +806,8 @@ describe('Parse User', () => { Parse.User.enableUnsafeCurrentUser(); let user = new CustomUser(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); user = await CustomUser.logInWith(provider.getAuthType(), provider.getAuthData()); expect(user._isLinked(provider)).toBe(true); @@ -817,8 +819,8 @@ describe('Parse User', () => { Parse.User.enableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith(provider.getAuthType(), provider.getAuthData()); expect(user._isLinked(provider)).toBe(true); @@ -830,8 +832,8 @@ describe('Parse User', () => { Parse.User.disableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.save(null, { useMasterKey: true }); await user.linkWith(provider.getAuthType(), provider.getAuthData(), { useMasterKey: true, @@ -845,8 +847,8 @@ describe('Parse User', () => { Parse.User.disableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); expect(user.isCurrent()).toBe(false); @@ -860,9 +862,10 @@ describe('Parse User', () => { }); it('linked account can login with authData', async () => { + Parse.User.disableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.save(null, { useMasterKey: true }); await user.linkWith(provider.getAuthType(), provider.getAuthData(), { useMasterKey: true, @@ -876,8 +879,8 @@ describe('Parse User', () => { it('can linking un-authenticated user without master key', async () => { const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.save(null, { useMasterKey: true }); await user.linkWith(provider.getAuthType(), provider.getAuthData()); expect(user.getSessionToken()).toBeDefined(); @@ -905,8 +908,8 @@ describe('Parse User', () => { }; Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith(provider.getAuthType(), provider.getAuthData()); expect(user._isLinked(provider)).toBe(true); @@ -925,8 +928,8 @@ describe('Parse User', () => { Parse.User.enableUnsafeCurrentUser(); Parse.FacebookUtils.init(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await Parse.FacebookUtils.link(user); expect(Parse.FacebookUtils.isLinked(user)).toBe(true); @@ -958,8 +961,8 @@ describe('Parse User', () => { auth_token_secret: 'G1tl1R0gaYKTyxw0uYJDKRoVhM16ifyLeMwIaKlFtPkQr', }; const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith('twitter', { authData }); @@ -982,8 +985,8 @@ describe('Parse User', () => { auth_token_secret: 'G1tl1R0gaYKTyxw0uYJDKRoVhM16ifyLeMwIaKlFtPkQr', }; const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith('twitter', { authData }); diff --git a/integration/test/helper.js b/integration/test/helper.js index 3517b75b6..68a9007b4 100644 --- a/integration/test/helper.js +++ b/integration/test/helper.js @@ -1,4 +1,4 @@ -jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; +jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000; const ParseServer = require('parse-server').default; const CustomAuth = require('./CustomAuth'); diff --git a/jasmine.json b/jasmine.json index 1d66e1118..9590307f3 100644 --- a/jasmine.json +++ b/jasmine.json @@ -6,6 +6,6 @@ "spec_files": [ "*Test.js" ], - "random": false, - "timeout": 10000 + "random": true, + "timeout": 20000 } diff --git a/src/ParseObject.js b/src/ParseObject.js index 516a36185..e939592fd 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -449,6 +449,10 @@ class ParseObject { stateController.mergeFirstPendingState(this._getStateIdentifier()); } + static _getClassMap() { + return classMap; + } + /** Public methods **/ initialize() { @@ -1884,6 +1888,18 @@ class ParseObject { } } + /** + * Unegisters a subclass of Parse.Object with a specific class name. + * + * @param {string} className The class name of the subclass + */ + static unregisterSubclass(className: string) { + if (typeof className !== 'string') { + throw new TypeError('The first argument must be a valid class name.'); + } + delete classMap[className]; + } + /** * Creates a new subclass of Parse.Object for the given Parse class name. * diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 6e72cc91a..9e222e4b9 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -3424,6 +3424,10 @@ describe('ParseObject Subclasses', () => { }).toThrow( 'You must register the subclass constructor. Did you attempt to register an instance of the subclass?' ); + + expect(() => { + ParseObject.unregisterSubclass(1234); + }).toThrow('The first argument must be a valid class name.'); }); it('can use on ParseObject subclass for multiple Parse.Object class names', () => { @@ -3435,12 +3439,27 @@ describe('ParseObject Subclasses', () => { ParseObject.registerSubclass('TestObject', MyParseObjects); ParseObject.registerSubclass('TestObject1', MyParseObjects); ParseObject.registerSubclass('TestObject2', MyParseObjects); + const obj = new MyParseObjects('TestObject'); expect(obj.className).toBe('TestObject'); const obj1 = new MyParseObjects('TestObject1'); expect(obj1.className).toBe('TestObject1'); const obj2 = new MyParseObjects('TestObject2'); expect(obj2.className).toBe('TestObject2'); + + let classMap = ParseObject._getClassMap(); + expect(classMap.TestObject).toEqual(MyParseObjects); + expect(classMap.TestObject1).toEqual(MyParseObjects); + expect(classMap.TestObject2).toEqual(MyParseObjects); + + ParseObject.unregisterSubclass('TestObject'); + ParseObject.unregisterSubclass('TestObject1'); + ParseObject.unregisterSubclass('TestObject2'); + + classMap = ParseObject._getClassMap(); + expect(classMap.TestObject).toBeUndefined(); + expect(classMap.TestObject1).toBeUndefined(); + expect(classMap.TestObject2).toBeUndefined(); }); it('can inflate subclasses from server JSON', () => {