Comparing version 1.0.4 to 1.0.5
396
index.js
@@ -1,226 +0,216 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _a; | ||
const rxjs_1 = require("rxjs"); | ||
const observedObjectsSymbol_1 = require("./observedObjectsSymbol"); | ||
/** | ||
* A Subject that will only memorize its subscribers | ||
* And remove them on unsubscribe. | ||
* Unsubscription will not close or stop the Subject itself. | ||
*/ | ||
class ReusableSubject extends rxjs_1.Subject { | ||
unsubscribe() { | ||
this.observers = []; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "rxjs", "./observedObjectsSymbol"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _a; | ||
const rxjs_1 = require("rxjs"); | ||
const observedObjectsSymbol_1 = require("./observedObjectsSymbol"); | ||
/** | ||
* A Subject that will only memorize its subscribers | ||
* And remove them on unsubscribe. | ||
* Unsubscription will not close or stop the Subject itself. | ||
*/ | ||
class ReusableSubject extends rxjs_1.Subject { | ||
unsubscribe() { | ||
this.observers = []; | ||
} | ||
class _ObservableObject { | ||
constructor(from = {}, optHandlers = {}) { | ||
this[_a] = {}; | ||
let setCustomHandler; | ||
let getCustomHandler; | ||
if (optHandlers && Object.keys(optHandlers).length) { | ||
if (optHandlers.set) { | ||
setCustomHandler = optHandlers.set; | ||
delete optHandlers.set; | ||
} | ||
if (optHandlers.get) { | ||
getCustomHandler = optHandlers.get; | ||
} | ||
} | ||
} | ||
class _ObservableObject { | ||
constructor(from = {}, optHandlers = {}) { | ||
this[_a] = {}; | ||
let setCustomHandler; | ||
let getCustomHandler; | ||
if (optHandlers && Object.keys(optHandlers).length) { | ||
if (optHandlers.set) { | ||
setCustomHandler = optHandlers.set; | ||
delete optHandlers.set; | ||
} | ||
if (optHandlers.get) { | ||
getCustomHandler = optHandlers.get; | ||
} | ||
} | ||
const handlers = Object.assign(optHandlers, { | ||
// Note for future: leave receiver as parameter even if not used | ||
// to keep args as the last and not include receiver in this one | ||
set: (obj, prop, value, receiver, ...args) => { | ||
let notificationChain; | ||
if (typeof value === "object") { | ||
// Creating the chain of properties that will be notified | ||
notificationChain = Object.assign({ | ||
[prop]: value, | ||
}, buildNotificationChain(value, prop)); | ||
if (setCustomHandler) { | ||
let setResult = setCustomHandler(obj, prop, value, receiver); | ||
if (setResult === false) { | ||
return setResult; | ||
} | ||
const handlers = Object.assign(optHandlers, { | ||
// Note for future: leave receiver as parameter even if not used | ||
// to keep args as the last and not include receiver in this one | ||
set: (obj, prop, value, receiver, ...args) => { | ||
let notificationChain; | ||
if (typeof value === "object") { | ||
// Creating the chain of properties that will be notified | ||
notificationChain = Object.assign({ | ||
[prop]: value, | ||
}, buildNotificationChain(value, prop)); | ||
if (setCustomHandler) { | ||
let setResult = setCustomHandler(obj, prop, value, receiver); | ||
if (setResult === false) { | ||
return setResult; | ||
} | ||
/* | ||
* We when we set a property which will be an object | ||
* we set it as a Proxy and pass it | ||
* an edited SETTER with binded trailing keys to reach | ||
* this property. | ||
* E.g. if we have an object structure like x.y.z.w | ||
* x, x.y and x.y.z will be Proxies; each property | ||
* will receive a setter with the parent keys. | ||
* w property, will receive below (in else), | ||
* ["x", "y", "z"] as args. | ||
* | ||
* We have to copy handlers to a new object to keep | ||
* the original `handlers.set` clean from any external argument | ||
*/ | ||
obj[prop] = new Proxy(value, Object.assign({}, handlers, { | ||
set: bindLast(handlers.set, ...args, prop), | ||
})); | ||
} | ||
else { | ||
/* | ||
* We when we set a property which will be an object | ||
* we set it as a Proxy and pass it | ||
* an edited SETTER with binded trailing keys to reach | ||
* this property. | ||
* E.g. if we have an object structure like x.y.z.w | ||
* x, x.y and x.y.z will be Proxies; each property | ||
* will receive a setter with the parent keys. | ||
* w property, will receive below (in else), | ||
* ["x", "y", "z"] as args. | ||
* | ||
* We have to copy handlers to a new object to keep | ||
* the original `handlers.set` clean from any external argument | ||
*/ | ||
obj[prop] = new Proxy(value, Object.assign({}, handlers, { | ||
set: bindLast(handlers.set, ...args, prop), | ||
})); | ||
} | ||
else { | ||
/* | ||
* We finalize the path of the keys passed in the above condition | ||
* to reach “object endpoint” (like "w" for the prev. example) | ||
* The path keys composition, let us subscribe to observables | ||
* with dot notation like x.y.z.w | ||
*/ | ||
if (obj[prop] === value) { | ||
/* | ||
* We finalize the path of the keys passed in the above condition | ||
* to reach “object endpoint” (like "w" for the prev. example) | ||
* The path keys composition, let us subscribe to observables | ||
* with dot notation like x.y.z.w | ||
* If the value is the same, we return true. | ||
* This cannot be considered as a fail. Also, failing would bring | ||
* a strict-mode script to throw a TypeError. | ||
*/ | ||
if (obj[prop] === value) { | ||
/* | ||
* If the value is the same, we return true. | ||
* This cannot be considered as a fail. Also, failing would bring | ||
* a strict-mode script to throw a TypeError. | ||
*/ | ||
return true; | ||
} | ||
if (setCustomHandler) { | ||
let setResult = setCustomHandler(obj, prop, value, receiver); | ||
if (setResult === false) { | ||
return setResult; | ||
} | ||
} | ||
obj[prop] = value; | ||
const elementKey = args.length ? [...args, prop].join(".") : prop; | ||
notificationChain = { | ||
[elementKey]: value | ||
}; | ||
return true; | ||
} | ||
Object.keys(notificationChain).forEach((keyPath) => { | ||
const value = notificationChain[keyPath]; | ||
// We want both single properties an complex objects to be notified when edited | ||
if (this[observedObjectsSymbol_1.observedObjects][keyPath]) { | ||
this[observedObjectsSymbol_1.observedObjects][keyPath].next(value); | ||
if (setCustomHandler) { | ||
let setResult = setCustomHandler(obj, prop, value, receiver); | ||
if (setResult === false) { | ||
return setResult; | ||
} | ||
}); | ||
return true; | ||
}, | ||
get: bindLast((target, prop, receiver, customGetter) => { | ||
if (customGetter !== undefined && !(prop in _ObservableObject.prototype)) { | ||
return customGetter(target, prop, receiver); | ||
} | ||
return Reflect.get(target, prop, receiver); | ||
}, getCustomHandler) | ||
}); | ||
return new Proxy(Object.assign(this, buildInitialProxyChain(from, handlers)), handlers); | ||
} | ||
/** | ||
* Registers a custom property to be observed. | ||
* | ||
* @param {string} prop - The property or object | ||
* property to subscribe to (e.g. `epsilon` | ||
* or `time.current`) | ||
*/ | ||
observe(prop) { | ||
if (!this[observedObjectsSymbol_1.observedObjects][prop]) { | ||
this[observedObjectsSymbol_1.observedObjects][prop] = new ReusableSubject(); | ||
} | ||
return this[observedObjectsSymbol_1.observedObjects][prop]; | ||
} | ||
/** | ||
* Unsubscribes from all the subscriptions in a specific pool | ||
* @param subscriptions | ||
*/ | ||
unsubscribeAll(subscriptions) { | ||
subscriptions.forEach(sub => sub.unsubscribe()); | ||
} | ||
/** | ||
* Returns the current image of a key of the main | ||
* object or a nested key. | ||
* | ||
* @param {string} path - dotted-notation path ("a.b.c") | ||
* @returns {any} - the whole observed object or part of it | ||
* @throws if the current path does not reflect to an available object | ||
*/ | ||
snapshot(path) { | ||
let snapshot; | ||
if (path && typeof path === "string") { | ||
snapshot = path.split(".").reduce((acc, current) => { | ||
if (!(current && typeof acc === "object" && !Array.isArray(acc) && acc.hasOwnProperty(current))) { | ||
throw new Error(`Cannot access to ${current} of ${path}. No key available`); | ||
obj[prop] = value; | ||
const elementKey = args.length ? [...args, prop].join(".") : prop; | ||
notificationChain = { | ||
[elementKey]: value | ||
}; | ||
} | ||
Object.keys(notificationChain).forEach((keyPath) => { | ||
const value = notificationChain[keyPath]; | ||
// We want both single properties an complex objects to be notified when edited | ||
if (this[observedObjectsSymbol_1.observedObjects][keyPath]) { | ||
this[observedObjectsSymbol_1.observedObjects][keyPath].next(value); | ||
} | ||
return acc[current]; | ||
}, this); | ||
if (typeof snapshot === "object") { | ||
return Object.assign({}, snapshot); | ||
}); | ||
return true; | ||
}, | ||
get: bindLast((target, prop, receiver, customGetter) => { | ||
if (customGetter !== undefined && !(prop in _ObservableObject.prototype)) { | ||
return customGetter(target, prop, receiver); | ||
} | ||
else { | ||
return snapshot; | ||
} | ||
} | ||
else { | ||
snapshot = Object.assign({}, this); | ||
// In the snapshot, we don't need the symbol that collects | ||
// All the observers | ||
delete snapshot[observedObjectsSymbol_1.observedObjects]; | ||
} | ||
return snapshot; | ||
} | ||
return Reflect.get(target, prop, receiver); | ||
}, getCustomHandler) | ||
}); | ||
return new Proxy(Object.assign(this, buildInitialProxyChain(from, handlers)), handlers); | ||
} | ||
_a = observedObjectsSymbol_1.observedObjects; | ||
// Workaround to allow us to recognize T's props as part of ObservableObject | ||
// https://stackoverflow.com/a/54737176/2929433 | ||
exports.ObservableObject = _ObservableObject; | ||
/** | ||
* Builds the initial object-proxy composed of proxies objects | ||
* @param sourceObject | ||
* @param handlers | ||
* Registers a custom property to be observed. | ||
* | ||
* @param {string} prop - The property or object | ||
* property to subscribe to (e.g. `epsilon` | ||
* or `time.current`) | ||
*/ | ||
function buildInitialProxyChain(sourceObject, handlers, ...args) { | ||
let chain = {}; | ||
for (const prop in sourceObject) { | ||
if (typeof sourceObject[prop] === "object" && !Array.isArray(sourceObject[prop])) { | ||
chain[prop] = buildInitialProxyChain(sourceObject[prop], Object.assign({}, handlers, { | ||
set: bindLast(handlers.set, ...args, prop) | ||
}), ...args, prop); | ||
} | ||
else { | ||
chain[prop] = sourceObject[prop]; | ||
} | ||
observe(prop) { | ||
if (!this[observedObjectsSymbol_1.observedObjects][prop]) { | ||
this[observedObjectsSymbol_1.observedObjects][prop] = new ReusableSubject(); | ||
} | ||
return new Proxy(chain, handlers); | ||
return this[observedObjectsSymbol_1.observedObjects][prop]; | ||
} | ||
/** | ||
* Builds the chain of properties that will be notified. | ||
* This is used when a property that is or will be | ||
* an object, is assigned. | ||
* The function will compose an object { "x.y.z": value } | ||
* for each key of each nested object. | ||
* @param source - Current object | ||
* @param args | ||
* Unsubscribes from all the subscriptions in a specific pool | ||
* @param subscriptions | ||
*/ | ||
function buildNotificationChain(source, ...args) { | ||
let chain = {}; | ||
for (const prop in source) { | ||
chain[[...args, prop].join(".")] = source[prop]; | ||
if (typeof source[prop] === "object" && !Array.isArray(source[prop])) { | ||
Object.assign(chain, buildNotificationChain(source[prop], ...args, prop)); | ||
} | ||
} | ||
return chain; | ||
unsubscribeAll(subscriptions) { | ||
subscriptions.forEach(sub => sub.unsubscribe()); | ||
} | ||
/** | ||
* Creates a function that accepts default arguments | ||
* with some other trailing arbitrary dev-defined arguments | ||
* Returns the current image of a key of the main | ||
* object or a nested key. | ||
* | ||
* E.g. Setter receives the following arguments: obj, prop, value, receiver. | ||
* We wrap the original function in another one that adds the arguments; | ||
* | ||
* @param {Function} fn - the original function | ||
* @param {any[]} boundArgs - the arbitrary arguments | ||
* @param {string} path - dotted-notation path ("a.b.c") | ||
* @returns {any} - the whole observed object or part of it | ||
* @throws if the current path does not reflect to an available object | ||
*/ | ||
function bindLast(fn, ...boundArgs) { | ||
return (...args) => fn(...args, ...boundArgs); | ||
snapshot(path) { | ||
let snapshot; | ||
if (path && typeof path === "string") { | ||
snapshot = path.split(".").reduce((acc, current) => { | ||
if (!(current && typeof acc === "object" && !Array.isArray(acc) && acc.hasOwnProperty(current))) { | ||
throw new Error(`Cannot access to ${current} of ${path}. No key available`); | ||
} | ||
return acc[current]; | ||
}, this); | ||
if (typeof snapshot === "object") { | ||
return Object.assign({}, snapshot); | ||
} | ||
else { | ||
return snapshot; | ||
} | ||
} | ||
else { | ||
snapshot = Object.assign({}, this); | ||
// In the snapshot, we don't need the symbol that collects | ||
// All the observers | ||
delete snapshot[observedObjectsSymbol_1.observedObjects]; | ||
} | ||
return snapshot; | ||
} | ||
}); | ||
} | ||
_a = observedObjectsSymbol_1.observedObjects; | ||
// Workaround to allow us to recognize T's props as part of ObservableObject | ||
// https://stackoverflow.com/a/54737176/2929433 | ||
exports.ObservableObject = _ObservableObject; | ||
/** | ||
* Builds the initial object-proxy composed of proxies objects | ||
* @param sourceObject | ||
* @param handlers | ||
*/ | ||
function buildInitialProxyChain(sourceObject, handlers, ...args) { | ||
let chain = {}; | ||
for (const prop in sourceObject) { | ||
if (typeof sourceObject[prop] === "object" && !Array.isArray(sourceObject[prop])) { | ||
chain[prop] = buildInitialProxyChain(sourceObject[prop], Object.assign({}, handlers, { | ||
set: bindLast(handlers.set, ...args, prop) | ||
}), ...args, prop); | ||
} | ||
else { | ||
chain[prop] = sourceObject[prop]; | ||
} | ||
} | ||
return new Proxy(chain, handlers); | ||
} | ||
/** | ||
* Builds the chain of properties that will be notified. | ||
* This is used when a property that is or will be | ||
* an object, is assigned. | ||
* The function will compose an object { "x.y.z": value } | ||
* for each key of each nested object. | ||
* @param source - Current object | ||
* @param args | ||
*/ | ||
function buildNotificationChain(source, ...args) { | ||
let chain = {}; | ||
for (const prop in source) { | ||
chain[[...args, prop].join(".")] = source[prop]; | ||
if (typeof source[prop] === "object" && !Array.isArray(source[prop])) { | ||
Object.assign(chain, buildNotificationChain(source[prop], ...args, prop)); | ||
} | ||
} | ||
return chain; | ||
} | ||
/** | ||
* Creates a function that accepts default arguments | ||
* with some other trailing arbitrary dev-defined arguments | ||
* | ||
* E.g. Setter receives the following arguments: obj, prop, value, receiver. | ||
* We wrap the original function in another one that adds the arguments; | ||
* | ||
* @param {Function} fn - the original function | ||
* @param {any[]} boundArgs - the arbitrary arguments | ||
*/ | ||
function bindLast(fn, ...boundArgs) { | ||
return (...args) => fn(...args, ...boundArgs); | ||
} |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -5,14 +6,3 @@ * Symbol that identify the map that will contain | ||
*/ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.observedObjects = Symbol("_observedObjects"); | ||
}); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.observedObjects = Symbol("_observedObjects"); |
{ | ||
"name": "roxe", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "Observe object changes through proxies", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -1,177 +0,167 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "..", "../observedObjectsSymbol"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const __1 = require(".."); | ||
const observedObjectsSymbol_1 = require("../observedObjectsSymbol"); | ||
describe("Creating a new observable object", () => { | ||
let oo; | ||
describe("Registration and observing for changes", () => { | ||
beforeEach(() => { | ||
// I think there is no way someone could do something like this. | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const __1 = require(".."); | ||
const observedObjectsSymbol_1 = require("../observedObjectsSymbol"); | ||
describe("Creating a new observable object", () => { | ||
let oo; | ||
describe("Registration and observing for changes", () => { | ||
beforeEach(() => { | ||
// I think there is no way someone could do something like this. | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
it("Should register an observer to a specific key of the object", () => { | ||
const observed = oo.observe("b.d.e"); | ||
// @ts-ignore - only because its the only way to check if it went fine | ||
expect(Object.keys(oo[observedObjectsSymbol_1.observedObjects]).includes("b.d.e")).toBeTruthy(); | ||
}); | ||
it("Should register an observer to a specific key of the object", () => { | ||
const observed = oo.observe("b.d.e"); | ||
// @ts-ignore - only because its the only way to check if it went fine | ||
expect(Object.keys(oo[observedObjectsSymbol_1.observedObjects]).includes("b.d.e")).toBeTruthy(); | ||
}); | ||
it("Should notify all the changes", () => { | ||
const observed = oo.observe("b.d.e"); | ||
observed.subscribe({ | ||
next: (newValue) => { | ||
expect(newValue).toBe(5); | ||
expect(oo.b.d.e).toBe(5); | ||
} | ||
}); | ||
it("Should notify all the changes", () => { | ||
const observed = oo.observe("b.d.e"); | ||
observed.subscribe({ | ||
next: (newValue) => { | ||
expect(newValue).toBe(5); | ||
expect(oo.b.d.e).toBe(5); | ||
} | ||
}); | ||
oo.b.d.e = 5; | ||
oo.b.d.e = 5; | ||
}); | ||
it("Unsubscribed object should not show any update", () => { | ||
const observed = oo.observe("b.d.e"); | ||
let subscription = observed.subscribe({ | ||
next: (newValue) => { | ||
// this won't be executed since unsubscribed | ||
expect(newValue).toBe(42); | ||
} | ||
}); | ||
it("Unsubscribed object should not show any update", () => { | ||
const observed = oo.observe("b.d.e"); | ||
let subscription = observed.subscribe({ | ||
next: (newValue) => { | ||
// this won't be executed since unsubscribed | ||
expect(newValue).toBe(42); | ||
} | ||
}); | ||
subscription.unsubscribe(); | ||
oo.b.d.e = 42; | ||
}); | ||
subscription.unsubscribe(); | ||
oo.b.d.e = 42; | ||
}); | ||
describe("Proxy handler", () => { | ||
let oo; | ||
it("Should attach custom handlers to the original one", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
}); | ||
describe("Proxy handler", () => { | ||
let oo; | ||
it("Should attach custom handlers to the original one", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
} | ||
}, { | ||
get(target, prop, receiver) { | ||
// this trap will be executed always, when getting values | ||
// from the observed objects | ||
if (typeof target[prop] === "number") { | ||
return Math.pow(target[prop], 2); | ||
} | ||
return target[prop]; | ||
} | ||
}, { | ||
get(target, prop, receiver) { | ||
// this trap will be executed always, when getting values | ||
// from the observed objects | ||
if (typeof target[prop] === "number") { | ||
return Math.pow(target[prop], 2); | ||
} | ||
}); | ||
expect(oo.b.c).toBe(4); | ||
return target[prop]; | ||
} | ||
}); | ||
expect(oo.b.c).toBe(4); | ||
}); | ||
describe("But first... let me take a snapshot", () => { | ||
let oo; | ||
it("Should return a full snapshot of the structure", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
}); | ||
describe("But first... let me take a snapshot", () => { | ||
let oo; | ||
it("Should return a full snapshot of the structure", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
} | ||
}); | ||
expect(compareObjects({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
}); | ||
expect(compareObjects({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
} | ||
}, oo.snapshot())).toBe(true); | ||
}); | ||
it("Should take a partial snapshot (object) of the main object", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
}, oo.snapshot())).toBe(true); | ||
}); | ||
it("Should take a partial snapshot (object) of the main object", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
} | ||
}); | ||
expect(compareObjects({ | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
}, oo.snapshot("b.d.f"))).toBe(true); | ||
} | ||
}); | ||
it("Should take a partial snapshot (value) of the main object", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
expect(compareObjects({ | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
}, oo.snapshot("b.d.f"))).toBe(true); | ||
}); | ||
it("Should take a partial snapshot (value) of the main object", () => { | ||
oo = new __1.ObservableObject({ | ||
a: 1, | ||
b: { | ||
c: 2, | ||
d: { | ||
e: 3, | ||
f: { | ||
g: 4, | ||
h: { | ||
i: 5, | ||
} | ||
} | ||
} | ||
}); | ||
expect(oo.snapshot("b.d.e")).toBe(3); | ||
} | ||
}); | ||
expect(oo.snapshot("b.d.e")).toBe(3); | ||
}); | ||
}); | ||
function compareObjects(obj1, obj2) { | ||
return Object.keys(obj1).every(key => { | ||
if (obj2[key] && typeof obj1[key] === "object") { | ||
return compareObjects(obj1[key], obj2[key]); | ||
} | ||
return obj2[key] === obj1[key]; | ||
}); | ||
} | ||
}); | ||
function compareObjects(obj1, obj2) { | ||
return Object.keys(obj1).every(key => { | ||
if (obj2[key] && typeof obj1[key] === "object") { | ||
return compareObjects(obj1[key], obj2[key]); | ||
} | ||
return obj2[key] === obj1[key]; | ||
}); | ||
} |
59209
437