@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
393 lines (258 loc) • 13 kB
text/coffeescript
suite "Placeholders", ()->
test "Property names with periods (.) indicate a placeholder", ()->
dispatcher = 'value': 'Medium'
objectA.prop = 'The size of this shirt is {{size}}, man!'
if isBrowser
inputA.value = 'The size of this shirt is {{size}}, man!'
regA.textContent = 'The size of this shirt is {{size}}, man!'
regA.setAttribute 'someattr', 'The size of this shirt is {{size}}, man!'
SimplyBind('value').of(dispatcher)
.to('prop.size').of(objectA)
if isBrowser
SimplyBind('value').of(dispatcher)
.to('value.size').of(inputA)
.and.to('textContent.size').of(regA)
.and.to('attr:someattr.size').of(regA)
expect(objectA.prop).to.equal 'The size of this shirt is Medium, man!'
if isBrowser
expect(inputA.value).to.equal 'The size of this shirt is Medium, man!'
expect(regA.textContent).to.equal 'The size of this shirt is Medium, man!'
expect(regA.getAttribute 'someattr').to.equal 'The size of this shirt is Medium, man!'
restartSandbox()
test "No changes should be made to the string when trying to update a placeholder that doesn't exist", ()->
objectB.prop1 = 'The size of this shirt is _____, man!'
binding = SimplyBind('prop1').of(objectA).to('prop1.size').of(objectB)
binding.set 'Medium'
expect(objectB.prop1).to.equal 'The size of this shirt is _____, man!'
restartSandbox()
test "The updating value doesn't have to be a string", ()->
objectB.prop1 = 'The size of this shirt is {{size}}, man!'
binding = SimplyBind('prop1').of(objectA).to('prop1.size').of(objectB)
binding.set false
expect(objectB.prop1).to.equal 'The size of this shirt is false, man!'
binding.set null
expect(objectB.prop1).to.equal 'The size of this shirt is null, man!'
binding.set {}
expect(objectB.prop1).to.equal 'The size of this shirt is [object Object], man!'
restartSandbox()
test "If a property name has more than one period, only the first will be accounted for", ()->
objectB.prop1 = 'The size of this shirt is {{size.redundant}}, man!'
binding = SimplyBind('prop1').of(objectA).to('prop1.size.redundant').of(objectB)
binding.set 'Medium'
expect(objectB.prop1).to.equal 'The size of this shirt is Medium, man!'
restartSandbox()
test "Custom placeholder markers can be set", ()->
SimplyBind.settings.placeholder = ['^^', '$$']
objectB.prop1 = 'The size of this shirt is ^^size$$, man!'
binding = SimplyBind('prop1').of(objectA).to('prop1.size').of(objectB)
binding.set 'XXS'
expect(objectB.prop1).to.equal 'The size of this shirt is XXS, man!'
SimplyBind.settings.placeholder = ['{{', '}}']
restartSandbox()
test "Target strings can have multiple placeholders", ()->
dispatcher = 'value': 'Large'
objectA.prop = 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
if isBrowser
inputA.value = 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
regA.textContent = 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
regA.setAttribute 'someattr', 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
SimplyBind('value').of(dispatcher)
.to('prop.size').of(objectA)
if isBrowser
SimplyBind('value').of(dispatcher)
.to('value.size').of(inputA)
.and.to('textContent.size').of(regA)
.and.to('attr:someattr.size').of(regA)
expect(objectA.prop).to.equal 'The size of this shirt is Large, and Large is Largely rad!'
if isBrowser
expect(inputA.value).to.equal 'The size of this shirt is Large, and Large is Largely rad!'
expect(regA.textContent).to.equal 'The size of this shirt is Large, and Large is Largely rad!'
expect(regA.getAttribute 'someattr').to.equal 'The size of this shirt is Large, and Large is Largely rad!'
restartSandbox()
test "Multiple values can be set for the same target that has multiple placeholders", ()->
dispatcher =
'verb': 'not'
'nounOne': 'small'
'nounTwo': 'pretty'
objectB.prop = 'The following text is {{verb}} {{nounOne}} and {{nounTwo}}'
if isBrowser
inputB.value = 'The following text is {{verb}} {{nounOne}} and {{nounTwo}}'
regB.setAttribute 'someattr', 'The following text is {{verb}} {{nounOne}} and {{nounTwo}}'
SimplyBind('verb').of(dispatcher)
.to('prop.verb').of(objectB)
if isBrowser
SimplyBind('verb').of(dispatcher)
.to('value.verb').of(inputB)
.and.to('textContent.verb').of(regB)
.and.to('attr:someattr.verb').of(regB)
SimplyBind('nounOne').of(dispatcher)
.to('prop.nounOne').of(objectB)
if isBrowser
SimplyBind('nounOne').of(dispatcher)
.to('value.nounOne').of(inputB)
.and.to('textContent.nounOne').of(regB)
.and.to('attr:someattr.nounOne').of(regB)
SimplyBind('nounTwo').of(dispatcher)
.to('prop.nounTwo').of(objectB)
if isBrowser
SimplyBind('nounTwo').of(dispatcher)
.to('value.nounTwo').of(inputB)
.and.to('textContent.nounTwo').of(regB)
.and.to('attr:someattr.nounTwo').of(regB)
values = ()-> if not isBrowser then [objectB.prop] else [objectB.prop, inputB.value, regB.textContent, regB.getAttribute 'someattr']
for value in values()
expect(value).to.equal 'The following text is not small and pretty'
dispatcher.verb = 'very'
dispatcher.nounOne = 'big'
dispatcher.nounTwo = 'ugly'
for value in values()
expect(value).to.equal 'The following text is very big and ugly'
restartSandbox()
test "Multiple values can be set for the same target that has multiple placeholders + transforms", ()->
dispatcher =
'verbOne': 'not'
'verbTwo': 'not'
'nounOne': 'small'
'nounTwo': 'pretty'
objectC.prop = 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
if isBrowser
inputC.value = 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
regC.textContent = 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
regC.setAttribute 'someattr', 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
SimplyBind('verbOne').of(dispatcher)
.to('prop.verbOne').of(objectC).transform (value)-> value.toUpperCase()
if isBrowser
SimplyBind('verbOne').of(dispatcher)
.to('value.verbOne').of(inputC)
.and.to('textContent.verbOne').of(regC)
.and.to('attr:someattr.verbOne').of(regC)
.transformAll (value)-> value.toUpperCase()
SimplyBind('verbTwo').of(dispatcher)
.to('prop.verbTwo').of(objectC).transform (value)-> value.toLowerCase()
if isBrowser
SimplyBind('verbTwo').of(dispatcher)
.to('value.verbTwo').of(inputC)
.and.to('textContent.verbTwo').of(regC)
.and.to('attr:someattr.verbTwo').of(regC)
.transformAll (value)-> value.toLowerCase()
SimplyBind('nounOne').of(dispatcher)
.to('prop.nounOne').of(objectC).transform (value)-> value.toUpperCase()
if isBrowser
SimplyBind('nounOne').of(dispatcher)
.to('value.nounOne').of(inputC)
.and.to('textContent.nounOne').of(regC)
.and.to('attr:someattr.nounOne').of(regC)
.transformAll (value)-> value.toUpperCase()
SimplyBind('nounTwo').of(dispatcher)
.to('prop.nounTwo').of(objectC).transform (value)-> value.toUpperCase()
if isBrowser
SimplyBind('nounTwo').of(dispatcher)
.to('value.nounTwo').of(inputC)
.and.to('textContent.nounTwo').of(regC)
.and.to('attr:someattr.nounTwo').of(regC)
.transformAll (value)-> value.toUpperCase()
values = ()-> if not isBrowser then [objectC.prop] else [objectC.prop, inputC.value, regC.textContent, regC.getAttribute 'someattr']
for value in values()
expect(value).to.equal 'The following text is NOT (not) SMALL and PRETTY'
dispatcher.verbOne = dispatcher.verbTwo = 'Very'
dispatcher.nounOne = 'Big'
dispatcher.nounTwo = 'Ugly'
for value in values()
expect(value).to.equal 'The following text is VERY (very) BIG and UGLY'
restartSandbox()
test "Multiple placeholder bindings to textContent with multiple placeholders in a single textNode containing nested placeholders should function properly", ()-> if not isBrowser then @skip() else
dispatcher = 'wordA':'', 'wordB':''
expect(regD.textContent).to.equal 'The following {{wordA}} and {{wordB}} replaced correctly'
SimplyBind('wordA').of(dispatcher)
.to('textContent.wordA').of(regD)
SimplyBind('wordB').of(dispatcher)
.to('textContent.wordB').of(regD)
expect(regD.textContent).to.equal 'The following and replaced correctly'
dispatcher.wordA = 'firstWord'
dispatcher.wordB = 'secondWord'
expect(regD.textContent).to.equal 'The following firstWord and secondWord replaced correctly'
restartSandbox()
test "A single publisher can bind to more than 1 placeholder in a subscriber", ()->
dispatcher = 'prop':'value'
receiver = 'prop':'{{first}} {{second}}'
SimplyBind('prop').of(dispatcher)
.to('prop.first').of(receiver)
.and.to('prop.second').of(receiver)
expect(receiver.prop).to.equal 'value value'
dispatcher.prop = 'anotherValue'
expect(receiver.prop).to.equal 'anotherValue anotherValue'
test "A single publisher can bind to more than 1 placeholder in a subscriber + transforms", ()->
dispatcher = 'prop':'VaLuE'
receiver = 'prop':'{{first}} {{second}}'
SimplyBind('prop').of(dispatcher)
.to('prop.first').of(receiver).transform (value)-> value.toLowerCase()
.and.to('prop.second').of(receiver).transform (value)-> value.toUpperCase()
expect(receiver.prop).to.equal 'value VALUE'
dispatcher.prop = 'anotherValue'
expect(receiver.prop).to.equal 'anothervalue ANOTHERVALUE'
test "When an object prop with placeholders is an updater, it can either update individual placeholder values or its entire value", ()->
dispatcher = 'nounA':'color', 'nounB':'car', 'adverb':'very', 'color':'red'
receiver = 'prop':'The {{nounA}} of this {{nounB}} is {{adverb}} {{color}}'
lastReceiver = 'prop':''
SimplyBind('nounA').of(dispatcher).to('prop.nounA').of(receiver)
SimplyBind('nounB').of(dispatcher).to('prop.nounB').of(receiver)
SimplyBind('adverb').of(dispatcher).to('prop.adverb').of(receiver)
SimplyBind('color').of(dispatcher).to('prop.color').of(receiver)
expect(receiver.prop).to.equal 'The color of this car is very red'
SimplyBind('prop.nounA').of(receiver)
.to('prop').of(lastReceiver)
expect(lastReceiver.prop).to.equal 'color'
dispatcher.nounA = 'colour'
expect(lastReceiver.prop).to.equal 'colour'
SimplyBind('prop.nounB').of(receiver)
.to('prop').of(lastReceiver)
expect(lastReceiver.prop).to.equal 'car'
dispatcher.nounB = 'airplane'
expect(lastReceiver.prop).to.equal 'airplane'
SimplyBind('prop').of(receiver)
.to('prop').of(lastReceiver)
expect(lastReceiver.prop).to.equal 'The colour of this airplane is very red'
test "When manually changing the value of the prop after binding, the application of the placeholders will ignore the changes", ()-> if not isBrowser then @skip() else
dispatcher = 'verb':'', 'nounOne':'', 'nounTwo':''
objectA.prop1 = regB.textContent
SimplyBind('verb').of(dispatcher)
.to('prop1.verb').of(objectA)
.and.to('textContent.verb').of(regB)
SimplyBind('nounOne').of(dispatcher)
.to('prop1.nounOne').of(objectA)
.and.to('textContent.nounOne').of(regB)
SimplyBind('nounTwo').of(dispatcher)
.to('prop1.nounTwo').of(objectA)
.and.to('textContent.nounTwo').of(regB)
dispatcher.verb = 'looking'
dispatcher.nounOne = 'nice'
dispatcher.nounTwo = 'long'
expect(objectA.prop1).to.equal 'The following text is looking nice and long'
expect(objectA.prop1).to.equal(regB.textContent)
upperCaseNodes = (nodes)-> for node in nodes
if node.nodeType is 3
node.textContent = node.textContent.toUpperCase()
else
upperCaseNodes(node)
upperCaseNodes(regB.childNodes)
objectA.prop1 = 'modified'
dispatcher.verb = 'not looking'
dispatcher.nounOne = 'ugly'
dispatcher.nounTwo = 'short'
expect(objectA.prop1).to.equal 'The following text is not looking ugly and short'
expect(objectA.prop1).to.equal(regB.textContent)
$regBH1.append '<span> and some other attribute</span>'
dispatcher.verb = 'looking'
dispatcher.nounOne = 'nice'
dispatcher.nounTwo = 'long'
expect(objectA.prop1).to.equal 'The following text is looking nice and long'
expect(regB.textContent).to.equal 'The following text is looking nice and long and some other attribute'
test "DOM Elements that contain textContent placeholders that encapsulate the entire textNode will behave normally", ()-> if not isBrowser then @skip() else
regA.innerHTML = 'This <span>{{placeholder}}</span> is separated from others'
expect(regA.textContent).to.equal 'This {{placeholder}} is separated from others'
SimplyBind('prop').of(objectA)
.to('textContent.placeholder').of(regA)
objectA.prop = 'word'
expect(regA.textContent).to.equal 'This word is separated from others'
restartSandbox()