UNPKG

@danielkalen/simplybind

Version:

Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.

222 lines (184 loc) 7.42 kB
import './setup'; import {TestObservationAdapter} from './adapter'; import {DirtyCheckProperty} from '../src/dirty-checking'; import {SetterObserver} from '../src/property-observation'; import { ValueAttributeObserver, XLinkAttributeObserver, DataAttributeObserver, StyleObserver } from '../src/element-observation'; import {SelectValueObserver} from '../src/select-value-observer'; import {CheckedObserver} from '../src/checked-observer'; import {createElement, createObserverLocator} from './shared'; import {FEATURE} from 'aurelia-pal'; import {Logger} from 'aurelia-logging'; describe('ObserverLocator', () => { var locator; beforeAll(() => { locator = createObserverLocator(); locator.addAdapter(new TestObservationAdapter(() => locator)); }); it('uses SetterObserver for defined, primitive properties on pojos', () => { var obj = { foo: 'bar' }, observer = locator.getObserver(obj, 'foo'); expect(observer instanceof SetterObserver).toBe(true); }); it('uses SetterObserver for ad-hoc properties on Elements', () => { var obj = createElement('<h1></h1>'), observer; obj.foo = 'bar'; observer = locator.getObserver(obj, 'foo'); expect(obj instanceof Element).toBe(true); expect(observer instanceof SetterObserver).toBe(true); }); it('warns when its not able to subscribe to a SetterObserver', () => { spyOn(Logger.prototype, 'warn'); var obj = {}; Object.defineProperties(obj, { 'foo': {configurable: true}, 'bar': {configurable: false} }); locator.getObserver(obj, 'foo').convertProperty(); expect(Logger.prototype.warn).not.toHaveBeenCalled(); locator.getObserver(obj, 'bar').convertProperty(); expect(Logger.prototype.warn).toHaveBeenCalledWith('Cannot observe property \'bar\' of object', obj); }); it('respects the enumerability in the SetterObserver', () => { var obj = {}; Object.defineProperties(obj, { 'foo': { configurable: true, enumerable: true }, 'bar': { configurable: true, enumerable: false } }); ['foo', 'bar', 'baz'].forEach(prop => { locator.getObserver(obj, prop).convertProperty(); }); expect(Object.keys(obj)).toEqual(['foo', 'baz']); }); it('uses ValueAttributeObserver for element value attributes', () => { var obj = createElement('<input />'), observer = locator.getObserver(obj, 'value'); expect(observer instanceof ValueAttributeObserver).toBe(true); obj = createElement('<textarea></textarea'), observer = locator.getObserver(obj, 'value'); expect(observer instanceof ValueAttributeObserver).toBe(true); }); it('uses DataAttributeObserver for data-* attributes', () => { var obj = createElement('<input data-foo="bar" />'), observer = locator.getObserver(obj, 'data-foo'); expect(observer instanceof DataAttributeObserver).toBe(true); }); it('uses DataAttributeObserver for aria-* attributes', () => { var obj = createElement('<input aria-hidden="bar" />'), observer = locator.getObserver(obj, 'aria-hidden'); expect(observer instanceof DataAttributeObserver).toBe(true); }); it('uses CheckedObserver for input checked attributes', () => { var obj = createElement('<input />'), observer = locator.getObserver(obj, 'checked'); expect(observer instanceof CheckedObserver).toBe(true); }); it('uses SelectValueObserver for select value attributes', () => { var obj = createElement('<select></select>'), observer = locator.getObserver(obj, 'value'); expect(observer instanceof SelectValueObserver).toBe(true); }); it('uses StyleObserver for style attributes', () => { var obj = createElement('<select></select>'), observer = locator.getObserver(obj, 'style'); expect(observer instanceof StyleObserver).toBe(true); }); it('uses StyleObserver for css attributes', () => { var obj = createElement('<select></select>'), observer = locator.getObserver(obj, 'css'); expect(observer instanceof StyleObserver).toBe(true); }); it('uses DirtyCheckProperty for defined, complex properties on pojos', () => { var obj = {}, foo, observer; Object.defineProperty(obj, 'foo', { get: function() { return foo; }, set: function(newValue) { foo = newValue; }, enumerable: true, configurable: true }); observer = locator.getObserver(obj, 'foo'); expect(observer instanceof DirtyCheckProperty).toBe(true); }); it('uses custom getObserver when provided on the descriptor\'s getter', () => { var obj = {}, foo, customObserver = {}, observer, descriptor = { get: function() { return foo; }, set: function(newValue) { foo = newValue; }, enumerable: true, configurable: true }; descriptor.get.getObserver = function(obj){ customObserver.target = obj; return customObserver; }; Object.defineProperty(obj, 'foo', descriptor); observer = locator.getObserver(obj, 'foo'); expect(observer).toBe(customObserver); expect(observer.target).toBe(obj); }); it('uses custom getObserver when provided on the descriptor\'s setter without getter', () => { var obj = {}, foo, customObserver = {}, observer, descriptor = { set: function(newValue) { foo = newValue; }, enumerable: true, configurable: true }; descriptor.set.getObserver = function(obj){ customObserver.target = obj; return customObserver; }; Object.defineProperty(obj, 'foo', descriptor); observer = locator.getObserver(obj, 'foo'); expect(observer).toBe(customObserver); expect(observer.target).toBe(obj); }); it('warns when its not able to bind to an object', () => { spyOn(Logger.prototype, 'warn'); var obj1 = {}; locator.createObserversLookup(obj1); expect(Logger.prototype.warn).not.toHaveBeenCalled(); expect('__observers__' in obj1).toBeTruthy(); var obj2 = Object.seal({}); locator.createObserversLookup(obj2); expect(Logger.prototype.warn).toHaveBeenCalledWith('Cannot add observers to object', obj2); expect('__observers__' in obj2).toBeFalsy(); }); it('uses Adapter for for defined, complex properties on pojos that adapter canObserve', () => { var obj = { handleWithAdapter: true }, foo, observer, adapter, descriptor; adapter = locator.adapters[0]; spyOn(adapter, 'getObserver').and.callThrough(); Object.defineProperty(obj, 'foo', { get: function() { return foo; }, set: function(newValue) { foo = newValue; }, enumerable: true, configurable: true }); descriptor = Object.getOwnPropertyDescriptor(obj, 'foo'); observer = locator.getObserver(obj, 'foo'); expect(observer.___from_adapter).toBe(true); expect(adapter.getObserver).toHaveBeenCalledWith(obj, 'foo', descriptor); }); it('getAccessor returns ValueAttributeObserver for input.value', () => { expect(locator.getAccessor(document.createElement('input'), 'value') instanceof ValueAttributeObserver).toBe(true); }); it('getAccessor returns SetterObserver for input.model', () => { expect(locator.getAccessor(document.createElement('input'), 'model') instanceof SetterObserver).toBe(true); }); });