diff --git a/src/ParseObject.js b/src/ParseObject.js index 87738d56f..c7276cd3c 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -347,10 +347,13 @@ class ParseObject { }; } - _finishFetch(serverData: AttributeMap) { + _finishFetch(serverData: AttributeMap, override?: boolean) { if (!this.id && serverData.objectId) { this.id = serverData.objectId; } + if (override) { + this._clearServerData(); + } const stateController = CoreManager.getObjectStateController(); stateController.initializeState(this._getStateIdentifier()); const decoded = {}; diff --git a/src/ParseUser.js b/src/ParseUser.js index d0303dac3..13d8356e2 100644 --- a/src/ParseUser.js +++ b/src/ParseUser.js @@ -1088,7 +1088,7 @@ const DefaultController = { stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); response.password = undefined; - user._finishFetch(response); + user._finishFetch(response, true); if (!canUseCurrentUser) { // We can't set the current user, so just return the one we logged in return Promise.resolve(user); diff --git a/src/__tests__/Cloud-test.js b/src/__tests__/Cloud-test.js index b65bff268..215b835b7 100644 --- a/src/__tests__/Cloud-test.js +++ b/src/__tests__/Cloud-test.js @@ -15,15 +15,22 @@ jest.dontMock('../ParseError'); jest.dontMock('../ParseObject'); jest.dontMock('../ParseQuery'); jest.dontMock('../Push'); +jest.dontMock('../parseDate'); +jest.dontMock('../ObjectStateMutations'); +jest.dontMock('../SingleInstanceStateController'); +jest.dontMock('../UniqueInstanceStateController'); const Cloud = require('../Cloud'); const CoreManager = require('../CoreManager'); const Push = require('../Push'); +const ParseObject = require('../ParseObject').default; const defaultController = CoreManager.getCloudController(); describe('Cloud', () => { beforeEach(() => { + ParseObject.enableSingleInstance(); + const run = jest.fn(); const getJobsData = jest.fn(); const startJob = jest.fn(); @@ -216,6 +223,48 @@ describe('CloudController', () => { }); }); + it('run same function twice with different responses', async () => { + const request = jest.fn(); + request.mockReturnValue( + Promise.resolve({ + success: true, + result: { + objectId: 'abc123', + className: 'Item', + __type: 'Object', + createdAt: '2015-01-01T00:00:00.000Z', + updatedAt: '2015-01-01T00:00:00.000Z', + label: 'foobar', + }, + }) + ); + + const ajax = jest.fn(); + CoreManager.setRESTController({ request: request, ajax: ajax }); + + const response1 = await Cloud.run('myfunction'); + expect(response1.get('label')).toBe('foobar'); + + request.mockReturnValue( + Promise.resolve({ + success: true, + result: { + objectId: 'abc123', + className: 'Item', + __type: 'Object', + createdAt: '2015-01-01T00:00:00.000Z', + updatedAt: '2015-01-01T00:00:00.000Z', + label2: 'control to confirm correct mock usage', + // Note that 'label' is not returned + }, + }) + ); + + const response2 = await Cloud.run('myfunction'); + expect(response2.get('label2')).toBe('control to confirm correct mock usage'); + expect(response2.get('label')).toBe(undefined); // Failing test PR #1442 + }); + it('startJob passes encoded requests', () => { Cloud.startJob('myJob', { value: 12, diff --git a/src/__tests__/ParseUser-test.js b/src/__tests__/ParseUser-test.js index 8691513a3..c50439aa9 100644 --- a/src/__tests__/ParseUser-test.js +++ b/src/__tests__/ParseUser-test.js @@ -940,6 +940,61 @@ describe('ParseUser', () => { }); }); + it('user who logs out, deletes attribute, then logs back in has correct attributes', async () => { + ParseUser.enableUnsafeCurrentUser(); + ParseUser._clearCache(); + CoreManager.setRESTController({ + async request(method, path) { + expect(path).toBe('login'); + return { + objectId: 'uid2', + username: 'username', + sessionToken: '123abc', + fieldToBeDeleted: 'This field is returned in the first login but not the second', + }; + }, + ajax() {}, + }); + + const user1 = await ParseUser.logIn('username', 'password'); + expect(user1.isCurrent()).toBe(true); + expect(user1.id).toBe('uid2'); + expect(user1.get('username')).toBe('username'); + expect(user1.get('fieldToBeDeleted')).toBe( + 'This field is returned in the first login but not the second' + ); + + CoreManager.setRESTController({ + async request() { + return {}; + }, + ajax() {}, + }); + + await ParseUser.logOut(); + expect(ParseUser.current()).toBe(null); + + CoreManager.setRESTController({ + async request(method, path) { + expect(path).toBe('login'); + + return { + objectId: 'uid2', + username: 'username', + sessionToken: '123abc', + // We assume fieldToBeDeleted was deleted while user was logged out + }; + }, + ajax() {}, + }); + + const user2 = await ParseUser.logIn('username', 'password'); + expect(user2.isCurrent()).toBe(true); + expect(user2.id).toBe('uid2'); + expect(user2.get('username')).toBe('username'); + expect(user2.get('fieldToBeDeleted')).toBe(undefined); // Failing test PR #1442 + }); + it('can retreive a user with sessionToken (me)', async () => { ParseUser.disableUnsafeCurrentUser(); ParseUser._clearCache(); diff --git a/src/decode.js b/src/decode.js index 42512fba1..b29bb8ebd 100644 --- a/src/decode.js +++ b/src/decode.js @@ -34,7 +34,7 @@ export default function decode(value: any): any { return ParseObject.fromJSON(value); } if (value.__type === 'Object' && value.className) { - return ParseObject.fromJSON(value); + return ParseObject.fromJSON(value, true); } if (value.__type === 'Relation') { // The parent and key fields will be populated by the parent