diff options
| author | Adam Abrons | 2010-01-05 16:36:58 -0800 |
|---|---|---|
| committer | Adam Abrons | 2010-01-05 16:36:58 -0800 |
| commit | c9c176a53b1632ca2b1c6ed27382ab72ac21d45d (patch) | |
| tree | b5f719a095c03ee9f8b2721ffdaf1e5ff8c11b41 /test/BinderTest.js | |
| download | angular.js-c9c176a53b1632ca2b1c6ed27382ab72ac21d45d.tar.bz2 | |
angular.js
Diffstat (limited to 'test/BinderTest.js')
| -rw-r--r-- | test/BinderTest.js | 1001 |
1 files changed, 1001 insertions, 0 deletions
diff --git a/test/BinderTest.js b/test/BinderTest.js new file mode 100644 index 00000000..d033996d --- /dev/null +++ b/test/BinderTest.js @@ -0,0 +1,1001 @@ +BinderTest = TestCase('BinderTest'); + +function compile(content, initialScope, config) { + var h = html(content); + config = config || {autoSubmit:true}; + var scope = new nglr.Scope(initialScope, "ROOT"); + h.data('scope', scope); + var binder = new nglr.Binder(h[0], new nglr.WidgetFactory(), new MockUrlWatcher(), config); + var datastore = new nglr.DataStore(); + scope.set("$datastore", datastore); + scope.set("$binder", binder); + scope.set("$anchor", binder.anchor); + binder.entity(scope); + binder.compile(); + return {node:h, binder:binder, scope:scope}; +} + +function compileToHtml(content) { + return compile(content).node.sortedHtml(); +} + + +BinderTest.prototype.testParseTextWithNoBindings = function(){ + var parts = nglr.Binder.parseBindings("a"); + assertEquals(parts.length, 1); + assertEquals(parts[0], "a"); + assertTrue(!nglr.Binder.binding(parts[0])); +}; + +BinderTest.prototype.testParseEmptyText = function(){ + var parts = nglr.Binder.parseBindings(""); + assertEquals(parts.length, 1); + assertEquals(parts[0], ""); + assertTrue(!nglr.Binder.binding(parts[0])); +}; + +BinderTest.prototype.testParseInnerBinding = function(){ + var parts = nglr.Binder.parseBindings("a{{b}}c"); + assertEquals(parts.length, 3); + assertEquals(parts[0], "a"); + assertTrue(!nglr.Binder.binding(parts[0])); + assertEquals(parts[1], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[1]), "b"); + assertEquals(parts[2], "c"); + assertTrue(!nglr.Binder.binding(parts[2])); +}; + +BinderTest.prototype.testParseEndingBinding = function(){ + var parts = nglr.Binder.parseBindings("a{{b}}"); + assertEquals(parts.length, 2); + assertEquals(parts[0], "a"); + assertTrue(!nglr.Binder.binding(parts[0])); + assertEquals(parts[1], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[1]), "b"); +}; + +BinderTest.prototype.testParseBeggingBinding = function(){ + var parts = nglr.Binder.parseBindings("{{b}}c"); + assertEquals(parts.length, 2); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); + assertEquals(parts[1], "c"); + assertTrue(!nglr.Binder.binding(parts[1])); +}; + +BinderTest.prototype.testParseLoanBinding = function(){ + var parts = nglr.Binder.parseBindings("{{b}}"); + assertEquals(parts.length, 1); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); +}; + +BinderTest.prototype.testParseTwoBindings = function(){ + var parts = nglr.Binder.parseBindings("{{b}}{{c}}"); + assertEquals(parts.length, 2); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); + assertEquals(parts[1], "{{c}}"); + assertEquals(nglr.Binder.binding(parts[1]), "c"); +}; + +BinderTest.prototype.testParseTwoBindingsWithTextInMiddle = function(){ + var parts = nglr.Binder.parseBindings("{{b}}x{{c}}"); + assertEquals(parts.length, 3); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); + assertEquals(parts[1], "x"); + assertTrue(!nglr.Binder.binding(parts[1])); + assertEquals(parts[2], "{{c}}"); + assertEquals(nglr.Binder.binding(parts[2]), "c"); +}; + +BinderTest.prototype.testParseMultiline = function(){ + var parts = nglr.Binder.parseBindings('"X\nY{{A\nB}}C\nD"'); + assertTrue(!!nglr.Binder.binding('{{A\nB}}')); + assertEquals(parts.length, 3); + assertEquals(parts[0], '"X\nY'); + assertEquals(parts[1], '{{A\nB}}'); + assertEquals(parts[2], 'C\nD"'); +}; + +BinderTest.prototype.testHasBinding = function(){ + assertTrue(nglr.Binder.hasBinding("{{a}}")); + assertTrue(!nglr.Binder.hasBinding("a")); + assertTrue(nglr.Binder.hasBinding("{{b}}x{{c}}")); +}; + + +BinderTest.prototype.tearDown = function(){ + jQuery("*", document).die(); + jQuery(document).unbind(); +}; + +BinderTest.prototype.testChangingTextfieldUpdatesModel = function(){ + var state = compile('<input type="text" name="model.price" value="abc">', {model:{}}); + state.binder.updateView(); + assertEquals('abc', state.scope.get('model').price); +}; + +BinderTest.prototype.testChangingTextareaUpdatesModel = function(){ + var form = html('<textarea name="model.note">abc</textarea>'); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals(scope.get('model').note, 'abc'); +}; + +BinderTest.prototype.testChangingRadioUpdatesModel = function(){ + var form = html('<input type="radio" name="model.price" value="A" checked>' + + '<input type="radio" name="model.price" value="B">'); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals(scope.get('model').price, 'A'); +}; + +BinderTest.prototype.testChangingCheckboxUpdatesModel = function(){ + var form = html('<input type="checkbox" name="model.price" value="A" checked>'); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals('A', scope.get('model').price); +}; + +BinderTest.prototype.testBindUpdate = function() { + var c = compile('<div ng-eval="a=123"/>'); + c.binder.updateView(); + assertEquals(123, c.scope.get('a')); +}; + +BinderTest.prototype.testChangingSelectNonSelectedUpdatesModel = function(){ + var form = html('<select name="model.price"><option value="A">A</option><option value="B">B</option></select>'); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals('A', scope.get('model').price); +}; + +BinderTest.prototype.testChangingMultiselectUpdatesModel = function(){ + var form = html('<select name="Invoice.options" multiple="multiple">' + + '<option value="A" selected>Gift wrap</option>' + + '<option value="B" selected>Extra padding</option>' + + '<option value="C">Expedite</option>' + + '</select>'); + var scope = new nglr.Scope({Invoice:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertJsonEquals(["A", "B"], scope.get('Invoice').options); +}; + +BinderTest.prototype.testChangingSelectSelectedUpdatesModel = function(){ + var form = html('<select name="model.price"><option>A</option><option selected value="b">B</option></select>'); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals(scope.get('model').price, 'b'); +}; + +BinderTest.prototype.testExecuteInitialization = function() { + var form = html('<div ng-init="a=123">'); + var scope = new nglr.Scope(); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0)); + binder.executeInit(); + assertEquals(scope.get('a'), 123); +}; + +BinderTest.prototype.testExecuteInitializationStatements = function() { + var form = html('<div ng-init="a=123;b=345">'); + var scope = new nglr.Scope(); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0)); + binder.executeInit(); + assertEquals(scope.get('a'), 123); + assertEquals(scope.get('b'), 345); +}; + +BinderTest.prototype.testApplyTextBindings = function(){ + var form = html('<div ng-bind="model.a">x</div>'); + var scope = new nglr.Scope({model:{a:123}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals('123', form.text()); +}; + +BinderTest.prototype.testReplaceBindingInTextWithSpan = function() { + assertEquals(compileToHtml("<b>a{{b}}c</b>"), '<b>a<span ng-bind="b"></span>c</b>'); + assertEquals(compileToHtml("<b>{{b}}</b>"), '<b><span ng-bind="b"></span></b>'); +}; + +BinderTest.prototype.testReplaceBindingCreatesCorrectNumberOfWidgets = function() { + var h = html("space{{a}}<b>{{a}}a{{a}}</b>{{a}}"); + h.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(h.get(0), new nglr.WidgetFactory()); + binder.compile(); + + assertEquals(4, h.scope().widgets.length); +}; + +BinderTest.prototype.testBindingSpaceConfusesIE = function() { + if (!nglr.msie) return; + var span = document.createElement("span"); + span.innerHTML = ' '; + var nbsp = span.firstChild.nodeValue; + assertEquals( + '<b><span ng-bind="a"></span><span>'+nbsp+'</span><span ng-bind="b"></span></b>', + compileToHtml("<b>{{a}} {{b}}</b>")); + assertEquals( + '<span ng-bind="A"></span><span>'+nbsp+'x </span><span ng-bind="B"></span><span>'+nbsp+'(</span><span ng-bind="C"></span>', + compileToHtml("{{A}} x {{B}} ({{C}})")); +}; + +BinderTest.prototype.testBindingOfAttributes = function() { + var form = html("<a href='http://s/a{{b}}c' foo='x'></a>"); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var attrbinding = form.find("a").attr("ng-bind-attr"); + var bindings = nglr.fromJson(attrbinding); + assertEquals("http://s/a{{b}}c", decodeURI(bindings.href)); + assertTrue(!bindings.foo); +}; + +BinderTest.prototype.testMarkMultipleAttributes = function() { + var form = html("<a href='http://s/a{{b}}c' foo='{{d}}'></a>"); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var attrbinding = form.find("a").attr("ng-bind-attr"); + var bindings = nglr.fromJson(attrbinding); + assertEquals(decodeURI(bindings.href), "http://s/a{{b}}c"); + assertEquals(bindings.foo, "{{d}}"); +}; + +BinderTest.prototype.testAttributesNoneBound = function() { + var form = html("<a href='abc' foo='def'></a>"); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var a = form.find("a"); + assertEquals(a.get(0).nodeName, "A"); + assertTrue(!a.attr("ng-bind-attr")); +}; + +BinderTest.prototype.testExistingAttrbindingIsAppended = function() { + var form = html("<a href='http://s/{{abc}}' ng-bind-attr='{\"b\":\"{{def}}\"}'></a>"); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var a = form.find("a"); + assertEquals('{"b":"{{def}}","href":"http://s/{{abc}}"}', a.attr('ng-bind-attr')); +}; + +BinderTest.prototype.testAttributesAreEvaluated = function(){ + var form = html('<a ng-bind-attr=\'{"a":"a", "b":"a+b={{a+b}}"}\'></a>'); + form.data('scope', new nglr.Scope({a:1, b:2})); + var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + var a = form.find("a"); + assertEquals(a.attr('a'), 'a'); + assertEquals(a.attr('b'), 'a+b=3'); +}; + +BinderTest.prototype.testInputsAreUpdated = function(){ + var form = + html('<input type="tEXt" name="A.text"/>' + + '<textarea name="A.textarea"/>' + + '<input name="A.radio" type="rADio" value="r"/>' + + '<input name="A.radioOff" type="rADio" value="r"/>' + + '<input name="A.checkbox" type="checkbox" value="c" />' + + '<input name="A.checkboxOff" type="checkbox" value="c" />' + + '<select name="A.select"><option>a</option><option value="S">b</option></select>'); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + form.data('scope', new nglr.Scope({A:{text:"t1", textarea:"t2", radio:"r", checkbox:"c", select:"S"}})); + binder.compile(); + binder.updateView(); + assertEquals(form.find("input[type=text]").attr('value'), 't1'); + assertEquals(form.find("textarea").attr('value'), 't2'); + assertTrue(form.find("input[name=A.radio]").attr('checked')); + assertTrue(!form.find("input[name=A.radioOff]").attr('checked')); + assertTrue(form.find("input[name=A.checkbox]").attr('checked')); + assertTrue(!form.find("input[name=A.checkboxOff]").attr('checked')); + assertEquals(form.find("select").attr('value'), 'S'); + assertEquals(form.find("option[selected]").text(), 'b'); +}; + +BinderTest.prototype.testInputTypeButtonActionExecutesInScope = function(){ + var savedCalled = false; + var c = compile('<input id="apply" type="button" ng-action="person.save()" value="Apply">'); + c.scope.set("person.save", function(){ + savedCalled = true; + }); + c.node.find("#apply").click(); + assertTrue(savedCalled); +}; + +BinderTest.prototype.testInputTypeButtonActionExecutesInScope = function(){ + expectAsserts(1); + var c = compile('<input id="apply" type="image" ng-action="action()">'); + c.scope.set("action", function(){ + assertTrue(true); + }); + c.node.find("#apply").click(); +}; + +BinderTest.prototype.testButtonElementActionExecutesInScope = function(){ + var savedCalled = false; + var c = compile('<button id="apply" ng-action="person.save()">Apply</button>'); + c.scope.set("person.save", function(){ + savedCalled = true; + }); + c.node.find("#apply").click(); + assertTrue(savedCalled); +}; + +BinderTest.prototype.testParseEmptyAnchor = function(){ + var binder = new nglr.Binder(null, null, new MockUrlWatcher()); + var anchor = binder.anchor; + binder.parseAnchor("a#x=1"); + assertEquals(1, binder.anchor.x); + binder.parseAnchor("a#"); + assertTrue("old values did not get removed", !binder.anchor.x); + assertTrue("anchor gor replaced", anchor === binder.anchor); + assertEquals('undefined', typeof (anchor[""])); +}; + +BinderTest.prototype.testParseAnchor = function(){ + var binder = new nglr.Binder(null, null, new MockUrlWatcher()); + binder.parseAnchor("a#x=1"); + assertEquals(binder.anchor.x, "1"); + binder.parseAnchor("a#a=b&c=%20&d"); + assertEquals(binder.anchor.a, 'b'); + assertEquals(binder.anchor.c, ' '); + assertTrue(binder.anchor.d !== null); + assertTrue(!binder.anchor.x); +}; + +BinderTest.prototype.testWriteAnchor = function(){ + var binder = new nglr.Binder(null, null, new MockUrlWatcher()); + binder.urlWatcher.setUrl('a'); + binder.anchor.a = 'b'; + binder.anchor.c = ' '; + binder.anchor.d = true; + binder.updateAnchor(); + assertEquals(binder.urlWatcher.getUrl(), "a#a=b&c=%20&d"); +}; + +BinderTest.prototype.testWriteAnchorAsPartOfTheUpdateView = function(){ + var binder = new nglr.Binder(html("<div/>")[0], null, new MockUrlWatcher()); + binder.urlWatcher.setUrl('a'); + $(binder.doc).data('scope', new nglr.Scope()); + binder.anchor.a = 'b'; + binder.updateView(); + assertEquals(binder.urlWatcher.getUrl(), "a#a=b"); +}; + +BinderTest.prototype.testRepeaterUpdateBindings = function(){ + var form = html('<ul><LI ng-repeat="item in model.items" ng-bind="item.a"/></ul>'); + var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); + var items = [{a:"A"}, {a:"B"}]; + form.data('scope', new nglr.Scope({model:{items:items}})); + binder.compile(); + + binder.updateView(); + assertEquals('<ul>' + + '<#comment></#comment>' + + '<li ng-bind="item.a" ng-repeat-index="0">A</li>' + + '<li ng-bind="item.a" ng-repeat-index="1">B</li>' + + '</ul>', form.sortedHtml()); + + items.unshift({a:'C'}); + binder.updateView(); + assertEquals('<ul>' + + '<#comment></#comment>' + + '<li ng-bind="item.a" ng-repeat-index="0">C</li>' + + '<li ng-bind="item.a" ng-repeat-index="1">A</li>' + + '<li ng-bind="item.a" ng-repeat-index="2">B</li>' + + '</ul>', form.sortedHtml()); + + items.shift(); + binder.updateView(); + assertEquals('<ul>' + + '<#comment></#comment>' + + '<li ng-bind="item.a" ng-repeat-index="0">A</li>' + + '<li ng-bind="item.a" ng-repeat-index="1">B</li>' + + '</ul>', form.sortedHtml()); +}; + +BinderTest.prototype.testRepeaterContentDoesNotBind = function(){ + var form = html('<ul><LI ng-repeat="item in model.items"><span ng-bind="item.a"/></li></ul>'); + form.data('scope', new nglr.Scope({model:{items:[{a:"A"}]}})); + var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals('<ul>' + + '<#comment></#comment>' + + '<li ng-repeat-index="0"><span ng-bind="item.a">A</span></li>' + + '</ul>', form.sortedHtml()); +}; + +BinderTest.prototype.testShouldBindActionsOnRepeaterClone = function(){ + var c = compile('<a ng-repeat="item in items" href="#" ng-action="result.value = item">link</a>'); + jQuery(c).die(); + c.scope.set('result.value', false); + c.scope.set('items', ['abc', 'xyz']); + c.scope.updateView(); + assertEquals(2, c.node.find("a").size()); + c.node.find("a:last").click(); + assertEquals('xyz', c.scope.get('result.value')); +}; + + + +BinderTest.prototype.testRepeaterInputContentDoesNotBind = function(){ + var form = + html('<ul><LI repeater="item in model.items">' + + '<input type="text" name="item.a" value="OLD"/></li></ul>'); + var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); + var items = [{a:"A"}]; + form.data('scope', new nglr.Scope({model:{items:items}})); + + assertEquals(form.find(":input").attr("value"), "OLD"); +}; + +BinderTest.prototype.testExpandEntityTag = function(){ + assertEquals( + '<div ng-entity="Person" ng-watch="$anchor.a:1"></div>', + compileToHtml('<div ng-entity="Person" ng-watch="$anchor.a:1"/>')); +}; + +BinderTest.prototype.testExpandEntityTagWithDefaults = function(){ + assertEquals( + '<div ng-entity="Person:{a:\"a\"}" ng-watch=""></div>', + compileToHtml('<div ng-entity=\'Person:{a:"a"}\'/>')); +}; + +BinderTest.prototype.testExpandEntityTagWithName = function(){ + var c = compile('<div ng-entity="friend=Person"/>'); + assertEquals( + '<div ng-entity="friend=Person" ng-watch="$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};"></div>', + c.node.sortedHtml()); + assertEquals("Person", c.scope.get("friend.$entity")); + assertEquals("friend", c.scope.get("friend.$$anchor")); +}; + +BinderTest.prototype.testExpandSubmitButtonToAction = function(){ + var html = compileToHtml('<input type="submit" value="Save">'); + assertTrue(html, html.indexOf('ng-action="$save()"') > 0 ); + assertTrue(html, html.indexOf('ng-bind-attr="{"disabled":"{{$invalidWidgets}}"}"') > 0 ); +}; + +BinderTest.prototype.testDoNotOverwriteCustomAction = function(){ + var html = compileToHtml('<input type="submit" value="Save" action="foo();">'); + assertTrue(html.indexOf('action="foo();"') > 0 ); +}; + +BinderTest.prototype.testReplaceFileUploadWithSwf = function(){ + expectAsserts(1); + var form = jQuery("body").append('<div id="testTag"><input type="file"></div>'); + form.data('scope', new nglr.Scope()); + var factory = {}; + var binder = new nglr.Binder(form.get(0), factory, new MockUrlWatcher()); + factory.createController = function(node){ + assertEquals(node.attr('type'), 'file'); + return {updateModel:function(){}}; + }; + binder.compile(); + jQuery("#testTag").remove(); +}; + +BinderTest.prototype.testRepeaterAdd = function(){ + var doc = $('<div><input type="text" name="item.x" ng-repeat="item in items"></div>'); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + doc.data('scope', new nglr.Scope({items:[{x:'a'}, {x:'b'}], $binder:binder})); + binder.compile(); + binder.updateView(); + assertEquals('a', doc.find(':input')[0].value); + assertEquals('b', doc.find(':input')[1].value); + + var first = doc.find('[ng-repeat-index="0"]'); + first[0].value = 'ABC'; + first.trigger('keyup'); + assertEquals(doc.scope().get('items')[0].x, 'ABC'); +}; + +BinderTest.prototype.testIfTextBindingThrowsErrorDecorateTheSpan = function(){ + var doc = $('<div>{{error.throw()}}</div>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('error.throw', function(){throw "ErrorMsg1";}); + binder.updateView(); + var span = doc.find('span'); + assertTrue(span.hasClass('ng-exception')); + assertEquals('ErrorMsg1', nglr.fromJson(span.text())); + assertEquals('"ErrorMsg1"', span.attr('ng-error')); + + scope.set('error.throw', function(){throw "MyError";}); + binder.updateView(); + span = doc.find('span'); + assertTrue(span.hasClass('ng-exception')); + assertTrue(span.text(), span.text().match('MyError') !== null); + assertEquals('"MyError"', span.attr('ng-error')); + + scope.set('error.throw', function(){return "ok";}); + binder.updateView(); + assertFalse(span.hasClass('ng-exception')); + assertEquals('ok', span.text()); + assertEquals(null, span.attr('ng-error')); +}; + +BinderTest.prototype.testIfAttrBindingThrowsErrorDecorateTheSpan = function(){ + var doc = $('<div attr="before {{error.throw()}} after"/>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('error.throw', function(){throw "ErrorMsg";}); + binder.updateView(); + assertTrue('ng-exception', doc.hasClass('ng-exception')); + assertEquals('before ["ErrorMsg"] after', doc.attr('attr')); + assertEquals('"ErrorMsg"', doc.attr('ng-error')); + + scope.set('error.throw', function(){ return 'X';}); + binder.updateView(); + assertFalse('!ng-exception', doc.hasClass('ng-exception')); + assertEquals('before X after', doc.attr('attr')); + assertEquals(null, doc.attr('ng-error')); +}; + +BinderTest.prototype.testNestedRepeater = function() { + var doc = html('<div ng-repeat="m in model" name="{{m.name}}">' + + '<ul name="{{i}}" ng-repeat="i in m.item"></ul>' + + '</div>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('model', [{name:'a', item:['a1', 'a2']}, {name:'b', item:['b1', 'b2']}]); + binder.updateView(); + + assertEquals( + //'<#comment></#comment>'+ + '<div name="a" ng-bind-attr="{"name":"{{m.name}}"}" ng-repeat-index="0">'+ + '<#comment></#comment>'+ + '<ul name="a1" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="0"></ul>'+ + '<ul name="a2" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="1"></ul>'+ + '</div>'+ + '<div name="b" ng-bind-attr="{"name":"{{m.name}}"}" ng-repeat-index="1">'+ + '<#comment></#comment>'+ + '<ul name="b1" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="0"></ul>'+ + '<ul name="b2" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="1"></ul>'+ + '</div>', doc.sortedHtml()); +}; + +BinderTest.prototype.testRadioButtonGetsPrefixed = function () { + var doc = html('<input ng-repeat="m in model" type="radio" name="m.a" value="on"/>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('model', ['a1', 'a2']); + binder.updateView(); + + assertEquals( + //'<#comment></#comment>'+ + '<input name="0:m.a" ng-repeat-index="0" type="radio" value="on"></input>'+ + '<input name="1:m.a" ng-repeat-index="1" type="radio" value="on"></input>', + doc.sortedHtml()); +}; + +BinderTest.prototype.testHideBindingExpression = function() { + var doc = html('<div ng-hide="hidden == 3"/>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('hidden', 3); + binder.updateView(); + + assertHidden(doc.children()); + + scope.set('hidden', 2); + binder.updateView(); + + assertVisible(doc.children()); +}; + +BinderTest.prototype.testHideBinding = function() { + var doc = html('<div ng-hide="hidden"/>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('hidden', 'true'); + binder.updateView(); + + assertHidden(doc.children()); + + scope.set('hidden', 'false'); + binder.updateView(); + + assertVisible(doc.children()); + + scope.set('hidden', ''); + binder.updateView(); + + assertVisible(doc.children()); +}; + +BinderTest.prototype.testShowBinding = function() { + var doc = html('<div ng-show="show"/>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('show', 'true'); + binder.updateView(); + + assertVisible(doc.children()); + + scope.set('show', 'false'); + binder.updateView(); + + assertHidden(doc.children()); + + scope.set('show', ''); + binder.updateView(); + + assertHidden(doc.children()); +}; + +BinderTest.prototype.testBindClassUndefined = function() { + var doc = compile('<div ng-class="undefined"/>'); + doc.binder.updateView(); + + assertEquals( + '<div ng-class="undefined"></div>', + doc.node.sortedHtml()); +}; + +BinderTest.prototype.testBindClass = function() { + var doc = html('<div ng-class="class"/>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.set('class', 'testClass'); + binder.updateView(); + + assertEquals(doc.sortedHtml(), + '<div class="testClass" ng-class="class"></div>'); + + scope.set('class', ['a', 'b']); + binder.updateView(); + + assertEquals(doc.sortedHtml(), + '<div class="a,b" ng-class="class"></div>'); +}; + +BinderTest.prototype.testBindClassEvenOdd = function() { + var x = compile('<div ng-repeat="i in [0,1]" ng-class-even="\'e\'" ng-class-odd="\'o\'"/>'); + x.binder.updateView(); + assertEquals( + '<div class="o" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat-index="0"></div>' + + '<div class="e" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat-index="1"></div>', + x.node.sortedHtml()); +}; + +BinderTest.prototype.testBindStyle = function() { + var doc = html('<div ng-style="style"/>'); + var scope = new nglr.Scope(); + doc.data('scope', scope); + var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + + scope.eval('style={color:"red"}'); + binder.updateView(); + + assertEquals("red", doc.find('div').css('color')); + + scope.eval('style={}'); + binder.updateView(); + + assertEquals(doc.sortedHtml(), '<div ng-style="style"></div>'); +}; + +BinderTest.prototype.testActionOnAHrefThrowsError = function(){ + var model = {books:[]}; + var state = compile('<a ng-action="throw {a:\'abc\', b:2};">Add Phone</a>', model); + var input = state.node.find('a'); + input.click(); + assertEquals('abc', nglr.fromJson(input.attr('ng-error')).a); + assertNotNull(input.data('qtip')); + assertTrue("should have an error class", input.hasClass('ng-exception')); + + input.attr('ng-action', '0'); + input.click(); + assertFalse('error class should be cleared', input.hasClass('ng-exception')); +}; + +BinderTest.prototype.testShoulIgnoreVbNonBindable = function(){ + var c = compile("{{a}}" + + "<div ng-non-bindable>{{a}}</div>" + + "<div ng-non-bindable=''>{{b}}</div>" + + "<div ng-non-bindable='true'>{{c}}</div>"); + c.scope.set('a', 123); + c.scope.updateView(); + assertEquals('123{{a}}{{b}}{{c}}', c.node.text()); +}; + +BinderTest.prototype.testOptionShouldUpdateParentToGetProperBinding = function() { + var c = compile('<select name="s"><option ng-repeat="i in [0,1]" value="{{i}}" ng-bind="i"></option></select>'); + c.scope.set('s', 1); + c.binder.updateView(); + assertEquals(1, c.node.find('select')[0].selectedIndex); +}; + +BinderTest.prototype.testRepeaterShouldBindInputsDefaults = function () { + var c = compile('<input value="123" name="item.name" ng-repeat="item in items">'); + c.scope.set('items', [{}, {name:'misko'}]); + c.binder.updateView(); + + assertEquals("123", c.scope.eval('items[0].name')); + assertEquals("misko", c.scope.eval('items[1].name')); +}; + +BinderTest.prototype.testRepeaterShouldCreateArray = function () { + var c = compile('<input value="123" name="item.name" ng-repeat="item in items">'); + c.binder.updateView(); + + assertEquals(0, c.scope.get('items').length); +}; + +BinderTest.prototype.testShouldTemplateBindPreElements = function () { + var c = compile('<pre>Hello {{name}}!</pre>'); + c.scope.set("name", "World"); + c.binder.updateView(); + + assertEquals('<pre ng-bind-template="Hello {{name}}!">Hello World!</pre>', c.node.sortedHtml()); +}; + +BinderTest.prototype.testDissableAutoSubmit = function() { + var c = compile('<input type="submit" value="S"/>', null, {autoSubmit:true}); + assertEquals( + '<input ng-action="$save()" ng-bind-attr="{"disabled":"{{$invalidWidgets}}"}" type="submit" value="S"></input>', + c.node.sortedHtml()); + + c = compile('<input type="submit" value="S"/>', null, {autoSubmit:false}); + assertEquals( + '<input type="submit" value="S"></input>', + c.node.sortedHtml()); +}; + +BinderTest.prototype.testSettingAnchorToNullOrUndefinedRemovesTheAnchorFromURL = function() { + var c = compile(''); + c.binder.urlWatcher.setUrl("http://server/#a=1&b=2"); + c.binder.parseAnchor(); + assertEquals('1', c.binder.anchor.a); + assertEquals('2', c.binder.anchor.b); + + c.binder.anchor.a = null; + c.binder.anchor.b = null; + c.binder.updateAnchor(); + assertEquals('http://server/#', c.binder.urlWatcher.getUrl()); +}; + +BinderTest.prototype.testFillInOptionValueWhenMissing = function() { + var c = compile('<select><option selected="true">A</option><option value="">B</option></select>'); + assertEquals( + '<select><option selected="true" value="A">A</option><option>B</option></select>', + c.node.sortedHtml()); +}; + +BinderTest.prototype.testValidateForm = function() { + var c = compile('<input name="name" ng-required>' + + '<div ng-repeat="item in items"><input name="item.name" ng-required/></div>'); + var items = [{}, {}]; + c.scope.set("items", items); + c.binder.updateView(); + assertEquals(3, c.scope.get("$invalidWidgets.length")); + + c.scope.set('name', 'abc'); + c.binder.updateView(); + assertEquals(2, c.scope.get("$invalidWidgets.length")); + + items[0].name = 'abc'; + c.binder.updateView(); + assertEquals(1, c.scope.get("$invalidWidgets.length")); + + items[1].name = 'abc'; + c.binder.updateView(); + assertEquals(0, c.scope.get("$invalidWidgets.length")); +}; + +BinderTest.prototype.testDeleteAttributeIfEvaluatesFalse = function() { + var c = compile( + '<input name="a0" ng-bind-attr="{disabled:\'{{true}}\'}"><input name="a1" ng-bind-attr="{disabled:\'{{false}}\'}">' + + '<input name="b0" ng-bind-attr="{disabled:\'{{1}}\'}"><input name="b1" ng-bind-attr="{disabled:\'{{0}}\'}">' + + '<input name="c0" ng-bind-attr="{disabled:\'{{[0]}}\'}"><input name="c1" ng-bind-attr="{disabled:\'{{[]}}\'}">'); + c.binder.updateView(); + var html = c.node.html(); + assertEquals(html + 0, 1, c.node.find("input[name='a0']:disabled").size()); + assertEquals(html + 1, 1, c.node.find("input[name='b0']:disabled").size()); + assertEquals(html + 2, 1, c.node.find("input[name='c0']:disabled").size()); + + assertEquals(html + 3, 0, c.node.find("input[name='a1']:disabled").size()); + assertEquals(html + 4, 0, c.node.find("input[name='b1']:disabled").size()); + assertEquals(html + 5, 0, c.node.find("input[name='c1']:disabled").size()); +}; + +BinderTest.prototype.testRepeaterErrorShouldBePlacedOnInstanceNotOnTemplateComment = function () { + var c = compile( + '<input name="person.{{name}}" ng-repeat="name in [\'a\', \'b\']" />'); + c.binder.updateView(); + assertTrue(c.node.find("input").hasClass("ng-exception")); +}; + +BinderTest.prototype.XtestItShouldApplyAttirbutesBeforeTheWidgetsAreMaterialized = function() { + var c = compile( + '<input name="person.{{name}}" ng-repeat="name in [\'a\', \'b\']" />'); + c.scope.set('person', {a:'misko', b:'adam'}); + c.binder.updateView(); + assertEquals("", c.node.html()); +}; + +BinderTest.prototype.testItShouldCallListenersWhenAnchorChanges = function() { + var log = ""; + var c = compile('<div ng-watch="$anchor.counter:count = count+1">'); + c.scope.set("count", 0); + c.scope.addWatchListener("$anchor.counter", function(newValue, oldValue){ + log += oldValue + "->" + newValue + ";"; + }); + assertEquals(0, c.scope.get("count")); + c.binder.onUrlChange("#counter=1"); + assertEquals(1, c.scope.get("count")); + c.binder.onUrlChange("#counter=1"); + assertEquals(1, c.scope.get("count")); + c.binder.onUrlChange("#counter=2"); + assertEquals(2, c.scope.get("count")); + c.binder.onUrlChange("#counter=2"); + assertEquals(2, c.scope.get("count")); + c.binder.onUrlChange("#"); + assertEquals("undefined->1;1->2;2->undefined;", log); + assertEquals(3, c.scope.get("count")); +}; + +BinderTest.prototype.testParseQueryString = function(){ + var binder = new nglr.Binder(); + assertJsonEquals({"a":"1"}, binder.parseQueryString("a=1")); + assertJsonEquals({"a":"1", "b":"two"}, binder.parseQueryString("a=1&b=two")); + assertJsonEquals({}, binder.parseQueryString("")); + + assertJsonEquals({"a":"1", "b":""}, binder.parseQueryString("a=1&b=")); + assertJsonEquals({"a":"1", "b":""}, binder.parseQueryString("a=1&b")); + assertJsonEquals({"a":"1", "b":" 2 "}, binder.parseQueryString("a=1&b=%202%20")); + assertJsonEquals({"a a":"1", "b":"2"}, binder.parseQueryString("a%20a=1&b=2")); + +}; + +BinderTest.prototype.testSetBinderAnchorTriggersListeners = function(){ + expectAsserts(2); + var doc = html("<div/>")[0]; + var binder = new nglr.Binder(doc, null, new MockUrlWatcher()); + var scope = new nglr.Scope({$binder:binder, $anchor:binder.anchor}); + jQuery(doc).data('scope', scope); + + scope.addWatchListener("$anchor.name", function(newVal, oldVal) { + assertEquals("new", newVal); + assertEquals(undefined, oldVal); + }); + + binder.anchor.name = "new"; + binder.onUrlChange("http://base#name=new"); +}; + +BinderTest.prototype.testItShouldDisplayErrorWhenActionIsSyntacticlyIncorect = function(){ + var c = compile( + '<input type="button" ng-action="greeting=\'ABC\'"/>' + + '<input type="button" ng-action=":garbage:"/>'); + c.node.find("input").click(); + assertEquals("ABC", c.scope.get('greeting')); + assertTrue(c.node.find(":input:last").hasClass("ng-exception")); +}; + +BinderTest.prototype.testItShouldSelectTheCorrectRadioBox = function() { + var c = compile( + '<input type="radio" name="sex" value="female"/>' + + '<input type="radio" name="sex" value="male"/>'); + + c.node.find("input[value=female]").click(); + assertEquals("female", c.scope.get("sex")); + assertEquals(1, c.node.find("input:checked").size()); + assertEquals("female", c.node.find("input:checked").attr("value")); + + c.node.find("input[value=male]").click(); + assertEquals("male", c.scope.get("sex")); + assertEquals(1, c.node.find("input:checked").size()); + assertEquals("male", c.node.find("input:checked").attr("value")); +}; + +BinderTest.prototype.testItShouldListenOnRightScope = function() { + var c = compile( + '<div ng-init="counter=0; gCounter=0" ng-watch="w:counter=counter+1">' + + '<div ng-repeat="n in [1,2,4]" ng-watch="w:counter=counter+1;w:$root.gCounter=$root.gCounter+n"/>'); + c.binder.executeInit(); + c.binder.updateView(); + assertEquals(0, c.scope.get("counter")); + assertEquals(0, c.scope.get("gCounter")); + + c.scope.set("w", "something"); + c.binder.updateView(); + assertEquals(1, c.scope.get("counter")); + assertEquals(7, c.scope.get("gCounter")); +}; + +BinderTest.prototype.testItShouldRepeatOnHashes = function() { + var x = compile('<div ng-repeat="(k,v) in {a:0,b:1}" ng-bind=\"k + v\"></div>'); + x.binder.updateView(); + assertEquals( + '<div ng-bind=\"k + v\" ng-repeat-index="0">a0</div>' + + '<div ng-bind=\"k + v\" ng-repeat-index="1">b1</div>', + x.node.sortedHtml()); +}; + +BinderTest.prototype.testItShouldFireChangeListenersBeforeUpdate = function(){ + var x = compile('<div ng-bind="name"></div>'); + x.scope.set("name", ""); + x.scope.set("watched", "change"); + x.scope.watch("watched:name=123"); + x.scope.updateView(); + assertEquals(123, x.scope.get("name")); + assertEquals( + '<div ng-bind="name">123</div>', + x.node.sortedHtml()); +}; + +BinderTest.prototype.testItShouldHandleMultilineBindings = function(){ + var x = compile('<div>{{\n 1 \n + \n 2 \n}}</div>'); + x.scope.updateView(); + assertEquals("3", x.node.text()); +}; + +BinderTest.prototype.testItBindHiddenInputFields = function(){ + var x = compile('<input type="hidden" name="myName" value="abc" />'); + x.scope.updateView(); + assertEquals("abc", x.scope.get("myName")); +}; + +BinderTest.prototype.testItShouldRenderMultiRootHtmlInBinding = function() { + var x = compile('<div>before {{a|html}}after</div>'); + x.scope.set("a", "a<b>c</b>d"); + x.binder.updateView(); + assertEquals( + '<div>before <span ng-bind="a|html">a<b>c</b>d</span>after</div>', + x.node.sortedHtml()); +}; |
