From 11b74bd339e2b737f5f7e29c460103db692dc1cd Mon Sep 17 00:00:00 2001 From: "Maier, Martin" Date: Mon, 2 Jul 2018 20:55:02 +0200 Subject: [PATCH] * improved test classes * renamed getProperty --> property * renamed replaceProperty --> replaceValueOf * renamed andDo --> withCloneAndDo --- package.json | 2 +- src/replicator.ts | 28 ++++----- src/tests/replicator.deprecated.spec.ts | 64 +++++++++---------- src/tests/replicator.spec.ts | 83 ++++++++++++------------- src/tests/testobjects.ts | 63 ++++++++----------- 5 files changed, 114 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index 61ad515..d378537 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typescript-immutable-helper", - "version": "0.5.0", + "version": "0.6.0", "description": "Helpers for handling immutable objects with typescript", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/replicator.ts b/src/replicator.ts index 44a11f4..264eda7 100644 --- a/src/replicator.ts +++ b/src/replicator.ts @@ -2,7 +2,7 @@ import * as _ from 'lodash' import {deepFreeze, isDeepFrozen} from './deepFreeze' /** - * Class that helps to replicate a new object by encapsulating a deep copy of the source object + * Class that helps to replaceValueOf a new object by encapsulating a deep copy of the source object * If input object is frozen by (@link Object.freeze()} or {@link deepFreeze} then the replica will be produced frozen * freeze in --> deep freeze out * Warns if source object is just frozen, not deep frozen @@ -29,30 +29,30 @@ export class ReplicationBuilder { /** * @deprecated since 0.4.1 - * use getProperty instead + * use property instead */ public getChild(childNode: K): ReplicaChildOperator { - return this.getProperty(childNode); + return this.property(childNode); } /** switch to child node * @param {K} childNode of the root node * @returns {ReplicaChildOperator} operator of child node **/ - public getProperty(childNode: K): ReplicaChildOperator { + public property(childNode: K): ReplicaChildOperator { let node = this.replica[childNode]; return new ReplicaChildOperator((() => this.build()), this.replica, node, childNode) } /** * @deprecated since 0.4.1 - * use replaceProperty instead + * use replaceValueOf instead */ public modify(childNode: K): PropertyModifier, T[K]> { - return this.replaceProperty(childNode); + return this.replaceValueOf(childNode); } - public replaceProperty(childNode: K): PropertyModifier, T[K]> { + public replaceValueOf(childNode: K): PropertyModifier, T[K]> { return new PropertyModifier, T[K]>(this, childNode, this.replica) } @@ -103,10 +103,10 @@ export class ReplicaChildOperator { /** * @deprecated since 0.4.1 - * use getProperty instead + * use property instead */ getChild(childNode: K): ReplicaChildOperator { - return this.getProperty(childNode); + return this.property(childNode); } @@ -114,20 +114,20 @@ export class ReplicaChildOperator { * @param {K} childNode of this node * @returns {ReplicaChildOperator} traversable child node **/ - getProperty(childNode: K): ReplicaChildOperator { + property(childNode: K): ReplicaChildOperator { let branch = this.node[childNode]; return new ReplicaChildOperator(this.buildFunction, this.replica, branch, this.relativePath + '.' + childNode) } /** * @deprecated since 0.4.1 - * use replaceProperty instead + * use replaceValueOf instead */ modify(childNode: K): PropertyModifier, T[K]> { - return this.replaceProperty(childNode); + return this.replaceValueOf(childNode); } - replaceProperty(childNode: K): PropertyModifier, T[K]> { + replaceValueOf(childNode: K): PropertyModifier, T[K]> { return new PropertyModifier, T[K]>(this, this.relativePath + '.' + childNode, this.replica) } @@ -201,7 +201,7 @@ export class PropertyModifier { * @param {(VT) => void} executeOnCloneFunction function that is executed * @returns {PT} */ - andDo(executeOnCloneFunction: (VT) => void): PT { + withCloneAndDo(executeOnCloneFunction: (VT) => void): PT { let currentvalue = _.get(this.replica, this.relativePathToRoot); executeOnCloneFunction(currentvalue); return this.parent; diff --git a/src/tests/replicator.deprecated.spec.ts b/src/tests/replicator.deprecated.spec.ts index 658fb43..4e53f3c 100644 --- a/src/tests/replicator.deprecated.spec.ts +++ b/src/tests/replicator.deprecated.spec.ts @@ -1,76 +1,76 @@ import {expect} from 'chai' -import {ClassorientedTeststate, SimpleTeststate, SubTypeA} from './testobjects' +import {Concert, Rockband, SimpleBand} from './testobjects' import {ReplicationBuilder} from '../replicator' import {deepFreeze, isDeepFrozen} from '../deepFreeze' describe('Deprecated API of ReplicationBuilder', () => { it('Inputstate must not be modified, output must be modified', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('subTypeA').getChild('subTypeB').modify('subTypeBAttribute').to('Test').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('band').getChild('homeland').modify('name').to('Test').build(); - expect(rootState.subTypeA.subTypeB.subTypeBAttribute).to.null; - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBAttribute).to.equal('Test') + expect(rootState.band.homeland.name).to.null; + expect(manipulatedRoot.band.homeland.name).to.equal('Test') }); it('Inputstate must not be modified, output must be modified', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('subTypeA').getChild('subTypeB').modify('subTypeBAttribute').to('Test').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('band').getChild('homeland').modify('name').to('Test').build(); - expect(rootState.subTypeA.subTypeB.subTypeBAttribute).to.null; - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBAttribute).to.equal('Test') + expect(rootState.band.homeland.name).to.null; + expect(manipulatedRoot.band.homeland.name).to.equal('Test') }); it('Inputstate must not be modified, output node must be deleted', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).delete('subTypeA').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).delete('band').build(); - expect(rootState.subTypeA).to.exist; - expect(manipulatedRoot.subTypeA).to.not.exist + expect(rootState.band).to.exist; + expect(manipulatedRoot.band).to.not.exist }); it('Inputstate must not be modified, output child node must be deleted', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('subTypeA').delete('subTypeB').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('band').delete('homeland').build(); - expect(rootState.subTypeA.subTypeB).to.exist; - expect(manipulatedRoot.subTypeA.subTypeB).to.not.exist + expect(rootState.band.homeland).to.exist; + expect(manipulatedRoot.band.homeland).to.not.exist }); it('to untyped structure: Inputstate must not be modified, output must be modified', () => { - let rootState = SimpleTeststate; - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('subTypeB').modify('subtypeBAttribute').to('Test').build(); + let rootState = SimpleBand; + let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('genre').modify('name').to('Test').build(); - expect(rootState.subTypeB.subtypeBAttribute).to.equal('initial'); - expect(manipulatedRoot.subTypeB.subtypeBAttribute).to.equal('Test') + expect(rootState.genre.name).to.equal('initial'); + expect(manipulatedRoot.genre.name).to.equal('Test') }); it('if input state is deep frozen --> output state must be deep frozen', () => { - let rootState = new ClassorientedTeststate(); + let rootState = new Concert(); deepFreeze(rootState); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('subTypeA').getChild('subTypeB').modify('subTypeBAttribute').to('Test').build(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('band').getChild('homeland').modify('name').to('Test').build(); - expect(rootState.subTypeA.subTypeB.subTypeBAttribute).to.null; - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBAttribute).to.equal('Test'); + expect(rootState.band.homeland.name).to.null; + expect(manipulatedRoot.band.homeland.name).to.equal('Test'); expect(isDeepFrozen(manipulatedRoot)).true }); it('redux like root state --> if input state is deep frozen --> output state must be deep frozen', () => { let rootState = { - subTypeA: new SubTypeA() + band: new Rockband() }; deepFreeze(rootState); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('subTypeA').getChild('subTypeB').modify('subTypeBArray') - .by((oldArray) => [...oldArray, 'Test']).build(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('band').modify('members') + .by((oldMembers) => [...oldMembers, 'Test']).build(); - expect(rootState.subTypeA.subTypeB.subTypeBArray.length).to.equal(0); - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBArray[0]).to.equal('Test'); + expect(rootState.band.members.length).to.equal(0); + expect(manipulatedRoot.band.members[0]).to.equal('Test'); expect(isDeepFrozen(manipulatedRoot)).true }); it('if input state is NOT frozen --> output state must NOT be frozen', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('subTypeA').getChild('subTypeB').modify('subTypeBAttribute').to('Test').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).getChild('band').getChild('homeland').modify('name').to('Test').build(); expect(Object.isFrozen(manipulatedRoot)).false }); diff --git a/src/tests/replicator.spec.ts b/src/tests/replicator.spec.ts index e4cd09b..70ab4f8 100644 --- a/src/tests/replicator.spec.ts +++ b/src/tests/replicator.spec.ts @@ -1,86 +1,85 @@ import {expect} from 'chai' -import {ClassorientedTeststate, ObjectArray, SimpleTeststate, SubTypeA} from './testobjects' +import {Concert, ObjectArray, Rockband, SimpleBand} from './testobjects' import {ReplicationBuilder} from '../replicator' import {deepFreeze, isDeepFrozen} from '../deepFreeze' describe('ReplicationBuilder', () => { it('should clone a property and execute function on the clone ', function () { - let rootState = new ClassorientedTeststate(); - let someNewValue = 'someNewValue'; - let manipulatedRoot = ReplicationBuilder.forObject(rootState).replaceProperty('subTypeA').andDo((clonedSubTypeA) => { - clonedSubTypeA.setSubTypeAAttribute(someNewValue) + let concert = new Concert(); + let newBandName = 'someNewName'; + let manipulatedRoot = ReplicationBuilder.forObject(concert).replaceValueOf('band').withCloneAndDo((band) => { + band.changeNameTo(newBandName) }).build(); - expect(manipulatedRoot.subTypeA.subTypeAAttribute).to.equal(someNewValue); + expect(manipulatedRoot.band.name).to.equal(newBandName); }); it('Inputstate must not be modified, output must be modified', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getProperty('subTypeA').getProperty('subTypeB').replaceProperty('subTypeBAttribute').with('Test').build(); + let concert = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(concert).property('band').property('homeland').replaceValueOf('name').with('Spain').build(); - expect(rootState.subTypeA.subTypeB.subTypeBAttribute).to.null; - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBAttribute).to.equal('Test') + expect(concert.band.homeland.name).to.null; + expect(manipulatedRoot.band.homeland.name).to.equal('Spain') }); it('Inputstate must not be modified, output must be modified', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getProperty('subTypeA').getProperty('subTypeB').replaceProperty('subTypeBAttribute').with('Test').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).property('band').property('homeland').replaceValueOf('name').with('Russia').build(); - expect(rootState.subTypeA.subTypeB.subTypeBAttribute).to.null; - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBAttribute).to.equal('Test') + expect(rootState.band.homeland.name).to.null; + expect(manipulatedRoot.band.homeland.name).to.equal('Russia') }); it('Inputstate must not be modified, output node must be deleted', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).removeProperty('subTypeA').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).removeProperty('band').build(); - expect(rootState.subTypeA).to.exist; - expect(manipulatedRoot.subTypeA).to.not.exist + expect(rootState.band).to.exist; + expect(manipulatedRoot.band).to.not.exist }); it('Inputstate must not be modified, output child node must be deleted', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getProperty('subTypeA').removeProperty('subTypeB').build(); + let rootState = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(rootState).property('band').removeProperty('homeland').build(); - expect(rootState.subTypeA.subTypeB).to.exist; - expect(manipulatedRoot.subTypeA.subTypeB).to.not.exist + expect(rootState.band.homeland).to.exist; + expect(manipulatedRoot.band.homeland).to.not.exist }); it('with untyped structure: Inputstate must not be modified, output must be modified', () => { - let rootState = SimpleTeststate; - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getProperty('subTypeB').replaceProperty('subtypeBAttribute').with('Test').build(); + let simpleBand = SimpleBand; + let manipulatedRoot = ReplicationBuilder.forObject(simpleBand).property('genre').replaceValueOf('name').with('Test').build(); - expect(rootState.subTypeB.subtypeBAttribute).to.equal('initial'); - expect(manipulatedRoot.subTypeB.subtypeBAttribute).to.equal('Test') + expect(simpleBand.genre.name).to.equal('initial'); + expect(manipulatedRoot.genre.name).to.equal('Test') }); it('if input state is deep frozen --> output state must be deep frozen', () => { - let rootState = new ClassorientedTeststate(); - deepFreeze(rootState); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getProperty('subTypeA').getProperty('subTypeB').replaceProperty('subTypeBAttribute').with('Test').build(); + let concert = new Concert(); + deepFreeze(concert); + let manipulatedRoot = ReplicationBuilder.forObject(concert).property('band').property('homeland').replaceValueOf('name').with('Test').build(); - expect(rootState.subTypeA.subTypeB.subTypeBAttribute).to.null; - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBAttribute).to.equal('Test'); + expect(concert.band.homeland.name).to.null; + expect(manipulatedRoot.band.homeland.name).to.equal('Test'); expect(isDeepFrozen(manipulatedRoot)).true }); it('redux like root state --> if input state is deep frozen --> output state must be deep frozen', () => { - let rootState = { - subTypeA: new SubTypeA() + let festival = { + band: new Rockband() }; - deepFreeze(rootState); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getProperty('subTypeA').getProperty('subTypeB').replaceProperty('subTypeBArray') - .by((oldArray) => [...oldArray, 'Test']).build(); + deepFreeze(festival); + let newFestival = ReplicationBuilder.forObject(festival).property('band').replaceValueOf('members') + .by((oldMembers) => [...oldMembers, 'NewRocker']).build(); - expect(rootState.subTypeA.subTypeB.subTypeBArray.length).to.equal(0); - expect(manipulatedRoot.subTypeA.subTypeB.subTypeBArray[0]).to.equal('Test'); - expect(isDeepFrozen(manipulatedRoot)).true + expect(festival.band.members.length).to.equal(0); + expect(newFestival.band.members[0]).to.equal('NewRocker'); + expect(isDeepFrozen(newFestival)).true }); it('if input state is NOT frozen --> output state must NOT be frozen', () => { - let rootState = new ClassorientedTeststate(); - let manipulatedRoot = ReplicationBuilder.forObject(rootState).getProperty('subTypeA').getProperty('subTypeB').replaceProperty('subTypeBAttribute').with('Test').build(); - + let concert = new Concert(); + let manipulatedRoot = ReplicationBuilder.forObject(concert).property('band').property('homeland').replaceValueOf('name').with('USA').build(); expect(Object.isFrozen(manipulatedRoot)).false }); diff --git a/src/tests/testobjects.ts b/src/tests/testobjects.ts index 8930733..c0c828f 100644 --- a/src/tests/testobjects.ts +++ b/src/tests/testobjects.ts @@ -1,49 +1,38 @@ -class ParentTypeA { - parentAttribute:string = "i am your father"; +interface Band { + name: string; + members: string[]; + homeland: Country; } -interface ISubTypeA{ - subTypeAAttribute: string; - subTypeAArray: string[]; - subTypeB: SubTypeB; -} - -export class SubTypeA extends ParentTypeA implements ISubTypeA{ - subTypeAAttribute: string = null; - subTypeAArray: string[] = []; - subTypeB: SubTypeB = new SubTypeB(); - - setSubTypeAAttribute(newValue: string) { - this.subTypeAAttribute = newValue; - } +export class Rockband implements Band { + name: string = null; + members: string[] = []; + homeland: Country = new Country(); - doNothingFunction() { - // do nothing - let test: number = 0; - test = 1 + changeNameTo(newValue: string) { + this.name = newValue; } } -class SubTypeB { - subTypeBAttribute: string = null; - subTypeBArray: string[] = []; - subTypeB: SubTypeC = new SubTypeC() +class Country { + name: string = null; + language: Language = new Language() } -class SubTypeC { - subTypeCAttribute: string = null; - subTypeCArray: string[] = [] +class Language { + name: String; } -export class ClassorientedTeststate { - subTypeA: SubTypeA = new SubTypeA() +export class Concert { + name: String; + band: Rockband = new Rockband() } -export const SimpleTeststate = { - subTypeAAttribute: 'initial', - subTypeAArray: ['initial'], - subTypeB: { - subtypeBAttribute: 'initial' +export const SimpleBand = { + name: 'initial', + members: ['initial'], + genre: { + name: 'initial' } }; @@ -65,9 +54,9 @@ export class SomeObject { export class ObjectArray { array: SomeObject[] = []; - constructor(count: number){ - for(let i = 0; i < count; i++){ - this.array[i] = new SomeObject("AttributeA"+ i,"AttributeB"+ i,"AttributeC"+ i,"AttributeD"+ i) + constructor(count: number) { + for (let i = 0; i < count; i++) { + this.array[i] = new SomeObject('AttributeA' + i, 'AttributeB' + i, 'AttributeC' + i, 'AttributeD' + i) } } } \ No newline at end of file