From b2966f1fbdb679b0875b624aafcd34127b35441d Mon Sep 17 00:00:00 2001 From: Joe D Date: Sun, 11 Oct 2015 09:23:37 -0400 Subject: [PATCH 1/4] Refer to current _map, not most recent _map --- data/observable/observable.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/data/observable/observable.ts b/data/observable/observable.ts index 565fc58e7..1dff4db84 100644 --- a/data/observable/observable.ts +++ b/data/observable/observable.ts @@ -15,16 +15,15 @@ export class Observable implements definition.Observable { constructor(json?: any) { if (json) { this._map = new Map(); - var that = this; var definePropertyFunc = function definePropertyFunc(propertyName) { Object.defineProperty(Observable.prototype, propertyName, { get: function () { - return that._map.get(propertyName); + return this._map.get(propertyName); }, set: function (value) { - that._map.set(propertyName, value); - that.notify(that._createPropertyChangeData(propertyName, value)); + this._map.set(propertyName, value); + this.notify(this._createPropertyChangeData(propertyName, value)); }, enumerable: true, configurable: true From 1d1153e85b7425f19471e700c2a957aef416ece9 Mon Sep 17 00:00:00 2001 From: Joe D Date: Mon, 12 Oct 2015 11:23:36 -0400 Subject: [PATCH 2/4] Added test Observable_shouldDistinguishSeparateObjects --- apps/tests/observable-tests.ts | 26 ++++++++++++++++++++++++++ apps/tests/testRunner.ts | 1 + 2 files changed, 27 insertions(+) create mode 100644 apps/tests/observable-tests.ts diff --git a/apps/tests/observable-tests.ts b/apps/tests/observable-tests.ts new file mode 100644 index 000000000..39bf58c50 --- /dev/null +++ b/apps/tests/observable-tests.ts @@ -0,0 +1,26 @@ +import TKUnit = require("./TKUnit"); +require("globals"); + +// +// # Observable module +// ``` JavaScript +import observableModule = require("data/observable"); +// ``` +// + +require("globals"); + +export var test_Observable_shouldDistinguishSeparateObjects = function () { + // + // ### Create two Observables from different objects. + // ``` JavaScript + var obj1 = {val: 1}; + var obj2 = {val: 2}; + var observable1 = new observableModule.Observable(obj1); + var observable2 = new observableModule.Observable(obj2); + // ``` + // + + TKUnit.assert(false, "Is this even being run"); + TKUnit.assert(observable1.get('val') === 1 && observable2.get('val') === 2, "Observable should keep separate objects separate!"); +}; diff --git a/apps/tests/testRunner.ts b/apps/tests/testRunner.ts index e74940ed8..7a60d30ca 100644 --- a/apps/tests/testRunner.ts +++ b/apps/tests/testRunner.ts @@ -43,6 +43,7 @@ allTests["APPLICATION SETTINGS"] = require("./application-settings-tests"); allTests["IMAGE SOURCE"] = require("./image-source-tests"); allTests["TIMER"] = require("./timer-tests"); allTests["COLOR"] = require("./color-tests"); +allTests["OBSERVABLE"] = require("./observable-tests"); allTests["OBSERVABLE-ARRAY"] = require("./observable-array-tests"); allTests["VIRTUAL-ARRAY"] = require("./virtual-array-tests"); allTests["OBSERVABLE"] = require("./ui/observable-tests"); From e64fa80b5620eea4cb9d22a616844372ab99aa54 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Tue, 13 Oct 2015 11:13:41 +0300 Subject: [PATCH 3/4] Extended the unit test and moved observable-tests out of the ui folder. --- CrossPlatformModules.csproj | 4 +- apps/tests/observable-tests.ts | 446 ++++++++++++++++++++++++++++-- apps/tests/testRunner.ts | 3 +- apps/tests/ui/observable-tests.ts | 407 --------------------------- 4 files changed, 433 insertions(+), 427 deletions(-) delete mode 100644 apps/tests/ui/observable-tests.ts diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 28de7d593..655640cd4 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -528,7 +528,7 @@ - + @@ -1992,7 +1992,7 @@ False - + \ No newline at end of file diff --git a/apps/tests/observable-tests.ts b/apps/tests/observable-tests.ts index 39bf58c50..7a68387e3 100644 --- a/apps/tests/observable-tests.ts +++ b/apps/tests/observable-tests.ts @@ -1,26 +1,440 @@ -import TKUnit = require("./TKUnit"); -require("globals"); - -// -// # Observable module +// +// # Observable +// Using Observable objects requires the "data/observable" module. // ``` JavaScript -import observableModule = require("data/observable"); +import observable = require("data/observable"); // ``` // -require("globals"); +import dependencyObservable = require("ui/core/dependency-observable"); +import TKUnit = require("./TKUnit"); +import types = require("utils/types"); +import proxy = require("ui/core/proxy"); -export var test_Observable_shouldDistinguishSeparateObjects = function () { - // - // ### Create two Observables from different objects. +var TESTED_NAME = "tested"; +class TestObservable extends observable.Observable { + public test() { + this._emit(TESTED_NAME); + } +} + +export var test_Observable_Constructor = function () { + // + // ### Creating an Observable // ``` JavaScript - var obj1 = {val: 1}; - var obj2 = {val: 2}; - var observable1 = new observableModule.Observable(obj1); - var observable2 = new observableModule.Observable(obj2); + var json = { + Name: "John", + Age: 34, + Married: true + }; + var person = new observable.Observable(json); + var name = person.get("Name"); + var age = person.get("Age"); + var married = person.get("Married"); + //// console.log(name + " " + age + " " + married); // Prints out "John 34 true" if uncommented. // ``` // + TKUnit.assert(name === "John", "Expected name is John"); + TKUnit.assert(age === 34, "Expected age is 34"); + TKUnit.assert(married === true, "Expected married is true"); +} - TKUnit.assert(false, "Is this even being run"); - TKUnit.assert(observable1.get('val') === 1 && observable2.get('val') === 2, "Observable should keep separate objects separate!"); +export var tests_DummyTestForCodeSnippet = function () { + // + // ### Responding to property changes + // ``` JavaScript + var person = new observable.Observable(); + person.set("Name", "John"); + person.set("Age", 34); + person.set("Married", true); + person.addEventListener(observable.Observable.propertyChangeEvent, function (pcd: observable.PropertyChangeData) { + //console.log(pcd.eventName.toString() + " " + pcd.propertyName.toString() + " " + pcd.value.toString()); + }); + person.set("Age", 35); + person.set("Married", false); + //// If uncommented, the console.log above produces the following output: + //// propertyChange Age 35 + //// propertyChange Married false + // ``` + // +} + +export var test_Observable_Members = function () { + var obj = new observable.Observable(); + TKUnit.assert(types.isDefined(obj.addEventListener), "Observable.addEventListener not defined"); + TKUnit.assert(types.isDefined(obj._createPropertyChangeData), "Observable.createPropertyChangeData not defined"); + TKUnit.assert(types.isDefined(obj._emit), "Observable.emit not defined"); + TKUnit.assert(types.isDefined(obj.get), "Observable.get not defined"); + TKUnit.assert(types.isDefined(obj.hasListeners), "Observable.hasListeners not defined"); + TKUnit.assert(types.isDefined(obj.notify), "Observable.notify not defined"); + TKUnit.assert(types.isDefined(obj.off), "Observable.off not defined"); + TKUnit.assert(types.isDefined(obj.on), "Observable.on not defined"); + TKUnit.assert(types.isDefined(obj.removeEventListener), "Observable.removeEventListener not defined"); + TKUnit.assert(types.isDefined(obj.set), "Observable.set not defined"); + TKUnit.assert(types.isDefined(obj.typeName), "Observable.typeName not defined"); +} + +export var test_Observable_UpdateAnotherPropertyWithinChangedCallback = function () { + var obj = new observable.Observable(); + + var changedCallback = function (pcd: observable.PropertyChangeData) { + if (pcd.propertyName === "name") { + pcd.object.set("test", "Changed test"); + } + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, changedCallback); + + obj.set("name", "Initial name"); + obj.set("test", "Initial test"); + + TKUnit.assert(obj.get("name") === "Initial name", "Initial value for property name is not correct!"); + TKUnit.assert(obj.get("test") === "Initial test", "Initial value for property test is not correct!"); + + obj.set("name", "Changed name"); + + TKUnit.assert(obj.get("name") === "Changed name", "Changed value for property name is not correct!"); + TKUnit.assert(obj.get("test") === "Changed test", "Changed value for property test is not correct!"); +} + +export var test_DependencyObservable_UpdateAnotherPropertyWithinChangedCallback = function () { + var obj = new dependencyObservable.DependencyObservable(); + + function onFirstPropertyChanged(data: dependencyObservable.PropertyChangeData) { + var testObj = data.object; + testObj._setValue(secondProperty, "Changed test"); + }; + + var firstProperty = new dependencyObservable.Property( + "first", + "obj", + new proxy.PropertyMetadata( + "", + dependencyObservable.PropertyMetadataSettings.None, + onFirstPropertyChanged + ) + ); + + var secondProperty = new dependencyObservable.Property( + "second", + "obj", + new proxy.PropertyMetadata( + "", + dependencyObservable.PropertyMetadataSettings.None, + null + ) + ); + + obj._setValue(firstProperty, "Initial name"); + obj._setValue(secondProperty, "Initial test"); + + TKUnit.assert(obj._getValue(firstProperty) === "Initial name", "Initial value for property name is not correct!"); + TKUnit.assert(obj._getValue(secondProperty) === "Initial test", "Initial value for property test is not correct!"); + + obj._setValue(firstProperty, "Changed name"); + + TKUnit.assert(obj._getValue(firstProperty) === "Changed name", "Changed value for property name is not correct!"); + TKUnit.assert(obj._getValue(secondProperty) === "Changed test", "Changed value for property test is not correct!"); +} + +export var test_Observable_addEventListener_SingleEvent = function () { + var obj = new observable.Observable(); + + var receivedCount = 0; + var callback = function (data: observable.PropertyChangeData) { + receivedCount++; + TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); + TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); + TKUnit.assert(data.propertyName === "testName", "PropertyChangeData.propertyName value not valid."); + TKUnit.assert(data.value === 1, "PropertyChangeData.value value not valid."); + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, callback); + obj.set("testName", 1); + TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); +} + +export var test_Observable_addEventListener_MultipleEvents = function () { + var obj = new TestObservable(); + + var receivedCount = 0; + var callback = function (data: observable.EventData) { + receivedCount++; + TKUnit.assert(data.object === obj, "EventData.object value not valid."); + + if (data.eventName === observable.Observable.propertyChangeEvent) { + var propertyData = data; + TKUnit.assert(propertyData.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); + TKUnit.assert(propertyData.propertyName === "testName", "PropertyChangeData.propertyName value not valid."); + TKUnit.assert(propertyData.value === 1, "PropertyChangeData.value value not valid."); + } else { + TKUnit.assert(data.eventName === TESTED_NAME, "Expected event name " + TESTED_NAME); + } + } + + var events = observable.Observable.propertyChangeEvent + "," + TESTED_NAME; + obj.addEventListener(events, callback); + obj.set("testName", 1); + obj.test(); + TKUnit.assert(receivedCount === 2, "Callbacks not raised properly."); +} + +export var test_Observable_addEventListener_MultipleEvents_ShouldTrim = function () { + var obj = new TestObservable(); + + var receivedCount = 0; + var callback = function (data: observable.EventData) { + receivedCount++; + } + + var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; + obj.addEventListener(events, callback); + TKUnit.assert(obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.addEventListener for multiple events should trim each event name."); + TKUnit.assert(obj.hasListeners(TESTED_NAME), "Observable.addEventListener for multiple events should trim each event name."); + + obj.set("testName", 1); + obj.test(); + + TKUnit.assert(receivedCount === 2, "Callbacks not raised properly."); +} + +export var test_Observable_addEventListener_MultipleCallbacks = function () { + var obj = new TestObservable(); + + var receivedCount = 0; + var callback1 = function (data: observable.EventData) { + receivedCount++; + } + + var callback2 = function (data: observable.EventData) { + receivedCount++; + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); + obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); + + obj.set("testName", 1); + TKUnit.assert(receivedCount === 2, "The propertyChanged notification should be raised twice."); +} + +export var test_Observable_addEventListener_MultipleCallbacks_MultipleEvents = function () { + var obj = new TestObservable(); + + var receivedCount = 0; + var callback1 = function (data: observable.EventData) { + receivedCount++; + } + + var callback2 = function (data: observable.EventData) { + receivedCount++; + } + + var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; + obj.addEventListener(events, callback1); + obj.addEventListener(events, callback2); + + obj.set("testName", 1); + obj.test(); + + TKUnit.assert(receivedCount === 4, "The propertyChanged notification should be raised twice."); +} + +export var test_Observable_removeEventListener_SingleEvent_SingleCallback = function () { + var obj = new observable.Observable(); + + var receivedCount = 0; + var callback = function (data: observable.EventData) { + receivedCount++; + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, callback); + obj.set("testName", 1); + + obj.removeEventListener(observable.Observable.propertyChangeEvent, callback); + TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly."); + + obj.set("testName", 2); + TKUnit.assert(receivedCount === 1, "Observable.removeEventListener not working properly."); +} + +export var test_Observable_removeEventListener_SingleEvent_MultipleCallbacks = function () { + var obj = new observable.Observable(); + + var receivedCount = 0; + var callback1 = function (data: observable.EventData) { + receivedCount++; + } + + var callback2 = function (data: observable.EventData) { + receivedCount++; + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); + obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); + obj.set("testName", 1); + + obj.removeEventListener(observable.Observable.propertyChangeEvent, callback1); + TKUnit.assert(obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners."); + + obj.set("testName", 2); + TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners."); + + obj.removeEventListener(observable.Observable.propertyChangeEvent, callback2); + TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners."); + + obj.set("testName", 3); + TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners."); +} + +export var test_Observable_removeEventListener_MutlipleEvents_SingleCallback = function () { + var obj = new TestObservable(); + + var receivedCount = 0; + var callback = function (data: observable.EventData) { + receivedCount++; + } + + var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; + obj.addEventListener(events, callback); + + obj.set("testName", 1); + obj.test(); + + obj.removeEventListener(events, callback); + + TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Expected result for hasObservers is false"); + TKUnit.assert(!obj.hasListeners(TESTED_NAME), "Expected result for hasObservers is false."); + + obj.set("testName", 2); + obj.test(); + + TKUnit.assert(receivedCount === 2, "Expected receive count is 2"); +} + +export var test_Observable_removeEventListener_SingleEvent_NoCallbackSpecified = function () { + var obj = new TestObservable(); + + var receivedCount = 0; + var callback1 = function (data: observable.EventData) { + receivedCount++; + } + + var callback2 = function (data: observable.EventData) { + receivedCount++; + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); + obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); + + obj.set("testName", 1); + obj.removeEventListener(observable.Observable.propertyChangeEvent); + + TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Expected result for hasObservers is false."); + + obj.set("testName", 2); + TKUnit.assert(receivedCount === 2, "Expected receive count is 2"); +} + +export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithDotNotation_RaisesPropertyChangedEvent = function () { + var json = { + count: 5 + }; + var obj = new observable.Observable(json); + + var receivedCount = 0; + var callback = function (data: observable.PropertyChangeData) { + receivedCount++; + TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); + TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); + TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid."); + TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid."); + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, callback); + + (obj).count++; + + TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); +} + +export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotation_RaisesPropertyChangedEvent = function () { + var json = { + count: 5 + }; + var obj = new observable.Observable(json); + + var receivedCount = 0; + var callback = function (data: observable.PropertyChangeData) { + receivedCount++; + TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); + TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); + TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid."); + TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid."); + } + + obj.addEventListener(observable.Observable.propertyChangeEvent, callback); + + obj["count"]++; + + TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); +} + +export function test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents() { + var observableInstance = new observable.Observable(); + var firstHandlerCalled = false; + var secondHandlerCalled= false; + + var firstHandler = function (args) { + observableInstance.off(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); + firstHandlerCalled = true; + } + + var secondHandler = function (args) { + observableInstance.off(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); + secondHandlerCalled = true; + } + + var firstObserver = new observable.Observable(); + var secondObserver = new observable.Observable(); + + observableInstance.on(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); + observableInstance.on(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); + + observableInstance.set("someProperty", "some value"); + + TKUnit.assertEqual(firstHandlerCalled, true); + TKUnit.assertEqual(secondHandlerCalled, true); +} + +export function test_Observable_shouldDistinguishSeparateObjects() { + var obj1 = {val: 1}; + var obj2 = {val: 2}; + var observable1 = new observable.Observable(obj1); + var observable2 = new observable.Observable(obj2); + + var val1 = observable1.get("val"); + var val2 = observable2.get("val"); + TKUnit.assert(val1 === 1 && val2 === 2, `Observable should keep separate objects separate! val1: ${val1}; val2: ${val2};`); + + var newValue1; + observable1.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { + newValue1 = data.value; + }); + + var newValue2; + observable2.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { + newValue2 = data.value; + }); + + observable1.set("val", 10); + TKUnit.wait(0.1); + TKUnit.assert(newValue1 === 10, "newValue1 should be 10"); + + observable2.set("val", 20); + TKUnit.wait(0.1); + TKUnit.assert(newValue2 === 20, "newValue2 should be 20"); + + val1 = observable1.get("val"); + val2 = observable2.get("val"); + TKUnit.assert(val1 === 10 && val2 === 20, `Observable should keep separate objects separate! val1: ${val1}; val2: ${val2};`); }; diff --git a/apps/tests/testRunner.ts b/apps/tests/testRunner.ts index 7a60d30ca..e3ea56eea 100644 --- a/apps/tests/testRunner.ts +++ b/apps/tests/testRunner.ts @@ -43,10 +43,9 @@ allTests["APPLICATION SETTINGS"] = require("./application-settings-tests"); allTests["IMAGE SOURCE"] = require("./image-source-tests"); allTests["TIMER"] = require("./timer-tests"); allTests["COLOR"] = require("./color-tests"); -allTests["OBSERVABLE"] = require("./observable-tests"); allTests["OBSERVABLE-ARRAY"] = require("./observable-array-tests"); allTests["VIRTUAL-ARRAY"] = require("./virtual-array-tests"); -allTests["OBSERVABLE"] = require("./ui/observable-tests"); +allTests["OBSERVABLE"] = require("./observable-tests"); allTests["DEPENDENCY-OBSERVABLE"] = require("./ui/dependency-observable-tests"); allTests["BINDABLE"] = require("./ui/bindable-tests"); allTests["BINDING-EXPRESSIONS"] = require("./ui/binding-expressions-tests"); diff --git a/apps/tests/ui/observable-tests.ts b/apps/tests/ui/observable-tests.ts deleted file mode 100644 index b8bb5598e..000000000 --- a/apps/tests/ui/observable-tests.ts +++ /dev/null @@ -1,407 +0,0 @@ -// -// # Observable -// Using Observable objects requires the "data/observable" module. -// ``` JavaScript -import observable = require("data/observable"); -// ``` -// - -import dependencyObservable = require("ui/core/dependency-observable"); -import TKUnit = require("../TKUnit"); -import types = require("utils/types"); -import proxy = require("ui/core/proxy"); - -var TESTED_NAME = "tested"; -class TestObservable extends observable.Observable { - public test() { - this._emit(TESTED_NAME); - } -} - -export var test_Observable_Constructor = function () { - // - // ### Creating an Observable - // ``` JavaScript - var json = { - Name: "John", - Age: 34, - Married: true - }; - var person = new observable.Observable(json); - var name = person.get("Name"); - var age = person.get("Age"); - var married = person.get("Married"); - //// console.log(name + " " + age + " " + married); // Prints out "John 34 true" if uncommented. - // ``` - // - TKUnit.assert(name === "John", "Expected name is John"); - TKUnit.assert(age === 34, "Expected age is 34"); - TKUnit.assert(married === true, "Expected married is true"); -} - -export var tests_DummyTestForCodeSnippet = function () { - // - // ### Responding to property changes - // ``` JavaScript - var person = new observable.Observable(); - person.set("Name", "John"); - person.set("Age", 34); - person.set("Married", true); - person.addEventListener(observable.Observable.propertyChangeEvent, function (pcd: observable.PropertyChangeData) { - //console.log(pcd.eventName.toString() + " " + pcd.propertyName.toString() + " " + pcd.value.toString()); - }); - person.set("Age", 35); - person.set("Married", false); - //// If uncommented, the console.log above produces the following output: - //// propertyChange Age 35 - //// propertyChange Married false - // ``` - // -} - -export var test_Observable_Members = function () { - var obj = new observable.Observable(); - TKUnit.assert(types.isDefined(obj.addEventListener), "Observable.addEventListener not defined"); - TKUnit.assert(types.isDefined(obj._createPropertyChangeData), "Observable.createPropertyChangeData not defined"); - TKUnit.assert(types.isDefined(obj._emit), "Observable.emit not defined"); - TKUnit.assert(types.isDefined(obj.get), "Observable.get not defined"); - TKUnit.assert(types.isDefined(obj.hasListeners), "Observable.hasListeners not defined"); - TKUnit.assert(types.isDefined(obj.notify), "Observable.notify not defined"); - TKUnit.assert(types.isDefined(obj.off), "Observable.off not defined"); - TKUnit.assert(types.isDefined(obj.on), "Observable.on not defined"); - TKUnit.assert(types.isDefined(obj.removeEventListener), "Observable.removeEventListener not defined"); - TKUnit.assert(types.isDefined(obj.set), "Observable.set not defined"); - TKUnit.assert(types.isDefined(obj.typeName), "Observable.typeName not defined"); -} - -export var test_Observable_UpdateAnotherPropertyWithinChangedCallback = function () { - var obj = new observable.Observable(); - - var changedCallback = function (pcd: observable.PropertyChangeData) { - if (pcd.propertyName === "name") { - pcd.object.set("test", "Changed test"); - } - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, changedCallback); - - obj.set("name", "Initial name"); - obj.set("test", "Initial test"); - - TKUnit.assert(obj.get("name") === "Initial name", "Initial value for property name is not correct!"); - TKUnit.assert(obj.get("test") === "Initial test", "Initial value for property test is not correct!"); - - obj.set("name", "Changed name"); - - TKUnit.assert(obj.get("name") === "Changed name", "Changed value for property name is not correct!"); - TKUnit.assert(obj.get("test") === "Changed test", "Changed value for property test is not correct!"); -} - -export var test_DependencyObservable_UpdateAnotherPropertyWithinChangedCallback = function () { - var obj = new dependencyObservable.DependencyObservable(); - - function onFirstPropertyChanged(data: dependencyObservable.PropertyChangeData) { - var testObj = data.object; - testObj._setValue(secondProperty, "Changed test"); - }; - - var firstProperty = new dependencyObservable.Property( - "first", - "obj", - new proxy.PropertyMetadata( - "", - dependencyObservable.PropertyMetadataSettings.None, - onFirstPropertyChanged - ) - ); - - var secondProperty = new dependencyObservable.Property( - "second", - "obj", - new proxy.PropertyMetadata( - "", - dependencyObservable.PropertyMetadataSettings.None, - null - ) - ); - - obj._setValue(firstProperty, "Initial name"); - obj._setValue(secondProperty, "Initial test"); - - TKUnit.assert(obj._getValue(firstProperty) === "Initial name", "Initial value for property name is not correct!"); - TKUnit.assert(obj._getValue(secondProperty) === "Initial test", "Initial value for property test is not correct!"); - - obj._setValue(firstProperty, "Changed name"); - - TKUnit.assert(obj._getValue(firstProperty) === "Changed name", "Changed value for property name is not correct!"); - TKUnit.assert(obj._getValue(secondProperty) === "Changed test", "Changed value for property test is not correct!"); -} - -export var test_Observable_addEventListener_SingleEvent = function () { - var obj = new observable.Observable(); - - var receivedCount = 0; - var callback = function (data: observable.PropertyChangeData) { - receivedCount++; - TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); - TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); - TKUnit.assert(data.propertyName === "testName", "PropertyChangeData.propertyName value not valid."); - TKUnit.assert(data.value === 1, "PropertyChangeData.value value not valid."); - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, callback); - obj.set("testName", 1); - TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); -} - -export var test_Observable_addEventListener_MultipleEvents = function () { - var obj = new TestObservable(); - - var receivedCount = 0; - var callback = function (data: observable.EventData) { - receivedCount++; - TKUnit.assert(data.object === obj, "EventData.object value not valid."); - - if (data.eventName === observable.Observable.propertyChangeEvent) { - var propertyData = data; - TKUnit.assert(propertyData.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); - TKUnit.assert(propertyData.propertyName === "testName", "PropertyChangeData.propertyName value not valid."); - TKUnit.assert(propertyData.value === 1, "PropertyChangeData.value value not valid."); - } else { - TKUnit.assert(data.eventName === TESTED_NAME, "Expected event name " + TESTED_NAME); - } - } - - var events = observable.Observable.propertyChangeEvent + "," + TESTED_NAME; - obj.addEventListener(events, callback); - obj.set("testName", 1); - obj.test(); - TKUnit.assert(receivedCount === 2, "Callbacks not raised properly."); -} - -export var test_Observable_addEventListener_MultipleEvents_ShouldTrim = function () { - var obj = new TestObservable(); - - var receivedCount = 0; - var callback = function (data: observable.EventData) { - receivedCount++; - } - - var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; - obj.addEventListener(events, callback); - TKUnit.assert(obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.addEventListener for multiple events should trim each event name."); - TKUnit.assert(obj.hasListeners(TESTED_NAME), "Observable.addEventListener for multiple events should trim each event name."); - - obj.set("testName", 1); - obj.test(); - - TKUnit.assert(receivedCount === 2, "Callbacks not raised properly."); -} - -export var test_Observable_addEventListener_MultipleCallbacks = function () { - var obj = new TestObservable(); - - var receivedCount = 0; - var callback1 = function (data: observable.EventData) { - receivedCount++; - } - - var callback2 = function (data: observable.EventData) { - receivedCount++; - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); - obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); - - obj.set("testName", 1); - TKUnit.assert(receivedCount === 2, "The propertyChanged notification should be raised twice."); -} - -export var test_Observable_addEventListener_MultipleCallbacks_MultipleEvents = function () { - var obj = new TestObservable(); - - var receivedCount = 0; - var callback1 = function (data: observable.EventData) { - receivedCount++; - } - - var callback2 = function (data: observable.EventData) { - receivedCount++; - } - - var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; - obj.addEventListener(events, callback1); - obj.addEventListener(events, callback2); - - obj.set("testName", 1); - obj.test(); - - TKUnit.assert(receivedCount === 4, "The propertyChanged notification should be raised twice."); -} - -export var test_Observable_removeEventListener_SingleEvent_SingleCallback = function () { - var obj = new observable.Observable(); - - var receivedCount = 0; - var callback = function (data: observable.EventData) { - receivedCount++; - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, callback); - obj.set("testName", 1); - - obj.removeEventListener(observable.Observable.propertyChangeEvent, callback); - TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly."); - - obj.set("testName", 2); - TKUnit.assert(receivedCount === 1, "Observable.removeEventListener not working properly."); -} - -export var test_Observable_removeEventListener_SingleEvent_MultipleCallbacks = function () { - var obj = new observable.Observable(); - - var receivedCount = 0; - var callback1 = function (data: observable.EventData) { - receivedCount++; - } - - var callback2 = function (data: observable.EventData) { - receivedCount++; - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); - obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); - obj.set("testName", 1); - - obj.removeEventListener(observable.Observable.propertyChangeEvent, callback1); - TKUnit.assert(obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners."); - - obj.set("testName", 2); - TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners."); - - obj.removeEventListener(observable.Observable.propertyChangeEvent, callback2); - TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners."); - - obj.set("testName", 3); - TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners."); -} - -export var test_Observable_removeEventListener_MutlipleEvents_SingleCallback = function () { - var obj = new TestObservable(); - - var receivedCount = 0; - var callback = function (data: observable.EventData) { - receivedCount++; - } - - var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; - obj.addEventListener(events, callback); - - obj.set("testName", 1); - obj.test(); - - obj.removeEventListener(events, callback); - - TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Expected result for hasObservers is false"); - TKUnit.assert(!obj.hasListeners(TESTED_NAME), "Expected result for hasObservers is false."); - - obj.set("testName", 2); - obj.test(); - - TKUnit.assert(receivedCount === 2, "Expected receive count is 2"); -} - -export var test_Observable_removeEventListener_SingleEvent_NoCallbackSpecified = function () { - var obj = new TestObservable(); - - var receivedCount = 0; - var callback1 = function (data: observable.EventData) { - receivedCount++; - } - - var callback2 = function (data: observable.EventData) { - receivedCount++; - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); - obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); - - obj.set("testName", 1); - obj.removeEventListener(observable.Observable.propertyChangeEvent); - - TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Expected result for hasObservers is false."); - - obj.set("testName", 2); - TKUnit.assert(receivedCount === 2, "Expected receive count is 2"); -} - -export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithDotNotation_RaisesPropertyChangedEvent = function () { - var json = { - count: 5 - }; - var obj = new observable.Observable(json); - - var receivedCount = 0; - var callback = function (data: observable.PropertyChangeData) { - receivedCount++; - TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); - TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); - TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid."); - TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid."); - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, callback); - - (obj).count++; - - TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); -} - -export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotation_RaisesPropertyChangedEvent = function () { - var json = { - count: 5 - }; - var obj = new observable.Observable(json); - - var receivedCount = 0; - var callback = function (data: observable.PropertyChangeData) { - receivedCount++; - TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); - TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); - TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid."); - TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid."); - } - - obj.addEventListener(observable.Observable.propertyChangeEvent, callback); - - obj["count"]++; - - TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); -} - -export function test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents() { - var observableInstance = new observable.Observable(); - var firstHandlerCalled = false; - var secondHandlerCalled= false; - - var firstHandler = function (args) { - observableInstance.off(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); - firstHandlerCalled = true; - } - - var secondHandler = function (args) { - observableInstance.off(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); - secondHandlerCalled = true; - } - - var firstObserver = new observable.Observable(); - var secondObserver = new observable.Observable(); - - observableInstance.on(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); - observableInstance.on(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); - - observableInstance.set("someProperty", "some value"); - - TKUnit.assertEqual(firstHandlerCalled, true); - TKUnit.assertEqual(secondHandlerCalled, true); -} From d8686422088efe5a173dc1701ace2c8ec6b7300f Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Tue, 13 Oct 2015 15:13:38 +0300 Subject: [PATCH 4/4] Fixed Issue #929. --- apps/tests/observable-tests.ts | 45 ++++++++++++++++++++++++++++++++-- data/observable/observable.ts | 31 ++++++++++++----------- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/apps/tests/observable-tests.ts b/apps/tests/observable-tests.ts index 7a68387e3..b0a29ddf4 100644 --- a/apps/tests/observable-tests.ts +++ b/apps/tests/observable-tests.ts @@ -379,7 +379,7 @@ export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotati TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); } -export function test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents() { +export var test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents = function() { var observableInstance = new observable.Observable(); var firstHandlerCalled = false; var secondHandlerCalled= false; @@ -406,7 +406,7 @@ export function test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAl TKUnit.assertEqual(secondHandlerCalled, true); } -export function test_Observable_shouldDistinguishSeparateObjects() { +export var test_ObservableCreatedWithJSON_shouldDistinguishSeparateObjects = function () { var obj1 = {val: 1}; var obj2 = {val: 2}; var observable1 = new observable.Observable(obj1); @@ -416,25 +416,66 @@ export function test_Observable_shouldDistinguishSeparateObjects() { var val2 = observable2.get("val"); TKUnit.assert(val1 === 1 && val2 === 2, `Observable should keep separate objects separate! val1: ${val1}; val2: ${val2};`); + var propName1; var newValue1; observable1.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { + propName1 = data.propertyName; newValue1 = data.value; }); + var propName2; var newValue2; observable2.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { + propName2 = data.propertyName; newValue2 = data.value; }); observable1.set("val", 10); TKUnit.wait(0.1); + TKUnit.assert(propName1 === "val", "propName1 should be 'val'"); TKUnit.assert(newValue1 === 10, "newValue1 should be 10"); observable2.set("val", 20); TKUnit.wait(0.1); + TKUnit.assert(propName2 === "val", "propName2 should be 'val'"); TKUnit.assert(newValue2 === 20, "newValue2 should be 20"); val1 = observable1.get("val"); val2 = observable2.get("val"); TKUnit.assert(val1 === 10 && val2 === 20, `Observable should keep separate objects separate! val1: ${val1}; val2: ${val2};`); }; + +export var test_ObservablesCreatedWithJSON_shouldNotInterfereWithOneAnother = function () { + var observable1 = new observable.Observable({ property1: 1 }); + var observable2 = new observable.Observable({ property2: 2 }); + + TKUnit.assert(observable1.get("property1") === 1, `Expected: 1; Actual: ${observable1.get("property1")}`); + TKUnit.assert(observable1.get("property2") === undefined, `Expected: undefined; Actual: ${observable1.get("property2") }`); + + TKUnit.assert(observable2.get("property1") === undefined, `Expected: undefined; Actual: ${observable2.get("property1") }`); + TKUnit.assert(observable2.get("property2") === 2, `Expected: 2; Actual: ${observable2.get("property2") }`); + + var propName1; + var newValue1; + observable1.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { + propName1 = data.propertyName; + newValue1 = data.value; + }); + + var propName2; + var newValue2; + observable2.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { + propName2 = data.propertyName; + newValue2 = data.value; + }); + + observable1.set("property1", 10); + TKUnit.wait(0.1); + TKUnit.assert(propName1 === "property1", "propName1 should be 'property1'"); + TKUnit.assert(newValue1 === 10, "newValue1 should be 10"); + + observable2.set("property2", 20); + TKUnit.wait(0.1); + TKUnit.assert(propName2 === "property2", "propName2 should be 'property2'"); + TKUnit.assert(newValue2 === 20, "newValue2 should be 20"); +}; \ No newline at end of file diff --git a/data/observable/observable.ts b/data/observable/observable.ts index 1dff4db84..e677fede6 100644 --- a/data/observable/observable.ts +++ b/data/observable/observable.ts @@ -15,30 +15,29 @@ export class Observable implements definition.Observable { constructor(json?: any) { if (json) { this._map = new Map(); - - var definePropertyFunc = function definePropertyFunc(propertyName) { - Object.defineProperty(Observable.prototype, propertyName, { - get: function () { - return this._map.get(propertyName); - }, - set: function (value) { - this._map.set(propertyName, value); - this.notify(this._createPropertyChangeData(propertyName, value)); - }, - enumerable: true, - configurable: true - }); - }; - for (var prop in json) { if (json.hasOwnProperty(prop)) { - definePropertyFunc(prop); + this._defineNewProperty(prop); this.set(prop, json[prop]); } } } } + private _defineNewProperty(propertyName: string): void { + Object.defineProperty(this, propertyName, { + get: function () { + return this._map.get(propertyName); + }, + set: function (value) { + this._map.set(propertyName, value); + this.notify(this._createPropertyChangeData(propertyName, value)); + }, + enumerable: true, + configurable: true + }); + } + get typeName(): string { return types.getClass(this); }