@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
318 lines (284 loc) • 13 kB
JavaScript
import './setup';
import {bindingMode} from '../src/binding-mode';
import {sourceContext} from '../src/connectable-binding';
import {
createElement,
checkDelay,
createObserverLocator,
getBinding
} from './shared';
import {createScopeForTest} from '../src/scope';
describe('CompositeObserver', () => {
let observerLocator;
beforeAll(() => {
observerLocator = createObserverLocator();
});
it('handles Conditional expressions', done => {
let obj = { condition: true, yes: 'yes', 'no': 'no' };
let el = createElement('<div></div>');
document.body.appendChild(el);
let binding = getBinding(observerLocator, obj, 'condition ? yes : no', el, 'textContent', bindingMode.oneWay).binding;
let conditionObserver = observerLocator.getObserver(obj, 'condition');
let yesObserver = observerLocator.getObserver(obj, 'yes');
let noObserver = observerLocator.getObserver(obj, 'no');
expect(conditionObserver.hasSubscribers()).toBe(false);
expect(yesObserver.hasSubscribers()).toBe(false);
expect(noObserver.hasSubscribers()).toBe(false);
binding.bind(createScopeForTest(obj));
expect(conditionObserver.hasSubscribers()).toBe(true);
expect(yesObserver.hasSubscribers()).toBe(true);
expect(noObserver.hasSubscribers()).toBe(false);
expect(el.textContent).toBe(obj.yes);
obj.condition = false;
setTimeout(() => {
expect(conditionObserver.hasSubscribers()).toBe(true);
expect(yesObserver.hasSubscribers()).toBe(false);
expect(noObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe(obj.no);
obj.no = 'noooo';
setTimeout(() => {
expect(el.textContent).toBe(obj.no);
binding.unbind();
expect(conditionObserver.hasSubscribers()).toBe(false);
expect(yesObserver.hasSubscribers()).toBe(false);
expect(noObserver.hasSubscribers()).toBe(false);
let threw = false;
try {
binding.call(sourceContext);
} catch(e) {
threw = true;
}
expect(threw).toBe(false);
document.body.removeChild(el);
done();
}, checkDelay * 2);
}, checkDelay * 2);
});
it('handles Binary expressions', done => {
let obj = { a: false, b: false, c: 1 };
let el = createElement('<div></div>');
document.body.appendChild(el);
let binding = getBinding(observerLocator, obj, 'a && b || c', el, 'textContent', bindingMode.oneWay).binding;
let aObserver = observerLocator.getObserver(obj, 'a');
let bObserver = observerLocator.getObserver(obj, 'b');
let cObserver = observerLocator.getObserver(obj, 'c');
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(cObserver.hasSubscribers()).toBe(false);
binding.bind(createScopeForTest(obj));
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(false);
expect(cObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe(obj.c.toString());
obj.a = true;
setTimeout(() => {
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
expect(cObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe(obj.c.toString());
obj.b = true;
setTimeout(() => {
expect(el.textContent).toBe(obj.a.toString());
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
expect(cObserver.hasSubscribers()).toBe(false);
binding.unbind();
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(cObserver.hasSubscribers()).toBe(false);
document.body.removeChild(el);
done();
}, checkDelay * 2);
}, checkDelay * 2);
});
it('handles PrefixNot expressions', done => {
let obj = { condition: true };
let el = createElement('<div></div>');
document.body.appendChild(el);
let binding = getBinding(observerLocator, obj, '!condition', el, 'textContent', bindingMode.oneWay).binding;
let conditionObserver = observerLocator.getObserver(obj, 'condition');
expect(conditionObserver.hasSubscribers()).toBe(false);
binding.bind(createScopeForTest(obj));
expect(conditionObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe((!obj.condition).toString());
obj.condition = false;
setTimeout(() => {
expect(conditionObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe((!obj.condition).toString());
binding.unbind();
expect(conditionObserver.hasSubscribers()).toBe(false);
document.body.removeChild(el);
done();
}, checkDelay * 2);
});
it('handles CallScope expressions', done => {
let obj = { a: 'a', b: 'b', test: (a, b) => a + b };
let el = createElement('<div></div>');
document.body.appendChild(el);
let binding = getBinding(observerLocator, obj, 'test(a, b)', el, 'textContent', bindingMode.oneWay).binding;
let aObserver = observerLocator.getObserver(obj, 'a');
let bObserver = observerLocator.getObserver(obj, 'b');
let testObserver = observerLocator.getObserver(obj, 'test');
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(testObserver.hasSubscribers()).toBe(false);
binding.bind(createScopeForTest(obj));
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
//expect(testObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe('ab');
obj.a = 'aa';
setTimeout(() => {
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
//expect(testObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe('aab');
binding.unbind();
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
//expect(testObserver.hasSubscribers()).toBe(false);
document.body.removeChild(el);
done();
}, checkDelay * 2);
});
it('handles CallScope expressions', done => {
let foo = { obj: { a: 'a', b: 'b', test: (a, b) => a + b } };
let obj = foo.obj;
let el = createElement('<div></div>');
document.body.appendChild(el);
let binding = getBinding(observerLocator, foo, 'obj.test(obj.a, obj.b)', el, 'textContent', bindingMode.oneWay).binding;
let aObserver = observerLocator.getObserver(obj, 'a');
let bObserver = observerLocator.getObserver(obj, 'b');
let testObserver = observerLocator.getObserver(obj, 'test');
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(testObserver.hasSubscribers()).toBe(false);
binding.bind(createScopeForTest(foo));
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
//expect(testObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe('ab');
obj.a = 'aa';
setTimeout(() => {
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
//expect(testObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe('aab');
binding.unbind();
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(testObserver.hasSubscribers()).toBe(false);
document.body.removeChild(el);
done();
}, checkDelay * 2);
});
it('handles CallFunction expressions', done => {
let foo = { obj: { a: 'a', b: 'b', test: (a, b) => a + b } };
let obj = foo.obj;
let el = createElement('<div></div>');
document.body.appendChild(el);
let binding = getBinding(observerLocator, foo, 'obj[\'test\'](obj.a, obj.b)', el, 'textContent', bindingMode.oneWay).binding;
let aObserver = observerLocator.getObserver(obj, 'a');
let bObserver = observerLocator.getObserver(obj, 'b');
let testObserver = observerLocator.getObserver(obj, 'test');
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(testObserver.hasSubscribers()).toBe(false);
binding.bind(createScopeForTest(foo));
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
expect(testObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe('ab');
obj.a = 'aa';
setTimeout(() => {
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
expect(testObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe('aab');
binding.unbind();
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(testObserver.hasSubscribers()).toBe(false);
document.body.removeChild(el);
done();
}, checkDelay * 2);
});
it('handles kitchen sink', done => {
let foo = { obj: { a: 'a', b: 'b', test: (a, b) => a + b, yes: true, no: false, x: { y: { z: 'z' } } } };
let obj = foo.obj;
let el = createElement('<div></div>');
document.body.appendChild(el);
let binding = getBinding(observerLocator, foo, 'obj[\'test\'](obj.a, obj.b) && obj.test(obj.a, obj.b) && obj.yes && !!obj.x.y.z || obj.no', el, 'textContent', bindingMode.oneWay).binding;
let objObserver = observerLocator.getObserver(foo, 'obj');
let aObserver = observerLocator.getObserver(obj, 'a');
let bObserver = observerLocator.getObserver(obj, 'b');
let testObserver = observerLocator.getObserver(obj, 'test');
let yesObserver = observerLocator.getObserver(obj, 'yes');
let noObserver = observerLocator.getObserver(obj, 'no');
let xObserver = observerLocator.getObserver(obj, 'x');
let yObserver = observerLocator.getObserver(obj.x, 'y');
let zObserver = observerLocator.getObserver(obj.x.y, 'z');
expect(objObserver.hasSubscribers()).toBe(false);
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(testObserver.hasSubscribers()).toBe(false);
expect(yesObserver.hasSubscribers()).toBe(false);
expect(noObserver.hasSubscribers()).toBe(false);
expect(xObserver.hasSubscribers()).toBe(false);
expect(yObserver.hasSubscribers()).toBe(false);
expect(zObserver.hasSubscribers()).toBe(false);
binding.bind(createScopeForTest(foo));
expect(objObserver.hasSubscribers()).toBe(true);
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
expect(testObserver.hasSubscribers()).toBe(true);
expect(yesObserver.hasSubscribers()).toBe(true);
expect(noObserver.hasSubscribers()).toBe(false);
expect(xObserver.hasSubscribers()).toBe(true);
expect(yObserver.hasSubscribers()).toBe(true);
expect(zObserver.hasSubscribers()).toBe(true);
expect(el.textContent).toBe('true');
obj.a = 0;
obj.b = 0;
setTimeout(() => {
expect(objObserver.hasSubscribers()).toBe(true);
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
expect(testObserver.hasSubscribers()).toBe(true);
expect(yesObserver.hasSubscribers()).toBe(false);
expect(noObserver.hasSubscribers()).toBe(true);
expect(xObserver.hasSubscribers()).toBe(false);
expect(yObserver.hasSubscribers()).toBe(false);
expect(zObserver.hasSubscribers()).toBe(false);
expect(el.textContent).toBe('false');
obj.a = true;
obj.b = true;
obj.x = null;
obj.no = 'hello world';
setTimeout(() => {
expect(objObserver.hasSubscribers()).toBe(true);
expect(aObserver.hasSubscribers()).toBe(true);
expect(bObserver.hasSubscribers()).toBe(true);
expect(testObserver.hasSubscribers()).toBe(true);
expect(yesObserver.hasSubscribers()).toBe(true);
expect(noObserver.hasSubscribers()).toBe(true);
expect(xObserver.hasSubscribers()).toBe(true);
expect(yObserver.hasSubscribers()).toBe(false);
expect(zObserver.hasSubscribers()).toBe(false);
expect(el.textContent).toBe('hello world');
binding.unbind();
expect(objObserver.hasSubscribers()).toBe(false);
expect(aObserver.hasSubscribers()).toBe(false);
expect(bObserver.hasSubscribers()).toBe(false);
expect(testObserver.hasSubscribers()).toBe(false);
expect(yesObserver.hasSubscribers()).toBe(false);
expect(noObserver.hasSubscribers()).toBe(false);
expect(xObserver.hasSubscribers()).toBe(false);
expect(yObserver.hasSubscribers()).toBe(false);
expect(zObserver.hasSubscribers()).toBe(false);
document.body.removeChild(el);
done();
}, checkDelay * 2);
}, checkDelay * 2);
});
});