Bläddra i källkod

functional tests + fix $index binding

Evan You 12 år sedan
förälder
incheckning
7a0bdc9971

+ 1 - 1
Gruntfile.js

@@ -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()
         })

+ 2 - 1
src/compiler.js

@@ -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

+ 4 - 0
src/observer.js

@@ -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)
+    }
 }
 
 /**

+ 4 - 4
src/utils.js

@@ -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
         })
     },
 

+ 1 - 0
test/functional/fixtures/encapsulation.html

@@ -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 - 6
test/functional/fixtures/expression.html

@@ -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>

+ 7 - 14
test/functional/fixtures/nested-props.html

@@ -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>

+ 9 - 16
test/functional/fixtures/nested-viewmodels.html

@@ -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

+ 25 - 21
test/functional/fixtures/repeated-items.html

@@ -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>

+ 7 - 8
test/functional/fixtures/repeated-vms.html

@@ -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'}
                     ]
                 }
             })

+ 5 - 3
test/functional/fixtures/share-data.html

@@ -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

+ 13 - 10
test/functional/specs/expression.js

@@ -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 () {

+ 35 - 0
test/functional/specs/nested-props.js

@@ -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()
+    })
+
+})

+ 20 - 0
test/functional/specs/nested-vms.js

@@ -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()
+    })
+
+})

+ 84 - 0
test/functional/specs/repeated-items.js

@@ -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()
+    })
+
+})

+ 27 - 0
test/functional/specs/repeated-vms.js

@@ -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()
+    })
+
+})

+ 24 - 0
test/functional/specs/share-data.js

@@ -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()
+    })
+
+})