UNPKG

@danielkalen/simplybind

Version:

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

235 lines (205 loc) 10.2 kB
import './setup'; import {SetterObserver} from '../src/property-observation'; import {DirtyCheckProperty} from '../src/dirty-checking'; import { ValueAttributeObserver, XLinkAttributeObserver, DataAttributeObserver, StyleObserver } from '../src/element-observation'; import { createElement, checkDelay, createObserverLocator, executeSharedPropertyObserverTests } from './shared'; function createSvgUseElement() { var html = [ '<svg>', ' <defs>', ' <g id="shape1">', ' <rect x="50" y="50" width="50" height="50" />', ' </g>', ' <g id="shape2">', ' <circle cx="50" cy="50" r="50" />', ' </g>', ' </defs>', ' <use xlink:href="#shape1" x="50" y="50" foo:bar="baz" />', '</svg>'].join('\n'); return createElement(html).lastElementChild; } describe('element observation', () => { var locator; beforeAll(() => { locator = createObserverLocator(); }); it('observes xlink namespaced attributes', () => { var el = createSvgUseElement(), observer = locator.getObserver(el, 'xlink:href'); expect(observer instanceof XLinkAttributeObserver).toBe(true); expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "use" element\'s "xlink:href" property is not supported.')); expect(observer.getValue()).toBe('#shape1'); observer.setValue('#shape2'); expect(observer.getValue()).toBe('#shape2'); }); it('observes non-xlink namespaced attributes', () => { var el = createSvgUseElement(), observer = locator.getObserver(el, 'foo:bar'); expect(observer instanceof DataAttributeObserver).toBe(true); expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "use" element\'s "foo:bar" property is not supported.')); expect(observer.getValue()).toBe('baz'); observer.setValue('qux'); expect(observer.getValue()).toBe('qux'); }); it('observes data-* attributes on Elements', () => { var el = createElement('<h1 data-foo="bar"></h1>'), observer = locator.getObserver(el, 'data-foo'); expect(observer instanceof DataAttributeObserver).toBe(true); expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "H1" element\'s "data-foo" property is not supported.')); expect(observer.getValue()).toBe('bar'); observer.setValue('baz'); expect(observer.getValue()).toBe('baz'); }); it('observes aria-* attributes on Elements', () => { var el = createElement('<h1 aria-hidden="true"></h1>'), observer = locator.getObserver(el, 'aria-hidden'); expect(observer instanceof DataAttributeObserver).toBe(true); expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "H1" element\'s "aria-hidden" property is not supported.')); expect(observer.getValue()).toBe('true'); observer.setValue('false'); expect(observer.getValue()).toBe('false'); }); it('observes data-* attributes on SVGElements', () => { var el = createElement('<svg data-foo="bar"></svg>'), observer = locator.getObserver(el, 'data-foo'); expect(observer instanceof DataAttributeObserver).toBe(true); expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "svg" element\'s "data-foo" property is not supported.')); expect(observer.getValue()).toBe('bar'); observer.setValue('baz'); expect(observer.getValue()).toBe('baz'); }); it('observes aria-* attributes on SVGElements', () => { var el = createElement('<svg aria-hidden="true"></svg>'), observer = locator.getObserver(el, 'aria-hidden'); expect(observer instanceof DataAttributeObserver).toBe(true); expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "svg" element\'s "aria-hidden" property is not supported.')); expect(observer.getValue()).toBe('true'); observer.setValue('false'); expect(observer.getValue()).toBe('false'); }); it('value attributes', (done) => { var cases = [ { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: 'bar' }, { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: undefined, expected: '' }, { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: null, expected: '' }, { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: 'bar' }, { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: undefined, expected: '' }, { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: null, expected: '' }, ], remaining = cases.length; cases.forEach(test => { var el = createElement(test.tag), observer = locator.getObserver(el, test.attr), callback = jasmine.createSpy('callback'), expected = test.hasOwnProperty('expected') ? test.expected : test.new; observer.subscribe(callback); expect(observer instanceof ValueAttributeObserver).toBe(true); expect(observer.getValue()).toBe(test.old); observer.setValue(test.new); expect(observer.getValue()).toBe(expected); setTimeout(() => { expect(callback).toHaveBeenCalledWith(expected, test.old); observer.unsubscribe(callback); remaining--; if (remaining === 0) { done(); } }, 0); }); }); it('native properties of Elements and SVGElements', () => { var cases = [ { tag: '<input type="text" />', attr: 'type', old: 'text', new: 'checkbox' }, { tag: '<input id="foo" />', attr: 'id', old: 'foo', new: 'bar' }, { tag: '<svg width="100"></svg>', attr: 'width', old: '100', new: '200' }, { tag: '<svg viewBox="0 0 100 100"></svg>', attr: 'viewBox', old: '0 0 100 100', new: '0 0 200 200' }, ]; cases.forEach(test => { var el = createElement(test.tag), observer = locator.getObserver(el, test.attr); expect(observer instanceof DirtyCheckProperty || observer instanceof DataAttributeObserver).toBe(true); expect(observer.getValue()).toBe(test.old); observer.setValue(test.new); expect(observer.getValue()).toBe(test.new); }); }); it('style attribute', () => { var el = createElement('<div></div>'), attrs = ['style', 'css'], i, observer; for(i = 0; i < attrs.length; i++) { observer = locator.getObserver(el, attrs[i]); expect(observer instanceof StyleObserver).toBe(true); expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "DIV" element\'s "' + attrs[i] + '" property is not supported.')); observer.setValue(' width : 30px;height:20px; background-color : red;background-image: url("http://aurelia.io/test.png"); '); //expect(observer.getValue()).toBe('width: 30px; height: 20px; background-image: url("http://aurelia.io/test.png"); background-color: red;'); expect(el.style.height).toBe('20px'); expect(el.style.width).toBe('30px'); expect(el.style.backgroundColor).toBe('red'); expect(el.style.backgroundImage).toBe('url("http://aurelia.io/test.png")'); observer.setValue(''); expect(el.style.height).toBe(''); expect(el.style.width).toBe(''); expect(el.style.backgroundColor).toBe(''); expect(el.style.backgroundImage).toBe(''); observer.setValue(` width : 25px ; background: url("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~:/?#[]%@!$&'()*+,;=");`); expect(el.style.width).toBe('25px'); expect(el.style.background).toBe(`url("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~:/?#[]%@!$&'()*+,;=")`); observer.setValue(''); expect(el.style.width).toBe(''); expect(el.style.background).toBe(''); observer.setValue(` width : 25px ; background: url('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~:/?#[]%@!$&\\'()*+,;=');`); expect(el.style.width).toBe('25px'); expect(el.style.background).toBe(`url("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~:/?#[]%@!$&'()*+,;=")`); observer.setValue(''); expect(el.style.width).toBe(''); expect(el.style.background).toBe(''); observer.setValue(` color : rgb( 255 , 255 , 255 ) ; background: url(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~:/?#[]%@!$&*+,;=);`); expect(el.style.color).toBe('rgb(255, 255, 255)'); expect(el.style.background).toBe(`url("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~:/?#[]%@!$&*+,;=")`); observer.setValue(''); expect(el.style.color).toBe(''); expect(el.style.background).toBe(''); observer.setValue(`background: url(data:image/gif;base64,R0lGODh0o/XBs/fNl3/zy7//wA7);`); expect(el.style.background).toBe(`url("data:image/gif;base64,R0lGODh0o/XBs/fNl3/zy7//wA7")`); observer.setValue(''); expect(el.style.width).toBe(''); expect(el.style.background).toBe(''); observer.setValue({ width: '50px', height: '40px', 'background-color': 'blue', 'background-image': 'url("http://aurelia.io/test2.png")' }); expect(el.style.height).toBe('40px'); expect(el.style.width).toBe('50px'); expect(el.style.backgroundColor).toBe('blue'); expect(el.style.backgroundImage).toBe('url("http://aurelia.io/test2.png")') observer.setValue({}); expect(el.style.height).toBe(''); expect(el.style.width).toBe(''); expect(el.style.backgroundColor).toBe(''); expect(el.style.backgroundImage).toBe(''); } }); describe('ad-hoc properites on Elements', () => { var obj, observer; beforeAll(() => { var locator = createObserverLocator(); obj = createElement('<foobar></foobar>'); obj.foo = 'bar'; observer = locator.getObserver(obj, 'foo'); }); it('should be a SetterObserver', () => { expect(observer instanceof SetterObserver).toBe(true); }); it('implements the property observer api', done => { executeSharedPropertyObserverTests(obj, observer, done); }); }); });