From e23cd9d30927d905aa7a8ce4d3fe358cccd9b1e3 Mon Sep 17 00:00:00 2001 From: Anthony Sneed Date: Thu, 14 Sep 2017 19:00:22 -0500 Subject: [PATCH] - Added trackable-entity.spec.ts. - Updated map and set specs. - Updated ReadMe. --- README.md | 65 ++++++++++++++++++++++++++++++- src/trackable-entity.spec.ts | 73 +++++++++++++++++++++++++++++++++++ src/trackable-map.spec.ts | 12 +++--- src/trackable-set.spec.ts | 74 ++++++++++++++++++------------------ 4 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 src/trackable-entity.spec.ts diff --git a/README.md b/README.md index c09ecbb..114ba95 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ Docs: Sample application: +> Note: You must change the TypeScript compiler target to "es2015" in **ts.config.json**. +> - Apps using trackable-entities can support most modern browsers (Chrome, Firefox, Safari, Edge, etc), but they will not be able to support legacy browsers (Internet Explorer). + ## Setup Install **trackable-entities** as a runtime dependency from npm. @@ -19,7 +22,7 @@ npm i --save trackable-entities ## Usage -To track property changes on an object, create a class that extends `TrackableEntity`. Then add a `constructor` that returns `super.proxify(this)`. For example +To track property changes on an object, create a class that extends `TrackableEntity`. Then add a `constructor` that returns `super.proxify(this)`. For example: ```js export class Product extends TrackableEntity { @@ -34,4 +37,62 @@ export class Product extends TrackableEntity { } ``` -Then set the `tracking` property to `true`. Modifying property values will change the `trackingState` from `Unchanged` to `Modified` and will populate the `modifiedProperties` collection with the names of properties that were changed, so that partial updates (PATCH) may be performed by an API that knows how to apply changes to a persistence store such as a database. \ No newline at end of file +Then set the `tracking` property to `true`. Modifying property values will change the `trackingState` from `Unchanged` to `Modified` and will populate the `modifiedProperties` collection with the names of properties that were changed, so that partial updates (PATCH) may be performed by an API that knows how to apply changes to a persistence store such as a database. + +```js +// Product extends TrackableEntity +product = new Product(1, 'Bacon', 1); + +// Turn on change tracking +product.tracking = true; + +// Modify a property +product.productName = 'Peas'; + +// Tracking state is set to Modified +expect(product.trackingState).toEqual(TrackingState.Modified); + +// Name of modified properties are tracked +expect(product.modifiedProperties.has('productName')).toBeTruthy(); +``` + +There are two collections that support change tracking: `TrackableSet` which extends `Set`, and `TrackableMap` which extends `Map`. When items are added their `trackingState` is set to `Added`, and when items are deleted their `trackingState` is set to `Deleted`. + +```js +// Create a new TrackableSet of products +const products = [ + new Product(1, 'Bacon', 1), + new Product(2, 'Lettuce', 2), + new Product(3, 'Tomatoes', 3), +]; +const trackableProducts = new TrackableSet(...products); + +// Turn on change tracking +product.tracking = true; + +// Add an entity +const addedProduct = new Product(4, 'Carrots', 4); +trackableProducts.add(addedProduct); + +// Tracking state is set to Added +expect(addedProduct.trackingState).toEqual(TrackingState.Added); + +// Remove an entity +const removedProduct = new Product(4, 'Carrots', 4); +trackableProducts.delete(removedProduct); + +// Tracking state is set to Added +expect(removedProduct.trackingState).toEqual(TrackingState.Deleted); +``` + +## Persistence + +Once the tracking state of entities have been set, you can pass them to a Web API where they can be persisted to a data store. [Trackable Entities](http://trackableentities.github.io) has a server-side [NuGet package](https://www.nuget.org/packages/TrackableEntities.EF.6) for persisting changes to a relational data store using [Entity Framework](https://docs.microsoft.com/en-us/ef/). The current version supports the full .NET Framework with EF 6, and a future version will support [.NET Core](https://www.microsoft.com/net/core) and EF Core as a [NetStandard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) library. + +## Roadmap + +We are currently developing the full API for **trackable-entities**. See the project [roadmap](https://github.com/TrackableEntities/trackable-entities-js/wiki/Roadmap) for more information. + +## Contributing + +If you'd like to help with this project, please contact @tonysneed via email (tony@tonysneed.com) or twitter (@tonysneed). \ No newline at end of file diff --git a/src/trackable-entity.spec.ts b/src/trackable-entity.spec.ts new file mode 100644 index 0000000..d9f7638 --- /dev/null +++ b/src/trackable-entity.spec.ts @@ -0,0 +1,73 @@ +import { Product } from './models/product.spec'; +import { TrackableSet } from './trackable-set'; +import { TrackingState } from './tracking-state'; + +describe('TrackableEntity', () => { + + let product: Product; + + beforeEach(() => { + product = new Product(1, 'Bacon', 1); + }); + + it('should be created', () => { + expect(product).toBeTruthy(); + }); + + it('should set entity TrackingState to Modified when tracking', (done) => { + + // Arrange + product.tracking = true; + + // Act + product.productName = 'Peas'; + + // Assert + expect(product.trackingState).toEqual(TrackingState.Modified); + done(); + }); + + it('should not set entity TrackingState to Modified when tracking but not changed', (done) => { + + // Arrange + product.tracking = true; + + // Act + product.productName = product.productName; + + // Assert + expect(product.trackingState).toEqual(TrackingState.Unchanged); + done(); + }); + + it('should not set entity TrackingState to Modified when not tracking', (done) => { + + // Arrange + product.tracking = true; + + // Act + product.tracking = false; + product.productName = 'Peas'; + + // Assert + expect(product.trackingState).toEqual(TrackingState.Unchanged); + done(); + }); + + it('should add to entity ModifiedProperties when tracking', (done) => { + + // Arrange + product.tracking = true; + + // Act + product.productName = 'Peas'; + product.unitPrice = 5; + + // Assert + expect(product.trackingState).toEqual(TrackingState.Modified); + expect(product.modifiedProperties.size).toEqual(2); + expect(product.modifiedProperties.has('productName')).toBeTruthy(); + expect(product.modifiedProperties.has('unitPrice')).toBeTruthy(); + done(); + }); +}); diff --git a/src/trackable-map.spec.ts b/src/trackable-map.spec.ts index f9a64fb..df46855 100644 --- a/src/trackable-map.spec.ts +++ b/src/trackable-map.spec.ts @@ -27,13 +27,13 @@ describe('TrackableMap', () => { // Arrange trackableMap.tracking = true; - const food = new Product(4, 'Carrots', 4); + const product = new Product(4, 'Carrots', 4); // Act - trackableMap.add(food.productName, food); + trackableMap.add(product.productName, product); // Assert - expect(food.trackingState).toEqual(TrackingState.Added); + expect(product.trackingState).toEqual(TrackingState.Added); done(); }); @@ -41,14 +41,14 @@ describe('TrackableMap', () => { // Arrange trackableMap.tracking = true; - const food = new Product(5, 'Carrots', 4); + const product = new Product(5, 'Carrots', 4); // Act trackableMap.tracking = false; - trackableMap.add(food.productName, food); + trackableMap.add(product.productName, product); // Assert - expect(food.trackingState).toEqual(TrackingState.Unchanged); + expect(product.trackingState).toEqual(TrackingState.Unchanged); done(); }); diff --git a/src/trackable-set.spec.ts b/src/trackable-set.spec.ts index 9a79d4a..7f08abd 100644 --- a/src/trackable-set.spec.ts +++ b/src/trackable-set.spec.ts @@ -7,12 +7,12 @@ describe('TrackableSet', () => { let trackableSet: TrackableSet; beforeEach(() => { - const foods = [ + const products = [ new Product(1, 'Bacon', 1), new Product(2, 'Lettuce', 2), new Product(3, 'Tomatoes', 3), ]; - trackableSet = new TrackableSet(...foods); + trackableSet = new TrackableSet(...products); }); it('should be created', () => { @@ -27,13 +27,13 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = new Product(4, 'Carrots', 4); + const product = new Product(4, 'Carrots', 4); // Act - trackableSet.add(food); + trackableSet.add(product); // Assert - expect(food.trackingState).toEqual(TrackingState.Added); + expect(product.trackingState).toEqual(TrackingState.Added); done(); }); @@ -41,14 +41,14 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = new Product(4, 'Carrots', 4); + const product = new Product(4, 'Carrots', 4); // Act trackableSet.tracking = false; - trackableSet.add(food); + trackableSet.add(product); // Assert - expect(food.trackingState).toEqual(TrackingState.Unchanged); + expect(product.trackingState).toEqual(TrackingState.Unchanged); done(); }); @@ -56,13 +56,13 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act - trackableSet.delete(food); + trackableSet.delete(product); // Assert - expect(food.trackingState).toEqual(TrackingState.Deleted); + expect(product.trackingState).toEqual(TrackingState.Deleted); done(); }); @@ -70,14 +70,14 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act trackableSet.tracking = false; - trackableSet.delete(food); + trackableSet.delete(product); // Assert - expect(food.trackingState).toEqual(TrackingState.Unchanged); + expect(product.trackingState).toEqual(TrackingState.Unchanged); done(); }); @@ -85,15 +85,15 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act - trackableSet.delete(food); + trackableSet.delete(product); // Assert - expect(food.trackingState).toEqual(TrackingState.Deleted); + expect(product.trackingState).toEqual(TrackingState.Deleted); const deletedEntities = [...(trackableSet as any).deletedEntities]; - expect(deletedEntities[0]).toBe(food); + expect(deletedEntities[0]).toBe(product); done(); }); @@ -101,14 +101,14 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act - trackableSet.delete(food); + trackableSet.delete(product); trackableSet.tracking = false; // Assert - expect(food.trackingState).toEqual(TrackingState.Deleted); + expect(product.trackingState).toEqual(TrackingState.Deleted); const deletedEntities = [...(trackableSet as any).deletedEntities]; expect(deletedEntities.length).toEqual(0); done(); @@ -118,13 +118,13 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act - food.productName = 'Peas'; + product.productName = 'Peas'; // Assert - expect(food.trackingState).toEqual(TrackingState.Modified); + expect(product.trackingState).toEqual(TrackingState.Modified); done(); }); @@ -132,13 +132,13 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act - food.productName = food.productName; + product.productName = product.productName; // Assert - expect(food.trackingState).toEqual(TrackingState.Unchanged); + expect(product.trackingState).toEqual(TrackingState.Unchanged); done(); }); @@ -146,14 +146,14 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act trackableSet.tracking = false; - food.productName = 'Peas'; + product.productName = 'Peas'; // Assert - expect(food.trackingState).toEqual(TrackingState.Unchanged); + expect(product.trackingState).toEqual(TrackingState.Unchanged); done(); }); @@ -161,17 +161,17 @@ describe('TrackableSet', () => { // Arrange trackableSet.tracking = true; - const food = [...trackableSet][0]; + const product = [...trackableSet][0]; // Act - food.productName = 'Peas'; - food.unitPrice = 5; + product.productName = 'Peas'; + product.unitPrice = 5; // Assert - expect(food.trackingState).toEqual(TrackingState.Modified); - expect(food.modifiedProperties.size).toEqual(2); - expect(food.modifiedProperties.has('productName')).toBeTruthy(); - expect(food.modifiedProperties.has('unitPrice')).toBeTruthy(); + expect(product.trackingState).toEqual(TrackingState.Modified); + expect(product.modifiedProperties.size).toEqual(2); + expect(product.modifiedProperties.has('productName')).toBeTruthy(); + expect(product.modifiedProperties.has('unitPrice')).toBeTruthy(); done(); }); });