diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index f253602134..a005c30e00 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -186,7 +186,7 @@ ObjectAPI.prototype.get = function (identifier, abortSignal) { identifier = utils.parseKeyString(identifier); let dirtyObject; if (this.isTransactionActive()) { - dirtyObject = this.transaction.getDirtyObject(keystring); + dirtyObject = this.transaction.getDirtyObject(identifier); } if (dirtyObject) { diff --git a/src/api/objects/ObjectAPISpec.js b/src/api/objects/ObjectAPISpec.js index 8c4f6d7830..041b4a1ee8 100644 --- a/src/api/objects/ObjectAPISpec.js +++ b/src/api/objects/ObjectAPISpec.js @@ -39,6 +39,7 @@ describe("The Object API", () => { type: "test-type" }; }); + describe("The save function", () => { it("Rejects if no provider available", () => { let rejected = false; @@ -332,6 +333,48 @@ describe("The Object API", () => { }); }); }); + + describe("transactions", () => { + beforeEach(() => { + spyOn(openmct.editor, 'isEditing').and.returnValue(true); + }); + + it('there is no active transaction', () => { + expect(objectAPI.isTransactionActive()).toBe(false); + }); + + it('start a transaction', () => { + objectAPI.startTransaction(); + expect(objectAPI.isTransactionActive()).toBe(true); + }); + + it('has active transaction', () => { + objectAPI.startTransaction(); + const activeTransaction = objectAPI.getActiveTransaction(); + expect(activeTransaction).not.toBe(null); + }); + + it('end a transaction', () => { + objectAPI.endTransaction(); + expect(objectAPI.isTransactionActive()).toBe(false); + }); + + it('returns dirty object on get', (done) => { + spyOn(objectAPI, 'supportsMutation').and.returnValue(true); + + objectAPI.startTransaction(); + objectAPI.mutate(mockDomainObject, 'name', 'dirty object'); + + const dirtyObject = objectAPI.transaction.getDirtyObject(mockDomainObject.identifier); + + objectAPI.get(mockDomainObject.identifier) + .then(object => { + const areEqual = JSON.stringify(object) === JSON.stringify(dirtyObject); + expect(areEqual).toBe(true); + }) + .finally(done); + }); + }); }); function hasOwnProperty(object, property) { diff --git a/src/api/objects/Transaction.js b/src/api/objects/Transaction.js index b544c112a8..0e5b1636c7 100644 --- a/src/api/objects/Transaction.js +++ b/src/api/objects/Transaction.js @@ -47,18 +47,19 @@ export default class Transaction { createDirtyObjectPromise(object, action) { return new Promise((resolve, reject) => { action(object) - .then(resolve) - .catch(reject) - .finally(() => { + .then((success) => { this.dirtyObjects.delete(object); - }); + resolve(success); + }) + .catch(reject); }); } - getDirtyObject(keystring) { + getDirtyObject(identifier) { let dirtyObject; this.dirtyObjects.forEach(object => { - if (this.objectAPI.makeKeyString(object.identifier) === keystring) { + const areIdsEqual = this.objectAPI.areIdsEqual(object.identifier, identifier); + if (areIdsEqual) { dirtyObject = object; } }); diff --git a/src/api/objects/TransactionSpec.js b/src/api/objects/TransactionSpec.js new file mode 100644 index 0000000000..7ed9bee4b6 --- /dev/null +++ b/src/api/objects/TransactionSpec.js @@ -0,0 +1,102 @@ +import Transaction from "./Transaction"; +import utils from 'objectUtils'; + +let openmct = {}; +let objectAPI; +let transaction; + +describe("Transaction Class", () => { + beforeEach(() => { + objectAPI = { + makeKeyString: (identifier) => utils.makeKeyString(identifier), + save: (object) => object, + mutate: (object, prop, value) => { + object[prop] = value; + + return object; + }, + refresh: (object) => Promise.resolve(object) + }; + + transaction = new Transaction(objectAPI); + + openmct.editor = { + isEditing: () => true + }; + }); + + it('has no dirty objects', () => { + expect(transaction.dirtyObjects.size).toEqual(0); + }); + + it('add(), adds object to dirtyObjects', () => { + const mockDomainObjects = createMockDomainObjects(); + transaction.add(mockDomainObjects[0]); + expect(transaction.dirtyObjects.size).toEqual(1); + }); + + it('cancel(), clears all dirtyObjects', (done) => { + const mockDomainObjects = createMockDomainObjects(3); + mockDomainObjects.forEach(transaction.add.bind(transaction)); + + expect(transaction.dirtyObjects.size).toEqual(3); + + transaction.cancel() + .then(success => { + expect(transaction.dirtyObjects.size).toEqual(0); + }).finally(done); + }); + + it('commit(), saves all dirtyObjects', (done) => { + const mockDomainObjects = createMockDomainObjects(3); + mockDomainObjects.forEach(transaction.add.bind(transaction)); + + expect(transaction.dirtyObjects.size).toEqual(3); + spyOn(objectAPI, 'save'); + transaction.commit() + .then(success => { + expect(transaction.dirtyObjects.size).toEqual(0); + expect(objectAPI.save.calls.count()).toEqual(3); + }).finally(done); + }); + + it('getDirtyObject(), returns correct dirtyObject', () => { + const mockDomainObjects = createMockDomainObjects(); + transaction.add(mockDomainObjects[0]); + + expect(transaction.dirtyObjects.size).toEqual(1); + const dirtyObject = transaction.getDirtyObject(mockDomainObjects[0].identifier); + + expect(dirtyObject).toEqual(mockDomainObjects[0]); + }); + + it('getDirtyObject(), returns empty dirtyObject for no active transaction', () => { + const mockDomainObjects = createMockDomainObjects(); + + expect(transaction.dirtyObjects.size).toEqual(0); + const dirtyObject = transaction.getDirtyObject(mockDomainObjects[0].identifier); + + expect(dirtyObject).toEqual(undefined); + }); +}); + +function createMockDomainObjects(size = 1) { + const objects = []; + + while (size > 0) { + const mockDomainObject = { + identifier: { + namespace: 'test-namespace', + key: `test-key-${size}` + }, + name: `test object ${size}`, + type: 'test-type' + }; + + objects.push(mockDomainObject); + + size--; + } + + return objects; +} diff --git a/src/ui/router/ApplicationRouterSpec.js b/src/ui/router/ApplicationRouterSpec.js index f6b2072122..d356d18543 100644 --- a/src/ui/router/ApplicationRouterSpec.js +++ b/src/ui/router/ApplicationRouterSpec.js @@ -49,9 +49,10 @@ describe('Application router utility functions', () => { }); it('The setSearchParam function sets an individual search parameter in the window location hash', () => { - openmct.router.setSearchParam('testParam', 'testValue'); + openmct.router.setSearchParam('testParam1', 'testValue1'); + const searchParams = openmct.router.getAllSearchParams(); - expect(searchParams.get('testParam')).toBe('testValue'); + expect(searchParams.get('testParam1')).toBe('testValue1'); }); it('The deleteSearchParam function deletes an individual search paramater in the window location hash', () => {