functional tests + fix $index binding

This commit is contained in:
Evan You 2013-10-28 13:22:16 -04:00
parent 495d799c3d
commit 7a0bdc9971
17 changed files with 278 additions and 84 deletions

View File

@ -120,7 +120,7 @@ module.exports = function( grunt ) {
cwd: path.resolve('test/functional')
}
}, function (err, res) {
if (err) grunt.fail.fatal(res.stdout)
if (err) grunt.fail.fatal(res.stdout || 'CasperJS test failed')
grunt.log.writeln(res.stdout)
done()
})

View File

@ -100,8 +100,9 @@ function Compiler (vm, options) {
}
// for repeated items, create an index binding
// which should be inenumerable but configurable
if (compiler.repeat) {
vm[compiler.repeatPrefix].$index = compiler.repeatIndex
def(vm[compiler.repeatPrefix], '$index', compiler.repeatIndex, false, true)
}
// now parse the DOM, during which we will create necessary bindings

View File

@ -79,6 +79,10 @@ function watchObject (obj, path, observer) {
bind(obj, key, path, observer)
}
}
// $index is inenumerable
if (obj.$index !== undefined) {
bind(obj, '$index', path, observer)
}
}
/**

View File

@ -26,12 +26,12 @@ var utils = module.exports = {
* This avoids it being included in JSON.stringify
* or for...in loops.
*/
defProtected: function (obj, key, val, enumerable) {
defProtected: function (obj, key, val, enumerable, configurable) {
if (obj.hasOwnProperty(key)) return
Object.defineProperty(obj, key, {
enumerable: !!enumerable,
configurable: false,
value: val
value : val,
enumerable : !!enumerable,
configurable : !!configurable
})
},

View File

@ -3,6 +3,7 @@
<head>
<title></title>
<meta charset="utf-8">
<script src="bind.js"></script>
<script src="../../../dist/seed.js"></script>
</head>
<body>

View File

@ -8,13 +8,13 @@
</head>
<body>
<div id="normal">
<p sd-text="one + ' ' + two + '!'"></p>
<input id="one" sd-model="one" name="one"> <input id="two" sd-model="two" name="two">
<p sd-text="one + ' ' + two.three + '!'"></p>
<input id="one" sd-model="one" name="one"> <input id="two" sd-model="two.three" name="two">
</div>
<div id="lazy">
<p sd-text="one + ' ' + two + '!'"></p>
<p sd-text="one + ' ' + two.three + '!'"></p>
<form id="form">
<input sd-model="one" name="three"> <input sd-model="two" name="four">
<input sd-model="one" name="three"> <input sd-model="two.three" name="four">
</form>
</div>
<script>
@ -22,7 +22,9 @@
el: '#normal',
scope: {
one: 'Hello',
two: 'World'
two: {
three: 'World'
}
}
})
@ -31,7 +33,9 @@
lazy: true,
scope: {
one: 'Hi',
two: 'Ho'
two: {
three: 'Ho'
}
}
})
</script>

View File

@ -3,6 +3,7 @@
<head>
<title></title>
<meta charset="utf-8">
<script src="bind.js"></script>
<script src="../../../dist/seed.js"></script>
</head>
<body>
@ -10,18 +11,16 @@
<h1>a.b.c : <span sd-text="a.b.c"></span></h1>
<h2>a.c : <span sd-text="a.c"></span></h2>
<h3>Computed property that concats the two: <span sd-text="d"></span></h3>
<button sd-on="click:one">one</button>
<button sd-on="click:two">two</button>
<button sd-on="click:three">three</button>
<p><input sd-model="msg"></p>
</div>
<div id="b">
<h1 sd-text="a.c"></h1>
<button class="one" sd-on="click:one">one</button>
<button class="two" sd-on="click:two">two</button>
<button class="three" sd-on="click:three">three</button>
<form id="form"><input name="msg" sd-model="msg"></form>
</div>
<script>
Seed.config({debug: true})
var data = { c: 555 }
var Demo = Seed.extend({
lazy: true,
init: function () {
this.msg = 'Yoyoyo'
this.a = data
@ -50,13 +49,7 @@
}}
}
})
var app = new Demo({ el: '#a' }),
app2 = new Seed({
el: '#b',
scope: {
a: data
}
})
var app = new Demo({ el: '#a' })
</script>
</body>
</html>

View File

@ -17,6 +17,7 @@
color: #F00;
}
</style>
<script src="bind.js"></script>
<script src="../../../dist/seed.js"></script>
</head>
<body>
@ -24,37 +25,29 @@
<p class="ancestor">{{name}} {{family}}</p>
<div sd-viewmodel="man" data-name="Jack">
<p>{{name}}, son of {{^name}}</p>
<p class="jack">{{name}}, son of {{^name}}</p>
<div sd-viewmodel="man" data-name="Mike">
<p>{{name}}, son of {{^name}}</p>
<p class="mike">{{name}}, son of {{^name}}</p>
<div sd-viewmodel="offspring" data-name="Tim">
<div sd-viewmodel="offspring" data-name="Tim" class="tim">
</div>
<div sd-viewmodel="offspring" data-name="Tom">
<div sd-viewmodel="offspring" data-name="Tom" class="tom">
</div>
</div>
<div sd-viewmodel="man" data-name="Jason">
<p>{{name}}, son of {{^name}}</p>
<p class="jason">{{name}}, son of {{^name}}</p>
<div sd-viewmodel="offspring" data-name="Andrew">
<div sd-viewmodel="offspring" data-name="Andrew" class="andrew">
</div>
</div>
</div>
</div>
<script type="text/sd-template" id="sd-template-offspring">
<p>
{{name}}, son of {{^name}},
<br>
grandson of {{^^name}},
<br>
great-grandson of {{$name}},
<br>
and offspring of family {{family}}.
</p>
<p>{{name}}, son of {{^name}}, grandson of {{^^name}}, great-grandson of {{$name}}, and offspring of family {{family}}.</p>
</script>
<script>
@ -71,7 +64,7 @@
})
var Offspring = Man.extend({
template: document.getElementById('sd-template-offspring').innerHTML
template: document.getElementById('sd-template-offspring').innerHTML.trim()
})
Seed

View File

@ -3,24 +3,25 @@
<head>
<title>SEED repeated items</title>
<meta charset="utf-8">
<script src="bind.js"></script>
<script src="../../../dist/seed.js"></script>
</head>
<body>
<div id="app">
<p>
<button sd-on="click:push">push</button>
<button sd-on="click:pop">pop</button>
<button sd-on="click:shift">shift</button>
<button sd-on="click:unshift">unshift</button>
<button sd-on="click:splice">splice</button>
<button sd-on="click:remove">remove</button>
<button sd-on="click:replace">replace</button>
<button sd-on="click:sort">sort</button>
<button sd-on="click:reverse">reverse</button>
<button class="push" sd-on="click:push">push</button>
<button class="pop" sd-on="click:pop">pop</button>
<button class="shift" sd-on="click:shift">shift</button>
<button class="unshift" sd-on="click:unshift">unshift</button>
<button class="splice" sd-on="click:splice">splice</button>
<button class="remove" sd-on="click:remove">remove</button>
<button class="replace" sd-on="click:replace">replace</button>
<button class="sort" sd-on="click:sort">sort</button>
<button class="reverse" sd-on="click:reverse">reverse</button>
</p>
<p>Total items: {{items.length}}</p>
<p>Total items: <span class="count" sd-text="items.length"></span></p>
<ul>
<li sd-repeat="item:items">
<li class="item" sd-repeat="item:items">
{{item.$index}} {{item.title}}
</li>
</ul>
@ -40,7 +41,7 @@
scope: {
items: items,
push: function () {
this.items.push({ title: randomChar() })
this.items.push({ title: getChar() })
},
pop: function () {
this.items.pop()
@ -49,16 +50,16 @@
this.items.shift()
},
unshift: function () {
this.items.unshift({ title: randomChar() })
this.items.unshift({ title: getChar() })
},
splice: function () {
this.items.splice(0, 1, { title: randomChar() }, { title: randomChar() })
this.items.splice(1, 1, { title: getChar() }, { title: getChar() })
},
replace: function () {
this.items.replace(randomPos(), { title: randomChar() })
this.items.replace(getPos(), { title: getChar() })
},
remove: function () {
this.items.remove(randomPos())
this.items.remove(getPos())
},
sort: function () {
this.items.sort(function (a, b) {
@ -71,12 +72,15 @@
}
})
function randomChar () {
return String.fromCharCode(Math.floor(Math.random() * 30 + 50))
}
var getChar = (function () {
var count = 0
return function () {
return (count++).toString()
}
})()
function randomPos () {
return Math.floor(Math.random() * items.length)
function getPos () {
return items.length - 1
}
</script>
</body>

View File

@ -3,22 +3,23 @@
<head>
<title></title>
<meta charset="utf-8">
<script src="bind.js"></script>
<script src="../../../dist/seed.js"></script>
</head>
<body>
<div sd-repeat="item:items" sd-viewmodel="item" sd-on="click:toggle">
{{item.title + msg}}
<div class="item" sd-repeat="item:items" sd-viewmodel="item" sd-on="click:click">
{{msg + ' ' + item.title}}
</div>
<script>
Seed.config({ debug: true })
Seed.viewmodel('item', Seed.extend({
init: function () {
this.item.title += 'ho'
this.item.title += ' init'
},
proto: {
toggle: function () {
this.item.title += 'hi'
click: function () {
this.item.title += ' click'
}
},
scope: {
@ -32,9 +33,7 @@
items: [
{title:'a'},
{title:'b'},
{title:'c'},
{title:'d'},
{title:'e'}
{title:'c'}
]
}
})

View File

@ -3,14 +3,15 @@
<head>
<title>SEED share data</title>
<meta charset="utf-8">
<script src="bind.js"></script>
<script src="../../../dist/seed.js"></script>
</head>
<body>
<div id="a">{{shared.msg}}</div>
<div id="b">{{shared.msg}}</div>
<div id="c">
<input sd-model="shared.msg" type="text">
</div>
<form id="c">
<input name="input" sd-model="shared.msg" type="text">
</form>
<div id="d">
<pre>{{source}}</pre>
</div>
@ -31,6 +32,7 @@
}
})
new Seed({
lazy: true,
el: '#c',
scope: {
shared: shared

View File

@ -1,4 +1,6 @@
casper.test.begin('Expression', 9, function (test) {
/* global normal */
casper.test.begin('Expression', 12, function (test) {
casper
.start('./fixtures/expression.html', function () {
@ -12,10 +14,17 @@ casper.test.begin('Expression', 9, function (test) {
// setting value
this.evaluate(function () {
/* global normal */
normal.one = 'Hola'
})
test.assertSelectorHasText('#normal p', 'Hola World!')
test.assertField('one', 'Hola')
// setting nested value
this.evaluate(function () {
normal.two.three = 'Casper'
})
test.assertSelectorHasText('#normal p', 'Hola Casper!')
test.assertField('two', 'Casper')
// lazy input
this.fill('#form', {
@ -25,14 +34,8 @@ casper.test.begin('Expression', 9, function (test) {
test.assertSelectorHasText('#lazy p', 'three four!')
// normal input
this.evaluate(function () {
var one = document.getElementById('one')
var e = document.createEvent('MouseEvent')
e.initMouseEvent('keyup', true, true, null, 1, 0, 0, 0, 0, false, false, false, false, 0, null)
one.value = 'Bye'
one.dispatchEvent(e)
})
test.assertSelectorHasText('#normal p', 'Bye World!')
this.sendKeys('#one', 'Bye')
test.assertSelectorHasText('#normal p', 'Bye Casper!')
})
.run(function () {

View File

@ -0,0 +1,35 @@
casper.test.begin('Nested Properties', 13, function (test) {
casper
.start('./fixtures/nested-props.html', function () {
test.assertSelectorHasText('h1 span', '')
test.assertSelectorHasText('h2 span', '555')
test.assertSelectorHasText('h3 span', 'Yoyoyo555')
this.click('.one')
test.assertSelectorHasText('h1 span', 'one')
test.assertSelectorHasText('h2 span', '1')
test.assertSelectorHasText('h3 span', 'Yoyoyoone1')
this.click('.two')
test.assertSelectorHasText('h1 span', 'two')
test.assertSelectorHasText('h2 span', '2')
test.assertSelectorHasText('h3 span', 'Yoyoyotwo2')
this.click('.three')
test.assertSelectorHasText('h1 span', 'three')
test.assertSelectorHasText('h2 span', '3')
test.assertSelectorHasText('h3 span', 'Yoyoyothree3')
this.fill('#form', {
msg: 'Oh yeah '
})
test.assertSelectorHasText('h3 span', 'Oh yeah three3')
})
.run(function () {
test.done()
})
})

View File

@ -0,0 +1,20 @@
casper.test.begin('Nested Viewmodels', 7, function (test) {
casper
.start('./fixtures/nested-viewmodels.html', function () {
test.assertSelectorHasText('.ancestor', 'Andy Johnson')
test.assertSelectorHasText('.jack', 'Jack, son of Andy')
test.assertSelectorHasText('.mike', 'Mike, son of Jack')
test.assertSelectorHasText('.jason', 'Jason, son of Jack')
test.assertSelectorHasText('.tim', 'Tim, son of Mike, grandson of Jack, great-grandson of Andy, and offspring of family Johnson.')
test.assertSelectorHasText('.tom', 'Tom, son of Mike, grandson of Jack, great-grandson of Andy, and offspring of family Johnson.')
test.assertSelectorHasText('.andrew', 'Andrew, son of Jason, grandson of Jack, great-grandson of Andy, and offspring of family Johnson.')
})
.run(function () {
test.done()
})
})

View File

@ -0,0 +1,84 @@
casper.test.begin('Repeated Items', 41, function (test) {
casper
.start('./fixtures/repeated-items.html', function () {
// initial values
test.assertSelectorHasText('.count', '3')
test.assertSelectorHasText('.item:nth-child(1)', '0 A')
test.assertSelectorHasText('.item:nth-child(2)', '1 B')
test.assertSelectorHasText('.item:nth-child(3)', '2 C')
this.click('.push')
test.assertSelectorHasText('.count', '4')
test.assertSelectorHasText('.item:nth-child(4)', '3 0')
this.click('.shift')
test.assertSelectorHasText('.count', '3')
test.assertSelectorHasText('.item:nth-child(1)', '0 B')
test.assertSelectorHasText('.item:nth-child(2)', '1 C')
test.assertSelectorHasText('.item:nth-child(3)', '2 0')
this.click('.pop')
test.assertSelectorHasText('.count', '2')
test.assertSelectorHasText('.item:nth-child(1)', '0 B')
test.assertSelectorHasText('.item:nth-child(2)', '1 C')
this.click('.unshift')
test.assertSelectorHasText('.count', '3')
test.assertSelectorHasText('.item:nth-child(1)', '0 1')
test.assertSelectorHasText('.item:nth-child(2)', '1 B')
test.assertSelectorHasText('.item:nth-child(3)', '2 C')
this.click('.splice')
test.assertSelectorHasText('.count', '4')
test.assertSelectorHasText('.item:nth-child(1)', '0 1')
test.assertSelectorHasText('.item:nth-child(2)', '1 2')
test.assertSelectorHasText('.item:nth-child(3)', '2 3')
test.assertSelectorHasText('.item:nth-child(4)', '3 C')
this.click('.remove')
test.assertSelectorHasText('.count', '3')
test.assertSelectorHasText('.item:nth-child(1)', '0 1')
test.assertSelectorHasText('.item:nth-child(2)', '1 2')
test.assertSelectorHasText('.item:nth-child(3)', '2 3')
this.click('.replace')
test.assertSelectorHasText('.count', '3')
test.assertSelectorHasText('.item:nth-child(1)', '0 1')
test.assertSelectorHasText('.item:nth-child(2)', '1 2')
test.assertSelectorHasText('.item:nth-child(3)', '2 4')
this.click('.reverse')
test.assertSelectorHasText('.count', '3')
test.assertSelectorHasText('.item:nth-child(1)', '0 4')
test.assertSelectorHasText('.item:nth-child(2)', '1 2')
test.assertSelectorHasText('.item:nth-child(3)', '2 1')
this.click('.sort')
test.assertSelectorHasText('.count', '3')
test.assertSelectorHasText('.item:nth-child(1)', '0 1')
test.assertSelectorHasText('.item:nth-child(2)', '1 2')
test.assertSelectorHasText('.item:nth-child(3)', '2 4')
// make sure things work on empty array
this.click('.pop')
this.click('.pop')
this.click('.pop')
this.click('.pop')
this.click('.shift')
this.click('.remove')
this.click('.replace')
this.click('.sort')
this.click('.reverse')
this.click('.splice')
test.assertSelectorHasText('.count', '2')
test.assertSelectorHasText('.item:nth-child(1)', '0 6')
test.assertSelectorHasText('.item:nth-child(2)', '1 7')
})
.run(function () {
test.done()
})
})

View File

@ -0,0 +1,27 @@
casper.test.begin('Repeated ViewModels', 7, function (test) {
casper
.start('./fixtures/repeated-vms.html', function () {
test.assertSelectorHasText('.item:nth-child(1)', 'msg a init')
test.assertSelectorHasText('.item:nth-child(2)', 'msg b init')
test.assertSelectorHasText('.item:nth-child(3)', 'msg c init')
// click everything to test event handlers (delegated)
this.click('.item:nth-child(1)')
test.assertSelectorHasText('.item:nth-child(1)', 'msg a init click')
this.click('.item:nth-child(2)')
test.assertSelectorHasText('.item:nth-child(2)', 'msg b init click')
this.click('.item:nth-child(3)')
test.assertSelectorHasText('.item:nth-child(3)', 'msg c init click')
// more clicks
this.click('.item:nth-child(1)')
test.assertSelectorHasText('.item:nth-child(1)', 'msg a init click click')
})
.run(function () {
test.done()
})
})

View File

@ -0,0 +1,24 @@
casper.test.begin('Sharing Data between VMs', 7, function (test) {
casper
.start('./fixtures/share-data.html', function () {
test.assertSelectorHasText('#a', 'hello')
test.assertSelectorHasText('#b', 'hello')
test.assertField('input', 'hello')
test.assertSelectorHasText('#d pre', '{"msg":"hello"}')
this.fill('#c', {
input: 'durrr'
})
test.assertSelectorHasText('#a', 'durrr')
test.assertSelectorHasText('#b', 'durrr')
test.assertSelectorHasText('#d pre', '{"msg":"durrr"}')
})
.run(function () {
test.done()
})
})