describe('Functional', function() {
var data, bindData, el, input, originalPrefix
beforeEach(function() {
originalPrefix = rivets.prefix;
rivets.prefix = 'data';
adapter = {
observe: function(obj, keypath, callback) {
obj.on(keypath, callback)
},
unobserve: function(obj, keypath, callback) {
obj.off(keypath, callback)
},
get: function(obj, keypath) {
return obj.get(keypath)
},
set: function(obj, keypath, value) {
attributes = {}
attributes[keypath] = value
obj.set(attributes)
}
}
sightglass.adapters[':'] = adapter
rivets.configure({preloadData: true})
data = new Data({
foo: 'bar',
items: [{name: 'a'}, {name: 'b'}]
})
bindData = {data: data}
el = document.createElement('div')
input = document.createElement('input')
input.setAttribute('type', 'text')
})
afterEach(function() {
rivets.prefix = originalPrefix
})
describe('Adapter', function() {
it('should read the initial value', function() {
sinon.spy(data, 'get')
el.setAttribute('data-text', 'data:foo')
rivets.bind(el, bindData)
data.get.calledWith('foo').should.be.true
})
it('should read the initial value unless preloadData is false', function() {
rivets.configure({preloadData: false})
sinon.spy(data, 'get')
el.setAttribute('data-value', 'data:foo')
rivets.bind(el, bindData)
data.get.called.should.be.false
})
it('should subscribe to updates', function() {
sinon.spy(data, 'on')
el.setAttribute('data-value', 'data:foo')
rivets.bind(el, bindData)
data.on.called.should.be.true
})
})
describe('Binds', function() {
describe('Text', function() {
it('should set the text content of the element', function() {
el.setAttribute('data-text', 'data:foo')
rivets.bind(el, bindData)
el.textContent.should.equal(data.get('foo'))
})
it('should correctly handle HTML in the content', function() {
el.setAttribute('data-text', 'data:foo')
value = 'Fail'
data.set({foo: value})
rivets.bind(el, bindData)
el.textContent.should.equal(value)
})
})
describe('HTML', function() {
it('should set the html content of the element', function() {
el.setAttribute('data-html', 'data:foo')
rivets.bind(el, bindData)
el.textContent.should.equal(data.get('foo'))
})
it('should correctly handle HTML in the content', function() {
el.setAttribute('data-html', 'data:foo')
value = 'Fail'
data.set({foo: value})
rivets.bind(el, bindData)
el.innerHTML.should.equal(value)
})
})
describe('Value', function() {
it('should set the value of the element', function() {
input.setAttribute('data-value', 'data:foo')
rivets.bind(input, bindData)
input.value.should.equal(data.get('foo'))
})
})
describe('Multiple', function() {
it('should bind a list of multiple elements', function() {
el.setAttribute('data-html', 'data:foo')
input.setAttribute('data-value', 'data:foo')
rivets.bind([el, input], bindData)
el.textContent.should.equal(data.get('foo'))
input.value.should.equal(data.get('foo'))
})
})
describe('Priority', function() {
beforeEach(function() {
rivets.binders.a = {bind: function(){}}
rivets.binders.b = {bind: function(){}}
sinon.spy(rivets.binders.a, 'bind')
sinon.spy(rivets.binders.b, 'bind')
el.setAttribute('data-a', 'data:foo')
el.setAttribute('data-b', 'data:foo')
})
describe('a:10, b:30', function() {
beforeEach(function() {
rivets.binders.a.priority = 10
rivets.binders.b.priority = 30
rivets.bind(el, bindData)
})
it('should bind b before a', function() {
rivets.binders.b.bind.calledBefore(rivets.binders.a.bind).should.be.true
})
})
describe('a:5, b:2', function() {
beforeEach(function() {
rivets.binders.a.priority = 5
rivets.binders.b.priority = 2
rivets.bind(el, bindData)
})
it('should bind a before b', function() {
rivets.binders.a.bind.calledBefore(rivets.binders.b.bind).should.be.true
})
})
describe('a:undefined, b:1', function() {
beforeEach(function() {
rivets.binders.b.priority = 1
rivets.bind(el, bindData)
})
it('should bind b before a', function() {
rivets.binders.b.bind.calledBefore(rivets.binders.a.bind).should.be.true
})
})
})
describe('Iteration', function() {
beforeEach(function(){
list = document.createElement('ul')
el.appendChild(list)
listItem = document.createElement('li')
listItem.setAttribute('data-each-item', 'data:items')
list.appendChild(listItem)
})
it('should loop over a collection and create new instances of that element + children', function() {
el.getElementsByTagName('li').length.should.equal(1)
rivets.bind(el, bindData)
el.getElementsByTagName('li').length.should.equal(2)
})
it('should not fail if the collection being bound to is null', function() {
data.set({ items: null})
rivets.bind(el, bindData)
el.getElementsByTagName('li').length.should.equal(0)
})
it('should re-loop over the collection and create new instances when the array changes', function() {
rivets.bind(el, bindData)
el.getElementsByTagName('li').length.should.equal(2)
newItems = [{name: 'a'}, {name: 'b'}, {name: 'c'}]
data.set({items: newItems})
el.getElementsByTagName('li').length.should.equal(3)
})
it('should allow binding to the iterated item as well as any parent contexts', function() {
span1 = document.createElement('span')
span1.setAttribute('data-text', 'item.name')
span2 = document.createElement('span')
span2.setAttribute('data-text', 'data:foo')
listItem.appendChild(span1)
listItem.appendChild(span2)
rivets.bind(el, bindData)
el.getElementsByTagName('span')[0].textContent.should.equal('a')
el.getElementsByTagName('span')[1].textContent.should.equal('bar')
})
it('should allow binding to the iterated element directly', function() {
listItem.setAttribute('data-text', 'item.name')
listItem.setAttribute('data-class', 'data:foo')
rivets.bind(el, bindData)
el.getElementsByTagName('li')[0].textContent.should.equal('a')
el.getElementsByTagName('li')[0].className.should.equal('bar')
})
it('should insert items between any surrounding elements', function(){
firstItem = document.createElement('li')
lastItem = document.createElement('li')
firstItem.textContent = 'first'
lastItem.textContent = 'last'
list.appendChild(lastItem)
list.insertBefore(firstItem, listItem)
listItem.setAttribute('data-text', 'item.name')
rivets.bind(el, bindData)
el.getElementsByTagName('li')[0].textContent.should.equal('first')
el.getElementsByTagName('li')[1].textContent.should.equal('a')
el.getElementsByTagName('li')[2].textContent.should.equal('b')
el.getElementsByTagName('li')[3].textContent.should.equal('last')
})
it('should allow binding to the iterated element index', function() {
listItem.setAttribute('data-index', 'index')
rivets.bind(el, bindData)
el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0')
el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1')
})
it('should allow binding to the iterated element index by using the %item% syntax', function() {
listItem.setAttribute('data-index', '%item%')
rivets.bind(el, bindData)
el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0')
el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1')
})
it('should allow the developer to configure the index attribute available in the iteration', function() {
rivets.configure({
iterationAlias : function(modelName) {
return modelName + 'Index';
}
});
listItem.setAttribute('data-index', 'itemIndex')
rivets.bind(el, bindData)
el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0')
el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1')
})
})
})
describe('Updates', function() {
it('should change the value', function() {
el.setAttribute('data-text', 'data:foo')
rivets.bind(el, bindData)
data.set({foo: 'some new value'})
el.textContent.should.equal(data.get('foo'))
})
})
describe('Input', function() {
it('should update the model value', function() {
input.setAttribute('data-value', 'data:foo')
rivets.bind(input, bindData)
input.value = 'some new value'
var event = document.createEvent('HTMLEvents')
event.initEvent('input', true, true)
input.dispatchEvent(event)
input.value.should.equal(data.get('foo'))
})
})
})