UNPKG

@danielkalen/simplybind

Version:

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

458 lines (328 loc) 15.6 kB
suite "Data Binding", ()-> suiteSetup(restartSandbox) import ./placeholders import ./throttling import ./delaying suite "Binding-Type Specific", ()-> import ./type-DOMValue import ./type-DOMCheckbox import ./type-DOMRadio import ./type-DOMAttr import ./type-Function import ./type-Array import ./type-Event import ./type-Proxy import ./type-ObjectProp suite "Group Bindings", ()-> import ./group-basic import ./group-transforms import ./group-conditions import ./group-misc import ./group-unbinding test "Infinite loops should not occur in looping update chains", ()-> SimplyBind.defaultOptions.updateEvenIfSame = true # ==== Objects ================================================================================= SimplyBind('prop').of(objectA) .to('prop').of(objectB).bothWays() .chainTo('prop').of(objectC).bothWays() .chainTo('prop').of(objectA).bothWays() objectA.prop = 'from objectA' expect(objectA.prop).to.equal 'from objectA' expect(objectB.prop).to.equal 'from objectA' expect(objectC.prop).to.equal 'from objectA' objectB.prop = 'from objectB' expect(objectA.prop).to.equal 'from objectB' expect(objectB.prop).to.equal 'from objectB' expect(objectC.prop).to.equal 'from objectB' # ==== Arrays ================================================================================= SimplyBind('array:list').of(objectA) .to('array:list').of(objectB).bothWays() .chainTo('array:list').of(objectC).bothWays() .chainTo('array:list').of(objectA).bothWays() expect(objectA.list.length).to.equal 10 expect(objectB.list.length).to.equal 10 expect(objectC.list.length).to.equal 10 expect(objectA.list).not.to.equal(objectB.list) expect(objectA.list).to.eql(objectB.list) expect(objectB.list).to.eql(objectC.list) objectA.list.pop() expect(objectA.list.length).to.equal 9 expect(objectB.list.length).to.equal 9 expect(objectC.list.length).to.equal 9 expect(objectA.list).not.to.equal(objectB.list) expect(objectA.list).to.eql(objectB.list) expect(objectB.list).to.eql(objectC.list) objectB.list.pop() expect(objectA.list.length).to.equal 8 expect(objectB.list.length).to.equal 8 expect(objectC.list.length).to.equal 8 expect(objectA.list).not.to.equal(objectB.list) expect(objectA.list).to.eql(objectB.list) expect(objectB.list).to.eql(objectC.list) objectC.list.push(1) expect(objectA.list.length).to.equal 9 expect(objectB.list.length).to.equal 9 expect(objectC.list.length).to.equal 9 expect(objectA.list).not.to.equal(objectB.list) expect(objectA.list).to.eql(objectB.list) expect(objectB.list).to.eql(objectC.list) if isBrowser # ==== DOMAttr ================================================================================= SimplyBind('attr:someattr').of(regA) .to('attr:someattr').of(regB).bothWays() .chainTo('attr:someattr').of(regC).bothWays() .chainTo('attr:someattr').of(regA).bothWays() # regA.setAttribute 'someattr', 'from regElementA' SimplyBind('attr:someattr').of(regA).set('from regElementA') expect(regA.getAttribute 'someattr').to.equal 'from regElementA' expect(regB.getAttribute 'someattr').to.equal 'from regElementA' expect(regC.getAttribute 'someattr').to.equal 'from regElementA' # regB.setAttribute 'someattr', 'from regElementB' SimplyBind('attr:someattr').of(regB).set('from regElementB') expect(regA.getAttribute 'someattr').to.equal 'from regElementB' expect(regB.getAttribute 'someattr').to.equal 'from regElementB' expect(regC.getAttribute 'someattr').to.equal 'from regElementB' # ==== DOMText ================================================================================= SimplyBind('textContent').of(regA) .to('textContent').of(regB).bothWays() .chainTo('textContent').of(regC).bothWays() .chainTo('textContent').of(regA).bothWays() SimplyBind('textContent').of(regA).set 'from regElementA' expect(regA.textContent).to.equal 'from regElementA' expect(regB.textContent).to.equal 'from regElementA' expect(regC.textContent).to.equal 'from regElementA' SimplyBind('textContent').of(regB).set 'from regElementB' expect(regA.textContent).to.equal 'from regElementB' expect(regB.textContent).to.equal 'from regElementB' expect(regC.textContent).to.equal 'from regElementB' # ==== DOMValue ================================================================================= SimplyBind('value').of(inputA) .to('value').of(inputB).bothWays() .chainTo('value').of(inputC).bothWays() .chainTo('value').of(inputA).bothWays() inputA.value = 'from inputElementA' inputA.emit 'change' expect(inputA.value).to.equal 'from inputElementA' expect(inputB.value).to.equal 'from inputElementA' expect(inputC.value).to.equal 'from inputElementA' inputB.value = 'from inputElementB' inputB.emit 'change' expect(inputA.value).to.equal 'from inputElementB' expect(inputB.value).to.equal 'from inputElementB' expect(inputC.value).to.equal 'from inputElementB' # ==== Events ================================================================================= SimplyBind('event:eventA').of(eventEmitterA) .to('event:eventB').of(eventEmitterA).bothWays() SimplyBind('event:eventB').of(eventEmitterA) .to('event:eventC').of(eventEmitterA).bothWays() SimplyBind('event:eventC').of(eventEmitterA) .to('event:eventA').of(eventEmitterA).bothWays() invokeCountA = invokeCountB = invokeCountC = 0 eventEmitterA.on 'eventA', ()-> invokeCountA++ eventEmitterA.on 'eventB', ()-> invokeCountB++ eventEmitterA.on 'eventC', ()-> invokeCountC++ eventEmitterA.emit 'eventA' eventEmitterA.emit 'eventB' eventEmitterA.emit 'eventC' expect(invokeCountA).to.equal 3 expect(invokeCountB).to.equal 3 expect(invokeCountC).to.equal 3 SimplyBind.defaultOptions.updateEvenIfSame = false restartSandbox() test "Infinite loops should not occur for colliding two-way bindings", ()-> SimplyBind.defaultOptions.updateEvenIfSame = true dispatcher = 'prop': 0 objectA.prop1 = 0 objectB.prop1 = 0 objectC.prop1 = 0 SimplyBind('prop').of(dispatcher) .to('prop1').of(objectA).bothWays().transform (v, ov)-> v+ov .and.to('prop1').of(objectB).bothWays().transform (v, ov)-> v+ov .and.to('prop1').of(objectC).bothWays().transform (v, ov)-> v+ov expect(dispatcher.prop).to.equal 0 expect(objectA.prop1).to.equal 0 expect(objectB.prop1).to.equal 0 expect(objectC.prop1).to.equal 0 dispatcher.prop = 1 expect(dispatcher.prop).to.equal 1 expect(objectA.prop1).to.equal 1 expect(objectB.prop1).to.equal 1 expect(objectC.prop1).to.equal 1 objectA.prop1 = 2 expect(dispatcher.prop).to.equal 3 expect(objectA.prop1).to.equal 2 expect(objectB.prop1).to.equal 4 # not 3 because dispatcher.prop gets a 2 from objectA.prop1 but then it transforms it to 3 and passes to this binding. expect(objectC.prop1).to.equal 4 # not 3 because dispatcher.prop gets a 2 from objectA.prop1 but then it transforms it to 3 and passes to this binding. objectC.prop1 = 3 expect(dispatcher.prop).to.equal 6 expect(objectA.prop1).to.equal 8 expect(objectB.prop1).to.equal 10 expect(objectC.prop1).to.equal 3 SimplyBind.defaultOptions.updateEvenIfSame = false restartSandbox() test "Infinite loops should occur only when a value is bound to a function and that function updates the value", ()-> invokeCount = 0 SimplyBind('prop1').of(objectA) .to ()-> unless invokeCount is 15 objectA.prop1 = ++invokeCount expect(invokeCount).to.equal(objectA.prop1) expect(invokeCount).to.equal(15) restartSandbox() test "Update subscribers", ()-> SimplyBind.defaultOptions.updateOnBind = false invokeCount = 'object': 0 'array': 0 'function': 0 'proxy': 0 'domAttr': 0 'domText': 0 'domValue': 0 'domCheckboxSingle': 0 'domCheckbox': 0 'domRadioSingle': 0 'domRadio': 0 'event': 0 # ==== Objects ================================================================================= SimplyBind('prop').of(objectA).to ()-> invokeCount.object++ objectA.prop = true expect(invokeCount.object).to.equal(1) # ==== Arrays ================================================================================= SimplyBind('array:list').of(objectA).to ()-> invokeCount.array++ objectA.list.push(1) objectA.list.unshift(1) objectA.list.pop() objectA.list.shift() objectA.list.splice(0,1) expect(invokeCount.array).to.equal(5) # ==== Functions ================================================================================= SimplyBind(()->true).to ()-> invokeCount.function++ expect(invokeCount.function).to.equal(1) # ==== Proxy functions ================================================================================= obj = base:10, test:(a,b)-> (a+b)*@base SimplyBind('func:test').of(obj).to (value)-> expect(value.result).to.equal 50 expect(value.args).to.eql [2,3] invokeCount.proxy++ obj.test(2,3) expect(invokeCount.proxy).to.equal(1) if isBrowser # ==== DOMAttr ================================================================================= SimplyBind('attr:someattr').of(regA).to ()-> invokeCount.domAttr++ regA.setAttribute('someattr', 10) SimplyBind('attr:someattr').of(regA).set(true) expect(invokeCount.domAttr).to.equal(1) # ==== DOMText ================================================================================= SimplyBind('textContent').of(regA).to ()-> invokeCount.domText++ SimplyBind('textContent').of(regA).set 'true' expect(invokeCount.domText).to.equal(1) # ==== DOMValue ================================================================================= SimplyBind('value').of(inputA).to ()-> invokeCount.domValue++ inputA.value = 'true' inputA.emit 'change' expect(invokeCount.domValue).to.equal(1) # ==== DOMCheckbox Single ================================================================================= SimplyBind('checked').of(checkboxA).to ()-> invokeCount.domCheckboxSingle++ checkboxA.checked = true checkboxA.emit 'change' expect(invokeCount.domCheckboxSingle).to.equal(1) # ==== DOMCheckbox ================================================================================= SimplyBind('checked').of(checkboxFields).to ()-> invokeCount.domCheckbox++ checkboxB.checked = true checkboxB.emit 'change' expect(invokeCount.domCheckbox).to.equal(1) # ==== DOMRadio Single ================================================================================= SimplyBind('checked').of(radioA).to ()-> invokeCount.domRadioSingle++ radioA.checked = true radioA.emit 'change' expect(invokeCount.domRadioSingle).to.equal(1) # ==== DOMRadio ================================================================================= SimplyBind('checked').of(radioFields).to ()-> invokeCount.domRadio++ radioB.checked = true radioB.emit 'change' expect(invokeCount.domRadio).to.equal(1) # ==== Event ================================================================================= SimplyBind.defaultOptions.updateEvenIfSame = true SimplyBind('event:someEvent').of(eventEmitterA).to ()-> invokeCount.event++ eventEmitterA.emit 'someEvent' expect(invokeCount.event).to.equal(1) SimplyBind.defaultOptions.updateEvenIfSame = false SimplyBind.defaultOptions.updateOnBind = true restartSandbox() test "Receive a value as a dependent", ()-> SimplyBind.defaultOptions.updateOnBind = false invokeCount = 'object': 0 'array': 0 'proxy': 0 'function': 0 'domAttr': 0 'domText': 0 'domValue': 0 'domCheckboxSingle': 0 'domCheckbox': 0 'domRadio': 0 'event': 0 # ==== Objects ================================================================================= SimplyBind('prop').of(objectA).to ()-> invokeCount.object++ SimplyBind(()-> true).to('prop').of(objectA) expect(objectA.prop).to.equal(true) expect(invokeCount.object).to.equal(1) # ==== Arrays ================================================================================= expect(objectA.list.length).to.equal(10) SimplyBind('array:list').of(objectA).to ()-> invokeCount.array++ SimplyBind(()-> [1,2]).to('array:list').of(objectA) expect(objectA.list.length).to.equal(2) expect(invokeCount.array).to.equal(1) # ==== Proxy functions ================================================================================= obj = base:10, test:(a,b)-> (a+b)*@base proxyInterface = SimplyBind('func:test').of(obj).to ()-> invokeCount.proxy++ SimplyBind(()-> true).to(proxyInterface) expect(invokeCount.proxy).to.equal(1) if isBrowser # ==== DOMAttr ================================================================================= SimplyBind('attr:someattr').of(regA).to ()-> invokeCount.domAttr++ SimplyBind(()-> 'true').to('attr:someattr').of(regA) expect(regA.getAttribute('someattr')).to.equal('true') expect(invokeCount.domAttr).to.equal(1) # ==== DOMText ================================================================================= SimplyBind('textContent').of(regA).to ()-> invokeCount.domText++ SimplyBind(()-> 'true').to('textContent').of(regA) expect(regA.textContent).to.equal('true') expect(invokeCount.domText).to.equal(1) # ==== DOMValue ================================================================================= SimplyBind('value').of(inputA).to ()-> invokeCount.domValue++ SimplyBind(()-> 'true').to('value').of(inputA) expect(inputA.value).to.equal('true') expect(invokeCount.domValue).to.equal(1) # ==== DOMCheckbox Single ================================================================================= expect(checkboxA.checked).to.be.false SimplyBind('checked').of(checkboxA).to ()-> invokeCount.domCheckboxSingle++ SimplyBind(()-> 'truthy').to('checked').of(checkboxA) expect(checkboxA.checked).to.be.true expect(invokeCount.domCheckboxSingle).to.equal(1) # ==== DOMCheckbox ================================================================================= SimplyBind('checked').of($checkboxFields).to ()-> invokeCount.domCheckbox++ SimplyBind(()-> ['checkboxB', 'checkboxC']).to('checked').of($checkboxFields) expect(checkboxA.checked).to.be.false expect(checkboxB.checked).to.be.true expect(checkboxC.checked).to.be.true expect(invokeCount.domCheckbox).to.equal(1) SimplyBind(()-> 'checkboxA').to('checked').of($checkboxFields) expect(checkboxA.checked).to.be.true expect(checkboxB.checked).to.be.false expect(checkboxC.checked).to.be.false expect(invokeCount.domCheckbox).to.equal(2) # ==== DOMRadio ================================================================================= SimplyBind('checked').of($radioFields).to ()-> invokeCount.domRadio++ SimplyBind(()-> 'radioC').to('checked').of($radioFields) expect(radioA.checked).to.be.false expect(radioB.checked).to.be.false expect(radioC.checked).to.be.true expect(invokeCount.domRadio).to.equal(1) # ==== Event ================================================================================= SimplyBind('event:someEvent').of(eventEmitterA).to ()-> invokeCount.event++ SimplyBind(()-> true).to('event:someEvent').of(eventEmitterA) expect(invokeCount.event).to.equal(1) SimplyBind.defaultOptions.updateOnBind = true restartSandbox()