UNPKG

@danielkalen/simplybind

Version:

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

1,432 lines (975 loc) 181 kB
if mocha? and window? isBrowser = true mocha.setup('tdd') mocha.bail() unless @isSauceLabsTest or location.hostname else isBrowser = false global.window = global chai = require 'chai' chaiSpies = require 'chai-spies' SimplyBind = require '../dist/simplybind.node.js' startSandbox = require('./spec-helper.js').startSandbox restartSandbox = require('./spec-helper.js').restartSandbox chai.use chaiSpies expect = chai.expect should = chai.should() suite "SimplyBind", ()-> suiteSetup(startSandbox) suite "Settings/Binding-Options", ()-> test "SimplyBind.settings should contain the global settings list", ()-> settings = SimplyBind.settings expect(settings.silent).not.to.be.undefined expect(settings.placeholder).not.to.be.undefined expect(settings.trackArrayChildren).not.to.be.undefined test "SimplyBind.defaultOptions should contain the default binding options list", ()-> options = SimplyBind.defaultOptions expect(options.dispatchEvents).not.to.be.undefined expect(options.updateEvenIfSame).not.to.be.undefined expect(options.updateOnBind).not.to.be.undefined expect(options.simpleSelector).not.to.be.undefined expect(options.promiseTransforms).not.to.be.undefined test "SimplyBind.settings.placeholder should only accept arrays with lengths of 2 as a new value", ()-> expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] SimplyBind.settings.placeholder = 'na' expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] SimplyBind.settings.placeholder = undefined expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] SimplyBind.settings.placeholder = null expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] SimplyBind.settings.placeholder = ()-> expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] SimplyBind.settings.placeholder = [] expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] SimplyBind.settings.placeholder = ['{{', '{{', '}}'] expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] SimplyBind.settings.placeholder = ['[[', ']]'] expect(SimplyBind.settings.placeholder).to.eql ['[[', ']]'] SimplyBind.settings.placeholder = ['{{', '}}'] expect(SimplyBind.settings.placeholder).to.eql ['{{', '}}'] test "A custom binding options object can be passed as SimplyBind's second argument to be used for all subscribers added to that publisher interface", ()-> expect(SimplyBind.defaultOptions.updateOnBind).to.be.true dispatcher = value:'uniqueValue' receiver = {1,2,3,4} SimplyBind('value', updateOnBind:false).of(dispatcher) .to('1').of(receiver) .and.to('2').of(receiver) .and.to('3').of(receiver) expect(receiver[1]).not.to.equal 'uniqueValue' expect(receiver[2]).not.to.equal 'uniqueValue' expect(receiver[3]).not.to.equal 'uniqueValue' SimplyBind('value').of(dispatcher) .to('4').of(receiver) expect(receiver[4]).to.equal 'uniqueValue' expect(SimplyBind.defaultOptions.updateOnBind).to.be.true restartSandbox() test "A truthy value can be passed as SimplyBind's third argument to save the specified options to all subscribers and future subscribers of a publisher", ()-> expect(SimplyBind.defaultOptions.updateOnBind).to.be.true dispatcher = value:'uniqueValue' receiver = {1,2,3,4} SimplyBind('value', {updateOnBind:false}, true).of(dispatcher) .to('1').of(receiver) .and.to('2').of(receiver) .and.to('3').of(receiver) expect(receiver[1]).not.to.equal 'uniqueValue' expect(receiver[2]).not.to.equal 'uniqueValue' expect(receiver[3]).not.to.equal 'uniqueValue' SimplyBind('value').of(dispatcher) .to('4').of(receiver) expect(receiver[4]).not.to.equal 'uniqueValue' expect(SimplyBind.defaultOptions.updateOnBind).to.be.true restartSandbox() test "A custom binding options object can be used even for a cached binding/publisher", ()-> expect(SimplyBind.defaultOptions.updateOnBind).to.be.true dispatcher = value:'uniqueValue' receiver = {1,2,3,4} SimplyBind('value', {updateOnBind:false}, true).of(dispatcher) .to('1').of(receiver) .and.to('2').of(receiver) .and.to('3').of(receiver) expect(receiver[1]).not.to.equal 'uniqueValue' expect(receiver[2]).not.to.equal 'uniqueValue' expect(receiver[3]).not.to.equal 'uniqueValue' SimplyBind('value', updateOnBind:true).of(dispatcher) .to('4').of(receiver) expect(receiver[4]).to.equal 'uniqueValue' expect(SimplyBind.defaultOptions.updateOnBind).to.be.true restartSandbox() test "Creating a new binding interface for a cached binding without passing an options object shouldn't overwrite existing options", ()-> expect(SimplyBind.defaultOptions.updateOnBind).to.be.true dispatcher = value:'uniqueValue' receiver = {1,2,3,4} SimplyBind('value', {updateOnBind:false}, true).of(dispatcher) .to('1').of(receiver) .and.to('2').of(receiver) .and.to('3').of(receiver) expect(receiver[1]).not.to.equal 'uniqueValue' expect(receiver[2]).not.to.equal 'uniqueValue' expect(receiver[3]).not.to.equal 'uniqueValue' SimplyBind('value', null, true).of(dispatcher) .to('4').of(receiver) expect(receiver[4]).not.to.equal 'uniqueValue' expect(SimplyBind.defaultOptions.updateOnBind).to.be.true restartSandbox() suite "Argument Values", ()-> suiteSetup(restartSandbox) suite "SimplyBind() function", ()-> test "Can only accept an object type of String, Number, Array, Function, or an existing binding instance", ()-> expect ()-> SimplyBind('s') .not.to.throw() expect ()-> SimplyBind(0) .not.to.throw() expect ()-> SimplyBind([]) .not.to.throw() expect ()-> SimplyBind(()->) .not.to.throw() expect ()-> SimplyBind(SimplyBind('prop1').of(objectA)) .not.to.throw() expect ()-> SimplyBind(true) .to.throw() expect ()-> SimplyBind(null) .to.throw() expect ()-> SimplyBind(undefined) .to.throw() expect ()-> SimplyBind({}) .to.throw() expect ()-> SimplyBind(new Date()) .to.throw() if isBrowser expect ()-> SimplyBind($('<div />')[0]) .to.throw() if window.Map and window.Set and window.Symbol expect ()-> SimplyBind(new Map()) .to.throw() expect ()-> SimplyBind(new WeakMap()) .to.throw() expect ()-> SimplyBind(new Set()) .to.throw() expect ()-> SimplyBind(new WeakSet()) .to.throw() expect ()-> SimplyBind(Symbol('a')) .to.throw() test "Should throw when passing an empty string", ()-> expect ()-> SimplyBind('') .to.throw() suite ".of() method", ()-> test "Can only accept non-primitive objects", ()-> expect ()-> SimplyBind(0).of([]) .not.to.throw() expect ()-> SimplyBind(0).of(()->) .not.to.throw() expect ()-> SimplyBind(0).of({}) .not.to.throw() expect ()-> SimplyBind(0).of(new Date()) .not.to.throw() if isBrowser expect ()-> SimplyBind(0).of($('<div />')[0]) .not.to.throw() if window.Map and window.Set and window.Symbol expect ()-> SimplyBind(0).of(new Map()) .not.to.throw() expect ()-> SimplyBind(0).of(new WeakMap()) .not.to.throw() expect ()-> SimplyBind(0).of(new Set()) .not.to.throw() expect ()-> SimplyBind(0).of(new WeakSet()) .not.to.throw() expect ()-> SimplyBind(0).of(Symbol(0)) .to.throw() expect ()-> SimplyBind(0).of(SimplyBind('prop1').of(objectA)) .not.to.throw() expect ()-> SimplyBind(0).of('s') .to.throw() expect ()-> SimplyBind(0).of(0) .to.throw() expect ()-> SimplyBind(0).of(true) .to.throw() expect ()-> SimplyBind(0).of(null) .to.throw() expect ()-> SimplyBind(0).of(undefined) .to.throw() test "Will extract and use the subject bound object from the instance if passed a binding instance", ()-> bound = SimplyBind('prop3').of(objectA).to('prop3').of(objectB) bound.set 'standard' expect(objectA.prop3).to.equal 'standard' expect(objectB.prop3).to.equal 'standard' bound2 = SimplyBind('prop4').of(bound).to('prop4').of(objectB) bound2.set 'ofBounded' expect(objectA.prop4).to.equal 'ofBounded' expect(objectB.prop4).to.equal 'ofBounded' suite ".ofEvent() method", ()-> test "Can only accept string values", ()-> expect ()-> SimplyBind(0).ofEvent('s') .not.to.throw() expect ()-> SimplyBind(0).of(objectA).toEvent('s') .not.to.throw() expect ()-> SimplyBind(0).ofEvent([]) .to.throw() expect ()-> SimplyBind(0).ofEvent(()->) .to.throw() expect ()-> SimplyBind(0).ofEvent({}) .to.throw() expect ()-> SimplyBind(0).ofEvent(new Date()) .to.throw() if isBrowser expect ()-> SimplyBind(0).ofEvent($('<div />')[0]) .to.throw() if window.Map and window.Set and window.Symbol expect ()-> SimplyBind(0).ofEvent(new Map()) .to.throw() expect ()-> SimplyBind(0).ofEvent(new WeakMap()) .to.throw() expect ()-> SimplyBind(0).ofEvent(new Set()) .to.throw() expect ()-> SimplyBind(0).ofEvent(new WeakSet()) .to.throw() expect ()-> SimplyBind(0).ofEvent(Symbol(0)) .to.throw() expect ()-> SimplyBind(0).ofEvent(SimplyBind(0).ofEvent('s')) .to.throw() expect ()-> SimplyBind(0).ofEvent(0) .to.throw() expect ()-> SimplyBind(0).ofEvent(true) .to.throw() expect ()-> SimplyBind(0).ofEvent(null) .to.throw() expect ()-> SimplyBind(0).ofEvent(undefined) .to.throw() suite ".transform/condition/All/Self() methods", ()-> test "Can only accept function values", ()-> window.origLog = console.warn console.warn = chai.spy() timesCalled = 0 SimplyBind(0).of({}).to(()->).transform(()->) expect(console.warn).to.have.been.called.exactly(timesCalled += 0) SimplyBind(0).of({}).to(()->).transformAll(()->) expect(console.warn).to.have.been.called.exactly(timesCalled += 0) SimplyBind(0).of({}).to(()->).condition(()->) expect(console.warn).to.have.been.called.exactly(timesCalled += 0) SimplyBind(0).of({}).to(()->).conditionAll(()->) expect(console.warn).to.have.been.called.exactly(timesCalled += 0) SimplyBind(0).of({}).transformSelf(()->) expect(console.warn).to.have.been.called.exactly(timesCalled += 0) SimplyBind(0).of({}).to(()->).transform([]) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).transformAll({}) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).conditionAll(new Date()) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) if isBrowser SimplyBind(0).of({}).to(()->).condition($('<div />')[0]) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) if window.Map and window.Set and window.Symbol SimplyBind(0).of({}).to(()->).transform(new Map()) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).transformAll(new WeakMap()) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).condition(new Set()) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).conditionAll(new WeakSet()) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).transformSelf(Symbol(0)) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).transform(SimplyBind('prop1').of(objectA)) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).transformAll('s') expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).condition(0) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).to(()->).conditionAll(true) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).transformSelf(null) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) SimplyBind(0).of({}).transformSelf(undefined) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) console.warn = origLog suite ".throttle()", ()-> test "Will only accept a truthy number-type argument or a boolean false", ()-> binding = SimplyBind('prop').of(objectA)._ expect(binding.throttleRate or binding.thR).to.be.undefined SimplyBind('prop').of(objectA).throttle(200) expect(binding.throttleRate or binding.thR).to.equal 200 SimplyBind('prop').of(objectA).throttle(false) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle(0) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle('0') expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle(NaN) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle({}) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle([]) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle(()->) expect(binding.throttleRate or binding.thR).to.equal.undefined if window.Map and window.Set and window.Symbol SimplyBind('prop').of(objectA).throttle(new Map()) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle(new WeakMap()) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle(new Set()) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle(new WeakSet()) expect(binding.throttleRate or binding.thR).to.equal.undefined SimplyBind('prop').of(objectA).throttle(Symbol(9)) expect(binding.throttleRate or binding.thR).to.equal.undefined restartSandbox() suite "Errors & Warnings", ()-> suiteSetup(restartSandbox) test "Warning when binding a property of a jQuery object containing multiple els", ()-> if not isBrowser then @skip() else window.origLog = console.warn console.warn = chai.spy() SimplyBind('value').of $('input') expect(console.warn).to.have.been.called() console.warn = origLog test "Warning when binding a property of a jQuery/NodeList/HTMLCollection object containing zero els", ()-> if not isBrowser then @skip() else expect ()-> SimplyBind('value').of $('nonexistent') .to.throw() expect ()-> SimplyBind('value').of document.querySelectorAll('nonexistent') .to.throw() expect ()-> SimplyBind('value').of document.getElementsByTagName('nonexistent') .to.throw() test "Warning when binding a property of a jQuery object containing zero els", ()-> if not isBrowser then @skip() else expect ()-> SimplyBind('value').of $('input[type="nonexistent"]') .to.throw() test "Warning when binding a property of a NodeList/HTMLCollection containing multiple els", ()-> if not isBrowser then @skip() else origLog = console.warn console.warn = chai.spy() SimplyBind('value').of document.querySelectorAll('input') expect(console.warn).to.have.been.called() console.warn = origLog test "No Warning when binding 'checked' of a jQuery object containing multiple radio/checkbox inputs", ()-> if not isBrowser then @skip() else origLog = console.warn console.warn = chai.spy() SimplyBind('checked').of $radioFields expect(console.warn).not.to.have.been.called() SimplyBind('value').of $radioFields expect(console.warn).to.have.been.called.exactly(1) console.warn = origLog test "No Warning when binding 'checked' of a NodeList/HTMLCollection/Array containing multiple radio/checkbox inputs", ()-> if not isBrowser then @skip() else origLog = console.warn console.warn = chai.spy() SimplyBind('checked').of $radioFields.toArray() expect(console.warn).not.to.have.been.called() SimplyBind('checked').of radioFields expect(console.warn).not.to.have.been.called() SimplyBind('value').of $radioFields.toArray() SimplyBind('value').of radioFields expect(console.warn).to.have.been.called.exactly(2) console.warn = origLog test "Warning when binding a property of a NodeList/HTMLCollection/Array/jQuery containing elements with mixed types", ()-> if not isBrowser then @skip() else origLog = console.warn console.warn = chai.spy() SimplyBind('checked').of $radioFields.add($checkboxFields).toArray() expect(console.warn).to.have.been.called.exactly(1) SimplyBind('checked').of $radioFields.add($checkboxFields) expect(console.warn).to.have.been.called.exactly(2) SimplyBind('checked').of document.querySelectorAll 'input' expect(console.warn).to.have.been.called.exactly(3) console.warn = origLog test "Warning when creating a binding and passing a non-function object to .transform/.condition/All", ()-> binding = SimplyBind('prop1').of(objectA).to('prop1').of(objectB) origLog = console.warn console.warn = chai.spy() timesCalled = 0 binding.transform [] expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transformAll [] expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform {} expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform new Date() expect(console.warn).to.have.been.called.exactly(timesCalled += 1) if isBrowser binding.transform $('<div />')[0] expect(console.warn).to.have.been.called.exactly(timesCalled += 1) if window.Map and window.Set and window.Symbol binding.transform new Map() expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform new WeakMap() expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform new Set() expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform new WeakSet() expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform Symbol(0) expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform 's' expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform 0 expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform true expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform null expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.transform undefined expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.condition [] expect(console.warn).to.have.been.called.exactly(timesCalled += 1) binding.conditionAll [] expect(console.warn).to.have.been.called.exactly(timesCalled += 1) console.warn = origLog restartSandbox() test "Warning when passed a non-number argument selector to an event binding", ()-> origLog = console.warn console.warn = chai.spy() SimplyBind('value').ofEvent 'someEvent' SimplyBind('15').ofEvent 'someEvent' SimplyBind(' 1aos95').ofEvent 'someEvent' expect(console.warn).to.have.been.called.exactly(1) console.warn = origLog test "No warnings should be thrown when SimplyBind.options.silent is on", ()-> SimplyBind.settings.silent = true origLog = console.warn console.warn = chai.spy() SimplyBind('value').ofEvent('someEvent') SimplyBind('prop3').of(objectA).to('prop3').of(objectB).transform [] expect(console.warn).not.to.have.been.called() console.warn = origLog SimplyBind.settings.silent = false test "Errors should still be thrown when SimplyBind.options.silent is on", ()-> SimplyBind.settings.silent = true expect ()-> SimplyBind '' .to.throw() SimplyBind.settings.silent = false test "No errors should be thrown when scanning for placeholders on a non-string object", ()-> dispatcher = 'prop':'value' receiver = 'prop':null expect(()-> SimplyBind('prop').of(dispatcher) .to('prop.placeholder').of(receiver) ).not.to.throw() expect(receiver.prop).not.to.equal('value') suite "Internal Behavior", ()-> suiteSetup(restartSandbox) test "A binding should have the correct public properies available", ()-> binding = SimplyBind('prop1').of(objectA).to('prop2').of(objectB) expect(binding.value).not.to.be.undefined expect(binding.original).not.to.be.undefined expect(binding.subscribers).not.to.be.undefined expect(binding._).not.to.be.undefined restartSandbox() test "Bindings to a nonexistent object prop should not create instantiate the object prop at first", ()-> expect(objectA.prop20).to.be.undefined expect(objectB.prop20).to.be.undefined binding = SimplyBind('prop20').of(objectA).to('prop20').of(objectB) expect(objectA.prop20).to.be.undefined expect(objectB.prop20).to.be.undefined binding.set 'new value' expect(objectA.prop20).to.equal 'new value' expect(objectB.prop20).to.equal 'new value' restartSandbox() test "The binding.value property and binding.get() method should return the same value", ()-> tempObject = 'a': 'with' 'b': 'text {{verb}} a placeholder' SimplyBind('a').of(tempObject).to('b.verb').of tempObject bound = SimplyBind('b.verb').of(tempObject) expect(tempObject.b).to.equal 'text with a placeholder' expect(bound.value).to.equal 'text with a placeholder' expect(bound.get()).to.equal 'with' tempObject.a = 'without' expect(tempObject.b).to.equal 'text without a placeholder' expect(bound.value).to.equal 'text without a placeholder' expect(bound.get()).to.equal 'without' suite "Method availablity in different stages", ()-> test "Stage 0 (Selection Stage)", ()-> binding = ()-> SimplyBind('1') expect(()-> binding().of objectA).not.to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').to.throw() expect(()-> binding().set 'value').to.throw() expect(()-> binding().get()).to.throw() expect(()-> binding().transform ()->).to.throw() expect(()-> binding().transformAll ()->).to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).to.throw() expect(()-> binding().conditionAll ()->).to.throw() expect(()-> binding().bothWays()).to.throw() expect(()-> binding().stopPolling()).to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).to.throw() expect(()-> binding().unBind()).to.throw() expect(()-> binding().chainTo ()->).to.throw() expect(()-> binding().updateSubsOnEvent 'a').to.throw() expect(()-> binding().removeEvent 'a').to.throw() expect(()-> binding().throttle 5).to.throw() binding = ()-> SimplyBind('1').ofEvent 'click' expect(()-> binding().of objectA).not.to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').to.throw() expect(()-> binding().set 'value').to.throw() expect(()-> binding().get()).to.throw() expect(()-> binding().transform ()->).to.throw() expect(()-> binding().transformAll ()->).to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).to.throw() expect(()-> binding().conditionAll ()->).to.throw() expect(()-> binding().bothWays()).to.throw() expect(()-> binding().stopPolling()).to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).to.throw() expect(()-> binding().unBind()).to.throw() expect(()-> binding().chainTo ()->).to.throw() expect(()-> binding().updateSubsOnEvent 'a').to.throw() expect(()-> binding().removeEvent 'a').to.throw() expect(()-> binding().throttle 5).to.throw() binding = ()-> SimplyBind('1').of(objectA).to 'prop1' expect(()-> binding().of objectA).not.to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').to.throw() expect(()-> binding().set 'value').to.throw() expect(()-> binding().get()).to.throw() expect(()-> binding().transform ()->).to.throw() expect(()-> binding().transformAll ()->).to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).to.throw() expect(()-> binding().conditionAll ()->).to.throw() expect(()-> binding().bothWays()).to.throw() expect(()-> binding().stopPolling()).to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).to.throw() expect(()-> binding().unBind()).to.throw() expect(()-> binding().chainTo ()->).to.throw() expect(()-> binding().updateSubsOnEvent 'a').to.throw() expect(()-> binding().removeEvent 'a').to.throw() expect(()-> binding().throttle 5).to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop:prop1').of(objectB).and.to 'prop2' expect(()-> binding().of objectA).not.to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').to.throw() expect(()-> binding().set 'value').to.throw() expect(()-> binding().get()).to.throw() expect(()-> binding().transform ()->).to.throw() expect(()-> binding().transformAll ()->).to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).to.throw() expect(()-> binding().conditionAll ()->).to.throw() expect(()-> binding().bothWays()).to.throw() expect(()-> binding().stopPolling()).to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).to.throw() expect(()-> binding().unBind()).to.throw() expect(()-> binding().chainTo ()->).to.throw() expect(()-> binding().updateSubsOnEvent 'a').to.throw() expect(()-> binding().removeEvent 'a').to.throw() expect(()-> binding().throttle 5).to.throw() test "Stage 1 (Indication Stage)", ()-> binding = ()-> SimplyBind('1').of objectA expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').not.to.throw() expect(()-> binding().toEvent '/local').not.to.throw() expect(()-> binding().and.to 'prop2').to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).to.throw() expect(()-> binding().transformAll ()->).to.throw() expect(()-> binding().transformSelf ()->).not.to.throw() expect(()-> binding().condition ()->).to.throw() expect(()-> binding().conditionAll ()->).to.throw() expect(()-> binding().bothWays()).to.throw() expect(()-> binding().stopPolling()).to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).to.throw() expect(()-> binding().unBind()).to.throw() expect(()-> binding().chainTo ()->).to.throw() expect(()-> binding().updateSubsOnEvent 'a').to.throw() expect(()-> binding().removeEvent 'a').to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').ofEvent('click').of eventEmitterA expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').not.to.throw() expect(()-> binding().toEvent '/local').not.to.throw() expect(()-> binding().and.to 'prop2').to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).to.throw() expect(()-> binding().transformAll ()->).to.throw() expect(()-> binding().transformSelf ()->).not.to.throw() expect(()-> binding().condition ()->).to.throw() expect(()-> binding().conditionAll ()->).to.throw() expect(()-> binding().bothWays()).to.throw() expect(()-> binding().stopPolling()).to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).to.throw() expect(()-> binding().unBind()).to.throw() expect(()-> binding().chainTo ()->).to.throw() expect(()-> binding().updateSubsOnEvent 'a').to.throw() expect(()-> binding().removeEvent 'a').to.throw() expect(()-> binding().throttle 5).not.to.throw() test "Stage 2 (Binding Complete Stage)", ()-> binding = ()-> SimplyBind('1').of(objectA).to('prop1').of objectB expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).and.to('prop2').of objectB expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).bothWays() expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transform ()-> expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transformAll ()-> expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).condition ()-> expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).conditionAll ()-> expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transform(()->).and.to ()-> expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transform(()->).and.to(()->).chainTo ()-> expect(()-> binding().of objectA).to.throw() expect(()-> binding().ofEvent objectA).to.throw() expect(()-> binding().to 'prop2').to.throw() expect(()-> binding().toEvent '/local').to.throw() expect(()-> binding().and.to 'prop2').not.to.throw() expect(()-> binding().set 'value').not.to.throw() expect(()-> binding().get()).not.to.throw() expect(()-> binding().transform ()->).not.to.throw() expect(()-> binding().transformAll ()->).not.to.throw() expect(()-> binding().transformSelf ()->).to.throw() expect(()-> binding().condition ()->).not.to.throw() expect(()-> binding().conditionAll ()->).not.to.throw() expect(()-> binding().bothWays()).not.to.throw() expect(()-> binding().stopPolling()).not.to.throw() expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw() expect(()-> binding().unBind()).not.to.throw() expect(()-> binding().chainTo ()->).not.to.throw() expect(()-> binding().updateSubsOnEvent 'a').not.to.throw() expect(()-> binding().removeEvent 'a').not.to.throw() expect(()-> binding().throttle 5).not.to.throw() suite "Cached Bindings", ()-> test "A cached version of the binding should be returned when re-creating an existing one", ()-> # ==== ObjectProp ================================================================================= expect(SimplyBind('prop1').of(objectA)._.ID) .to.equal(SimplyBind('prop1').of(objectA)._.ID) expect(SimplyBind('prop1').of(objectA).to('prop1').of(objectB)._.subs[0].ID) .to.equal(SimplyBind('prop1').of(objectB)._.ID) if isBrowser # ==== DOMAttr ================================================================================= expect(SimplyBind('attr:someattr').of(regA)._.ID) .to.equal(SimplyBind('attr:someattr').of(regA)._.ID) expect(SimplyBind('attr:someattr').of(regA).to('attr:someattr').of(regB)._.subs[0].ID) .to.equal(SimplyBind('attr:someattr').of(regB)._.ID) # ==== DOMValue ================================================================================= expect(SimplyBind('value').of(inputA)._.ID) .to.equal(SimplyBind('value').of(inputA)._.ID) expect(SimplyBind('value').of(inputA).to('value').of(inputB)._.subs[0].ID) .to.equal(SimplyBind('value').of(inputB)._.ID) # ==== DOMCheckbox Single ================================================================================= expect(SimplyBind('checked').of(checkboxA)._.ID) .to.equal(SimplyBind('checked').of(checkboxA)._.ID) expect(SimplyBind('checked').of(checkboxA).to('checked').of(checkboxB)._.subs[0].ID) .to.equal(SimplyBind('checked').of(checkboxB)._.ID) # ==== DOMCheckbox ================================================================================= expect(SimplyBind('checked').of($checkboxFields)._.ID) .to.equal(SimplyBind('checked').of($checkboxFields)._.ID) # ==== DOMRadio ================================================================================= expect(SimplyBind('checked').of($radioFields)._.ID) .to.equal(SimplyBind('checked').of($radioFields)._.ID) # ==== DOMText ================================================================================= expect(SimplyBind('textContent').of(regA)._.ID) .to.equal(SimplyBind('textContent').of(regA)._.ID) expect(SimplyBind('textContent').of(regA).to('textContent').of(regB)._.subs[0].ID) .to.equal(SimplyBind('textContent').of(regB)._.ID) # ==== Event ================================================================================= expect(SimplyBind(0).ofEvent('someEvent').of(eventEmitterA)._.ID) .to.equal(SimplyBind(0).ofEvent('someEvent').of(eventEmitterA)._.ID) expect(SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).toEvent('someEvent').of(eventEmitterB)._.subs[0].ID) .to.equal(SimplyBind(0).ofEvent('someEvent').of(eventEmitterB)._.ID) # ==== Array ================================================================================= expect(SimplyBind(arrayA)._.ID) .to.equal(SimplyBind(arrayA)._.ID) expect(SimplyBind(arrayA).to(arrayB)._.subs[0].ID) .to.equal(SimplyBind(arrayB)._.ID) # ==== Function ================================================================================= expect(SimplyBind(fnA)._.ID) .to.equal(SimplyBind(fnA)._.ID) expect(SimplyBind(fnA).to(fnB)._.subs[0].ID) .to.equal(SimplyBind(fnB)._.ID) restartSandbox() test "When binding an object prop with a placeholder indicator in the selector for an existing binding that didn't have indicators, placeholders will be rescanned", ()-> dispatcher = 'prop':'This is an {{size}} size shirt' receiver = 'prop':'' SimplyBind('prop').of(dispatcher) .to('prop').of(receiver) expect(receiver.prop).to.equal(dispatcher.prop) expect(dispatcher.prop).to.equal 'This is an {{size}} size shirt' SimplyBind('prop').of(dispatcher) .to('prop.size').of(receiver) dispatcher.prop = 'XXL' expect(dispatcher.prop).to.equal 'XXL' expect(receiver.prop).to.equal 'This is an XXL size shirt' test "When re-binding a binding with placeholder where the global placeholders regEx has changed to a diff regEx from present in the original binding, the binding's regEx shouldn't change", ()-> dispatcher = 'noun':'', 'adjective':'' receiver = 'prop':'This {{noun}} is **adjective**' SimplyBind('noun').of(dispatcher) .to('prop.noun').of(receiver) dispatcher.noun = 'test' expect(receiver.prop).to.equal 'This test is **adjective**' SimplyBind.settings.placeholder = ['**', '**'] SimplyBind('adjective').of(dispatcher) .to('prop.adjective').of(receiver) dispatcher.adjective = 'good' expect(receiver.prop).to.equal 'This test is **adjective**' dispatcher.noun = 'suite' expect(receiver.prop).to.equal 'This suite is **adjective**' SimplyBind.settings.placeholder = ['{{', '}}'] test "When re-binding an object prop that was deleted manually make sure to re-make the property a live one", ()-> objectA.prop = 1 SimplyBind('prop').of(objectA) .to('prop').of(objectB) expect(objectB.prop).to.equal(objectA.prop) objectA.prop = 2 expect(objectB.prop).to.equal(objectA.prop) delete objectB.prop objectA.prop = 3 expect(objectB.prop).not.to.equal(objectA.prop) SimplyBind('prop').of(objectA) .to('prop').of(objectB) expect(objectB.prop).to.equal(objectA.prop) objectA.prop = 4 expect(objectB.prop).to.equal(objectA.prop) restartSandbox() test "When binding an index of an array that's brand new and the array is tracked and so are its children, the new index should be made so it also tracked", ()-> SimplyBind.settings.trackArrayChildren = true invokeCount = 0 SimplyBind(arrayA).to ()-> invokeCount++ expect(invokeCount).to.equal 1 arrayA[3] = 44 expect(invokeCount).to.equal 2 arrayA[20] = 21 expect(invokeCount).to.equal 2 SimplyBind('18').of(arrayA) arrayA[18] = 188 expect(invokeCount).to.equal 4 arrayA[18] = 1888 expect(invokeCount).to.equal 5 SimplyBind.settings.trackArrayChildren = false restartSandbox() suite "Data Binding Behavior", ()-> suiteSetup(restartSandbox) test "Object properties should behave the same way across all object types", ()-> dispatcher = 'standard': objectA 'array': arrayA 'function': ()-> 'domDiv': if isBrowser then $('<div />')[0] else {} 'domInput': if isBrowser then $('<input />')[0] else {} 'domNode': if isBrowser then $('<div>text</div>')[0].childNodes[0] else {} 'date': new Date() receiver = 'standard': objectB 'array': arrayB 'function': ()-> 'domDiv': if isBrowser then $('<div />')[0] else {} 'domInput': if isBrowser then $('<input />')[0] else {} 'domNode': if isBrowser then $('<div>text</div>')[0].childNodes[0] else {} 'date': new Date() for name,object of dispatcher SimplyBind('prop').of(dispatcher[name]).to('prop').of(receiver[name]) expect(receiver[name].prop).to.be.undefined SimplyBind('prop').of(dispatcher[name]).set(100) expect(receiver[name].prop).to.equal(100) restartSandbox() test "Object properties should be made into live properties unless they are inherited or unconfigurable", ()-> SimplyBind.defaultOptions.updateOnBind = false invokeCount = 0 ### ========================================================================== ### SimplyBind('prop1').of(objectA).to ()-> invokeCount++ expect(invokeCount).to.equal(0) objectA.prop1 = !objectA.prop1 expect(invokeCount).to.equal(1) ### ========================================================================== ### SimplyBind('nonexistent').of(objectA).to ()-> invokeCount++ expect(invokeCount).to.equal(1) objectA.nonexistent = !objectA.nonexistent expect(invokeCount).to.equal(2) ### ========================================================================== ### Object.defineProperty objectA, 'protected', value:'cant touch this' SimplyBind('protected').of(objectA).to ()-> invokeCount++ expect(invokeCount).to.equal(2) objectA.protected = !objectA.protected expect(invokeCount).to.equal(2) ### ========================================================================== ### obj = Object.create(protected:'cant touch this') SimplyBind('protected').of(obj).to ()-> invokeCount++ expect(invokeCount).to.equal(2) obj.protected = !obj.protected expect(invokeCount).to.equal(2) SimplyBind.defaultOptions.updateOnBind = true restartSandbox() test "The property's original setter (if exists) should be invoked during a live property update", ()-> prop7 = 'hey!' invokeCountGet = 0 invokeCountSet = 0 Object.defineProperty objectA, 'prop7', enumerable: true configurable: true get: ()-> invokeCountGet++ prop7 set: (val)-> invokeCountSet++ prop7 = val expect(objectA.prop7).to.equal 'hey!' expect(invokeCountGet).to.equal 1 expect(invokeCountSet).to.equal 0 SimplyBind('prop7').of(objectA).to('prop3').of objectB objectA.prop7 = 'hello!' expect(invokeCountGet).to.equal 2 expect(invokeCountSet).to.equal 1 expect(objectA.prop7