UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

296 lines (232 loc) 9.2 kB
import {expect} from "chai" import {extend} from "../../../source/data/extend.mjs"; import {isObject} from "../../../source/types/is.mjs"; import {Observer} from "../../../source/types/observer.mjs"; import {ProxyObserver} from "../../../source/types/proxyobserver.mjs"; describe('ProxyObserver', function () { describe('create', function () { it('should return instanceof ProxyObserver', function () { let o = new ProxyObserver({}); extend(o.getSubject(), {a: 4}); expect(o.getRealSubject()).is.eql({a: 4}); }); }); describe('setSubject', function () { it('should run observer', function (done) { let o = new ProxyObserver({a: 1}); expect(o.getRealSubject()).is.eql({a: 1}); let counter = 0; o.attachObserver(new Observer(function () { if (isObject(this) && this instanceof ProxyObserver) { counter++; // only one change as notify runs later if (counter === 1) { done(); return; } done("called for error"); return; } done(new Error("this is not ProxyObserver")) })) o.getSubject().c = 4; expect(o.getRealSubject()).is.eql({a: 1, c: 4}); o.setSubject({b: 2}); expect(o.getRealSubject()).is.eql({b: 2}); o.getSubject().d = 5; expect(o.getRealSubject()).is.eql({b: 2, d: 5}); }); }); describe('create', function () { it('should return instanceof ProxyObserver', function () { let o = new ProxyObserver({}); expect(o).is.instanceOf(ProxyObserver); }); }); describe('proxy objects', function () { it('Proxy objects should not be recommitted', function () { let o = new ProxyObserver({a: {b: true}}); o.getSubject().a.b = o.getSubject(); o.getSubject().a.b.a.b = false; //expect(o.getSubject()===p.getSubject()).to.be.true; }); }); describe('notify observer', function () { it('should inform observer', function (done) { let counter = 0; // the method is called twice, once for defineProperty and for set trap let o = new Observer(function (a) { if (isObject(this) && this instanceof ProxyObserver) { counter++; if (counter === 1) { done(); } return; } done(new Error("this is not ProxyObserver")) }, true) let realSubject = { a: { b: { c: true }, d: 5 } } let p = new ProxyObserver(realSubject); expect(p).is.instanceOf(ProxyObserver); expect(p.attachObserver(o)).is.instanceOf(ProxyObserver); let s = p.getSubject(); let r = p.getRealSubject(); s.a.d = false; s.a.b.d = 2; }); }); /** * @see https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/43 */ describe('Reflect', function () { it('check Reflect.set() with Subject', function () { let r; const obj = (new ProxyObserver({})).getSubject(); [ "yes!", undefined, "no" ].forEach(function (value) { expect(() => { obj['data'] = value }).to.not.throw(); //r = Reflect.set(obj, 'data', value) //expect(r).to.be.true; }) }); [ ['that', {that: 'checked1'}], ['that', new Proxy({that: 'checked2'}, {})], ['that', (new ProxyObserver({that: 'checked3'})).getRealSubject()], ['that', (new ProxyObserver({that: 'checked4'})).getSubject()] ].forEach(function (data) { let key = data.shift() let target = data.shift() it('check Reflect.set(' + key + ',' + JSON.stringify(target) + ') should writable', function () { let value; [ "1", undefined, true, undefined, false, undefined, undefined ].forEach(function (values) { expect(Reflect.getOwnPropertyDescriptor( target, key)['writable'], 'writable for ' + value + ' should return true').to.be.true; expect(Reflect.set(target, key, value), 'set for ' + value + ' should return true').to.be.true; expect(Reflect.getOwnPropertyDescriptor( target, key)['writable'], 'writable for ' + value + ' should return true').to.be.true; }); }) }) }); describe('notify observer with changes', function () { let original, observer, proxy; before(function () { original = {}; proxy = new ProxyObserver(original); }); [ ['a', 4, JSON.stringify({a: 4})], ['b', 9, JSON.stringify({a: 4, b: 9})] ].forEach(function (data) { let a = data.shift() let b = data.shift() let c = data.shift() it('set key ' + a + ' value ' + b + ' expect ' + c, function (done) { let counter = 0; observer = new Observer(function () { counter++; expect(JSON.stringify(this.getRealSubject())).is.equal(c); if (counter === 1) { this.detachObserver(observer); done(); } }) proxy.attachObserver(observer) let subject = proxy.getSubject() subject[a] = b; }); }); }) describe('notify observer ', function () { it('should return promise', function () { let proxy = new ProxyObserver({}); let observer = new Observer(function () { }) proxy.attachObserver(observer) expect(proxy.notifyObservers()).is.instanceOf(Promise); }); }); describe('setPrototypeOf', function () { it('should allow setPrototypeOf and notify observer', function (done) { const proxy = new ProxyObserver({}); const observer = new Observer(function () { done(); }); proxy.attachObserver(observer); const proto = {flag: true}; Object.setPrototypeOf(proxy.getSubject(), proto); expect(Object.getPrototypeOf(proxy.getSubject())).to.equal(proto); }); }); describe('deleteProperty', function () { it('should notify observers when deleting a key', function (done) { const proxy = new ProxyObserver({a: 1}); const observer = new Observer(function () { expect(proxy.getRealSubject()).to.eql({}); done(); }); proxy.attachObserver(observer); delete proxy.getSubject().a; }); }); describe('symbol properties', function () { it('should not notify observers for symbol keys', function (done) { const proxy = new ProxyObserver({}); let called = false; const observer = new Observer(function () { called = true; }); proxy.attachObserver(observer); proxy.getSubject()[Symbol("hidden")] = "value"; setTimeout(() => { expect(called).to.be.false; done(); }, 0); }); }); describe('runtime compatibility', function () { it('should allow nested object access without DOM globals', function () { const proxy = new ProxyObserver({options: {enabled: true}}); expect(proxy.getSubject().options.enabled).to.equal(true); }); it('should preserve setter semantics for accessor-backed instances', function () { let setterCalls = 0; class Example { set value(v) { setterCalls++; this._value = v; } get value() { return this._value; } } const instance = new Example(); const proxy = new ProxyObserver({instance}); proxy.getSubject().instance.value = 7; expect(setterCalls).to.equal(1); expect(instance.value).to.equal(7); expect(Object.prototype.hasOwnProperty.call(instance, 'value')).to.be.false; }); }); })